feat(stage2): Fix GTKW script, optional mdwn gen for debug
[pinmux.git] / src / stage2.py
1 #!/usr/bin/env python3
2 """
3 pinmux documented here https://libre-soc.org/docs/pinmux/
4 """
5 from nmigen import Elaboratable, Module, Signal, Record, Array, Cat
6 from nmigen.hdl.rec import Layout
7 from nmigen.utils import log2_int
8 from nmigen.cli import rtlil
9 from soc.minerva.wishbone import make_wb_layout
10 from nmutil.util import wrap
11 #from soc.bus.test.wb_rw import wb_read, wb_write
12
13 from nmutil.gtkw import write_gtkw
14
15 cxxsim = False
16 if cxxsim:
17 from nmigen.sim.cxxsim import Simulator, Settle, Delay
18 else:
19 from nmigen.sim import Simulator, Settle, Delay
20
21 from spec.iomux import IOMuxBlockSingle
22 from spec.base import PinSpec
23 from spec.jtag import iotypes
24 from spec.pinfunctions import pinspec
25
26 import code
27
28 io_layout = (("i", 1),
29 ("oe", 1),
30 ("o", 1)
31 )
32
33 uart_layout = (("rx", 1),
34 ("tx", 1),
35 ("oe", 1)
36 )
37 uart_tx_layout = (("o", 1),
38 ("oe", 1)
39 )
40 GPIO_MUX = 0
41 UART_MUX = 1
42 I2C_MUX = 2
43
44 """
45 Really basic example, uart tx/rx and i2c sda/scl pinmux
46 """
47 class ManPinmux(Elaboratable):
48 def __init__(self, ps):
49 print("Test Manual Pinmux!")
50 self.gen_pinmux_dict(ps)
51
52 # Automatically create the necessary periph/pad Records/Signals
53 # depending on the given dict specification
54 for pad in self.requested.keys():
55 self.pads[pad] = {}
56 self.pads[pad]["pad"] = Record(name=pad, layout=io_layout)
57 self.muxes[pad] = IOMuxBlockSingle(self.n_ports)
58 for mux in self.requested[pad].keys():
59 periph = self.requested[pad][mux]["periph"]
60 suffix = self.requested[pad][mux]["suffix"]
61 sig = self.requested[pad][mux]["signal"][:-1]
62 sig_type = iotypes[self.requested[pad][mux]["signal"][-1]]
63 #print(sig, sig_type)
64 if sig_type == iotypes['*']:
65 self.pads[pad][mux] = Record(name="%s%s" % (sig, suffix),
66 layout=io_layout)
67 elif sig_type == iotypes['+']:
68 self.pads[pad][mux] = Signal(name="%s%s_o" % (sig, suffix))
69 elif sig_type == iotypes['-']:
70 self.pads[pad][mux] = Signal(name="%s%s_i" % (sig, suffix))
71 print(self.pads)
72
73 def elaborate(self, platform):
74 m = Module()
75 comb, sync = m.d.comb, m.d.sync
76 muxes = self.muxes
77 port = self.port
78 pads = self.pads
79 for pad in pads.keys():
80 m.submodules[pad+"_mux"] = muxes[pad]
81 # all muxes controlled by the same multi-bit signal
82 comb += muxes[pad].port.eq(port)
83
84 # print(self.requested)
85 # print(self.pads)
86
87 # ---------------------------
88 # This section connects the periphs to the assigned ports
89 # ---------------------------
90 for pad in pads.keys():
91 for mux in self.requested[pad].keys():
92 periph = self.requested[pad][mux]["periph"]
93 #suffix = self.requested[pad][mux]["suffix"]
94 sig = self.requested[pad][mux]["signal"][:-1]
95 sig_type = iotypes[self.requested[pad][mux]["signal"][-1]]
96 num = int(mux)
97 if sig_type == iotypes['*']:
98 comb += muxes[pad].periph_ports[num].o.eq(pads[pad][mux].o)
99 comb += muxes[pad].periph_ports[num].oe.eq(
100 pads[pad][mux].oe)
101 comb += pads[pad][mux].i.eq(muxes[pad].periph_ports[num].i)
102 elif sig_type == iotypes['+']:
103 comb += muxes[pad].periph_ports[num].o.eq(pads[pad][mux])
104 elif sig_type == iotypes['-']:
105 comb += pads[pad][mux].eq(muxes[pad].periph_ports[num].i)
106 # ---------------------------
107 # Here is where the muxes are assigned to the actual pads
108 # ---------------------------
109 for pad in pads.keys():
110 comb += pads[pad]["pad"].o.eq(muxes[pad].out_port.o)
111 comb += pads[pad]["pad"].oe.eq(muxes[pad].out_port.oe)
112 comb += muxes[pad].out_port.i.eq(pads[pad]["pad"].i)
113
114 return m
115
116 def __iter__(self):
117 for pad in list(self.pads.keys()):
118 for field in self.pads[pad]["pad"].fields.values():
119 yield field
120 for mux in self.pads[pad].keys():
121 if type(self.pads[pad][mux]) == Signal:
122 yield self.pads[pad][mux]
123 else:
124 for field in self.pads[pad][mux].fields.values():
125 yield field
126 yield self.port
127
128 def ports(self):
129 return list(self)
130
131 def gen_pinmux_dict(self, ps, write_file=False):
132 if write_file:
133 with open("test.mdwn", "w") as of:
134 pinout, bankspec, pin_spec, fixedpins = ps.write(of)
135 #print(ps.items())
136 #print(ps.byspec)
137 #print(ps.fnspec)
138 # TODO: get from ps
139 self.requested = {}
140 self.n_ports = 4
141 self.port = Signal(log2_int(self.n_ports))
142 self.pads = {}
143 self.muxes = {}
144
145 # Create local list of peripheral names defined in pinfunctions.py
146 defined_func = []
147 for pfunc in pinspec:
148 defined_func.append(pfunc[0])
149
150 for pin in ps.items():
151 pin_no = pin[0]
152 for mux in pin[1].keys():
153 bank = pin[1][mux][1]
154 signal_str = pin[1][mux][0]
155 pad = "%s%d" % (bank, pin_no)
156 # Get the signal name prefix
157 index_under = signal_str.find('_')
158 """
159 periph format: [periph+suffix]
160 GPIO periph format: [periph+bank+suffix]
161 Problem is that GPIO has a different suffix to UART/TWI.
162 Assuming that other peripherals may have their own name formats.
163 keep stripping last chars from string until remainder matches
164 one of the existing peripheral names
165 probably very inefficient...
166 NO ERROR CHECKING
167 """
168 periph = signal_str[:index_under]
169 func = signal_str[index_under+1:]
170 while periph != '':
171 if periph in defined_func:
172 break # Found valid periph
173 periph = periph.rstrip(periph[-1])
174
175 # flag for peripheral string, needed as GPIO has a diff format
176 # to UART and TWI, TODO: may need to check for other periph
177 if periph == "GPIO":
178 check_string = periph + bank
179 else:
180 check_string = periph
181
182 # Find the suffix for the specified periph/pin
183 suffix = ''
184 for a in ps.fnspec.items():
185 for key in a[1]:
186 if check_string in key:
187 print(key, a[1][key])
188 suffix = a[1][key].suffix
189 else:
190 continue
191
192 # key to use in PinSpec.byspec has format: [perith+':'+suffix]
193 # need to get the suffix from Pin object
194 #index = len(periph)
195 #print(signal_str[index:index_under])
196 signal = ''
197 for sig_spec in ps.byspec[periph+':'+suffix]:
198 if func in sig_spec:
199 signal = sig_spec
200 #suffix = ps.fnspec[fnspec_key][fnspec_key]
201 print(pad, signal_str, signal_str[:index_under],
202 periph, func, suffix, signal, mux)
203 print("Now adding to internal pinmux dict")
204 if not (pad in self.requested.keys()):
205 self.requested[pad] = {}
206 self.requested[pad][mux] = {"periph":periph, "suffix":suffix,
207 "signal":signal}
208 print(self.requested)
209
210 def set_port(dut, port, delay=1e-6):
211 yield dut.port.eq(port)
212 yield Delay(delay)
213
214 """
215 GPIO test function
216 Set the gpio output based on given data sequence, checked at pad.o
217 Then sends the same byte via pad.i to gpio input
218 """
219 def gpio(gpio, pad, data, delay=1e-6):
220 # Output test - Control GPIO output
221 yield gpio.oe.eq(1)
222 yield Delay(delay)
223 n_bits = len(bin(data)[2:])
224 read = 0
225 for i in range(0, n_bits):
226 bit = (data >> i) & 0x1
227 yield gpio.o.eq(bit)
228 yield Delay(delay)
229 temp = yield pad.o
230 read |= (temp << i)
231 assert data == read, f"GPIO Sent: %x | Pad Read: %x" % (data, read)
232 # Input test - Control Pad input
233 yield gpio.oe.eq(0)
234 yield Delay(delay)
235 read2 = 0
236 for i in range(0, n_bits):
237 bit = (read >> i) & 0x1
238 yield pad.i.eq(bit)
239 yield Delay(delay)
240 temp = yield gpio.i
241 read2 |= (temp << i)
242 assert read2 == read, f"Pad Sent: %x | GPIO Read: %x" % (data, read)
243 # reset input signal
244 yield pad.i.eq(0)
245 yield Delay(delay)
246
247 """
248 UART test function
249 Sends a byte via uart tx, checked at output pad
250 Then sends the same byte via input pad to uart rx
251 Input and output pads are different, so must specify both
252 """
253 def uart_send(tx, rx, pad_tx, pad_rx, byte, delay=1e-6):
254 # Drive uart tx - check the word seen at the Pad
255 print(type(tx))
256 #yield tx.oe.eq(1)
257 yield tx.eq(1)
258 yield Delay(2*delay)
259 yield tx.eq(0) # start bit
260 yield Delay(delay)
261 read = 0
262 # send one byte, lsb first
263 for i in range(0, 8):
264 bit = (byte >> i) & 0x1
265 yield tx.eq(bit)
266 yield Delay(delay)
267 test_bit = yield pad_tx.o
268 read |= (test_bit << i)
269 yield tx.eq(1) # stop bit
270 yield Delay(delay)
271 assert byte == read, f"UART Sent: %x | Pad Read: %x" % (byte, read)
272 # Drive Pad i - check word at uart rx
273 yield pad_rx.i.eq(1)
274 yield Delay(2*delay)
275 yield pad_rx.i.eq(0) # start bit
276 yield Delay(delay)
277 read2 = 0
278 for i in range(0, 8):
279 bit = (read >> i) & 0x1
280 yield pad_rx.i.eq(bit)
281 yield Delay(delay)
282 test_bit = yield rx
283 read2 |= (test_bit << i)
284 yield pad_rx.i.eq(1) # stop bit
285 yield Delay(delay)
286 assert read == read2, f"Pad Sent: %x | UART Read: %x" % (read, read2)
287
288 """
289 I2C test function
290 Sends a byte via SDA.o (peripheral side), checked at output pad
291 Then sends the same byte via input pad to master SDA.i
292 This transaction doesn't make the distinction between read/write bit.
293 """
294 def i2c_send(sda, scl, sda_pad, byte, delay=1e-6):
295 # No checking yet
296 # No pull-up on line implemented, set high instead
297 yield sda.oe.eq(1)
298 yield sda.o.eq(1)
299 yield scl.oe.eq(1)
300 yield scl.o.eq(1)
301 yield sda_pad.i.eq(1)
302 yield Delay(delay)
303 read = 0
304 yield sda.o.eq(0) # start bit
305 yield Delay(delay)
306 for i in range(0, 8):
307 bit = (byte >> i) & 0x1
308 yield sda.o.eq(bit)
309 yield scl.o.eq(0)
310 yield Delay(delay/2)
311 yield scl.o.eq(1)
312 temp = yield sda_pad.o
313 read |= (temp << i)
314 yield Delay(delay/2)
315 yield sda.o.eq(1) # Master releases SDA line
316 yield sda.oe.eq(0)
317 assert byte == read, f"I2C Sent: %x | Pad Read: %x" % (byte, read)
318 # Slave ACK
319 yield sda_pad.i.eq(0)
320 yield scl.o.eq(0)
321 yield Delay(delay/2)
322 yield scl.o.eq(1)
323 yield Delay(delay/2)
324 # Send byte back to master
325 read2 = 0
326 for i in range(0, 8):
327 bit = (read >> i) & 0x1
328 yield sda_pad.i.eq(bit)
329 yield scl.o.eq(0)
330 yield Delay(delay/2)
331 yield scl.o.eq(1)
332 temp = yield sda.i
333 read2 |= (temp << i)
334 yield Delay(delay/2)
335 assert read == read2, f"Pad Sent: %x | I2C Read: %x" % (read, read2)
336 # Master ACK
337 yield sda.oe.eq(1)
338 yield sda.o.eq(0)
339 yield scl.o.eq(0)
340 yield Delay(delay/2)
341 yield scl.o.eq(1)
342 yield Delay(delay/2)
343 # Stop condition - SDA line high after SCL high
344 yield scl.o.eq(0)
345 yield Delay(delay/2)
346 yield scl.o.eq(1)
347 yield Delay(delay/2)
348 yield sda.o.eq(1) # 'release' the SDA line
349
350 # Test the GPIO/UART/I2C connectivity
351 def test_man_pinmux(dut):
352 requested = dut.requested
353 # TODO: Convert to automatic
354 # [{"pad":%s, "port":%d}, {"pad":%s, "port":%d},...]
355 #gpios = [{"padname":"N1", "port":GPIO_MUX},
356 # {"padname":"N2", "port":GPIO_MUX}]
357 # [[txPAD, MUXx, rxPAD, MUXx],...] - diff ports not supported yet
358 #uarts = [{"txpadname":"N1", "rxpadname":"N2", "mux":UART_MUX}]
359 uarts = {}
360 # [[sdaPAD, MUXx, sclPAD, MUXx],...] - diff ports not supported yet
361 #i2cs = [{"sdapadname":"N1", "sclpadname":"N2", "mux":I2C_MUX}]
362 i2cs = {}
363
364 gpios = []
365 delay = 1e-6
366 for pad in requested.keys():
367 for mux in requested[pad].keys():
368 periph = requested[pad][mux]["periph"]
369 suffix = requested[pad][mux]["suffix"]
370 if periph == "GPIO":
371 # [{"padname":%s, "port": %d}, ...]
372 gpios.append({"padname":pad, "mux": mux})
373 if periph == "UART":
374 # Make sure dict exists
375 if not (suffix in uarts.keys()):
376 uarts[suffix] = {}
377
378 if requested[pad][mux]["signal"][:-1] == "TX":
379 uarts[suffix]["txpadname"] = pad
380 uarts[suffix]["txmux"] = mux
381 elif requested[pad][mux]["signal"][:-1] == "RX":
382 uarts[suffix]["rxpadname"] = pad
383 uarts[suffix]["rxmux"] = mux
384 if periph == "TWI":
385 if not (suffix in i2cs.keys()):
386 i2cs[suffix] = {}
387 if requested[pad][mux]["signal"][:-1] == "SDA":
388 i2cs[suffix]["sdapadname"] = pad
389 i2cs[suffix]["sdamux"] = mux
390 elif requested[pad][mux]["signal"][:-1] == "SCL":
391 i2cs[suffix]["sclpadname"] = pad
392 i2cs[suffix]["sclmux"] = mux
393 print(gpios)
394 print(uarts)
395 print(i2cs)
396
397 # GPIO test
398 for gpio_periph in gpios:
399 padname = gpio_periph["padname"]
400 gpio_port = gpio_periph["mux"]
401 gp = dut.pads[padname][gpio_port]
402 pad = dut.pads[padname]["pad"]
403 yield from set_port(dut, gpio_port)
404 yield from gpio(gp, pad, 0x5a5)
405
406 # UART test
407 for suffix in uarts.keys():
408 txpadname = uarts[suffix]["txpadname"]
409 rxpadname = uarts[suffix]["rxpadname"]
410 uart_port = uarts[suffix]["txmux"] # TODO: Assuming same mux setting
411 tx = dut.pads[txpadname][uart_port]
412 rx = dut.pads[rxpadname][uart_port]
413 txpad = dut.pads[txpadname]["pad"]
414 rxpad = dut.pads[rxpadname]["pad"]
415 yield from set_port(dut, UART_MUX)
416 yield from uart_send(tx, rx, txpad, rxpad, 0x42)
417
418 # I2C test
419 for suffix in i2cs.keys():
420 sdapadname = i2cs[suffix]["sdapadname"]
421 sclpadname = i2cs[suffix]["sclpadname"]
422 i2c_port = i2cs[suffix]["sdamux"] # TODO: Assuming same mux setting
423 sda = dut.pads[sdapadname][i2c_port]
424 scl = dut.pads[sclpadname][i2c_port]
425 sdapad = dut.pads[sdapadname]["pad"]
426 yield from set_port(dut, I2C_MUX)
427 yield from i2c_send(sda, scl, sdapad, 0x67)
428
429 def gen_gtkw_doc(module_name, requested, filename):
430 # GTKWave doc generation
431 style = {
432 '': {'base': 'hex'},
433 'in': {'color': 'orange'},
434 'out': {'color': 'yellow'},
435 'debug': {'module': 'top', 'color': 'red'}
436 }
437 # Create a trace list, each block expected to be a tuple()
438 traces = []
439 temp = 0
440 n_ports = 0
441 for pad in requested.keys():
442 temp = len(requested[pad].keys())
443 if n_ports < temp:
444 n_ports = temp
445 temp_traces = ("Pad %s" % pad, [])
446 # Pad signals
447 temp_traces[1].append(('%s__i' % pad, 'in'))
448 temp_traces[1].append(('%s__o' % pad, 'out'))
449 temp_traces[1].append(('%s__oe' % pad, 'out'))
450 for mux in requested[pad].keys():
451 periph = requested[pad][mux]["periph"]
452 suffix = requested[pad][mux]["suffix"]
453 # TODO: cleanup
454 pin = requested[pad][mux]["signal"][:-1]
455
456 # TODO: Automate this!
457 if periph == "GPIO":
458 temp_traces[1].append(('GPIO%s__i' % suffix, 'in'))
459 temp_traces[1].append(('GPIO%s__o' % suffix, 'out'))
460 temp_traces[1].append(('GPIO%s__oe' % suffix, 'out'))
461 elif periph == "UART":
462 if pin == "TX":
463 temp_traces[1].append(('%s%s_o' % (pin, suffix), 'out'))
464 elif pin == "RX":
465 temp_traces[1].append(('%s%s_i' % (pin, suffix), 'in'))
466 elif periph == "TWI":
467 temp_traces[1].append(('%s%s__i' % (pin, suffix), 'in'))
468 temp_traces[1].append(('%s%s__o' % (pin, suffix), 'out'))
469 temp_traces[1].append(('%s%s__oe' % (pin, suffix), 'out'))
470 traces.append(temp_traces)
471
472 # master port signal
473 temp_traces = ('Misc', [
474 ('port[%d:0]' % ((n_ports-1).bit_length()-1), 'in')
475 ])
476 traces.append(temp_traces)
477
478 #print(traces)
479
480 write_gtkw(filename+".gtkw", filename+".vcd", traces, style,
481 module=module_name)
482
483
484 def sim_man_pinmux(ps):
485 filename = "test_man_pinmux"
486 """
487 requested = {"N1": {"mux%d" % GPIO_MUX: ["gpio", 0, '0*'],
488 "mux%d" % UART_MUX: ["uart", 0, 'tx+'],
489 "mux%d" % I2C_MUX: ["i2c", 0, 'sda*']},
490 "N2": {"mux%d" % GPIO_MUX: ["gpio", 1, '*'],
491 "mux%d" % UART_MUX: ["uart", 0, 'rx-'],
492 "mux%d" % I2C_MUX: ["i2c", 0, 'scl*']},
493 "N3": {"mux%d" % GPIO_MUX: ["gpio", 2, '0*']},
494 "N4": {"mux%d" % GPIO_MUX: ["gpio", 3, '0*']}
495 }
496 """
497 dut = ManPinmux(ps)
498 vl = rtlil.convert(dut, ports=dut.ports())
499 with open(filename+".il", "w") as f:
500 f.write(vl)
501
502 m = Module()
503 m.submodules.manpinmux = dut
504
505 sim = Simulator(m)
506
507 sim.add_process(wrap(test_man_pinmux(dut)))
508 sim_writer = sim.write_vcd(filename+".vcd")
509 with sim_writer:
510 sim.run()
511 gen_gtkw_doc("top.manpinmux", dut.requested, filename)
512
513
514 if __name__ == '__main__':
515 #pinbanks = []
516 #fixedpins = []
517 #function_names = []
518 #testspec = PinSpec()
519 pinbanks = {
520 'A': (4, 4), # bankname: (num of pins, muxwidth)
521 'B': (2, 4),
522 #'C': (24, 1),
523 #'D': (93, 1),
524 }
525 fixedpins = {
526 'POWER_GPIO': [
527 'VDD_GPIOB',
528 'GND_GPIOB',
529 ]}
530 function_names = {'TWI0': 'I2C 0',
531 'UART0': 'UART (TX/RX) 0',
532 }
533 ps = PinSpec(pinbanks, fixedpins, function_names)
534 # Unit number, (Bank, pin #), mux, start, num # pins
535 ps.gpio("", ('A', 0), 0, 0, 4)
536 ps.gpio("2", ('B', 0), 0, 0, 2)
537 ps.uart("0", ('A', 0), 1)
538 ps.i2c("0", ('A', 0), 2)
539 sim_man_pinmux(ps)
540
541 """
542 desc_dict_keys = ['UART0', 'TWI0', 'GPIOA_A0', 'GPIOA_A1', 'GPIOA_A2',
543 'GPIOA_A3']
544 eint = []
545 pwm = []
546 desc = {'UART0': 'Basic serial TX/RX serial port',
547 'TWI0': 'I2C interface',
548 'GPIOA_A0': 'Test GPIO0',
549 'GPIOA_A1': 'Test GPIO1',
550 'GPIOA_A2': 'Test GPIO2',
551 'GPIOA_A3': 'Test GPIO3'}
552 ps.add_scenario("Test Manual Pinmux", desc_dict_keys, eint, pwm, desc)
553 """
554 #gen_pinmux_dict(ps)
555 #code.interact(local=locals())