redo JTAG to not use Pins clas, it is by pinspec
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Mon, 15 Nov 2021 14:15:01 +0000 (14:15 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Mon, 15 Nov 2021 14:15:01 +0000 (14:15 +0000)
not by resources

therefore forget Pins class and wire up jtag IOConn directly by calling
C4MJTAG.add_io() directly

next step is to wire up the Shift Register stuff

src/spec/jtag.py
src/spec/testing_stage1.py

index f5feec50beca142b854cf4f5ae5dac1d3a2834ee..efda2806c07e6f01f3e8501e9bcdd8245fc63991 100644 (file)
@@ -15,6 +15,12 @@ iotypes = {'-': IOType.In,
            '*': IOType.InTriOut,
         }
 
            '*': IOType.InTriOut,
         }
 
+resiotypes = {'i': IOType.In,
+           'o': IOType.Out,
+           'oe': IOType.TriOut,
+           'io': IOType.InTriOut,
+        }
+
 scanlens = {IOType.In: 1,
            IOType.Out: 1,
            IOType.TriOut: 2,
 scanlens = {IOType.In: 1,
            IOType.Out: 1,
            IOType.TriOut: 2,
@@ -30,13 +36,16 @@ def dummy_pinset():
              'gpio': gpios,
              'i2c': ['sda*', 'scl+']}
 
              'gpio': gpios,
              'i2c': ['sda*', 'scl+']}
 
