move prototype / proof-of-concept code from ASICPlatform / Blinker test
[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.hdl.rec import Layout
6 from nmigen import Elaboratable, Signal, Module, Instance
7 from collections import OrderedDict
8 from jtag import JTAG, resiotypes
9 from copy import deepcopy
10 from nmigen.cli import rtlil
11 import sys
12
13 # extra dependencies for jtag testing (?)
14 from soc.bus.sram import SRAM
15
16 from nmigen import Memory
17 from nmigen.sim import Simulator, Delay, Settle, Tick
18
19 from nmutil.util import wrap
20
21 from soc.debug.jtagutils import (jtag_read_write_reg,
22 jtag_srv, jtag_set_reset,
23 jtag_set_ir, jtag_set_get_dr)
24
25 from c4m.nmigen.jtag.tap import TAP, IOType
26 from c4m.nmigen.jtag.bus import Interface as JTAGInterface
27 from soc.debug.dmi import DMIInterface, DBGCore
28 from soc.debug.test.dmi_sim import dmi_sim
29 from soc.debug.test.jtagremote import JTAGServer, JTAGClient
30 from nmigen.build.res import ResourceError
31
32 # Was thinking of using these functions, but skipped for simplicity for now
33 # XXX nope. the output from JSON file.
34 #from pinfunctions import (i2s, lpc, emmc, sdmmc, mspi, mquadspi, spi,
35 # quadspi, i2c, mi2c, jtag, uart, uartfull, rgbttl, ulpi, rgmii, flexbus1,
36 # flexbus2, sdram1, sdram2, sdram3, vss, vdd, sys, eint, pwm, gpio)
37
38 # File for stage 1 pinmux tested proposed by Luke,
39 # https://bugs.libre-soc.org/show_bug.cgi?id=50#c10
40
41
42 def dummy_pinset():
43 # sigh this needs to come from pinmux.
44 gpios = []
45 for i in range(4):
46 gpios.append("%d*" % i)
47 return {'uart': ['tx+', 'rx-'],
48 'gpio': gpios,
49 #'jtag': ['tms-', 'tdi-', 'tdo+', 'tck+'],
50 'i2c': ['sda*', 'scl+']}
51
52 """
53 a function is needed which turns the results of dummy_pinset()
54 into:
55
56 [UARTResource("uart", 0, tx=..., rx=..),
57 I2CResource("i2c", 0, scl=..., sda=...),
58 Resource("gpio", 0, Subsignal("i"...), Subsignal("o"...)
59 Resource("gpio", 1, Subsignal("i"...), Subsignal("o"...)
60 ...
61 ]
62 """
63
64
65 def create_resources(pinset):
66 resources = []
67 for periph, pins in pinset.items():
68 print(periph, pins)
69 if periph == 'i2c':
70 #print("I2C required!")
71 resources.append(I2CResource('i2c', 0, sda='sda', scl='scl'))
72 elif periph == 'uart':
73 #print("UART required!")
74 resources.append(UARTResource('uart', 0, tx='tx', rx='rx'))
75 elif periph == 'gpio':
76 #print("GPIO required!")
77 print ("GPIO is defined as '*' type, meaning i, o and oe needed")
78 ios = []
79 for pin in pins:
80 pname = "gpio"+pin[:-1] # strip "*" on end
81 # urrrr... tristsate and io assume a single pin which is
82 # of course exactly what we don't want in an ASIC: we want
83 # *all three* pins but the damn port is not outputted
84 # as a triplet, it's a single Record named "io". sigh.
85 # therefore the only way to get a triplet of i/o/oe
86 # is to *actually* create explicit triple pins
87 # XXX ARRRGH, doesn't work
88 #pad = Subsignal("io",
89 # Pins("%s_i %s_o %s_oe" % (pname, pname, pname),
90 # dir="io", assert_width=3))
91 #ios.append(Resource(pname, 0, pad))
92 pads = []
93 pads.append(Subsignal("i",
94 Pins(pname+"_i", dir="i", assert_width=1)))
95 pads.append(Subsignal("o",
96 Pins(pname+"_o", dir="o", assert_width=1)))
97 pads.append(Subsignal("oe",
98 Pins(pname+"_oe", dir="o", assert_width=1)))
99 ios.append(Resource.family(pname, 0, default_name=pname,
100 ios=pads))
101 resources.append(Resource.family(periph, 0, default_name="gpio",
102 ios=ios))
103
104 # add clock and reset
105 clk = Resource("clk", 0, Pins("sys_clk", dir="i"))
106 rst = Resource("rst", 0, Pins("sys_rst", dir="i"))
107 resources.append(clk)
108 resources.append(rst)
109 return resources
110
111
112 def JTAGResource(*args):
113 io = []
114 io.append(Subsignal("tms", Pins("tms", dir="i", assert_width=1)))
115 io.append(Subsignal("tdi", Pins("tdi", dir="i", assert_width=1)))
116 io.append(Subsignal("tck", Pins("tck", dir="i", assert_width=1)))
117 io.append(Subsignal("tdo", Pins("tdo", dir="o", assert_width=1)))
118 return Resource.family(*args, default_name="jtag", ios=io)
119
120 def UARTResource(*args, rx, tx):
121 io = []
122 io.append(Subsignal("rx", Pins(rx, dir="i", assert_width=1)))
123 io.append(Subsignal("tx", Pins(tx, dir="o", assert_width=1)))
124 return Resource.family(*args, default_name="uart", ios=io)
125
126
127 def I2CResource(*args, scl, sda):
128 ios = []
129 pads = []
130 pads.append(Subsignal("i", Pins(sda+"_i", dir="i", assert_width=1)))
131 pads.append(Subsignal("o", Pins(sda+"_o", dir="o", assert_width=1)))
132 pads.append(Subsignal("oe", Pins(sda+"_oe", dir="o", assert_width=1)))
133 ios.append(Resource.family(sda, 0, default_name=sda, ios=pads))
134 pads = []
135 pads.append(Subsignal("i", Pins(scl+"_i", dir="i", assert_width=1)))
136 pads.append(Subsignal("o", Pins(scl+"_o", dir="o", assert_width=1)))
137 pads.append(Subsignal("oe", Pins(scl+"_oe", dir="o", assert_width=1)))
138 ios.append(Resource.family(scl, 0, default_name=scl, ios=pads))
139 return Resource.family(*args, default_name="i2c", ios=ios)
140
141
142 # top-level demo module.
143 class Blinker(Elaboratable):
144 def __init__(self, pinset, resources):
145 self.jtag = JTAG({}, "sync", resources=resources)
146 memory = Memory(width=32, depth=16)
147 self.sram = SRAM(memory=memory, bus=self.jtag.wb)
148
149 def elaborate(self, platform):
150 jtag_resources = self.jtag.pad_mgr.resources
151 m = Module()
152 m.submodules.jtag = self.jtag
153 m.submodules.sram = self.sram
154
155 count = Signal(5)
156 m.d.sync += count.eq(count+1)
157 print ("resources", platform, jtag_resources.items())
158 gpio = self.jtag.request('gpio')
159 print (gpio, gpio.layout, gpio.fields)
160 # get the GPIO bank, mess about with some of the pins
161 m.d.comb += gpio.gpio0.o.eq(1)
162 m.d.comb += gpio.gpio1.o.eq(gpio.gpio2.i)
163 m.d.comb += gpio.gpio1.oe.eq(count[4])
164 m.d.sync += count[0].eq(gpio.gpio1.i)
165 # get the UART resource, mess with the output tx
166 uart = self.jtag.request('uart')
167 print (uart, uart.fields)
168 intermediary = Signal()
169 m.d.comb += uart.tx.eq(intermediary)
170 m.d.comb += intermediary.eq(uart.rx)
171
172 return self.jtag.boundary_elaborate(m, platform)
173
174 def ports(self):
175 return list(self)
176
177 def __iter__(self):
178 yield from self.jtag.iter_ports()
179
180 '''
181 _trellis_command_templates = [
182 r"""
183 {{invoke_tool("yosys")}}
184 {{quiet("-q")}}
185 {{get_override("yosys_opts")|options}}
186 -l {{name}}.rpt
187 {{name}}.ys
188 """,
189 ]
190 '''
191
192 # sigh, have to create a dummy platform for now.
193 # TODO: investigate how the heck to get it to output ilang. or verilog.
194 # or, anything, really. but at least it doesn't barf
195 class ASICPlatform(TemplatedPlatform):
196 connectors = []
197 resources = OrderedDict()
198 required_tools = []
199 command_templates = ['/bin/true'] # no command needed: stops barfing
200 file_templates = {
201 **TemplatedPlatform.build_script_templates,
202 "{{name}}.il": r"""
203 # {{autogenerated}}
204 {{emit_rtlil()}}
205 """,
206 "{{name}}.debug.v": r"""
207 /* {{autogenerated}} */
208 {{emit_debug_verilog()}}
209 """,
210 }
211 toolchain = None
212 default_clk = "clk" # should be picked up / overridden by platform sys.clk
213 default_rst = "rst" # should be picked up / overridden by platform sys.rst
214
215 def __init__(self, resources, jtag):
216 self.jtag = jtag
217 super().__init__()
218
219 # create set of pin resources based on the pinset, this is for the core
220 #jtag_resources = self.jtag.pad_mgr.resources
221 self.add_resources(resources)
222
223 # add JTAG without scan
224 self.add_resources([JTAGResource('jtag', 0)], no_boundary_scan=True)
225
226 def add_resources(self, resources, no_boundary_scan=False):
227 print ("ASICPlatform add_resources", resources)
228 return super().add_resources(resources)
229
230 #def iter_ports(self):
231 # yield from super().iter_ports()
232 # for io in self.jtag.ios.values():
233 # print ("iter ports", io.layout, io)
234 # for field in io.core.fields:
235 # yield getattr(io.core, field)
236 # for field in io.pad.fields:
237 # yield getattr(io.pad, field)
238
239 # XXX these aren't strictly necessary right now but the next
240 # phase is to add JTAG Boundary Scan so it maaay be worth adding?
241 # at least for the print statements
242 def get_input(self, pin, port, attrs, invert):
243 self._check_feature("single-ended input", pin, attrs,
244 valid_xdrs=(0,), valid_attrs=None)
245
246 m = Module()
247 print (" get_input", pin, "port", port, port.layout)
248 m.d.comb += pin.i.eq(self._invert_if(invert, port))
249 return m
250
251 def get_output(self, pin, port, attrs, invert):
252 self._check_feature("single-ended output", pin, attrs,
253 valid_xdrs=(0,), valid_attrs=None)
254
255 m = Module()
256 print (" get_output", pin, "port", port, port.layout)
257 m.d.comb += port.eq(self._invert_if(invert, pin.o))
258 return m
259
260 def get_tristate(self, pin, port, attrs, invert):
261 self._check_feature("single-ended tristate", pin, attrs,
262 valid_xdrs=(0,), valid_attrs=None)
263
264 print (" get_tristate", pin, "port", port, port.layout)
265 m = Module()
266 print (" pad", pin, port, attrs)
267 print (" pin", pin.layout)
268 return m
269 # m.submodules += Instance("$tribuf",
270 # p_WIDTH=pin.width,
271 # i_EN=pin.oe,
272 # i_A=self._invert_if(invert, pin.o),
273 # o_Y=port,
274 # )
275 m.d.comb += io.core.o.eq(pin.o)
276 m.d.comb += io.core.oe.eq(pin.oe)
277 m.d.comb += pin.i.eq(io.core.i)
278 m.d.comb += io.pad.i.eq(port.i)
279 m.d.comb += port.o.eq(io.pad.o)
280 m.d.comb += port.oe.eq(io.pad.oe)
281 return m
282
283 def get_input_output(self, pin, port, attrs, invert):
284 self._check_feature("single-ended input/output", pin, attrs,
285 valid_xdrs=(0,), valid_attrs=None)
286
287 print (" get_input_output", pin, "port", port, port.layout)
288 m = Module()
289 print (" port layout", port.layout)
290 print (" pin", pin)
291 print (" layout", pin.layout)
292 #m.submodules += Instance("$tribuf",
293 # p_WIDTH=pin.width,
294 # i_EN=io.pad.oe,
295 # i_A=self._invert_if(invert, io.pad.o),
296 # o_Y=port,
297 #)
298 # Create aliases for the port sub-signals
299 port_i = port.io[0]
300 port_o = port.io[1]
301 port_oe = port.io[2]
302
303 m.d.comb += pin.i.eq(self._invert_if(invert, port_i))
304 m.d.comb += port_o.eq(self._invert_if(invert, pin.o))
305 m.d.comb += port_oe.eq(pin.oe)
306
307 return m
308
309 def toolchain_prepare(self, fragment, name, **kwargs):
310 """override toolchain_prepare in order to grab the fragment
311 """
312 self.fragment = fragment
313 return super().toolchain_prepare(fragment, name, **kwargs)
314
315 """
316 and to create a Platform instance with that list, and build
317 something random
318
319 p=Platform()
320 p.resources=listofstuff
321 p.build(Blinker())
322 """
323 pinset = dummy_pinset()
324 print(pinset)
325 resources = create_resources(pinset)
326 top = Blinker(pinset, resources)
327
328 vl = rtlil.convert(top, ports=top.ports())
329 with open("test_jtag_blinker.il", "w") as f:
330 f.write(vl)
331
332 if True:
333 # XXX these modules are all being added *AFTER* the build process links
334 # everything together. the expectation that this would work is...
335 # unrealistic. ordering, clearly, is important.
336
337 # dut = JTAG(test_pinset(), wb_data_wid=64, domain="sync")
338 top.jtag.stop = False
339 # rather than the client access the JTAG bus directly
340 # create an alternative that the client sets
341 class Dummy: pass
342 cdut = Dummy()
343 cdut.cbus = JTAGInterface()
344
345 # set up client-server on port 44843-something
346 top.jtag.s = JTAGServer()
347 cdut.c = JTAGClient()
348 top.jtag.s.get_connection()
349 #else:
350 # print ("running server only as requested, use openocd remote to test")
351 # sys.stdout.flush()
352 # top.jtag.s.get_connection(None) # block waiting for connection
353
354 # take copy of ir_width and scan_len
355 cdut._ir_width = top.jtag._ir_width
356 cdut.scan_len = top.jtag.scan_len
357
358 p = ASICPlatform (resources, top.jtag)
359 p.build(top)
360 # this is what needs to gets treated as "top", after "main module" top
361 # is augmented with IO pads with JTAG tacked on. the expectation that
362 # the get_input() etc functions will be called magically by some other
363 # function is unrealistic.
364 top_fragment = p.fragment
365
366 # XXX simulating top (the module that does not itself contain IO pads
367 # because that's covered by build) cannot possibly be expected to work
368 # particularly when modules have been added *after* the platform build()
369 # function has been called.
370
371 sim = Simulator(top)
372 sim.add_clock(1e-6, domain="sync") # standard clock
373
374 sim.add_sync_process(wrap(jtag_srv(top))) #? jtag server
375 #if len(sys.argv) != 2 or sys.argv[1] != 'server':
376 sim.add_sync_process(wrap(jtag_sim(cdut, top.jtag))) # actual jtag tester
377 sim.add_sync_process(wrap(dmi_sim(top.jtag))) # handles (pretends to be) DMI
378
379 with sim.write_vcd("dmi2jtag_test_srv.vcd"):
380 sim.run()