6c131c0c0c867a0a68a0b894996a50db6b195a3f
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
)
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():
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
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]]
75 if sig_type
== iotypes
['*']:
76 self
.pads
[pad
][mux
] = Record(name
="%s%s" % (sig
, suffix
),
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
))
84 def elaborate(self
, platform
):
86 comb
, sync
= m
.d
.comb
, m
.d
.sync
89 for pad
in pads
.keys():
90 if len(self
.requested
[pad
]) == 1:
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
)
98 # print(self.requested)
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:
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
)
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]]
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(
133 comb
+= muxes
[pad
].periph_ports
[num
].oe
.eq(
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(
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
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
)
157 print("=============")
159 print("=============")
160 for pad
in list(self
.pads
.keys()):
161 for field
in self
.pads
[pad
]["pad"].fields
.values():
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():
172 print("%s is a var, not Sig/Rec, skipping!" % mux
)
177 def gen_pinmux_dict(self
, ps
, write_file
=False):
179 with
open("test.mdwn", "w") as of
:
180 pinout
, bankspec
, pin_spec
, fixedpins
= ps
.write(of
)
188 # Create local list of peripheral names defined in pinfunctions.py
190 for pfunc
in pinspec
:
191 defined_func
.append(pfunc
[0])
193 for pin
in ps
.items():
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('_')
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...
211 periph
= signal_str
[:index_under
]
212 func
= signal_str
[index_under
+1:]
214 if periph
in defined_func
:
215 break # Found valid periph
216 periph
= periph
.rstrip(periph
[-1])
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
221 check_string
= periph
+ bank
223 check_string
= periph
225 # Find the suffix for the specified periph/pin
227 for a
in ps
.fnspec
.items():
229 if check_string
in key
:
230 print(key
, a
[1][key
])
231 suffix
= a
[1][key
].suffix
235 # key to use in PinSpec.byspec has format: [perith+':'+suffix]
236 # need to get the suffix from Pin object
238 #print(signal_str[index:index_under])
240 for sig_spec
in ps
.byspec
[periph
+':'+suffix
]:
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
,
251 print(self
.requested
)
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
)
257 yield dut
.pads
[pad
]["port"].eq(port
)
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
265 def gpio(gpio
, pad
, data
, delay
=1e-6):
266 # Output test - Control GPIO output
269 n_bits
= len(bin(data
)[2:])
271 for i
in range(0, n_bits
):
272 bit
= (data
>> i
) & 0x1
277 assert data
== read
, f
"GPIO Sent: %x | Pad Read: %x" % (data
, read
)
278 # Input test - Control Pad input
282 for i
in range(0, n_bits
):
283 bit
= (read
>> i
) & 0x1
288 assert read2
== read
, f
"Pad Sent: %x | GPIO Read: %x" % (data
, read
)
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
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
305 yield tx
.eq(0) # start bit
308 # send one byte, lsb first
309 for i
in range(0, 8):
310 bit
= (byte
>> i
) & 0x1
313 test_bit
= yield pad_tx
.o
314 read |
= (test_bit
<< i
)
315 yield tx
.eq(1) # stop bit
317 assert byte
== read
, f
"UART Sent: %x | Pad Read: %x" % (byte
, read
)
318 # Drive Pad i - check word at uart rx
321 yield pad_rx
.i
.eq(0) # start bit
324 for i
in range(0, 8):
325 bit
= (read
>> i
) & 0x1
326 yield pad_rx
.i
.eq(bit
)
329 read2 |
= (test_bit
<< i
)
330 yield pad_rx
.i
.eq(1) # stop bit
332 assert read
== read2
, f
"Pad Sent: %x | UART Read: %x" % (read
, read2
)
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.
340 def i2c_send(sda
, scl
, sda_pad
, byte
, delay
=1e-6):
342 # No pull-up on line implemented, set high instead
347 yield sda_pad
.i
.eq(1)
350 yield sda
.o
.eq(0) # start bit
352 for i
in range(0, 8):
353 bit
= (byte
>> i
) & 0x1
358 temp
= yield sda_pad
.o
361 yield sda
.o
.eq(1) # Master releases SDA line
363 assert byte
== read
, f
"I2C Sent: %x | Pad Read: %x" % (byte
, read
)
365 yield sda_pad
.i
.eq(0)
370 # Send byte back to master
372 for i
in range(0, 8):
373 bit
= (read
>> i
) & 0x1
374 yield sda_pad
.i
.eq(bit
)
381 assert read
== read2
, f
"Pad Sent: %x | I2C Read: %x" % (read
, read2
)
389 # Stop condition - SDA line high after SCL high
394 yield sda
.o
.eq(1) # 'release' the SDA line
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}]
406 # [[sdaPAD, MUXx, sclPAD, MUXx],...] - diff ports not supported yet
407 #i2cs = [{"sdapadname":"N1", "sclpadname":"N2", "mux":I2C_MUX}]
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"]
417 # [{"padname":%s, "port": %d}, ...]
418 gpios
.append({"padname":pad
, "mux": mux
})
420 # Make sure dict exists
421 if not (suffix
in uarts
.keys()):
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
431 if not (suffix
in i2cs
.keys()):
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
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)
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)
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)
479 def gen_gtkw_doc(module_name
, requested
, filename
):
480 # GTKWave doc generation
483 'in': {'color': 'orange'},
484 'out': {'color': 'yellow'},
485 'debug': {'module': 'top', 'color': 'red'}
487 # Create a trace list, each block expected to be a tuple()
491 for pad
in requested
.keys():
492 temp
= len(requested
[pad
].keys())
495 temp_traces
= ("Pad %s" % pad
, [])
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 - only exists if more than one periph per pad
501 if len(requested
[pad
]) >= 1:
502 temp_traces
[1].append(('%s_port[%d:0]'
503 % (pad
, (n_ports
-1).bit_length()-1), 'in'))
505 traces
.append(temp_traces
)
506 temp_traces
= ("Pad %s Peripherals" % pad
, [])
507 for mux
in requested
[pad
].keys():
508 periph
= requested
[pad
][mux
]["periph"]
509 suffix
= requested
[pad
][mux
]["suffix"]
511 pin
= requested
[pad
][mux
]["signal"][:-1]
513 sig_type
= iotypes
[requested
[pad
][mux
]["signal"][-1]]
514 #print(sig, sig_type)
516 name_format
= "%s%s" % (pin
, suffix
)
518 name_format
= "%s%s" % (pin
, suffix
)
519 if sig_type
== iotypes
['*']:
520 temp_traces
[1].append(('%s__i' % name_format
, 'in'))
521 temp_traces
[1].append(('%s__o' % name_format
, 'out'))
522 temp_traces
[1].append(('%s__oe' % name_format
, 'out'))
523 # Single underscore because Signal, not Record
524 if sig_type
== iotypes
['+']:
525 temp_traces
[1].append(('%s_o' % name_format
, 'out'))
526 if sig_type
== iotypes
['-']:
527 temp_traces
[1].append(('%s_i' % name_format
, 'in'))
528 traces
.append(temp_traces
)
532 write_gtkw(filename
+".gtkw", filename
+".vcd", traces
, style
,
536 def sim_man_pinmux(ps
):
537 filename
= "test_man_pinmux"
539 requested = {"N1": {"mux%d" % GPIO_MUX: ["gpio", 0, '0*'],
540 "mux%d" % UART_MUX: ["uart", 0, 'tx+'],
541 "mux%d" % I2C_MUX: ["i2c", 0, 'sda*']},
542 "N2": {"mux%d" % GPIO_MUX: ["gpio", 1, '*'],
543 "mux%d" % UART_MUX: ["uart", 0, 'rx-'],
544 "mux%d" % I2C_MUX: ["i2c", 0, 'scl*']},
545 "N3": {"mux%d" % GPIO_MUX: ["gpio", 2, '0*']},
546 "N4": {"mux%d" % GPIO_MUX: ["gpio", 3, '0*']}
550 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
551 with
open(filename
+".il", "w") as f
:
555 m
.submodules
.manpinmux
= dut
559 sim
.add_process(wrap(test_man_pinmux(dut
)))
560 sim_writer
= sim
.write_vcd(filename
+".vcd")
563 gen_gtkw_doc("top.manpinmux", dut
.requested
, filename
)
566 if __name__
== '__main__':
570 #testspec = PinSpec()
572 'A': (4, 4), # bankname: (num of pins, muxwidth)
582 function_names
= {'TWI0': 'I2C 0',
583 'UART0': 'UART (TX/RX) 0',
585 ps
= PinSpec(pinbanks
, fixedpins
, function_names
)
586 # Unit number, (Bank, pin #), mux, start, num # pins
587 ps
.gpio("", ('A', 0), 0, 0, 4)
588 ps
.gpio("2", ('B', 0), 0, 0, 2)
589 ps
.uart("0", ('A', 0), 1)
590 ps
.i2c("0", ('A', 0), 2)
594 desc_dict_keys = ['UART0', 'TWI0', 'GPIOA_A0', 'GPIOA_A1', 'GPIOA_A2',
598 desc = {'UART0': 'Basic serial TX/RX serial port',
599 'TWI0': 'I2C interface',
600 'GPIOA_A0': 'Test GPIO0',
601 'GPIOA_A1': 'Test GPIO1',
602 'GPIOA_A2': 'Test GPIO2',
603 'GPIOA_A3': 'Test GPIO3'}
604 ps.add_scenario("Test Manual Pinmux", desc_dict_keys, eint, pwm, desc)
607 #code.interact(local=locals())