more messing about
[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 # get the UART resource, mess with the output tx
170 uart = self.jtag.request('uart')
171 print ("uart fields", uart, uart.fields)
172 self.intermediary = Signal()
173 m.d.comb += uart.tx.eq(self.intermediary)
174 m.d.comb += self.intermediary.eq(uart.rx)
175
176 # to even be able to get at objects, you first have to make them
177 # available - i.e. not as local variables
178 self.gpio = gpio
179 self.uart = uart
180
181 # sigh these wire up to the pads so you cannot set Signals
182 # that are already wired
183 if self.no_jtag_connect: # bypass jtag pad connect for testing purposes
184 return m
185 return self.jtag.boundary_elaborate(m, platform)
186
187 def ports(self):
188 return list(self)
189
190 def __iter__(self):
191 yield from self.jtag.iter_ports()
192
193 '''
194 _trellis_command_templates = [
195 r"""
196 {{invoke_tool("yosys")}}
197 {{quiet("-q")}}
198 {{get_override("yosys_opts")|options}}
199 -l {{name}}.rpt
200 {{name}}.ys
201 """,
202 ]
203 '''
204
205 # sigh, have to create a dummy platform for now.
206 # TODO: investigate how the heck to get it to output ilang. or verilog.
207 # or, anything, really. but at least it doesn't barf
208 class ASICPlatform(TemplatedPlatform):
209 connectors = []
210 resources = OrderedDict()
211 required_tools = []
212 command_templates = ['/bin/true'] # no command needed: stops barfing
213 file_templates = {
214 **TemplatedPlatform.build_script_templates,
215 "{{name}}.il": r"""
216 # {{autogenerated}}
217 {{emit_rtlil()}}
218 """,
219 "{{name}}.debug.v": r"""
220 /* {{autogenerated}} */
221 {{emit_debug_verilog()}}
222 """,
223 }
224 toolchain = None
225 default_clk = "clk" # should be picked up / overridden by platform sys.clk
226 default_rst = "rst" # should be picked up / overridden by platform sys.rst
227
228 def __init__(self, resources, jtag):
229 self.jtag = jtag
230 super().__init__()
231
232 # create set of pin resources based on the pinset, this is for the core
233 #jtag_resources = self.jtag.pad_mgr.resources
234 self.add_resources(resources)
235
236 # add JTAG without scan
237 self.add_resources([JTAGResource('jtag', 0)], no_boundary_scan=True)
238
239 def add_resources(self, resources, no_boundary_scan=False):
240 print ("ASICPlatform add_resources", resources)
241 return super().add_resources(resources)
242
243 #def iter_ports(self):
244 # yield from super().iter_ports()
245 # for io in self.jtag.ios.values():
246 # print ("iter ports", io.layout, io)
247 # for field in io.core.fields:
248 # yield getattr(io.core, field)
249 # for field in io.pad.fields:
250 # yield getattr(io.pad, field)
251
252 # XXX these aren't strictly necessary right now but the next
253 # phase is to add JTAG Boundary Scan so it maaay be worth adding?
254 # at least for the print statements
255 def get_input(self, pin, port, attrs, invert):
256 self._check_feature("single-ended input", pin, attrs,
257 valid_xdrs=(0,), valid_attrs=None)
258
259 m = Module()
260 print (" get_input", pin, "port", port, port.layout)
261 m.d.comb += pin.i.eq(self._invert_if(invert, port))
262 return m
263
264 def get_output(self, pin, port, attrs, invert):
265 self._check_feature("single-ended output", pin, attrs,
266 valid_xdrs=(0,), valid_attrs=None)
267
268 m = Module()
269 print (" get_output", pin, "port", port, port.layout)
270 m.d.comb += port.eq(self._invert_if(invert, pin.o))
271 return m
272
273 def get_tristate(self, pin, port, attrs, invert):
274 self._check_feature("single-ended tristate", pin, attrs,
275 valid_xdrs=(0,), valid_attrs=None)
276
277 print (" get_tristate", pin, "port", port, port.layout)
278 m = Module()
279 print (" pad", pin, port, attrs)
280 print (" pin", pin.layout)
281 return m
282 # m.submodules += Instance("$tribuf",
283 # p_WIDTH=pin.width,
284 # i_EN=pin.oe,
285 # i_A=self._invert_if(invert, pin.o),
286 # o_Y=port,
287 # )
288 m.d.comb += io.core.o.eq(pin.o)
289 m.d.comb += io.core.oe.eq(pin.oe)
290 m.d.comb += pin.i.eq(io.core.i)
291 m.d.comb += io.pad.i.eq(port.i)
292 m.d.comb += port.o.eq(io.pad.o)
293 m.d.comb += port.oe.eq(io.pad.oe)
294 return m
295
296 def get_input_output(self, pin, port, attrs, invert):
297 self._check_feature("single-ended input/output", pin, attrs,
298 valid_xdrs=(0,), valid_attrs=None)
299
300 print (" get_input_output", pin, "port", port, port.layout)
301 m = Module()
302 print (" port layout", port.layout)
303 print (" pin", pin)
304 print (" layout", pin.layout)
305 #m.submodules += Instance("$tribuf",
306 # p_WIDTH=pin.width,
307 # i_EN=io.pad.oe,
308 # i_A=self._invert_if(invert, io.pad.o),
309 # o_Y=port,
310 #)
311 # Create aliases for the port sub-signals
312 port_i = port.io[0]
313 port_o = port.io[1]
314 port_oe = port.io[2]
315
316 m.d.comb += pin.i.eq(self._invert_if(invert, port_i))
317 m.d.comb += port_o.eq(self._invert_if(invert, pin.o))
318 m.d.comb += port_oe.eq(pin.oe)
319
320 return m
321
322 def toolchain_prepare(self, fragment, name, **kwargs):
323 """override toolchain_prepare in order to grab the fragment
324 """
325 self.fragment = fragment
326 return super().toolchain_prepare(fragment, name, **kwargs)
327
328 """
329 and to create a Platform instance with that list, and build
330 something random
331
332 p=Platform()
333 p.resources=listofstuff
334 p.build(Blinker())
335 """
336 pinset = dummy_pinset()
337 print(pinset)
338 resources = create_resources(pinset)
339 top = Blinker(pinset, resources)
340
341 vl = rtlil.convert(top, ports=top.ports())
342 with open("test_jtag_blinker.il", "w") as f:
343 f.write(vl)
344
345 if False:
346 # XXX these modules are all being added *AFTER* the build process links
347 # everything together. the expectation that this would work is...
348 # unrealistic. ordering, clearly, is important.
349
350 # dut = JTAG(test_pinset(), wb_data_wid=64, domain="sync")
351 top.jtag.stop = False
352 # rather than the client access the JTAG bus directly
353 # create an alternative that the client sets
354 class Dummy: pass
355 cdut = Dummy()
356 cdut.cbus = JTAGInterface()
357
358 # set up client-server on port 44843-something
359 top.jtag.s = JTAGServer()
360 cdut.c = JTAGClient()
361 top.jtag.s.get_connection()
362 #else:
363 # print ("running server only as requested, use openocd remote to test")
364 # sys.stdout.flush()
365 # top.jtag.s.get_connection(None) # block waiting for connection
366
367 # take copy of ir_width and scan_len
368 cdut._ir_width = top.jtag._ir_width
369 cdut.scan_len = top.jtag.scan_len
370
371 p = ASICPlatform (resources, top.jtag)
372 p.build(top)
373 # this is what needs to gets treated as "top", after "main module" top
374 # is augmented with IO pads with JTAG tacked on. the expectation that
375 # the get_input() etc functions will be called magically by some other
376 # function is unrealistic.
377 top_fragment = p.fragment
378
379 # XXX simulating top (the module that does not itself contain IO pads
380 # because that's covered by build) cannot possibly be expected to work
381 # particularly when modules have been added *after* the platform build()
382 # function has been called.
383
384 def test_case0():
385 print("Starting sanity test case!")
386 print("printing out list of stuff in top")
387 print ("JTAG IOs", top.jtag.ios)
388 # ok top now has a variable named "gpio", let's enumerate that too
389 print("printing out list of stuff in top.gpio and its type")
390 print(top.gpio.__class__.__name__, dir(top.gpio))
391 # ok, it's a nmigen Record, therefore it has a layout. let's print
392 # that too
393 print("top.gpio is a Record therefore has fields and a layout")
394 print(" layout:", top.gpio.layout)
395 print(" fields:", top.gpio.fields)
396 print("Fun never ends...")
397 print(" layout, gpio2:", top.gpio.layout['gpio2'])
398 print(" fields, gpio2:", top.gpio.fields['gpio2'])
399 print(top.jtag.__class__.__name__, dir(top.jtag))
400
401 # etc etc. you get the general idea
402 delayVal = 0.2e-6
403 yield top.uart.rx.eq(0)
404 yield Delay(delayVal)
405 yield Settle()
406 yield top.gpio.gpio2.o.eq(0)
407 yield top.gpio.gpio3.o.eq(1)
408 yield Delay(delayVal)
409 yield Settle()
410 yield top.gpio.gpio2.oe.eq(1)
411 yield top.gpio.gpio3.oe.eq(1)
412 #yield top.jtag.gpio.gpio2.i.eq(1)
413 yield Delay(delayVal)
414 yield Settle()
415 gpio_o2 = 0
416 for _ in range(20):
417 # get a value first (as an integer). you were trying to set
418 # it to the actual Signal. this is not going to work. or if
419 # it does, it's very scary.
420 gpio_o2 = not gpio_o2
421 yield top.gpio.gpio2.o.eq(gpio_o2)
422
423 # ditto: here you are trying to set to an AST expression
424 # which is inadviseable (likely to fail)
425 yield top.gpio.gpio3.o.eq(~top.gpio.gpio3.o)
426 yield Delay(delayVal)
427 yield Settle()
428 # grab the JTAG resource pad
429 uart_pad = top.jtag.resource_table_pads[('uart', 0)]
430 yield uart_pad.rx.i.eq(gpio_o2)
431 yield Delay(delayVal)
432 yield Settle()
433 yield # one clock cycle
434 tx_val = yield uart_pad.tx.o
435 print ("xmit uart", tx_val, gpio_o2)
436
437 print ("jtag pad table keys")
438 print (top.jtag.resource_table_pads.keys())
439 uart_pad = top.jtag.resource_table_pads[('uart', 0)]
440 print ("uart pad", uart_pad)
441 print ("uart pad", uart_pad.layout)
442
443 yield top.gpio.gpio2.oe.eq(0)
444 yield top.gpio.gpio3.oe.eq(0)
445 #yield top.jtag.gpio.gpio2.i.eq(0)
446 yield Delay(delayVal)
447 yield Settle()
448
449 # Code borrowed from cesar, runs, but shouldn't actually work because of
450 # self. statements and non-existent signal names.
451 def test_case1():
452 print("Example test case")
453 yield Passive()
454 while True:
455 # Settle() is needed to give a quick response to
456 # the zero delay case
457 yield Settle()
458 # wait for rel_o to become active
459 while not (yield self.rel_o):
460 yield
461 yield Settle()
462 # read the transaction parameters
463 assert self.expecting, "an unexpected result was produced"
464 delay = (yield self.delay)
465 expected = (yield self.expected)
466 # wait for `delay` cycles
467 for _ in range(delay):
468 yield
469 # activate go_i for one cycle
470 yield self.go_i.eq(1)
471 yield self.count.eq(self.count + 1)
472 yield
473 # check received data against the expected value
474 result = (yield self.port)
475 assert result == expected,\
476 f"expected {expected}, received {result}"
477 yield self.go_i.eq(0)
478 yield self.port.eq(0)
479
480 sim = Simulator(top)
481 sim.add_clock(1e-6, domain="sync") # standard clock
482
483 #sim.add_sync_process(wrap(jtag_srv(top))) #? jtag server
484 #if len(sys.argv) != 2 or sys.argv[1] != 'server':
485 #sim.add_sync_process(wrap(jtag_sim(cdut, top.jtag))) # actual jtag tester
486 #sim.add_sync_process(wrap(dmi_sim(top.jtag))) # handles (pretends to be) DMI
487
488 #sim.add_sync_process(wrap(test_case1()))
489 sim.add_sync_process(wrap(test_case0()))
490
491 with sim.write_vcd("blinker_test.vcd"):
492 sim.run()