create new function teststate_check_regs which is called by check_regs
[soc.git] / src / soc / debug / jtag.py
index b1091dac28e31765bdf028191e68bb791894cbed..4b4f17a97d672c3f22b668008a1c058ac2500da6 100644 (file)
@@ -3,7 +3,8 @@
 using Staf Verhaegen (Chips4Makers) wishbone TAP
 """
 
-from nmigen import (Module, Signal, Elaboratable)
+from collections import OrderedDict
+from nmigen import (Module, Signal, Elaboratable, Cat)
 from nmigen.cli import rtlil
 from c4m.nmigen.jtag.tap import IOType
 from soc.debug.dmi import  DMIInterface, DBGCore
@@ -12,72 +13,129 @@ from soc.debug.dmi2jtag import DMITAP
 # map from pinmux to c4m jtag iotypes
 iotypes = {'-': IOType.In,
            '+': IOType.Out,
-           '*': IOType.InTriOut}
+           '>': IOType.TriOut,
+           '*': IOType.InTriOut,
+        }
+
+scanlens = {IOType.In: 1,
+           IOType.Out: 1,
+           IOType.TriOut: 2,
+           IOType.InTriOut: 3,
+            }
+
+def dummy_pinset():
+    # sigh this needs to come from pinmux.
+    gpios = []
+    for i in range(16):
+        gpios.append("%d*" % i)
+    return {'uart': ['tx+', 'rx-'],
+             'gpio': gpios,
+             'i2c': ['sda*', 'scl+']}
 
 # TODO: move to suitable location
 class Pins:
-
-    def __init__(self):
-
-        # sigh this needs to come from pinmux.
-        gpios = []
-        for i in range(16):
-            gpios.append("gpio%d*" % i)
-        self.io_names = {'serial': ['tx+', 'rx-'], 'gpio': gpios}
+    """declare a list of pins, including name and direction.  grouped by fn
+    the pin dictionary needs to be in a reliable order so that the JTAG
+    Boundary Scan is also in a reliable order
+    """
+    def __init__(self, pindict):
+        self.io_names = OrderedDict()
+        if isinstance(pindict, OrderedDict):
+            self.io_names.update(pindict)
+        else:
+            keys = list(pindict.keys())
+            keys.sort()
+            for k in keys:
+                self.io_names[k] = pindict[k]
 
     def __iter__(self):
-        # start parsing io_names and create IOConn Records
+        # start parsing io_names and enumerate them to return pin specs
+        scan_idx = 0
         for fn, pins in self.io_names.items():
             for pin in pins:
                 # decode the pin name and determine the c4m jtag io type
                 name, pin_type = pin[:-1], pin[-1]
                 iotype = iotypes[pin_type]
                 pin_name = "%s_%s" % (fn, name)
-                yield (fn, name, iotype, pin_name)
+                yield (fn, name, iotype, pin_name, scan_idx)
+                scan_idx += scanlens[iotype] # inc boundary reg scan offset
+
 
 class JTAG(DMITAP, Pins):
-    def __init__(self):
+    # 32-bit data width here so that it matches with litex
+    def __init__(self, pinset, domain, wb_data_wid=32):
+        self.domain = domain
         DMITAP.__init__(self, ir_width=4)
-        Pins.__init__(self)
-
-        # sigh this needs to come from pinmux.
-        gpios = []
-        for i in range(16):
-            gpios.append("gpio%d*" % i)
-        self.io_names = {'serial': ['tx+', 'rx-'], 'gpio': gpios}
-
-        # start parsing io_names and create IOConn Records
-        self.ios = []
-        for fn, pin, iotype, pin_name in list(self):
-            self.ios.append(self.add_io(iotype=iotype, name=pin_name))
+        Pins.__init__(self, pinset)
+
+        # enumerate pin specs and create IOConn Records.
+        # we store the boundary scan register offset in the IOConn record
+        self.ios = [] # these are enumerated in external_ports
+        self.scan_len = 0
+        for fn, pin, iotype, pin_name, scan_idx in list(self):
+            io = self.add_io(iotype=iotype, name=pin_name)
+            io._scan_idx = scan_idx # hmm shouldn't really do this
+            self.scan_len += scan_idx # record full length of boundary scan
+            self.ios.append(io)
 
         # this is redundant.  or maybe part of testing, i don't know.
-        self.sr = self.add_shiftreg(ircode=4, length=3)
+        self.sr = self.add_shiftreg(ircode=4, length=3,
+                                    domain=domain)
 
-        # create and connect wishbone 
-        self.wb = self.add_wishbone(ircodes=[5, 6, 7],
-                                   address_width=29, data_width=64,
-                                   name="jtag_wb")
+        # create and connect wishbone
+        self.wb = self.add_wishbone(ircodes=[5, 6, 7], features={'err'},
+                                   address_width=30, data_width=wb_data_wid,
+                                   granularity=8, # 8-bit wide
+                                   name="jtag_wb",
+                                   domain=domain)
 
         # create DMI2JTAG (goes through to dmi_sim())
-        self.dmi = self.add_dmi(ircodes=[8, 9, 10])
+        self.dmi = self.add_dmi(ircodes=[8, 9, 10],
+                                    domain=domain)
+
+        # use this for enable/disable of parts of the ASIC.
+        # XXX make sure to add the _en sig to en_sigs list
+        self.wb_icache_en = Signal(reset=1)
+        self.wb_dcache_en = Signal(reset=1)
+        self.wb_sram_en = Signal(reset=1)
+        self.en_sigs = en_sigs = Cat(self.wb_icache_en, self.wb_dcache_en,
+                                     self.wb_sram_en)
+        self.sr_en = self.add_shiftreg(ircode=11, length=len(en_sigs),
+                                       domain=domain)
 
     def elaborate(self, platform):
         m = super().elaborate(platform)
         m.d.comb += self.sr.i.eq(self.sr.o) # loopback as part of test?
+
+        # provide way to enable/disable wishbone caches and SRAM
+        # just in case of issues
+        # see https://bugs.libre-soc.org/show_bug.cgi?id=520
+        with m.If(self.sr_en.oe):
+            m.d.sync += self.en_sigs.eq(self.sr_en.o)
+        # also make it possible to read the enable/disable current state
+        with m.If(self.sr_en.ie):
+            m.d.comb += self.sr_en.i.eq(self.en_sigs)
+
+        # create a fake "stall"
+        #wb = self.wb
+        #m.d.comb += wb.stall.eq(wb.cyc & ~wb.ack) # No burst support
+
         return m
 
     def external_ports(self):
-        ports = super().external_ports()
-        ports += list(self.wb.fields.values())
+        """create a list of ports that goes into the top level il (or verilog)
+        """
+        ports = super().external_ports()           # gets JTAG signal names
+        ports += list(self.wb.fields.values())     # wishbone signals
         for io in self.ios:
-            ports += list(io.core.fields.values())
-            ports += list(io.pad.fields.values())
+            ports += list(io.core.fields.values()) # io "core" signals
+            ports += list(io.pad.fields.values())  # io "pad" signals"
         return ports
 
 
 if __name__ == '__main__':
-    dut = JTAG()
+    pinset = dummy_pinset()
+    dut = JTAG(pinset)
 
     vl = rtlil.convert(dut)
     with open("test_jtag.il", "w") as f: