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