From b46f81fae4c2700547ef791606fe20ed71c4fa81 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 10 Sep 2019 17:31:25 +0100 Subject: [PATCH] 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 --- Makefile | 9 +- dmi_dtm_tb.vhdl | 42 +++++++++- microwatt.core | 2 + scripts/mw_debug.py | 45 +++++++++- soc.vhdl | 19 +++-- wishbone_debug_master.vhdl | 167 +++++++++++++++++++++++++++++++++++++ 6 files changed, 268 insertions(+), 16 deletions(-) create mode 100644 wishbone_debug_master.vhdl diff --git a/Makefile b/Makefile index a554529..b675a8f 100644 --- a/Makefile +++ b/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))) diff --git a/dmi_dtm_tb.vhdl b/dmi_dtm_tb.vhdl index d872c13..fe60c12 100644 --- a/dmi_dtm_tb.vhdl +++ b/dmi_dtm_tb.vhdl @@ -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; diff --git a/microwatt.core b/microwatt.core index 04b9d2c..6efe7c9 100644 --- a/microwatt.core +++ b/microwatt.core @@ -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 diff --git a/scripts/mw_debug.py b/scripts/mw_debug.py index fe48743..f22039c 100755 --- a/scripts/mw_debug.py +++ b/scripts/mw_debug.py @@ -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__": diff --git a/soc.vhdl b/soc.vhdl index 735d86c..dcc25a7 100644 --- a/soc.vhdl +++ b/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; diff --git a/wishbone_debug_master.vhdl b/wishbone_debug_master.vhdl new file mode 100644 index 0000000..51441d5 --- /dev/null +++ b/wishbone_debug_master.vhdl @@ -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;