add pinmux docs
[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
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 intermediary = Signal()
172 m.d.comb += uart.tx.eq(intermediary)
173 m.d.comb += intermediary.eq(uart.rx)
174
175 return self.jtag.boundary_elaborate(m, platform)
176
177 def ports(self):
178 return list(self)
179
180 def __iter__(self):
181 yield from self.jtag.iter_ports()
182
183 '''
184 _trellis_command_templates = [
185 r"""
186 {{invoke_tool("yosys")}}
187 {{quiet("-q")}}
188 {{get_override("yosys_opts")|options}}
189 -l {{name}}.rpt
190 {{name}}.ys
191 """,
192 ]
193 '''
194
195 # sigh, have to create a dummy platform for now.
196 # TODO: investigate how the heck to get it to output ilang. or verilog.
197 # or, anything, really. but at least it doesn't barf
198 class ASICPlatform(TemplatedPlatform):
199 connectors = []
200 resources = OrderedDict()
201 required_tools = []
202 command_templates = ['/bin/true'] # no command needed: stops barfing
203 file_templates = {
204 **TemplatedPlatform.build_script_templates,
205 "{{name}}.il": r"""
206 # {{autogenerated}}
207 {{emit_rtlil()}}
208 """,
209 "{{name}}.debug.v": r"""
210 /* {{autogenerated}} */
211 {{emit_debug_verilog()}}
212 """,
213 }
214 toolchain = None
215 default_clk = "clk" # should be picked up / overridden by platform sys.clk
216 default_rst = "rst" # should be picked up / overridden by platform sys.rst
217
218 def __init__(self, resources, jtag):
219 self.jtag = jtag
220 super().__init__()
221
222 # create set of pin resources based on the pinset, this is for the core
223 #jtag_resources = self.jtag.pad_mgr.resources
224 self.add_resources(resources)
225
226 # add JTAG without scan
227 self.add_resources([JTAGResource('jtag', 0)], no_boundary_scan=True)
228
229 def add_resources(self, resources, no_boundary_scan=False):
230 print ("ASICPlatform add_resources", resources)
231 return super().add_resources(resources)
232
233 #def iter_ports(self):
234 # yield from super().iter_ports()
235 # for io in self.jtag.ios.values():
236 # print ("iter ports", io.layout, io)
237 # for field in io.core.fields:
238 # yield getattr(io.core, field)
239 # for field in io.pad.fields:
240 # yield getattr(io.pad, field)
241
242 # XXX these aren't strictly necessary right now but the next
243 # phase is to add JTAG Boundary Scan so it maaay be worth adding?
244 # at least for the print statements
245 def get_input(self, pin, port, attrs, invert):
246 self._check_feature("single-ended input", pin, attrs,
247 valid_xdrs=(0,), valid_attrs=None)
248
249 m = Module()
250 print (" get_input", pin, "port", port, port.layout)
251 m.d.comb += pin.i.eq(self._invert_if(invert, port))
252 return m
253
254 def get_output(self, pin, port, attrs, invert):
255 self._check_feature("single-ended output", pin, attrs,
256 valid_xdrs=(0,), valid_attrs=None)
257
258 m = Module()
259 print (" get_output", pin, "port", port, port.layout)
260 m.d.comb += port.eq(self._invert_if(invert, pin.o))
261 return m
262
263 def get_tristate(self, pin, port, attrs, invert):
264 self._check_feature("single-ended tristate", pin, attrs,
265 valid_xdrs=(0,), valid_attrs=None)
266
267 print (" get_tristate", pin, "port", port, port.layout)
268 m = Module()
269 print (" pad", pin, port, attrs)
270 print (" pin", pin.layout)
271 return m
272 # m.submodules += Instance("$tribuf",
273 # p_WIDTH=pin.width,
274 # i_EN=pin.oe,
275 # i_A=self._invert_if(invert, pin.o),
276 # o_Y=port,
277 # )
278 m.d.comb += io.core.o.eq(pin.o)
279 m.d.comb += io.core.oe.eq(pin.oe)
280 m.d.comb += pin.i.eq(io.core.i)
281 m.d.comb += io.pad.i.eq(port.i)
282 m.d.comb += port.o.eq(io.pad.o)
283 m.d.comb += port.oe.eq(io.pad.oe)
284 return m
285
286 def get_input_output(self, pin, port, attrs, invert):
287 self._check_feature("single-ended input/output", pin, attrs,
288 valid_xdrs=(0,), valid_attrs=None)
289
290 print (" get_input_output", pin, "port", port, port.layout)
291 m = Module()
292 print (" port layout", port.layout)
293 print (" pin", pin)
294 print (" layout", pin.layout)
295 #m.submodules += Instance("$tribuf",
296 # p_WIDTH=pin.width,
297 # i_EN=io.pad.oe,
298 # i_A=self._invert_if(invert, io.pad.o),
299 # o_Y=port,
300 #)
301 # Create aliases for the port sub-signals
302 port_i = port.io[0]
303 port_o = port.io[1]
304 port_oe = port.io[2]
305
306 m.d.comb += pin.i.eq(self._invert_if(invert, port_i))
307 m.d.comb += port_o.eq(self._invert_if(invert, pin.o))
308 m.d.comb += port_oe.eq(pin.oe)
309
310 return m
311
312 def toolchain_prepare(self, fragment, name, **kwargs):
313 """override toolchain_prepare in order to grab the fragment
314 """
315 self.fragment = fragment
316 return super().toolchain_prepare(fragment, name, **kwargs)
317
318 """
319 and to create a Platform instance with that list, and build
320 something random
321
322 p=Platform()
323 p.resources=listofstuff
324 p.build(Blinker())
325 """
326 pinset = dummy_pinset()
327 print(pinset)
328 resources = create_resources(pinset)
329 top = Blinker(pinset, resources)
330
331 vl = rtlil.convert(top, ports=top.ports())
332 with open("test_jtag_blinker.il", "w") as f:
333 f.write(vl)
334
335 if True:
336 # XXX these modules are all being added *AFTER* the build process links
337 # everything together. the expectation that this would work is...
338 # unrealistic. ordering, clearly, is important.
339
340 # dut = JTAG(test_pinset(), wb_data_wid=64, domain="sync")
341 top.jtag.stop = False
342 # rather than the client access the JTAG bus directly
343 # create an alternative that the client sets
344 class Dummy: pass
345 cdut = Dummy()
346 cdut.cbus = JTAGInterface()
347
348 # set up client-server on port 44843-something
349 top.jtag.s = JTAGServer()
350 cdut.c = JTAGClient()
351 top.jtag.s.get_connection()
352 #else:
353 # print ("running server only as requested, use openocd remote to test")
354 # sys.stdout.flush()
355 # top.jtag.s.get_connection(None) # block waiting for connection
356
357 # take copy of ir_width and scan_len
358 cdut._ir_width = top.jtag._ir_width
359 cdut.scan_len = top.jtag.scan_len
360
361 p = ASICPlatform (resources, top.jtag)
362 p.build(top)
363 # this is what needs to gets treated as "top", after "main module" top
364 # is augmented with IO pads with JTAG tacked on. the expectation that
365 # the get_input() etc functions will be called magically by some other
366 # function is unrealistic.
367 top_fragment = p.fragment
368
369 # XXX simulating top (the module that does not itself contain IO pads
370 # because that's covered by build) cannot possibly be expected to work
371 # particularly when modules have been added *after* the platform build()
372 # function has been called.
373
374 sim = Simulator(top)
375 sim.add_clock(1e-6, domain="sync") # standard clock
376
377 sim.add_sync_process(wrap(jtag_srv(top))) #? jtag server
378 #if len(sys.argv) != 2 or sys.argv[1] != 'server':
379 sim.add_sync_process(wrap(jtag_sim(cdut, top.jtag))) # actual jtag tester
380 sim.add_sync_process(wrap(dmi_sim(top.jtag))) # handles (pretends to be) DMI
381
382 with sim.write_vcd("dmi2jtag_test_srv.vcd"):
383 sim.run()