Added prefix to layout, modifyingfunctions for testing
[pinmux.git] / src / spec / simple_gpio.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
9 from nmigen import Elaboratable, Module, Signal, Record, Array, Cat
10 from nmigen.hdl.rec import Layout
11 from nmigen.utils import log2_int
12 from nmigen.cli import rtlil
13 from soc.minerva.wishbone import make_wb_layout
14 from nmutil.util import wrap
15 from soc.bus.test.wb_rw import wb_read, wb_write
16
17 cxxsim = False
18 if cxxsim:
19 from nmigen.sim.cxxsim import Simulator, Settle
20 else:
21 from nmigen.sim import Simulator, Settle
22
23 # Layout of 8-bit configuration word:
24 # bank_select[2:0] i/o | pden puen ien oe
25 OESHIFT = 0
26 IESHIFT = 1
27 PUSHIFT = 2
28 PDSHIFT = 3
29 IOSHIFT = 4
30 BANKSHIFT = 5
31 NUMBANKBITS = 3 # only supporting 8 banks (0-7)
32
33 # For future testing:
34 WORDSIZE = 4 # in bytes
35
36 class SimpleGPIO(Elaboratable):
37
38 def __init__(self, n_gpio=16):
39 self.n_gpio = n_gpio
40 class Spec: pass
41 spec = Spec()
42 spec.addr_wid = 30
43 spec.mask_wid = 4
44 spec.reg_wid = 32
45 self.bus = Record(make_wb_layout(spec), name="gpio_wb")
46
47 self.bank_sel = Array([Signal(NUMBANKBITS) for _ in range(n_gpio)])
48 self.gpio_o = Signal(n_gpio)
49 self.gpio_oe = Signal(n_gpio)
50 self.gpio_i = Signal(n_gpio)
51 self.gpio_ie = Signal(n_gpio)
52 self.pden = Signal(n_gpio)
53 self.puen = Signal(n_gpio)
54
55 csrbus_layout = (("oe", 1),
56 ("ie", 1),
57 ("puen", 1),
58 ("pden", 1),
59 ("io", 1),
60 ("bank_sel", NUMBANKBITS)
61 )
62 # self.csrbus = Record(csrbus_layout)
63
64 print("CSRBUS layout: ", csrbus_layout)
65
66 # create array - probably a cleaner way to do this...
67 temp = []
68 for i in range(0, WORDSIZE):
69 temp_str = "word{}".format(i)
70 temp.append(Record(name=temp_str, layout=csrbus_layout))
71 self.multicsrbus = Array(temp)
72
73
74 #gpio_layout = (("oe", 1),
75 # ("ie", 1),
76 # ("puen", 1),
77 # ("pden", 1),
78 # ("o", 1),
79 # ("bank_sel", NUMBANKBITS),
80 # ("i", 1),
81 # )
82
83 #self.gpios = Array([Record(gpio_layout) for _ in range(n_gpio)])
84
85 def elaborate(self, platform):
86 m = Module()
87 comb, sync = m.d.comb, m.d.sync
88
89 bus = self.bus
90 wb_rd_data = bus.dat_r
91 wb_wr_data = bus.dat_w
92 wb_ack = bus.ack
93
94 bank_sel = self.bank_sel
95 gpio_o = self.gpio_o
96 gpio_oe = self.gpio_oe
97 gpio_i = self.gpio_i
98 gpio_ie = self.gpio_ie
99 pden = self.pden
100 puen = self.puen
101 # csrbus = self.csrbus
102 multi = self.multicsrbus
103 #gpios = self.gpios
104
105 comb += wb_ack.eq(0)
106
107 gpio_addr = Signal(log2_int(self.n_gpio))
108 gpio_o_list = Array(list(gpio_o))
109 print(bank_sel)
110 print(gpio_o_list)
111 gpio_oe_list = Array(list(gpio_oe))
112 gpio_i_list = Array(list(gpio_i))
113 gpio_ie_list = Array(list(gpio_ie))
114 pden_list = Array(list(pden))
115 puen_list = Array(list(puen))
116
117 # Flag for indicating rd/wr transactions
118 new_transaction = Signal(1)
119
120 #print("Types:")
121 #print("gpio_addr: ", type(gpio_addr))
122 #print("gpio_o_list: ", type(gpio_o_list))
123 #print("bank_sel: ", type(bank_sel))
124
125 # One address used to configure CSR, set output, read input
126 with m.If(bus.cyc & bus.stb):
127 comb += wb_ack.eq(1) # always ack
128 sync += gpio_addr.eq(bus.adr)
129 sync += new_transaction.eq(1)
130 with m.If(bus.we): # write
131 # Configure CSR
132 for byte in range(0, WORDSIZE):
133 sync += multi[byte].eq(wb_wr_data[byte*8:8+byte*8])
134 with m.Else(): # read
135 # Concatinate the GPIO configs that are on the same "row" or
136 # address and send
137 multi_cat = []
138 for i in range(0, WORDSIZE):
139 multi_cat.append(multi[i])
140 comb += wb_rd_data.eq(Cat(multi_cat))
141 with m.Else():
142 sync += new_transaction.eq(0)
143 # Update the state of "io" while no WB transactions
144 for byte in range(0, WORDSIZE):
145 with m.If(gpio_oe_list[gpio_addr] & (~gpio_ie_list[gpio_addr])):
146 sync += multi[byte].io.eq(gpio_o_list[gpio_addr+byte])
147 with m.If(gpio_ie_list[gpio_addr] & (~gpio_oe_list[gpio_addr])):
148 sync += multi[byte].io.eq(gpio_i_list[gpio_addr+byte])
149 with m.Else():
150 sync += multi[byte].io.eq(multi[byte].io)
151
152 # Only update GPIOs config if a new transaction happened last cycle
153 # (read or write). Always lags from csrbus by 1 clk cycle, most
154 # sane way I could think of while using Record().
155 with m.If(new_transaction):
156 for byte in range(0, WORDSIZE):
157 sync += gpio_oe_list[gpio_addr+byte].eq(multi[byte].oe)
158 sync += gpio_ie_list[gpio_addr+byte].eq(multi[byte].ie)
159 sync += puen_list[gpio_addr+byte].eq(multi[byte].puen)
160 sync += pden_list[gpio_addr+byte].eq(multi[byte].pden)
161 # Check to prevent output being set if GPIO configured as input
162 # TODO: Is this necessary? PAD might deal with this
163 # check GPIO is in output mode and NOT input (oe high, ie low)
164 with m.If(gpio_oe_list[gpio_addr] & (~gpio_ie_list[gpio_addr])):
165 sync += gpio_o_list[gpio_addr+byte].eq(multi[byte].io)
166 sync += bank_sel[gpio_addr+byte].eq(multi[byte].bank_sel)
167 return m
168
169 def __iter__(self):
170 for field in self.bus.fields.values():
171 yield field
172 yield self.gpio_o
173
174 def ports(self):
175 return list(self)
176
177 def config_to_regval(oe, ie, puen, pden, outval, bank_sel):
178 csr_val = ( (oe << OESHIFT)
179 | (ie << IESHIFT)
180 | (puen << PUSHIFT)
181 | (pden << PDSHIFT)
182 | (outval << IOSHIFT)
183 | (bank_sel << BANKSHIFT) )
184 print("Created CSR value: {1:x}".format(csr_val))
185
186 return csr_val # return the config state
187
188
189 # TODO: probably make into class (or return state in a variable)
190 def gpio_config(dut, gpio, oe, ie, puen, pden, outval, bank_sel, check=False):
191 csr_val = ( (oe << OESHIFT)
192 | (ie << IESHIFT)
193 | (puen << PUSHIFT)
194 | (pden << PDSHIFT)
195 | (outval << IOSHIFT)
196 | (bank_sel << BANKSHIFT) )
197 print("Configuring GPIO{0} CSR to {1:x}".format(gpio, csr_val))
198 yield from wb_write(dut.bus, gpio, csr_val)
199 yield # Allow one clk cycle to propagate
200
201 if(check):
202 # Check the written value
203 test_csr = yield from gpio_rd_csr(dut, gpio)
204 assert test_csr == csr_val
205
206 return csr_val # return the config state
207
208 # Not used normally - only for debug
209 def reg_write(dut, gpio, reg_val):
210 print("Configuring CSR to {0:x}".format(reg_val))
211 yield from wb_write(dut.bus, gpio, reg_val)
212
213 # TODO: Return the configuration states
214 def gpio_rd_csr(dut, gpio):
215 csr_val = yield from wb_read(dut.bus, gpio)
216 print("GPIO{0} | CSR: {1:x}".format(gpio, csr_val))
217 print("Output Enable: {0:b}".format((csr_val >> OESHIFT) & 1))
218 print("Input Enable: {0:b}".format((csr_val >> IESHIFT) & 1))
219 print("Pull-Up Enable: {0:b}".format((csr_val >> PUSHIFT) & 1))
220 print("Pull-Down Enable: {0:b}".format((csr_val >> PDSHIFT) & 1))
221 if ((csr_val >> IESHIFT) & 1):
222 print("Input: {0:b}".format((csr_val >> IOSHIFT) & 1))
223 else:
224 print("Output: {0:b}".format((csr_val >> IOSHIFT) & 1))
225 bank_sel_mask = (2**NUMBANKBITS)-1
226 print("Bank Sel: {0:b}".format((csr_val >> BANKSHIFT) & bank_sel_mask))
227 # gpio_parse_csr(csr_val)
228 return csr_val
229
230 # TODO
231 def gpio_rd_input(dut, gpio):
232 in_val = yield from wb_read(dut.bus, gpio)
233 in_val = (in_val >> IOSHIFT) & 1
234 print("GPIO{0} | Input: {1:b}".format(gpio, in_val))
235 return in_val
236
237 def gpio_set_out(dut, gpio, csr_val, output):
238 print("Setting GPIO{0} output to {1}".format(gpio, output))
239 yield from wb_write(dut.bus, gpio, csr_val | (output<<IOSHIFT))
240 yield # Allow one clk cycle to propagate
241
242 # TODO: There's probably a cleaner way to clear the bit...
243 def gpio_set_in_pad(dut, gpio, in_val):
244 old_in_val = yield dut.gpio_i
245 if in_val:
246 new_in_val = old_in_val | (in_val << gpio)
247 else:
248 temp = (old_in_val >> gpio) & 1
249 if temp:
250 mask = ~(1 << gpio)
251 new_in_val = old_in_val & mask
252 else:
253 new_in_val = old_in_val
254 print("Previous GPIO i: {0:b} | New GPIO i: {1:b}"
255 .format(old_in_val, new_in_val))
256 yield dut.gpio_i.eq(new_in_val)
257 yield # Allow one clk cycle to propagate
258
259 def gpio_test_in_pattern(dut, pattern):
260 num_gpios = len(dut.gpio_o)
261 print("Test pattern:")
262 print(pattern)
263 for pat in range(0, len(pattern)):
264 for gpio in range(0, num_gpios):
265 yield from gpio_set_in_pad(dut, gpio, pattern[pat])
266 yield
267 temp = yield from gpio_rd_input(dut, gpio)
268 print("Pattern: {0}, Reading {1}".format(pattern[pat], temp))
269 assert (temp == pattern[pat])
270 pat += 1
271 if pat == len(pattern):
272 break
273
274 def test_gpio_single(dut, gpio, use_random=True):
275 oe = 1
276 ie = 0
277 output = 0
278 puen = 0
279 pden = 0
280 if use_random:
281 bank_sel = randint(0, 2**NUMBANKBITS)
282 print("Random bank_select: {0:b}".format(bank_sel))
283 else:
284 bank_sel = 3 # not special, chose for testing
285
286 gpio_csr = yield from gpio_config(dut, gpio, oe, ie, puen, pden, output,
287 bank_sel, check=True)
288 # Enable output
289 output = 1
290 gpio_csr = yield from gpio_config(dut, gpio, oe, ie, puen, pden, output,
291 bank_sel, check=True)
292
293
294 def sim_gpio(dut, use_random=True):
295 print(dir(dut))
296 print(dut)
297 num_gpios = len(dut.gpio_o)
298 if use_random:
299 bank_sel = randint(0, 2**NUMBANKBITS)
300 print("Random bank_select: {0:b}".format(bank_sel))
301 else:
302 bank_sel = 3 # not special, chose for testing
303 """
304 oe = 1
305 ie = 0
306 output = 0
307 puen = 0 # 1
308 pden = 0
309 gpio_csr = [0] * num_gpios
310 # Configure GPIOs for
311 for gpio in range(0, num_gpios):
312 gpio_csr[gpio] = yield from gpio_config(dut, gpio, oe, ie, puen,
313 pden, output, bank_sel)
314 # Set outputs
315 output = 1
316 for gpio in range(0, num_gpios):
317 yield from gpio_set_out(dut, gpio, gpio_csr[gpio], output)
318
319 # Read CSR
320 for gpio in range(0, num_gpios):
321 temp_csr = yield from gpio_rd_csr(dut, gpio)
322 assert ((temp_csr>>IOSHIFT) & 1) == output
323 # Configure for input
324 oe = 0
325 ie = 1
326 for gpio in range(0, num_gpios):
327 gpio_csr[gpio] = yield from gpio_config(dut, gpio, oe, ie, puen,
328 pden, output, bank_sel)
329
330 temp = yield from gpio_rd_input(dut, gpio)
331 assert temp == 0
332
333 yield from gpio_set_in_pad(dut, gpio, 1)
334 temp = yield from gpio_rd_input(dut, gpio)
335 assert temp == 1
336
337 # TODO: not working yet
338 #test_pattern = []
339 #for i in range(0, (num_gpios * 2)):
340 # test_pattern.append(randint(0,1))
341 #yield from gpio_test_in_pattern(dut, test_pattern)
342 """
343
344 yield from test_gpio_single(dut, 0, use_random=True)
345
346 #reg_val = 0xC56271A2
347 #reg_val = 0xFFFFFFFF
348 #yield from reg_write(dut, 0, reg_val)
349 #yield from reg_write(dut, 0, reg_val)
350 #yield
351
352 #csr_val = yield from wb_read(dut.bus, 0)
353 #print("CSR Val: {0:x}".format(csr_val))
354 print("Finished the simple GPIO block test!")
355
356 def test_gpio():
357 num_gpio = 4
358 dut = SimpleGPIO(num_gpio)
359 vl = rtlil.convert(dut, ports=dut.ports())
360 with open("test_gpio.il", "w") as f:
361 f.write(vl)
362
363 m = Module()
364 m.submodules.xics_icp = dut
365
366 sim = Simulator(m)
367 sim.add_clock(1e-6)
368
369 sim.add_sync_process(wrap(sim_gpio(dut, use_random=False)))
370 sim_writer = sim.write_vcd('test_gpio.vcd')
371 with sim_writer:
372 sim.run()
373
374
375 if __name__ == '__main__':
376 test_gpio()
377