soc/cores/jtag: add Xilinx JTAG TAPs support and simple JTAG PHY (can be used for...
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Fri, 6 Sep 2019 09:55:41 +0000 (11:55 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Fri, 6 Sep 2019 09:55:41 +0000 (11:55 +0200)
litex/soc/cores/jtag.py

index 6140649b6bb4b55a06dfe297dc5d1b9f176cf3df..dfc1fd71e82813669f9faee35924d2be3065cef5 100644 (file)
@@ -1,8 +1,10 @@
-# This file is Copyright (c) 2019 Antti Lukats <antti.lukats@gmail.com>$
 # This file is Copyright (c) 2019 Florent Kermarrec <florent@enjoy-digital.fr>
+# This file is Copyright (c) 2019 Antti Lukats <antti.lukats@gmail.com>
+# This file is Copyright (c) 2017 Robert Jordens <jordens@gmail.com>
 # License: BSD
 
 from migen import *
+from migen.genlib.cdc import AsyncResetSynchronizer
 
 from litex.soc.interconnect import stream
 
@@ -10,7 +12,7 @@ from litex.soc.interconnect import stream
 
 class JTAGAtlantic(Module):
     def __init__(self):
-        self.sink = sink = stream.Endpoint([("data", 8)])
+        self.sink   =   sink = stream.Endpoint([("data", 8)])
         self.source = source = stream.Endpoint([("data", 8)])
 
         # # #
@@ -32,3 +34,143 @@ class JTAGAtlantic(Module):
             i_t_dav=source.ready,
             o_t_ena=source.valid,
         )
+
+# Xilinx JTAG --------------------------------------------------------------------------------------
+
+class XilinxJTAG(Module):
+    def __init__(self, primitive, chain=1):
+        self.reset   = Signal()
+        self.capture = Signal()
+        self.shift   = Signal()
+        self.update  = Signal()
+
+        self.tck = Signal()
+        self.tms = Signal()
+        self.tdi = Signal()
+        self.tdo = Signal()
+
+        # # #
+
+        self.specials += \
+            Instance(primitive,
+                p_JTAG_CHAIN=chain,
+
+                o_RESET=self.reset,
+                o_CAPTURE=self.capture,
+                o_SHIFT=self.shift,
+                o_UPDATE=self.update,
+
+                o_TCK=self.tck,
+                o_TMS=self.tms,
+                o_TDI=self.tdi,
+                i_TDO=self.tdo,
+            )
+
+class S6JTAG(XilinxJTAG):
+    def __init__(self, *args, **kwargs):
+        XilinxJTAG.__init__(self, primitive="BSCAN_SPARTAN6", *args, **kwargs)
+
+
+class S7JTAG(XilinxJTAG):
+    def __init__(self, *args, **kwargs):
+        XilinxJTAG.__init__(self, primitive="BSCANE2", *args, **kwargs)
+
+
+class USJTAG(XilinxJTAG):
+    def __init__(self, *args, **kwargs):
+        XilinxJTAG.__init__(self, primitive="BSCANE2", *args, **kwargs)
+
+# JTAG PHY -----------------------------------------------------------------------------------------
+
+class JTAGPHY(Module):
+    def __init__(self, jtag=None, device=None, data_width=8, clock_domain="sys"):
+        """JTAG PHY
+
+        Provides a simple JTAG to LiteX stream module to easily stream data to/from the FPGA
+        over JTAG.
+
+        Wire format: data_width + 2 bits, LSB first.
+
+        Host to Target:
+          - TX ready : bit 0
+          - RX data: : bit 1 to data_width
+          - RX valid : bit data_width + 1
+
+        Target to Host:
+          - RX ready : bit 0
+          - TX data  : bit 1 to data_width
+          - TX valid : bit data_width + 1
+        """
+        self.sink   =   sink = stream.Endpoint([("data", data_width)])
+        self.source = source = stream.Endpoint([("data", data_width)])
+
+        # # #
+
+        valid = Signal()
+        data  = Signal(data_width)
+        count = Signal(max=data_width)
+
+        # JTAG TAP ---------------------------------------------------------------------------------
+        if jtag is None:
+            if device[:3] == "xc6":
+                jtag = S6JTAG()
+            elif device[:3] == "xc7":
+                jtag = S7JTAG()
+            elif device[:4] in ["xcku", "xcvu"]:
+                jtag = USJTAG()
+            else:
+                raise NotImplementedError
+            self.submodules += jtag
+
+        # JTAG clock domain ------------------------------------------------------------------------
+        self.clock_domains.cd_jtag = ClockDomain()
+        self.comb += ClockSignal("jtag").eq(jtag.tck)
+        self.specials += AsyncResetSynchronizer(self.cd_jtag, ResetSignal("sys"))
+
+        # JTAG clock domain crossing ---------------------------------------------------------------
+        if clock_domain != "jtag":
+            tx_cdc = stream.AsyncFIFO([("data", data_width)], 4)
+            tx_cdc = ClockDomainsRenamer({"write": clock_domain, "read": "jtag"})(tx_cdc)
+            rx_cdc = stream.AsyncFIFO([("data", data_width)], 4)
+            rx_cdc = ClockDomainsRenamer({"write": "jtag", "read": clock_domain})(rx_cdc)
+            self.submodules += tx_cdc, rx_cdc
+            self.comb += [
+                sink.connect(tx_cdc.sink),
+                rx_cdc.source.connect(source)
+            ]
+            sink, source = tx_cdc.source, rx_cdc.sink
+
+        # JTAG Xfer FSM ----------------------------------------------------------------------------
+        fsm = FSM(reset_state="XFER-READY")
+        fsm = ClockDomainsRenamer("jtag")(fsm)
+        fsm = ResetInserter()(fsm)
+        self.submodules += fsm
+        self.comb += fsm.reset.eq(jtag.reset | jtag.capture)
+        fsm.act("XFER-READY",
+            jtag.tdo.eq(source.ready),
+            If(jtag.shift,
+                sink.ready.eq(jtag.tdi),
+                NextValue(valid, sink.valid),
+                NextValue(data,  sink.data),
+                NextValue(count, 0),
+                NextState("XFER-DATA")
+            )
+        )
+        fsm.act("XFER-DATA",
+            jtag.tdo.eq(data),
+            If(jtag.shift,
+                NextValue(count, count + 1),
+                NextValue(data, Cat(data[1:], jtag.tdi)),
+                If(count == (data_width - 1),
+                    NextState("XFER-VALID")
+                )
+            )
+        )
+        fsm.act("XFER-VALID",
+            jtag.tdo.eq(valid),
+            If(jtag.shift,
+                source.valid.eq(jtag.tdi),
+                NextState("XFER-READY")
+            )
+        )
+        self.comb += source.data.eq(data)