e07601f9201b3c39975db41a439234ff51262824
[pinmux.git] / src / spec / testing_stage1.py
1 #!/usr/bin/env python3
2 """
3 pinmux documented here https://libre-soc.org/docs/pinmux/
4 """
5 from nmigen.build.dsl import Resource, Subsignal, Pins
6 from nmigen.build.plat import TemplatedPlatform
7 from nmigen.build.res import ResourceManager, ResourceError
8 from nmigen.hdl.rec import Layout
9 from nmigen import Elaboratable, Signal, Module, Instance
10 from collections import OrderedDict
11 from jtag import JTAG, resiotypes
12 from copy import deepcopy
13 from nmigen.cli import rtlil
14 import sys
15
16 # extra dependencies for jtag testing (?)
17 #from soc.bus.sram import SRAM
18
19 #from nmigen import Memory
20 from nmigen.sim import Simulator, Delay, Settle, Tick, Passive
21
22 from nmutil.util import wrap
23
24 #from soc.debug.jtagutils import (jtag_read_write_reg,
25 # jtag_srv, jtag_set_reset,
26 # jtag_set_ir, jtag_set_get_dr)
27
28 from c4m.nmigen.jtag.tap import TAP, IOType
29 from c4m.nmigen.jtag.bus import Interface as JTAGInterface
30 #from soc.debug.dmi import DMIInterface, DBGCore
31 #from soc.debug.test.dmi_sim import dmi_sim
32 #from soc.debug.test.jtagremote import JTAGServer, JTAGClient
33 from nmigen.build.res import ResourceError
34
35 # Was thinking of using these functions, but skipped for simplicity for now
36 # XXX nope. the output from JSON file.
37 #from pinfunctions import (i2s, lpc, emmc, sdmmc, mspi, mquadspi, spi,
38 # quadspi, i2c, mi2c, jtag, uart, uartfull, rgbttl, ulpi, rgmii, flexbus1,
39 # flexbus2, sdram1, sdram2, sdram3, vss, vdd, sys, eint, pwm, gpio)
40
41 # File for stage 1 pinmux tested proposed by Luke,
42 # https://bugs.libre-soc.org/show_bug.cgi?id=50#c10
43
44
45 def dummy_pinset():
46 # sigh this needs to come from pinmux.
47 gpios = []
48 for i in range(4):
49 gpios.append("%d*" % i)
50 return {'uart': ['tx+', 'rx-'],
51 'gpio': gpios,
52 #'jtag': ['tms-', 'tdi-', 'tdo+', 'tck+'],
53 'i2c': ['sda*', 'scl+']}
54
55 """
56 a function is needed which turns the results of dummy_pinset()
57 into:
58
59 [UARTResource("uart", 0, tx=..., rx=..),
60 I2CResource("i2c", 0, scl=..., sda=...),
61 Resource("gpio", 0, Subsignal("i"...), Subsignal("o"...)
62 Resource("gpio", 1, Subsignal("i"...), Subsignal("o"...)
63 ...
64 ]
65 """
66
67
68 def create_resources(pinset):
69 resources = []
70 for periph, pins in pinset.items():
71 print(periph, pins)
72 if periph == 'i2c':
73 #print("I2C required!")
74 resources.append(I2CResource('i2c', 0, sda='sda', scl='scl'))
75 elif periph == 'uart':
76 #print("UART required!")
77 resources.append(UARTResource('uart', 0, tx='tx', rx='rx'))
78 elif periph == 'gpio':
79 #print("GPIO required!")
80 print ("GPIO is defined as '*' type, meaning i, o and oe needed")
81 ios = []
82 for pin in pins:
83 pname = "gpio"+pin[:-1] # strip "*" on end
84 # urrrr... tristsate and io assume a single pin which is
85 # of course exactly what we don't want in an ASIC: we want
86 # *all three* pins but the damn port is not outputted
87 # as a triplet, it's a single Record named "io". sigh.
88 # therefore the only way to get a triplet of i/o/oe
89 # is to *actually* create explicit triple pins
90 # XXX ARRRGH, doesn't work
91 #pad = Subsignal("io",
92 # Pins("%s_i %s_o %s_oe" % (pname, pname, pname),
93 # dir="io", assert_width=3))
94 #ios.append(Resource(pname, 0, pad))
95 pads = []
96 pads.append(Subsignal("i",
97 Pins(pname+"_i", dir="i", assert_width=1)))
98 pads.append(Subsignal("o",
99 Pins(pname+"_o", dir="o", assert_width=1)))
100 pads.append(Subsignal("oe",
101 Pins(pname+"_oe", dir="o", assert_width=1)))
102 ios.append(Resource.family(pname, 0, default_name=pname,
103 ios=pads))
104 resources.append(Resource.family(periph, 0, default_name="gpio",
105 ios=ios))
106
107 # add clock and reset
108 clk = Resource("clk", 0, Pins("sys_clk", dir="i"))
109 rst = Resource("rst", 0, Pins("sys_rst", dir="i"))
110 resources.append(clk)
111 resources.append(rst)
112 return resources
113
114
115 def JTAGResource(*args):
116 io = []
117 io.append(Subsignal("tms", Pins("tms", dir="i", assert_width=1)))
118 io.append(Subsignal("tdi", Pins("tdi", dir="i", assert_width=1)))
119 io.append(Subsignal("tck", Pins("tck", dir="i", assert_width=1)))
120 io.append(Subsignal("tdo", Pins("tdo", dir="o", assert_width=1)))
121 return Resource.family(*args, default_name="jtag", ios=io)
122
123 def UARTResource(*args, rx, tx):
124 io = []
125 io.append(Subsignal("rx", Pins(rx, dir="i", assert_width=1)))
126 io.append(Subsignal("tx", Pins(tx, dir="o", assert_width=1)))
127 return Resource.family(*args, default_name="uart", ios=io)
128
129
130 def I2CResource(*args, scl, sda):
131 ios = []
132 pads = []
133 pads.append(Subsignal("i", Pins(sda+"_i", dir="i", assert_width=1)))
134 pads.append(Subsignal("o", Pins(sda+"_o", dir="o", assert_width=1)))
135 pads.append(Subsignal("oe", Pins(sda+"_oe", dir="o", assert_width=1)))
136 ios.append(Resource.family(sda, 0, default_name=sda, ios=pads))
137 pads = []
138 pads.append(Subsignal("i", Pins(scl+"_i", dir="i", assert_width=1)))
139 pads.append(Subsignal("o", Pins(scl+"_o", dir="o", assert_width=1)))
140 pads.append(Subsignal("oe", Pins(scl+"_oe", dir="o", assert_width=1)))
141 ios.append(Resource.family(scl, 0, default_name=scl, ios=pads))
142 return Resource.family(*args, default_name="i2c", ios=ios)
143
144
145 # top-level demo module.
146 class Blinker(Elaboratable):
147 def __init__(self, pinset, resources, no_jtag_connect=False):
148 self.no_jtag_connect = no_jtag_connect
149 self.jtag = JTAG({}, "sync", resources=resources)
150 #memory = Memory(width=32, depth=16)
151 #self.sram = SRAM(memory=memory, bus=self.jtag.wb)
152
153 def elaborate(self, platform):
154 jtag_resources = self.jtag.pad_mgr.resources
155 m = Module()
156 m.submodules.jtag = self.jtag
157 #m.submodules.sram = self.sram
158
159 #count = Signal(5)
160 #m.d.sync += count.eq(count+1)
161 print ("resources", platform, jtag_resources.items())
162 gpio = self.jtag.request('gpio')
163 print (gpio, gpio.layout, gpio.fields)
164 # get the GPIO bank, mess about with some of the pins
165 #m.d.comb += gpio.gpio0.o.eq(1)
166 #m.d.comb += gpio.gpio1.o.eq(gpio.gpio2.i)
167 #m.d.comb += gpio.gpio1.oe.eq(count[4])
168 #m.d.sync += count[0].eq(gpio.gpio1.i)
169
170 num_gpios = 4
171 gpio_o_test = Signal(num_gpios)
172 gpio_oe_test = Signal(num_gpios)
173 # Wire up the output signal of each gpio by XOR'ing each bit of gpio_o_test with gpio's input
174 # Wire up each bit of gpio_oe_test signal to oe signal of each gpio.
175 # Turn into a loop at some point, probably a way without
176 # using get_attr()
177 m.d.comb += gpio.gpio0.o.eq(gpio_o_test[0] ^ gpio.gpio0.i)
178 m.d.comb += gpio.gpio1.o.eq(gpio_o_test[1] ^ gpio.gpio1.i)
179 m.d.comb += gpio.gpio2.o.eq(gpio_o_test[2] ^ gpio.gpio2.i)
180 m.d.comb += gpio.gpio3.o.eq(gpio_o_test[3] ^ gpio.gpio3.i)
181
182 m.d.comb += gpio.gpio0.oe.eq(gpio_oe_test[0])
183 m.d.comb += gpio.gpio1.oe.eq(gpio_oe_test[1])
184 m.d.comb += gpio.gpio2.oe.eq(gpio_oe_test[2])
185 m.d.comb += gpio.gpio3.oe.eq(gpio_oe_test[3])
186
187 # get the UART resource, mess with the output tx
188 uart = self.jtag.request('uart')
189 print ("uart fields", uart, uart.fields)
190 self.intermediary = Signal()
191 m.d.comb += uart.tx.eq(self.intermediary)
192 m.d.comb += self.intermediary.eq(uart.rx)
193
194 # to even be able to get at objects, you first have to make them
195 # available - i.e. not as local variables
196 self.gpio = gpio
197 self.uart = uart
198 self.gpio_o_test = gpio_o_test
199 self.gpio_oe_test = gpio_oe_test
200
201 # sigh these wire up to the pads so you cannot set Signals
202 # that are already wired
203 if self.no_jtag_connect: # bypass jtag pad connect for testing purposes
204 return m
205 return self.jtag.boundary_elaborate(m, platform)
206
207 def ports(self):
208 return list(self)
209
210 def __iter__(self):
211 yield from self.jtag.iter_ports()
212
213 '''
214 _trellis_command_templates = [
215 r"""
216 {{invoke_tool("yosys")}}
217 {{quiet("-q")}}
218 {{get_override("yosys_opts")|options}}
219 -l {{name}}.rpt
220 {{name}}.ys
221 """,
222 ]
223 '''
224
225 # sigh, have to create a dummy platform for now.
226 # TODO: investigate how the heck to get it to output ilang. or verilog.
227 # or, anything, really. but at least it doesn't barf
228 class ASICPlatform(TemplatedPlatform):
229 connectors = []
230 resources = OrderedDict()
231 required_tools = []
232 command_templates = ['/bin/true'] # no command needed: stops barfing
233 file_templates = {
234 **TemplatedPlatform.build_script_templates,
235 "{{name}}.il": r"""
236 # {{autogenerated}}
237 {{emit_rtlil()}}
238 """,
239 "{{name}}.debug.v": r"""
240 /* {{autogenerated}} */
241 {{emit_debug_verilog()}}
242 """,
243 }
244 toolchain = None
245 default_clk = "clk" # should be picked up / overridden by platform sys.clk
246 default_rst = "rst" # should be picked up / overridden by platform sys.rst
247
248 def __init__(self, resources, jtag):
249 self.jtag = jtag
250 super().__init__()
251
252 # create set of pin resources based on the pinset, this is for the core
253 #jtag_resources = self.jtag.pad_mgr.resources
254 self.add_resources(resources)
255
256 # add JTAG without scan
257 self.add_resources([JTAGResource('jtag', 0)], no_boundary_scan=True)
258
259 def add_resources(self, resources, no_boundary_scan=False):
260 print ("ASICPlatform add_resources", resources)
261 return super().add_resources(resources)
262
263 #def iter_ports(self):
264 # yield from super().iter_ports()
265 # for io in self.jtag.ios.values():
266 # print ("iter ports", io.layout, io)
267 # for field in io.core.fields:
268 # yield getattr(io.core, field)
269 # for field in io.pad.fields:
270 # yield getattr(io.pad, field)
271
272 # XXX these aren't strictly necessary right now but the next
273 # phase is to add JTAG Boundary Scan so it maaay be worth adding?
274 # at least for the print statements
275 def get_input(self, pin, port, attrs, invert):
276 self._check_feature("single-ended input", pin, attrs,
277 valid_xdrs=(0,), valid_attrs=None)
278
279 m = Module()
280 print (" get_input", pin, "port", port, port.layout)
281 m.d.comb += pin.i.eq(self._invert_if(invert, port))
282 return m
283
284 def get_output(self, pin, port, attrs, invert):
285 self._check_feature("single-ended output", pin, attrs,
286 valid_xdrs=(0,), valid_attrs=None)
287
288 m = Module()
289 print (" get_output", pin, "port", port, port.layout)
290 m.d.comb += port.eq(self._invert_if(invert, pin.o))
291 return m
292
293 def get_tristate(self, pin, port, attrs, invert):
294 self._check_feature("single-ended tristate", pin, attrs,
295 valid_xdrs=(0,), valid_attrs=None)
296
297 print (" get_tristate", pin, "port", port, port.layout)
298 m = Module()
299 print (" pad", pin, port, attrs)
300 print (" pin", pin.layout)
301 return m
302 # m.submodules += Instance("$tribuf",
303 # p_WIDTH=pin.width,
304 # i_EN=pin.oe,
305 # i_A=self._invert_if(invert, pin.o),
306 # o_Y=port,
307 # )
308 m.d.comb += io.core.o.eq(pin.o)
309 m.d.comb += io.core.oe.eq(pin.oe)
310 m.d.comb += pin.i.eq(io.core.i)
311 m.d.comb += io.pad.i.eq(port.i)
312 m.d.comb += port.o.eq(io.pad.o)
313 m.d.comb += port.oe.eq(io.pad.oe)
314 return m
315
316 def get_input_output(self, pin, port, attrs, invert):
317 self._check_feature("single-ended input/output", pin, attrs,
318 valid_xdrs=(0,), valid_attrs=None)
319
320 print (" get_input_output", pin, "port", port, port.layout)
321 m = Module()
322 print (" port layout", port.layout)
323 print (" pin", pin)
324 print (" layout", pin.layout)
325 #m.submodules += Instance("$tribuf",
326 # p_WIDTH=pin.width,
327 # i_EN=io.pad.oe,
328 # i_A=self._invert_if(invert, io.pad.o),
329 # o_Y=port,
330 #)
331 # Create aliases for the port sub-signals
332 port_i = port.io[0]
333 port_o = port.io[1]
334 port_oe = port.io[2]
335
336 m.d.comb += pin.i.eq(self._invert_if(invert, port_i))
337 m.d.comb += port_o.eq(self._invert_if(invert, pin.o))
338 m.d.comb += port_oe.eq(pin.oe)
339
340 return m
341
342 def toolchain_prepare(self, fragment, name, **kwargs):
343 """override toolchain_prepare in order to grab the fragment
344 """
345 self.fragment = fragment
346 return super().toolchain_prepare(fragment, name, **kwargs)
347
348 """
349 and to create a Platform instance with that list, and build
350 something random
351
352 p=Platform()
353 p.resources=listofstuff
354 p.build(Blinker())
355 """
356 pinset = dummy_pinset()
357 print(pinset)
358 resources = create_resources(pinset)
359 top = Blinker(pinset, resources, no_jtag_connect=False)#True)
360
361 vl = rtlil.convert(top, ports=top.ports())
362 with open("test_jtag_blinker.il", "w") as f:
363 f.write(vl)
364
365 if False:
366 # XXX these modules are all being added *AFTER* the build process links
367 # everything together. the expectation that this would work is...
368 # unrealistic. ordering, clearly, is important.
369
370 # dut = JTAG(test_pinset(), wb_data_wid=64, domain="sync")
371 top.jtag.stop = False
372 # rather than the client access the JTAG bus directly
373 # create an alternative that the client sets
374 class Dummy: pass
375 cdut = Dummy()
376 cdut.cbus = JTAGInterface()
377
378 # set up client-server on port 44843-something
379 top.jtag.s = JTAGServer()
380 cdut.c = JTAGClient()
381 top.jtag.s.get_connection()
382 #else:
383 # print ("running server only as requested, use openocd remote to test")
384 # sys.stdout.flush()
385 # top.jtag.s.get_connection(None) # block waiting for connection
386
387 # take copy of ir_width and scan_len
388 cdut._ir_width = top.jtag._ir_width
389 cdut.scan_len = top.jtag.scan_len
390
391 p = ASICPlatform (resources, top.jtag)
392 p.build(top)
393 # this is what needs to gets treated as "top", after "main module" top
394 # is augmented with IO pads with JTAG tacked on. the expectation that
395 # the get_input() etc functions will be called magically by some other
396 # function is unrealistic.
397 top_fragment = p.fragment
398
399 # XXX simulating top (the module that does not itself contain IO pads
400 # because that's covered by build) cannot possibly be expected to work
401 # particularly when modules have been added *after* the platform build()
402 # function has been called.
403
404 def test_case0():
405 print("Starting sanity test case!")
406 print("printing out list of stuff in top")
407 print ("JTAG IOs", top.jtag.ios)
408 # ok top now has a variable named "gpio", let's enumerate that too
409 print("printing out list of stuff in top.gpio and its type")
410 print(top.gpio.__class__.__name__, dir(top.gpio))
411 # ok, it's a nmigen Record, therefore it has a layout. let's print
412 # that too
413 print("top.gpio is a Record therefore has fields and a layout")
414 print(" layout:", top.gpio.layout)
415 print(" fields:", top.gpio.fields)
416 print("Fun never ends...")
417 print(" layout, gpio2:", top.gpio.layout['gpio2'])
418 print(" fields, gpio2:", top.gpio.fields['gpio2'])
419 print(top.jtag.__class__.__name__, dir(top.jtag))
420 print("Pads:")
421 print(top.jtag.resource_table_pads[('gpio', 0)])
422
423 # etc etc. you get the general idea
424 delayVal = 0.2e-6
425 yield top.uart.rx.eq(0)
426 yield Delay(delayVal)
427 yield Settle()
428 yield top.gpio.gpio2.o.eq(0)
429 yield top.gpio.gpio3.o.eq(1)
430 yield
431 yield top.gpio.gpio3.oe.eq(1)
432 yield
433 yield top.gpio.gpio3.oe.eq(0)
434 # grab the JTAG resource pad
435 gpios_pad = top.jtag.resource_table_pads[('gpio', 0)]
436 yield gpios_pad.gpio3.i.eq(1)
437 yield Delay(delayVal)
438 yield Settle()
439 yield top.gpio.gpio2.oe.eq(1)
440 yield top.gpio.gpio3.oe.eq(1)
441 yield gpios_pad.gpio3.i.eq(0)
442 yield top.jtag.gpio.gpio2.i.eq(1)
443 yield Delay(delayVal)
444 yield Settle()
445 gpio_o2 = 0
446 for _ in range(20):
447 # get a value first (as an integer). you were trying to set
448 # it to the actual Signal. this is not going to work. or if
449 # it does, it's very scary.
450 gpio_o2 = not gpio_o2
451 yield top.gpio.gpio2.o.eq(gpio_o2)
452
453 # ditto: here you are trying to set to an AST expression
454 # which is inadviseable (likely to fail)
455 gpio_o3 = not gpio_o2
456 yield top.gpio.gpio3.o.eq(gpio_o3)
457 yield Delay(delayVal)
458 yield Settle()
459 # grab the JTAG resource pad
460 uart_pad = top.jtag.resource_table_pads[('uart', 0)]
461 yield uart_pad.rx.i.eq(gpio_o2)
462 yield Delay(delayVal)
463 yield Settle()
464 yield # one clock cycle
465 tx_val = yield uart_pad.tx.o
466 print ("xmit uart", tx_val, gpio_o2)
467
468 print ("jtag pad table keys")
469 print (top.jtag.resource_table_pads.keys())
470 uart_pad = top.jtag.resource_table_pads[('uart', 0)]
471 print ("uart pad", uart_pad)
472 print ("uart pad", uart_pad.layout)
473
474 yield top.gpio.gpio2.oe.eq(0)
475 yield top.gpio.gpio3.oe.eq(0)
476 yield top.jtag.gpio.gpio2.i.eq(0)
477 yield Delay(delayVal)
478 yield Settle()
479
480 # Code borrowed from cesar, runs, but shouldn't actually work because of
481 # self. statements and non-existent signal names.
482 def test_case1():
483 print("Example test case")
484 yield Passive()
485 while True:
486 # Settle() is needed to give a quick response to
487 # the zero delay case
488 yield Settle()
489 # wait for rel_o to become active
490 while not (yield self.rel_o):
491 yield
492 yield Settle()
493 # read the transaction parameters
494 assert self.expecting, "an unexpected result was produced"
495 delay = (yield self.delay)
496 expected = (yield self.expected)
497 # wait for `delay` cycles
498 for _ in range(delay):
499 yield
500 # activate go_i for one cycle
501 yield self.go_i.eq(1)
502 yield self.count.eq(self.count + 1)
503 yield
504 # check received data against the expected value
505 result = (yield self.port)
506 assert result == expected,\
507 f"expected {expected}, received {result}"
508 yield self.go_i.eq(0)
509 yield self.port.eq(0)
510
511 def test_gpios():
512 print("Starting GPIO test case!")
513 # Grab GPIO pad resource from JTAG BS
514 gpios_pad = top.jtag.resource_table_pads[('gpio', 0)]
515
516 # Have the sim run through a for-loop where the gpio_o_test is
517 # incremented like a counter (0000, 0001...)
518 # At each iteration of the for-loop, assert:
519 # + output set at core matches output seen at pad
520 # TODO + input set at pad matches input seen at core
521 # TODO + if gpio_o_test bit is cleared, output seen at pad matches
522 # input seen at pad
523 num_gpio_o_states = top.gpio_o_test.width**2
524 print("Num of permutations of gpio_o_test record: ", num_gpio_o_states)
525 for gpio_o_val in range(0, num_gpio_o_states):
526 yield top.gpio_o_test.eq(gpio_o_val)
527 yield Settle()
528 yield # Move to the next clk cycle
529
530 print(type(top.gpio.gpio0.o), type(gpios_pad.gpio0.o))
531 print(top.gpio.gpio0.o, gpios_pad.gpio0.o)
532 core_out = yield top.gpio.gpio0.o
533 pad_out = yield gpios_pad.gpio0.o
534 assert core_out == pad_out
535
536
537 # Another for loop to run through gpio_oe_test. Assert:
538 # + oe set at core matches oe seen at pad.
539 # TODO
540
541 sim = Simulator(top)
542 sim.add_clock(1e-6, domain="sync") # standard clock
543
544 #sim.add_sync_process(wrap(jtag_srv(top))) #? jtag server
545 #if len(sys.argv) != 2 or sys.argv[1] != 'server':
546 #sim.add_sync_process(wrap(jtag_sim(cdut, top.jtag))) # actual jtag tester
547 #sim.add_sync_process(wrap(dmi_sim(top.jtag))) # handles (pretends to be) DMI
548
549 #sim.add_sync_process(wrap(test_case1()))
550 #sim.add_sync_process(wrap(test_case0()))
551 sim.add_sync_process(wrap(test_gpios()))
552
553 with sim.write_vcd("blinker_test.vcd"):
554 sim.run()