mirror of
https://github.com/antonblanchard/microwatt.git
synced 2026-01-13 15:18:09 +00:00
The goal is to have the icache fit in BRAM by latching the output into a register. In order to avoid timing issues , we need to give the BRAM a full cycle on reads, and thus we souce the BRAM address directly from fetch1 latched NIA. (Note: This will be problematic if/when we want to hash the address, we'll probably be better off having fetch1 latch a fully hashed address along with the normal one, so the icache can use the former to address the BRAM and pass the latter along) One difficulty is that we cannot really stall the icache without adding more combo logic that would break the "one full cycle" BRAM model. This means that on stalls from decode, by the time we stall fetch1, it has already gone to the next address, which the icache is already latching. We work around this by having a "stash" buffer in fetch2 that will stash away the icache output on a stall, and override the output of the icache with the content of the stash buffer when unstalling. This requires a rewrite of the stop/step debug logic as well. We now do most of the hard work in fetch1 which makes more sense. Note: Vivado is still not inferring an built-in output register for the BRAMs. I don't want to add another cycle... I don't fully understand why it wouldn't be able to treat current_row as such but clearly it won't. At least the timing seems good enough now for 100Mhz, possibly more. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
153 lines
4.2 KiB
VHDL
153 lines
4.2 KiB
VHDL
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
library work;
|
|
use work.common.all;
|
|
|
|
entity core_debug is
|
|
port (
|
|
clk : in std_logic;
|
|
rst : in std_logic;
|
|
|
|
dmi_addr : in std_ulogic_vector(3 downto 0);
|
|
dmi_din : in std_ulogic_vector(63 downto 0);
|
|
dmi_dout : out std_ulogic_vector(63 downto 0);
|
|
dmi_req : in std_ulogic;
|
|
dmi_wr : in std_ulogic;
|
|
dmi_ack : out std_ulogic;
|
|
|
|
-- Debug actions
|
|
core_stop : out std_ulogic;
|
|
core_rst : out std_ulogic;
|
|
icache_rst : out std_ulogic;
|
|
|
|
-- Core status inputs
|
|
terminate : in std_ulogic;
|
|
core_stopped : in std_ulogic;
|
|
nia : in std_ulogic_vector(63 downto 0);
|
|
|
|
-- Misc
|
|
terminated_out : out std_ulogic
|
|
);
|
|
end core_debug;
|
|
|
|
architecture behave of core_debug is
|
|
-- DMI needs fixing... make a one clock pulse
|
|
signal dmi_req_1: std_ulogic;
|
|
|
|
-- CTRL register (direct actions, write 1 to act, read back 0)
|
|
-- bit 0 : Core stop
|
|
-- bit 1 : Core reset (doesn't clear stop)
|
|
-- bit 2 : Icache reset
|
|
-- bit 3 : Single step
|
|
-- bit 4 : Core start
|
|
constant DBG_CORE_CTRL : std_ulogic_vector(3 downto 0) := "0000";
|
|
constant DBG_CORE_CTRL_STOP : integer := 0;
|
|
constant DBG_CORE_CTRL_RESET : integer := 1;
|
|
constant DBG_CORE_CTRL_ICRESET : integer := 2;
|
|
constant DBG_CORE_CTRL_STEP : integer := 3;
|
|
constant DBG_CORE_CTRL_START : integer := 4;
|
|
|
|
-- STAT register (read only)
|
|
-- bit 0 : Core stopping (wait til bit 1 set)
|
|
-- bit 1 : Core stopped
|
|
-- bit 2 : Core terminated (clears with start or reset)
|
|
constant DBG_CORE_STAT : std_ulogic_vector(3 downto 0) := "0001";
|
|
constant DBG_CORE_STAT_STOPPING : integer := 0;
|
|
constant DBG_CORE_STAT_STOPPED : integer := 1;
|
|
constant DBG_CORE_STAT_TERM : integer := 2;
|
|
|
|
-- NIA register (read only for now)
|
|
constant DBG_CORE_NIA : std_ulogic_vector(3 downto 0) := "0010";
|
|
|
|
-- Some internal wires
|
|
signal stat_reg : std_ulogic_vector(63 downto 0);
|
|
|
|
-- Some internal latches
|
|
signal stopping : std_ulogic;
|
|
signal do_step : std_ulogic;
|
|
signal do_reset : std_ulogic;
|
|
signal do_icreset : std_ulogic;
|
|
signal terminated : std_ulogic;
|
|
|
|
begin
|
|
-- Single cycle register accesses on DMI
|
|
dmi_ack <= dmi_req;
|
|
|
|
-- Status register read composition
|
|
stat_reg <= (2 => terminated,
|
|
1 => core_stopped,
|
|
0 => stopping,
|
|
others => '0');
|
|
|
|
-- DMI read data mux
|
|
with dmi_addr select dmi_dout <=
|
|
stat_reg when DBG_CORE_STAT,
|
|
nia when DBG_CORE_NIA,
|
|
(others => '0') when others;
|
|
|
|
-- DMI writes
|
|
reg_write: process(clk)
|
|
begin
|
|
if rising_edge(clk) then
|
|
-- Reset the 1-cycle "do" signals
|
|
do_step <= '0';
|
|
do_reset <= '0';
|
|
do_icreset <= '0';
|
|
|
|
if (rst) then
|
|
stopping <= '0';
|
|
terminated <= '0';
|
|
else
|
|
-- Edge detect on dmi_req for 1-shot pulses
|
|
dmi_req_1 <= dmi_req;
|
|
if dmi_req = '1' and dmi_req_1 = '0' then
|
|
if dmi_wr = '1' then
|
|
report("DMI write to " & to_hstring(dmi_addr));
|
|
|
|
-- Control register actions
|
|
if dmi_addr = DBG_CORE_CTRL then
|
|
if dmi_din(DBG_CORE_CTRL_RESET) = '1' then
|
|
do_reset <= '1';
|
|
terminated <= '0';
|
|
end if;
|
|
if dmi_din(DBG_CORE_CTRL_STOP) = '1' then
|
|
stopping <= '1';
|
|
end if;
|
|
if dmi_din(DBG_CORE_CTRL_STEP) = '1' then
|
|
do_step <= '1';
|
|
terminated <= '0';
|
|
end if;
|
|
if dmi_din(DBG_CORE_CTRL_ICRESET) = '1' then
|
|
do_icreset <= '1';
|
|
end if;
|
|
if dmi_din(DBG_CORE_CTRL_START) = '1' then
|
|
stopping <= '0';
|
|
terminated <= '0';
|
|
end if;
|
|
end if;
|
|
else
|
|
report("DMI read from " & to_string(dmi_addr));
|
|
end if;
|
|
end if;
|
|
|
|
-- Set core stop on terminate. We'll be stopping some time *after*
|
|
-- the offending instruction, at least until we can do back flushes
|
|
-- that preserve NIA which we can't just yet.
|
|
if terminate = '1' then
|
|
stopping <= '1';
|
|
terminated <= '1';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- Core control signals generated by the debug module
|
|
core_stop <= stopping and not do_step;
|
|
core_rst <= do_reset;
|
|
icache_rst <= do_icreset;
|
|
terminated_out <= terminated;
|
|
end behave;
|
|
|