FPGAでTD4を作成する2(レジスタ、データセレクタ作成)
「CPUの創りかた」という本に載っている4bitCPU「TD4」をFPGAで作成します。
各パーツごとに作成して最後に全体を統合します。
今回はレジスタ、データセレクタを作成します。
TD4の作成について
使用ボード:Basys2 (Xilinx)
ツール:ISE Project Navigator 14.7
シミュレーション:Isim
言語:VHDL
TD4の全体構成をブロック図で表したのが下図です。
今回はFPGAで作りますが、構成は「CPUの創りかた」に合わせていきます。
ちなみに矢印の色が青とオレンジの2色ありますが、色の違いに特に意味はなく、単に見やすくしただけです。
前回の記事はこちらです。
www.hirobiro-life.com
レジスタの機能
TD4のレジスタは4種類あり、本書内ではフリップフロップで実現しています。
デコーダから入力されるLoad信号で4つあるレジスタのうち1つを更新し、残りの3つは値を保持します。
各レジスタにはタイミング制御のためのクロックとリセット信号を入力します。
AレジスタとBレジスタはデータセレクタに4bitの信号を送信します。
CレジスタはLEDへの出力ポートとして使い、最後のDレジスタはROMのアドレスを指定するプログラムカウンタとして使用します。
プログラムカウンタはクロック信号が入るたびに+1して次のROMアドレスを指定できるようにしています。
レジスタのコード
以下はVHDLで書いたレジスタのコードです。
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity register4 is Port ( CLK : in STD_LOGIC; RST : in STD_LOGIC; LOAD : in STD_LOGIC_VECTOR(3 downto 0); IN_DATA : in STD_LOGIC_VECTOR(3 downto 0); OUT_A : out STD_LOGIC_VECTOR(3 downto 0); OUT_B : out STD_LOGIC_VECTOR(3 downto 0); OUT_LD : out STD_LOGIC_VECTOR(3 downto 0); ADDRESS : out STD_LOGIC_VECTOR(3 downto 0)); end register4; architecture RTL of register4 is signal reg_a, reg_b, reg_c, reg_d : STD_LOGIC_VECTOR(3 downto 0); begin OUT_A <= reg_a; OUT_B <= reg_b; OUT_LD <= reg_c; ADDRESS <= reg_d; process (CLK, RST) begin if(RST = '1')then reg_a <= (others => '0'); reg_b <= (others => '0'); reg_c <= (others => '0'); reg_d <= (others => '0'); elsif(rising_edge(CLK))then --プログラムカウンタ(reg_d)を+1する reg_d <= reg_d + 1; if(LOAD(0) = '0') then reg_a <= IN_DATA; elsif(LOAD(1) = '0') then reg_b <= IN_DATA; elsif(LOAD(2) = '0') then reg_c <= IN_DATA; elsif(LOAD(3) = '0') then reg_d <= IN_DATA; end if; end if; end process; end RTL;
レジスタのシミュレーション
上で書いたレジスタのコードをシミュレーションするためのテストベンチのコードが以下です。
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY register4_tb IS END register4_tb; ARCHITECTURE behavior OF register4_tb IS -- Component Declaration for the Unit Under Test (UUT) COMPONENT register4 PORT( CLK : IN std_logic; RST : IN std_logic; LOAD : IN std_logic_vector(3 downto 0); IN_DATA : IN std_logic_vector(3 downto 0); OUT_A : OUT std_logic_vector(3 downto 0); OUT_B : OUT std_logic_vector(3 downto 0); OUT_LD : OUT std_logic_vector(3 downto 0); ADDRESS : OUT std_logic_vector(3 downto 0) ); END COMPONENT; --Inputs signal CLK : std_logic; signal RST : std_logic := '0'; signal LOAD : std_logic_vector(3 downto 0) := (others => '1'); signal IN_DATA : std_logic_vector(3 downto 0) := (others => '0'); --Outputs signal OUT_A : std_logic_vector(3 downto 0); signal OUT_B : std_logic_vector(3 downto 0); signal OUT_LD : std_logic_vector(3 downto 0); signal ADDRESS : std_logic_vector(3 downto 0); -- Clock period definitions constant CLK_period : time := 10 ns; BEGIN -- Instantiate the Unit Under Test (UUT) uut: register4 PORT MAP ( CLK => CLK, RST => RST, LOAD => LOAD, IN_DATA => IN_DATA, OUT_A => OUT_A, OUT_B => OUT_B, OUT_LD => OUT_LD, ADDRESS => ADDRESS ); -- Clock process definitions process begin CLK <= '0'; wait for CLK_period/2; CLK <= '1'; wait for CLK_period/2; end process; --LOADはローアクティブ process begin IN_DATA <= "1010"; wait for 7 ns; LOAD <= "1110"; wait for 40 ns; LOAD <= "1101"; wait for 20 ns; RST <= '1'; wait for 10 ns; RST <= '0'; IN_DATA <= "1100"; wait for 40 ns; LOAD <= "1011"; wait for 40 ns; LOAD <= "0111"; wait; end process; END;
シミュレーション結果は以下です。
LOAD信号を変更するたびにA-Dの各レジスタのうち指定されたレジスタのデータが書き換わっています。
またリセット信号(RST)が入ると各レジスタの値が"0000"に書き換わっています。
またプログラムカウンタのDレジスタの値はクロックが立ち上がるたびに+1されています。
データセレクタの機能
データセレクタは4入力(Aレジスタ、Bレジスタ、スイッチ入力、"0000")のうちから1つを選び、ALUに出力します。
この選択にはデコーダから入力される2bitのセレクト信号を利用します。
データセレクタのコード
以下はVHDLで書いたデータセレクタのコードです。
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity data_selector is Port ( IN_A : in STD_LOGIC_VECTOR(3 downto 0); IN_B : in STD_LOGIC_VECTOR(3 downto 0); IN_SW : in STD_LOGIC_VECTOR(3 downto 0); SEL_A : in STD_LOGIC; SEL_B : in STD_LOGIC; OUT_Y : out STD_LOGIC_VECTOR(3 downto 0)); end data_selector; architecture RTL of data_selector is begin process (IN_A, IN_B, IN_SW, SEL_A, SEL_B) begin if(SEL_A = '0' and SEL_B = '0') then OUT_Y <= IN_A; elsif(SEL_A = '1' and SEL_B = '0') then OUT_Y <= IN_B; elsif(SEL_A = '0' and SEL_B = '1') then OUT_Y <= IN_SW; else OUT_Y <= "0000"; end if; end process; end RTL;
データセレクタのシミュレーション
データセレクタのテストベンチコードが以下です。
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY data_selector_tb IS END data_selector_tb; ARCHITECTURE behavior OF data_selector_tb IS -- Component Declaration for the Unit Under Test (UUT) COMPONENT data_selector Port ( IN_A : in STD_LOGIC_VECTOR(3 downto 0); IN_B : in STD_LOGIC_VECTOR(3 downto 0); IN_SW : in STD_LOGIC_VECTOR(3 downto 0); SEL_A : in STD_LOGIC; SEL_B : in STD_LOGIC; OUT_Y : out STD_LOGIC_VECTOR(3 downto 0)); END COMPONENT; --Inputs signal IN_A : STD_LOGIC_VECTOR(3 downto 0) := "1111"; signal IN_B : STD_LOGIC_VECTOR(3 downto 0) := "0011"; signal IN_SW : STD_LOGIC_VECTOR(3 downto 0) := "1100"; signal SEL_A : STD_LOGIC:= '0'; signal SEL_B : STD_LOGIC:= '0'; --Outputs signal OUT_Y : STD_LOGIC_VECTOR(3 downto 0); BEGIN -- Instantiate the Unit Under Test (UUT) uut: data_selector PORT MAP ( IN_A => IN_A, IN_B => IN_B, IN_SW => IN_SW, SEL_A => SEL_A, SEL_B => SEL_B, OUT_Y => OUT_Y ); -- Stimulus process stim_proc: process begin wait for 100 ns; SEL_A <= '1'; wait for 100 ns; SEL_A <= '0'; SEL_B <= '1'; wait for 100 ns; SEL_A <= '1'; wait; end process; END;
シミュレーション結果は以下です。
SEL_A, SEL_B信号に応じてIN_A, IN_B, IN_SW, "0000"のうちいずれかをOUT_Yに書き込んでいます。
まとめ
今回はレジスタとデータセレクタを作成しました。
AレジスタとBレジスタはデータセレクタに4bitの信号を送信します。
CレジスタはLEDへの出力ポートとして使い、DレジスタはROMのアドレスを指定するプログラムカウンタとして使用します。
データセレクタは4入力(Aレジスタ、Bレジスタ、スイッチ入力、"0000")のうちから1つを選び、ALUに出力します。
次の記事はこちらです。
www.hirobiro-life.com