FPGA超入門 その2-VHDLとVelilog-
ハードウェア記述言語は様々なものがありますが、代表的な2つ(VHDLとVelilog)について、簡単に説明します。
VHDL
IEEE 1076として標準化されています。最新版はIEEE 1076-2019ですが、まだ対応したツールが少ないので、IEEE 1076-2008が実質的な最新版と言えるでしょう。
拡張子は.vhdです。windowsだとハードディスクイメージファイルと受け取られてしまうこともあります。.vhdlでも可だったかと思います。
Velilog
IEEE 1364として標準化されています。最新版はIEEE 1364-2005です。後継言語として、System Verilogがあり、こちらもIEEE 1800として標準化されています。最新版はIEEE 1800-2017です。Velilogより色々と扱い易くなっている面もあるので、これから習得するのであれば、System Verilogの方がよいかもしれません。
verilogの拡張子は.v、System Verilogの拡張子は.svです。
記述例
例として、下記のようなserial_adder回路について、それぞれの記述例を示します。
色の付いた四角で囲まれた部分の機能は、それぞれ下記のような感じです。
ブロック | 機能 |
---|---|
half_adder | AとBのXORをSに出力 AとBのANDをCに出力 |
full_adder | 後段のhalf_adderのAに前段のhalf_adderのSを入力 後段のhalf_adderのBにXを入力 後段のhalf_adderのSをSに出力 前段と後段のhalf_adderのCのORをCに出力 |
serial_adder | AとBをそれぞれfull_adderのAとBに入力 full_adderのSをSに出力 full_adderのCをD-FFのDに入力 D-FFのQをfull_adderのXに入力 CLKはD-FFに接続 |
それぞれソースコードとして内側から順に記述してみます。 |
VHDL
library IEEE;
use IEEE.std_logic_1164.all;
entity half_adder is
port (
A : in std_logic;
B : in std_logic;
S : out std_logic;
C : out std_logic
);
end entity half_adder;
architecture RTL of half_adder is
begin
S <= A xor B;
C <= A and B;
end architecture RTL;
library IEEE;
use IEEE.std_logic_1164.all;
entity full_adder is
port (
A : in std_logic;
B : in std_logic;
X : in std_logic;
S : out std_logic;
C : out std_logic
);
end entity full_adder;
architecture RTL of full_adder is
component half_adder is
port (
A : in std_logic;
B : in std_logic;
S : out std_logic;
C : out std_logic
);
end component half_adder;
signal S1 : std_logic;
signal C1 : std_logic;
signal C2 : std_logic;
begin
half_adder1 : half_adder
port map (
A => A,
B => B,
S => S1,
C => C1
);
half_adder2 : half_adder
port map (
A => S1,
B => X,
S => S,
C => C2
);
C <= C1 or C2;
end architecture RTL;
library IEEE;
use IEEE.std_logic_1164.all;
entity serial_adder is
port (
A : in std_logic;
B : in std_logic;
CLK : in std_logic;
S : out std_logic
);
end entity serial_adder;
architecture RTL of serial_adder is
component full_adder is
port (
A : in std_logic;
B : in std_logic;
X : in std_logic;
S : out std_logic;
C : out std_logic
);
end component full_adder;
signal C1 : std_logic;
signal Q1 : std_logic;
begin
full_adder1 : full_adder
port map (
A => A,
B => B,
X => Q1,
S => S,
C => C1
);
process(CLK) begin
if(CLK'event and CLK = '1') then
Q1 <= C1;
end if;
end process;
end architecture RTL;
Velilog
module half_adder
(
input wire A,
input wire B,
output wire S,
output wire C
);
assign S = A ^ B;
assign C = A & B;
endmodule
module full_adder
(
input wire A,
input wire B,
input wire X,
output wire S,
output wire C
);
wire S1;
wire C1;
wire C2;
half_adder half_adder1
(
.A(A),
.B(B),
.S(S1),
.C(C1)
);
half_adder half_adder2
(
.A(S1),
.B(X),
.S(S),
.C(C2)
);
assign C = C1 | C2;
endmodule
module serial_adder
(
input wire A,
input wire B,
input wire CLK,
output wire S
);
wire C1;
reg Q1;
full_adder full_adder1
(
.A(A),
.B(B),
.X(Q1),
.S(S),
.C(C1)
);
always@(posedge CLK) begin
Q1 <= C1;
end
endmodule
細かいことは追々書いていくことにして、こんな感じで回路を記述していきます。
実はこのserial_adderは、2進数の足し算の筆算を行うような回路になっていて、加算したい2つの数を2進数にして、CLK毎に最下位bitから順に入力していくと、出力Sに最下位bitから順に加算結果が出てきます。
full_adderはA+B+Xの足し算を行い、結果がCとS(Cがbit1、Sがbit0)に出てきますので、もっと簡単な書き方で同じ動作を実現することが出来ます。
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
entity serial_adder2 is
port (
A : in std_logic;
B : in std_logic;
CLK : in std_logic;
S : out std_logic
);
end entity serial_adder2;
architecture RTL of serial_adder2 is
signal add : std_logic_vector(1 downto 0);
signal Q1 : std_logic;
begin
add <= ('0' & A) + ('0' & B) + ('0' & Q1);
S <= add(0);
process(CLK) begin
if(CLK'event and CLK = '1') then
Q1 <= add(1);
end if;
end process;
end architecture RTL;
module serial_adder2
(
input wire A,
input wire B,
input wire CLK,
output wire S
);
wire [1:0] add;
reg Q1;
assign add = A + B + Q1;
assign S = add[0];
always@(posedge CLK) begin
Q1 <= add[1];
end
endmodule
このように、同じ機能を実現するのに全く違った書き方もあります。上記の場合は恐らく似たような回路が合成されると思いますが、途中の回路構成が全く異なっても最終的な結果は同じになるように作ることもあります。それは、例えばリソースを少なくするものだったり、早く動作するものだったり。どう実現するかを考えるのも醍醐味であり、難しいところでもあると思います。
Discussion