Created GPIOManager class for tracking config state, setting/reading configuration.
[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("SimpleGPIO: 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(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(self.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 multi = self.multicsrbus
82
83 comb += wb_ack.eq(0)
84
85 row_start = Signal(log2_int(self.n_gpio))
86 # Flag for indicating rd/wr transactions
87 new_transaction = Signal(1)
88
89 #print("Types:")
90 #print("gpio_addr: ", type(gpio_addr))
91
92 # One address used to configure CSR, set output, read input
93 with m.If(bus.cyc & bus.stb):
94 comb += wb_ack.eq(1) # always ack
95 # Probably wasteful
96 sync += row_start.eq(bus.adr * self.wordsize)
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[row_start+byte].oe):
114 sync += multi[byte].io.eq(gpio_ports[row_start+byte].o)
115 with m.Else():
116 sync += multi[byte].io.eq(gpio_ports[row_start+byte].i)
117 # Only update GPIOs config if a new transaction happened last cycle
118 # (read or write). Always lags from multi 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[row_start+byte].oe.eq(multi[byte].oe)
123 sync += gpio_ports[row_start+byte].puen.eq(multi[byte].puen)
124 sync += gpio_ports[row_start+byte].pden.eq(multi[byte].pden)
125 # Check to prevent output being set if GPIO configured as input
126 # TODO: No checking is done if ie/oe high together
127 with m.If(gpio_ports[row_start+byte].oe):
128 sync += gpio_ports[row_start+byte].o.eq(multi[byte].io)
129 sync += gpio_ports[row_start+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 def gpio_test_in_pattern(dut, pattern):
141 num_gpios = len(dut.gpio_ports)
142 print("Test pattern:")
143 print(pattern)
144 for pat in range(0, len(pattern)):
145 for gpio in range(0, num_gpios):
146 yield from gpio_set_in_pad(dut, gpio, pattern[pat])
147 yield
148 temp = yield from gpio_rd_input(dut, gpio)
149 print("Pattern: {0}, Reading {1}".format(pattern[pat], temp))
150 assert (temp == pattern[pat])
151 pat += 1
152 if pat == len(pattern):
153 break
154
155 def test_gpio_single(dut, gpio, use_random=True):
156 oe = 1
157 ie = 0
158 output = 0
159 puen = 0
160 pden = 0
161 if use_random:
162 bank = randint(0, (2**NUMBANKBITS)-1)
163 print("Random bank select: {0:b}".format(bank))
164 else:
165 bank = 3 # not special, chose for testing
166
167 gpio_csr = yield from gpio_config(dut, gpio, oe, ie, puen, pden, output,
168 bank, check=True)
169 # Enable output
170 output = 1
171 gpio_csr = yield from gpio_config(dut, gpio, oe, ie, puen, pden, output,
172 bank, check=True)
173
174 # Shadow reg container class
175 class GPIOConfigReg():
176 def __init__(self):
177 self.oe=0
178 self.ie=0
179 self.puen=0
180 self.pden=0
181 self.io=0
182 self.bank=0
183
184 def set(self, oe=0, ie=0, puen=0, pden=0, outval=0, bank=0):
185 self.oe=oe
186 self.ie=ie
187 self.puen=puen
188 self.pden=pden
189 self.io=outval
190 self.bank=bank
191
192 def set_out(self, outval):
193 self.io=outval
194
195 # Object for storing each gpio's config state
196
197 class GPIOManager():
198 def __init__(self, dut, layout):
199 self.dut = dut
200 # arrangement of config bits making up csr word
201 self.csr_layout = layout
202 self.shift_dict = self._create_shift_dict()
203 self.n_gpios = len(self.dut.gpio_ports)
204 print(dir(self.dut))
205 # Since GPIO HDL block already has wordsize parameter, use directly
206 # Alternatively, can derive from WB data r/w buses (div by 8 for bytes)
207 #self.wordsize = len(self.dut.gpio_wb__dat_w) / 8
208 self.wordsize = self.dut.wordsize
209 self.n_rows = ceil(self.n_gpios / self.wordsize)
210 self.shadow_csr = []
211 for i in range(self.n_gpios):
212 self.shadow_csr.append(GPIOConfigReg())
213
214 # The shifting of control bits in the configuration word is dependent on the
215 # defined layout. To prevent maintaining the shift constants in a separate
216 # location, the same layout is used to generate a dictionary of bit shifts
217 # with which the configuration word can be produced!
218 def _create_shift_dict(self):
219 shift = 0
220 shift_dict = {}
221 for i in range(0, len(self.csr_layout)):
222 shift_dict[self.csr_layout[i][0]] = shift
223 shift += self.csr_layout[i][1]
224 print(shift_dict)
225 return shift_dict
226
227 def _parse_gpio_arg(self, gpio_str):
228 # TODO: No input checking!
229 print("Given GPIO/range string: {}".format(gpio_str))
230 if gpio_str == "all":
231 start = 0
232 end = self.n_gpios
233 elif '-' in gpio_str:
234 start, end = gpio_str.split('-')
235 start = int(start)
236 end = int(end) + 1
237 if (end < start) or (end > self.n_gpios):
238 raise Exception("Second GPIO must be higher than first and"
239 + " must be lower or equal to last available GPIO.")
240 else:
241 start = int(gpio_str)
242 if start >= self.n_gpios:
243 raise Exception("GPIO must be less/equal to last GPIO.")
244 end = start + 1
245 print("Setting config for GPIOs {0} until {1}".format(start, end))
246 return start, end
247
248 # Take config parameters of specified GPIOs, and combine them to produce
249 # bytes for sending via WB bus
250 def _pack_csr(self, start, end):
251 #start, end = self._parse_gpio_arg(gpio_str)
252 num_csr = end-start
253 csr = [0] * num_csr
254 for i in range(0, num_csr):
255 gpio = i + start
256 print("Pack: gpio{}".format(gpio))
257 csr[i] = ((self.shadow_csr[gpio].oe << self.shift_dict['oe'])
258 | (self.shadow_csr[gpio].ie << self.shift_dict['ie'])
259 | (self.shadow_csr[gpio].puen << self.shift_dict['puen'])
260 | (self.shadow_csr[gpio].pden << self.shift_dict['pden'])
261 | (self.shadow_csr[gpio].io << self.shift_dict['io'])
262 | (self.shadow_csr[gpio].bank << self.shift_dict['bank']))
263
264 print("GPIO{0} Packed CSR: {1:x}".format(gpio, csr[i]))
265
266 return csr # return the config byte list
267
268 def rd_csr(self, row_start):
269 row_word = yield from wb_read(self.dut.bus, row_start)
270 print("Returned CSR: {0:x}".format(row_word))
271 return row_word
272
273 def rd_input(self, row_start):
274 in_val = yield from wb_read(dut.bus, gpio)
275 in_val = (in_val >> IOSHIFT) & 1
276 print("GPIO{0} | Input: {1:b}".format(gpio, in_val))
277 return in_val
278
279 def print_info(self):
280 print("----------")
281 print("GPIO Block Info:")
282 print("Number of GPIOs: {}".format(self.n_gpios))
283 print("WB Data bus width (in bytes): {}".format(self.wordsize))
284 print("Number of rows: {}".format(self.n_rows))
285 print("----------")
286
287 # Write all shadow registers to GPIO block
288 def wr_all(self):
289 # UPDATE using ALL shadow registers
290 csr = self._pack_csr(0, self.n_gpios)
291 #start_addr = floor(start / self.wordsize)
292 start_addr = 0
293 curr_gpio = 0
294 for row in range(0, self.n_rows):
295 row_word = 0
296 start_byte = curr_gpio % self.wordsize
297 for byte in range(start_byte, self.wordsize):
298 if curr_gpio > self.n_gpios:
299 break
300 #row_word += csr[byte] << (8 * byte)
301 row_word += csr[curr_gpio] << (8 * byte)
302 curr_gpio += 1
303 print("Configuring CSR to {0:x} to addr: {1:x}"
304 .format(row_word, start_addr+row))
305 yield from wb_write(self.dut.bus, start_addr+row, row_word)
306 yield # Allow one clk cycle to propagate
307
308 if(True): #check):
309 test_row = yield from self.rd_csr(start_addr+row)
310 assert row_word == test_row
311
312
313 def config(self, gpio_str, oe, ie, puen, pden, outval, bank, check=False):
314 start, end = self._parse_gpio_arg(gpio_str)
315 # Update the shadow configuration
316 for gpio in range(start, end):
317 print(oe, ie, puen, pden, outval, bank)
318 self.shadow_csr[gpio].set(oe, ie, puen, pden, outval, bank)
319
320 yield from self.wr_all()
321
322 # Set/Clear the output bit for single or group of GPIOs
323 def set_out(self, gpio_str, outval):
324 start, end = self._parse_gpio_arg(gpio_str)
325 for gpio in range(start, end):
326 self.shadow_csr[gpio].set_out(outval)
327
328 if start == end:
329 print("Setting GPIO{0} output to {1}".format(start, outval))
330 else:
331 print("Setting GPIOs {0}-{1} output to {2}"
332 .format(start, end-1, outval))
333
334 yield from self.wr_all()
335 """
336 # Not used normally - only for debug
337 def reg_write(dut, gpio, reg_val):
338 print("Configuring CSR to {0:x}".format(reg_val))
339 yield from wb_write(dut.bus, gpio, reg_val)
340
341 # TODO: There's probably a cleaner way to clear the bit...
342 def gpio_set_in_pad(dut, gpio, in_val):
343 old_in_val = yield dut.gpio_i
344 if in_val:
345 new_in_val = old_in_val | (in_val << gpio)
346 else:
347 temp = (old_in_val >> gpio) & 1
348 if temp:
349 mask = ~(1 << gpio)
350 new_in_val = old_in_val & mask
351 else:
352 new_in_val = old_in_val
353 print("Previous GPIO i: {0:b} | New GPIO i: {1:b}"
354 .format(old_in_val, new_in_val))
355 yield dut.gpio_i.eq(new_in_val)
356 yield # Allow one clk cycle to propagate
357 """
358
359 def sim_gpio(dut, use_random=True):
360 #print(dut)
361 #print(dir(dut.gpio_ports))
362 #print(len(dut.gpio_ports))
363
364 gpios = GPIOManager(dut, csrbus_layout)
365 gpios.print_info()
366 # TODO: not working yet
367 #test_pattern = []
368 #for i in range(0, (num_gpios * 2)):
369 # test_pattern.append(randint(0,1))
370 #yield from gpio_test_in_pattern(dut, test_pattern)
371
372 #yield from gpio_config(dut, start_gpio, oe, ie, puen, pden, outval, bank, end_gpio, check=False, wordsize=4)
373 #reg_val = 0xC56271A2
374 #reg_val = 0xFFFFFFFF
375 #yield from reg_write(dut, 0, reg_val)
376 #yield from reg_write(dut, 0, reg_val)
377 #yield
378
379 #csr_val = yield from wb_read(dut.bus, 0)
380 #print("CSR Val: {0:x}".format(csr_val))
381 print("Finished the simple GPIO block test!")
382
383 def test_gpio():
384 num_gpio = 8
385 wordsize = 4 # Number of bytes in the WB data word
386 dut = SimpleGPIO(wordsize, num_gpio)
387 vl = rtlil.convert(dut, ports=dut.ports())
388 with open("test_gpio.il", "w") as f:
389 f.write(vl)
390
391 m = Module()
392 m.submodules.xics_icp = dut
393
394 sim = Simulator(m)
395 sim.add_clock(1e-6)
396
397 #sim.add_sync_process(wrap(sim_gpio(dut, use_random=False)))
398 sim.add_sync_process(wrap(test_gpioman(dut)))
399 sim_writer = sim.write_vcd('test_gpio.vcd')
400 with sim_writer:
401 sim.run()
402
403 def test_gpioman(dut):
404 gpios = GPIOManager(dut, csrbus_layout)
405 gpios.print_info()
406 #gpios._parse_gpio_arg("all")
407 #gpios._parse_gpio_arg("0")
408 #gpios._parse_gpio_arg("1-3")
409 #gpios._parse_gpio_arg("20")
410
411 oe = 1
412 ie = 0
413 puen = 0
414 pden = 1
415 outval = 0
416 bank = 3
417 yield from gpios.config("0-3", oe=1, ie=0, puen=0, pden=1, outval=0, bank=2)
418 ie = 1
419 yield from gpios.config("4-7", oe=0, ie=1, puen=0, pden=1, outval=0, bank=2)
420 yield from gpios.set_out("0-3", outval=1)
421
422
423 if __name__ == '__main__':
424 test_gpio()
425