3 pinmux documented here https://libre-soc.org/docs/pinmux/
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
13 from nmutil
.gtkw
import write_gtkw
17 from nmigen
.sim
.cxxsim
import Simulator
, Settle
, Delay
19 from nmigen
.sim
import Simulator
, Settle
, Delay
21 from spec
.iomux
import IOMuxBlockSingle
22 from spec
.base
import PinSpec
23 from spec
.jtag
import iotypes
24 from spec
.pinfunctions
import pinspec
28 io_layout
= (("i", 1),
33 uart_layout
= (("rx", 1),
37 uart_tx_layout
= (("o", 1),
45 Really basic example, uart tx/rx and i2c sda/scl pinmux
47 class ManPinmux(Elaboratable
):
48 def __init__(self
, ps
):
49 print("Test Manual Pinmux!")
50 self
.gen_pinmux_dict(ps
)
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():
57 self
.pads
[pad
]["pad"] = Record(name
=pad
, layout
=io_layout
)
58 if len(self
.requested
[pad
]) == 1:
59 pass # skip mux creation
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]]
68 if sig_type
== iotypes
['*']:
69 self
.pads
[pad
][mux
] = Record(name
="%s%s" % (sig
, suffix
),
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
))
77 def elaborate(self
, platform
):
79 comb
, sync
= m
.d
.comb
, m
.d
.sync
83 for pad
in pads
.keys():
84 if len(self
.requested
[pad
]) == 1:
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
)
91 # print(self.requested)
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:
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
)
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]]
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(
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
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
)
144 for pad
in list(self
.pads
.keys()):
145 for field
in self
.pads
[pad
]["pad"].fields
.values():
147 for mux
in self
.pads
[pad
].keys():
148 if type(self
.pads
[pad
][mux
]) == Signal
:
149 yield self
.pads
[pad
][mux
]
151 for field
in self
.pads
[pad
][mux
].fields
.values():
158 def gen_pinmux_dict(self
, ps
, write_file
=False):
160 with
open("test.mdwn", "w") as of
:
161 pinout
, bankspec
, pin_spec
, fixedpins
= ps
.write(of
)
168 self
.port
= Signal(log2_int(self
.n_ports
))
172 # Create local list of peripheral names defined in pinfunctions.py
174 for pfunc
in pinspec
:
175 defined_func
.append(pfunc
[0])
177 for pin
in ps
.items():
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('_')
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...
195 periph
= signal_str
[:index_under
]
196 func
= signal_str
[index_under
+1:]
198 if periph
in defined_func
:
199 break # Found valid periph
200 periph
= periph
.rstrip(periph
[-1])
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
205 check_string
= periph
+ bank
207 check_string
= periph
209 # Find the suffix for the specified periph/pin
211 for a
in ps
.fnspec
.items():
213 if check_string
in key
:
214 print(key
, a
[1][key
])
215 suffix
= a
[1][key
].suffix
219 # key to use in PinSpec.byspec has format: [perith+':'+suffix]
220 # need to get the suffix from Pin object
222 #print(signal_str[index:index_under])
224 for sig_spec
in ps
.byspec
[periph
+':'+suffix
]:
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
,
235 print(self
.requested
)
237 def set_port(dut
, port
, delay
=1e-6):
238 yield dut
.port
.eq(port
)
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
246 def gpio(gpio
, pad
, data
, delay
=1e-6):
247 # Output test - Control GPIO output
250 n_bits
= len(bin(data
)[2:])
252 for i
in range(0, n_bits
):
253 bit
= (data
>> i
) & 0x1
258 assert data
== read
, f
"GPIO Sent: %x | Pad Read: %x" % (data
, read
)
259 # Input test - Control Pad input
263 for i
in range(0, n_bits
):
264 bit
= (read
>> i
) & 0x1
269 assert read2
== read
, f
"Pad Sent: %x | GPIO Read: %x" % (data
, read
)
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
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
286 yield tx
.eq(0) # start bit
289 # send one byte, lsb first
290 for i
in range(0, 8):
291 bit
= (byte
>> i
) & 0x1
294 test_bit
= yield pad_tx
.o
295 read |
= (test_bit
<< i
)
296 yield tx
.eq(1) # stop bit
298 assert byte
== read
, f
"UART Sent: %x | Pad Read: %x" % (byte
, read
)
299 # Drive Pad i - check word at uart rx
302 yield pad_rx
.i
.eq(0) # start bit
305 for i
in range(0, 8):
306 bit
= (read
>> i
) & 0x1
307 yield pad_rx
.i
.eq(bit
)
310 read2 |
= (test_bit
<< i
)
311 yield pad_rx
.i
.eq(1) # stop bit
313 assert read
== read2
, f
"Pad Sent: %x | UART Read: %x" % (read
, read2
)
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.
321 def i2c_send(sda
, scl
, sda_pad
, byte
, delay
=1e-6):
323 # No pull-up on line implemented, set high instead
328 yield sda_pad
.i
.eq(1)
331 yield sda
.o
.eq(0) # start bit
333 for i
in range(0, 8):
334 bit
= (byte
>> i
) & 0x1
339 temp
= yield sda_pad
.o
342 yield sda
.o
.eq(1) # Master releases SDA line
344 assert byte
== read
, f
"I2C Sent: %x | Pad Read: %x" % (byte
, read
)
346 yield sda_pad
.i
.eq(0)
351 # Send byte back to master
353 for i
in range(0, 8):
354 bit
= (read
>> i
) & 0x1
355 yield sda_pad
.i
.eq(bit
)
362 assert read
== read2
, f
"Pad Sent: %x | I2C Read: %x" % (read
, read2
)
370 # Stop condition - SDA line high after SCL high
375 yield sda
.o
.eq(1) # 'release' the SDA line
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}]
387 # [[sdaPAD, MUXx, sclPAD, MUXx],...] - diff ports not supported yet
388 #i2cs = [{"sdapadname":"N1", "sclpadname":"N2", "mux":I2C_MUX}]
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"]
398 # [{"padname":%s, "port": %d}, ...]
399 gpios
.append({"padname":pad
, "mux": mux
})
401 # Make sure dict exists
402 if not (suffix
in uarts
.keys()):
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
412 if not (suffix
in i2cs
.keys()):
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
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)
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)
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)
456 def gen_gtkw_doc(module_name
, requested
, filename
):
457 # GTKWave doc generation
460 'in': {'color': 'orange'},
461 'out': {'color': 'yellow'},
462 'debug': {'module': 'top', 'color': 'red'}
464 # Create a trace list, each block expected to be a tuple()
468 for pad
in requested
.keys():
469 temp
= len(requested
[pad
].keys())
472 temp_traces
= ("Pad %s" % pad
, [])
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"]
481 pin
= requested
[pad
][mux
]["signal"][:-1]
484 sig_type = iotypes[self.requested[pad][mux]["signal"][-1]]
485 #print(sig, sig_type)
487 name_format = "%s%s" % (periph, suffix)
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'))
495 # TODO: Automate this!
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":
502 temp_traces
[1].append(('%s%s_o' % (pin
, suffix
), 'out'))
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
)
512 temp_traces
= ('Misc', [
513 ('port[%d:0]' % ((n_ports
-1).bit_length()-1), 'in')
515 traces
.append(temp_traces
)
519 write_gtkw(filename
+".gtkw", filename
+".vcd", traces
, style
,
523 def sim_man_pinmux(ps
):
524 filename
= "test_man_pinmux"
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*']}
537 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
538 with
open(filename
+".il", "w") as f
:
542 m
.submodules
.manpinmux
= dut
546 sim
.add_process(wrap(test_man_pinmux(dut
)))
547 sim_writer
= sim
.write_vcd(filename
+".vcd")
550 gen_gtkw_doc("top.manpinmux", dut
.requested
, filename
)
553 if __name__
== '__main__':
557 #testspec = PinSpec()
559 'A': (4, 4), # bankname: (num of pins, muxwidth)
569 function_names
= {'TWI0': 'I2C 0',
570 'UART0': 'UART (TX/RX) 0',
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)
581 desc_dict_keys = ['UART0', 'TWI0', 'GPIOA_A0', 'GPIOA_A1', 'GPIOA_A2',
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)
594 #code.interact(local=locals())