dd7a84fa76b00b20aa1bfd5e8572a1f54745abf6
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 # Automatically create the necessary periph/pad Records/Signals
53 # depending on the given dict specification
54 for pad
in self
.requested
.keys():
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]]
64 if sig_type
== iotypes
['*']:
65 self
.pads
[pad
][mux
] = Record(name
="%s%s" % (sig
, suffix
),
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
))
73 def elaborate(self
, platform
):
75 comb
, sync
= m
.d
.comb
, m
.d
.sync
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
)
84 # print(self.requested)
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]]
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(
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
)
117 for pad
in list(self
.pads
.keys()):
118 for field
in self
.pads
[pad
]["pad"].fields
.values():
120 for mux
in self
.pads
[pad
].keys():
121 if type(self
.pads
[pad
][mux
]) == Signal
:
122 yield self
.pads
[pad
][mux
]
124 for field
in self
.pads
[pad
][mux
].fields
.values():
131 def gen_pinmux_dict(self
, ps
, write_file
=False):
133 with
open("test.mdwn", "w") as of
:
134 pinout
, bankspec
, pin_spec
, fixedpins
= ps
.write(of
)
141 self
.port
= Signal(log2_int(self
.n_ports
))
145 # Create local list of peripheral names defined in pinfunctions.py
147 for pfunc
in pinspec
:
148 defined_func
.append(pfunc
[0])
150 for pin
in ps
.items():
152 for mux
in pin
[1].keys():
153 bank
= pin
[1][mux
][1]
154 signal_str
= pin
[1][mux
][0]
155 pad
= "%s%d" % (bank
, pin_no
)
156 # Get the signal name prefix
157 index_under
= signal_str
.find('_')
159 periph format: [periph+suffix]
160 GPIO periph format: [periph+bank+suffix]
161 Problem is that GPIO has a different suffix to UART/TWI.
162 Assuming that other peripherals may have their own name formats.
163 keep stripping last chars from string until remainder matches
164 one of the existing peripheral names
165 probably very inefficient...
168 periph
= signal_str
[:index_under
]
169 func
= signal_str
[index_under
+1:]
171 if periph
in defined_func
:
172 break # Found valid periph
173 periph
= periph
.rstrip(periph
[-1])
175 # flag for peripheral string, needed as GPIO has a diff format
176 # to UART and TWI, TODO: may need to check for other periph
178 check_string
= periph
+ bank
180 check_string
= periph
182 # Find the suffix for the specified periph/pin
184 for a
in ps
.fnspec
.items():
186 if check_string
in key
:
187 print(key
, a
[1][key
])
188 suffix
= a
[1][key
].suffix
192 # key to use in PinSpec.byspec has format: [perith+':'+suffix]
193 # need to get the suffix from Pin object
195 #print(signal_str[index:index_under])
197 for sig_spec
in ps
.byspec
[periph
+':'+suffix
]:
200 #suffix = ps.fnspec[fnspec_key][fnspec_key]
201 print(pad
, signal_str
, signal_str
[:index_under
],
202 periph
, func
, suffix
, signal
, mux
)
203 print("Now adding to internal pinmux dict")
204 if not (pad
in self
.requested
.keys()):
205 self
.requested
[pad
] = {}
206 self
.requested
[pad
][mux
] = {"periph":periph
, "suffix":suffix
,
208 print(self
.requested
)
210 def set_port(dut
, port
, delay
=1e-6):
211 yield dut
.port
.eq(port
)
216 Set the gpio output based on given data sequence, checked at pad.o
217 Then sends the same byte via pad.i to gpio input
219 def gpio(gpio
, pad
, data
, delay
=1e-6):
220 # Output test - Control GPIO output
223 n_bits
= len(bin(data
)[2:])
225 for i
in range(0, n_bits
):
226 bit
= (data
>> i
) & 0x1
231 assert data
== read
, f
"GPIO Sent: %x | Pad Read: %x" % (data
, read
)
232 # Input test - Control Pad input
236 for i
in range(0, n_bits
):
237 bit
= (read
>> i
) & 0x1
242 assert read2
== read
, f
"Pad Sent: %x | GPIO Read: %x" % (data
, read
)
249 Sends a byte via uart tx, checked at output pad
250 Then sends the same byte via input pad to uart rx
251 Input and output pads are different, so must specify both
253 def uart_send(tx
, rx
, pad_tx
, pad_rx
, byte
, delay
=1e-6):
254 # Drive uart tx - check the word seen at the Pad
259 yield tx
.eq(0) # start bit
262 # send one byte, lsb first
263 for i
in range(0, 8):
264 bit
= (byte
>> i
) & 0x1
267 test_bit
= yield pad_tx
.o
268 read |
= (test_bit
<< i
)
269 yield tx
.eq(1) # stop bit
271 assert byte
== read
, f
"UART Sent: %x | Pad Read: %x" % (byte
, read
)
272 # Drive Pad i - check word at uart rx
275 yield pad_rx
.i
.eq(0) # start bit
278 for i
in range(0, 8):
279 bit
= (read
>> i
) & 0x1
280 yield pad_rx
.i
.eq(bit
)
283 read2 |
= (test_bit
<< i
)
284 yield pad_rx
.i
.eq(1) # stop bit
286 assert read
== read2
, f
"Pad Sent: %x | UART Read: %x" % (read
, read2
)
290 Sends a byte via SDA.o (peripheral side), checked at output pad
291 Then sends the same byte via input pad to master SDA.i
292 This transaction doesn't make the distinction between read/write bit.
294 def i2c_send(sda
, scl
, sda_pad
, byte
, delay
=1e-6):
296 # No pull-up on line implemented, set high instead
301 yield sda_pad
.i
.eq(1)
304 yield sda
.o
.eq(0) # start bit
306 for i
in range(0, 8):
307 bit
= (byte
>> i
) & 0x1
312 temp
= yield sda_pad
.o
315 yield sda
.o
.eq(1) # Master releases SDA line
317 assert byte
== read
, f
"I2C Sent: %x | Pad Read: %x" % (byte
, read
)
319 yield sda_pad
.i
.eq(0)
324 # Send byte back to master
326 for i
in range(0, 8):
327 bit
= (read
>> i
) & 0x1
328 yield sda_pad
.i
.eq(bit
)
335 assert read
== read2
, f
"Pad Sent: %x | I2C Read: %x" % (read
, read2
)
343 # Stop condition - SDA line high after SCL high
348 yield sda
.o
.eq(1) # 'release' the SDA line
350 # Test the GPIO/UART/I2C connectivity
351 def test_man_pinmux(dut
):
352 requested
= dut
.requested
353 # TODO: Convert to automatic
354 # [{"pad":%s, "port":%d}, {"pad":%s, "port":%d},...]
355 #gpios = [{"padname":"N1", "port":GPIO_MUX},
356 # {"padname":"N2", "port":GPIO_MUX}]
357 # [[txPAD, MUXx, rxPAD, MUXx],...] - diff ports not supported yet
358 #uarts = [{"txpadname":"N1", "rxpadname":"N2", "mux":UART_MUX}]
360 # [[sdaPAD, MUXx, sclPAD, MUXx],...] - diff ports not supported yet
361 #i2cs = [{"sdapadname":"N1", "sclpadname":"N2", "mux":I2C_MUX}]
366 for pad
in requested
.keys():
367 for mux
in requested
[pad
].keys():
368 periph
= requested
[pad
][mux
]["periph"]
369 suffix
= requested
[pad
][mux
]["suffix"]
371 # [{"padname":%s, "port": %d}, ...]
372 gpios
.append({"padname":pad
, "mux": mux
})
374 # Make sure dict exists
375 if not (suffix
in uarts
.keys()):
378 if requested
[pad
][mux
]["signal"][:-1] == "TX":
379 uarts
[suffix
]["txpadname"] = pad
380 uarts
[suffix
]["txmux"] = mux
381 elif requested
[pad
][mux
]["signal"][:-1] == "RX":
382 uarts
[suffix
]["rxpadname"] = pad
383 uarts
[suffix
]["rxmux"] = mux
385 if not (suffix
in i2cs
.keys()):
387 if requested
[pad
][mux
]["signal"][:-1] == "SDA":
388 i2cs
[suffix
]["sdapadname"] = pad
389 i2cs
[suffix
]["sdamux"] = mux
390 elif requested
[pad
][mux
]["signal"][:-1] == "SCL":
391 i2cs
[suffix
]["sclpadname"] = pad
392 i2cs
[suffix
]["sclmux"] = mux
398 for gpio_periph
in gpios
:
399 padname
= gpio_periph
["padname"]
400 gpio_port
= gpio_periph
["mux"]
401 gp
= dut
.pads
[padname
][gpio_port
]
402 pad
= dut
.pads
[padname
]["pad"]
403 yield from set_port(dut
, gpio_port
)
404 yield from gpio(gp
, pad
, 0x5a5)
407 for suffix
in uarts
.keys():
408 txpadname
= uarts
[suffix
]["txpadname"]
409 rxpadname
= uarts
[suffix
]["rxpadname"]
410 uart_port
= uarts
[suffix
]["txmux"] # TODO: Assuming same mux setting
411 tx
= dut
.pads
[txpadname
][uart_port
]
412 rx
= dut
.pads
[rxpadname
][uart_port
]
413 txpad
= dut
.pads
[txpadname
]["pad"]
414 rxpad
= dut
.pads
[rxpadname
]["pad"]
415 yield from set_port(dut
, UART_MUX
)
416 yield from uart_send(tx
, rx
, txpad
, rxpad
, 0x42)
419 for suffix
in i2cs
.keys():
420 sdapadname
= i2cs
[suffix
]["sdapadname"]
421 sclpadname
= i2cs
[suffix
]["sclpadname"]
422 i2c_port
= i2cs
[suffix
]["sdamux"] # TODO: Assuming same mux setting
423 sda
= dut
.pads
[sdapadname
][i2c_port
]
424 scl
= dut
.pads
[sclpadname
][i2c_port
]
425 sdapad
= dut
.pads
[sdapadname
]["pad"]
426 yield from set_port(dut
, I2C_MUX
)
427 yield from i2c_send(sda
, scl
, sdapad
, 0x67)
429 def gen_gtkw_doc(module_name
, requested
, filename
):
430 # GTKWave doc generation
433 'in': {'color': 'orange'},
434 'out': {'color': 'yellow'},
435 'debug': {'module': 'top', 'color': 'red'}
437 # Create a trace list, each block expected to be a tuple()
441 for pad
in requested
.keys():
442 temp
= len(requested
[pad
].keys())
445 temp_traces
= ("Pad %s" % pad
, [])
447 temp_traces
[1].append(('%s__i' % pad
, 'in'))
448 temp_traces
[1].append(('%s__o' % pad
, 'out'))
449 temp_traces
[1].append(('%s__oe' % pad
, 'out'))
450 for mux
in requested
[pad
].keys():
451 periph
= requested
[pad
][mux
]["periph"]
452 suffix
= requested
[pad
][mux
]["suffix"]
454 pin
= requested
[pad
][mux
]["signal"][:-1]
456 # TODO: Automate this!
458 temp_traces
[1].append(('GPIO%s__i' % suffix
, 'in'))
459 temp_traces
[1].append(('GPIO%s__o' % suffix
, 'out'))
460 temp_traces
[1].append(('GPIO%s__oe' % suffix
, 'out'))
461 elif periph
== "UART":
463 temp_traces
[1].append(('%s%s_o' % (pin
, suffix
), 'out'))
465 temp_traces
[1].append(('%s%s_i' % (pin
, suffix
), 'in'))
466 elif periph
== "TWI":
467 temp_traces
[1].append(('%s%s__i' % (pin
, suffix
), 'in'))
468 temp_traces
[1].append(('%s%s__o' % (pin
, suffix
), 'out'))
469 temp_traces
[1].append(('%s%s__oe' % (pin
, suffix
), 'out'))
470 traces
.append(temp_traces
)
473 temp_traces
= ('Misc', [
474 ('port[%d:0]' % ((n_ports
-1).bit_length()-1), 'in')
476 traces
.append(temp_traces
)
480 write_gtkw(filename
+".gtkw", filename
+".vcd", traces
, style
,
484 def sim_man_pinmux(ps
):
485 filename
= "test_man_pinmux"
487 requested = {"N1": {"mux%d" % GPIO_MUX: ["gpio", 0, '0*'],
488 "mux%d" % UART_MUX: ["uart", 0, 'tx+'],
489 "mux%d" % I2C_MUX: ["i2c", 0, 'sda*']},
490 "N2": {"mux%d" % GPIO_MUX: ["gpio", 1, '*'],
491 "mux%d" % UART_MUX: ["uart", 0, 'rx-'],
492 "mux%d" % I2C_MUX: ["i2c", 0, 'scl*']},
493 "N3": {"mux%d" % GPIO_MUX: ["gpio", 2, '0*']},
494 "N4": {"mux%d" % GPIO_MUX: ["gpio", 3, '0*']}
498 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
499 with
open(filename
+".il", "w") as f
:
503 m
.submodules
.manpinmux
= dut
507 sim
.add_process(wrap(test_man_pinmux(dut
)))
508 sim_writer
= sim
.write_vcd(filename
+".vcd")
511 gen_gtkw_doc("top.manpinmux", dut
.requested
, filename
)
514 if __name__
== '__main__':
518 #testspec = PinSpec()
520 'A': (4, 4), # bankname: (num of pins, muxwidth)
530 function_names
= {'TWI0': 'I2C 0',
531 'UART0': 'UART (TX/RX) 0',
533 ps
= PinSpec(pinbanks
, fixedpins
, function_names
)
534 # Unit number, (Bank, pin #), mux, start, num # pins
535 ps
.gpio("", ('A', 0), 0, 0, 4)
536 ps
.gpio("2", ('B', 0), 0, 0, 2)
537 ps
.uart("0", ('A', 0), 1)
538 ps
.i2c("0", ('A', 0), 2)
542 desc_dict_keys = ['UART0', 'TWI0', 'GPIOA_A0', 'GPIOA_A1', 'GPIOA_A2',
546 desc = {'UART0': 'Basic serial TX/RX serial port',
547 'TWI0': 'I2C interface',
548 'GPIOA_A0': 'Test GPIO0',
549 'GPIOA_A1': 'Test GPIO1',
550 'GPIOA_A2': 'Test GPIO2',
551 'GPIOA_A3': 'Test GPIO3'}
552 ps.add_scenario("Test Manual Pinmux", desc_dict_keys, eint, pwm, desc)
555 #code.interact(local=locals())