------------------------------------------------------------------------------- -- Project ELE8304 : Circuits intégrés à très grande échelle ------------------------------------------------------------------------------- -- File riscv_decode.vhd -- Authors Titouan Luard <luardtitouan@gmail.com> -- Yann Roberge <yann.roberge@polymtl.ca> -- Lab ELE8304-9 -- Date 2021-11-28 ------------------------------------------------------------------------------- -- Brief RISC-V Decode pipeline stage -- Breaks down instructions into their specific components, -- Reads from & Writes into the register file, -- Generate outputs needed for the Execute stage ------------------------------------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.math_real.all; library work; use work.riscv_pkg.all; entity riscv_decode is port ( -- Entrées provenant de l'étage FETCH i_fetch_reg : in fetch_reg; -- Entrées provenant de l'étage WRITE-BACK i_wr_addr : in std_logic_vector(REG_WIDTH-1 downto 0); i_wr_data : in std_logic_vector(XLEN-1 downto 0); i_we : in std_logic; -- Entrée provenant de l'étage EXECUTE i_flush : in std_logic; -- Entrées externes au pipeline i_rstn : in std_logic; i_clk : in std_logic; -- Sorties rs1_data : out std_logic_vector(XLEN-1 downto 0); rs2_data : out std_logic_vector(XLEN-1 downto 0); o_decode_reg : out decode_reg); end entity riscv_decode; architecture beh of riscv_decode is -- Signaux internes Predecode signal opcode : std_logic_vector(OPCODE_WIDTH-1 downto 0); signal funct3 : std_logic_vector(2 downto 0); signal funct7 : std_logic_vector(6 downto 0); signal rs1_addr : std_logic_vector(REG_WIDTH-1 downto 0); signal rs2_addr : std_logic_vector(REG_WIDTH-1 downto 0); signal rd : std_logic_vector(REG_WIDTH-1 downto 0); -- opcode funct3 funct7 rs1 rs2 -- jump -- branch -- imm -- Signaux internes Decode -- alu_opcode -- shamt -- arith -- sign signal jump : std_logic; signal branch : std_logic; signal alu_opcode : std_logic_vector(ALUOP_WIDTH-1 downto 0); signal arith : std_logic; signal shamt : std_logic_vector(SHAMT_WIDTH-1 downto 0); signal sign : std_logic; signal imm : std_logic_vector(XLEN-1 downto 0); alias instruction is i_fetch_reg.instruction; begin -- Predecode -- Décompose les signaux en fonction de leurs types (opcodes) opcode <= i_fetch_reg.instruction(OPCODE_WIDTH-1 downto 0); predecode: process (opcode) is begin case opcode is -- R-TYPE -- FIXME: Le code répété when OPCODE_JALR => funct7 <= instruction(31 downto 25); rs2_addr <= instruction(24 downto 20); rs1_addr <= instruction(19 downto 15); funct3 <= instruction(14 downto 12); rd <= instruction(11 downto 7); when OPCODE_ALU_R_TYPE => funct7 <= instruction(31 downto 25); rs2_addr <= instruction(24 downto 20); rs1_addr <= instruction(19 downto 15); funct3 <= instruction(14 downto 12); rd <= instruction(11 downto 7); -- I-TYPE -- FIXME: Le code répété when OPCODE_LW => funct7 <= (others => '0'); rs2_addr <= (others => '0'); rs1_addr <= instruction(19 downto 15); funct3 <= instruction(14 downto 12); rd <= instruction(11 downto 7); when OPCODE_ALU_I_TYPE => funct7 <= (others => '0'); rs2_addr <= (others => '0'); rs1_addr <= instruction(19 downto 15); funct3 <= instruction(14 downto 12); rd <= instruction(11 downto 7); -- S-TYPE when OPCODE_SW => funct7 <= (others => '0'); rs2_addr <= instruction(24 downto 20); rs1_addr <= instruction(19 downto 15); funct3 <= instruction(14 downto 12); rd <= (others => '0'); -- B-TYPE when OPCODE_BEQ => funct7 <= (others => '0'); rs2_addr <= instruction(24 downto 20); rs1_addr <= instruction(19 downto 15); funct3 <= instruction(14 downto 12); rd <= (others => '0'); -- U-TYPE when OPCODE_LUI => funct7 <= (others => '0'); rs2_addr <= (others => '0'); rs1_addr <= (others => '0'); funct3 <= (others => '0'); rd <= instruction(11 downto 7); -- J-TYPE when OPCODE_JAL => funct7 <= (others => '0'); rs2_addr <= (others => '0'); rs1_addr <= (others => '0'); funct3 <= (others => '0'); rd <= instruction(11 downto 7); -- Unsupported instructions when others => funct7 <= (others => '0'); rs2_addr <= (others => '0'); rs1_addr <= (others => '0'); funct3 <= (others => '0'); rd <= (others => '0'); end case; end process predecode; -- Gestion des valeurs immédiates immediate : process (opcode) begin case opcode is -- I-TYPE -- FIXME: Le code répété when OPCODE_LW => imm(31 downto 11) <= (others => instruction(31)); imm(10 downto 5) <= instruction(30 downto 25); imm(4 downto 1) <= instruction(24 downto 21); imm(0) <= instruction(20); when OPCODE_ALU_I_TYPE => imm(31 downto 11) <= (others => instruction(31)); imm(10 downto 5) <= instruction(30 downto 25); imm(4 downto 1) <= instruction(24 downto 21); imm(0) <= instruction(20); -- S-TYPE when OPCODE_SW => imm(31 downto 11) <= (others => instruction(31)); imm(10 downto 5) <= instruction(30 downto 25); imm(4 downto 1) <= instruction(11 downto 8); imm(0) <= instruction(7); -- B-TYPE when OPCODE_BEQ => imm(31 downto 12) <= (others => instruction(31)); imm(11) <= instruction(7); imm(10 downto 5) <= instruction(30 downto 25); imm(4 downto 1) <= instruction(11 downto 8); imm(0) <= '0'; -- U-TYPE when OPCODE_LUI => -- FIXME: Pourquoi pas avoir compacté les 2 lignes suivantes -- à la figure 3 de l'énoncé? imm(31) <= instruction(31); imm(30 downto 20) <= instruction(30 downto 20); imm(19 downto 12) <= instruction(19 downto 12); imm(11 downto 0) <= (others => '0'); -- J-TYPE when OPCODE_JAL => imm(31 downto 20) <= (others => instruction(31)); imm(19 downto 12) <= instruction(19 downto 12); imm(11) <= instruction(20); imm(10 downto 5) <= instruction(30 downto 25); imm(4 downto 1) <= instruction(24 downto 21); imm(0) <= '0'; -- R-TYPE -- Unsupported instructions when others => imm <= (others => '0'); end case; end process immediate; -- Instanciation du Register File register_file: entity work.riscv_rf port map ( i_clk => i_clk, i_rstn => i_rstn, i_we => i_we, i_addr_ra => rs1_addr, o_data_ra => rs1_data, i_addr_rb => rs2_addr, o_data_rb => rs2_data, i_addr_w => i_wr_addr, i_data_w => i_wr_data ); -- Decode --signal jump : std_logic; --signal branch : std_logic; decode_jump_decode: process (funct7, funct3, opcode) is begin case opcode is -- R-TYPE when OPCODE_JALR => jump <= '1'; branch <= '0'; alu_opcode <= (others => '0'); arith <= '0'; shamt <= (others => '0'); sign <= SIGN_UNSIGNED; when OPCODE_ALU_R_TYPE => jump <= '0'; branch <= '0'; case funct3 is when FUNCT3_ADD => -- FUNCT7 distingue la soustraction de l'addition -- toutes les opérations sont signées case funct7 is => when FUNCT7_SUB_ARITH_SH => arith <= ARITH_SUBTRACT; when others => arith <= ARITH_ADD; end case; shamt <= (others => '0'); alu_opcode <= ALUOP_ADD; sign <= SIGN_SIGNED; when FUNCT3_SL => jump <= '0'; branch <= '0'; alu_opcode <= ALUOP_SH_LEFT; arith <= '0'; -- le shift gauche est toujours logique shamt <= (others => '0'); sign <= SIGN_UNSIGNED; when FUNCT3_SLT => when FUNCT3_SLTU => when FUNCT3_XOR => when FUNCT3_SR => when FUNCT3_OR => when FUNCT3_AND => when others => -- on n'arrive jamais ici parce que toutes les -- possibilités de FUNCT3 sont couvertes par le code plus haut alu_opcode <= (others => '0'); arith <= '0'; shamt <= (others => '0'); sign <= SIGN_UNSIGNED; end case; alu_opcode <= (others => '0'); sign <= SIGN_UNSIGNED; -- I-TYPE when OPCODE_LW => jump <= '0'; branch <= '0'; alu_opcode <= (others => '0'); sign <= SIGN_UNSIGNED; when OPCODE_ALU_I_TYPE => -- S-TYPE when OPCODE_SW => jump <= '0'; branch <= '0'; alu_opcode <= (others => '0'); sign <= SIGN_UNSIGNED; -- B-TYPE when OPCODE_BEQ => jump <= '0'; branch <= '1'; alu_opcode <= (others => '0'); sign <= SIGN_UNSIGNED; -- U-TYPE when OPCODE_LUI => jump <= '0'; branch <= '0'; alu_opcode <= (others => '0'); sign <= SIGN_UNSIGNED; -- J-TYPE when OPCODE_JAL => jump <= '1'; branch <= '0'; alu_opcode <= (others => '0'); sign <= SIGN_UNSIGNED; -- R-TYPE -- Unsupported instructions when others => end case; end process decode_jump_decode; --signal alu_opcode : std_logic_vector(ALUOP_WIDTH-1 downto 0); --signal arith : std_logic; --signal shamt : std_logic_vector(SHAMT_WIDTH-1 downto 0); --signal sign : std_logic; decode_calc: process (funct7, funct3, opcode) is begin case opcode is -- R-TYPE when OPCODE_JALR => jump <= '1'; branch <= '0'; alu_opcode <= (others => '0'); arith <= '0'; shamt <= (others => '0'); sign <= SIGN_UNSIGNED; when OPCODE_ALU_R_TYPE => jump <= '0'; branch <= '0'; case funct3 is when FUNCT3_ADD => -- FUNCT7 distingue la soustraction de l'addition -- toutes les opérations sont signées case funct7 is => when FUNCT7_SUB_ARITH_SH => arith <= ARITH_SUBTRACT; when others => arith <= ARITH_ADD; end case; shamt <= (others => '0'); alu_opcode <= ALUOP_ADD; sign <= SIGN_SIGNED; when FUNCT3_SL => jump <= '0'; branch <= '0'; alu_opcode <= ALUOP_SH_LEFT; arith <= '0'; -- le shift gauche est toujours logique shamt <= (others => '0'); sign <= SIGN_UNSIGNED; when FUNCT3_SLT => when FUNCT3_SLTU => when FUNCT3_XOR => when FUNCT3_SR => when FUNCT3_OR => when FUNCT3_AND => when others => -- on n'arrive jamais ici parce que toutes les -- possibilités de FUNCT3 sont couvertes par le code plus haut alu_opcode <= (others => '0'); arith <= '0'; shamt <= (others => '0'); sign <= SIGN_UNSIGNED; end case; alu_opcode <= (others => '0'); sign <= SIGN_UNSIGNED; -- I-TYPE when OPCODE_LW => jump <= '0'; branch <= '0'; alu_opcode <= (others => '0'); sign <= SIGN_UNSIGNED; when OPCODE_ALU_I_TYPE => -- S-TYPE when OPCODE_SW => jump <= '0'; branch <= '0'; alu_opcode <= (others => '0'); sign <= SIGN_UNSIGNED; -- B-TYPE when OPCODE_BEQ => jump <= '0'; branch <= '1'; alu_opcode <= (others => '0'); sign <= SIGN_UNSIGNED; -- U-TYPE when OPCODE_LUI => jump <= '0'; branch <= '0'; alu_opcode <= (others => '0'); sign <= SIGN_UNSIGNED; -- J-TYPE when OPCODE_JAL => jump <= '1'; branch <= '0'; alu_opcode <= (others => '0'); sign <= SIGN_UNSIGNED; -- R-TYPE -- Unsupported instructions when others => end case; end process decode_jump_decode; -- Écrire le registre ID/EX -- DEBUG: o_decode_reg.shamt <= (others => '1'); o_decode_reg.arith <= '1'; o_decode_reg.sign <= '1'; o_decode_reg.jump <= '1'; o_decode_reg.branch <= '1'; o_decode_reg.pc <= (others => '1'); o_decode_reg.imm <= (others => '1'); -- Références: -- Écrire le registre pointé -- write_reg_file: -- process (i_clk) is -- begin -- -- if rising_edge(i_clk) then -- if i_rstn = '1' then -- if reg_file_wr_en = '1' then -- reg_file(to_integer(unsigned(i_addr_w))) <= i_data_w; -- else -- reg_file <= reg_file; -- end if; -- else -- reg_file <= (others => (others => '0')); -- end if; -- end if; -- end process write_reg_file; -- Transférer la valeur écrite vers un tampon -- transfer_write_buffers: -- process (i_clk) is -- begin -- if rising_edge(i_clk) then -- if i_rstn = '1' then -- write_buffer <= i_data_w; -- else -- write_buffer <= (others => '0'); -- end if; -- end if; -- end process transfer_write_buffers; end architecture beh;