+
 # TODO: move to suitable location
 class Pins:
     """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
     """
 # TODO: move to suitable location
 class Pins:
     """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):
+    def __init__(self, pindict=None):
+        if pindict is None:
+            pindict = {}
         self.io_names = OrderedDict()
         if isinstance(pindict, OrderedDict):
             self.io_names.update(pindict)
         self.io_names = OrderedDict()
         if isinstance(pindict, OrderedDict):
             self.io_names.update(pindict)
@@ -68,13 +77,13 @@ class JTAG(TAP, Pins):
 
         # enumerate pin specs and create IOConn Records.
         # we store the boundary scan register offset in the IOConn record
 
         # 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.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.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)
+            self.ios[pin_name] = io
 
         # this is redundant.  or maybe part of testing, i don't know.
         self.sr = self.add_shiftreg(ircode=4, length=3,
 
         # this is redundant.  or maybe part of testing, i don't know.
         self.sr = self.add_shiftreg(ircode=4, length=3,
@@ -125,7 +134,7 @@ class JTAG(TAP, Pins):
         """
         ports = super().external_ports()           # gets JTAG signal names
         ports += list(self.wb.fields.values())     # wishbone signals
         """
         ports = super().external_ports()           # gets JTAG signal names
         ports += list(self.wb.fields.values())     # wishbone signals
-        for io in self.ios:
+        for io in self.ios.values():
             ports += list(io.core.fields.values()) # io "core" signals
             ports += list(io.pad.fields.values())  # io "pad" signals"
         return ports
             ports += list(io.core.fields.values()) # io "core" signals
             ports += list(io.pad.fields.values())  # io "pad" signals"
         return ports
index 0767e84498f1f228b9ea4d9a792420485bf9af08..5349ee9ab23564d03f5d946e01f11b98b1192259 100644 (file)
@@ -4,7 +4,7 @@ from nmigen.build.plat import TemplatedPlatform
 from nmigen.build.res import ResourceManager, ResourceError
 from nmigen import Elaboratable, Signal, Module, Instance
 from collections import OrderedDict
 from nmigen.build.res import ResourceManager, ResourceError
 from nmigen import Elaboratable, Signal, Module, Instance
 from collections import OrderedDict
-from jtag import JTAG
+from jtag import JTAG, resiotypes
 from copy import deepcopy
 
 # Was thinking of using these functions, but skipped for simplicity for now
 from copy import deepcopy
 
 # Was thinking of using these functions, but skipped for simplicity for now
@@ -99,7 +99,7 @@ def I2CResource(*args, scl, sda):
 # and can't have one until a clock has been established by ASICPlatform.
 class Blinker(Elaboratable):
     def __init__(self, pinset):
 # and can't have one until a clock has been established by ASICPlatform.
 class Blinker(Elaboratable):
     def __init__(self, pinset):
-        self.jtag = JTAG(pinset, "sync")
+        self.jtag = JTAG({}, "sync")
 
     def elaborate(self, platform):
         m = Module()
 
     def elaborate(self, platform):
         m = Module()
@@ -155,16 +155,23 @@ class ASICPlatform(TemplatedPlatform):
     toolchain = None
     default_clk = "clk" # should be picked up / overridden by platform sys.clk
     default_rst = "rst" # should be picked up / overridden by platform sys.rst
     toolchain = None
     default_clk = "clk" # should be picked up / overridden by platform sys.clk
     default_rst = "rst" # should be picked up / overridden by platform sys.rst
-    def __init__(self, pinset):
+
+    def __init__(self, resources, jtag):
         self.pad_mgr = ResourceManager([], [])
         self.pad_mgr = ResourceManager([], [])
+        self.jtag = jtag
         super().__init__()
         # create set of pin resources based on the pinset, this is for the core
         super().__init__()
         # create set of pin resources based on the pinset, this is for the core
-        resources = create_resources(pinset)
         self.add_resources(resources)
         # record resource lookup between core IO names and pads
         self.padlookup = {}
 
     def request(self, name, number=0, *, dir=None, xdr=None):
         self.add_resources(resources)
         # record resource lookup between core IO names and pads
         self.padlookup = {}
 
     def request(self, name, number=0, *, dir=None, xdr=None):
+        """request a Resource (e.g. name="uart", number=0) which will
+        return a data structure containing Records of all the pins.
+
+        this override will also - automatically - create a JTAG Boundary Scan
+        connection *without* any change to the actual Platform.request() API
+        """
         # okaaaay, bit of shenanigens going on: the important data structure
         # here is Resourcemanager._ports.  requests add to _ports, which is
         # what needs redirecting.  therefore what has to happen is to
         # okaaaay, bit of shenanigens going on: the important data structure
         # here is Resourcemanager._ports.  requests add to _ports, which is
         # what needs redirecting.  therefore what has to happen is to
@@ -192,16 +199,29 @@ class ASICPlatform(TemplatedPlatform):
         print ("core", core)
         print ("pads", pads)
 
         print ("core", core)
         print ("pads", pads)
 
-        # each of these returns a tuple (res, pin, port, attrs)
+        # pad/core each return a list of tuples of (res, pin, port, attrs)
         for pad, core in zip(pads, core):
         for pad, core in zip(pads, core):
+            # create a lookup on pin name to get at the hidden pad instance
+            # this pin name will be handed to get_input, get_output etc.
+            # and without the padlookup you can't find the (duplicate) pad.
+            # note that self.padlookup and self.jtag.ios use the *exact* same
+            # pin.name per pin
             pin = pad[1]
             corepin = core[1]
             if pin is None: continue # skip when pin is None
             assert corepin is not None # if pad was None, core should be too
             print ("iter", pad, pin.name)
             assert pin.name not in self.padlookup # no overwrites allowed!
             pin = pad[1]
             corepin = core[1]
             if pin is None: continue # skip when pin is None
             assert corepin is not None # if pad was None, core should be too
             print ("iter", pad, pin.name)
             assert pin.name not in self.padlookup # no overwrites allowed!
-            assert pin.name == corepin.name # has to be the same!
-            self.padlookup[pin.name] = core
+            assert pin.name == corepin.name       # has to be the same!
+            self.padlookup[pin.name] = pad        # store pad by pin name
+
+            # now add the IO Shift Register.  first identify the type
+            # then request a JTAG IOConn. we can't wire it up (yet) because
+            # we don't have a Module() instance. doh. that comes in get_input
+            # and get_output etc. etc.
+            iotype = resiotypes[pin.dir] # look up the C4M-JTAG IOType
+            io = self.jtag.add_io(iotype=iotype, name=pin.name) # create IOConn
+            self.jtag.ios[pin.name] = io # store IOConn Record by pin name
 
         # finally return the value just like ResourceManager.request()
         return value
 
         # finally return the value just like ResourceManager.request()
         return value
@@ -275,7 +295,9 @@ something random
    p.build(Blinker())
 """
 pinset = dummy_pinset()
    p.build(Blinker())
 """
 pinset = dummy_pinset()
+top = Blinker(pinset)
 print(pinset)
 print(pinset)
-p = ASICPlatform (pinset)
-p.build(Blinker(pinset))
+resources = create_resources(pinset)
+p = ASICPlatform (resources, top.jtag)
+p.build(top)