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