turn bi-directional GPIO pads back into triplet independent
[pinmux.git] / src / spec / testing_stage1.py
1 #!/usr/bin/env python3
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
11 import sys
12
13 # extra dependencies for jtag testing (?)
14 from soc.bus.sram import SRAM
15
16 from nmigen import Memory
17 from nmigen.sim import Simulator, Delay, Settle, Tick
18
19 from nmutil.util import wrap
20
21 from soc.debug.jtagutils import (jtag_read_write_reg,
22 jtag_srv, jtag_set_reset,
23 jtag_set_ir, jtag_set_get_dr)
24
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
31
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)
37
38 # File for stage 1 pinmux tested proposed by Luke,
39 # https://bugs.libre-soc.org/show_bug.cgi?id=50#c10
40
41
42 def dummy_pinset():
43 # sigh this needs to come from pinmux.
44 gpios = []
45 for i in range(4):
46 gpios.append("%d*" % i)
47 return {'uart': ['tx+', 'rx-'],
48 'gpio': gpios,
49 #'jtag': ['tms-', 'tdi-', 'tdo+', 'tck+'],
50 'i2c': ['sda*', 'scl+']}
51
52 """
53 a function is needed which turns the results of dummy_pinset()
54 into:
55
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"...)
60 ...
61 ]
62 """
63
64
65 def create_resources(pinset):
66 resources = []
67 for periph, pins in pinset.items():
68 print(periph, pins)
69 if periph == 'i2c':
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")
78 ios = []
79 for pin in pins:
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))
92 pads = []
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,
100 ios=pads))
101 resources.append(Resource.family(periph, 0, default_name="gpio",
102 ios=ios))
103
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)
109 return resources
110
111
112 def JTAGResource(*args):
113 io = []
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)
119
120 def UARTResource(*args, rx, tx):
121 io = []
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)
125
126
127 def I2CResource(*args, scl, sda):
128 ios = []
129 pads = []
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))
134 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)
140
141
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
147 """
148 eqs = []
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))
159 return eqs
160
161
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 = {}
176 self.jtag.eqs = []
177 memory = Memory(width=32, depth=16)
178 self.sram = SRAM(memory=memory, bus=self.jtag.wb)
179
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
184 continue
185 self.add_jtag_request(resource.name, resource.number)
186
187 def elaborate(self, platform):
188 jtag_resources = self.jtag.pad_mgr.resources
189 core_resources = self.jtag.core_mgr.resources
190 m = Module()
191 m.submodules.jtag = self.jtag
192 m.submodules.sram = self.sram
193
194 count = Signal(5)
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)
210
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)
219
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)
226
227 # add the eq assignments connecting up JTAG boundary scan to core
228 m.d.comb += self.jtag.eqs
229 return m
230
231 def ports(self):
232 return list(self)
233
234 def __iter__(self):
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
240
241 def jtag_request(self, name, number=0, *, dir=None, xdr=None):
242 return self.jtag.resource_table[(name, number)]
243
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.
247
248 this override will also - automatically - create a JTAG Boundary Scan
249 connection *without* any change to the actual Platform.request() API
250 """
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)
261
262 # take a copy of the requests made
263 self.jtag.requests_made.append((name, number, dir, xdr))
264
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)
271
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"
278 print ("core", core)
279 print ("pads", pads)
280
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
287 # pin.name per pin
288 padpin = pad[1]
289 corepin = core[1]
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
297
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
305
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
309
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)]
347
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)
351
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
356
357 # and the *PAD* value so that it can be wired up externally as well
358 self.jtag.resource_table_pads[(name, number)] = pvalue
359
360
361 '''
362 _trellis_command_templates = [
363 r"""
364 {{invoke_tool("yosys")}}
365 {{quiet("-q")}}
366 {{get_override("yosys_opts")|options}}
367 -l {{name}}.rpt
368 {{name}}.ys
369 """,
370 ]
371 '''
372
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):
377 connectors = []
378 resources = OrderedDict()
379 required_tools = []
380 command_templates = ['/bin/true'] # no command needed: stops barfing
381 file_templates = {
382 **TemplatedPlatform.build_script_templates,
383 "{{name}}.il": r"""
384 # {{autogenerated}}
385 {{emit_rtlil()}}
386 """,
387 "{{name}}.debug.v": r"""
388 /* {{autogenerated}} */
389 {{emit_debug_verilog()}}
390 """,
391 }
392 toolchain = None
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
395
396 def __init__(self, resources, jtag):
397 self.jtag = jtag
398 super().__init__()
399
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)
403
404 # add JTAG without scan
405 self.add_resources([JTAGResource('jtag', 0)], no_boundary_scan=True)
406
407 def add_resources(self, resources, no_boundary_scan=False):
408 print ("ASICPlatform add_resources", resources)
409 return super().add_resources(resources)
410
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)
419
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)
426
427 m = Module()
428 print (" get_input", pin, "port", port, port.layout)
429 m.d.comb += pin.i.eq(self._invert_if(invert, port))
430 return m
431
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)
435
436 m = Module()
437 print (" get_output", pin, "port", port, port.layout)
438 m.d.comb += port.eq(self._invert_if(invert, pin.o))
439 return m
440
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)
444
445 print (" get_tristate", pin, "port", port, port.layout)
446 m = Module()
447 print (" pad", pin, port, attrs)
448 print (" pin", pin.layout)
449 return m
450 # m.submodules += Instance("$tribuf",
451 # p_WIDTH=pin.width,
452 # i_EN=pin.oe,
453 # i_A=self._invert_if(invert, pin.o),
454 # o_Y=port,
455 # )
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)
462 return m
463
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)
467
468 print (" get_input_output", pin, "port", port, port.layout)
469 m = Module()
470 print (" port layout", port.layout)
471 print (" pin", pin)
472 print (" layout", pin.layout)
473 #m.submodules += Instance("$tribuf",
474 # p_WIDTH=pin.width,
475 # i_EN=io.pad.oe,
476 # i_A=self._invert_if(invert, io.pad.o),
477 # o_Y=port,
478 #)
479 # Create aliases for the port sub-signals
480 port_i = port.io[0]
481 port_o = port.io[1]
482 port_oe = port.io[2]
483
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)
487
488 return m
489
490 def toolchain_prepare(self, fragment, name, **kwargs):
491 """override toolchain_prepare in order to grab the fragment
492 """
493 self.fragment = fragment
494 return super().toolchain_prepare(fragment, name, **kwargs)
495
496 """
497 and to create a Platform instance with that list, and build
498 something random
499
500 p=Platform()
501 p.resources=listofstuff
502 p.build(Blinker())
503 """
504 pinset = dummy_pinset()
505 print(pinset)
506 resources = create_resources(pinset)
507 top = Blinker(pinset, resources)
508
509 #vl = rtlil.convert(top, ports=top.ports())
510 #with open("test_jtag_blinker.il", "w") as f:
511 # f.write(vl)
512
513 if True:
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.
517
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
522 class Dummy: pass
523 cdut = Dummy()
524 cdut.cbus = JTAGInterface()
525
526 # set up client-server on port 44843-something
527 top.jtag.s = JTAGServer()
528 cdut.c = JTAGClient()
529 top.jtag.s.get_connection()
530 #else:
531 # print ("running server only as requested, use openocd remote to test")
532 # sys.stdout.flush()
533 # top.jtag.s.get_connection(None) # block waiting for connection
534
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
538
539 p = ASICPlatform (resources, top.jtag)
540 p.build(top)
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
546
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.
551
552 sim = Simulator(top)
553 sim.add_clock(1e-6, domain="sync") # standard clock
554
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
559
560 with sim.write_vcd("dmi2jtag_test_srv.vcd"):
561 sim.run()