$(GHDL) -a $(GHDLFLAGS) $<
common.o: decode_types.o
-core_tb.o: common.o core.o soc.o
-core.o: common.o wishbone_types.o fetch1.o fetch2.o icache.o decode1.o decode2.o register_file.o cr_file.o execute1.o execute2.o loadstore1.o loadstore2.o multiply.o writeback.o
+sim_jtag.o: sim_jtag_socket.o
+core_tb.o: common.o core.o soc.o sim_jtag.o
+core.o: common.o wishbone_types.o fetch1.o fetch2.o icache.o decode1.o decode2.o register_file.o cr_file.o execute1.o execute2.o loadstore1.o loadstore2.o multiply.o writeback.o core_debug.o
+core_debug.o:
cr_file.o: common.o
crhelpers.o: common.o
decode1.o: common.o decode_types.o
soc_reset_tb: fpga/soc_reset_tb.o fpga/soc_reset.o
$(GHDL) -e $(GHDLFLAGS) soc_reset_tb
-core_tb: core_tb.o simple_ram_behavioural_helpers_c.o sim_console_c.o
- $(GHDL) -e $(GHDLFLAGS) -Wl,simple_ram_behavioural_helpers_c.o -Wl,sim_console_c.o $@
+core_tb: core_tb.o simple_ram_behavioural_helpers_c.o sim_console_c.o sim_jtag_socket_c.o
+ $(GHDL) -e $(GHDLFLAGS) -Wl,simple_ram_behavioural_helpers_c.o -Wl,sim_console_c.o -Wl,sim_jtag_socket_c.o $@
fetch_tb: fetch_tb.o
$(GHDL) -e $(GHDLFLAGS) $@
end record;
type Fetch1ToFetch2Type is record
- nia: std_ulogic_vector(63 downto 0);
+ nia: std_ulogic_vector(63 downto 0);
+ pipe_stop : std_ulogic;
end record;
type Fetch2ToDecode1Type is record
valid: std_ulogic;
+ stop_mark : std_ulogic;
nia: std_ulogic_vector(63 downto 0);
insn: std_ulogic_vector(31 downto 0);
end record;
- constant Fetch2ToDecode1Init : Fetch2ToDecode1Type := (valid => '0', others => (others => '0'));
+ constant Fetch2ToDecode1Init : Fetch2ToDecode1Type := (valid => '0', stop_mark => '0', others => (others => '0'));
type Decode1ToDecode2Type is record
valid: std_ulogic;
+ stop_mark : std_ulogic;
nia: std_ulogic_vector(63 downto 0);
insn: std_ulogic_vector(31 downto 0);
decode: decode_rom_t;
end record;
- constant Decode1ToDecode2Init : Decode1ToDecode2Type := (valid => '0', decode => decode_rom_init, others => (others => '0'));
+ constant Decode1ToDecode2Init : Decode1ToDecode2Type := (valid => '0', stop_mark => '0', decode => decode_rom_init, others => (others => '0'));
type Fetch2ToIcacheType is record
req: std_ulogic;
wishbone_data_in : in wishbone_slave_out;
wishbone_data_out : out wishbone_master_out;
- -- Added for debug, ghdl doesn't support external names unfortunately
- registers : out regfile;
- terminate_out : out std_ulogic
+ 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;
+
+ terminated_out : out std_logic
);
end core;
signal flush: std_ulogic;
signal complete: std_ulogic;
-
signal terminate: std_ulogic;
+ signal core_rst: std_ulogic;
+
+ -- Debug actions
+ signal dbg_core_stop: std_ulogic;
+ signal dbg_core_rst: std_ulogic;
+ signal dbg_icache_rst: std_ulogic;
+
+ -- Debug status
+ signal dbg_core_is_stopped: std_ulogic;
+
+ -- For sim
+ signal registers: regfile;
+
begin
- terminate_out <= terminate;
+ core_rst <= dbg_core_rst or rst;
fetch1_0: entity work.fetch1
generic map (
)
port map (
clk => clk,
- rst => rst,
+ rst => core_rst,
stall_in => fetch1_stall_in,
flush_in => flush,
e_in => execute1_to_fetch1,
fetch2_0: entity work.fetch2
port map (
clk => clk,
- rst => rst,
+ rst => core_rst,
stall_in => fetch2_stall_in,
stall_out => fetch2_stall_out,
flush_in => flush,
i_in => icache_to_fetch2,
i_out => fetch2_to_icache,
+ stop_in => dbg_core_stop,
f_in => fetch1_to_fetch2,
f_out => fetch2_to_decode1
);
)
port map(
clk => clk,
- rst => rst,
+ rst => rst or dbg_icache_rst,
i_in => fetch2_to_icache,
i_out => icache_to_fetch2,
wishbone_out => wishbone_insn_out,
decode1_0: entity work.decode1
port map (
clk => clk,
- rst => rst,
+ rst => core_rst,
stall_in => decode1_stall_in,
flush_in => flush,
f_in => fetch2_to_decode1,
decode2_0: entity work.decode2
port map (
clk => clk,
- rst => rst,
+ rst => core_rst,
stall_out => decode2_stall_out,
flush_in => flush,
complete_in => complete,
+ stopped_out => dbg_core_is_stopped,
d_in => decode1_to_decode2,
e_out => decode2_to_execute1,
l_out => decode2_to_loadstore1,
complete_out => complete
);
+ debug_0: entity work.core_debug
+ port map (
+ clk => clk,
+ rst => rst,
+ dmi_addr => dmi_addr,
+ dmi_din => dmi_din,
+ dmi_dout => dmi_dout,
+ dmi_req => dmi_req,
+ dmi_wr => dmi_wr,
+ dmi_ack => dmi_ack,
+ core_stop => dbg_core_stop,
+ core_rst => dbg_core_rst,
+ icache_rst => dbg_icache_rst,
+ terminate => terminate,
+ core_stopped => dbg_core_is_stopped,
+ nia => fetch1_to_fetch2.nia,
+ terminated_out => terminated_out
+ );
+
+ -- Dump registers if core terminates
+ sim_terminate_test: if SIM generate
+ dump_registers: process(all)
+ begin
+ if terminate = '1' then
+ loop_0: for i in 0 to 31 loop
+ report "REG " & to_hstring(registers(i));
+ end loop loop_0;
+ end if;
+ end process;
+ end generate;
+
end behave;
--- /dev/null
+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
+ if (rst) then
+ stopping <= '0';
+ terminated <= '0';
+ else
+ -- Reset the 1-cycle "do" signals
+ do_step <= '0';
+ do_reset <= '0';
+ do_icreset <= '0';
+
+ -- 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;
+
library ieee;
use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
library work;
use work.common.all;
uart0_txd => open
);
- clk_process: process
- begin
- clk <= '0';
- wait for clk_period/2;
- clk <= '1';
- wait for clk_period/2;
- end process;
-
- rst_process: process
- begin
- rst <= '1';
- wait for 10*clk_period;
- rst <= '0';
- wait;
- end process;
+ clk_process: process
+ begin
+ clk <= '0';
+ wait for clk_period/2;
+ clk <= '1';
+ wait for clk_period/2;
+ end process;
+
+ rst_process: process
+ begin
+ rst <= '1';
+ wait for 10*clk_period;
+ rst <= '0';
+ wait;
+ end process;
+
+ jtag: entity work.sim_jtag;
end;
v.valid := f_in.valid;
v.nia := f_in.nia;
v.insn := f_in.insn;
+ v.stop_mark := f_in.stop_mark;
ppc_insn := PPC_ILLEGAL;
complete_in : in std_ulogic;
stall_out : out std_ulogic;
+ stopped_out : out std_ulogic;
+
flush_in: in std_ulogic;
d_in : in Decode1ToDecode2Type;
-- through the pipeline.
stall_out <= '0';
is_valid := d_in.valid;
+
+ -- Handle debugger stop
+ stopped_out <= '0';
+ if d_in.stop_mark = '1' and v_int.outstanding = 0 then
+ stopped_out <= '1';
+ end if;
+
case v_int.state is
when IDLE =>
- if (flush_in = '0') and (d_in.valid = '1') and (d_in.decode.sgl_pipe = '1') then
+ if (flush_in = '0') and (is_valid = '1') and (d_in.decode.sgl_pipe = '1') then
if v_int.outstanding /= 0 then
v_int.state := WAIT_FOR_PREV_TO_COMPLETE;
stall_out <= '1';
-- Update outputs
f_out <= r;
+
+ report "fetch1 R:" & std_ulogic'image(e_in.redirect) & " v.nia:" & to_hstring(v.nia) & " f_out.nia:" & to_hstring(f_out.nia);
end process;
end architecture behaviour;
stall_out : out std_ulogic;
flush_in : in std_ulogic;
+ stop_in : in std_ulogic;
i_in : in IcacheToFetch2Type;
i_out : out Fetch2ToIcacheType;
v.valid := i_in.ack;
v.nia := f_in.nia;
v.insn := i_in.insn;
- stall_out <= not i_in.ack;
+ stall_out <= stop_in or not i_in.ack;
-
- if flush_in = '1' then
+ if flush_in = '1' or stop_in = '1' then
v.valid := '0';
end if;
+ v.stop_mark := stop_in;
-- Update registers
rin <= v;
library ieee;
use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
use ieee.math_real.all;
-
use std.textio.all;
+use std.env.stop;
library work;
use work.common.all;
-- UART0 signals:
uart0_txd : out std_ulogic;
- uart0_rxd : in std_ulogic
+ uart0_rxd : in std_ulogic;
+
+ -- Misc (to use for things like LEDs)
+ core_terminated : out std_ulogic
);
end entity soc;
signal wb_bram_out : wishbone_slave_out;
constant mem_adr_bits : positive := positive(ceil(log2(real(MEMORY_SIZE))));
- -- Core debug signals (used in SIM only)
- signal registers : regfile;
- signal terminate : std_ulogic;
-
-- DMI debug bus signals
signal dmi_addr : std_ulogic_vector(7 downto 0);
signal dmi_din : std_ulogic_vector(63 downto 0);
wishbone_insn_out => wishbone_icore_out,
wishbone_data_in => wishbone_dcore_in,
wishbone_data_out => wishbone_dcore_out,
- registers => registers,
- terminate_out => terminate
+ dmi_addr => dmi_addr(3 downto 0),
+ dmi_dout => dmi_core_dout,
+ dmi_din => dmi_dout,
+ dmi_wr => dmi_wr,
+ dmi_ack => dmi_core_ack,
+ dmi_req => dmi_core_req
);
-- Wishbone bus master arbiter & mux
end process slave_intercon;
-- Simulated memory and UART
- sim_terminate_test: if SIM generate
-
- -- Dump registers if core terminates
- dump_registers: process(all)
- begin
- if terminate = '1' then
- loop_0: for i in 0 to 31 loop
- report "REG " & to_hstring(registers(i));
- end loop loop_0;
- assert false report "end of test" severity failure;
- end if;
- end process;
-
- end generate;
-- UART0 wishbone slave
-- XXX FIXME: Need a proper wb64->wb8 adapter that
-- DMI interconnect
dmi_intercon: process(dmi_addr, dmi_req,
- dmi_wb_ack, dmi_wb_dout,
- dmi_core_ack, dmi_core_dout)
+ dmi_wb_ack, dmi_wb_dout,
+ dmi_core_ack, dmi_core_dout)
-- DMI address map (each address is a full 64-bit register)
--
variable slave : slave_type;
begin
-- Simple address decoder
- if dmi_addr(7 downto 0) = "000000--" then
+ slave := SLAVE_NONE;
+ if std_match(dmi_addr, "000000--") then
slave := SLAVE_WB;
- elsif dmi_addr(7 downto 0) = "0001----" then
+ elsif std_match(dmi_addr, "0001----") then
slave := SLAVE_CORE;
- else
- slave := SLAVE_NONE;
end if;
-- DMI muxing
dmi_ack <= dmi_req;
dmi_din <= (others => '1');
end case;
- end process;
- -- Core dummy
- dmi_core_ack <= dmi_core_req;
- dmi_core_dout <= x"0000000000000000";
+ -- SIM magic exit
+ if SIM and dmi_req = '1' and dmi_addr = "11111111" and dmi_wr = '1' then
+ stop;
+ end if;
+ end process;
-- Wishbone debug master (TODO: Add a DMI address decoder)
wishbone_debug: entity work.wishbone_debug_master