例えばテスト用に次のような「プログラム」を保持するメモリを設計してみましょう。
| アドレス | 内容 | 命令 |
| 0 | 0000 0001 | mov 1, r0 |
| 1 | 0010 0010 | add r0, 2, r0 |
| 2 | 0110 0010 | jmp 2 |
※ヒント: 例えばaddr=0を与えられたら、data="0000 0001"を出力する回路、ということになる
以下、ひな形:
entity mem is
port( addr: in std_logic_vector(3 downto 0);
data: out std_logic_vector(7 downto 0));
end mem;
architecture Behavioral of mem is
begin
...
end Behavioral;
(1日目はこのあたりまでを目安にしておくとよいでしょう)
またst=1でr0またはr1へ書き込まれる値を、st=0のときにresに決めておく、 というルールを決めておきます。
| op | 命令の内容 | res | resを代入する先 |
| 0000 | mov imm, r0 | imm | r0 |
| 0001 | mov imm, r1 | imm | r1 |
| 0010 | add r0, imm, r0 | r0 + imm | r0 |
| 0011 | |||
| ... |
またプログラムカウンタ(PC)の更新は、jmp/jz命令では分岐先のアドレス (つまりimmの値)に、それ以外の命令では+1を行う、ことになります。 つまり、op(とjz命令ではZフラグの値)に応じて、 pcの値を更新すればよいことになります。 先ほどと同様に、pcの値の更新の内容を整理しておきましょう。
| op | 命令の内容 | 次のpcの値 |
| 0000 | mov imm, r0 | pc + 1 |
| 0001 | mov imm, r1 | pc + 1 |
| ... | ... | ... |
| 0110 | jmp imm | imm |
| ... | ... | ... |
以下、ひな形:
entity cpu is
port( clk, rst: in std_logic;
addr: out std_logic_vector(3 downto 0);
data: in std_logic_vector(7 downto 0);
r0, r1: out std_logic_vector(3 downto 0);
z : out std_logic
);
end cpu;
architecture Behavioral of cpu is
signal op, imm, pc, res : std_logic_vector(3 downto 0);
signal r0_reg, r1_reg : std_logic_vector(3 downto 0); -- r0/r1 variable
signal z_reg, st : std_logic; -- z flag variable
begin
op <= data(7 downto 4); -- op from mem's data
imm <= data(3 downto 0); -- imm from mem's data
addr <= ... -- address for mem
r0 <= r0_reg; -- r0 output
r1 <= r1_reg; -- r1 output
z <= z_reg; -- z output
process (rst, clk) begin
if (rst = '1') then
-- reset variables
pc <= "0000"; z_reg <= '0'; st <= '0'; r0_reg <= "0000"; r1_reg <= "0000";
elsif (clk'event and clk = '1') then
if (st = '0') then
-- state 0 : fetch, register updadate preparation
-- setting res
case op is
when "0000" => res <= imm;
...
when others => null;
end case;
-- st update
st <= '1';
else
-- state 1 : register & PC & Z update
-- PC update
if (op = "0110") then pc ... -- jmp
elsif (op = "0111" and ...) then pc <= imm; -- jz
else pc <= pc + 1;
end if;
-- register update
case op is
when "0000" => r0_reg <= res;
when "0001" => r1_reg <= res;
...
when others => null;
end case;
-- Z flag update
z_reg <= '0';
case op is
when "0000" => if (res = "0000") then z_reg <= '1'; end if;
...
when others => null;
end case;
-- st update
st <= '0';
end if;
end if;
end process;
end Behavioral;
またこのほか、ピン定義ファイルとしてsub3_cpu.ucfをプロジェクトに追加しておきます。
以下、cpu_topのひな型(ほぼこの通り使えばOKのはず)。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity cpu_top is
port (
CLK50M : in STD_LOGIC;
SA : out STD_LOGIC_VECTOR(3 downto 0);
SG : out STD_LOGIC_VECTOR(7 downto 0);
SW : in STD_LOGIC_VECTOR(5 downto 0);
LED : out std_logic_vector(1 downto 0)
);
end cpu_top;
architecture Behavioral of cpu_top is
component sw_clk
port (
ia, ib: in std_logic;
clk : out std_logic
);
end component;
component cpu
port( clk, rst: in std_logic;
addr: out std_logic_vector(3 downto 0);
data: in std_logic_vector(7 downto 0);
r0, r1: out std_logic_vector(3 downto 0);
z : out std_logic
);
end component;
component seg7
Port ( clk : in STD_LOGIC;
rst : in STD_LOGIC;
d0 : in STD_LOGIC_VECTOR (3 downto 0);
d1 : in STD_LOGIC_VECTOR (3 downto 0);
d2 : in STD_LOGIC_VECTOR (3 downto 0);
d3 : in STD_LOGIC_VECTOR (3 downto 0);
sa : out STD_LOGIC_VECTOR (3 downto 0);
sg : out STD_LOGIC_VECTOR (7 downto 0));
end component;
component mem is
port( addr: in std_logic_vector(3 downto 0);
data: out std_logic_vector(7 downto 0));
end component;
signal ia, ib, clk_cpu, rst, z : std_logic;
signal addr, r0, r1 : std_logic_vector(3 downto 0);
signal data : std_logic_vector(7 downto 0);
begin
ia <= SW(4); ib <= SW(5); -- sw_clk's input
rst <= SW(2);
LED(0) <= clk_cpu;
LED(1) <= z;
icpu : cpu port map(clk_cpu, rst, addr, data, r0, r1);
iseg7 : seg7 port map(CLK50M, rst, r0, r1, addr, data(7 downto 4), SA, SG);
isw_clk : sw_clk port map(ia, ib, clk_cpu);
imem : mem port map(addr, data);
end Behavioral;
(2日目はこのあたりまでを目安にしておくとよいでしょう) (補足) 新規ファイルをつくるとき、必要な冒頭の定義(includeのようなもの)が入らない場合があるようです。冒頭のuse文の付近に次の2行がない場合は、追記しておいてください。
use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL;
プログラム1: レジスタへの値の代入
addr data 0 00000001 : mov 1, r0 1 00010010 : mov 2, r1 2 01100010 : jmp 2
プログラム2: 乗算(3×4=12)
addr data 0 00000000 : mov 0, r0 1 00010100 : mov 4, r1 2 00100011 : add r0, 3, r0 3 01011111 : add r1, 15, r1 4 01110110 : jz 6 5 01100010 : jmp 2 6 01100110 : jmp 6この他にも、例えば除算の実行などのいろいろなプログラムを記述して 実行させてみましょう。