FPGAでTD4を作成する3(ALU作成)

「CPUの創りかた」という本に載っている4bitCPU「TD4」をFPGAで作成します。
各パーツごとに作成して最後に全体を統合します。
3回目となる今回はALUを作成します。 ====

TD4の作成について

 

【開発環境】
使用ボード:Basys2 (Xilinx)
ツール:ISE Project Navigator 14.7
シミュレーション:Isim
言語:VHDL

TD4の全体構成をブロック図で表したのが下図です。
今回はFPGAで作りますが、構成は「CPUの創りかた」に合わせていきます。
ちなみに矢印の色が青とオレンジの2色ありますが、色の違いに特に意味はなく、単に見やすくしただけです。


f:id:hirokun1735:20190103213506j:plain

前回の記事はこちらです。

 

www.hiro-hard.info

 

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入力の加算の結果を元に場合分けして出力します。
結果は以下になります。


f:id:hirokun1735:20190107201658j:plain

 

キャリーフラグのコード

全加算器×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全体の回路は以下のようになります。


f:id:hirokun1735:20190107212011j:plain

 

まとめ

今回はALUを作成しました。
TD4のALUは4bit用の加算器とキャリーフラグ用フリップフロップで作成しています。
キャリーフラグを用いて16を超えたことを判定することで条件分岐の機能を持たせています。


次の記事はこちらです。

 

www.hiro-hard.info