use work.common.all;
entity fetch1 is
- generic(
- RESET_ADDRESS : std_logic_vector(63 downto 0) := (others => '0')
+ generic(
+ RESET_ADDRESS : std_logic_vector(63 downto 0) := (others => '0');
+ ALT_RESET_ADDRESS : std_logic_vector(63 downto 0) := (others => '0')
);
- port(
- clk : in std_ulogic;
- rst : in std_ulogic;
+ port(
+ clk : in std_ulogic;
+ rst : in std_ulogic;
- -- Control inputs:
- stall_in : in std_ulogic;
- flush_in : in std_ulogic;
+ -- Control inputs:
+ stall_in : in std_ulogic;
+ flush_in : in std_ulogic;
+ stop_in : in std_ulogic;
+ alt_reset_in : in std_ulogic;
- -- redirect from execution unit
- e_in : in Execute1ToFetch1Type;
+ -- redirect from execution unit
+ e_in : in Execute1ToFetch1Type;
- -- fetch data out
- f_out : out Fetch1ToFetch2Type
+ -- Request to icache
+ i_out : out Fetch1ToIcacheType
);
end entity fetch1;
architecture behaviour of fetch1 is
- type reg_internal_type is record
- nia_next : std_ulogic_vector(63 downto 0);
- end record;
- signal r_int, rin_int : reg_internal_type;
- signal r, rin : Fetch1ToFetch2Type;
+ type stop_state_t is (RUNNING, STOPPED, RESTARTING);
+ type reg_internal_t is record
+ stop_state: stop_state_t;
+ end record;
+ signal r, r_next : Fetch1ToIcacheType;
+ signal r_int, r_next_int : reg_internal_t;
begin
- regs : process(clk)
- begin
- if rising_edge(clk) then
- r <= rin;
- r_int <= rin_int;
+
+ regs : process(clk)
+ begin
+ if rising_edge(clk) then
+ if r /= r_next then
+ report "fetch1 rst:" & std_ulogic'image(rst) &
+ " IR:" & std_ulogic'image(e_in.virt_mode) &
+ " P:" & std_ulogic'image(e_in.priv_mode) &
+ " R:" & std_ulogic'image(e_in.redirect) &
+ " S:" & std_ulogic'image(stall_in) &
+ " T:" & std_ulogic'image(stop_in) &
+ " nia:" & to_hstring(r_next.nia) &
+ " SM:" & std_ulogic'image(r_next.stop_mark);
+ end if;
+ r <= r_next;
+ r_int <= r_next_int;
+ end if;
+ end process;
+
+ comb : process(all)
+ variable v : Fetch1ToIcacheType;
+ variable v_int : reg_internal_t;
+ variable increment : boolean;
+ begin
+ v := r;
+ v_int := r_int;
+
+ if rst = '1' then
+ if alt_reset_in = '1' then
+ v.nia := ALT_RESET_ADDRESS;
+ else
+ v.nia := RESET_ADDRESS;
+ end if;
+ v.virt_mode := '0';
+ v.priv_mode := '1';
+ v_int.stop_state := RUNNING;
+ elsif e_in.redirect = '1' then
+ v.nia := e_in.redirect_nia;
+ v.virt_mode := e_in.virt_mode;
+ v.priv_mode := e_in.priv_mode;
+ elsif stall_in = '0' then
+
+ -- For debug stop/step to work properly we need a little bit of
+ -- trickery here. If we just stop incrementing and send stop marks
+ -- when stop_in is set, then we'll increment on the cycle it clears
+ -- and end up never executing the instruction we were stopped on.
+ --
+ -- Avoid this along with the opposite issue when stepping (stop is
+ -- cleared for only one cycle) is handled by the state machine below
+ --
+ -- By default, increment addresses
+ increment := true;
+ case v_int.stop_state is
+ when RUNNING =>
+ -- If we are running and stop_in is set, then stop incrementing,
+ -- we are now stopped.
+ if stop_in = '1' then
+ increment := false;
+ v_int.stop_state := STOPPED;
end if;
- end process;
-
- comb : process(all)
- variable v : Fetch1ToFetch2Type;
- variable v_int : reg_internal_type;
- begin
- v := r;
- v_int := r_int;
-
- if stall_in = '0' then
- v.nia := r_int.nia_next;
- v_int.nia_next := std_logic_vector(unsigned(r_int.nia_next) + 4);
+ when STOPPED =>
+ -- When stopped, never increment. If stop is cleared, go to state
+ -- "restarting" but still don't increment that cycle. stop_in is
+ -- now 0 so we'll send the NIA down without a stop mark.
+ increment := false;
+ if stop_in = '0' then
+ v_int.stop_state := RESTARTING;
end if;
-
- if e_in.redirect = '1' then
- v.nia := e_in.redirect_nia;
- v_int.nia_next := std_logic_vector(unsigned(e_in.redirect_nia) + 4);
+ when RESTARTING =>
+ -- We have just sent the NIA down, we can start incrementing again.
+ -- If stop_in is still not set, go back to running normally.
+ -- If stop_in is set again (that was a one-cycle "step"), go
+ -- back to "stopped" state which means we'll stop incrementing
+ -- on the next cycle. This ensures we increment the PC once after
+ -- sending one instruction without a stop mark. Since stop_in is
+ -- now set, the new PC will be sent with a stop mark and thus not
+ -- executed.
+ if stop_in = '0' then
+ v_int.stop_state := RUNNING;
+ else
+ v_int.stop_state := STOPPED;
end if;
+ end case;
- if rst = '1' then
- v.nia := RESET_ADDRESS;
- v_int.nia_next := std_logic_vector(unsigned(RESET_ADDRESS) + 4);
- end if;
+ if increment then
+ v.nia := std_logic_vector(unsigned(v.nia) + 4);
+ end if;
+ end if;
+
+ v.req := not rst;
+ v.stop_mark := stop_in;
+
+ r_next <= v;
+ r_next_int <= v_int;
- -- Update registers
- rin <= v;
- rin_int <= v_int;
+ -- Update outputs to the icache
+ i_out <= r;
- -- Update outputs
- f_out <= r;
- end process;
+ end process;
end architecture behaviour;