------------------------------------------------------------------------------- -- Project ELE8304 : Circuits intégrés à très grande échelle ------------------------------------------------------------------------------- -- File riscv_execute.vhd -- Authors Titouan Luard <luardtitouan@gmail.com> -- Yann Roberge <yann.roberge@polymtl.ca> -- Lab GRM - Polytechnique Montreal -- Date 2021-12-04 ------------------------------------------------------------------------------- -- Brief Execute stage of the pipeline ------------------------------------------------------------------------------- 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_execute is port ( -- Entrées de l'étage Decode i_decode_reg : in decode_reg; i_rs1_data : in std_logic_vector(XLEN-1 downto 0); i_rs2_data : in std_logic_vector(XLEN-1 downto 0); -- Entrées de l'étage Write Back (forwarding) i_write_back_reg : in write_back_reg; -- Sorties o_pc_transfer : out std_logic; o_pc_target : out std_logic_vector(XLEN-1 downto 0); o_stall : out std_logic; o_flush : out std_logic; -- Entrées externes au pipeline i_rstn : in std_logic; i_clk : in std_logic; -- Sorties registrées o_execute_reg : out execute_reg); end entity riscv_execute; architecture beh of riscv_execute is signal rs1_data : std_logic_vector(XLEN-1 downto 0); signal rs2_data : std_logic_vector(XLEN-1 downto 0); signal alu_src1 : std_logic_vector(XLEN-1 downto 0); signal alu_src2 : std_logic_vector(XLEN-1 downto 0); signal shamt : std_logic_vector(SHAMT_WIDTH-1 downto 0); signal add_to_imm : std_logic_vector(XLEN-1 downto 0); -- Sorties signal alu_result : std_logic_vector(XLEN-1 downto 0); --signal store_data : std_logic; alias opcode is i_decode_reg.opcode; alias jump is i_decode_reg.jump; alias branch is i_decode_reg.branch; alias imm is i_decode_reg.imm; alias pc is i_decode_reg.pc; constant ALL_ZEROES : std_logic_vector(XLEN-1 downto 0) := (others => '0'); signal dummy : std_logic; -- doesn't do anything begin -- PC transfer -- transfert si jump ou si la condition du BEQ est vraie -- flush la pipeline dans ce même cas -- toute la gestion de conflit se fait ici -- ie. branch prediction pc_transfer: process (all) is begin if (jump = '1') or ((branch = '1') and (alu_result = ALL_ZEROES)) then o_flush <= '1'; o_pc_transfer <= '1'; elsif opcode = OPCODE_LW then o_flush <= '1'; o_pc_transfer <= '0'; else o_flush <= '0'; o_pc_transfer <= '0'; end if; end process pc_transfer; -- Résoudre le conflit de données en cas de LW o_stall <= '1' when opcode = OPCODE_LW else '0'; -- Résolution des conflits de données par forwarding -- Si un des registres lues au RF à l'étage décode a été modifié entre-temps -- par les étages Execute et Memory, alors on utilise la donnée écrite par -- Execute ou la donnée chargée par Memory à la place de la donnée lue au RF resolve_data_conflicts: process (all) is begin -- FIXME: les lignes trop longues if i_decode_reg.rs1_addr = o_execute_reg.rd_addr then rs1_data <= o_execute_reg.alu_result; elsif (i_decode_reg.rs1_addr = i_write_back_reg.wr_addr) and i_write_back_reg.we = '1' then rs1_data <= i_write_back_reg.wr_data; else rs1_data <= i_rs1_data; end if; if i_decode_reg.rs2_addr = o_execute_reg.rd_addr then rs2_data <= o_execute_reg.alu_result; elsif (i_decode_reg.rs2_addr = i_write_back_reg.wr_addr) and i_write_back_reg.we = '1' then rs2_data <= i_write_back_reg.wr_data; else rs2_data <= i_rs2_data; end if; end process resolve_data_conflicts; -- Résolution des opérations arithmétiques et logiques -- Shamt: encoded in IMM for I-TYPE, else encoded in RS2 pick_shamt: process (all) is begin case opcode is when OPCODE_ALU_R_TYPE => shamt <= rs2_data(SHAMT_WIDTH-1 downto 0); when OPCODE_ALU_I_TYPE => shamt <= imm(SHAMT_WIDTH-1 downto 0); when others => shamt <= (others => '0'); end case; end process pick_shamt; alu_feeder_src1: process (all) is begin -- Select src1 case opcode is when OPCODE_JAL | OPCODE_JALR => alu_src1 <= pc; when others => alu_src1 <= rs1_data; end case; end process alu_feeder_src1; alu_feeder_src2: process (all) is begin -- Select src2 case opcode is when OPCODE_ALU_R_TYPE | OPCODE_BEQ => alu_src2 <= i_rs2_data; when OPCODE_ALU_I_TYPE | OPCODE_LUI | OPCODE_SW => alu_src2 <= imm; when OPCODE_JAL | OPCODE_JALR => alu_src2 <= std_logic_vector(to_unsigned(4, alu_src2'length)); when others => alu_src2 <= (others => '0'); end case; end process alu_feeder_src2; -- Instanciate alu alu: entity work.riscv_alu port map ( i_arith => i_decode_reg.arith, i_sign => i_decode_reg.sign, i_opcode => i_decode_reg.alu_opcode, i_shamt => shamt, i_src1 => alu_src1, i_src2 => alu_src2, o_res => alu_result ); -- JAL & BEQ: IMM + PC -- JALR: IMM + BASE add_feeder: process (all) is begin case opcode is when OPCODE_JALR => add_to_imm <= rs1_data; -- JAL & BEQ when others => add_to_imm <= pc; end case; end process add_feeder; -- Ajustement du PC en cas de saut ou branchement -- Instanciate adder dut: entity work.riscv_adder generic map ( N => XLEN ) port map ( i_a => imm, i_b => add_to_imm, i_sign => SIGN_UNSIGNED, i_sub => ARITH_ADD, o_sum(XLEN) => dummy, o_sum(XLEN-1 downto 0) => o_pc_target ); -- Écriture du registre execute -- same as decode register write thingy without flush input write_reg_ex_me: process (i_clk) is begin if rising_edge(i_clk) then if i_rstn = '1' then o_execute_reg.opcode <= opcode; o_execute_reg.alu_result <= alu_result; o_execute_reg.store_data <= rs2_data; o_execute_reg.rd_addr <= i_decode_reg.rd_addr; else o_execute_reg.opcode <= (others => '0'); o_execute_reg.alu_result <= (others => '0'); o_execute_reg.store_data <= (others => '0'); o_execute_reg.rd_addr <= (others => '0'); end if; end if; end process write_reg_ex_me; -- DEBUG --o_pc_target <= (others => '1'); --o_execute_reg.alu_result <= (others => '1'); --o_execute_reg.store_data <= '1'; end architecture beh;