33d79b5e3fa6ffb3e4ca2e401103ab2fbad4e1b3
[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 import Elaboratable, Signal, Module, Instance
6 from collections import OrderedDict
7 from jtag import JTAG, resiotypes
8 from copy import deepcopy
9
10 # extra dependencies for jtag testing (?)
11 from soc.bus.sram import SRAM
12
13 from nmigen import Memory
14 from nmigen.sim import Simulator, Delay, Settle, Tick
15
16 from nmutil.util import wrap
17
18 from soc.debug.jtagutils import (jtag_read_write_reg,
19 jtag_srv, jtag_set_reset,
20 jtag_set_ir, jtag_set_get_dr)
21
22 from c4m.nmigen.jtag.tap import TAP, IOType
23 from c4m.nmigen.jtag.bus import Interface as JTAGInterface
24 from soc.debug.dmi import DMIInterface, DBGCore
25 from soc.debug.test.dmi_sim import dmi_sim
26 from soc.debug.test.jtagremote import JTAGServer, JTAGClient
27
28 # Was thinking of using these functions, but skipped for simplicity for now
29 # XXX nope. the output from JSON file.
30 #from pinfunctions import (i2s, lpc, emmc, sdmmc, mspi, mquadspi, spi,
31 # quadspi, i2c, mi2c, jtag, uart, uartfull, rgbttl, ulpi, rgmii, flexbus1,
32 # flexbus2, sdram1, sdram2, sdram3, vss, vdd, sys, eint, pwm, gpio)
33
34 # File for stage 1 pinmux tested proposed by Luke,
35 # https://bugs.libre-soc.org/show_bug.cgi?id=50#c10
36
37
38 def dummy_pinset():
39 # sigh this needs to come from pinmux.
40 gpios = []
41 for i in range(4):
42 gpios.append("%d*" % i)
43 return {'uart': ['tx+', 'rx-'],
44 'gpio': gpios,
45 'i2c': ['sda*', 'scl+']}
46
47 """
48 a function is needed which turns the results of dummy_pinset()
49 into:
50
51 [UARTResource("uart", 0, tx=..., rx=..),
52 I2CResource("i2c", 0, scl=..., sda=...),
53 Resource("gpio", 0, Subsignal("i"...), Subsignal("o"...)
54 Resource("gpio", 1, Subsignal("i"...), Subsignal("o"...)
55 ...
56 ]
57 """
58
59
60 def create_resources(pinset):
61 resources = []
62 for periph, pins in pinset.items():
63 print(periph, pins)
64 if periph == 'i2c':
65 #print("I2C required!")
66 resources.append(I2CResource('i2c', 0, sda='sda', scl='scl'))
67 elif periph == 'uart':
68 #print("UART required!")
69 resources.append(UARTResource('uart', 0, tx='tx', rx='rx'))
70 elif periph == 'gpio':
71 #print("GPIO required!")
72 print ("GPIO is defined as '*' type, meaning i, o and oe needed")
73 ios = []
74 for pin in pins:
75 pname = "gpio"+pin[:-1] # strip "*" on end
76 # urrrr... tristsate and io assume a single pin which is
77 # of course exactly what we don't want in an ASIC: we want
78 # *all three* pins but the damn port is not outputted
79 # as a triplet, it's a single Record named "io". sigh.
80 # therefore the only way to get a triplet of i/o/oe
81 # is to *actually* create explicit triple pins
82 pad = Subsignal("io",
83 Pins("%s_i %s_o %s_oe" % (pname, pname, pname),
84 dir="io", assert_width=3))
85 ios.append(Resource(pname, 0, pad))
86 resources.append(Resource.family(periph, 0, default_name="gpio",
87 ios=ios))
88
89 # add clock and reset
90 clk = Resource("clk", 0, Pins("sys_clk", dir="i"))
91 rst = Resource("rst", 0, Pins("sys_rst", dir="i"))
92 resources.append(clk)
93 resources.append(rst)
94 return resources
95
96
97 def UARTResource(*args, rx, tx):
98 io = []
99 io.append(Subsignal("rx", Pins(rx, dir="i", assert_width=1)))
100 io.append(Subsignal("tx", Pins(tx, dir="o", assert_width=1)))
101 return Resource.family(*args, default_name="uart", ios=io)
102
103
104 def I2CResource(*args, scl, sda):
105 io = []
106 io.append(Subsignal("scl", Pins(scl, dir="io", assert_width=1)))
107 io.append(Subsignal("sda", Pins(sda, dir="io", assert_width=1)))
108 return Resource.family(*args, default_name="i2c", ios=io)
109
110
111 # ridiculously-simple top-level module. doesn't even have a sync domain
112 # and can't have one until a clock has been established by ASICPlatform.
113 class Blinker(Elaboratable):
114 def __init__(self, pinset):
115 self.jtag = JTAG({}, "sync")
116 memory = Memory(width=32, depth=16)
117 self.sram = SRAM(memory=memory, bus=self.jtag.wb)
118
119
120 def elaborate(self, platform):
121 m = Module()
122 m.submodules.jtag = self.jtag
123 m.submodules.sram = self.sram
124
125 count = Signal(5)
126 m.d.sync += count.eq(5)
127 print ("resources", platform.resources.items())
128 gpio = platform.request('gpio')
129 print (gpio, gpio.layout, gpio.fields)
130 # get the GPIO bank, mess about with some of the pins
131 m.d.comb += gpio.gpio0.io.o.eq(1)
132 m.d.comb += gpio.gpio1.io.o.eq(gpio.gpio2.io.i)
133 m.d.comb += gpio.gpio1.io.oe.eq(count[4])
134 m.d.sync += count[0].eq(gpio.gpio1.io.i)
135 # get the UART resource, mess with the output tx
136 uart = platform.request('uart')
137 print (uart, uart.fields)
138 intermediary = Signal()
139 m.d.comb += uart.tx.eq(intermediary)
140 m.d.comb += intermediary.eq(uart.rx)
141 return m
142
143
144 '''
145 _trellis_command_templates = [
146 r"""
147 {{invoke_tool("yosys")}}
148 {{quiet("-q")}}
149 {{get_override("yosys_opts")|options}}
150 -l {{name}}.rpt
151 {{name}}.ys
152 """,
153 ]
154 '''
155
156 # sigh, have to create a dummy platform for now.
157 # TODO: investigate how the heck to get it to output ilang. or verilog.
158 # or, anything, really. but at least it doesn't barf
159 class ASICPlatform(TemplatedPlatform):
160 connectors = []
161 resources = OrderedDict()
162 required_tools = []
163 command_templates = ['/bin/true']
164 file_templates = {
165 **TemplatedPlatform.build_script_templates,
166 "{{name}}.il": r"""
167 # {{autogenerated}}
168 {{emit_rtlil()}}
169 """,
170 "{{name}}.debug.v": r"""
171 /* {{autogenerated}} */
172 {{emit_debug_verilog()}}
173 """,
174 }
175 toolchain = None
176 default_clk = "clk" # should be picked up / overridden by platform sys.clk
177 default_rst = "rst" # should be picked up / overridden by platform sys.rst
178
179 def __init__(self, resources, jtag):
180 self.pad_mgr = ResourceManager([], [])
181 self.jtag = jtag
182 super().__init__()
183 # create set of pin resources based on the pinset, this is for the core
184 self.add_resources(resources)
185 # record resource lookup between core IO names and pads
186 self.padlookup = {}
187
188 def request(self, name, number=0, *, dir=None, xdr=None):
189 """request a Resource (e.g. name="uart", number=0) which will
190 return a data structure containing Records of all the pins.
191
192 this override will also - automatically - create a JTAG Boundary Scan
193 connection *without* any change to the actual Platform.request() API
194 """
195 # okaaaay, bit of shenanigens going on: the important data structure
196 # here is Resourcemanager._ports. requests add to _ports, which is
197 # what needs redirecting. therefore what has to happen is to
198 # capture the number of ports *before* the request. sigh.
199 start_ports = len(self._ports)
200 value = super().request(name, number, dir=dir, xdr=xdr)
201 end_ports = len(self._ports)
202
203 # now make a corresponding (duplicate) request to the pad manager
204 # BUT, if it doesn't exist, don't sweat it: all it means is, the
205 # application did not request Boundary Scan for that resource.
206 pad_start_ports = len(self.pad_mgr._ports)
207 try:
208 pvalue = self.pad_mgr.request(name, number, dir=dir, xdr=xdr)
209 except AssertionError:
210 return value
211 pad_end_ports = len(self.pad_mgr._ports)
212
213 # ok now we have the lengths: now create a lookup between the pad
214 # and the core, so that JTAG boundary scan can be inserted in between
215 core = self._ports[start_ports:end_ports]
216 pads = self.pad_mgr._ports[pad_start_ports:pad_end_ports]
217 # oops if not the same numbers added. it's a duplicate. shouldn't happen
218 assert len(core) == len(pads), "argh, resource manager error"
219 print ("core", core)
220 print ("pads", pads)
221
222 # pad/core each return a list of tuples of (res, pin, port, attrs)
223 for pad, core in zip(pads, core):
224 # create a lookup on pin name to get at the hidden pad instance
225 # this pin name will be handed to get_input, get_output etc.
226 # and without the padlookup you can't find the (duplicate) pad.
227 # note that self.padlookup and self.jtag.ios use the *exact* same
228 # pin.name per pin
229 pin = pad[1]
230 corepin = core[1]
231 if pin is None: continue # skip when pin is None
232 assert corepin is not None # if pad was None, core should be too
233 print ("iter", pad, pin.name)
234 print ("existing pads", self.padlookup.keys())
235 assert pin.name not in self.padlookup # no overwrites allowed!
236 assert pin.name == corepin.name # has to be the same!
237 self.padlookup[pin.name] = pad # store pad by pin name
238
239 # now add the IO Shift Register. first identify the type
240 # then request a JTAG IOConn. we can't wire it up (yet) because
241 # we don't have a Module() instance. doh. that comes in get_input
242 # and get_output etc. etc.
243 iotype = resiotypes[pin.dir] # look up the C4M-JTAG IOType
244 io = self.jtag.add_io(iotype=iotype, name=pin.name) # create IOConn
245 self.jtag.ios[pin.name] = io # store IOConn Record by pin name
246
247 # finally return the value just like ResourceManager.request()
248 return value
249
250 def add_resources(self, resources, no_boundary_scan=False):
251 super().add_resources(resources)
252 if no_boundary_scan:
253 return
254 # make a *second* - identical - set of pin resources for the IO ring
255 padres = deepcopy(resources)
256 self.pad_mgr.add_resources(padres)
257
258 #def iter_ports(self):
259 # yield from super().iter_ports()
260 # for io in self.jtag.ios.values():
261 # print ("iter ports", io.layout, io)
262 # for field in io.core.fields:
263 # yield getattr(io.core, field)
264 # for field in io.pad.fields:
265 # yield getattr(io.pad, field)
266
267 # XXX these aren't strictly necessary right now but the next
268 # phase is to add JTAG Boundary Scan so it maaay be worth adding?
269 # at least for the print statements
270 def get_input(self, pin, port, attrs, invert):
271 self._check_feature("single-ended input", pin, attrs,
272 valid_xdrs=(0,), valid_attrs=None)
273
274 m = Module()
275 print (" get_input", pin, "port", port, port.layout)
276 if pin.name in ['clk_0', 'rst_0']: # sigh
277 # simple pass-through from port to pin
278 print("No JTAG chain in-between")
279 m.d.comb += pin.i.eq(self._invert_if(invert, port))
280 return m
281 (padres, padpin, padport, padattrs) = self.padlookup[pin.name]
282 io = self.jtag.ios[pin.name]
283 print (" pad", padres, padpin, padport, attrs)
284 print (" padpin", padpin.layout)
285 print (" jtag", io.core.layout, io.pad.layout)
286 m.d.comb += pin.i.eq(self._invert_if(invert, port))
287 m.d.comb += padpin.i.eq(padport)
288 m.d.comb += padport.io.eq(io.core.i)
289 m.d.comb += io.pad.i.eq(pin.i)
290
291 print("+=+=+= pin: ", pin)
292 print("+=+=+= port: ", port.layout)
293 print("+=+=+= pad pin: ", padpin)
294 print("+=+=+= pad port: ", padport)
295 return m
296
297 def get_output(self, pin, port, attrs, invert):
298 self._check_feature("single-ended output", pin, attrs,
299 valid_xdrs=(0,), valid_attrs=None)
300
301 m = Module()
302 print (" get_output", pin, "port", port, port.layout)
303 if pin.name in ['clk_0', 'rst_0']: # sigh
304 # simple pass-through from pin to port
305 print("No JTAG chain in-between")
306 m.d.comb += port.eq(self._invert_if(invert, pin.o))
307 return m
308 (padres, padpin, padport, padattrs) = self.padlookup[pin.name]
309 io = self.jtag.ios[pin.name]
310 print (" pad", padres, padpin, padport, padattrs)
311 print (" pin", padpin.layout)
312 print (" jtag", io.core.layout, io.pad.layout)
313 m.d.comb += port.eq(self._invert_if(invert, pin.o))
314 m.d.comb += padport.io.eq(self._invert_if(invert, padpin.o))
315 m.d.comb += io.core.o.eq(port.io)
316 m.d.comb += padpin.o.eq(io.pad.o)
317 return m
318
319 def get_tristate(self, pin, port, attrs, invert):
320 self._check_feature("single-ended tristate", pin, attrs,
321 valid_xdrs=(0,), valid_attrs=None)
322
323 print (" get_tristate", pin, "port", port, port.layout)
324 m = Module()
325 if pin.name in ['clk_0', 'rst_0']: # sigh
326 print("No JTAG chain in-between")
327 m.submodules += Instance("$tribuf",
328 p_WIDTH=pin.width,
329 i_EN=pin.oe,
330 i_A=self._invert_if(invert, pin.o),
331 o_Y=port,
332 )
333 return m
334 (res, pin, port, attrs) = self.padlookup[pin.name]
335 io = self.jtag.ios[pin.name]
336 print (" pad", res, pin, port, attrs)
337 print (" pin", pin.layout)
338 print (" jtag", io.core.layout, io.pad.layout)
339 #m.submodules += Instance("$tribuf",
340 # p_WIDTH=pin.width,
341 # i_EN=io.pad.oe,
342 # i_A=self._invert_if(invert, io.pad.o),
343 # o_Y=port,
344 #)
345 m.d.comb += io.core.o.eq(pin.o)
346 m.d.comb += io.core.oe.eq(pin.oe)
347 m.d.comb += pin.i.eq(io.core.i)
348 m.d.comb += io.pad.i.eq(port.i)
349 m.d.comb += port.o.eq(io.pad.o)
350 m.d.comb += port.oe.eq(io.pad.oe)
351 return m
352
353 def get_input_output(self, pin, port, attrs, invert):
354 self._check_feature("single-ended input/output", pin, attrs,
355 valid_xdrs=(0,), valid_attrs=None)
356
357 print (" get_input_output", pin, "port", port, port.layout)
358 m = Module()
359 if pin.name in ['clk_0', 'rst_0']: # sigh
360 print("No JTAG chain in-between")
361 m.submodules += Instance("$tribuf",
362 p_WIDTH=pin.width,
363 i_EN=pin.oe,
364 i_A=self._invert_if(invert, pin.o),
365 o_Y=port,
366 )
367 m.d.comb += pin.i.eq(self._invert_if(invert, port))
368 return m
369 (padres, padpin, padport, padattrs) = self.padlookup[pin.name]
370 io = self.jtag.ios[pin.name]
371 print (" pad", padres, padpin, padport, padattrs)
372 print (" pin", padpin.layout)
373 print (" port layout", port.layout)
374 print (" jtag", io.core.layout, io.pad.layout)
375 #m.submodules += Instance("$tribuf",
376 # p_WIDTH=pin.width,
377 # i_EN=io.pad.oe,
378 # i_A=self._invert_if(invert, io.pad.o),
379 # o_Y=port,
380 #)
381 # Create aliases for the port sub-signals
382 port_i = port.io[0]
383 port_o = port.io[1]
384 port_oe = port.io[2]
385
386 padport_i = padport.io[0]
387 padport_o = padport.io[1]
388 padport_oe = padport.io[2]
389
390 # Connect SoC pins to SoC port
391 m.d.comb += pin.i.eq(port_i)
392 m.d.comb += port_o.eq(pin.o)
393 m.d.comb += port_oe.eq(pin.oe)
394 # Connect SoC port to JTAG io.core side
395 m.d.comb += port_i.eq(io.core.i)
396 m.d.comb += io.core.o.eq(port_o)
397 m.d.comb += io.core.oe.eq(port_oe)
398 # Connect JTAG io.pad side to pad port
399 m.d.comb += io.pad.i.eq(padport_i)
400 m.d.comb += padport_o.eq(io.pad.o)
401 m.d.comb += padport_oe.eq(io.pad.oe)
402 # Connect pad port to pad pins
403 m.d.comb += padport_i.eq(padpin.i)
404 m.d.comb += padpin.o.eq(padport_o)
405 m.d.comb += padpin.oe.eq(padport_oe)
406 return m
407
408 def toolchain_prepare(self, fragment, name, **kwargs):
409 """override toolchain_prepare in order to grab the fragment
410 """
411 self.fragment = fragment
412 return super().toolchain_prepare(fragment, name, **kwargs)
413
414 """
415 and to create a Platform instance with that list, and build
416 something random
417
418 p=Platform()
419 p.resources=listofstuff
420 p.build(Blinker())
421 """
422 pinset = dummy_pinset()
423 top = Blinker(pinset)
424
425 # XXX these modules are all being added *AFTER* the build process links
426 # everything together. the expectation that this would work is... unrealistic.
427 # ordering, clearly, is important.
428
429 # dut = JTAG(test_pinset(), wb_data_wid=64, domain="sync")
430 top.jtag.stop = False
431 # rather than the client access the JTAG bus directly
432 # create an alternative that the client sets
433 class Dummy: pass
434 cdut = Dummy()
435 cdut.cbus = JTAGInterface()
436
437 # set up client-server on port 44843-something
438 top.jtag.s = JTAGServer()
439 cdut.c = JTAGClient()
440 top.jtag.s.get_connection()
441 #else:
442 # print ("running server only as requested, use openocd remote to test")
443 # sys.stdout.flush()
444 # top.jtag.s.get_connection(None) # block waiting for connection
445
446 # take copy of ir_width and scan_len
447 cdut._ir_width = top.jtag._ir_width
448 cdut.scan_len = top.jtag.scan_len
449
450 print(pinset)
451 resources = create_resources(pinset)
452 p = ASICPlatform (resources, top.jtag)
453 p.build(top)
454 # this is what needs to gets treated as "top", after "main module" top
455 # is augmented with IO pads with JTAG tacked on. the expectation that
456 # the get_input() etc functions will be called magically by some other
457 # function is unrealistic.
458 top_fragment = p.fragment
459
460 # XXX simulating top (the module that does not itself contain IO pads
461 # because that's covered by build) cannot possibly be expected to work
462 # particularly when modules have been added *after* the platform build()
463 # function has been called.
464
465 sim = Simulator(top_fragment)
466 sim.add_clock(1e-6, domain="sync") # standard clock
467
468 sim.add_sync_process(wrap(jtag_srv(top))) #? jtag server
469 #if len(sys.argv) != 2 or sys.argv[1] != 'server':
470 sim.add_sync_process(wrap(jtag_sim(cdut, top.jtag))) # actual jtag tester
471 sim.add_sync_process(wrap(dmi_sim(top.jtag))) # handles (pretends to be) DMI
472
473 with sim.write_vcd("dmi2jtag_test_srv.vcd"):
474 sim.run()