copied more code, still broken
[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
117 def elaborate(self, platform):
118 m = Module()
119 m.submodules.jtag = self.jtag
120 count = Signal(5)
121 m.d.sync += count.eq(5)
122 print ("resources", platform.resources.items())
123 gpio = platform.request('gpio')
124 print (gpio, gpio.layout, gpio.fields)
125 # get the GPIO bank, mess about with some of the pins
126 m.d.comb += gpio.gpio0.io.o.eq(1)
127 m.d.comb += gpio.gpio1.io.o.eq(gpio.gpio2.io.i)
128 m.d.comb += gpio.gpio1.io.oe.eq(count[4])
129 m.d.sync += count[0].eq(gpio.gpio1.io.i)
130 # get the UART resource, mess with the output tx
131 uart = platform.request('uart')
132 print (uart, uart.fields)
133 intermediary = Signal()
134 m.d.comb += uart.tx.eq(intermediary)
135 m.d.comb += intermediary.eq(uart.rx)
136 return m
137
138
139 '''
140 _trellis_command_templates = [
141 r"""
142 {{invoke_tool("yosys")}}
143 {{quiet("-q")}}
144 {{get_override("yosys_opts")|options}}
145 -l {{name}}.rpt
146 {{name}}.ys
147 """,
148 ]
149 '''
150
151 # sigh, have to create a dummy platform for now.
152 # TODO: investigate how the heck to get it to output ilang. or verilog.
153 # or, anything, really. but at least it doesn't barf
154 class ASICPlatform(TemplatedPlatform):
155 connectors = []
156 resources = OrderedDict()
157 required_tools = []
158 command_templates = ['/bin/true']
159 file_templates = {
160 **TemplatedPlatform.build_script_templates,
161 "{{name}}.il": r"""
162 # {{autogenerated}}
163 {{emit_rtlil()}}
164 """,
165 "{{name}}.debug.v": r"""
166 /* {{autogenerated}} */
167 {{emit_debug_verilog()}}
168 """,
169 }
170 toolchain = None
171 default_clk = "clk" # should be picked up / overridden by platform sys.clk
172 default_rst = "rst" # should be picked up / overridden by platform sys.rst
173
174 def __init__(self, resources, jtag):
175 self.pad_mgr = ResourceManager([], [])
176 self.jtag = jtag
177 super().__init__()
178 # create set of pin resources based on the pinset, this is for the core
179 self.add_resources(resources)
180 # record resource lookup between core IO names and pads
181 self.padlookup = {}
182
183 def request(self, name, number=0, *, dir=None, xdr=None):
184 """request a Resource (e.g. name="uart", number=0) which will
185 return a data structure containing Records of all the pins.
186
187 this override will also - automatically - create a JTAG Boundary Scan
188 connection *without* any change to the actual Platform.request() API
189 """
190 # okaaaay, bit of shenanigens going on: the important data structure
191 # here is Resourcemanager._ports. requests add to _ports, which is
192 # what needs redirecting. therefore what has to happen is to
193 # capture the number of ports *before* the request. sigh.
194 start_ports = len(self._ports)
195 value = super().request(name, number, dir=dir, xdr=xdr)
196 end_ports = len(self._ports)
197
198 # now make a corresponding (duplicate) request to the pad manager
199 # BUT, if it doesn't exist, don't sweat it: all it means is, the
200 # application did not request Boundary Scan for that resource.
201 pad_start_ports = len(self.pad_mgr._ports)
202 try:
203 pvalue = self.pad_mgr.request(name, number, dir=dir, xdr=xdr)
204 except AssertionError:
205 return value
206 pad_end_ports = len(self.pad_mgr._ports)
207
208 # ok now we have the lengths: now create a lookup between the pad
209 # and the core, so that JTAG boundary scan can be inserted in between
210 core = self._ports[start_ports:end_ports]
211 pads = self.pad_mgr._ports[pad_start_ports:pad_end_ports]
212 # oops if not the same numbers added. it's a duplicate. shouldn't happen
213 assert len(core) == len(pads), "argh, resource manager error"
214 print ("core", core)
215 print ("pads", pads)
216
217 # pad/core each return a list of tuples of (res, pin, port, attrs)
218 for pad, core in zip(pads, core):
219 # create a lookup on pin name to get at the hidden pad instance
220 # this pin name will be handed to get_input, get_output etc.
221 # and without the padlookup you can't find the (duplicate) pad.
222 # note that self.padlookup and self.jtag.ios use the *exact* same
223 # pin.name per pin
224 pin = pad[1]
225 corepin = core[1]
226 if pin is None: continue # skip when pin is None
227 assert corepin is not None # if pad was None, core should be too
228 print ("iter", pad, pin.name)
229 print ("existing pads", self.padlookup.keys())
230 assert pin.name not in self.padlookup # no overwrites allowed!
231 assert pin.name == corepin.name # has to be the same!
232 self.padlookup[pin.name] = pad # store pad by pin name
233
234 # now add the IO Shift Register. first identify the type
235 # then request a JTAG IOConn. we can't wire it up (yet) because
236 # we don't have a Module() instance. doh. that comes in get_input
237 # and get_output etc. etc.
238 iotype = resiotypes[pin.dir] # look up the C4M-JTAG IOType
239 io = self.jtag.add_io(iotype=iotype, name=pin.name) # create IOConn
240 self.jtag.ios[pin.name] = io # store IOConn Record by pin name
241
242 # finally return the value just like ResourceManager.request()
243 return value
244
245 def add_resources(self, resources, no_boundary_scan=False):
246 super().add_resources(resources)
247 if no_boundary_scan:
248 return
249 # make a *second* - identical - set of pin resources for the IO ring
250 padres = deepcopy(resources)
251 self.pad_mgr.add_resources(padres)
252
253 #def iter_ports(self):
254 # yield from super().iter_ports()
255 # for io in self.jtag.ios.values():
256 # print ("iter ports", io.layout, io)
257 # for field in io.core.fields:
258 # yield getattr(io.core, field)
259 # for field in io.pad.fields:
260 # yield getattr(io.pad, field)
261
262 # XXX these aren't strictly necessary right now but the next
263 # phase is to add JTAG Boundary Scan so it maaay be worth adding?
264 # at least for the print statements
265 def get_input(self, pin, port, attrs, invert):
266 self._check_feature("single-ended input", pin, attrs,
267 valid_xdrs=(0,), valid_attrs=None)
268
269 m = Module()
270 print (" get_input", pin, "port", port, port.layout)
271 if pin.name in ['clk_0', 'rst_0']: # sigh
272 # simple pass-through from port to pin
273 print("No JTAG chain in-between")
274 m.d.comb += pin.i.eq(self._invert_if(invert, port))
275 return m
276 (padres, padpin, padport, padattrs) = self.padlookup[pin.name]
277 io = self.jtag.ios[pin.name]
278 print (" pad", padres, padpin, padport, attrs)
279 print (" padpin", padpin.layout)
280 print (" jtag", io.core.layout, io.pad.layout)
281 m.d.comb += pin.i.eq(self._invert_if(invert, port))
282 m.d.comb += padpin.i.eq(padport)
283 m.d.comb += padport.io.eq(io.core.i)
284 m.d.comb += io.pad.i.eq(pin.i)
285
286 print("+=+=+= pin: ", pin)
287 print("+=+=+= port: ", port.layout)
288 print("+=+=+= pad pin: ", padpin)
289 print("+=+=+= pad port: ", padport)
290 return m
291
292 def get_output(self, pin, port, attrs, invert):
293 self._check_feature("single-ended output", pin, attrs,
294 valid_xdrs=(0,), valid_attrs=None)
295
296 m = Module()
297 print (" get_output", pin, "port", port, port.layout)
298 if pin.name in ['clk_0', 'rst_0']: # sigh
299 # simple pass-through from pin to port
300 print("No JTAG chain in-between")
301 m.d.comb += port.eq(self._invert_if(invert, pin.o))
302 return m
303 (padres, padpin, padport, padattrs) = self.padlookup[pin.name]
304 io = self.jtag.ios[pin.name]
305 print (" pad", padres, padpin, padport, padattrs)
306 print (" pin", padpin.layout)
307 print (" jtag", io.core.layout, io.pad.layout)
308 m.d.comb += port.eq(self._invert_if(invert, pin.o))
309 m.d.comb += padport.io.eq(self._invert_if(invert, padpin.o))
310 m.d.comb += io.core.o.eq(port.io)
311 m.d.comb += padpin.o.eq(io.pad.o)
312 return m
313
314 def get_tristate(self, pin, port, attrs, invert):
315 self._check_feature("single-ended tristate", pin, attrs,
316 valid_xdrs=(0,), valid_attrs=None)
317
318 print (" get_tristate", pin, "port", port, port.layout)
319 m = Module()
320 if pin.name in ['clk_0', 'rst_0']: # sigh
321 print("No JTAG chain in-between")
322 m.submodules += Instance("$tribuf",
323 p_WIDTH=pin.width,
324 i_EN=pin.oe,
325 i_A=self._invert_if(invert, pin.o),
326 o_Y=port,
327 )
328 return m
329 (res, pin, port, attrs) = self.padlookup[pin.name]
330 io = self.jtag.ios[pin.name]
331 print (" pad", res, pin, port, attrs)
332 print (" pin", pin.layout)
333 print (" jtag", io.core.layout, io.pad.layout)
334 #m.submodules += Instance("$tribuf",
335 # p_WIDTH=pin.width,
336 # i_EN=io.pad.oe,
337 # i_A=self._invert_if(invert, io.pad.o),
338 # o_Y=port,
339 #)
340 m.d.comb += io.core.o.eq(pin.o)
341 m.d.comb += io.core.oe.eq(pin.oe)
342 m.d.comb += pin.i.eq(io.core.i)
343 m.d.comb += io.pad.i.eq(port.i)
344 m.d.comb += port.o.eq(io.pad.o)
345 m.d.comb += port.oe.eq(io.pad.oe)
346 return m
347
348 def get_input_output(self, pin, port, attrs, invert):
349 self._check_feature("single-ended input/output", pin, attrs,
350 valid_xdrs=(0,), valid_attrs=None)
351
352 print (" get_input_output", pin, "port", port, port.layout)
353 m = Module()
354 if pin.name in ['clk_0', 'rst_0']: # sigh
355 print("No JTAG chain in-between")
356 m.submodules += Instance("$tribuf",
357 p_WIDTH=pin.width,
358 i_EN=pin.oe,
359 i_A=self._invert_if(invert, pin.o),
360 o_Y=port,
361 )
362 m.d.comb += pin.i.eq(self._invert_if(invert, port))
363 return m
364 (padres, padpin, padport, padattrs) = self.padlookup[pin.name]
365 io = self.jtag.ios[pin.name]
366 print (" pad", padres, padpin, padport, padattrs)
367 print (" pin", padpin.layout)
368 print (" port layout", port.layout)
369 print (" jtag", io.core.layout, io.pad.layout)
370 #m.submodules += Instance("$tribuf",
371 # p_WIDTH=pin.width,
372 # i_EN=io.pad.oe,
373 # i_A=self._invert_if(invert, io.pad.o),
374 # o_Y=port,
375 #)
376 # Create aliases for the port sub-signals
377 port_i = port.io[0]
378 port_o = port.io[1]
379 port_oe = port.io[2]
380
381 padport_i = padport.io[0]
382 padport_o = padport.io[1]
383 padport_oe = padport.io[2]
384
385 # Connect SoC pins to SoC port
386 m.d.comb += pin.i.eq(port_i)
387 m.d.comb += port_o.eq(pin.o)
388 m.d.comb += port_oe.eq(pin.oe)
389 # Connect SoC port to JTAG io.core side
390 m.d.comb += port_i.eq(io.core.i)
391 m.d.comb += io.core.o.eq(port_o)
392 m.d.comb += io.core.oe.eq(port_oe)
393 # Connect JTAG io.pad side to pad port
394 m.d.comb += io.pad.i.eq(padport_i)
395 m.d.comb += padport_o.eq(io.pad.o)
396 m.d.comb += padport_oe.eq(io.pad.oe)
397 # Connect pad port to pad pins
398 m.d.comb += padport_i.eq(padpin.i)
399 m.d.comb += padpin.o.eq(padport_o)
400 m.d.comb += padpin.oe.eq(padport_oe)
401 return m
402
403
404 """
405 and to create a Platform instance with that list, and build
406 something random
407
408 p=Platform()
409 p.resources=listofstuff
410 p.build(Blinker())
411 """
412 pinset = dummy_pinset()
413 top = Blinker(pinset)
414 print(pinset)
415 resources = create_resources(pinset)
416 p = ASICPlatform (resources, top.jtag)
417 p.build(top)
418
419 # dut = JTAG(test_pinset(), wb_data_wid=64, domain="sync")
420 top.jtag.stop = False
421 # rather than the client access the JTAG bus directly
422 # create an alternative that the client sets
423 class Dummy: pass
424 cdut = Dummy()
425 cdut.cbus = JTAGInterface()
426
427 # set up client-server on port 44843-something
428 top.jtag.s = JTAGServer()
429 cdut.c = JTAGClient()
430 top.jtag.s.get_connection()
431 #else:
432 # print ("running server only as requested, use openocd remote to test")
433 # sys.stdout.flush()
434 # top.jtag.s.get_connection(None) # block waiting for connection
435
436 # take copy of ir_width and scan_len
437 cdut._ir_width = top.jtag._ir_width
438 cdut.scan_len = top.jtag.scan_len
439
440 memory = Memory(width=64, depth=16)
441 sram = SRAM(memory=memory, bus=top.jtag.wb)
442
443 #m = Module()
444 #m.submodules.ast = dut
445 #m.submodules.sram = sram
446
447 sim = Simulator(top)
448 sim.add_clock(1e-6, domain="sync") # standard clock
449
450 sim.add_sync_process(wrap(jtag_srv(top))) #? jtag server
451 #if len(sys.argv) != 2 or sys.argv[1] != 'server':
452 sim.add_sync_process(wrap(jtag_sim(cdut, top.jtag))) # actual jtag tester
453 sim.add_sync_process(wrap(dmi_sim(top.jtag))) # handles (pretends to be) DMI
454
455 with sim.write_vcd("dmi2jtag_test_srv.vcd"):
456 sim.run()