feat(stage2.py): Added peripheral Rec/Sig declaration based on given dict
[pinmux.git] / src / spec / 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 iomux import IOMuxBlockSingle
22
23 io_layout = (("i", 1),
24 ("oe", 1),
25 ("o", 1)
26 )
27
28 uart_layout = (("rx", 1),
29 ("tx", 1),
30 ("oe", 1)
31 )
32 uart_tx_layout = (("o", 1),
33 ("oe", 1)
34 )
35 GPIO_BANK = 0
36 UART_BANK = 1
37 I2C_BANK = 2
38
39 """
40 Really basic example, uart tx/rx and i2c sda/scl pinmux
41 """
42 class ManPinmux(Elaboratable):
43 def __init__(self, pad_names):
44 print("Test Manual Pinmux!")
45
46 self.requested = {"N1": {"mux%d" % GPIO_BANK: ["gpio", 0],
47 "mux%d" % UART_BANK: ["uart", 0, "tx"],
48 "mux%d" % I2C_BANK: ["i2c", 0, "sda"]},
49 "N2": {"mux%d" % GPIO_BANK: ["gpio", 1],
50 "mux%d" % UART_BANK: ["uart", 0, "rx"],
51 "mux%d" % I2C_BANK: ["i2c", 0, "scl"]}
52 }
53 self.n_banks = 4
54 self.iomux1 = IOMuxBlockSingle(self.n_banks)
55 self.iomux2 = IOMuxBlockSingle(self.n_banks)
56 self.bank = Signal(log2_int(self.n_banks))
57 self.pads = {pad_names[0]:{}, pad_names[1]:{}}
58 for pad in self.requested.keys():
59 self.pads[pad]["pad"] = Record(name=pad, layout=io_layout)
60
61 for mux in self.requested[pad].keys():
62 periph = self.requested[pad][mux][0]
63 unit_num = self.requested[pad][mux][1]
64 if len(self.requested[pad][mux]) == 3:
65 pin = self.requested[pad][mux][2]
66 else:
67 pin = "io"
68 if periph == "gpio":
69 self.pads[pad][mux] = Record(name="gp%d" % unit_num,
70 layout=io_layout)
71 elif periph == "uart":
72 if pin == "tx":
73 self.pads[pad][mux] = Record(name="tx%d" % unit_num,
74 layout=uart_tx_layout)
75 elif pin == "rx":
76 self.pads[pad][mux] = Signal(name="rx%d" % unit_num)
77 elif periph == "i2c":
78 if pin == "sda":
79 self.pads[pad][mux] = Record(name="sda%d" % unit_num,
80 layout=io_layout)
81 elif pin == "scl":
82 self.pads[pad][mux] = Record(name="scl%d" % unit_num,
83 layout=io_layout)
84
85 def elaborate(self, platform):
86 m = Module()
87 comb, sync = m.d.comb, m.d.sync
88 iomux1 = self.iomux1
89 iomux2 = self.iomux2
90 m.submodules.iomux1 = iomux1
91 m.submodules.iomux2 = iomux2
92
93 pads = self.pads
94 pad0 = self.pads["N1"]["pad"]
95 gp0 = self.pads["N1"]["mux%d" % GPIO_BANK]
96 gp1 = self.pads["N2"]["mux%d" % GPIO_BANK]
97 tx = self.pads["N1"]["mux%d" % UART_BANK]
98 rx = self.pads["N2"]["mux%d" % UART_BANK]
99 sda = self.pads["N1"]["mux%d" % I2C_BANK]
100 scl = self.pads["N2"]["mux%d" % I2C_BANK]
101 bank = self.bank
102
103 comb += iomux1.bank.eq(bank)
104 comb += iomux2.bank.eq(bank)
105
106 # ---------------------------
107 # This section is muxing only - doesn'care about pad names
108 # ---------------------------
109 # gpio - gpio0 on Pad1, gpio1 on Pad2
110 comb += iomux1.bank_ports[GPIO_BANK].o.eq(gp0.o)
111 comb += iomux1.bank_ports[GPIO_BANK].oe.eq(gp0.oe)
112 comb += gp0.i.eq(iomux1.bank_ports[GPIO_BANK].i)
113 comb += iomux2.bank_ports[GPIO_BANK].o.eq(gp1.o)
114 comb += iomux2.bank_ports[GPIO_BANK].oe.eq(gp1.oe)
115 comb += gp1.i.eq(iomux2.bank_ports[GPIO_BANK].i)
116 # uart Pad 1 tx, Pad 2 rx
117 comb += iomux1.bank_ports[UART_BANK].o.eq(tx.o)
118 comb += iomux1.bank_ports[UART_BANK].oe.eq(tx.oe)
119 comb += rx.eq(iomux2.bank_ports[UART_BANK].i)
120 # i2c Pad 1 sda, Pad 2 scl
121 comb += iomux1.bank_ports[I2C_BANK].o.eq(sda.o)
122 comb += iomux1.bank_ports[I2C_BANK].oe.eq(sda.oe)
123 comb += sda.i.eq(iomux1.bank_ports[I2C_BANK].i)
124 comb += iomux2.bank_ports[I2C_BANK].o.eq(scl.o)
125 comb += iomux2.bank_ports[I2C_BANK].oe.eq(scl.oe)
126 comb += scl.i.eq(iomux2.bank_ports[I2C_BANK].i)
127
128 # ---------------------------
129 # Here is where the muxes are assigned to the actual pads
130 # ---------------------------
131 # TODO: for-loop to autoconnect muxes to pads (n_pads var?)
132 comb += pads['N1']["pad"].o.eq(iomux1.out_port.o)
133 comb += pads['N1']["pad"].oe.eq(iomux1.out_port.oe)
134 comb += iomux1.out_port.i.eq(pads['N1']["pad"].i)
135 comb += pads['N2']["pad"].o.eq(iomux2.out_port.o)
136 comb += pads['N2']["pad"].oe.eq(iomux2.out_port.oe)
137 comb += iomux2.out_port.i.eq(pads['N2']["pad"].i)
138
139 return m
140
141 def __iter__(self):
142 for pad in list(self.pads.keys()):
143 for field in self.pads[pad]["pad"].fields.values():
144 yield field
145 #for field in self.uart.fields.values():
146 # yield field
147 #for field in self.i2c["sda"].fields.values():
148 # yield field
149 #for field in self.i2c["scl"].fields.values():
150 # yield field
151 yield self.bank
152
153 def ports(self):
154 return list(self)
155
156 def set_bank(dut, bank, delay=1e-6):
157 yield dut.bank.eq(bank)
158 yield Delay(delay)
159
160 """
161 GPIO test function
162 Set the gpio output based on given data sequence, checked at pad.o
163 Then sends the same byte via pad.i to gpio input
164 """
165 def gpio(gpio, pad, data, delay=1e-6):
166 # Output test - Control GPIO output
167 yield gpio.oe.eq(1)
168 yield Delay(delay)
169 n_bits = len(bin(data)[2:])
170 read = 0
171 for i in range(0, n_bits):
172 bit = (data >> i) & 0x1
173 yield gpio.o.eq(bit)
174 yield Delay(delay)
175 temp = yield pad.o
176 read |= (temp << i)
177 assert data == read, f"GPIO Sent: %x | Pad Read: %x" % (data, read)
178 # Input test - Control Pad input
179 yield gpio.oe.eq(0)
180 yield Delay(delay)
181 read2 = 0
182 for i in range(0, n_bits):
183 bit = (read >> i) & 0x1
184 yield pad.i.eq(bit)
185 yield Delay(delay)
186 temp = yield gpio.i
187 read2 |= (temp << i)
188 assert read2 == read, f"Pad Sent: %x | GPIO Read: %x" % (data, read)
189 # reset input signal
190 yield pad.i.eq(0)
191 yield Delay(delay)
192
193 """
194 UART test function
195 Sends a byte via uart tx, checked at output pad
196 Then sends the same byte via input pad to uart rx
197 Input and output pads are different, so must specify both
198 """
199 def uart_send(tx, rx, pad_tx, pad_rx, byte, delay=1e-6):
200 # Drive uart tx - check the word seen at the Pad
201 yield tx.oe.eq(1)
202 yield tx.o.eq(1)
203 yield Delay(2*delay)
204 yield tx.o.eq(0) # start bit
205 yield Delay(delay)
206 read = 0
207 # send one byte, lsb first
208 for i in range(0, 8):
209 bit = (byte >> i) & 0x1
210 yield tx.o.eq(bit)
211 yield Delay(delay)
212 test_bit = yield pad_tx.o
213 read |= (test_bit << i)
214 yield tx.o.eq(1) # stop bit
215 yield Delay(delay)
216 assert byte == read, f"UART Sent: %x | Pad Read: %x" % (byte, read)
217 # Drive Pad i - check word at uart rx
218 yield pad_rx.i.eq(1)
219 yield Delay(2*delay)
220 yield pad_rx.i.eq(0) # start bit
221 yield Delay(delay)
222 read2 = 0
223 for i in range(0, 8):
224 bit = (read >> i) & 0x1
225 yield pad_rx.i.eq(bit)
226 yield Delay(delay)
227 test_bit = yield rx
228 read2 |= (test_bit << i)
229 yield pad_rx.i.eq(1) # stop bit
230 yield Delay(delay)
231 assert read == read2, f"Pad Sent: %x | UART Read: %x" % (read, read2)
232
233 """
234 I2C test function
235 Sends a byte via SDA.o (peripheral side), checked at output pad
236 Then sends the same byte via input pad to master SDA.i
237 This transaction doesn't make the distinction between read/write bit.
238 """
239 def i2c_send(sda, scl, sda_pad, byte, delay=1e-6):
240 # No checking yet
241 # No pull-up on line implemented, set high instead
242 yield sda.oe.eq(1)
243 yield sda.o.eq(1)
244 yield scl.oe.eq(1)
245 yield scl.o.eq(1)
246 yield sda_pad.i.eq(1)
247 yield Delay(delay)
248 read = 0
249 yield sda.o.eq(0) # start bit
250 yield Delay(delay)
251 for i in range(0, 8):
252 bit = (byte >> i) & 0x1
253 yield sda.o.eq(bit)
254 yield scl.o.eq(0)
255 yield Delay(delay/2)
256 yield scl.o.eq(1)
257 temp = yield sda_pad.o
258 read |= (temp << i)
259 yield Delay(delay/2)
260 yield sda.o.eq(1) # Master releases SDA line
261 yield sda.oe.eq(0)
262 assert byte == read, f"I2C Sent: %x | Pad Read: %x" % (byte, read)
263 # Slave ACK
264 yield sda_pad.i.eq(0)
265 yield scl.o.eq(0)
266 yield Delay(delay/2)
267 yield scl.o.eq(1)
268 yield Delay(delay/2)
269 # Send byte back to master
270 read2 = 0
271 for i in range(0, 8):
272 bit = (read >> i) & 0x1
273 yield sda_pad.i.eq(bit)
274 yield scl.o.eq(0)
275 yield Delay(delay/2)
276 yield scl.o.eq(1)
277 temp = yield sda.i
278 read2 |= (temp << i)
279 yield Delay(delay/2)
280 assert read == read2, f"Pad Sent: %x | I2C Read: %x" % (read, read2)
281 # Master ACK
282 yield sda.oe.eq(1)
283 yield sda.o.eq(0)
284 yield scl.o.eq(0)
285 yield Delay(delay/2)
286 yield scl.o.eq(1)
287 yield Delay(delay/2)
288 # Stop condition - SDA line high after SCL high
289 yield scl.o.eq(0)
290 yield Delay(delay/2)
291 yield scl.o.eq(1)
292 yield Delay(delay/2)
293 yield sda.o.eq(1) # 'release' the SDA line
294
295 # Test the GPIO/UART/I2C connectivity
296 def test_man_pinmux(dut, pad_names):
297 delay = 1e-6
298 # GPIO test
299 yield from set_bank(dut, GPIO_BANK)
300 yield from gpio(dut.pads["N1"]["mux%d" % GPIO_BANK],
301 dut.pads["N1"]["pad"], 0x5a5)
302 yield from gpio(dut.pads["N2"]["mux%d" % GPIO_BANK],
303 dut.pads["N2"]["pad"], 0x5a5)
304 # UART test
305 yield from set_bank(dut, UART_BANK)
306 yield from uart_send(dut.pads["N1"]["mux%d" % UART_BANK],
307 dut.pads["N2"]["mux%d" % UART_BANK],
308 dut.pads['N1']["pad"], dut.pads['N2']["pad"], 0x42)
309 #yield dut.pads['N2'].i.eq(0)
310 #yield Delay(delay)
311 # I2C test
312 yield from set_bank(dut, I2C_BANK)
313 yield from i2c_send(dut.pads["N1"]["mux%d" % I2C_BANK],
314 dut.pads["N2"]["mux%d" % I2C_BANK],
315 dut.pads['N1']["pad"], 0x67)
316
317 def sim_man_pinmux():
318 filename = "test_man_pinmux"
319 pad_names = ["N1", "N2"]
320 dut = ManPinmux(pad_names)
321 vl = rtlil.convert(dut, ports=dut.ports())
322 with open(filename+".il", "w") as f:
323 f.write(vl)
324
325 m = Module()
326 m.submodules.manpinmux = dut
327
328 sim = Simulator(m)
329
330 sim.add_process(wrap(test_man_pinmux(dut, pad_names)))
331 sim_writer = sim.write_vcd(filename+".vcd")
332 with sim_writer:
333 sim.run()
334 #gen_gtkw_doc("top.manpinmux", dut.n_banks, filename)
335
336 if __name__ == '__main__':
337 sim_man_pinmux()