fix(iomux): Fix port signal length (given mux size non-power of 2)
[pinmux.git] / src / spec / iomux.py
1 """Simple GPIO peripheral on wishbone
2
3 This is an extremely simple GPIO peripheral intended for use in XICS
4 testing, however it could also be used as an actual GPIO peripheral
5
6 Modified for use with pinmux, will probably change the class name later.
7 """
8 from random import randint, shuffle
9 #from math import ceil, floor
10 from nmigen import Elaboratable, Module, Signal, Record, Array, Cat
11 from nmigen.hdl.rec import Layout
12 from nmigen.utils import log2_int
13 from nmigen.cli import rtlil
14 #from soc.minerva.wishbone import make_wb_layout
15 from nmutil.util import wrap
16 #from soc.bus.test.wb_rw import wb_read, wb_write
17
18 from nmutil.gtkw import write_gtkw
19
20 cxxsim = False
21 if cxxsim:
22 from nmigen.sim.cxxsim import Simulator, Settle, Delay
23 else:
24 from nmigen.sim import Simulator, Settle, Delay
25
26 io_layout = (("i", 1),
27 ("oe", 1),
28 ("o", 1)
29 )
30
31 # This block produces an N-to-1 mux with N 3-bit periph ports and one pad port.
32 # The peripheral ports are intended to be wired to peripheral functions,
33 # while the pad port will connect to the I/O pad.
34 # Peripheral and output ports have o/oe/i signals, and the port signal is used
35 # to select between the peripheral ports.
36 class IOMuxBlockSingle(Elaboratable):
37
38 def __init__(self, n_ports=4):
39 print("1-bit IO Mux Block")
40 self.n_ports = n_ports
41 portsize = n_ports.bit_length()
42 self.port = Signal(portsize)
43
44 temp = []
45 for i in range(self.n_ports):
46 name = "port%d" % i
47 temp.append(Record(name=name, layout=io_layout))
48 self.periph_ports = Array(temp)
49
50 self.out_port = Record(name="IO", layout=io_layout)
51
52 def elaborate(self, platform):
53 m = Module()
54 comb, sync = m.d.comb, m.d.sync
55
56 port = self.port
57 periph_ports = self.periph_ports
58 out_port = self.out_port
59
60 # Connect IO Pad output port to one of the peripheral IOs
61 # Connect peripheral inputs to the IO pad input
62 comb += self.out_port.o.eq(self.periph_ports[port].o)
63 comb += self.out_port.oe.eq(self.periph_ports[port].oe)
64
65 comb += self.periph_ports[port].i.eq(self.out_port.i)
66
67 return m
68
69 def connect_port_to_io(self, domain, port_arg):
70 domain += self.out_port.o.eq(self.periph_ports[port_arg].o)
71 domain += self.out_port.oe.eq(self.periph_ports[port_arg].oe)
72 domain += self.periph_ports[port_arg].i.eq(self.out_port.i)
73
74 def __iter__(self):
75 """ Get member signals for Verilog form. """
76 for field in self.out_port.fields.values():
77 yield field
78 for port in range(self.n_ports):
79 for field in self.periph_ports[port].fields.values():
80 yield field
81 yield self.port
82
83 def ports(self):
84 return list(self)
85
86 # Method to test a particular peripheral port
87 # when rand_order is True, previous and consecutive ports are
88 # random (but NOT equal to given port)
89 def test_single_port(dut, port, rand_order=True, delay=1e-6):
90 if rand_order:
91 print("Randomising the prev and next ports")
92 prev_port=port
93 while(prev_port == port):
94 prev_port = randint(0, dut.n_ports-1)
95 next_port=port
96 while(next_port == port):
97 next_port = randint(0, dut.n_ports-1)
98 else:
99 # Set the prev and next ports as consecutive ports
100 if port == 0:
101 prev_port = dut.n_ports - 1
102 else:
103 prev_port = port - 1
104
105 if port == dut.n_ports:
106 next_port = 0
107 else:
108 next_port = port + 1
109
110 print("Prev=%d, Given=%d, Next=%d" % (prev_port, port, next_port))
111
112 # Clear o/oe, delay, set port i
113 # Set to previous port, delay
114 # Assert port i == 0
115 # Set to desired port
116 # Assert port i == 1
117 # Set o/oe, delay
118 # Assert o, oe == 1
119 # Set to next port, delay
120 # Assert port i == 0
121 yield dut.periph_ports[port].o.eq(0)
122 yield Delay(delay)
123 yield dut.periph_ports[port].oe.eq(0)
124 yield Delay(delay)
125 yield dut.out_port.i.eq(1)
126 yield Delay(delay)
127
128 yield dut.port.eq(prev_port)
129 yield Delay(delay)
130
131 test_i = yield dut.periph_ports[port].i
132 assert(test_i == 0)
133
134 yield dut.port.eq(port)
135 yield Delay(delay)
136
137 test_o = yield dut.out_port.o
138 test_oe = yield dut.out_port.oe
139 test_i = yield dut.periph_ports[port].i
140 assert(test_o == 0)
141 assert(test_oe == 0)
142 assert(test_i == 1)
143
144 yield dut.periph_ports[port].o.eq(1)
145 yield Delay(delay)
146 yield dut.periph_ports[port].oe.eq(1)
147 yield Delay(delay)
148
149 test_o = yield dut.out_port.o
150 test_oe = yield dut.out_port.oe
151 assert(test_o == 1)
152 assert(test_oe == 1)
153
154 yield dut.port.eq(next_port)
155 yield Delay(delay)
156
157 test_i = yield dut.periph_ports[port].i
158 assert(test_i == 0)
159
160 def test_iomux(dut, rand_order=True):
161 print("------START----------------------")
162 #print(dir(dut.periph_ports[0]))
163 #print(dut.periph_ports[0].fields)
164
165 # Produce a test list of port values
166 test_port_vec = list(range(0, dut.n_ports))
167 #print(test_port_vec)
168 # Randomise for wider testing
169 if rand_order:
170 shuffle(test_port_vec)
171 #print(test_port_vec)
172 for i in range(dut.n_ports):
173 yield from test_single_port(dut, test_port_vec[i], rand_order)
174
175 print("Finished the 1-bit IO mux block test!")
176
177 def gen_gtkw_doc(module_name, n_ports, filename):
178 # GTKWave doc generation
179 style = {
180 '': {'base': 'hex'},
181 'in': {'color': 'orange'},
182 'out': {'color': 'yellow'},
183 'debug': {'module': 'top', 'color': 'red'}
184 }
185
186 # Create a trace list, each block expected to be a tuple()
187 traces = []
188 for port in range(0, n_ports):
189 temp_traces = ('Bank%d' % port, [
190 ('port%d__i' % port, 'in'),
191 ('port%d__o' % port, 'out'),
192 ('port%d__oe' % port, 'out')
193 ])
194 traces.append(temp_traces)
195
196 temp_traces = ('Misc', [
197 ('port[%d:0]' % ((n_ports-1).bit_length()-1), 'in')
198 ])
199 traces.append(temp_traces)
200 temp_traces = ('IO port to pad', [
201 ('IO__i', 'in'),
202 ('IO__o', 'out'),
203 ('IO__oe', 'out')
204 ])
205 traces.append(temp_traces)
206 #print(traces)
207
208 write_gtkw(filename+".gtkw", filename+".vcd", traces, style,
209 module=module_name)
210
211 def sim_iomux(rand_order=True):
212 filename = "test_iomux" # Doesn't include extension
213 n_ports = 8
214 dut = IOMuxBlockSingle(n_ports)
215 vl = rtlil.convert(dut, ports=dut.ports())
216 with open(filename+".il", "w") as f:
217 f.write(vl)
218
219 m = Module()
220 m.submodules.pinmux = dut
221
222 sim = Simulator(m)
223
224 sim.add_process(wrap(test_iomux(dut, rand_order)))
225 sim_writer = sim.write_vcd(filename+".vcd")
226 with sim_writer:
227 sim.run()
228
229 gen_gtkw_doc("top.pinmux", dut.n_ports, filename)
230
231
232
233 if __name__ == '__main__':
234 sim_iomux(rand_order=True)
235