add a core-mgr *and* a pad-mgr to JTAG class so that an *entire* set
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sun, 28 Nov 2021 15:02:16 +0000 (15:02 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sun, 28 Nov 2021 15:02:16 +0000 (15:02 +0000)
of "resources" is connected inside the module... *outside* of the Platform.
the Platform then only needs a straight port-to-port connection *NOT*
"the Platform connects up JTAG Boundary Scan"

src/spec/testing_stage1.py

index 4daa9865f0d861c839b6b6f86ab4576d71532b08..b4531b31496c4b6a780642814be11c1d8f5dfadf 100644 (file)
@@ -6,6 +6,8 @@ from nmigen import Elaboratable, Signal, Module, Instance
 from collections import OrderedDict
 from jtag import JTAG, resiotypes
 from copy import deepcopy
 from collections import OrderedDict
 from jtag import JTAG, resiotypes
 from copy import deepcopy
+from nmigen.cli import rtlil
+import sys
 
 # extra dependencies for jtag testing (?)
 from soc.bus.sram import SRAM
 
 # extra dependencies for jtag testing (?)
 from soc.bus.sram import SRAM
@@ -123,7 +125,9 @@ def I2CResource(*args, scl, sda):
 class Blinker(Elaboratable):
     def __init__(self, pinset, resources):
         self.jtag = JTAG({}, "sync")
 class Blinker(Elaboratable):
     def __init__(self, pinset, resources):
         self.jtag = JTAG({}, "sync")
+        self.jtag.pad_mgr = ResourceManager([], [])
         self.jtag.core_mgr = ResourceManager([], [])
         self.jtag.core_mgr = ResourceManager([], [])
+        self.jtag.pad_mgr.add_resources(resources)
         self.jtag.core_mgr.add_resources(resources)
         # record resource lookup between core IO names and pads
         self.jtag.padlookup = {}
         self.jtag.core_mgr.add_resources(resources)
         # record resource lookup between core IO names and pads
         self.jtag.padlookup = {}
@@ -132,15 +136,16 @@ class Blinker(Elaboratable):
         self.sram = SRAM(memory=memory, bus=self.jtag.wb)
 
     def elaborate(self, platform):
         self.sram = SRAM(memory=memory, bus=self.jtag.wb)
 
     def elaborate(self, platform):
-        jtag_resources = self.jtag.core_mgr.resources
+        jtag_resources = self.jtag.pad_mgr.resources
+        core_resources = self.jtag.core_mgr.resources
         m = Module()
         m.submodules.jtag = self.jtag
         m.submodules.sram = self.sram
 
         count = Signal(5)
         m.d.sync += count.eq(count+1)
         m = Module()
         m.submodules.jtag = self.jtag
         m.submodules.sram = self.sram
 
         count = Signal(5)
         m.d.sync += count.eq(count+1)
-        print ("resources", jtag_resources.items())
-        gpio = self.jtag_request('gpio')
+        print ("resources", platform, jtag_resources.items())
+        gpio = self.jtag_request(m, 'gpio')
         print (gpio, gpio.layout, gpio.fields)
         # get the GPIO bank, mess about with some of the pins
         m.d.comb += gpio.gpio0.io.o.eq(1)
         print (gpio, gpio.layout, gpio.fields)
         # get the GPIO bank, mess about with some of the pins
         m.d.comb += gpio.gpio0.io.o.eq(1)
@@ -148,7 +153,7 @@ class Blinker(Elaboratable):
         m.d.comb += gpio.gpio1.io.oe.eq(count[4])
         m.d.sync += count[0].eq(gpio.gpio1.io.i)
         # get the UART resource, mess with the output tx
         m.d.comb += gpio.gpio1.io.oe.eq(count[4])
         m.d.sync += count[0].eq(gpio.gpio1.io.i)
         # get the UART resource, mess with the output tx
-        uart = self.jtag_request('uart')
+        uart = self.jtag_request(m, 'uart')
         print (uart, uart.fields)
         intermediary = Signal()
         m.d.comb += uart.tx.eq(intermediary)
         print (uart, uart.fields)
         intermediary = Signal()
         m.d.comb += uart.tx.eq(intermediary)
@@ -169,22 +174,32 @@ class Blinker(Elaboratable):
 
         return m
 
 
         return m
 
-    def jtag_request(self, name, number=0, *, dir=None, xdr=None):
+    def ports(self):
+        return list(self)
+
+    def __iter__(self):
+        yield self.jtag.bus.tdi
+        yield self.jtag.bus.tdo
+        yield self.jtag.bus.tck
+        yield self.jtag.bus.tms
+
+    def jtag_request(self, m, 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
         """
         """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
         """
+        pad_mgr = self.jtag.pad_mgr
         core_mgr = self.jtag.core_mgr
         padlookup = self.jtag.padlookup
         # 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
         # capture the number of ports *before* the request. sigh.
         core_mgr = self.jtag.core_mgr
         padlookup = self.jtag.padlookup
         # 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
         # capture the number of ports *before* the request. sigh.
-        start_ports = len(core_mgr._ports)
-        value = core_mgr.request(name, number, dir=dir, xdr=xdr)
-        end_ports = len(core_mgr._ports)
+        start_ports = len(core_mgr._ports)
+        value = core_mgr.request(name, number, dir=dir, xdr=xdr)
+        end_ports = len(core_mgr._ports)
 
         # take a copy of the requests made
         self.jtag.requests_made.append((name, number, dir, xdr))
 
         # take a copy of the requests made
         self.jtag.requests_made.append((name, number, dir, xdr))
@@ -192,47 +207,85 @@ class Blinker(Elaboratable):
         # now make a corresponding (duplicate) request to the pad manager
         # BUT, if it doesn't exist, don't sweat it: all it means is, the
         # application did not request Boundary Scan for that resource.
         # now make a corresponding (duplicate) request to the pad manager
         # BUT, if it doesn't exist, don't sweat it: all it means is, the
         # application did not request Boundary Scan for that resource.
-        pad_start_ports = len(core_mgr._ports)
-        pvalue = core_mgr.request(name, number, dir=dir, xdr=xdr)
-        pad_end_ports = len(core_mgr._ports)
+        pad_start_ports = len(pad_mgr._ports)
+        pvalue = pad_mgr.request(name, number, dir=dir, xdr=xdr)
+        pad_end_ports = len(pad_mgr._ports)
 
         # ok now we have the lengths: now create a lookup between the pad
         # and the core, so that JTAG boundary scan can be inserted in between
 
         # ok now we have the lengths: now create a lookup between the pad
         # and the core, so that JTAG boundary scan can be inserted in between
-        #core = self._ports[start_ports:end_ports]
-        pads = core_mgr._ports[pad_start_ports:pad_end_ports]
+        core = core_mgr._ports[start_ports:end_ports]
+        pads = pad_mgr._ports[pad_start_ports:pad_end_ports]
         # oops if not the same numbers added. it's a duplicate. shouldn't happen
         # oops if not the same numbers added. it's a duplicate. shouldn't happen
-        #assert len(core) == len(pads), "argh, resource manager error"
-        #print ("core", core)
+        assert len(core) == len(pads), "argh, resource manager error"
+        print ("core", core)
         print ("pads", pads)
 
         # pad/core each return a list of tuples of (res, pin, port, attrs)
         print ("pads", pads)
 
         # pad/core each return a list of tuples of (res, pin, port, attrs)
-        #for pad, core in zip(pads, core):
-        for pad in pads:
+        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
             # 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)
+            padpin = pad[1]
+            corepin = core[1]
+            if padpin is None: continue # skip when pin is None
+            assert corepin is not None # if pad was None, core should be too
+            print ("iter", pad, padpin.name)
             print ("existing pads", padlookup.keys())
             print ("existing pads", padlookup.keys())
-            assert pin.name not in padlookup # no overwrites allowed!
-            #assert pin.name == corepin.name       # has to be the same!
-            padlookup[pin.name] = pad        # store pad by pin name
+            assert padpin.name not in padlookup # no overwrites allowed!
+            assert padpin.name == corepin.name       # has to be the same!
+            padlookup[padpin.name] = (core, 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.
 
             # 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 pvalue
+            iotype = resiotypes[padpin.dir] # look up the C4M-JTAG IOType
+            io = self.jtag.add_io(iotype=iotype, name=padpin.name) # IOConn
+            self.jtag.ios[padpin.name] = io # store IOConn Record by pin name
+
+            # and connect up core to pads based on type
+            if padpin.dir == 'i':
+                print ("jtag_request add input pin", padpin)
+                print ("                   corepin", corepin)
+                print ("   jtag io core", io.core)
+                print ("   jtag io pad", io.pad)
+                # corepin is to be returned, here. so, connect jtag corein to it
+                m.d.comb += corepin.i.eq(io.core.i)
+                # and padpin to JTAG pad
+                m.d.comb += io.pad.i.eq(padpin.i)
+            elif padpin.dir == 'o':
+                print ("jtag_request add output pin", padpin)
+                print ("                    corepin", corepin)
+                print ("   jtag io core", io.core)
+                print ("   jtag io pad", io.pad)
+                # corepin is to be returned, here. connect it to jtag core out
+                m.d.comb += io.core.o.eq(corepin.o)
+                # and JTAG pad to padpin
+                m.d.comb += padpin.o.eq(io.pad.o)
+            elif padpin.dir == 'io':
+                print ("jtag_request add io    pin", padpin)
+                print ("                   corepin", corepin)
+                print ("   jtag io core", io.core)
+                print ("   jtag io pad", io.pad)
+                # corepin is to be returned, here. so, connect jtag corein to it
+                m.d.comb += corepin.i.eq(io.core.i)
+                # and padpin to JTAG pad
+                m.d.comb += io.pad.i.eq(padpin.i)
+                # corepin is to be returned, here. connect it to jtag core out
+                m.d.comb += io.core.o.eq(corepin.o)
+                # and JTAG pad to padpin
+                m.d.comb += padpin.o.eq(io.pad.o)
+                # corepin is to be returned, here. connect it to jtag core out
+                m.d.comb += io.core.oe.eq(corepin.oe)
+                # and JTAG pad to padpin
+                m.d.comb += padpin.oe.eq(io.pad.oe)
+
+        # finally return the *CORE* value just like ResourceManager.request()
+        # so that the module using this can connect to *CORE* i/o to the
+        # resource.  pads are taken care of
+        return value
 
 
 '''
 
 
 '''
@@ -275,7 +328,7 @@ class ASICPlatform(TemplatedPlatform):
         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
-        #jtag_resources = self.jtag.core_mgr.resources
+        #jtag_resources = self.jtag.pad_mgr.resources
         self.add_resources(resources)
 
         # add JTAG without scan
         self.add_resources(resources)
 
         # add JTAG without scan
@@ -288,7 +341,8 @@ class ASICPlatform(TemplatedPlatform):
         this override will also - automatically - create a JTAG Boundary Scan
         connection *without* any change to the actual Platform.request() API
         """
         this override will also - automatically - create a JTAG Boundary Scan
         connection *without* any change to the actual Platform.request() API
         """
-        core_mgr = self.jtag.core_mgr
+        pad_mgr = self.jtag.pad_mgr
+        pad_mgr = self.jtag.pad_mgr
         padlookup = self.jtag.padlookup
         # okaaaay, bit of shenanigens going on: the important data structure
         # here is Resourcemanager._ports.  requests add to _ports, which is
         padlookup = self.jtag.padlookup
         # okaaaay, bit of shenanigens going on: the important data structure
         # here is Resourcemanager._ports.  requests add to _ports, which is
@@ -301,17 +355,17 @@ class ASICPlatform(TemplatedPlatform):
         # now make a corresponding (duplicate) request to the pad manager
         # BUT, if it doesn't exist, don't sweat it: all it means is, the
         # application did not request Boundary Scan for that resource.
         # now make a corresponding (duplicate) request to the pad manager
         # BUT, if it doesn't exist, don't sweat it: all it means is, the
         # application did not request Boundary Scan for that resource.
-        pad_start_ports = len(core_mgr._ports)
+        pad_start_ports = len(pad_mgr._ports)
         try:
         try:
-            pvalue = core_mgr.request(name, number, dir=dir, xdr=xdr)
+            pvalue = pad_mgr.request(name, number, dir=dir, xdr=xdr)
         except ResourceError:
             return value
         except ResourceError:
             return value
-        pad_end_ports = len(core_mgr._ports)
+        pad_end_ports = len(pad_mgr._ports)
 
         # ok now we have the lengths: now create a lookup between the pad
         # and the core, so that JTAG boundary scan can be inserted in between
         core = self._ports[start_ports:end_ports]
 
         # ok now we have the lengths: now create a lookup between the pad
         # and the core, so that JTAG boundary scan can be inserted in between
         core = self._ports[start_ports:end_ports]
-        pads = core_mgr._ports[pad_start_ports:pad_end_ports]
+        pads = pad_mgr._ports[pad_start_ports:pad_end_ports]
         # oops if not the same numbers added. it's a duplicate. shouldn't happen
         assert len(core) == len(pads), "argh, resource manager error"
         print ("core", core)
         # oops if not the same numbers added. it's a duplicate. shouldn't happen
         assert len(core) == len(pads), "argh, resource manager error"
         print ("core", core)
@@ -353,7 +407,7 @@ class ASICPlatform(TemplatedPlatform):
             return
         # make a *second* - identical - set of pin resources for the IO ring
         padres = deepcopy(resources)
             return
         # make a *second* - identical - set of pin resources for the IO ring
         padres = deepcopy(resources)
-        self.jtag.core_mgr.add_resources(padres)
+        self.jtag.pad_mgr.add_resources(padres)
 
     #def iter_ports(self):
     #    yield from super().iter_ports()
 
     #def iter_ports(self):
     #    yield from super().iter_ports()
@@ -508,8 +562,8 @@ class ASICPlatform(TemplatedPlatform):
         padport_oe = padport.io[2]
 
         # connect i 
         padport_oe = padport.io[2]
 
         # connect i 
-        m.d.comb += pin.i[0].eq(io.core.i)
-        m.d.comb += padpin.i[0].eq(pin.i[0])
+        m.d.comb += pin.i.eq(io.core.i)
+        m.d.comb += padpin.i.eq(pin.i)
         m.d.comb += padport_i.eq(self._invert_if(invert, port_i))
         m.d.comb += io.pad.i.eq(padport_i)
 
         m.d.comb += padport_i.eq(self._invert_if(invert, port_i))
         m.d.comb += io.pad.i.eq(padport_i)
 
@@ -546,46 +600,52 @@ print(pinset)
 resources = create_resources(pinset)
 top = Blinker(pinset, resources)
 
 resources = create_resources(pinset)
 top = Blinker(pinset, resources)
 
-
-# XXX these modules are all being added *AFTER* the build process links
-# everything together.  the expectation that this would work is... unrealistic.
-# ordering, clearly, is important.
-
-# dut = JTAG(test_pinset(), wb_data_wid=64, domain="sync")
-top.jtag.stop = False
-# rather than the client access the JTAG bus directly
-# create an alternative that the client sets
-class Dummy: pass
-cdut = Dummy()
-cdut.cbus = JTAGInterface()
-
-# set up client-server on port 44843-something
-top.jtag.s = JTAGServer()
-cdut.c = JTAGClient()
-top.jtag.s.get_connection()
-#else:
-#    print ("running server only as requested, use openocd remote to test")
-#    sys.stdout.flush()
-#    top.jtag.s.get_connection(None) # block waiting for connection
-
-# take copy of ir_width and scan_len
-cdut._ir_width = top.jtag._ir_width
-cdut.scan_len = top.jtag.scan_len
-
-p = ASICPlatform (resources, top.jtag)
-p.build(top)
-# this is what needs to gets treated as "top", after "main module" top
-# is augmented with IO pads with JTAG tacked on.  the expectation that
-# the get_input() etc functions will be called magically by some other
-# function is unrealistic.
-top_fragment = p.fragment
+vl = rtlil.convert(top, ports=top.ports())
+with open("test_jtag_blinker.il", "w") as f:
+    f.write(vl)
+
+sys.exit(0)
+
+if False:
+    # XXX these modules are all being added *AFTER* the build process links
+    # everything together.  the expectation that this would work is...
+    # unrealistic.  ordering, clearly, is important.
+
+    # dut = JTAG(test_pinset(), wb_data_wid=64, domain="sync")
+    top.jtag.stop = False
+    # rather than the client access the JTAG bus directly
+    # create an alternative that the client sets
+    class Dummy: pass
+    cdut = Dummy()
+    cdut.cbus = JTAGInterface()
+
+    # set up client-server on port 44843-something
+    top.jtag.s = JTAGServer()
+    cdut.c = JTAGClient()
+    top.jtag.s.get_connection()
+    #else:
+    #    print ("running server only as requested, use openocd remote to test")
+    #    sys.stdout.flush()
+    #    top.jtag.s.get_connection(None) # block waiting for connection
+
+    # take copy of ir_width and scan_len
+    cdut._ir_width = top.jtag._ir_width
+    cdut.scan_len = top.jtag.scan_len
+
+    p = ASICPlatform (resources, top.jtag)
+    p.build(top)
+    # this is what needs to gets treated as "top", after "main module" top
+    # is augmented with IO pads with JTAG tacked on.  the expectation that
+    # the get_input() etc functions will be called magically by some other
+    # function is unrealistic.
+    top_fragment = p.fragment
 
 # XXX simulating top (the module that does not itself contain IO pads
 # because that's covered by build) cannot possibly be expected to work
 # particularly when modules have been added *after* the platform build()
 # function has been called.
 
 
 # XXX simulating top (the module that does not itself contain IO pads
 # because that's covered by build) cannot possibly be expected to work
 # particularly when modules have been added *after* the platform build()
 # function has been called.
 
-sim = Simulator(top_fragment)
+sim = Simulator(top)
 sim.add_clock(1e-6, domain="sync")      # standard clock
 
 sim.add_sync_process(wrap(jtag_srv(top))) #? jtag server
 sim.add_clock(1e-6, domain="sync")      # standard clock
 
 sim.add_sync_process(wrap(jtag_srv(top))) #? jtag server