d69e94ac994f4c48c752b675b10f55fd41d8c8d8
[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 bank ports and one pad port.
32 # The bank ports are intended to be wired to peripheral functions,
33 # while the pad port will connect to the I/O pad.
34 # Each port has o/oe/i signals, and the bank signal is used to select
35 # between the bank ports.
36 class IOMuxBlockSingle(Elaboratable):
37
38 def __init__(self, n_banks=4):
39 print("1-bit IO Mux Block")
40 self.n_banks = n_banks
41 self.bank = Signal(log2_int(self.n_banks))
42
43 temp = []
44 for i in range(self.n_banks):
45 name = "bank%d" % i
46 temp.append(Record(name=name, layout=io_layout))
47 self.bank_ports = Array(temp)
48
49 self.out_port = Record(name="IO", layout=io_layout)
50
51 def elaborate(self, platform):
52 m = Module()
53 comb, sync = m.d.comb, m.d.sync
54
55 bank = self.bank
56 bank_ports = self.bank_ports
57 out_port = self.out_port
58
59 # Connect IO Pad output port to one of the peripheral IOs
60 # Connect peripheral inputs to the IO pad input
61 comb += self.out_port.o.eq(self.bank_ports[bank].o)
62 comb += self.out_port.oe.eq(self.bank_ports[bank].oe)
63
64 comb += self.bank_ports[bank].i.eq(self.out_port.i)
65
66 return m
67
68 def connect_bank_to_io(self, domain, bank_arg):
69 domain += self.out_port.o.eq(self.bank_ports[bank_arg].o)
70 domain += self.out_port.oe.eq(self.bank_ports[bank_arg].oe)
71 domain += self.bank_ports[bank_arg].i.eq(self.out_port.i)
72
73 def __iter__(self):
74 """ Get member signals for Verilog form. """
75 for field in self.out_port.fields.values():
76 yield field
77 for bank in range(self.n_banks):
78 for field in self.bank_ports[bank].fields.values():
79 yield field
80 yield self.bank
81
82 def ports(self):
83 return list(self)
84
85 # Method to test a particular bank port
86 # when rand_order is True, previous and consecutive banks are
87 # random (but NOT equal to given bank)
88 def test_single_bank(dut, bank, rand_order=True, delay=1e-6):
89 if rand_order:
90 print("Randomising the prev and next banks")
91 prev_bank=bank
92 while(prev_bank == bank):
93 prev_bank = randint(0, dut.n_banks-1)
94 next_bank=bank
95 while(next_bank == bank):
96 next_bank = randint(0, dut.n_banks-1)
97 else:
98 # Set the prev and next banks as consecutive banks
99 if bank == 0:
100 prev_bank = dut.n_banks - 1
101 else:
102 prev_bank = bank - 1
103
104 if bank == dut.n_banks:
105 next_bank = 0
106 else:
107 next_bank = bank + 1
108
109 print("Prev=%d, Given=%d, Next=%d" % (prev_bank, bank, next_bank))
110
111 # Clear o/oe, delay, set port i
112 # Set to previous bank, delay
113 # Assert bank i == 0
114 # Set to desired bank
115 # Assert bank i == 1
116 # Set o/oe, delay
117 # Assert o, oe == 1
118 # Set to next bank, delay
119 # Assert bank i == 0
120 yield dut.bank_ports[bank].o.eq(0)
121 yield Delay(delay)
122 yield dut.bank_ports[bank].oe.eq(0)
123 yield Delay(delay)
124 yield dut.out_port.i.eq(1)
125 yield Delay(delay)
126
127 yield dut.bank.eq(prev_bank)
128 yield Delay(delay)
129
130 test_i = yield dut.bank_ports[bank].i
131 assert(test_i == 0)
132
133 yield dut.bank.eq(bank)
134 yield Delay(delay)
135
136 test_o = yield dut.out_port.o
137 test_oe = yield dut.out_port.oe
138 test_i = yield dut.bank_ports[bank].i
139 assert(test_o == 0)
140 assert(test_oe == 0)
141 assert(test_i == 1)
142
143 yield dut.bank_ports[bank].o.eq(1)
144 yield Delay(delay)
145 yield dut.bank_ports[bank].oe.eq(1)
146 yield Delay(delay)
147
148 test_o = yield dut.out_port.o
149 test_oe = yield dut.out_port.oe
150 assert(test_o == 1)
151 assert(test_oe == 1)
152
153 yield dut.bank.eq(next_bank)
154 yield Delay(delay)
155
156 test_i = yield dut.bank_ports[bank].i
157 assert(test_i == 0)
158
159 def test_iomux(dut, rand_order=True):
160 print("------START----------------------")
161 #print(dir(dut.bank_ports[0]))
162 #print(dut.bank_ports[0].fields)
163
164 # Produce a test list of bank values
165 test_bank_vec = list(range(0, dut.n_banks))
166 #print(test_bank_vec)
167 # Randomise for wider testing
168 if rand_order:
169 shuffle(test_bank_vec)
170 #print(test_bank_vec)
171 for i in range(dut.n_banks):
172 yield from test_single_bank(dut, test_bank_vec[i], rand_order)
173
174 print("Finished the 1-bit IO mux block test!")
175
176 def gen_gtkw_doc(module_name, n_banks, filename):
177 # GTKWave doc generation
178 style = {
179 '': {'base': 'hex'},
180 'in': {'color': 'orange'},
181 'out': {'color': 'yellow'},
182 'debug': {'module': 'top', 'color': 'red'}
183 }
184
185 # Create a trace list, each block expected to be a tuple()
186 traces = []
187 for bank in range(0, n_banks):
188 temp_traces = ('Bank%d' % bank, [
189 ('bank%d__i' % bank, 'in'),
190 ('bank%d__o' % bank, 'out'),
191 ('bank%d__oe' % bank, 'out')
192 ])
193 traces.append(temp_traces)
194
195 temp_traces = ('Misc', [
196 ('bank[%d:0]' % ((n_banks-1).bit_length()-1), 'in')
197 ])
198 traces.append(temp_traces)
199 temp_traces = ('IO port to pad', [
200 ('IO__i', 'in'),
201 ('IO__o', 'out'),
202 ('IO__oe', 'out')
203 ])
204 traces.append(temp_traces)
205 #print(traces)
206
207 write_gtkw(filename+".gtkw", filename+".vcd", traces, style,
208 module=module_name)
209
210 def sim_iomux(rand_order=True):
211 filename = "test_iomux" # Doesn't include extension
212 n_banks = 8
213 dut = IOMuxBlockSingle(n_banks)
214 vl = rtlil.convert(dut, ports=dut.ports())
215 with open(filename+".il", "w") as f:
216 f.write(vl)
217
218 m = Module()
219 m.submodules.pinmux = dut
220
221 sim = Simulator(m)
222
223 sim.add_process(wrap(test_iomux(dut, rand_order)))
224 sim_writer = sim.write_vcd(filename+".vcd")
225 with sim_writer:
226 sim.run()
227
228 gen_gtkw_doc("top.pinmux", dut.n_banks, filename)
229
230
231
232 if __name__ == '__main__':
233 sim_iomux(rand_order=True)
234