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