initial commit of JTAGToDMI debug interface translated from
authorCole Poirier <colepoirier@gmail.com>
Wed, 16 Sep 2020 20:48:08 +0000 (13:48 -0700)
committerCole Poirier <colepoirier@gmail.com>
Wed, 16 Sep 2020 20:48:08 +0000 (13:48 -0700)
microwatt/dmi_dtm_xilinx.vhdl

src/soc/experiment/dmi_dtm_xilinx.py [new file with mode: 0644]

diff --git a/src/soc/experiment/dmi_dtm_xilinx.py b/src/soc/experiment/dmi_dtm_xilinx.py
new file mode 100644 (file)
index 0000000..13ad20c
--- /dev/null
@@ -0,0 +1,296 @@
+"""JTAGToDMI
+
+based on Anton Blanchard microwatt dmi_dtm_xilinx.vhdl
+
+"""
+
+from enum import Enum, unique
+from nmigen import (Module, Signal, Elaboratable, Cat, Signal)
+from nmigen.cli import main
+from nmigen.cli import rtlil
+from nmutil.iocontrol import RecordObject
+from nmutil.byterev import byte_reverse
+from nmutil.mask import Mask
+from nmigen.util import log2_int
+
+# -- 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";
+#
+#     attribute ASYNC_REG : string;
+#     attribute ASYNC_REG of jtag_req_0: signal is "TRUE";
+#     attribute ASYNC_REG of jtag_req_1: signal is "TRUE";
+#     attribute ASYNC_REG of dmi_ack_0: signal is "TRUE";
+#     attribute ASYNC_REG of dmi_ack_1: signal is "TRUE";
+# 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');
+#          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;