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