diff --git a/vhdl/Makefile b/vhdl/Makefile new file mode 100644 index 0000000..4b4a917 --- /dev/null +++ b/vhdl/Makefile @@ -0,0 +1,9 @@ +.PHONY: sim +sim: risc_v.vhd tb_risc_v.vhd + ghdl -a --std=08 risc_v.vhd tb_risc_v.vhd + ghdl -e --std=08 tb_risc_v + ghdl -r --std=08 tb_risc_v --vcd=tb_risc_v.vcd --wave=tb_risc_v.ghw + +.PHONY: clean +clean: + rm tb_risc_v tb_risc_v.ghw tb_risc_v.vcd work* *.o diff --git a/vhdl/risc_v.vhd b/vhdl/risc_v.vhd new file mode 100644 index 0000000..1b14fc8 --- /dev/null +++ b/vhdl/risc_v.vhd @@ -0,0 +1,192 @@ +library ieee; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; + + +entity risc_v is + port ( + reset_n_i : in std_logic; + clk_i : in std_logic + ); +end entity; + + + +architecture rtl of risc_v is + + type t_slv_array is array (natural range <>) of std_logic_vector; + subtype t_reg_file is t_slv_array(0 to 31)(31 downto 0); + subtype t_imem is t_slv_array(natural range 0 to 8)(31 downto 0); + + -- Test program + constant c_imem : t_imem := ( + -- ADDI, x14, x0, 0 + b"0000_0000_0000_0000_0000_0111_0001_0011", + -- ADDI, x12, x0, 1010 + b"0000_0000_1010_0000_0000_0110_0001_0011", + -- ADDI, x13, x0, 1 + b"0000_0000_0001_0000_0000_0110_1001_0011", + -- LOOP begin + -- ADD, x14, x13, x14 + b"0000_0000_1110_0110_1000_0111_0011_0011", + -- ADDI, x13, x13, 1 + b"0000_0000_0001_0110_1000_0110_1001_0011", + -- BLT, x13, x12, 1111111111000 (branch to LOOP begin if x13 < x12) + b"1111_1110_1100_0110_1100_1100_1110_0011", + -- ADDI, x0, x0, 1010 (Test write ignore to x0) + b"0000_0000_1010_0000_0000_0000_0001_0011", + -- ADDI, x30, x14, 111111010100 + b"1111_1101_0100_0111_0000_1111_0001_0011", + -- BGE, x0, x0, 0 (Infinite loop) + b"0000_0000_0000_0000_0101_0000_0110_0011"); + + signal s_reg_file : t_reg_file; + + signal s_instr : std_logic_vector(31 downto 0); + signal s_imm : std_logic_vector(31 downto 0); + signal s_dec_bits : std_logic_vector(10 downto 0); + signal s_src1_value : std_logic_vector(31 downto 0); + signal s_src2_value : std_logic_vector(31 downto 0); + + signal s_pc : unsigned(31 downto 0); + signal s_next_pc : unsigned(31 downto 0); + signal s_br_tgt_br : unsigned(31 downto 0); + + signal s_result : signed(31 downto 0); + + signal s_taken_br : boolean; + signal s_is_r_instr : boolean; + signal s_is_i_instr : boolean; + signal s_is_s_instr : boolean; + signal s_is_b_instr : boolean; + signal s_is_u_instr : boolean; + signal s_is_j_instr : boolean; + signal s_rd_valid : boolean; + signal s_funct3_valid : boolean; + signal s_rs1_valid : boolean; + signal s_rs2_valid : boolean; + signal s_funct7_valid : boolean; + signal s_imm_valid : boolean; + signal s_is_beq : boolean; + signal s_is_bne : boolean; + signal s_is_blt : boolean; + signal s_is_bge : boolean; + signal s_is_bltu : boolean; + signal s_is_bgeu : boolean; + signal s_is_addi : boolean; + signal s_is_add : boolean; + + alias a_opcode : std_logic_vector(6 downto 0) is s_instr(6 downto 0); + alias a_rd : std_logic_vector(4 downto 0) is s_instr(11 downto 7); + alias a_funct3 : std_logic_vector(2 downto 0) is s_instr(14 downto 12); + alias a_rs1 : std_logic_vector(4 downto 0) is s_instr(19 downto 15); + alias a_rs2 : std_logic_vector(4 downto 0) is s_instr(24 downto 20); + alias a_funct7 : std_logic_vector(6 downto 0) is s_instr(31 downto 25); + +begin + + -- prog counter next state logic + s_next_pc <= 32x"0" when not reset_n_i else + s_br_tgt_br when s_taken_br else + s_pc + 4; + + -- prog counter register + process (clk_i) is + begin + if rising_edge(clk_i) then + s_pc <= s_next_pc; + end if; + end process; + + -- Instruction memory + s_instr <= c_imem(to_integer(s_pc(31 downto 2))); + + -- Decode + -- Decode instruction type + s_is_r_instr <= s_instr(6 downto 2) = "01011" or + s_instr(6 downto 2) = "01100" or + s_instr(6 downto 2) = "01110" or + s_instr(6 downto 2) = "10100"; + s_is_i_instr <= std_match(s_instr(6 downto 2), "0000-") or + std_match(s_instr(6 downto 2), "001-0") or + s_instr(6 downto 2) = "11001"; + s_is_s_instr <= std_match(s_instr(6 downto 2), "0100-"); + s_is_b_instr <= s_instr(6 downto 2) = "11000"; + s_is_u_instr <= std_match(s_instr(6 downto 2), "0-101"); + s_is_j_instr <= s_instr(6 downto 2) = "11011"; + + -- Extract instruction fields + s_imm <= (31 downto 11 => s_instr(31), + 10 downto 0 => s_instr(30 downto 20)) when s_is_i_instr else + (31 downto 11 => s_instr(31), + 10 downto 5 => s_instr(30 downto 25), + 4 downto 0 => s_instr(11 downto 7)) when s_is_s_instr else + (31 downto 12 => s_instr(31), + 11 => s_instr(7), + 10 downto 5 => s_instr(30 downto 25), + 4 downto 1 => s_instr(11 downto 8), + 0 => '0') when s_is_b_instr else + ( 31 => s_instr(31), + 30 downto 12 => s_instr(30 downto 12), + 11 downto 0 => 12x"0") when s_is_u_instr else + (31 downto 20 => s_instr(31), + 19 downto 12 => s_instr(19 downto 12), + 11 => s_instr(20), + 10 downto 1 => s_instr(30 downto 21), + 0 => '0') when s_is_j_instr else + 32x"0"; + + -- Calculate instruction fields valids + s_rd_valid <= s_is_r_instr or s_is_i_instr or s_is_u_instr or s_is_j_instr; + s_funct3_valid <= s_is_r_instr or s_is_i_instr or s_is_s_instr or s_is_b_instr; + s_rs1_valid <= s_funct3_valid; + s_rs2_valid <= s_is_r_instr or s_is_s_instr or s_is_b_instr; + s_funct7_valid <= s_is_r_instr; + s_imm_valid <= not s_is_r_instr; + + -- Instruction code decoding + s_dec_bits <= (a_funct7(5), a_funct3, a_opcode); + s_is_beq <= std_match(s_dec_bits, b"-_000_1100011"); + s_is_bne <= std_match(s_dec_bits, b"-_001_1100011"); + s_is_blt <= std_match(s_dec_bits, b"-_100_1100011"); + s_is_bge <= std_match(s_dec_bits, b"-_101_1100011"); + s_is_bltu <= std_match(s_dec_bits, b"-_110_1100011"); + s_is_bgeu <= std_match(s_dec_bits, b"-_111_1100011"); + s_is_addi <= std_match(s_dec_bits, b"-_000_0010011"); + s_is_add <= s_dec_bits = b"0_000_0110011"; + + -- ALU + s_result <= signed(s_src1_value) + signed(s_imm) when s_is_addi else + signed(s_src1_value) + signed(s_src2_value) when s_is_add else + 32x"0"; + + -- Branch logic + s_taken_br <= s_src1_value = s_src2_value when s_is_beq else + s_src1_value /= s_src2_value when s_is_bne else + signed(s_src1_value) < signed(s_src2_value) when s_is_blt else + signed(s_src1_value) >= signed(s_src2_value) when s_is_bge else + unsigned(s_src1_value) < unsigned(s_src2_value) when s_is_bltu else + unsigned(s_src1_value) >= unsigned(s_src2_value) when s_is_bgeu else + false; + s_br_tgt_br <= s_pc + unsigned(s_imm); + + -- Register file + process (clk_i) is + begin + if rising_edge(clk_i) then + if reset_n_i = '0' then + s_reg_file <= (others => 32x"0"); + else + if s_rd_valid and a_rd /= 5x"0" then + s_reg_file(to_integer(unsigned(a_rd))) <= std_logic_vector(s_result); + end if; + end if; + end if; + end process; + + s_src1_value <= s_reg_file(to_integer(unsigned(a_rs1))) when s_rs1_valid else + (others => '0'); + s_src2_value <= s_reg_file(to_integer(unsigned(a_rs2))) when s_rs2_valid else + (others => '0'); + +end architecture rtl; diff --git a/vhdl/tb_risc_v.vhd b/vhdl/tb_risc_v.vhd new file mode 100644 index 0000000..fed8bfb --- /dev/null +++ b/vhdl/tb_risc_v.vhd @@ -0,0 +1,40 @@ +library ieee; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; + +use std.env.all; + + +entity tb_risc_v is +end entity tb_risc_v; + + +architecture sim of tb_risc_v is + + + signal s_clk : std_logic := '1'; + signal s_reset_n : std_logic := '0'; + + +begin + + + s_clk <= not s_clk after 5 ns; + s_reset_n <= '1' after 20 ns; + + + DUT : entity work.risc_v + port map ( + reset_n_i => s_reset_n, + clk_i => s_clk + ); + + + process is + begin + wait for 380 ns; + stop(0); + end process; + + +end architecture sim;