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