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