Wishbone debug module
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>
Tue, 10 Sep 2019 16:31:25 +0000 (17:31 +0100)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Fri, 20 Sep 2019 05:07:49 +0000 (15:07 +1000)
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>
Makefile
dmi_dtm_tb.vhdl
microwatt.core
scripts/mw_debug.py
soc.vhdl
wishbone_debug_master.vhdl [new file with mode: 0644]

index a554529cde02e085e60b2cadf1ba36bfb41c98b5..b675a8f5c8088b0d7c1f5c7de860a2ce5127fdf0 100644 (file)
--- 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)))
 
index d872c138a440a6ef86fa5dee2bd5e0f9a34e4304..fe60c12356d3412ac3c06ca3f35730dd594ce1b2 100644 (file)
@@ -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;
index 04b9d2c34f49ff7ef1a1e291422e1a60050bcfc6..6efe7c9261bc8487d1efd5cca0972b6f7d5ed183 100644 (file)
@@ -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
 
index fe487438d1842d93b28c412413be5c1df61873f8..f22039c01eb186feca2d4cc04f7a113154fb537c 100755 (executable)
@@ -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__":
index 735d86ccc584fcb79eaa641b8685df9b66bf002a..dcc25a791bae54baa654d12c23d08b58b7c4b96b 100644 (file)
--- 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 (file)
index 0000000..51441d5
--- /dev/null
@@ -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;