FPGAでTD4を作成する3(ALU作成)
「CPUの創りかた」という本に載っている4bitCPU「TD4」をFPGAで作成します。
各パーツごとに作成して最後に全体を統合します。
3回目となる今回はALUを作成します。
TD4の作成について
使用ボード:Basys2 (Xilinx)
ツール:ISE Project Navigator 14.7
シミュレーション:Isim
言語:VHDL
TD4の全体構成をブロック図で表したのが下図です。
今回はFPGAで作りますが、構成は「CPUの創りかた」に合わせていきます。
ちなみに矢印の色が青とオレンジの2色ありますが、色の違いに特に意味はなく、単に見やすくしただけです。
前回の記事はこちらです。
www.hirobiro-life.com
ALUの機能
ALU(Arithmetic Logic Unit)とはCPUの演算回路です。
TD4の場合は加算器の機能しかありません。4bitの2入力と桁上がりのキャリー入力を備えています。
加算した結果が16以上となり4bitを超えると加算器は繰り上がり(キャリー)を出力します。
この機能を利用して条件分岐命令を実現できます。
4bitなので全加算器が4個、キャリーフラグ用にフリップフロップを1個使用しています。
これをFPGAで作成します。
全加算器のコード
ALUには全加算器が4個用いられているので、まずは1個の全加算器のコードを書きます。
これを4つ組み合わせてキャリーフラグもくっつけてALUとします。
つまり階層設計にします。
以下が全加算器のVHDLコードです。
データセレクタから送られる入力IN_Y、ROMから送られる入力IN_DATA、下位桁の全加算器から送られる入力CINを各bitごとに加算し、その結果によって場合分けしています。
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity f_adder is Port ( IN_Y : in STD_LOGIC_VECTOR(1 downto 0); IN_DATA : in STD_LOGIC_VECTOR(1 downto 0); CIN : in STD_LOGIC_VECTOR(1 downto 0); CRR : out STD_LOGIC_VECTOR(1 downto 0); DATA : out STD_LOGIC); end f_adder; architecture RTL of f_adder is signal q : std_logic_vector(1 downto 0); begin process (q, IN_Y, IN_DATA, CIN) begin q <= IN_Y + IN_DATA + CIN; if(q = 3)then DATA <= '1'; CRR <= "01"; elsif(q = 2) then DATA <= '0'; CRR <= "01"; elsif(q = 1) then DATA <= '1'; CRR <= "00"; else DATA <= '0'; CRR <= "00"; end if; end process; end RTL;
全加算器のシミュレーション
テストベンチのコードが以下になります。
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY f_adder_tb IS END f_adder_tb; ARCHITECTURE behavior OF f_adder_tb IS -- Component Declaration for the Unit Under Test (UUT) COMPONENT f_adder PORT( IN_Y : IN STD_LOGIC_VECTOR(1 downto 0); IN_DATA : IN STD_LOGIC_VECTOR(1 downto 0); CIN : IN STD_LOGIC_VECTOR(1 downto 0); CRR : OUT STD_LOGIC_VECTOR(1 downto 0); DATA : OUT STD_LOGIC ); END COMPONENT; --Inputs signal IN_Y : std_logic_vector(1 downto 0); signal IN_DATA : std_logic_vector(1 downto 0); signal CIN : std_logic_vector(1 downto 0); --Outputs signal CRR : std_logic_vector(1 downto 0); signal DATA : std_logic; BEGIN -- Instantiate the Unit Under Test (UUT) uut: f_adder PORT MAP ( IN_Y => IN_Y, IN_DATA => IN_DATA, CIN => CIN, CRR => CRR, DATA => DATA ); process begin IN_Y <= "00"; IN_DATA <= "00"; CIN <= "00"; wait for 100 ns; IN_Y <= "01"; wait for 100 ns; IN_DATA <= "01"; wait for 100 ns; CIN <= "01"; wait for 100 ns; IN_DATA <= "00"; wait for 100 ns; IN_Y <= "00"; wait; end process; END;
3つの入力を一定時間ごとに切り替えています。3入力の加算の結果を元に場合分けして出力します。
結果は以下になります。
キャリーフラグのコード
全加算器×4の繰り上がりを判定(つまり結果が16以上になったかどうかを判定)するキャリーフラグは前回の記事で作成したレジスタのコードと同様の構成にしました。
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity carry_flag is Port ( CLK : in STD_LOGIC; RST : in STD_LOGIC; CARRY : in STD_LOGIC_VECTOR(1 downto 0); FLAG : out STD_LOGIC); end carry_flag; architecture RTL of carry_flag is signal c_flag : std_logic; begin FLAG <= c_flag; process (CLK, RST) begin if(RST = '1')then c_flag <= '0'; elsif(rising_edge(CLK))then if(CARRY = "01") then c_flag <= '1'; else c_flag <= '0'; end if; end if; end process; end RTL;
ALUのコード
全加算器4つとキャリーフラグを結合させて上位モジュールであるALUを作成します。
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity ALU is Port ( CLK : in STD_LOGIC; RST : in STD_LOGIC; IN_Y : in STD_LOGIC_VECTOR(3 downto 0); ROM_DATA : in STD_LOGIC_VECTOR(3 downto 0); OUT_DATA : out STD_LOGIC_VECTOR(3 downto 0); C_FLAG : out STD_LOGIC); end ALU; architecture RTL of ALU is component f_adder Port ( IN_Y : in STD_LOGIC_VECTOR(1 downto 0); IN_DATA : in STD_LOGIC_VECTOR(1 downto 0); CIN : in STD_LOGIC_VECTOR(1 downto 0); CRR : out STD_LOGIC_VECTOR(1 downto 0); DATA : out STD_LOGIC); end component; component carry_flag Port ( CLK : in STD_LOGIC; RST : in STD_LOGIC; CARRY : in STD_LOGIC_VECTOR(1 downto 0); FLAG : out STD_LOGIC); end component; signal in_y0 : STD_LOGIC_VECTOR(1 downto 0); signal in_y1 : STD_LOGIC_VECTOR(1 downto 0); signal in_y2 : STD_LOGIC_VECTOR(1 downto 0); signal in_y3 : STD_LOGIC_VECTOR(1 downto 0); signal in_data0 : STD_LOGIC_VECTOR(1 downto 0); signal in_data1 : STD_LOGIC_VECTOR(1 downto 0); signal in_data2 : STD_LOGIC_VECTOR(1 downto 0); signal in_data3 : STD_LOGIC_VECTOR(1 downto 0); signal crr0 : STD_LOGIC_VECTOR(1 downto 0); signal crr1 : STD_LOGIC_VECTOR(1 downto 0); signal crr2 : STD_LOGIC_VECTOR(1 downto 0); signal crr3 : STD_LOGIC_VECTOR(1 downto 0); signal data0 : std_logic; signal data1 : std_logic; signal data2 : std_logic; signal data3 : std_logic; begin in_y0 <= '0' & IN_Y(0); in_y1 <= '0' & IN_Y(1); in_y2 <= '0' & IN_Y(2); in_y3 <= '0' & IN_Y(3); in_data0 <= '0' & ROM_DATA(0); in_data1 <= '0' & ROM_DATA(1); in_data2 <= '0' & ROM_DATA(2); in_data3 <= '0' & ROM_DATA(3); U20 : f_adder port map(in_y0, in_data0, "00", crr0, data0); U21 : f_adder port map(in_y1, in_data1, crr0, crr1, data1); U22 : f_adder port map(in_y2, in_data2, crr1, crr2, data2); U23 : f_adder port map(in_y3, in_data3, crr2, crr3, data3); U24 : carry_flag port map(clk, rst, crr3, c_flag); OUT_DATA <= data3 & data2 & data1 & data0; end RTL;
ALU全体の回路は以下のようになります。
まとめ
今回はALUを作成しました。
TD4のALUは4bit用の加算器とキャリーフラグ用フリップフロップで作成しています。
キャリーフラグを用いて16を超えたことを判定することで条件分岐の機能を持たせています。
次の記事はこちらです。
www.hirobiro-life.com