mirror of
https://github.com/antonblanchard/microwatt.git
synced 2026-03-26 18:13:10 +00:00
Wishbone debug module
This adds a debug module off the DMI (debug) bus which can act as a wishbone master to generate read and write cycles. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
9
Makefile
9
Makefile
@@ -41,12 +41,13 @@ 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 dmi_dtm_xilinx.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
|
||||
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)
|
||||
@@ -79,8 +80,8 @@ 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
|
||||
$(GHDL) -e $(GHDLFLAGS) $@
|
||||
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)))
|
||||
|
||||
|
||||
@@ -50,9 +50,23 @@ begin
|
||||
dmi_ack => dmi_ack
|
||||
);
|
||||
|
||||
-- Dummy loopback until a debug module is present
|
||||
dmi_din <= dmi_dout;
|
||||
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
|
||||
@@ -209,6 +223,28 @@ begin
|
||||
-- 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;
|
||||
|
||||
@@ -25,6 +25,7 @@ filesets:
|
||||
- multiply.vhdl
|
||||
- writeback.vhdl
|
||||
- insn_helpers.vhdl
|
||||
- wishbone_debug_master.vhdl
|
||||
- core.vhdl
|
||||
- icache.vhdl
|
||||
file_type : vhdlSource-2008
|
||||
@@ -32,6 +33,7 @@ filesets:
|
||||
soc:
|
||||
files:
|
||||
- wishbone_arbiter.vhdl
|
||||
- wishbone_debug_master.vhdl
|
||||
- soc.vhdl
|
||||
file_type : vhdlSource-2008
|
||||
|
||||
|
||||
@@ -54,8 +54,49 @@ def main():
|
||||
urc.set_instruction("USER2")
|
||||
urc.shift_ir()
|
||||
|
||||
print("Reading 0x00: %x" % do_read(urc, 0))
|
||||
print("Reading 0xaa: %x" % do_read(urc, 0xaa))
|
||||
print("Reading memory at 0:")
|
||||
do_write(urc, 0, 0)
|
||||
do_write(urc, 2, 0x7ff)
|
||||
print("00: %016x" % do_read(urc, 1))
|
||||
print("08: %016x" % do_read(urc, 1))
|
||||
print("10: %016x" % do_read(urc, 1))
|
||||
print("18: %016x" % do_read(urc, 1))
|
||||
do_write(urc, 0, 0x10)
|
||||
do_write(urc, 1, 0xabcdef0123456789)
|
||||
do_write(urc, 0, 0)
|
||||
do_write(urc, 2, 0x7ff)
|
||||
print("00: %016x" % do_read(urc, 1))
|
||||
print("08: %016x" % do_read(urc, 1))
|
||||
print("10: %016x" % do_read(urc, 1))
|
||||
print("18: %016x" % do_read(urc, 1))
|
||||
|
||||
# urc.set_dr_in(0,73,0);
|
||||
# print("Test DR_IN 1:", urc.get_dr_in_string())
|
||||
# urc.set_dr_in(0xa,3,0);
|
||||
# print("Test DR_IN 2:", urc.get_dr_in_string())
|
||||
# urc.set_dr_in(0x5,7,4);
|
||||
# print("Test DR_IN 3:", urc.get_dr_in_string())
|
||||
# urc.set_dr_in(1,73,73);
|
||||
# print("Test DR_IN 4:", urc.get_dr_in_string())
|
||||
|
||||
# print("Reading ADDR reg: %x" % do_read(urc, 0))
|
||||
# print("Writing all 1's to it:")
|
||||
# do_write(urc, 0, 0xffffffffffffffff)
|
||||
# print("Reading ADDR reg: %x" % do_read(urc, 0))
|
||||
# print("Writing 0xabcdef0123456789 to it:")
|
||||
# do_write(urc, 0, 0xabcdef0123456789)
|
||||
# print("Reading ADDR reg: %x" % do_read(urc, 0))
|
||||
|
||||
|
||||
|
||||
# urc.set_dr_in(0x1,41,0)
|
||||
# print("Sending:", urc.get_dr_in_string())
|
||||
# urc.shift_dr()
|
||||
# urc.set_dr_in(0x0,41,0)
|
||||
# urc.shift_dr()
|
||||
# print("Got1:", urc.get_dr_out_string())
|
||||
# urc.shift_dr()
|
||||
# print("Got2:", hex(urc.get_dr_out()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
19
soc.vhdl
19
soc.vhdl
@@ -92,10 +92,6 @@ begin
|
||||
wb_out => wb_master_out, wb_in => wb_master_in
|
||||
);
|
||||
|
||||
-- Dummy wishbone debug module
|
||||
wishbone_debug_out.cyc <= '0';
|
||||
wishbone_debug_out.stb <= '0';
|
||||
|
||||
-- Wishbone slaves address decoder & mux
|
||||
slave_intercon: process(wb_master_out, wb_bram_out, wb_uart0_out)
|
||||
-- Selected slave
|
||||
@@ -202,8 +198,17 @@ begin
|
||||
dmi_ack => dmi_ack
|
||||
);
|
||||
|
||||
-- Dummy loopback until a debug module is present
|
||||
dmi_din <= dmi_dout;
|
||||
dmi_ack <= dmi_ack;
|
||||
-- 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_din,
|
||||
dmi_din => dmi_dout,
|
||||
dmi_wr => dmi_wr,
|
||||
dmi_ack => dmi_ack,
|
||||
dmi_req => dmi_req,
|
||||
wb_in => wishbone_debug_in,
|
||||
wb_out => wishbone_debug_out);
|
||||
|
||||
|
||||
end architecture behaviour;
|
||||
|
||||
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;
|
||||
Reference in New Issue
Block a user