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