Exctract cpu into its own module
This commit is contained in:
parent
9e61fd9456
commit
634596fd0f
70
cpu/alu.vhdl
Normal file
70
cpu/alu.vhdl
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.numeric_std.all;
|
||||||
|
|
||||||
|
entity alu is
|
||||||
|
port(
|
||||||
|
a: in std_logic_vector(15 downto 0);
|
||||||
|
b: in std_logic_vector(15 downto 0);
|
||||||
|
sel: in std_logic_vector(3 downto 0);
|
||||||
|
|
||||||
|
flag: out std_logic;
|
||||||
|
q: out std_logic_vector(15 downto 0)
|
||||||
|
);
|
||||||
|
end alu;
|
||||||
|
|
||||||
|
architecture behavior of alu is
|
||||||
|
begin
|
||||||
|
|
||||||
|
process(a, b, sel) is
|
||||||
|
variable tmp: std_logic_vector(31 downto 0);
|
||||||
|
variable idx: natural;
|
||||||
|
begin
|
||||||
|
|
||||||
|
q <= x"0000";
|
||||||
|
flag <= '0';
|
||||||
|
tmp := x"00000000";
|
||||||
|
idx := 0;
|
||||||
|
|
||||||
|
case sel is
|
||||||
|
when "0011" => -- q = a + b, flag is carry
|
||||||
|
tmp(16 downto 0) := std_logic_vector(unsigned('0' & a) + unsigned(b));
|
||||||
|
q <= tmp(15 downto 0);
|
||||||
|
flag <= tmp(16);
|
||||||
|
when "0100" => -- q = a - b, flag if a < b
|
||||||
|
tmp(16 downto 0) := std_logic_vector(unsigned('1' & a) - unsigned(b));
|
||||||
|
q <= tmp(15 downto 0);
|
||||||
|
flag <= not tmp(16);
|
||||||
|
when "0101" => q <= a or b;
|
||||||
|
when "0110" => q <= a and b;
|
||||||
|
when "0111" => q <= not a;
|
||||||
|
when "1000" => q <= a xor b;
|
||||||
|
when "1001" => -- left shift by 1
|
||||||
|
q <= a(14 downto 0) & '0';
|
||||||
|
flag <= a(15);
|
||||||
|
when "1010" => -- right shift by b(3 downto 0)
|
||||||
|
idx := to_integer(unsigned(b(3 downto 0)));
|
||||||
|
tmp(15 - idx downto 0) := a(15 downto idx);
|
||||||
|
q <= tmp(15 downto 0);
|
||||||
|
flag <= a(0);
|
||||||
|
when "1011" => -- q = a * b
|
||||||
|
--tmp := std_logic_vector(unsigned(a) * unsigned(b));
|
||||||
|
q <= tmp(15 downto 0);
|
||||||
|
when "1100" => -- flag if a = b
|
||||||
|
if (a = b) then
|
||||||
|
flag <= '1';
|
||||||
|
else
|
||||||
|
flag <= '0';
|
||||||
|
end if;
|
||||||
|
|
||||||
|
--- room for a few more things:
|
||||||
|
-- - rotate
|
||||||
|
-- - not / nand
|
||||||
|
-- - gt...
|
||||||
|
|
||||||
|
when others =>
|
||||||
|
-- do nothing
|
||||||
|
end case;
|
||||||
|
end process;
|
||||||
|
|
||||||
|
end behavior;
|
197
cpu/cpu.vhdl
Normal file
197
cpu/cpu.vhdl
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.numeric_std.all;
|
||||||
|
|
||||||
|
entity cpu is
|
||||||
|
port(
|
||||||
|
clk: in std_logic;
|
||||||
|
rst: in std_logic;
|
||||||
|
|
||||||
|
code_data: in std_logic_vector(15 downto 0);
|
||||||
|
code_addr: out std_logic_vector(15 downto 0);
|
||||||
|
|
||||||
|
mem_in: in std_logic_vector(15 downto 0);
|
||||||
|
mem_out: out std_logic_vector(15 downto 0);
|
||||||
|
mem_addr: out std_logic_vector(15 downto 0);
|
||||||
|
mem_write: out std_logic;
|
||||||
|
mem_read: out std_logic
|
||||||
|
);
|
||||||
|
end entity cpu;
|
||||||
|
|
||||||
|
architecture behavior of cpu is
|
||||||
|
component alu is
|
||||||
|
port(
|
||||||
|
a: in std_logic_vector(15 downto 0);
|
||||||
|
b: in std_logic_vector(15 downto 0);
|
||||||
|
sel: in std_logic_vector(3 downto 0);
|
||||||
|
|
||||||
|
flag: out std_logic;
|
||||||
|
q: out std_logic_vector(15 downto 0)
|
||||||
|
);
|
||||||
|
end component;
|
||||||
|
|
||||||
|
component reg is
|
||||||
|
port(
|
||||||
|
clk : in std_logic;
|
||||||
|
rst : in std_logic;
|
||||||
|
|
||||||
|
d : in std_logic_vector(15 downto 0);
|
||||||
|
q : out std_logic_vector(15 downto 0)
|
||||||
|
);
|
||||||
|
end component;
|
||||||
|
|
||||||
|
signal alu_a: std_logic_vector(15 downto 0);
|
||||||
|
signal alu_b: std_logic_vector(15 downto 0);
|
||||||
|
signal alu_q: std_logic_vector(15 downto 0);
|
||||||
|
signal alu_sel: std_logic_vector(3 downto 0);
|
||||||
|
signal alu_flag: std_logic;
|
||||||
|
|
||||||
|
signal load_reg_next, load_reg: std_logic_vector(15 downto 0);
|
||||||
|
signal load_addr_next, load_addr: std_logic_vector(15 downto 0);
|
||||||
|
|
||||||
|
type regbank is array(0 to 15) of std_logic_vector(15 downto 0);
|
||||||
|
signal reg_d: regbank;
|
||||||
|
signal reg_q: regbank;
|
||||||
|
|
||||||
|
type cpu_state_t is (RUN, LOAD, BRANCH);
|
||||||
|
signal cpu_state, cpu_state_next: cpu_state_t;
|
||||||
|
begin
|
||||||
|
cpu_alu: alu port map(a => alu_a, b => alu_b, sel => alu_sel, flag => alu_flag, q => alu_q);
|
||||||
|
|
||||||
|
load_reg_r: reg port map(clk => clk, rst => rst, d => load_reg_next, q => load_reg);
|
||||||
|
load_addr_r: reg port map(clk => clk, rst => rst, d => load_addr_next, q => load_addr);
|
||||||
|
|
||||||
|
allregs:
|
||||||
|
for i in 0 to 15 generate
|
||||||
|
regx: reg port map(clk => clk, rst => rst, d => reg_d(i), q => reg_q(i));
|
||||||
|
end generate allregs;
|
||||||
|
|
||||||
|
process(clk, rst)
|
||||||
|
begin
|
||||||
|
if rst = '1' then
|
||||||
|
cpu_state <= BRANCH; -- wait a cycle at first
|
||||||
|
elsif rising_edge(clk) then
|
||||||
|
cpu_state <= cpu_state_next;
|
||||||
|
end if;
|
||||||
|
end process;
|
||||||
|
|
||||||
|
code_addr <= reg_q(14);
|
||||||
|
|
||||||
|
process(code_data, reg_q, mem_in, alu_q, alu_flag, cpu_state, load_addr, load_reg) is
|
||||||
|
variable inst: std_logic_vector(15 downto 0);
|
||||||
|
variable regn_0: natural;
|
||||||
|
variable regn_1: natural;
|
||||||
|
variable regn_2: natural;
|
||||||
|
variable do_alu: std_logic;
|
||||||
|
begin
|
||||||
|
mem_write <= '0';
|
||||||
|
mem_read <= '0';
|
||||||
|
mem_addr <= x"0000";
|
||||||
|
mem_out <= x"0000";
|
||||||
|
|
||||||
|
alu_sel <= "0000";
|
||||||
|
alu_a <= x"0000";
|
||||||
|
alu_b <= x"0000";
|
||||||
|
|
||||||
|
do_alu := '0';
|
||||||
|
|
||||||
|
for i in 0 to 15 loop
|
||||||
|
reg_d(i) <= reg_q(i);
|
||||||
|
end loop;
|
||||||
|
|
||||||
|
cpu_state_next <= RUN;
|
||||||
|
|
||||||
|
load_reg_next <= load_reg;
|
||||||
|
load_addr_next <= load_addr;
|
||||||
|
|
||||||
|
case cpu_state is
|
||||||
|
when RUN =>
|
||||||
|
reg_d(14) <= std_logic_vector(unsigned(reg_q(14)) + 2);
|
||||||
|
inst := code_data;
|
||||||
|
when LOAD =>
|
||||||
|
inst := x"0000"; -- NOP
|
||||||
|
mem_addr <= load_addr; -- maintain this until we're done reading
|
||||||
|
if load_reg(3 downto 0) = x"e" then
|
||||||
|
cpu_state_next <= BRANCH;
|
||||||
|
else
|
||||||
|
reg_d(14) <= std_logic_vector(unsigned(reg_q(14)) + 2);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
regn_0 := to_integer(unsigned(load_reg(3 downto 0)));
|
||||||
|
reg_d(regn_0) <= mem_in;
|
||||||
|
when BRANCH =>
|
||||||
|
inst := x"0000"; -- NOP
|
||||||
|
reg_d(14) <= std_logic_vector(unsigned(reg_q(14)) + 2);
|
||||||
|
end case;
|
||||||
|
|
||||||
|
regn_0 := to_integer(unsigned(inst(11 downto 8)));
|
||||||
|
regn_1 := to_integer(unsigned(inst(7 downto 4)));
|
||||||
|
regn_2 := to_integer(unsigned(inst(3 downto 0)));
|
||||||
|
|
||||||
|
case inst(15 downto 12) is
|
||||||
|
when "0000" => -- NOP
|
||||||
|
when "0001" => -- LOAD rn, [rm, imm] (imm is signed 4 bits)
|
||||||
|
mem_read <= '1';
|
||||||
|
cpu_state_next <= LOAD;
|
||||||
|
reg_d(14) <= reg_q(14); -- halt the prefetcher
|
||||||
|
|
||||||
|
load_addr_next <= std_logic_vector(signed(reg_q(regn_1)) + signed(inst(3 downto 0) & '0'));
|
||||||
|
mem_addr <= std_logic_vector(signed(reg_q(regn_1)) + signed(inst(3 downto 0) & '0'));
|
||||||
|
load_reg_next(3 downto 0) <= inst(11 downto 8);
|
||||||
|
when "0010" => -- STORE rn, [rm, imm]
|
||||||
|
mem_write <= '1';
|
||||||
|
mem_addr <= std_logic_vector(signed(reg_q(regn_1)) + signed(inst(3 downto 0) & '0'));
|
||||||
|
mem_out <= reg_q(regn_0);
|
||||||
|
|
||||||
|
--- ALU stuff
|
||||||
|
when "0011" => do_alu := '1'; -- ADD rd, rn, rm (rd := rn + rm)
|
||||||
|
when "0100" => do_alu := '1'; -- SUB rd, rn, rm (rd := rn - rm)
|
||||||
|
when "0101" => do_alu := '1'; -- OR rd, rn, rm (rd := rn or rm)
|
||||||
|
when "0110" => do_alu := '1'; -- AND rd, rn, rm (rd := rn and rm)
|
||||||
|
when "0111" => do_alu := '1'; -- NOT rd, rn (rd := not rn)
|
||||||
|
when "1000" => do_alu := '1'; -- XOR rd, rn, rm (rd := rn xor rm)
|
||||||
|
when "1001" => -- SETH rd, imm
|
||||||
|
reg_d(regn_0)(15 downto 8) <= inst(7 downto 0);
|
||||||
|
when "1010" => -- SHR rd, rn, imm (rd := rn >> imm)
|
||||||
|
alu_sel <= inst(15 downto 12);
|
||||||
|
alu_a <= reg_q(regn_1);
|
||||||
|
alu_b <= x"000" & inst(3 downto 0);
|
||||||
|
reg_d(regn_0) <= alu_q;
|
||||||
|
when "1011" => do_alu := '1'; -- MUL rd, rn, rm (rd := rn * rm)
|
||||||
|
|
||||||
|
when "1100" => -- CMP rn, rm (flag := 1 if equal)
|
||||||
|
alu_sel <= "1100";
|
||||||
|
alu_a <= reg_q(regn_0);
|
||||||
|
alu_b <= reg_q(regn_1);
|
||||||
|
reg_d(15)(0) <= alu_flag;
|
||||||
|
|
||||||
|
when "1101" => -- BEQ [rn, imm] (jump to [rn, imm] if flag is set, imm is signed 8 bits)
|
||||||
|
if reg_q(15)(0) = '1' then
|
||||||
|
reg_d(14) <= std_logic_vector(signed(reg_q(regn_0)) + signed(inst(7 downto 0) & '0'));
|
||||||
|
cpu_state_next <= BRANCH;
|
||||||
|
end if;
|
||||||
|
when "1110" => -- SET rd, imm (rd := imm, imm is 8 bit)
|
||||||
|
reg_d(regn_0) <= x"00" & inst(7 downto 0);
|
||||||
|
when "1111" => -- BNEQ [rn, imm]
|
||||||
|
if reg_q(15)(0) = '0' then
|
||||||
|
reg_d(14) <= std_logic_vector(signed(reg_q(regn_0)) + signed(inst(7 downto 0) & '0'));
|
||||||
|
cpu_state_next <= BRANCH;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
when others => -- do nothing
|
||||||
|
end case;
|
||||||
|
|
||||||
|
if do_alu = '1' then
|
||||||
|
-- 1:1 mapping
|
||||||
|
alu_sel <= inst(15 downto 12);
|
||||||
|
alu_a <= reg_q(regn_1);
|
||||||
|
alu_b <= reg_q(regn_2);
|
||||||
|
reg_d(regn_0) <= alu_q;
|
||||||
|
reg_d(15)(0) <= alu_flag;
|
||||||
|
if inst(11 downto 8) = x"e" then
|
||||||
|
cpu_state_next <= BRANCH;
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
end process;
|
||||||
|
|
||||||
|
end behavior;
|
123
cpu/cpu_test.vhdl
Normal file
123
cpu/cpu_test.vhdl
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.numeric_std.all;
|
||||||
|
use std.textio.all;
|
||||||
|
|
||||||
|
entity cpu_test is
|
||||||
|
end cpu_test;
|
||||||
|
|
||||||
|
architecture rtl of cpu_test is
|
||||||
|
|
||||||
|
component cpu is port(
|
||||||
|
clk: in std_logic;
|
||||||
|
rst: in std_logic;
|
||||||
|
|
||||||
|
code_data: in std_logic_vector(15 downto 0);
|
||||||
|
code_addr: out std_logic_vector(15 downto 0);
|
||||||
|
|
||||||
|
mem_in: in std_logic_vector(15 downto 0);
|
||||||
|
mem_out: out std_logic_vector(15 downto 0);
|
||||||
|
mem_addr: out std_logic_vector(15 downto 0);
|
||||||
|
mem_write: out std_logic;
|
||||||
|
mem_read: out std_logic
|
||||||
|
);
|
||||||
|
end component;
|
||||||
|
|
||||||
|
type romtype is array(0 to 11) of std_logic_vector(15 downto 0);
|
||||||
|
signal romdata: romtype := (
|
||||||
|
x"0000", -- NOP
|
||||||
|
x"e02a", -- SET r0, 42
|
||||||
|
x"e125", -- SET r1, 37
|
||||||
|
x"2010", -- STORE r0, [r1]
|
||||||
|
x"1210", -- LOAD r2, [r1]
|
||||||
|
x"3322", -- ADD r3, r2, r2
|
||||||
|
x"2310", -- STORE r3, [r1]
|
||||||
|
x"c020", -- CMP r0, r2
|
||||||
|
x"de01", -- BEQ pc, 2
|
||||||
|
x"0000", -- NOP
|
||||||
|
x"ee00", -- SET pc, 0
|
||||||
|
x"0000"
|
||||||
|
);
|
||||||
|
|
||||||
|
signal finished, clk, rst: std_logic := '0';
|
||||||
|
signal mem_write, mem_read: std_logic;
|
||||||
|
signal rom_data, rom_addr, mem_in, mem_out, mem_addr: std_logic_vector(15 downto 0);
|
||||||
|
|
||||||
|
begin
|
||||||
|
dut: cpu port map(clk => clk, rst => rst,
|
||||||
|
code_data => rom_data, code_addr => rom_addr,
|
||||||
|
mem_in => mem_in, mem_out => mem_out, mem_addr => mem_addr,
|
||||||
|
mem_write => mem_write, mem_read => mem_read);
|
||||||
|
|
||||||
|
-- clock
|
||||||
|
process
|
||||||
|
begin
|
||||||
|
if finished = '0' then
|
||||||
|
clk <= not clk;
|
||||||
|
wait for 5 ns;
|
||||||
|
else
|
||||||
|
clk <= '0';
|
||||||
|
wait;
|
||||||
|
end if;
|
||||||
|
end process;
|
||||||
|
|
||||||
|
-- rom
|
||||||
|
process(clk) is
|
||||||
|
variable code_index: natural;
|
||||||
|
variable data_index: natural;
|
||||||
|
constant alignment: positive := 16 / 8;
|
||||||
|
begin
|
||||||
|
if rising_edge(clk) then
|
||||||
|
code_index := to_integer(unsigned(rom_addr)) / alignment;
|
||||||
|
rom_data <= romdata(code_index);
|
||||||
|
end if;
|
||||||
|
end process;
|
||||||
|
|
||||||
|
process(rom_addr)
|
||||||
|
constant alignment: positive := 16 / 8;
|
||||||
|
variable index: natural;
|
||||||
|
begin
|
||||||
|
end process;
|
||||||
|
|
||||||
|
process
|
||||||
|
begin
|
||||||
|
rst <= '1';
|
||||||
|
|
||||||
|
wait for 1 ns;
|
||||||
|
assert(rom_addr=x"0000") report "Fail rst" severity error;
|
||||||
|
|
||||||
|
rst <= '0';
|
||||||
|
|
||||||
|
wait for 10 ns;
|
||||||
|
assert(rom_addr=x"0002") report "Fail PC advance @00" severity error;
|
||||||
|
|
||||||
|
wait for 30 ns;
|
||||||
|
assert(rom_addr=x"0008") report "Fail PC @06" severity error;
|
||||||
|
assert(mem_write='1') report "Fail set mem_write to 1" severity error;
|
||||||
|
assert(mem_addr=x"0025") report "Fail set mem_addr to 0x25" severity error;
|
||||||
|
assert(mem_out=x"002a") report "Fail set mem_out to 42" severity error;
|
||||||
|
|
||||||
|
wait for 10 ns;
|
||||||
|
assert(rom_addr=x"000a") report "Fail PC @08" severity error;
|
||||||
|
assert(mem_write='0') report "Fail set mem_write to 0" severity error;
|
||||||
|
assert(mem_read='1') report "Fail set mem_read to 1" severity error;
|
||||||
|
assert(mem_addr=x"0025") report "Fail set mem_addr to 0x25" severity error;
|
||||||
|
mem_in <= x"002a";
|
||||||
|
|
||||||
|
wait for 30 ns;
|
||||||
|
assert(rom_addr=x"000e") report "Fail PC @0c" severity error;
|
||||||
|
assert(mem_write='1') report "Fail set mem_write to 1" severity error;
|
||||||
|
assert(mem_addr=x"0025") report "Fail set mem_addr to 0x25" severity error;
|
||||||
|
assert(mem_out=x"0054") report "Fail set mem_out to 84" severity error;
|
||||||
|
|
||||||
|
wait for 40 ns;
|
||||||
|
assert(rom_addr=x"0016") report "Fail to branch" severity error;
|
||||||
|
|
||||||
|
wait for 10 ns;
|
||||||
|
assert(rom_addr=x"0000") report "Fail to jump" severity error;
|
||||||
|
|
||||||
|
assert false report "Test done." severity note;
|
||||||
|
finished <= '1';
|
||||||
|
wait;
|
||||||
|
end process;
|
||||||
|
end rtl;
|
18
cpu/makefile
Normal file
18
cpu/makefile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
all: sim
|
||||||
|
sim: test.ghw
|
||||||
|
|
||||||
|
sim_sources = cpu_test.vhdl
|
||||||
|
sources = cpu.vhdl alu.vhdl reg.vhdl
|
||||||
|
test_entity = cpu_test
|
||||||
|
|
||||||
|
|
||||||
|
test.ghw: work-obj93.cf
|
||||||
|
ghdl -r $(test_entity) --wave=$@
|
||||||
|
|
||||||
|
|
||||||
|
work-obj93.cf: $(sim_sources) $(sources)
|
||||||
|
ghdl -a $^
|
||||||
|
|
||||||
|
PHONY: sim
|
||||||
|
|
||||||
|
.PRECIOUS: test.ghw
|
55
cpu/reg.vhdl
Normal file
55
cpu/reg.vhdl
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
----------------------------------------------------------------------------------
|
||||||
|
-- Company:
|
||||||
|
-- Engineer:
|
||||||
|
--
|
||||||
|
-- Create Date: 02/13/2021 01:13:18 PM
|
||||||
|
-- Design Name:
|
||||||
|
-- Module Name: register - Behavioral
|
||||||
|
-- Project Name:
|
||||||
|
-- Target Devices:
|
||||||
|
-- Tool Versions:
|
||||||
|
-- Description:
|
||||||
|
--
|
||||||
|
-- Dependencies:
|
||||||
|
--
|
||||||
|
-- Revision:
|
||||||
|
-- Revision 0.01 - File Created
|
||||||
|
-- Additional Comments:
|
||||||
|
--
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
library IEEE;
|
||||||
|
use IEEE.STD_LOGIC_1164.ALL;
|
||||||
|
|
||||||
|
-- Uncomment the following library declaration if using
|
||||||
|
-- arithmetic functions with Signed or Unsigned values
|
||||||
|
--use IEEE.NUMERIC_STD.ALL;
|
||||||
|
|
||||||
|
-- Uncomment the following library declaration if instantiating
|
||||||
|
-- any Xilinx leaf cells in this code.
|
||||||
|
--library UNISIM;
|
||||||
|
--use UNISIM.VComponents.all;
|
||||||
|
|
||||||
|
entity reg is
|
||||||
|
Port ( d : in STD_LOGIC_VECTOR (15 downto 0);
|
||||||
|
q : out STD_LOGIC_VECTOR (15 downto 0);
|
||||||
|
rst : in STD_LOGIC;
|
||||||
|
clk : in STD_LOGIC);
|
||||||
|
end reg;
|
||||||
|
|
||||||
|
architecture Behavioral of reg is
|
||||||
|
begin
|
||||||
|
|
||||||
|
process(clk, rst) is
|
||||||
|
begin
|
||||||
|
if (rst = '1') then
|
||||||
|
q <= x"0000";
|
||||||
|
else
|
||||||
|
if rising_edge(clk) then
|
||||||
|
q <= d;
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
end process;
|
||||||
|
|
||||||
|
end Behavioral;
|
Loading…
Reference in New Issue
Block a user