2 from nmigen
.build
.dsl
import Resource
, Subsignal
, Pins
3 from nmigen
.build
.plat
import TemplatedPlatform
4 from nmigen
.build
.res
import ResourceManager
, ResourceError
5 from nmigen
.hdl
.rec
import Layout
6 from nmigen
import Elaboratable
, Signal
, Module
, Instance
7 from collections
import OrderedDict
8 from jtag
import JTAG
, resiotypes
9 from copy
import deepcopy
10 from nmigen
.cli
import rtlil
13 # extra dependencies for jtag testing (?)
14 from soc
.bus
.sram
import SRAM
16 from nmigen
import Memory
17 from nmigen
.sim
import Simulator
, Delay
, Settle
, Tick
19 from nmutil
.util
import wrap
21 from soc
.debug
.jtagutils
import (jtag_read_write_reg
,
22 jtag_srv
, jtag_set_reset
,
23 jtag_set_ir
, jtag_set_get_dr
)
25 from c4m
.nmigen
.jtag
.tap
import TAP
, IOType
26 from c4m
.nmigen
.jtag
.bus
import Interface
as JTAGInterface
27 from soc
.debug
.dmi
import DMIInterface
, DBGCore
28 from soc
.debug
.test
.dmi_sim
import dmi_sim
29 from soc
.debug
.test
.jtagremote
import JTAGServer
, JTAGClient
30 from nmigen
.build
.res
import ResourceError
32 # Was thinking of using these functions, but skipped for simplicity for now
33 # XXX nope. the output from JSON file.
34 #from pinfunctions import (i2s, lpc, emmc, sdmmc, mspi, mquadspi, spi,
35 # quadspi, i2c, mi2c, jtag, uart, uartfull, rgbttl, ulpi, rgmii, flexbus1,
36 # flexbus2, sdram1, sdram2, sdram3, vss, vdd, sys, eint, pwm, gpio)
38 # File for stage 1 pinmux tested proposed by Luke,
39 # https://bugs.libre-soc.org/show_bug.cgi?id=50#c10
43 # sigh this needs to come from pinmux.
46 gpios
.append("%d*" % i
)
47 return {'uart': ['tx+', 'rx-'],
49 #'jtag': ['tms-', 'tdi-', 'tdo+', 'tck+'],
50 'i2c': ['sda*', 'scl+']}
53 a function is needed which turns the results of dummy_pinset()
56 [UARTResource("uart", 0, tx=..., rx=..),
57 I2CResource("i2c", 0, scl=..., sda=...),
58 Resource("gpio", 0, Subsignal("i"...), Subsignal("o"...)
59 Resource("gpio", 1, Subsignal("i"...), Subsignal("o"...)
65 def create_resources(pinset
):
67 for periph
, pins
in pinset
.items():
70 #print("I2C required!")
71 resources
.append(I2CResource('i2c', 0, sda
='sda', scl
='scl'))
72 elif periph
== 'uart':
73 #print("UART required!")
74 resources
.append(UARTResource('uart', 0, tx
='tx', rx
='rx'))
75 elif periph
== 'gpio':
76 #print("GPIO required!")
77 print ("GPIO is defined as '*' type, meaning i, o and oe needed")
80 pname
= "gpio"+pin
[:-1] # strip "*" on end
81 # urrrr... tristsate and io assume a single pin which is
82 # of course exactly what we don't want in an ASIC: we want
83 # *all three* pins but the damn port is not outputted
84 # as a triplet, it's a single Record named "io". sigh.
85 # therefore the only way to get a triplet of i/o/oe
86 # is to *actually* create explicit triple pins
87 # XXX ARRRGH, doesn't work
88 #pad = Subsignal("io",
89 # Pins("%s_i %s_o %s_oe" % (pname, pname, pname),
90 # dir="io", assert_width=3))
91 #ios.append(Resource(pname, 0, pad))
93 pads
.append(Subsignal("i",
94 Pins(pname
+"_i", dir="i", assert_width
=1)))
95 pads
.append(Subsignal("o",
96 Pins(pname
+"_o", dir="o", assert_width
=1)))
97 pads
.append(Subsignal("oe",
98 Pins(pname
+"_oe", dir="o", assert_width
=1)))
99 ios
.append(Resource
.family(pname
, 0, default_name
=pname
,
101 resources
.append(Resource
.family(periph
, 0, default_name
="gpio",
104 # add clock and reset
105 clk
= Resource("clk", 0, Pins("sys_clk", dir="i"))
106 rst
= Resource("rst", 0, Pins("sys_rst", dir="i"))
107 resources
.append(clk
)
108 resources
.append(rst
)
112 def JTAGResource(*args
):
114 io
.append(Subsignal("tms", Pins("tms", dir="i", assert_width
=1)))
115 io
.append(Subsignal("tdi", Pins("tdi", dir="i", assert_width
=1)))
116 io
.append(Subsignal("tck", Pins("tck", dir="i", assert_width
=1)))
117 io
.append(Subsignal("tdo", Pins("tdo", dir="o", assert_width
=1)))
118 return Resource
.family(*args
, default_name
="jtag", ios
=io
)
120 def UARTResource(*args
, rx
, tx
):
122 io
.append(Subsignal("rx", Pins(rx
, dir="i", assert_width
=1)))
123 io
.append(Subsignal("tx", Pins(tx
, dir="o", assert_width
=1)))
124 return Resource
.family(*args
, default_name
="uart", ios
=io
)
127 def I2CResource(*args
, scl
, sda
):
130 pads
.append(Subsignal("i", Pins(sda
+"_i", dir="i", assert_width
=1)))
131 pads
.append(Subsignal("o", Pins(sda
+"_o", dir="o", assert_width
=1)))
132 pads
.append(Subsignal("oe", Pins(sda
+"_oe", dir="o", assert_width
=1)))
133 ios
.append(Resource
.family(sda
, 0, default_name
=sda
, ios
=pads
))
135 pads
.append(Subsignal("i", Pins(scl
+"_i", dir="i", assert_width
=1)))
136 pads
.append(Subsignal("o", Pins(scl
+"_o", dir="o", assert_width
=1)))
137 pads
.append(Subsignal("oe", Pins(scl
+"_oe", dir="o", assert_width
=1)))
138 ios
.append(Resource
.family(scl
, 0, default_name
=scl
, ios
=pads
))
139 return Resource
.family(*args
, default_name
="i2c", ios
=ios
)
142 def recurse_down(asicpad
, jtagpad
):
143 """recurse_down: messy ASIC-to-JTAG pad matcher which expects
144 at some point some Records named i, o and oe, and wires them
145 up in the right direction according to those names. "i" for
146 input must come *from* the ASIC pad and connect *to* the JTAG pad
149 for asiclayout
, jtaglayout
in zip(asicpad
.layout
, jtagpad
.layout
):
150 apad
= getattr(asicpad
, asiclayout
[0])
151 jpad
= getattr(jtagpad
, jtaglayout
[0])
152 print ("recurse_down", asiclayout
, jtaglayout
, apad
, jpad
)
153 if isinstance(asiclayout
[1], Layout
):
154 eqs
+= recurse_down(apad
, jpad
)
155 elif asiclayout
[0] == 'i':
156 eqs
.append(jpad
.eq(apad
))
157 elif asiclayout
[0] in ['o', 'oe']:
158 eqs
.append(apad
.eq(jpad
))
162 # top-level demo module.
163 class Blinker(Elaboratable
):
164 def __init__(self
, pinset
, resources
):
165 self
.jtag
= JTAG({}, "sync")
166 self
.jtag
.pad_mgr
= ResourceManager([], [])
167 self
.jtag
.core_mgr
= ResourceManager([], [])
168 self
.jtag
.pad_mgr
.add_resources(resources
)
169 self
.jtag
.core_mgr
.add_resources(resources
)
170 # record resource lookup between core IO names and pads
171 self
.jtag
.padlookup
= {}
172 self
.jtag
.requests_made
= []
173 self
.jtag
.boundary_scan_pads
= []
174 self
.jtag
.resource_table
= {}
175 self
.jtag
.resource_table_pads
= {}
177 memory
= Memory(width
=32, depth
=16)
178 self
.sram
= SRAM(memory
=memory
, bus
=self
.jtag
.wb
)
180 # allocate all resources in advance in pad/core ResourceManagers
181 for resource
in resources
:
182 print ("JTAG resource", resource
)
183 if resource
.name
in ['clk', 'rst']: # hack
185 self
.add_jtag_request(resource
.name
, resource
.number
)
187 def elaborate(self
, platform
):
188 jtag_resources
= self
.jtag
.pad_mgr
.resources
189 core_resources
= self
.jtag
.core_mgr
.resources
191 m
.submodules
.jtag
= self
.jtag
192 m
.submodules
.sram
= self
.sram
195 m
.d
.sync
+= count
.eq(count
+1)
196 print ("resources", platform
, jtag_resources
.items())
197 gpio
= self
.jtag_request('gpio')
198 print (gpio
, gpio
.layout
, gpio
.fields
)
199 # get the GPIO bank, mess about with some of the pins
200 m
.d
.comb
+= gpio
.gpio0
.o
.eq(1)
201 m
.d
.comb
+= gpio
.gpio1
.o
.eq(gpio
.gpio2
.i
)
202 m
.d
.comb
+= gpio
.gpio1
.oe
.eq(count
[4])
203 m
.d
.sync
+= count
[0].eq(gpio
.gpio1
.i
)
204 # get the UART resource, mess with the output tx
205 uart
= self
.jtag_request('uart')
206 print (uart
, uart
.fields
)
207 intermediary
= Signal()
208 m
.d
.comb
+= uart
.tx
.eq(intermediary
)
209 m
.d
.comb
+= intermediary
.eq(uart
.rx
)
211 # platform requested: make the exact same requests,
212 # then add JTAG afterwards
213 if platform
is not None:
214 for (name
, number
, dir, xdr
) in self
.jtag
.requests_made
:
215 asicpad
= platform
.request(name
, number
, dir=dir, xdr
=xdr
)
216 jtagpad
= self
.jtag
.resource_table_pads
[(name
, number
)]
217 print ("jtagpad", jtagpad
, jtagpad
.layout
)
218 m
.d
.comb
+= recurse_down(asicpad
, jtagpad
)
220 # wire up JTAG otherwise we are in trouble (no clock)
221 jtag
= platform
.request('jtag')
222 m
.d
.comb
+= self
.jtag
.bus
.tdi
.eq(jtag
.tdi
)
223 m
.d
.comb
+= self
.jtag
.bus
.tck
.eq(jtag
.tck
)
224 m
.d
.comb
+= self
.jtag
.bus
.tms
.eq(jtag
.tms
)
225 m
.d
.comb
+= jtag
.tdo
.eq(self
.jtag
.bus
.tdo
)
227 # add the eq assignments connecting up JTAG boundary scan to core
228 m
.d
.comb
+= self
.jtag
.eqs
235 yield self
.jtag
.bus
.tdi
236 yield self
.jtag
.bus
.tdo
237 yield self
.jtag
.bus
.tck
238 yield self
.jtag
.bus
.tms
239 yield from self
.jtag
.boundary_scan_pads
241 def jtag_request(self
, name
, number
=0, *, dir=None, xdr
=None):
242 return self
.jtag
.resource_table
[(name
, number
)]
244 def add_jtag_request(self
, name
, number
=0, *, dir=None, xdr
=None):
245 """request a Resource (e.g. name="uart", number=0) which will
246 return a data structure containing Records of all the pins.
248 this override will also - automatically - create a JTAG Boundary Scan
249 connection *without* any change to the actual Platform.request() API
251 pad_mgr
= self
.jtag
.pad_mgr
252 core_mgr
= self
.jtag
.core_mgr
253 padlookup
= self
.jtag
.padlookup
254 # okaaaay, bit of shenanigens going on: the important data structure
255 # here is Resourcemanager._ports. requests add to _ports, which is
256 # what needs redirecting. therefore what has to happen is to
257 # capture the number of ports *before* the request. sigh.
258 start_ports
= len(core_mgr
._ports
)
259 value
= core_mgr
.request(name
, number
, dir=dir, xdr
=xdr
)
260 end_ports
= len(core_mgr
._ports
)
262 # take a copy of the requests made
263 self
.jtag
.requests_made
.append((name
, number
, dir, xdr
))
265 # now make a corresponding (duplicate) request to the pad manager
266 # BUT, if it doesn't exist, don't sweat it: all it means is, the
267 # application did not request Boundary Scan for that resource.
268 pad_start_ports
= len(pad_mgr
._ports
)
269 pvalue
= pad_mgr
.request(name
, number
, dir=dir, xdr
=xdr
)
270 pad_end_ports
= len(pad_mgr
._ports
)
272 # ok now we have the lengths: now create a lookup between the pad
273 # and the core, so that JTAG boundary scan can be inserted in between
274 core
= core_mgr
._ports
[start_ports
:end_ports
]
275 pads
= pad_mgr
._ports
[pad_start_ports
:pad_end_ports
]
276 # oops if not the same numbers added. it's a duplicate. shouldn't happen
277 assert len(core
) == len(pads
), "argh, resource manager error"
281 # pad/core each return a list of tuples of (res, pin, port, attrs)
282 for pad
, core
in zip(pads
, core
):
283 # create a lookup on pin name to get at the hidden pad instance
284 # this pin name will be handed to get_input, get_output etc.
285 # and without the padlookup you can't find the (duplicate) pad.
286 # note that self.padlookup and self.jtag.ios use the *exact* same
290 if padpin
is None: continue # skip when pin is None
291 assert corepin
is not None # if pad was None, core should be too
292 print ("iter", pad
, padpin
.name
)
293 print ("existing pads", padlookup
.keys())
294 assert padpin
.name
not in padlookup
# no overwrites allowed!
295 assert padpin
.name
== corepin
.name
# has to be the same!
296 padlookup
[padpin
.name
] = pad
# store pad by pin name
298 # now add the IO Shift Register. first identify the type
299 # then request a JTAG IOConn. we can't wire it up (yet) because
300 # we don't have a Module() instance. doh. that comes in get_input
301 # and get_output etc. etc.
302 iotype
= resiotypes
[padpin
.dir] # look up the C4M-JTAG IOType
303 io
= self
.jtag
.add_io(iotype
=iotype
, name
=padpin
.name
) # IOConn
304 self
.jtag
.ios
[padpin
.name
] = io
# store IOConn Record by pin name
306 # and connect up core to pads based on type. could create
307 # Modules here just like in Platform.get_input/output but
308 # in some ways it is clearer by being simpler to wire them globally
310 if padpin
.dir == 'i':
311 print ("jtag_request add input pin", padpin
)
312 print (" corepin", corepin
)
313 print (" jtag io core", io
.core
)
314 print (" jtag io pad", io
.pad
)
315 # corepin is to be returned, here. so, connect jtag corein to it
316 self
.jtag
.eqs
+= [corepin
.i
.eq(io
.core
.i
)]
317 # and padpin to JTAG pad
318 self
.jtag
.eqs
+= [io
.pad
.i
.eq(padpin
.i
)]
319 self
.jtag
.boundary_scan_pads
.append(padpin
.i
)
320 elif padpin
.dir == 'o':
321 print ("jtag_request add output pin", padpin
)
322 print (" corepin", corepin
)
323 print (" jtag io core", io
.core
)
324 print (" jtag io pad", io
.pad
)
325 # corepin is to be returned, here. connect it to jtag core out
326 self
.jtag
.eqs
+= [io
.core
.o
.eq(corepin
.o
)]
327 # and JTAG pad to padpin
328 self
.jtag
.eqs
+= [padpin
.o
.eq(io
.pad
.o
)]
329 self
.jtag
.boundary_scan_pads
.append(padpin
.o
)
330 elif padpin
.dir == 'io':
331 print ("jtag_request add io pin", padpin
)
332 print (" corepin", corepin
)
333 print (" jtag io core", io
.core
)
334 print (" jtag io pad", io
.pad
)
335 # corepin is to be returned, here. so, connect jtag corein to it
336 self
.jtag
.eqs
+= [corepin
.i
.eq(io
.core
.i
)]
337 # and padpin to JTAG pad
338 self
.jtag
.eqs
+= [io
.pad
.i
.eq(padpin
.i
)]
339 # corepin is to be returned, here. connect it to jtag core out
340 self
.jtag
.eqs
+= [io
.core
.o
.eq(corepin
.o
)]
341 # and JTAG pad to padpin
342 self
.jtag
.eqs
+= [padpin
.o
.eq(io
.pad
.o
)]
343 # corepin is to be returned, here. connect it to jtag core out
344 self
.jtag
.eqs
+= [io
.core
.oe
.eq(corepin
.oe
)]
345 # and JTAG pad to padpin
346 self
.jtag
.eqs
+= [padpin
.oe
.eq(io
.pad
.oe
)]
348 self
.jtag
.boundary_scan_pads
.append(padpin
.i
)
349 self
.jtag
.boundary_scan_pads
.append(padpin
.o
)
350 self
.jtag
.boundary_scan_pads
.append(padpin
.oe
)
352 # finally record the *CORE* value just like ResourceManager.request()
353 # so that the module using this can connect to *CORE* i/o to the
354 # resource. pads are taken care of
355 self
.jtag
.resource_table
[(name
, number
)] = value
357 # and the *PAD* value so that it can be wired up externally as well
358 self
.jtag
.resource_table_pads
[(name
, number
)] = pvalue
362 _trellis_command_templates = [
364 {{invoke_tool("yosys")}}
366 {{get_override("yosys_opts")|options}}
373 # sigh, have to create a dummy platform for now.
374 # TODO: investigate how the heck to get it to output ilang. or verilog.
375 # or, anything, really. but at least it doesn't barf
376 class ASICPlatform(TemplatedPlatform
):
378 resources
= OrderedDict()
380 command_templates
= ['/bin/true'] # no command needed: stops barfing
382 **TemplatedPlatform
.build_script_templates
,
387 "{{name}}.debug.v": r
"""
388 /* {{autogenerated}} */
389 {{emit_debug_verilog()}}
393 default_clk
= "clk" # should be picked up / overridden by platform sys.clk
394 default_rst
= "rst" # should be picked up / overridden by platform sys.rst
396 def __init__(self
, resources
, jtag
):
400 # create set of pin resources based on the pinset, this is for the core
401 #jtag_resources = self.jtag.pad_mgr.resources
402 self
.add_resources(resources
)
404 # add JTAG without scan
405 self
.add_resources([JTAGResource('jtag', 0)], no_boundary_scan
=True)
407 def add_resources(self
, resources
, no_boundary_scan
=False):
408 print ("ASICPlatform add_resources", resources
)
409 return super().add_resources(resources
)
411 #def iter_ports(self):
412 # yield from super().iter_ports()
413 # for io in self.jtag.ios.values():
414 # print ("iter ports", io.layout, io)
415 # for field in io.core.fields:
416 # yield getattr(io.core, field)
417 # for field in io.pad.fields:
418 # yield getattr(io.pad, field)
420 # XXX these aren't strictly necessary right now but the next
421 # phase is to add JTAG Boundary Scan so it maaay be worth adding?
422 # at least for the print statements
423 def get_input(self
, pin
, port
, attrs
, invert
):
424 self
._check
_feature
("single-ended input", pin
, attrs
,
425 valid_xdrs
=(0,), valid_attrs
=None)
428 print (" get_input", pin
, "port", port
, port
.layout
)
429 m
.d
.comb
+= pin
.i
.eq(self
._invert
_if
(invert
, port
))
432 def get_output(self
, pin
, port
, attrs
, invert
):
433 self
._check
_feature
("single-ended output", pin
, attrs
,
434 valid_xdrs
=(0,), valid_attrs
=None)
437 print (" get_output", pin
, "port", port
, port
.layout
)
438 m
.d
.comb
+= port
.eq(self
._invert
_if
(invert
, pin
.o
))
441 def get_tristate(self
, pin
, port
, attrs
, invert
):
442 self
._check
_feature
("single-ended tristate", pin
, attrs
,
443 valid_xdrs
=(0,), valid_attrs
=None)
445 print (" get_tristate", pin
, "port", port
, port
.layout
)
447 print (" pad", pin
, port
, attrs
)
448 print (" pin", pin
.layout
)
450 # m.submodules += Instance("$tribuf",
453 # i_A=self._invert_if(invert, pin.o),
456 m
.d
.comb
+= io
.core
.o
.eq(pin
.o
)
457 m
.d
.comb
+= io
.core
.oe
.eq(pin
.oe
)
458 m
.d
.comb
+= pin
.i
.eq(io
.core
.i
)
459 m
.d
.comb
+= io
.pad
.i
.eq(port
.i
)
460 m
.d
.comb
+= port
.o
.eq(io
.pad
.o
)
461 m
.d
.comb
+= port
.oe
.eq(io
.pad
.oe
)
464 def get_input_output(self
, pin
, port
, attrs
, invert
):
465 self
._check
_feature
("single-ended input/output", pin
, attrs
,
466 valid_xdrs
=(0,), valid_attrs
=None)
468 print (" get_input_output", pin
, "port", port
, port
.layout
)
470 print (" port layout", port
.layout
)
472 print (" layout", pin
.layout
)
473 #m.submodules += Instance("$tribuf",
476 # i_A=self._invert_if(invert, io.pad.o),
479 # Create aliases for the port sub-signals
484 m
.d
.comb
+= pin
.i
.eq(self
._invert
_if
(invert
, port_i
))
485 m
.d
.comb
+= port_o
.eq(self
._invert
_if
(invert
, pin
.o
))
486 m
.d
.comb
+= port_oe
.eq(pin
.oe
)
490 def toolchain_prepare(self
, fragment
, name
, **kwargs
):
491 """override toolchain_prepare in order to grab the fragment
493 self
.fragment
= fragment
494 return super().toolchain_prepare(fragment
, name
, **kwargs
)
497 and to create a Platform instance with that list, and build
501 p.resources=listofstuff
504 pinset
= dummy_pinset()
506 resources
= create_resources(pinset
)
507 top
= Blinker(pinset
, resources
)
509 #vl = rtlil.convert(top, ports=top.ports())
510 #with open("test_jtag_blinker.il", "w") as f:
514 # XXX these modules are all being added *AFTER* the build process links
515 # everything together. the expectation that this would work is...
516 # unrealistic. ordering, clearly, is important.
518 # dut = JTAG(test_pinset(), wb_data_wid=64, domain="sync")
519 top
.jtag
.stop
= False
520 # rather than the client access the JTAG bus directly
521 # create an alternative that the client sets
524 cdut
.cbus
= JTAGInterface()
526 # set up client-server on port 44843-something
527 top
.jtag
.s
= JTAGServer()
528 cdut
.c
= JTAGClient()
529 top
.jtag
.s
.get_connection()
531 # print ("running server only as requested, use openocd remote to test")
533 # top.jtag.s.get_connection(None) # block waiting for connection
535 # take copy of ir_width and scan_len
536 cdut
._ir
_width
= top
.jtag
._ir
_width
537 cdut
.scan_len
= top
.jtag
.scan_len
539 p
= ASICPlatform (resources
, top
.jtag
)
541 # this is what needs to gets treated as "top", after "main module" top
542 # is augmented with IO pads with JTAG tacked on. the expectation that
543 # the get_input() etc functions will be called magically by some other
544 # function is unrealistic.
545 top_fragment
= p
.fragment
547 # XXX simulating top (the module that does not itself contain IO pads
548 # because that's covered by build) cannot possibly be expected to work
549 # particularly when modules have been added *after* the platform build()
550 # function has been called.
553 sim
.add_clock(1e-6, domain
="sync") # standard clock
555 sim
.add_sync_process(wrap(jtag_srv(top
))) #? jtag server
556 #if len(sys.argv) != 2 or sys.argv[1] != 'server':
557 sim
.add_sync_process(wrap(jtag_sim(cdut
, top
.jtag
))) # actual jtag tester
558 sim
.add_sync_process(wrap(dmi_sim(top
.jtag
))) # handles (pretends to be) DMI
560 with sim
.write_vcd("dmi2jtag_test_srv.vcd"):