FPGAでTD4を作成する2(レジスタ、データセレクタ作成)

「CPUの創りかた」という本に載っている4bitCPU「TD4」をFPGAで作成します。
各パーツごとに作成して最後に全体を統合します。
今回はレジスタ、データセレクタを作成します。 ====

TD4の作成について

 

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

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


f:id:hirokun1735:20190103213506j:plain


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

 

www.hiro-hard.info

 

レジスタの機能

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されています。


f:id:hirokun1735:20190107201916j:plain

 

データセレクタの機能

データセレクタは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に書き込んでいます。


f:id:hirokun1735:20190107202119j:plain

 

まとめ

今回はレジスタとデータセレクタを作成しました。
AレジスタとBレジスタはデータセレクタに4bitの信号を送信します。
CレジスタはLEDへの出力ポートとして使い、DレジスタはROMのアドレスを指定するプログラムカウンタとして使用します。


データセレクタは4入力(Aレジスタ、Bレジスタ、スイッチ入力、"0000")のうちから1つを選び、ALUに出力します。


次の記事はこちらです。

 

www.hiro-hard.info