mirror of
https://github.com/antonblanchard/microwatt.git
synced 2026-01-13 15:18:09 +00:00
Merge pull request #69 from antonblanchard/debug-module
Merge debug module patches
This commit is contained in:
commit
ba783fddd5
35
Makefile
35
Makefile
@ -1,8 +1,9 @@
|
||||
GHDL=ghdl
|
||||
GHDLFLAGS=--std=08
|
||||
GHDLFLAGS=--std=08 -Psim-unisim
|
||||
CFLAGS=-O2 -Wall
|
||||
|
||||
all = core_tb simple_ram_behavioural_tb soc_reset_tb icache_tb multiply_tb
|
||||
all = core_tb simple_ram_behavioural_tb soc_reset_tb icache_tb multiply_tb dmi_dtm_tb
|
||||
|
||||
# XXX
|
||||
# loadstore_tb fetch_tb
|
||||
|
||||
@ -12,8 +13,10 @@ all: $(all)
|
||||
$(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
|
||||
@ -40,17 +43,26 @@ simple_ram_behavioural_helpers.o:
|
||||
simple_ram_behavioural_tb.o: wishbone_types.o simple_ram_behavioural.o
|
||||
simple_ram_behavioural.o: wishbone_types.o simple_ram_behavioural_helpers.o
|
||||
sim_uart.o: wishbone_types.o sim_console.o
|
||||
soc.o: common.o wishbone_types.o core.o wishbone_arbiter.o sim_uart.o simple_ram_behavioural.o
|
||||
soc.o: common.o wishbone_types.o core.o wishbone_arbiter.o sim_uart.o simple_ram_behavioural.o dmi_dtm_xilinx.o wishbone_debug_master.o
|
||||
wishbone_arbiter.o: wishbone_types.o
|
||||
wishbone_types.o:
|
||||
writeback.o: common.o
|
||||
dmi_dtm_tb.o: dmi_dtm_xilinx.o wishbone_debug_master.o
|
||||
dmi_dtm_xilinx.o: sim-unisim/unisim_vcomponents.o
|
||||
wishbone_debug_master.o: wishbone_types.o
|
||||
|
||||
UNISIM_BITS = sim-unisim/unisim_vcomponents.vhdl sim-unisim/BSCANE2.vhdl sim-unisim/BUFG.vhdl
|
||||
sim-unisim/unisim_vcomponents.o: $(UNISIM_BITS)
|
||||
$(GHDL) -a $(GHDLFLAGS) --work=unisim --workdir=sim-unisim $^
|
||||
|
||||
|
||||
fpga/soc_reset_tb.o: fpga/soc_reset.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) $@
|
||||
@ -70,6 +82,9 @@ simple_ram_tb: simple_ram_tb.o
|
||||
simple_ram_behavioural_tb: simple_ram_behavioural_helpers_c.o simple_ram_behavioural_tb.o
|
||||
$(GHDL) -e $(GHDLFLAGS) -Wl,simple_ram_behavioural_helpers_c.o $@
|
||||
|
||||
dmi_dtm_tb: dmi_dtm_tb.o simple_ram_behavioural_helpers_c.o
|
||||
$(GHDL) -e $(GHDLFLAGS) -Wl,simple_ram_behavioural_helpers_c.o $@
|
||||
|
||||
tests = $(sort $(patsubst tests/%.out,%,$(wildcard tests/*.out)))
|
||||
|
||||
check: $(tests) test_micropython test_micropython_long
|
||||
@ -86,4 +101,8 @@ test_micropython_long: core_tb
|
||||
@./scripts/test_micropython_long.py
|
||||
|
||||
clean:
|
||||
rm -f *.o work-*cf $(all)
|
||||
rm -f *.o work-*cf unisim-*cf $(all)
|
||||
rm -f sim-unisim/*.o sim-unisim/unisim-*cf
|
||||
|
||||
distclean: clean
|
||||
rm -f *~ fpga/~
|
||||
|
||||
@ -13,23 +13,26 @@ package common is
|
||||
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;
|
||||
|
||||
74
core.vhdl
74
core.vhdl
@ -20,9 +20,14 @@ entity core is
|
||||
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;
|
||||
|
||||
@ -73,11 +78,24 @@ architecture behave of core is
|
||||
signal flush: std_ulogic;
|
||||
|
||||
signal complete: std_ulogic;
|
||||
|
||||
signal terminate: std_ulogic;
|
||||
signal core_rst: std_ulogic;
|
||||
signal icache_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 (
|
||||
@ -85,7 +103,7 @@ begin
|
||||
)
|
||||
port map (
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
rst => core_rst,
|
||||
stall_in => fetch1_stall_in,
|
||||
flush_in => flush,
|
||||
e_in => execute1_to_fetch1,
|
||||
@ -97,12 +115,13 @@ begin
|
||||
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
|
||||
);
|
||||
@ -116,17 +135,19 @@ begin
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
rst => icache_rst,
|
||||
i_in => fetch2_to_icache,
|
||||
i_out => icache_to_fetch2,
|
||||
wishbone_out => wishbone_insn_out,
|
||||
wishbone_in => wishbone_insn_in
|
||||
);
|
||||
|
||||
icache_rst <= rst or dbg_icache_rst;
|
||||
|
||||
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,
|
||||
@ -138,10 +159,11 @@ begin
|
||||
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,
|
||||
@ -222,4 +244,36 @@ begin
|
||||
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;
|
||||
assert false report "end of test" severity failure;
|
||||
end if;
|
||||
end process;
|
||||
end generate;
|
||||
|
||||
end behave;
|
||||
|
||||
152
core_debug.vhdl
Normal file
152
core_debug.vhdl
Normal file
@ -0,0 +1,152 @@
|
||||
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;
|
||||
|
||||
31
core_tb.vhdl
31
core_tb.vhdl
@ -1,5 +1,6 @@
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
library work;
|
||||
use work.common.all;
|
||||
@ -29,19 +30,21 @@ begin
|
||||
uart0_txd => open
|
||||
);
|
||||
|
||||
clk_process: process
|
||||
begin
|
||||
clk <= '0';
|
||||
wait for clk_period/2;
|
||||
clk <= '1';
|
||||
wait for clk_period/2;
|
||||
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;
|
||||
rst_process: process
|
||||
begin
|
||||
rst <= '1';
|
||||
wait for 10*clk_period;
|
||||
rst <= '0';
|
||||
wait;
|
||||
end process;
|
||||
|
||||
jtag: entity work.sim_jtag;
|
||||
end;
|
||||
|
||||
@ -248,6 +248,7 @@ begin
|
||||
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;
|
||||
|
||||
|
||||
11
decode2.vhdl
11
decode2.vhdl
@ -16,6 +16,8 @@ entity decode2 is
|
||||
complete_in : in std_ulogic;
|
||||
stall_out : out std_ulogic;
|
||||
|
||||
stopped_out : out std_ulogic;
|
||||
|
||||
flush_in: in std_ulogic;
|
||||
|
||||
d_in : in Decode1ToDecode2Type;
|
||||
@ -330,9 +332,16 @@ begin
|
||||
-- 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';
|
||||
|
||||
30
dmi_dtm_dummy.vhdl
Normal file
30
dmi_dtm_dummy.vhdl
Normal file
@ -0,0 +1,30 @@
|
||||
-- Dummy/empty DMI interface to make toplevel happy on unsupported FPGAs
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
|
||||
library work;
|
||||
use work.wishbone_types.all;
|
||||
|
||||
entity dmi_dtm is
|
||||
generic(ABITS : INTEGER:=8;
|
||||
DBITS : INTEGER:=32);
|
||||
|
||||
port(sys_clk : in std_ulogic;
|
||||
sys_reset : in std_ulogic;
|
||||
dmi_addr : out std_ulogic_vector(ABITS - 1 downto 0);
|
||||
dmi_din : in std_ulogic_vector(DBITS - 1 downto 0);
|
||||
dmi_dout : out std_ulogic_vector(DBITS - 1 downto 0);
|
||||
dmi_req : out std_ulogic;
|
||||
dmi_wr : out std_ulogic;
|
||||
dmi_ack : in std_ulogic
|
||||
);
|
||||
end entity dmi_dtm;
|
||||
|
||||
architecture behaviour of dmi_dtm is
|
||||
dmi_addr <= (others => '0');
|
||||
dmi_dout <= (others => '0');
|
||||
dmi_req <= '0';
|
||||
dmi_wr <= '0';
|
||||
end architecture behaviour;
|
||||
|
||||
250
dmi_dtm_tb.vhdl
Normal file
250
dmi_dtm_tb.vhdl
Normal file
@ -0,0 +1,250 @@
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
library work;
|
||||
use work.common.all;
|
||||
use work.wishbone_types.all;
|
||||
|
||||
library unisim;
|
||||
use unisim.vcomponents.all;
|
||||
|
||||
entity dmi_dtm_tb is
|
||||
end dmi_dtm_tb;
|
||||
|
||||
architecture behave of dmi_dtm_tb is
|
||||
signal clk : std_ulogic;
|
||||
signal rst : std_ulogic;
|
||||
constant clk_period : time := 10 ns;
|
||||
constant jclk_period : time := 30 ns;
|
||||
|
||||
-- DMI debug bus signals
|
||||
signal dmi_addr : std_ulogic_vector(7 downto 0);
|
||||
signal dmi_din : std_ulogic_vector(63 downto 0);
|
||||
signal dmi_dout : std_ulogic_vector(63 downto 0);
|
||||
signal dmi_req : std_ulogic;
|
||||
signal dmi_wr : std_ulogic;
|
||||
signal dmi_ack : std_ulogic;
|
||||
|
||||
-- Global JTAG signals (used by BSCANE2 inside dmi_dtm
|
||||
alias j : glob_jtag_t is glob_jtag;
|
||||
|
||||
-- Wishbone interfaces
|
||||
signal wishbone_ram_in : wishbone_slave_out;
|
||||
signal wishbone_ram_out : wishbone_master_out;
|
||||
|
||||
begin
|
||||
dtm: entity work.dmi_dtm
|
||||
generic map(
|
||||
ABITS => 8,
|
||||
DBITS => 64
|
||||
)
|
||||
port map(
|
||||
sys_clk => clk,
|
||||
sys_reset => 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
|
||||
);
|
||||
|
||||
simple_ram_0: entity work.mw_soc_memory
|
||||
generic map(RAM_INIT_FILE => "simple_ram_behavioural.bin",
|
||||
MEMORY_SIZE => 524288)
|
||||
port map(clk => clk, rst => rst,
|
||||
wishbone_in => wishbone_ram_out,
|
||||
wishbone_out => wishbone_ram_in);
|
||||
|
||||
wishbone_debug_0: entity work.wishbone_debug_master
|
||||
port map(clk => clk, rst => rst,
|
||||
dmi_addr => dmi_addr(1 downto 0),
|
||||
dmi_dout => dmi_din,
|
||||
dmi_din => dmi_dout,
|
||||
dmi_wr => dmi_wr,
|
||||
dmi_ack => dmi_ack,
|
||||
dmi_req => dmi_req,
|
||||
wb_in => wishbone_ram_in,
|
||||
wb_out => wishbone_ram_out);
|
||||
|
||||
-- system clock
|
||||
sys_clk: process
|
||||
begin
|
||||
clk <= '1';
|
||||
wait for clk_period / 2;
|
||||
clk <= '0';
|
||||
wait for clk_period / 2;
|
||||
end process sys_clk;
|
||||
|
||||
-- system sim: just reset and wait
|
||||
sys_sim: process
|
||||
begin
|
||||
rst <= '1';
|
||||
wait for clk_period;
|
||||
rst <= '0';
|
||||
wait;
|
||||
end process;
|
||||
|
||||
-- jtag sim process
|
||||
sim_jtag: process
|
||||
procedure clock(count: in INTEGER) is
|
||||
begin
|
||||
for i in 1 to count loop
|
||||
j.tck <= '0';
|
||||
wait for jclk_period/2;
|
||||
j.tck <= '1';
|
||||
wait for jclk_period/2;
|
||||
end loop;
|
||||
end procedure clock;
|
||||
|
||||
procedure shift_out(val: in std_ulogic_vector) is
|
||||
begin
|
||||
for i in 0 to val'length-1 loop
|
||||
j.tdi <= val(i);
|
||||
clock(1);
|
||||
end loop;
|
||||
end procedure shift_out;
|
||||
|
||||
procedure shift_in(val: out std_ulogic_vector) is
|
||||
begin
|
||||
for i in val'length-1 downto 0 loop
|
||||
val := j.tdo & val(val'length-1 downto 1);
|
||||
clock(1);
|
||||
end loop;
|
||||
end procedure shift_in;
|
||||
|
||||
procedure send_command(
|
||||
addr : in std_ulogic_vector(7 downto 0);
|
||||
data : in std_ulogic_vector(63 downto 0);
|
||||
op : in std_ulogic_vector(1 downto 0)) is
|
||||
begin
|
||||
j.capture <= '1';
|
||||
clock(1);
|
||||
j.capture <= '0';
|
||||
clock(1);
|
||||
j.shift <= '1';
|
||||
shift_out(op);
|
||||
shift_out(data);
|
||||
shift_out(addr);
|
||||
j.shift <= '0';
|
||||
j.update <= '1';
|
||||
clock(1);
|
||||
j.update <= '0';
|
||||
clock(1);
|
||||
end procedure send_command;
|
||||
|
||||
procedure read_resp(
|
||||
op : out std_ulogic_vector(1 downto 0);
|
||||
data : out std_ulogic_vector(63 downto 0)) is
|
||||
|
||||
variable addr : std_ulogic_vector(7 downto 0);
|
||||
begin
|
||||
j.capture <= '1';
|
||||
clock(1);
|
||||
j.capture <= '0';
|
||||
clock(1);
|
||||
j.shift <= '1';
|
||||
shift_in(op);
|
||||
shift_in(data);
|
||||
shift_in(addr);
|
||||
j.shift <= '0';
|
||||
j.update <= '1';
|
||||
clock(1);
|
||||
j.update <= '0';
|
||||
clock(1);
|
||||
end procedure read_resp;
|
||||
|
||||
procedure dmi_write(addr : in std_ulogic_vector(7 downto 0);
|
||||
data : in std_ulogic_vector(63 downto 0)) is
|
||||
variable resp_op : std_ulogic_vector(1 downto 0);
|
||||
variable resp_data : std_ulogic_vector(63 downto 0);
|
||||
variable timeout : integer;
|
||||
begin
|
||||
send_command(addr, data, "10");
|
||||
loop
|
||||
read_resp(resp_op, resp_data);
|
||||
case resp_op is
|
||||
when "00" =>
|
||||
return;
|
||||
when "11" =>
|
||||
timeout := timeout + 1;
|
||||
assert timeout < 0
|
||||
report "dmi_write timed out !" severity error;
|
||||
when others =>
|
||||
assert 0 > 1 report "dmi_write got odd status: " &
|
||||
to_hstring(resp_op) severity error;
|
||||
end case;
|
||||
end loop;
|
||||
end procedure dmi_write;
|
||||
|
||||
|
||||
procedure dmi_read(addr : in std_ulogic_vector(7 downto 0);
|
||||
data : out std_ulogic_vector(63 downto 0)) is
|
||||
variable resp_op : std_ulogic_vector(1 downto 0);
|
||||
variable timeout : integer;
|
||||
begin
|
||||
send_command(addr, (others => '0'), "01");
|
||||
loop
|
||||
read_resp(resp_op, data);
|
||||
case resp_op is
|
||||
when "00" =>
|
||||
return;
|
||||
when "11" =>
|
||||
timeout := timeout + 1;
|
||||
assert timeout < 0
|
||||
report "dmi_read timed out !" severity error;
|
||||
when others =>
|
||||
assert 0 > 1 report "dmi_read got odd status: " &
|
||||
to_hstring(resp_op) severity error;
|
||||
end case;
|
||||
end loop;
|
||||
end procedure dmi_read;
|
||||
|
||||
variable data : std_ulogic_vector(63 downto 0);
|
||||
begin
|
||||
-- init & reset
|
||||
j.reset <= '1';
|
||||
j.sel <= "0000";
|
||||
j.capture <= '0';
|
||||
j.update <= '0';
|
||||
j.shift <= '0';
|
||||
j.tdi <= '0';
|
||||
j.tms <= '0';
|
||||
j.runtest <= '0';
|
||||
clock(5);
|
||||
j.reset <= '0';
|
||||
clock(5);
|
||||
|
||||
-- select chain 2
|
||||
j.sel <= "0010";
|
||||
clock(1);
|
||||
|
||||
-- send command
|
||||
dmi_read(x"00", data);
|
||||
report "Read addr reg:" & to_hstring(data);
|
||||
report "Writing addr reg to all 1's";
|
||||
dmi_write(x"00", (others => '1'));
|
||||
dmi_read(x"00", data);
|
||||
report "Read addr reg:" & to_hstring(data);
|
||||
|
||||
report "Writing ctrl reg to all 1's";
|
||||
dmi_write(x"02", (others => '1'));
|
||||
dmi_read(x"02", data);
|
||||
report "Read ctrl reg:" & to_hstring(data);
|
||||
|
||||
report "Read memory at 0...\n";
|
||||
dmi_write(x"00", x"0000000000000000");
|
||||
dmi_write(x"02", x"00000000000007ff");
|
||||
dmi_read(x"01", data);
|
||||
report "00:" & to_hstring(data);
|
||||
dmi_read(x"01", data);
|
||||
report "08:" & to_hstring(data);
|
||||
dmi_read(x"01", data);
|
||||
report "10:" & to_hstring(data);
|
||||
dmi_read(x"01", data);
|
||||
report "18:" & to_hstring(data);
|
||||
clock(10);
|
||||
std.env.finish;
|
||||
end process;
|
||||
end behave;
|
||||
276
dmi_dtm_xilinx.vhdl
Normal file
276
dmi_dtm_xilinx.vhdl
Normal file
@ -0,0 +1,276 @@
|
||||
-- Xilinx internal JTAG to DMI interface
|
||||
--
|
||||
-- DMI bus
|
||||
--
|
||||
-- req : ____/------------\_____
|
||||
-- addr: xxxx< >xxxxx
|
||||
-- dout: xxxx< >xxxxx
|
||||
-- wr : xxxx< >xxxxx
|
||||
-- din : xxxxxxxxxxxx< >xxx
|
||||
-- ack : ____________/------\___
|
||||
--
|
||||
-- * addr/dout set along with req, can be latched on same cycle by slave
|
||||
-- * ack & din remain up until req is dropped by master, the slave must
|
||||
-- provide a stable output on din on reads during that time.
|
||||
-- * req remains low at until at least one sysclk after ack seen down.
|
||||
--
|
||||
-- JTAG (tck) DMI (sys_clk)
|
||||
--
|
||||
-- * jtag_req = 1
|
||||
-- (jtag_req_0) *
|
||||
-- (jtag_req_1) -> * dmi_req = 1 >
|
||||
-- *.../...
|
||||
-- * dmi_ack = 1 <
|
||||
-- * (dmi_ack_0)
|
||||
-- * <- (dmi_ack_1)
|
||||
-- * jtag_req = 0 (and latch dmi_din)
|
||||
-- (jtag_req_0) *
|
||||
-- (jtag_req_1) -> * dmi_req = 0 >
|
||||
-- * dmi_ack = 0 <
|
||||
-- * (dmi_ack_0)
|
||||
-- * <- (dmi_ack_1)
|
||||
--
|
||||
-- jtag_req can go back to 1 when jtag_rsp_1 is 0
|
||||
--
|
||||
-- Questions/TODO:
|
||||
-- - I use 2 flip fops for sync, is that enough ?
|
||||
-- - I treat the jtag_reset as an async reset, is that necessary ?
|
||||
-- - Dbl check reset situation since we have two different resets
|
||||
-- each only resetting part of the logic...
|
||||
-- - Look at optionally removing the synchronizer on the ack path,
|
||||
-- assuming JTAG is always slow enough that ack will have been
|
||||
-- stable long enough by the time CAPTURE comes in.
|
||||
-- - We could avoid the latched request by not shifting while a
|
||||
-- request is in progress (and force TDO to 1 to return a busy
|
||||
-- status).
|
||||
--
|
||||
-- WARNING: This isn't the real DMI JTAG protocol (at least not yet).
|
||||
-- a command while busy will be ignored. A response of "11"
|
||||
-- means the previous command is still going, try again.
|
||||
-- As such We don't implement the DMI "error" status, and
|
||||
-- we don't implement DTMCS yet... This may still all change
|
||||
-- but for now it's easier that way as the real DMI protocol
|
||||
-- requires for a command to work properly that enough TCK
|
||||
-- are sent while IDLE and I'm having trouble getting that
|
||||
-- working with UrJtag and the Xilinx BSCAN2 for now.
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.math_real.all;
|
||||
|
||||
library work;
|
||||
use work.wishbone_types.all;
|
||||
|
||||
library unisim;
|
||||
use unisim.vcomponents.all;
|
||||
|
||||
entity dmi_dtm is
|
||||
generic(ABITS : INTEGER:=8;
|
||||
DBITS : INTEGER:=32);
|
||||
|
||||
port(sys_clk : in std_ulogic;
|
||||
sys_reset : in std_ulogic;
|
||||
dmi_addr : out std_ulogic_vector(ABITS - 1 downto 0);
|
||||
dmi_din : in std_ulogic_vector(DBITS - 1 downto 0);
|
||||
dmi_dout : out std_ulogic_vector(DBITS - 1 downto 0);
|
||||
dmi_req : out std_ulogic;
|
||||
dmi_wr : out std_ulogic;
|
||||
dmi_ack : in std_ulogic
|
||||
-- dmi_err : in std_ulogic TODO: Add error response
|
||||
);
|
||||
end entity dmi_dtm;
|
||||
|
||||
architecture behaviour of dmi_dtm is
|
||||
|
||||
-- Signals coming out of the BSCANE2 block
|
||||
signal jtag_reset : std_ulogic;
|
||||
signal capture : std_ulogic;
|
||||
signal update : std_ulogic;
|
||||
signal drck : std_ulogic;
|
||||
signal jtag_clk : std_ulogic;
|
||||
signal sel : std_ulogic;
|
||||
signal shift : std_ulogic;
|
||||
signal tdi : std_ulogic;
|
||||
signal tdo : std_ulogic;
|
||||
signal tck : std_ulogic;
|
||||
|
||||
-- ** JTAG clock domain **
|
||||
|
||||
-- Shift register
|
||||
signal shiftr : std_ulogic_vector(ABITS + DBITS + 1 downto 0);
|
||||
|
||||
-- Latched request
|
||||
signal request : std_ulogic_vector(ABITS + DBITS + 1 downto 0);
|
||||
|
||||
-- A request is present
|
||||
signal jtag_req : std_ulogic;
|
||||
|
||||
-- Synchronizer for jtag_rsp (sys clk -> jtag_clk)
|
||||
signal dmi_ack_0 : std_ulogic;
|
||||
signal dmi_ack_1 : std_ulogic;
|
||||
|
||||
-- ** sys clock domain **
|
||||
|
||||
-- Synchronizer for jtag_req (jtag clk -> sys clk)
|
||||
signal jtag_req_0 : std_ulogic;
|
||||
signal jtag_req_1 : std_ulogic;
|
||||
|
||||
-- ** combination signals
|
||||
signal jtag_bsy : std_ulogic;
|
||||
signal op_valid : std_ulogic;
|
||||
signal rsp_op : std_ulogic_vector(1 downto 0);
|
||||
|
||||
-- ** Constants **
|
||||
constant DMI_REQ_NOP : std_ulogic_vector(1 downto 0) := "00";
|
||||
constant DMI_REQ_RD : std_ulogic_vector(1 downto 0) := "01";
|
||||
constant DMI_REQ_WR : std_ulogic_vector(1 downto 0) := "10";
|
||||
constant DMI_RSP_OK : std_ulogic_vector(1 downto 0) := "00";
|
||||
constant DMI_RSP_BSY : std_ulogic_vector(1 downto 0) := "11";
|
||||
|
||||
begin
|
||||
|
||||
-- Implement the Xilinx bscan2 for series 7 devices (TODO: use PoC to
|
||||
-- wrap this if compatibility is required with older devices).
|
||||
bscan : BSCANE2
|
||||
generic map (
|
||||
JTAG_CHAIN => 2
|
||||
)
|
||||
port map (
|
||||
CAPTURE => capture,
|
||||
DRCK => drck,
|
||||
RESET => jtag_reset,
|
||||
RUNTEST => open,
|
||||
SEL => sel,
|
||||
SHIFT => shift,
|
||||
TCK => tck,
|
||||
TDI => tdi,
|
||||
TMS => open,
|
||||
UPDATE => update,
|
||||
TDO => tdo
|
||||
);
|
||||
|
||||
-- Some examples out there suggest buffering the clock so it's
|
||||
-- treated as a proper clock net. This is probably needed when using
|
||||
-- drck (the gated clock) but I'm using the real tck here to avoid
|
||||
-- missing the update phase so maybe not...
|
||||
--
|
||||
clkbuf : BUFG
|
||||
port map (
|
||||
-- I => drck,
|
||||
I => tck,
|
||||
O => jtag_clk
|
||||
);
|
||||
|
||||
|
||||
-- dmi_req synchronization
|
||||
dmi_req_sync : process(sys_clk)
|
||||
begin
|
||||
-- sys_reset is synchronous
|
||||
if rising_edge(sys_clk) then
|
||||
if (sys_reset = '1') then
|
||||
jtag_req_0 <= '0';
|
||||
jtag_req_1 <= '0';
|
||||
else
|
||||
jtag_req_0 <= jtag_req;
|
||||
jtag_req_1 <= jtag_req_0;
|
||||
end if;
|
||||
end if;
|
||||
end process;
|
||||
dmi_req <= jtag_req_1;
|
||||
|
||||
-- dmi_ack synchronization
|
||||
dmi_ack_sync: process(jtag_clk, jtag_reset)
|
||||
begin
|
||||
-- jtag_reset is async (see comments)
|
||||
if jtag_reset = '1' then
|
||||
dmi_ack_0 <= '0';
|
||||
dmi_ack_1 <= '0';
|
||||
elsif rising_edge(jtag_clk) then
|
||||
dmi_ack_0 <= dmi_ack;
|
||||
dmi_ack_1 <= dmi_ack_0;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- jtag_bsy indicates whether we can start a new request, we can when
|
||||
-- we aren't already processing one (jtag_req) and the synchronized ack
|
||||
-- of the previous one is 0.
|
||||
--
|
||||
jtag_bsy <= jtag_req or dmi_ack_1;
|
||||
|
||||
-- decode request type in shift register
|
||||
with shiftr(1 downto 0) select op_valid <=
|
||||
'1' when DMI_REQ_RD,
|
||||
'1' when DMI_REQ_WR,
|
||||
'0' when others;
|
||||
|
||||
-- encode response op
|
||||
rsp_op <= DMI_RSP_BSY when jtag_bsy = '1' else DMI_RSP_OK;
|
||||
|
||||
-- Some DMI out signals are directly driven from the request register
|
||||
dmi_addr <= request(ABITS + DBITS + 1 downto DBITS + 2);
|
||||
dmi_dout <= request(DBITS + 1 downto 2);
|
||||
dmi_wr <= '1' when request(1 downto 0) = DMI_REQ_WR else '0';
|
||||
|
||||
-- TDO is wired to shift register bit 0
|
||||
tdo <= shiftr(0);
|
||||
|
||||
-- Main state machine. Handles shift registers, request latch and
|
||||
-- jtag_req latch. Could be split into 3 processes but it's probably
|
||||
-- not worthwhile.
|
||||
--
|
||||
shifter: process(jtag_clk, jtag_reset)
|
||||
begin
|
||||
if jtag_reset = '1' then
|
||||
shiftr <= (others => '0');
|
||||
request <= (others => '0');
|
||||
jtag_req <= '0';
|
||||
elsif rising_edge(jtag_clk) then
|
||||
|
||||
-- Handle jtag "commands" when sel is 1
|
||||
if sel = '1' then
|
||||
-- Shift state, rotate the register
|
||||
if shift = '1' then
|
||||
shiftr <= tdi & shiftr(ABITS + DBITS + 1 downto 1);
|
||||
end if;
|
||||
|
||||
-- Update state (trigger)
|
||||
--
|
||||
-- Latch the request if we aren't already processing one and
|
||||
-- it has a valid command opcode.
|
||||
--
|
||||
if update = '1' and op_valid = '1' then
|
||||
if jtag_bsy = '0' then
|
||||
request <= shiftr;
|
||||
jtag_req <= '1';
|
||||
end if;
|
||||
-- Set the shift register "op" to "busy". This will prevent
|
||||
-- us from re-starting the command on the next update if
|
||||
-- the command completes before that.
|
||||
shiftr(1 downto 0) <= DMI_RSP_BSY;
|
||||
end if;
|
||||
|
||||
-- Request completion.
|
||||
--
|
||||
-- Capture the response data for reads and clear request flag.
|
||||
--
|
||||
-- Note: We clear req (and thus dmi_req) here which relies on tck
|
||||
-- ticking and sel set. This means we are stuck with dmi_req up if
|
||||
-- the jtag interface stops. Slaves must be resilient to this.
|
||||
--
|
||||
if jtag_req = '1' and dmi_ack_1 = '1' then
|
||||
jtag_req <= '0';
|
||||
if request(1 downto 0) = DMI_REQ_RD then
|
||||
request(DBITS + 1 downto 2) <= dmi_din;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- Capture state, grab latch content with updated status
|
||||
if capture = '1' then
|
||||
shiftr <= request(ABITS + DBITS + 1 downto 2) & rsp_op;
|
||||
end if;
|
||||
|
||||
end if;
|
||||
end if;
|
||||
end process;
|
||||
end architecture behaviour;
|
||||
|
||||
@ -68,6 +68,8 @@ begin
|
||||
|
||||
-- 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;
|
||||
|
||||
@ -15,6 +15,7 @@ entity fetch2 is
|
||||
stall_out : out std_ulogic;
|
||||
|
||||
flush_in : in std_ulogic;
|
||||
stop_in : in std_ulogic;
|
||||
|
||||
i_in : in IcacheToFetch2Type;
|
||||
i_out : out Fetch2ToIcacheType;
|
||||
@ -49,12 +50,12 @@ begin
|
||||
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;
|
||||
|
||||
@ -25,13 +25,16 @@ filesets:
|
||||
- multiply.vhdl
|
||||
- writeback.vhdl
|
||||
- insn_helpers.vhdl
|
||||
- wishbone_debug_master.vhdl
|
||||
- core.vhdl
|
||||
- icache.vhdl
|
||||
- core_debug.vhdl
|
||||
file_type : vhdlSource-2008
|
||||
|
||||
soc:
|
||||
files:
|
||||
- wishbone_arbiter.vhdl
|
||||
- wishbone_debug_master.vhdl
|
||||
- soc.vhdl
|
||||
file_type : vhdlSource-2008
|
||||
|
||||
@ -46,6 +49,14 @@ filesets:
|
||||
- fpga/firmware.hex : {copyto : firmware.hex, file_type : user}
|
||||
file_type : vhdlSource-2008
|
||||
|
||||
debug_xilinx:
|
||||
files:
|
||||
- dmi_dtm_xilinx.vhdl : {file_type : vhdlSource-2008}
|
||||
|
||||
debug_dummy:
|
||||
files:
|
||||
- dmi_dtm_dummy.vhdl : {file_type : vhdlSource-2008}
|
||||
|
||||
nexys_a7:
|
||||
files:
|
||||
- fpga/nexys_a7.xdc : {file_type : xdc}
|
||||
@ -69,7 +80,7 @@ filesets:
|
||||
targets:
|
||||
nexys_a7:
|
||||
default_tool: vivado
|
||||
filesets: [core, nexys_a7, soc, fpga]
|
||||
filesets: [core, nexys_a7, soc, fpga, debug_xilinx]
|
||||
parameters : [memory_size, ram_init_file]
|
||||
tools:
|
||||
vivado: {part : xc7a100tcsg324-1}
|
||||
@ -77,7 +88,7 @@ targets:
|
||||
|
||||
nexys_video:
|
||||
default_tool: vivado
|
||||
filesets: [core, nexys_video, soc, fpga]
|
||||
filesets: [core, nexys_video, soc, fpga, debug_xilinx]
|
||||
parameters : [memory_size, ram_init_file]
|
||||
tools:
|
||||
vivado: {part : xc7a200tsbg484-1}
|
||||
@ -85,7 +96,7 @@ targets:
|
||||
|
||||
arty_a7-35:
|
||||
default_tool: vivado
|
||||
filesets: [core, arty_a7-35, soc, fpga]
|
||||
filesets: [core, arty_a7-35, soc, fpga, debug_xilinx]
|
||||
parameters : [memory_size, ram_init_file]
|
||||
tools:
|
||||
vivado: {part : xc7a35ticsg324-1L}
|
||||
@ -93,7 +104,7 @@ targets:
|
||||
|
||||
cmod_a7-35:
|
||||
default_tool: vivado
|
||||
filesets: [core, cmod_a7-35, soc, fpga]
|
||||
filesets: [core, cmod_a7-35, soc, fpga, debug_xilinx]
|
||||
parameters : [memory_size, ram_init_file, reset_low=false]
|
||||
tools:
|
||||
vivado: {part : xc7a35tcpg236-1}
|
||||
|
||||
7
scripts/mw_debug/Makefile
Normal file
7
scripts/mw_debug/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
CFLAGS = -O2 -g -Wall -std=c99
|
||||
|
||||
all: mw_debug
|
||||
|
||||
mw_debug: mw_debug.c
|
||||
$(CC) -o $@ $^ -lurjtag
|
||||
|
||||
583
scripts/mw_debug/mw_debug.c
Normal file
583
scripts/mw_debug/mw_debug.c
Normal file
@ -0,0 +1,583 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <getopt.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <urjtag/urjtag.h>
|
||||
|
||||
#define DBG_WB_ADDR 0x00
|
||||
#define DBG_WB_DATA 0x01
|
||||
#define DBG_WB_CTRL 0x02
|
||||
|
||||
#define DBG_CORE_CTRL 0x10
|
||||
#define DBG_CORE_CTRL_STOP (1 << 0)
|
||||
#define DBG_CORE_CTRL_RESET (1 << 1)
|
||||
#define DBG_CORE_CTRL_ICRESET (1 << 2)
|
||||
#define DBG_CORE_CTRL_STEP (1 << 3)
|
||||
#define DBG_CORE_CTRL_START (1 << 4)
|
||||
|
||||
#define DBG_CORE_STAT 0x11
|
||||
#define DBG_CORE_STAT_STOPPING (1 << 0)
|
||||
#define DBG_CORE_STAT_STOPPED (1 << 1)
|
||||
#define DBG_CORE_STAT_TERM (1 << 2)
|
||||
|
||||
#define DBG_CORE_NIA 0x12
|
||||
|
||||
static bool debug;
|
||||
|
||||
struct backend {
|
||||
int (*init)(const char *target);
|
||||
int (*reset)(void);
|
||||
int (*command)(uint8_t op, uint8_t addr, uint64_t *data);
|
||||
};
|
||||
static struct backend *b;
|
||||
|
||||
static void check(int r, const char *failstr)
|
||||
{
|
||||
if (r >= 0)
|
||||
return;
|
||||
fprintf(stderr, "Error %s\n", failstr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* -------------- SIM backend -------------- */
|
||||
|
||||
static int sim_fd = -1;
|
||||
|
||||
static int sim_init(const char *target)
|
||||
{
|
||||
struct sockaddr_in saddr;
|
||||
struct hostent *hp;
|
||||
const char *p, *host;
|
||||
int port, rc;
|
||||
|
||||
if (!target)
|
||||
target = "localhost:13245";
|
||||
p = strchr(target, ':');
|
||||
host = strndup(target, p - target);
|
||||
if (p && *p)
|
||||
p++;
|
||||
else
|
||||
p = "13245";
|
||||
port = strtoul(p, NULL, 10);
|
||||
if (debug)
|
||||
printf("Opening sim backend host '%s' port %d\n", host, port);
|
||||
|
||||
sim_fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (sim_fd < 0) {
|
||||
fprintf(stderr, "Error opening socket: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
hp = gethostbyname(host);
|
||||
if (!hp) {
|
||||
fprintf(stderr,"Unknown host '%s'\n", host);
|
||||
return -1;
|
||||
}
|
||||
memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length);
|
||||
saddr.sin_port = htons(port);
|
||||
saddr.sin_family = PF_INET;
|
||||
rc = connect(sim_fd, (struct sockaddr *)&saddr, sizeof(saddr));
|
||||
if (rc < 0) {
|
||||
close(sim_fd);
|
||||
fprintf(stderr,"Connection to '%s' failed: %s\n",
|
||||
host, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sim_reset(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void add_bits(uint8_t **p, int *b, uint64_t d, int c)
|
||||
{
|
||||
uint8_t md = 1 << *b;
|
||||
uint64_t ms = 1;
|
||||
|
||||
while (c--) {
|
||||
if (d & ms)
|
||||
(**p) |= md;
|
||||
ms <<= 1;
|
||||
if (*b == 7) {
|
||||
*b = 0;
|
||||
(*p)++;
|
||||
md = 1;
|
||||
} else {
|
||||
(*b)++;
|
||||
md <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t read_bits(uint8_t **p, int *b, int c)
|
||||
{
|
||||
uint8_t ms = 1 << *b;
|
||||
uint64_t md = 1;
|
||||
uint64_t d = 0;
|
||||
|
||||
while (c--) {
|
||||
if ((**p) & ms)
|
||||
d |= md;
|
||||
md <<= 1;
|
||||
if (*b == 7) {
|
||||
*b = 0;
|
||||
(*p)++;
|
||||
ms = 1;
|
||||
} else {
|
||||
(*b)++;
|
||||
ms <<= 1;
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
static int sim_command(uint8_t op, uint8_t addr, uint64_t *data)
|
||||
{
|
||||
uint8_t buf[16], *p;
|
||||
uint64_t d = data ? *data : 0;
|
||||
int r, s, b = 0;
|
||||
|
||||
memset(buf, 0, 16);
|
||||
p = buf+1;
|
||||
add_bits(&p, &b, op, 2);
|
||||
add_bits(&p, &b, d, 64);
|
||||
add_bits(&p, &b, addr, 8);
|
||||
if (b)
|
||||
p++;
|
||||
buf[0] = 74;
|
||||
if (0)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<(p-buf); i++)
|
||||
printf("%02x ", buf[i]);
|
||||
printf("\n");
|
||||
}
|
||||
write(sim_fd, buf, p - buf);
|
||||
r = read(sim_fd, buf, 127);
|
||||
if (0 && r > 0) {
|
||||
int i;
|
||||
|
||||
for (i=0; i<r; i++)
|
||||
printf("%02x ", buf[i]);
|
||||
printf("\n");
|
||||
}
|
||||
p = buf+1;
|
||||
b = 0;
|
||||
r = read_bits(&p, &b, 2);
|
||||
if (data)
|
||||
*data = read_bits(&p, &b, 64);
|
||||
return r;
|
||||
}
|
||||
|
||||
static struct backend sim_backend = {
|
||||
.init = sim_init,
|
||||
.reset = sim_reset,
|
||||
.command = sim_command,
|
||||
};
|
||||
|
||||
/* -------------- JTAG backend -------------- */
|
||||
|
||||
static urj_chain_t *jc;
|
||||
|
||||
static int jtag_init(const char *target)
|
||||
{
|
||||
const char *sep;
|
||||
const char *cable;
|
||||
char *params[] = { NULL, };
|
||||
urj_part_t *p;
|
||||
uint32_t id;
|
||||
int rc, part;
|
||||
|
||||
if (!target)
|
||||
target = "DigilentHS1";
|
||||
sep = strchr(target, ':');
|
||||
cable = strndup(target, sep - target);
|
||||
if (sep && *sep) {
|
||||
fprintf(stderr, "jtag cable params not supported yet\n");
|
||||
return -1;
|
||||
}
|
||||
if (debug)
|
||||
printf("Opening jtag backend cable '%s'\n", cable);
|
||||
|
||||
jc = urj_tap_chain_alloc();
|
||||
if (!jc) {
|
||||
fprintf(stderr, "Failed to alloc JTAG\n");
|
||||
return -1;
|
||||
}
|
||||
jc->main_part = 0;
|
||||
|
||||
rc = urj_tap_chain_connect(jc, cable, params);
|
||||
if (rc != URJ_STATUS_OK) {
|
||||
fprintf(stderr, "JTAG cable detect failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* XXX Hard wire part 0, that might need to change (use params and detect !) */
|
||||
rc = urj_tap_manual_add(jc, 6);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "JTAG failed to add part !\n");
|
||||
return -1;
|
||||
}
|
||||
if (jc->parts == NULL || jc->parts->len == 0) {
|
||||
fprintf(stderr, "JTAG Something's wrong after adding part !\n");
|
||||
return -1;
|
||||
}
|
||||
urj_part_parts_set_instruction(jc->parts, "BYPASS");
|
||||
|
||||
jc->active_part = part = 0;
|
||||
|
||||
p = urj_tap_chain_active_part(jc);
|
||||
if (!p) {
|
||||
fprintf(stderr, "Failed to get active JTAG part\n");
|
||||
return -1;
|
||||
}
|
||||
rc = urj_part_data_register_define(p, "IDCODE_REG", 32);
|
||||
if (rc != URJ_STATUS_OK) {
|
||||
fprintf(stderr, "JTAG failed to add IDCODE_REG register !\n");
|
||||
return -1;
|
||||
}
|
||||
if (urj_part_instruction_define(p, "IDCODE", "001001", "IDCODE_REG") == NULL) {
|
||||
fprintf(stderr, "JTAG failed to add IDCODE instruction !\n");
|
||||
return -1;
|
||||
}
|
||||
rc = urj_part_data_register_define(p, "USER2_REG", 74);
|
||||
if (rc != URJ_STATUS_OK) {
|
||||
fprintf(stderr, "JTAG failed to add USER2_REG register !\n");
|
||||
return -1;
|
||||
}
|
||||
if (urj_part_instruction_define(p, "USER2", "000011", "USER2_REG") == NULL) {
|
||||
fprintf(stderr, "JTAG failed to add USER2 instruction !\n");
|
||||
return -1;
|
||||
}
|
||||
urj_part_set_instruction(p, "IDCODE");
|
||||
urj_tap_chain_shift_instructions(jc);
|
||||
urj_tap_chain_shift_data_registers(jc, 1);
|
||||
id = urj_tap_register_get_value(p->active_instruction->data_register->out);
|
||||
printf("Found device ID: 0x%08x\n", id);
|
||||
urj_part_set_instruction(p, "USER2");
|
||||
urj_tap_chain_shift_instructions(jc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jtag_reset(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int jtag_command(uint8_t op, uint8_t addr, uint64_t *data)
|
||||
{
|
||||
urj_part_t *p = urj_tap_chain_active_part(jc);
|
||||
urj_part_instruction_t *insn;
|
||||
urj_data_register_t *dr;
|
||||
uint64_t d = data ? *data : 0;
|
||||
int rc;
|
||||
|
||||
if (!p)
|
||||
return -1;
|
||||
insn = p->active_instruction;
|
||||
if (!insn)
|
||||
return -1;
|
||||
dr = insn->data_register;
|
||||
if (!dr)
|
||||
return -1;
|
||||
rc = urj_tap_register_set_value_bit_range(dr->in, op, 1, 0);
|
||||
if (rc != URJ_STATUS_OK)
|
||||
return -1;
|
||||
rc = urj_tap_register_set_value_bit_range(dr->in, d, 65, 2);
|
||||
if (rc != URJ_STATUS_OK)
|
||||
return -1;
|
||||
rc = urj_tap_register_set_value_bit_range(dr->in, addr, 73, 66);
|
||||
if (rc != URJ_STATUS_OK)
|
||||
return -1;
|
||||
rc = urj_tap_chain_shift_data_registers(jc, 1);
|
||||
if (rc != URJ_STATUS_OK)
|
||||
return -1;
|
||||
rc = urj_tap_register_get_value_bit_range(dr->out, 1, 0);
|
||||
if (data)
|
||||
*data = urj_tap_register_get_value_bit_range(dr->out, 65, 2);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct backend jtag_backend = {
|
||||
.init = jtag_init,
|
||||
.reset = jtag_reset,
|
||||
.command = jtag_command,
|
||||
};
|
||||
|
||||
static int dmi_read(uint8_t addr, uint64_t *data)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = b->command(1, addr, data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
for (;;) {
|
||||
rc = b->command(0, 0, data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (rc == 0)
|
||||
return 0;
|
||||
if (rc != 3)
|
||||
fprintf(stderr, "Unknown status code %d !\n", rc);
|
||||
}
|
||||
}
|
||||
|
||||
static int dmi_write(uint8_t addr, uint64_t data)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = b->command(2, addr, &data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
for (;;) {
|
||||
rc = b->command(0, 0, NULL);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (rc == 0)
|
||||
return 0;
|
||||
if (rc != 3)
|
||||
fprintf(stderr, "Unknown status code %d !\n", rc);
|
||||
}
|
||||
}
|
||||
|
||||
static void core_status(void)
|
||||
{
|
||||
uint64_t stat, nia;
|
||||
const char *statstr, *statstr2;
|
||||
|
||||
check(dmi_read(DBG_CORE_STAT, &stat), "reading core status");
|
||||
check(dmi_read(DBG_CORE_NIA, &nia), "reading core NIA");
|
||||
|
||||
if (debug)
|
||||
printf("Core status = 0x%llx\n", (unsigned long long)stat);
|
||||
statstr = "running";
|
||||
statstr2 = "";
|
||||
if (stat & DBG_CORE_STAT_STOPPED) {
|
||||
statstr = "stopped";
|
||||
if (!(stat & DBG_CORE_STAT_STOPPING))
|
||||
statstr2 = " (restarting?)";
|
||||
else if (stat & DBG_CORE_STAT_TERM)
|
||||
statstr2 = " (terminated)";
|
||||
} else if (stat & DBG_CORE_STAT_STOPPING)
|
||||
statstr = "stopping";
|
||||
else if (stat & DBG_CORE_STAT_TERM)
|
||||
statstr = "odd state (TERM but no STOP)";
|
||||
printf("Core: %s%s\n", statstr, statstr2);
|
||||
printf(" NIA: %016llx\n", (unsigned long long)nia);
|
||||
}
|
||||
|
||||
static void core_stop(void)
|
||||
{
|
||||
check(dmi_write(DBG_CORE_CTRL, DBG_CORE_CTRL_STOP), "stopping core");
|
||||
}
|
||||
|
||||
static void core_start(void)
|
||||
{
|
||||
check(dmi_write(DBG_CORE_CTRL, DBG_CORE_CTRL_START), "starting core");
|
||||
}
|
||||
|
||||
static void core_reset(void)
|
||||
{
|
||||
check(dmi_write(DBG_CORE_CTRL, DBG_CORE_CTRL_START), "resetting core");
|
||||
}
|
||||
|
||||
static void core_step(void)
|
||||
{
|
||||
uint64_t stat;
|
||||
|
||||
check(dmi_read(DBG_CORE_STAT, &stat), "reading core status");
|
||||
|
||||
if (!(stat & DBG_CORE_STAT_STOPPED)) {
|
||||
printf("Core not stopped !\n");
|
||||
return;
|
||||
}
|
||||
check(dmi_write(DBG_CORE_CTRL, DBG_CORE_CTRL_STEP), "stepping core");
|
||||
}
|
||||
|
||||
static void icache_reset(void)
|
||||
{
|
||||
check(dmi_write(DBG_CORE_CTRL, DBG_CORE_CTRL_ICRESET), "resetting icache");
|
||||
}
|
||||
|
||||
static void mem_read(uint64_t addr, uint64_t count)
|
||||
{
|
||||
uint64_t data;
|
||||
int i, rc;
|
||||
|
||||
rc = dmi_write(2, 0x7ff);
|
||||
if (rc < 0)
|
||||
return;
|
||||
rc = dmi_write(0, addr);
|
||||
if (rc < 0)
|
||||
return;
|
||||
for (i = 0; i < count; i++) {
|
||||
rc = dmi_read(1, &data);
|
||||
if (rc < 0)
|
||||
return;
|
||||
printf("%016llx: %016llx\n",
|
||||
(unsigned long long)addr,
|
||||
(unsigned long long)data);
|
||||
addr += 8;
|
||||
}
|
||||
}
|
||||
|
||||
static void load(const char *filename, uint64_t addr)
|
||||
{
|
||||
uint64_t data;
|
||||
int fd, rc, count;
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Failed to open '%s': %s\n", filename, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
// XX dumb, do better
|
||||
rc = dmi_write(2, 0x7ff);
|
||||
if (rc < 0)
|
||||
return;
|
||||
rc = dmi_write(0, addr);
|
||||
if (rc < 0)
|
||||
return;
|
||||
count = 0;
|
||||
for (;;) {
|
||||
data = 0;
|
||||
rc = read(fd, &data, 8);
|
||||
if (rc <= 0)
|
||||
break;
|
||||
// if (rc < 8) XXX fixup endian ?
|
||||
dmi_write(1, data);
|
||||
count += 8;
|
||||
if (!(count % 1024))
|
||||
printf("%x...\n", count);
|
||||
}
|
||||
printf("%x done.\n", count);
|
||||
}
|
||||
|
||||
static void usage(const char *cmd)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <command> <args>\n", cmd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *progname = argv[0];
|
||||
const char *target = NULL;
|
||||
int rc, i = 1;
|
||||
|
||||
b = NULL;
|
||||
|
||||
while(1) {
|
||||
int c, oindex;
|
||||
static struct option lopts[] = {
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ "backend", required_argument, 0, 'b' },
|
||||
{ "target", required_argument, 0, 't' },
|
||||
{ "debug", no_argument, 0, 'd' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
c = getopt_long(argc, argv, "dhb:t:", lopts, &oindex);
|
||||
if (c < 0)
|
||||
break;
|
||||
switch(c) {
|
||||
case 'h':
|
||||
usage(progname);
|
||||
break;
|
||||
case 'b':
|
||||
if (strcmp(optarg, "sim") == 0)
|
||||
b = &sim_backend;
|
||||
else if (strcmp(optarg, "jtag") == 0)
|
||||
b = &jtag_backend;
|
||||
else {
|
||||
fprintf(stderr, "Unknown backend %s\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
target = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
debug = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (b == NULL) {
|
||||
fprintf(stderr, "No backend selected\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rc = b->init(target);
|
||||
if (rc < 0)
|
||||
exit(1);
|
||||
for (i = optind; i < argc; i++) {
|
||||
if (strcmp(argv[i], "dmiread") == 0) {
|
||||
uint8_t addr;
|
||||
uint64_t data;
|
||||
|
||||
if ((i+1) >= argc)
|
||||
usage(argv[0]);
|
||||
addr = strtoul(argv[++i], NULL, 16);
|
||||
dmi_read(addr, &data);
|
||||
printf("%02x: %016llx\n", addr, (unsigned long long)data);
|
||||
} else if (strcmp(argv[i], "dmiwrite") == 0) {
|
||||
uint8_t addr;
|
||||
uint64_t data;
|
||||
|
||||
if ((i+2) >= argc)
|
||||
usage(argv[0]);
|
||||
addr = strtoul(argv[++i], NULL, 16);
|
||||
data = strtoul(argv[++i], NULL, 16);
|
||||
dmi_write(addr, data);
|
||||
} else if (strcmp(argv[i], "creset") == 0) {
|
||||
core_reset();
|
||||
} else if (strcmp(argv[i], "stop") == 0) {
|
||||
core_stop();
|
||||
} else if (strcmp(argv[i], "start") == 0) {
|
||||
core_start();
|
||||
} else if (strcmp(argv[i], "step") == 0) {
|
||||
core_step();
|
||||
} else if (strcmp(argv[i], "quit") == 0) {
|
||||
dmi_write(0xff, 0);
|
||||
} else if (strcmp(argv[i], "status") == 0) {
|
||||
/* do nothing, always done below */
|
||||
} else if (strcmp(argv[i], "mr") == 0) {
|
||||
uint64_t addr, count = 1;
|
||||
|
||||
if ((i+1) >= argc)
|
||||
usage(argv[0]);
|
||||
addr = strtoul(argv[++i], NULL, 16);
|
||||
if (((i+1) < argc) && isdigit(argv[i+1][0]))
|
||||
count = strtoul(argv[++i], NULL, 16);
|
||||
mem_read(addr, count);
|
||||
} else if (strcmp(argv[i], "load") == 0) {
|
||||
const char *filename;
|
||||
uint64_t addr = 0;
|
||||
|
||||
if ((i+1) >= argc)
|
||||
usage(argv[0]);
|
||||
filename = argv[++i];
|
||||
if (((i+1) < argc) && isdigit(argv[i+1][0]))
|
||||
addr = strtoul(argv[++i], NULL, 16);
|
||||
load(filename, addr);
|
||||
} else {
|
||||
fprintf(stderr, "Unknown command %s\n", argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
core_status();
|
||||
return 0;
|
||||
}
|
||||
39
sim-unisim/BSCANE2.vhdl
Normal file
39
sim-unisim/BSCANE2.vhdl
Normal file
@ -0,0 +1,39 @@
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.ALL;
|
||||
|
||||
library unisim;
|
||||
use unisim.vcomponents.all;
|
||||
|
||||
entity BSCANE2 is
|
||||
generic(jtag_chain: INTEGER);
|
||||
port(capture : out std_logic;
|
||||
drck : out std_logic;
|
||||
reset : out std_logic;
|
||||
runtest : out std_logic;
|
||||
sel : out std_logic;
|
||||
shift : out std_logic;
|
||||
tck : out std_logic;
|
||||
tdi : out std_logic;
|
||||
tms : out std_logic;
|
||||
update : out std_logic;
|
||||
tdo : in std_logic
|
||||
);
|
||||
end BSCANE2;
|
||||
|
||||
architecture behaviour of BSCANE2 is
|
||||
alias j : glob_jtag_t is glob_jtag;
|
||||
begin
|
||||
sel <= j.sel(jtag_chain);
|
||||
tck <= j.tck;
|
||||
drck <= tck and sel and (capture or shift);
|
||||
capture <= j.capture;
|
||||
reset <= j.reset;
|
||||
runtest <= j.runtest;
|
||||
shift <= j.shift;
|
||||
tdi <= j.tdi;
|
||||
tms <= j.tms;
|
||||
update <= j.update;
|
||||
j.tdo <= tdo;
|
||||
end architecture behaviour;
|
||||
|
||||
12
sim-unisim/BUFG.vhdl
Normal file
12
sim-unisim/BUFG.vhdl
Normal file
@ -0,0 +1,12 @@
|
||||
library IEEE;
|
||||
use IEEE.std_logic_1164.all;
|
||||
|
||||
entity BUFG is
|
||||
port(I : in std_logic;
|
||||
O : out std_logic
|
||||
);
|
||||
end BUFG;
|
||||
architecture behaviour of BUFG is
|
||||
begin
|
||||
O <= I;
|
||||
end architecture behaviour;
|
||||
45
sim-unisim/unisim_vcomponents.vhdl
Normal file
45
sim-unisim/unisim_vcomponents.vhdl
Normal file
@ -0,0 +1,45 @@
|
||||
library IEEE;
|
||||
use IEEE.std_logic_1164.all;
|
||||
|
||||
package vcomponents is
|
||||
|
||||
-- Global JTAG signals. Xilinx implementation hooks that up to
|
||||
-- their internal JTAG tap, we just expose them for the testbench
|
||||
-- to use. These are used by our BSCANE2 block.
|
||||
--
|
||||
type glob_jtag_t is record
|
||||
reset : std_logic;
|
||||
tck : std_logic;
|
||||
tdo : std_logic;
|
||||
tdi : std_logic;
|
||||
tms : std_logic;
|
||||
sel : std_logic_vector(4 downto 1);
|
||||
capture : std_logic;
|
||||
shift : std_logic;
|
||||
update : std_logic;
|
||||
runtest : std_logic;
|
||||
end record glob_jtag_t;
|
||||
signal glob_jtag : glob_jtag_t;
|
||||
|
||||
component BSCANE2 is
|
||||
generic(jtag_chain: integer);
|
||||
port(capture : out std_logic;
|
||||
drck : out std_logic;
|
||||
reset : out std_logic;
|
||||
runtest : out std_logic;
|
||||
sel : out std_logic;
|
||||
shift : out std_logic;
|
||||
tck : out std_logic;
|
||||
tdi : out std_logic;
|
||||
tms : out std_logic;
|
||||
update : out std_logic;
|
||||
tdo : in std_logic
|
||||
);
|
||||
end component BSCANE2;
|
||||
|
||||
component BUFG is
|
||||
port(I : in std_logic;
|
||||
O : out std_logic
|
||||
);
|
||||
end component BUFG;
|
||||
end package vcomponents;
|
||||
105
sim_jtag.vhdl
Normal file
105
sim_jtag.vhdl
Normal file
@ -0,0 +1,105 @@
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
library work;
|
||||
use work.sim_jtag_socket.all;
|
||||
|
||||
library unisim;
|
||||
use unisim.vcomponents.all;
|
||||
|
||||
entity sim_jtag is
|
||||
end sim_jtag;
|
||||
|
||||
architecture behaviour of sim_jtag is
|
||||
begin
|
||||
jtag: process
|
||||
-- Global JTAG signals (used by BSCANE2 inside dmi_dtm
|
||||
alias j : glob_jtag_t is glob_jtag;
|
||||
|
||||
-- Super fast JTAG clock for sim. For debugging the JTAG module,
|
||||
-- change this to something much larger, for example 60ns, to reflect
|
||||
-- more realistic conditions.
|
||||
constant jclk_period : time := 1 ns;
|
||||
|
||||
-- Polling the socket... this could be made slower when nothing
|
||||
-- is connected once we have that indication from the C code.
|
||||
constant poll_period : time := 100 ns;
|
||||
|
||||
-- Number of dummy JTAG clocks to inject after a command. (I haven't
|
||||
-- got that working with UrJtag but at least with sim, having the
|
||||
-- right number here allows the synchronizers time to complete a
|
||||
-- command on the first message exchange, thus avoiding the need
|
||||
-- for two full shifts for a response.
|
||||
constant dummy_clocks : integer := 80;
|
||||
|
||||
procedure clock(count: in INTEGER) is
|
||||
begin
|
||||
for i in 1 to count loop
|
||||
j.tck <= '0';
|
||||
wait for jclk_period/2;
|
||||
j.tck <= '1';
|
||||
wait for jclk_period/2;
|
||||
end loop;
|
||||
end procedure clock;
|
||||
|
||||
procedure clock_command(cmd: in std_ulogic_vector;
|
||||
rsp: out std_ulogic_vector) is
|
||||
begin
|
||||
j.capture <= '1';
|
||||
clock(1);
|
||||
j.capture <= '0';
|
||||
clock(1);
|
||||
j.shift <= '1';
|
||||
for i in 0 to cmd'length-1 loop
|
||||
j.tdi <= cmd(i);
|
||||
rsp := rsp(1 to rsp'length-1) & j.tdo;
|
||||
clock(1);
|
||||
end loop;
|
||||
j.shift <= '0';
|
||||
j.update <= '1';
|
||||
clock(1);
|
||||
j.update <= '0';
|
||||
clock(1);
|
||||
end procedure clock_command;
|
||||
|
||||
variable cmd : std_ulogic_vector(0 to 247);
|
||||
variable rsp : std_ulogic_vector(0 to 247);
|
||||
variable msize : std_ulogic_vector(7 downto 0);
|
||||
variable size : integer;
|
||||
|
||||
begin
|
||||
|
||||
-- init & reset
|
||||
j.reset <= '1';
|
||||
j.sel <= "0000";
|
||||
j.capture <= '0';
|
||||
j.update <= '0';
|
||||
j.shift <= '0';
|
||||
j.tdi <= '0';
|
||||
j.tms <= '0';
|
||||
j.runtest <= '0';
|
||||
clock(5);
|
||||
j.reset <= '0';
|
||||
clock(5);
|
||||
|
||||
-- select chain USER2
|
||||
-- XXX TODO: Send that via protocol instead
|
||||
-- XXX TODO: Also maybe have the C code tell us if connected or not
|
||||
-- and clock when connected.
|
||||
j.sel <= "0010";
|
||||
clock(1);
|
||||
rsp := (others => '0');
|
||||
while true loop
|
||||
wait for poll_period;
|
||||
sim_jtag_read_msg(cmd, msize);
|
||||
size := to_integer(unsigned(msize));
|
||||
if size /= 0 and size < 248 then
|
||||
clock_command(cmd(0 to size-1),
|
||||
rsp(0 to size-1));
|
||||
sim_jtag_write_msg(rsp, msize);
|
||||
clock(dummy_clocks);
|
||||
end if;
|
||||
end loop;
|
||||
end process;
|
||||
end;
|
||||
24
sim_jtag_socket.vhdl
Normal file
24
sim_jtag_socket.vhdl
Normal file
@ -0,0 +1,24 @@
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
|
||||
package sim_jtag_socket is
|
||||
procedure sim_jtag_read_msg(out_msg : out std_ulogic_vector(247 downto 0);
|
||||
out_size : out std_ulogic_vector(7 downto 0));
|
||||
attribute foreign of sim_jtag_read_msg : procedure is "VHPIDIRECT sim_jtag_read_msg";
|
||||
procedure sim_jtag_write_msg(in_msg : in std_ulogic_vector(247 downto 0);
|
||||
in_size : in std_ulogic_vector(7 downto 0));
|
||||
attribute foreign of sim_jtag_write_msg : procedure is "VHPIDIRECT sim_jtag_write_msg";
|
||||
end sim_jtag_socket;
|
||||
|
||||
package body sim_jtag_socket is
|
||||
procedure sim_jtag_read_msg(out_msg : out std_ulogic_vector(247 downto 0);
|
||||
out_size : out std_ulogic_vector(7 downto 0)) is
|
||||
begin
|
||||
assert false report "VHPI" severity failure;
|
||||
end sim_jtag_read_msg;
|
||||
procedure sim_jtag_write_msg(in_msg : in std_ulogic_vector(247 downto 0);
|
||||
in_size : in std_ulogic_vector(7 downto 0)) is
|
||||
begin
|
||||
assert false report "VHPI" severity failure;
|
||||
end sim_jtag_write_msg;
|
||||
end sim_jtag_socket;
|
||||
222
sim_jtag_socket_c.c
Normal file
222
sim_jtag_socket_c.c
Normal file
@ -0,0 +1,222 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
/* XXX Make that some parameter */
|
||||
#define TCP_PORT 13245
|
||||
#define MAX_PACKET 32
|
||||
|
||||
#define vhpi0 2 /* forcing 0 */
|
||||
#define vhpi1 3 /* forcing 1 */
|
||||
|
||||
static void to_std_logic_vector(unsigned long val, unsigned char *p,
|
||||
unsigned long len)
|
||||
{
|
||||
if (len > 64) {
|
||||
fprintf(stderr, "%s: invalid length %lu\n", __func__, len);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (unsigned long i = 0; i < len; i++) {
|
||||
if ((val >> (len-1-i) & 1))
|
||||
*p = vhpi1;
|
||||
else
|
||||
*p = vhpi0;
|
||||
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t from_std_logic_vector(unsigned char *p, unsigned long len)
|
||||
{
|
||||
unsigned long ret = 0;
|
||||
|
||||
if (len > 64) {
|
||||
fprintf(stderr, "%s: invalid length %lu\n", __func__, len);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (unsigned long i = 0; i < len; i++) {
|
||||
unsigned char bit;
|
||||
|
||||
if (*p == vhpi0) {
|
||||
bit = 0;
|
||||
} else if (*p == vhpi1) {
|
||||
bit = 1;
|
||||
} else {
|
||||
fprintf(stderr, "%s: bad bit %d\n", __func__, *p);
|
||||
bit = 0;
|
||||
}
|
||||
|
||||
ret = (ret << 1) | bit;
|
||||
p++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fd = -1;
|
||||
static int cfd = -1;
|
||||
|
||||
static void open_socket(void)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int opt, rc, flags;
|
||||
|
||||
if (fd >= 0 || fd < -1)
|
||||
return;
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Failed to open debug socket !\r\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
flags = fcntl(fd, F_GETFL);
|
||||
if (flags >= 0)
|
||||
rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
if (flags < 0 || rc < 0) {
|
||||
fprintf(stderr, "Failed to configure debug socket !\r\n");
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(TCP_PORT);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
opt = 1;
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to bind debug socket !\r\n");
|
||||
goto fail;
|
||||
}
|
||||
rc = listen(fd,1);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to listen to debug socket !\r\n");
|
||||
goto fail;
|
||||
}
|
||||
fprintf(stderr, "Debug socket ready\r\n");
|
||||
return;
|
||||
fail:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
fd = -2;
|
||||
}
|
||||
|
||||
static void check_connection(void)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addr_len = sizeof(addr);
|
||||
|
||||
cfd = accept(fd, (struct sockaddr *)&addr, &addr_len);
|
||||
if (cfd < 0)
|
||||
return;
|
||||
fprintf(stderr, "Debug client connected !\r\n");
|
||||
}
|
||||
|
||||
void sim_jtag_read_msg(unsigned char *out_msg, unsigned char *out_size)
|
||||
{
|
||||
unsigned char data[MAX_PACKET];
|
||||
unsigned char size = 0;
|
||||
struct pollfd fdset[1];
|
||||
int rc, i;
|
||||
|
||||
if (fd == -1)
|
||||
open_socket();
|
||||
if (fd < 0)
|
||||
goto finish;
|
||||
if (cfd < 0)
|
||||
check_connection();
|
||||
if (cfd < 0)
|
||||
goto finish;
|
||||
|
||||
memset(fdset, 0, sizeof(fdset));
|
||||
fdset[0].fd = cfd;
|
||||
fdset[0].events = POLLIN;
|
||||
rc = poll(fdset, 1, 0);
|
||||
if (rc <= 0)
|
||||
goto finish;
|
||||
rc = read(cfd, data, MAX_PACKET);
|
||||
if (rc < 0)
|
||||
fprintf(stderr, "Debug read error, assuming client disconnected !\r\n");
|
||||
if (rc == 0)
|
||||
fprintf(stderr, "Debug client disconnected !\r\n");
|
||||
if (rc <= 0) {
|
||||
close(cfd);
|
||||
cfd = -1;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
#if 0
|
||||
fprintf(stderr, "Got message:\n\r");
|
||||
{
|
||||
for (i=0; i<rc; i++)
|
||||
fprintf(stderr, "%02x ", data[i]);
|
||||
fprintf(stderr, "\n\r");
|
||||
}
|
||||
#endif
|
||||
size = data[0]; /* Size in bits */
|
||||
|
||||
/* Special sizes */
|
||||
if (size == 255) {
|
||||
/* JTAG reset, message to translate */
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (((rc - 1) * 8) < size) {
|
||||
fprintf(stderr, "Debug short read: %d bytes for %d bits, truncating\r\n",
|
||||
rc - 1, size);
|
||||
size = (rc - 1) * 8;
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
int byte = i >> 3;
|
||||
int bit = 1 << (i & 7);
|
||||
out_msg[i] = (data[byte+1] & bit) ? vhpi1 : vhpi0;
|
||||
}
|
||||
finish:
|
||||
to_std_logic_vector(size, out_size, 8);
|
||||
}
|
||||
|
||||
void sim_jtag_write_msg(unsigned char *in_msg, unsigned char *in_size)
|
||||
{
|
||||
unsigned char data[MAX_PACKET];
|
||||
unsigned char size;
|
||||
int rc, i;
|
||||
|
||||
size = from_std_logic_vector(in_size, 8);
|
||||
data[0] = size;
|
||||
for (i = 0; i < size; i++) {
|
||||
int byte = i >> 3;
|
||||
int bit = 1 << (i & 7);
|
||||
if (in_msg[i] == vhpi1)
|
||||
data[byte+1] |= bit;
|
||||
else
|
||||
data[byte+1] &= ~bit;
|
||||
}
|
||||
rc = (size + 7) / 8;
|
||||
|
||||
#if 0
|
||||
fprintf(stderr, "Sending response:\n\r");
|
||||
{
|
||||
for (i=0; i<rc; i++)
|
||||
fprintf(stderr, "%02x ", data[i]);
|
||||
fprintf(stderr, "\n\r");
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = write(cfd, data, rc);
|
||||
if (rc < 0)
|
||||
fprintf(stderr, "Debug write error, ignoring\r\n");
|
||||
}
|
||||
|
||||
139
soc.vhdl
139
soc.vhdl
@ -1,8 +1,9 @@
|
||||
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;
|
||||
@ -24,7 +25,10 @@ entity soc is
|
||||
|
||||
-- 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;
|
||||
|
||||
@ -35,6 +39,8 @@ architecture behaviour of soc is
|
||||
signal wishbone_dcore_out : wishbone_master_out;
|
||||
signal wishbone_icore_in : wishbone_slave_out;
|
||||
signal wishbone_icore_out : wishbone_master_out;
|
||||
signal wishbone_debug_in : wishbone_slave_out;
|
||||
signal wishbone_debug_out : wishbone_master_out;
|
||||
|
||||
-- Wishbone master (output of arbiter):
|
||||
signal wb_master_in : wishbone_slave_out;
|
||||
@ -50,10 +56,21 @@ architecture behaviour of soc is
|
||||
signal wb_bram_out : wishbone_slave_out;
|
||||
constant mem_adr_bits : positive := positive(ceil(log2(real(MEMORY_SIZE))));
|
||||
|
||||
-- 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);
|
||||
signal dmi_dout : std_ulogic_vector(63 downto 0);
|
||||
signal dmi_req : std_ulogic;
|
||||
signal dmi_wr : std_ulogic;
|
||||
signal dmi_ack : std_ulogic;
|
||||
|
||||
-- Per slave DMI signals
|
||||
signal dmi_wb_dout : std_ulogic_vector(63 downto 0);
|
||||
signal dmi_wb_req : std_ulogic;
|
||||
signal dmi_wb_ack : std_ulogic;
|
||||
signal dmi_core_dout : std_ulogic_vector(63 downto 0);
|
||||
signal dmi_core_req : std_ulogic;
|
||||
signal dmi_core_ack : std_ulogic;
|
||||
begin
|
||||
|
||||
-- Processor core
|
||||
@ -68,21 +85,22 @@ begin
|
||||
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
|
||||
wishbone_arbiter_0: entity work.wishbone_arbiter
|
||||
port map(
|
||||
clk => system_clk,
|
||||
rst => rst,
|
||||
wb1_in => wishbone_dcore_out,
|
||||
wb1_out => wishbone_dcore_in,
|
||||
wb2_in => wishbone_icore_out,
|
||||
wb2_out => wishbone_icore_in,
|
||||
wb_out => wb_master_out,
|
||||
wb_in => wb_master_in
|
||||
clk => system_clk, rst => rst,
|
||||
wb1_in => wishbone_dcore_out, wb1_out => wishbone_dcore_in,
|
||||
wb2_in => wishbone_icore_out, wb2_out => wishbone_icore_in,
|
||||
wb3_in => wishbone_debug_out, wb3_out => wishbone_debug_in,
|
||||
wb_out => wb_master_out, wb_in => wb_master_in
|
||||
);
|
||||
|
||||
-- Wishbone slaves address decoder & mux
|
||||
@ -122,20 +140,6 @@ begin
|
||||
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
|
||||
@ -174,4 +178,81 @@ begin
|
||||
wishbone_out => wb_bram_out
|
||||
);
|
||||
|
||||
-- DMI(debug bus) <-> JTAG bridge
|
||||
dtm: entity work.dmi_dtm
|
||||
generic map(
|
||||
ABITS => 8,
|
||||
DBITS => 64
|
||||
)
|
||||
port map(
|
||||
sys_clk => system_clk,
|
||||
sys_reset => 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
|
||||
);
|
||||
|
||||
-- DMI interconnect
|
||||
dmi_intercon: process(dmi_addr, dmi_req,
|
||||
dmi_wb_ack, dmi_wb_dout,
|
||||
dmi_core_ack, dmi_core_dout)
|
||||
|
||||
-- DMI address map (each address is a full 64-bit register)
|
||||
--
|
||||
-- Offset: Size: Slave:
|
||||
-- 0 4 Wishbone
|
||||
-- 10 16 Core
|
||||
|
||||
type slave_type is (SLAVE_WB,
|
||||
SLAVE_CORE,
|
||||
SLAVE_NONE);
|
||||
variable slave : slave_type;
|
||||
begin
|
||||
-- Simple address decoder
|
||||
slave := SLAVE_NONE;
|
||||
if std_match(dmi_addr, "000000--") then
|
||||
slave := SLAVE_WB;
|
||||
elsif std_match(dmi_addr, "0001----") then
|
||||
slave := SLAVE_CORE;
|
||||
end if;
|
||||
|
||||
-- DMI muxing
|
||||
dmi_wb_req <= '0';
|
||||
dmi_core_req <= '0';
|
||||
case slave is
|
||||
when SLAVE_WB =>
|
||||
dmi_wb_req <= dmi_req;
|
||||
dmi_ack <= dmi_wb_ack;
|
||||
dmi_din <= dmi_wb_dout;
|
||||
when SLAVE_CORE =>
|
||||
dmi_core_req <= dmi_req;
|
||||
dmi_ack <= dmi_core_ack;
|
||||
dmi_din <= dmi_core_dout;
|
||||
when others =>
|
||||
dmi_ack <= dmi_req;
|
||||
dmi_din <= (others => '1');
|
||||
end case;
|
||||
|
||||
-- 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
|
||||
port map(clk => system_clk, rst => rst,
|
||||
dmi_addr => dmi_addr(1 downto 0),
|
||||
dmi_dout => dmi_wb_dout,
|
||||
dmi_din => dmi_dout,
|
||||
dmi_wr => dmi_wr,
|
||||
dmi_ack => dmi_wb_ack,
|
||||
dmi_req => dmi_wb_req,
|
||||
wb_in => wishbone_debug_in,
|
||||
wb_out => wishbone_debug_out);
|
||||
|
||||
|
||||
end architecture behaviour;
|
||||
|
||||
@ -4,54 +4,76 @@ use ieee.std_logic_1164.all;
|
||||
library work;
|
||||
use work.wishbone_types.all;
|
||||
|
||||
-- TODO: Use an array of master/slaves with parametric size
|
||||
entity wishbone_arbiter is
|
||||
port (
|
||||
clk : in std_ulogic;
|
||||
rst : in std_ulogic;
|
||||
port (clk : in std_ulogic;
|
||||
rst : in std_ulogic;
|
||||
|
||||
wb1_in : in wishbone_master_out;
|
||||
wb1_out : out wishbone_slave_out;
|
||||
wb1_in : in wishbone_master_out;
|
||||
wb1_out : out wishbone_slave_out;
|
||||
|
||||
wb2_in : in wishbone_master_out;
|
||||
wb2_out : out wishbone_slave_out;
|
||||
wb2_in : in wishbone_master_out;
|
||||
wb2_out : out wishbone_slave_out;
|
||||
|
||||
wb_out : out wishbone_master_out;
|
||||
wb_in : in wishbone_slave_out
|
||||
);
|
||||
wb3_in : in wishbone_master_out;
|
||||
wb3_out : out wishbone_slave_out;
|
||||
|
||||
wb_out : out wishbone_master_out;
|
||||
wb_in : in wishbone_slave_out
|
||||
);
|
||||
end wishbone_arbiter;
|
||||
|
||||
architecture behave of wishbone_arbiter is
|
||||
type wishbone_arbiter_state_t is (IDLE, WB1_BUSY, WB2_BUSY);
|
||||
type wishbone_arbiter_state_t is (IDLE, WB1_BUSY, WB2_BUSY, WB3_BUSY);
|
||||
signal state : wishbone_arbiter_state_t := IDLE;
|
||||
begin
|
||||
wb1_out <= wb_in when state = WB1_BUSY else wishbone_slave_out_init;
|
||||
wb2_out <= wb_in when state = WB2_BUSY else wishbone_slave_out_init;
|
||||
|
||||
wb_out <= wb1_in when state = WB1_BUSY else wb2_in when state = WB2_BUSY else wishbone_master_out_init;
|
||||
wishbone_muxes: process(state, wb_in, wb1_in, wb2_in, wb3_in)
|
||||
begin
|
||||
-- Requests from masters are fully muxed
|
||||
wb_out <= wb1_in when state = WB1_BUSY else
|
||||
wb2_in when state = WB2_BUSY else
|
||||
wb3_in when state = WB3_BUSY else
|
||||
wishbone_master_out_init;
|
||||
|
||||
-- Responses from slave don't need to mux the data bus
|
||||
wb1_out.dat <= wb_in.dat;
|
||||
wb2_out.dat <= wb_in.dat;
|
||||
wb3_out.dat <= wb_in.dat;
|
||||
wb1_out.ack <= wb_in.ack when state = WB1_BUSY else '0';
|
||||
wb2_out.ack <= wb_in.ack when state = WB2_BUSY else '0';
|
||||
wb3_out.ack <= wb_in.ack when state = WB3_BUSY else '0';
|
||||
end process;
|
||||
|
||||
wishbone_arbiter_process: process(clk)
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
if rst = '1' then
|
||||
state <= IDLE;
|
||||
else
|
||||
case state is
|
||||
when IDLE =>
|
||||
if wb1_in.cyc = '1' then
|
||||
state <= WB1_BUSY;
|
||||
elsif wb2_in.cyc = '1' then
|
||||
state <= WB2_BUSY;
|
||||
end if;
|
||||
when WB1_BUSY =>
|
||||
if wb1_in.cyc = '0' then
|
||||
state <= IDLE;
|
||||
end if;
|
||||
when WB2_BUSY =>
|
||||
if wb2_in.cyc = '0' then
|
||||
state <= IDLE;
|
||||
end if;
|
||||
end case;
|
||||
end if;
|
||||
end if;
|
||||
if rising_edge(clk) then
|
||||
if rst = '1' then
|
||||
state <= IDLE;
|
||||
else
|
||||
case state is
|
||||
when IDLE =>
|
||||
if wb1_in.cyc = '1' then
|
||||
state <= WB1_BUSY;
|
||||
elsif wb2_in.cyc = '1' then
|
||||
state <= WB2_BUSY;
|
||||
elsif wb3_in.cyc = '1' then
|
||||
state <= WB3_BUSY;
|
||||
end if;
|
||||
when WB1_BUSY =>
|
||||
if wb1_in.cyc = '0' then
|
||||
state <= IDLE;
|
||||
end if;
|
||||
when WB2_BUSY =>
|
||||
if wb2_in.cyc = '0' then
|
||||
state <= IDLE;
|
||||
end if;
|
||||
when WB3_BUSY =>
|
||||
if wb3_in.cyc = '0' then
|
||||
state <= IDLE;
|
||||
end if;
|
||||
end case;
|
||||
end if;
|
||||
end if;
|
||||
end process;
|
||||
end behave;
|
||||
|
||||
167
wishbone_debug_master.vhdl
Normal file
167
wishbone_debug_master.vhdl
Normal file
@ -0,0 +1,167 @@
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
library work;
|
||||
use work.wishbone_types.all;
|
||||
|
||||
entity wishbone_debug_master is
|
||||
port(clk : in std_ulogic;
|
||||
rst : in std_ulogic;
|
||||
|
||||
-- Debug bus interface
|
||||
dmi_addr : in std_ulogic_vector(1 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;
|
||||
|
||||
-- Wishbone master interface
|
||||
wb_out : out wishbone_master_out;
|
||||
wb_in : in wishbone_slave_out
|
||||
);
|
||||
end entity wishbone_debug_master;
|
||||
|
||||
architecture behaviour of wishbone_debug_master is
|
||||
|
||||
-- ** Register offsets definitions. All registers are 64-bit
|
||||
constant DBG_WB_ADDR : std_ulogic_vector(1 downto 0) := "00";
|
||||
constant DBG_WB_DATA : std_ulogic_vector(1 downto 0) := "01";
|
||||
constant DBG_WB_CTRL : std_ulogic_vector(1 downto 0) := "10";
|
||||
constant DBG_WB_RSVD : std_ulogic_vector(1 downto 0) := "11";
|
||||
|
||||
-- CTRL register:
|
||||
--
|
||||
-- bit 0..7 : SEL bits (byte enables)
|
||||
-- bit 8 : address auto-increment
|
||||
-- bit 10..9 : auto-increment value:
|
||||
-- 00 - +1
|
||||
-- 01 - +2
|
||||
-- 10 - +4
|
||||
-- 11 - +8
|
||||
|
||||
-- ** Address and control registers and read data
|
||||
signal reg_addr : std_ulogic_vector(63 downto 0);
|
||||
signal reg_ctrl_out : std_ulogic_vector(63 downto 0);
|
||||
signal reg_ctrl : std_ulogic_vector(10 downto 0);
|
||||
signal data_latch : std_ulogic_vector(63 downto 0);
|
||||
|
||||
type state_t is (IDLE, WB_CYCLE, DMI_WAIT);
|
||||
signal state : state_t;
|
||||
|
||||
begin
|
||||
|
||||
-- Hard wire unused bits to 0
|
||||
reg_ctrl_out <= (63 downto 11 => '0',
|
||||
10 downto 0 => reg_ctrl);
|
||||
|
||||
-- DMI read data mux
|
||||
with dmi_addr select dmi_dout <=
|
||||
reg_addr when DBG_WB_ADDR,
|
||||
data_latch when DBG_WB_DATA,
|
||||
reg_ctrl_out when DBG_WB_CTRL,
|
||||
(others => '0') when others;
|
||||
|
||||
-- ADDR and CTRL register writes
|
||||
reg_write : process(clk)
|
||||
subtype autoinc_inc_t is integer range 1 to 8;
|
||||
function decode_autoinc(c : std_ulogic_vector(1 downto 0))
|
||||
return autoinc_inc_t is
|
||||
begin
|
||||
case c is
|
||||
when "00" => return 1;
|
||||
when "01" => return 2;
|
||||
when "10" => return 4;
|
||||
when "11" => return 8;
|
||||
-- Below shouldn't be necessary but GHDL complains
|
||||
when others => return 8;
|
||||
end case;
|
||||
end function decode_autoinc;
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
if (rst) then
|
||||
reg_addr <= (others => '0');
|
||||
reg_ctrl <= (others => '0');
|
||||
else -- Standard register writes
|
||||
if dmi_req and dmi_wr then
|
||||
if dmi_addr = DBG_WB_ADDR then
|
||||
reg_addr <= dmi_din;
|
||||
elsif dmi_addr = DBG_WB_CTRL then
|
||||
reg_ctrl <= dmi_din(10 downto 0);
|
||||
end if;
|
||||
end if;
|
||||
-- Address register auto-increment
|
||||
if state = WB_CYCLE and (wb_in.ack and reg_ctrl(8))= '1' then
|
||||
reg_addr <= std_ulogic_vector(unsigned(reg_addr) +
|
||||
decode_autoinc(reg_ctrl(10 downto 9)));
|
||||
end if;
|
||||
end if;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- ACK is hard wired to req for register writes. For data read/writes
|
||||
-- (aka commands), it's sent when the state machine got the WB ack.
|
||||
--
|
||||
-- Note: We never set it to 1, we just pass dmi_req back when acking.
|
||||
-- This fullfills two purposes:
|
||||
--
|
||||
-- * Avoids polluting the ack signal when another DMI slave is
|
||||
-- selected. This allows the decoder to just OR all the acks
|
||||
-- together rather than mux them.
|
||||
--
|
||||
-- * Makes ack go down on the same cycle as req goes down, thus
|
||||
-- saving a clock cycle. This is safe because we know that
|
||||
-- the state machine will no longer be in DMI_WAIT state on
|
||||
-- the next cycle, so we won't be bouncing the signal back up.
|
||||
--
|
||||
dmi_ack <= dmi_req when (dmi_addr /= DBG_WB_DATA or state = DMI_WAIT) else '0';
|
||||
|
||||
-- Some WB signals are direct wires from registers or DMI
|
||||
wb_out.adr <= reg_addr;
|
||||
wb_out.dat <= dmi_din;
|
||||
wb_out.sel <= reg_ctrl(7 downto 0);
|
||||
wb_out.we <= dmi_wr;
|
||||
|
||||
-- We always move WB cyc and stb simultaneously (no pipelining yet...)
|
||||
wb_out.cyc <= '1' when state = WB_CYCLE else '0';
|
||||
wb_out.stb <= '1' when state = WB_CYCLE else '0';
|
||||
|
||||
-- Data latch. WB will take the read data away as soon as the cycle
|
||||
-- terminates but we must maintain it on DMI until req goes down, so
|
||||
-- we latch it. (Q: Should we move that latch to dmi_dtm itself ?)
|
||||
--
|
||||
latch_reads : process(clk)
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
if state = WB_CYCLE and wb_in.ack = '1' and dmi_wr = '0' then
|
||||
data_latch <= wb_in.dat;
|
||||
end if;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- Command state machine (generate wb_cyc)
|
||||
wb_trigger : process(clk)
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
if (rst) then
|
||||
state <= IDLE;
|
||||
else
|
||||
case state is
|
||||
when IDLE =>
|
||||
if dmi_req = '1' and dmi_addr = DBG_WB_DATA then
|
||||
state <= WB_CYCLE;
|
||||
end if;
|
||||
when WB_CYCLE =>
|
||||
if wb_in.ack then
|
||||
state <= DMI_WAIT;
|
||||
end if;
|
||||
when DMI_WAIT =>
|
||||
if dmi_req = '0' then
|
||||
state <= IDLE;
|
||||
end if;
|
||||
end case;
|
||||
end if;
|
||||
end if;
|
||||
end process;
|
||||
end architecture behaviour;
|
||||
Loading…
x
Reference in New Issue
Block a user