f8e1f20f00d0577426b09aaf2ee17cca4caffbd3
[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, Const
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
23 else:
24 from nmigen.sim import Simulator, Settle
25
26 # Layout of 8-bit configuration word:
27 # bank[2:0] i/o | pden puen ien oe
28 NUMBANKBITS = 3 # max 3 bits, only supporting 4 banks (0-3)
29 csrbus_layout = (("oe", 1),
30 ("ie", 1),
31 ("puen", 1),
32 ("pden", 1),
33 ("io", 1),
34 ("bank", NUMBANKBITS)
35 )
36
37 gpio_layout = (("i", 1),
38 ("oe", 1),
39 ("o", 1),
40 ("puen", 1),
41 ("pden", 1),
42 ("bank", NUMBANKBITS)
43 )
44
45 class SimpleGPIO(Elaboratable):
46
47 def __init__(self, wordsize=4, n_gpio=16):
48 self.wordsize = wordsize
49 self.n_gpio = n_gpio
50 self.n_rows = ceil(self.n_gpio / self.wordsize)
51 print("SimpleGPIO: WB Data # of bytes: {0}, #GPIOs: {1}, Rows: {2}"
52 .format(self.wordsize, self.n_gpio, self.n_rows))
53 class Spec: pass
54 spec = Spec()
55 spec.addr_wid = 30
56 spec.mask_wid = 4
57 spec.reg_wid = wordsize*8 # 32
58 self.bus = Record(make_wb_layout(spec), name="gpio_wb")
59
60 temp = []
61 for i in range(self.n_gpio):
62 name = "gpio{}".format(i)
63 temp.append(Record(name=name, layout=gpio_layout))
64 self.gpio_ports = Array(temp)
65
66 def elaborate(self, platform):
67 m = Module()
68 comb, sync = m.d.comb, m.d.sync
69
70 bus = self.bus
71 wb_rd_data = bus.dat_r
72 wb_wr_data = bus.dat_w
73 wb_ack = bus.ack
74
75 gpio_ports = self.gpio_ports
76
77 # MultiCSR read and write buses
78 rd_multi = []
79 for i in range(self.wordsize):
80 name = "rd_word%d" % i
81 rd_multi.append(Record(name=name, layout=csrbus_layout))
82
83 wr_multi = []
84 for i in range(self.wordsize):
85 name = "wr_word%d" % i
86 wr_multi.append(Record(name=name, layout=csrbus_layout))
87
88 # Connecting intermediate signals to the WB data buses
89 # allows the use of Records/Layouts
90 # Split the WB data into bytes for use with individual GPIOs
91 comb += Cat(*wr_multi).eq(wb_wr_data)
92 # Connect GPIO config bytes to form a single word
93 comb += wb_rd_data.eq(Cat(*rd_multi))
94 for i in range(len(bus.sel)):
95 sync += rd_multi[i].eq(0)
96
97 # One address used to configure CSR, set output, read input
98 with m.If(bus.cyc & bus.stb):
99 with m.If(bus.we): # write
100 # Update the GPIO configs with sent parameters
101 for i in range(len(bus.sel)):
102 GPIO_num = Signal(16) # fixed for now
103 comb += GPIO_num.eq(bus.adr*len(bus.sel)+i)
104 with m.If(bus.sel[i]):
105 sync += gpio_ports[GPIO_num].oe.eq(wr_multi[i].oe)
106 sync += gpio_ports[GPIO_num].puen.eq(wr_multi[i].puen)
107 sync += gpio_ports[GPIO_num].pden.eq(wr_multi[i].pden)
108 with m.If (wr_multi[i].oe):
109 sync += gpio_ports[GPIO_num].o.eq(wr_multi[i].io)
110 with m.Else():
111 sync += gpio_ports[GPIO_num].o.eq(0)
112 sync += gpio_ports[GPIO_num].bank.eq(wr_multi[i].bank)
113 with m.Else(): # read
114 # Update the read multi bus with current GPIO configs
115 # not ack'ing as we need to wait 1 clk cycle before data ready
116 for i in range(len(bus.sel)):
117 GPIO_num = Signal(16) # fixed for now
118 comb += GPIO_num.eq(bus.adr*len(bus.sel)+i)
119 with m.If(bus.sel[i]):
120 sync += rd_multi[i].oe.eq(gpio_ports[GPIO_num].oe)
121 sync += rd_multi[i].ie.eq(~gpio_ports[GPIO_num].oe)
122 sync += rd_multi[i].puen.eq(gpio_ports[GPIO_num].puen)
123 sync += rd_multi[i].pden.eq(gpio_ports[GPIO_num].pden)
124 with m.If (gpio_ports[GPIO_num].oe):
125 sync += rd_multi[i].io.eq(gpio_ports[GPIO_num].o)
126 with m.Else():
127 sync += rd_multi[i].io.eq(gpio_ports[GPIO_num].i)
128 sync += rd_multi[i].bank.eq(gpio_ports[GPIO_num].bank)
129 sync += wb_ack.eq(1) # ack after latching data
130 with m.Else():
131 sync += wb_ack.eq(0)
132
133 return m
134
135 def __iter__(self):
136 for field in self.bus.fields.values():
137 yield field
138 for gpio in range(len(self.gpio_ports)):
139 for field in self.gpio_ports[gpio].fields.values():
140 yield field
141
142 def ports(self):
143 return list(self)
144
145 """
146 def gpio_test_in_pattern(dut, pattern):
147 num_gpios = len(dut.gpio_ports)
148 print("Test pattern:")
149 print(pattern)
150 for pat in range(0, len(pattern)):
151 for gpio in range(0, num_gpios):
152 yield gpio_set_in_pad(dut, gpio, pattern[pat])
153 yield
154 temp = yield from gpio_rd_input(dut, gpio)
155 print("Pattern: {0}, Reading {1}".format(pattern[pat], temp))
156 assert (temp == pattern[pat])
157 pat += 1
158 if pat == len(pattern):
159 break
160 """
161
162 def test_gpio_single(dut, gpio, use_random=True):
163 oe = 1
164 ie = 0
165 output = 0
166 puen = 0
167 pden = 0
168 if use_random:
169 bank = randint(0, (2**NUMBANKBITS)-1)
170 print("Random bank select: {0:b}".format(bank))
171 else:
172 bank = 3 # not special, chose for testing
173
174 gpio_csr = yield from gpio_config(dut, gpio, oe, ie, puen, pden, output,
175 bank, check=True)
176 # Enable output
177 output = 1
178 gpio_csr = yield from gpio_config(dut, gpio, oe, ie, puen, pden, output,
179 bank, check=True)
180
181 # Shadow reg container class
182 class GPIOConfigReg():
183 def __init__(self, shift_dict):
184 self.shift_dict = shift_dict
185 self.oe=0
186 self.ie=1 # By default gpio set as input
187 self.puen=0
188 self.pden=0
189 self.io=0
190 self.bank=0
191 self.packed=0
192
193 def set(self, oe=0, ie=0, puen=0, pden=0, io=0, bank=0):
194 self.oe=oe
195 self.ie=ie
196 self.puen=puen
197 self.pden=pden
198 self.io=io
199 self.bank=bank
200 self.pack() # Produce packed byte for sending
201
202 def set_out(self, outval):
203 self.io=outval
204 self.pack() # Produce packed byte for sending
205
206 # Take config parameters of specified GPIOs, and combine them to produce
207 # bytes for sending via WB bus
208 def pack(self):
209 self.packed = ((self.oe << self.shift_dict['oe'])
210 | (self.ie << self.shift_dict['ie'])
211 | (self.puen << self.shift_dict['puen'])
212 | (self.pden << self.shift_dict['pden'])
213 | (self.io << self.shift_dict['io'])
214 | (self.bank << self.shift_dict['bank']))
215
216 #print("GPIO Packed CSR: {0:x}".format(self.packed))
217
218 # Object for storing each gpio's config state
219
220 class GPIOManager():
221 def __init__(self, dut, layout, wb_bus):
222 self.dut = dut
223 self.wb_bus = wb_bus
224 # arrangement of config bits making up csr word
225 self.csr_layout = layout
226 self.shift_dict = self._create_shift_dict()
227 self.n_gpios = len(self.dut.gpio_ports)
228 print(dir(self.dut))
229 # Get the number of bits of the WB sel signal
230 # indicates the number of gpios per address
231 self.n_gp_per_adr = len(self.dut.bus.sel)
232 # Shows if data is byte/half-word/word/qword addressable?
233 self.granuality = len(self.dut.bus.dat_w) // self.n_gp_per_adr
234 self.n_rows = ceil(self.n_gpios / self.n_gp_per_adr)
235 self.shadow_csr = []
236 for i in range(self.n_gpios):
237 self.shadow_csr.append(GPIOConfigReg(self.shift_dict))
238
239 def print_info(self):
240 print("----------")
241 print("GPIO Block Info:")
242 print("Number of GPIOs: %d" % self.n_gpios)
243 print("GPIOs per WB data word: %d" % self.n_gp_per_adr)
244 print("WB data granuality: %d" % self.granuality)
245 print("Number of address rows: %d" % self.n_rows)
246 print("----------")
247
248 # The shifting of control bits in the configuration word is dependent on the
249 # defined layout. To prevent maintaining the shift constants in a separate
250 # location, the same layout is used to generate a dictionary of bit shifts
251 # with which the configuration word can be produced!
252 def _create_shift_dict(self):
253 shift = 0
254 shift_dict = {}
255 for i in range(0, len(self.csr_layout)):
256 shift_dict[self.csr_layout[i][0]] = shift
257 shift += self.csr_layout[i][1]
258 print(shift_dict)
259 return shift_dict
260
261 def _parse_gpio_arg(self, gpio_str):
262 # TODO: No input checking!
263 print("Given GPIO/range string: {}".format(gpio_str))
264 if gpio_str == "all":
265 start = 0
266 end = self.n_gpios
267 elif '-' in gpio_str:
268 start, end = gpio_str.split('-')
269 start = int(start)
270 end = int(end) + 1
271 if (end < start) or (end > self.n_gpios):
272 raise Exception("Second GPIO must be higher than first and"
273 + " must be lower or equal to last available GPIO.")
274 else:
275 start = int(gpio_str)
276 if start >= self.n_gpios:
277 raise Exception("GPIO must be less/equal to last GPIO.")
278 end = start + 1
279 print("Parsed GPIOs {0} until {1}".format(start, end))
280 return start, end
281
282 # Take a combined word and update shadow reg's
283 # TODO: convert hard-coded sizes to use the csrbus_layout (or dict?)
284 def update_single_shadow(self, csr_byte, gpio):
285 oe = (csr_byte >> self.shift_dict['oe']) & 0x1
286 ie = (csr_byte >> self.shift_dict['ie']) & 0x1
287 puen = (csr_byte >> self.shift_dict['puen']) & 0x1
288 pden = (csr_byte >> self.shift_dict['pden']) & 0x1
289 io = (csr_byte >> self.shift_dict['io']) & 0x1
290 bank = (csr_byte >> self.shift_dict['bank']) & 0x3
291
292 print("csr={0:x} | oe={1}, ie={2}, puen={3}, pden={4}, io={5}, bank={6}"
293 .format(csr_byte, oe, ie, puen, pden, io, bank))
294
295 self.shadow_csr[gpio].set(oe, ie, puen, pden, io, bank)
296 return oe, ie, puen, pden, io, bank
297
298 # Update multiple configuration registers
299 def wr(self, gp_start, gp_end, check=False):
300 # Some maths to determine how many transactions, and at which
301 # address to start transmitting
302 n_gp_config = gp_end - gp_start
303 adr_start = gp_start // self.n_gp_per_adr
304 n_adr = ceil(n_gp_config / self.n_gp_per_adr)
305
306 curr_gpio = gp_start
307 # cycle through addresses, each iteration is a WB tx
308 for adr in range(adr_start, adr_start + n_adr):
309 tx_sel = 0
310 tx_word = 0
311 # cycle through every WB sel bit, and add configs of
312 # corresponding gpios
313 for i in range(0, self.n_gp_per_adr):
314 # if current gpio's location in the WB data word matches sel bit
315 if (curr_gpio % self.n_gp_per_adr) == i:
316 print("gpio%d" % curr_gpio)
317 tx_sel += 1 << i
318 tx_word += (self.shadow_csr[curr_gpio].packed
319 << (self.granuality * i))
320 curr_gpio += 1
321 # stop if we processed all required gpios
322 if curr_gpio >= gp_end:
323 break
324 print("Adr: %x | Sel: %x | TX Word: %x" % (adr, tx_sel, tx_word))
325 yield from wb_write(self.wb_bus, adr, tx_word, tx_sel)
326 yield # Allow one clk cycle to propagate
327
328 if(check):
329 row_word = yield from wb_read(self.wb_bus, adr, tx_sel)
330 assert config_word == read_word
331
332 def rd(self, gp_start, gp_end):
333 # Some maths to determine how many transactions, and at which
334 # address to start transmitting
335 n_gp_config = gp_end - gp_start
336 adr_start = gp_start // self.n_gp_per_adr
337 n_adr = ceil(n_gp_config / self.n_gp_per_adr)
338
339 curr_gpio = gp_start
340 # cycle through addresses, each iteration is a WB tx
341 for adr in range(adr_start, adr_start + n_adr):
342 tx_sel = 0
343 # cycle through every WB sel bit, and add configs of
344 # corresponding gpios
345 for i in range(0, self.n_gp_per_adr):
346 # if current gpio's location in the WB data word matches sel bit
347 if (curr_gpio % self.n_gp_per_adr) == i:
348 print("gpio%d" % curr_gpio)
349 tx_sel += 1 << i
350 curr_gpio += 1
351 # stop if we processed all required gpios
352 if curr_gpio >= gp_end:
353 break
354 print("Adr: %x | Sel: %x " % (adr, tx_sel))
355 row_word = yield from wb_read(self.wb_bus, adr, tx_sel)
356
357 mask = (2**self.granuality) - 1
358 for i in range(self.n_gp_per_adr):
359 if ((tx_sel >> i) & 1) == 1:
360 single_csr = (row_word >> (i*self.granuality)) & mask
361 curr_gpio = adr*self.n_gp_per_adr + i
362 #print("rd gpio%d" % curr_gpio)
363 self.update_single_shadow(single_csr, curr_gpio)
364
365 # Write all shadow registers to GPIO block
366 def wr_all(self, check=False):
367 for row in range(0, self.n_rows):
368 yield from self.wr(0, self.n_gpios, check)
369
370 # Read all GPIO block row addresses and update shadow reg's
371 def rd_all(self, check=False):
372 for row in range(0, self.n_rows):
373 yield from self.rd(0, self.n_gpios)
374
375 def config(self, gpio_str, oe, ie, puen, pden, outval, bank, check=False):
376 start, end = self._parse_gpio_arg(gpio_str)
377 # Update the shadow configuration
378 for gpio in range(start, end):
379 # print(oe, ie, puen, pden, outval, bank)
380 self.shadow_csr[gpio].set(oe, ie, puen, pden, outval, bank)
381 # TODO: only update the required rows?
382 #yield from self.wr_all()
383 yield from self.wr(start, end)
384
385 # Set/Clear the output bit for single or group of GPIOs
386 def set_out(self, gpio_str, outval):
387 start, end = self._parse_gpio_arg(gpio_str)
388 for gpio in range(start, end):
389 self.shadow_csr[gpio].set_out(outval)
390
391 if start == end:
392 print("Setting GPIO{0} output to {1}".format(start, outval))
393 else:
394 print("Setting GPIOs {0}-{1} output to {2}"
395 .format(start, end-1, outval))
396
397 yield from self.wr(start, end)
398
399 def rd_input(self, gpio_str):
400 start, end = self._parse_gpio_arg(gpio_str)
401 #read_data = [0] * self.n_rows
402 #for row in range(0, self.n_rows):
403 # read_data[row] = yield from self.rd_row(row)
404 yield from self.rd(start, end)
405
406 num_to_read = (end - start)
407 read_in = [0] * num_to_read
408 curr_gpio = 0
409 for i in range(0, num_to_read):
410 read_in[i] = self.shadow_csr[curr_gpio].io
411 curr_gpio += 1
412
413 print("GPIOs %d until %d, i=%s".format(start, end, read_in))
414 return read_in
415
416 # TODO: There's probably a cleaner way to clear the bit...
417 def sim_set_in_pad(self, gpio_str, in_val):
418 start, end = self._parse_gpio_arg(gpio_str)
419 for gpio in range(start, end):
420 old_in_val = yield self.dut.gpio_ports[gpio].i
421 print(old_in_val)
422 print("GPIO{0} Previous i: {1:b} | New i: {2:b}"
423 .format(gpio, old_in_val, in_val))
424 yield self.dut.gpio_ports[gpio].i.eq(in_val)
425 yield # Allow one clk cycle to propagate
426
427 def rd_shadow(self):
428 shadow_csr = [0] * self.n_gpios
429 for gpio in range(0, self.n_gpios):
430 shadow_csr[gpio] = self.shadow_csr[gpio].packed
431
432 hex_str = ""
433 for reg in shadow_csr:
434 hex_str += " "+hex(reg)
435 print("Shadow reg's: ", hex_str)
436
437 return shadow_csr
438
439
440 def sim_gpio(dut, use_random=True):
441 #print(dut)
442 #print(dir(dut.gpio_ports))
443 #print(len(dut.gpio_ports))
444
445 gpios = GPIOManager(dut, csrbus_layout)
446 gpios.print_info()
447 # TODO: not working yet
448 #test_pattern = []
449 #for i in range(0, (num_gpios * 2)):
450 # test_pattern.append(randint(0,1))
451 #yield from gpio_test_in_pattern(dut, test_pattern)
452
453 #yield from gpio_config(dut, start_gpio, oe, ie, puen, pden, outval, bank, end_gpio, check=False, wordsize=4)
454 #reg_val = 0xC56271A2
455 #reg_val = 0xFFFFFFFF
456 #yield from reg_write(dut, 0, reg_val)
457 #yield from reg_write(dut, 0, reg_val)
458 #yield
459
460 #csr_val = yield from wb_read(dut.bus, 0)
461 #print("CSR Val: {0:x}".format(csr_val))
462 print("Finished the simple GPIO block test!")
463
464 def gen_gtkw_doc(n_gpios, wordsize, filename):
465 # GTKWave doc generation
466 wb_data_width = wordsize*8
467 n_rows = ceil(n_gpios/wordsize)
468 style = {
469 '': {'base': 'hex'},
470 'in': {'color': 'orange'},
471 'out': {'color': 'yellow'},
472 'debug': {'module': 'top', 'color': 'red'}
473 }
474
475 # Create a trace list, each block expected to be a tuple()
476 traces = []
477 wb_traces = ('Wishbone Bus', [
478 ('gpio_wb__cyc', 'in'),
479 ('gpio_wb__stb', 'in'),
480 ('gpio_wb__we', 'in'),
481 ('gpio_wb__adr[27:0]', 'in'),
482 ('gpio_wb__sel[3:0]', 'in'),
483 ('gpio_wb__dat_w[{}:0]'.format(wb_data_width-1), 'in'),
484 ('gpio_wb__dat_r[{}:0]'.format(wb_data_width-1), 'out'),
485 ('gpio_wb__ack', 'out'),
486 ])
487 traces.append(wb_traces)
488
489 gpio_internal_traces = ('Internal', [
490 ('clk', 'in'),
491 ('new_transaction'),
492 ('rst', 'in')
493 ])
494 traces.append(gpio_internal_traces)
495
496 traces.append({'comment': 'Multi-byte GPIO config read bus'})
497 for word in range(0, wordsize):
498 prefix = "rd_word{}__".format(word)
499 single_word = []
500 word_signals = []
501 single_word.append('Word{}'.format(word))
502 word_signals.append((prefix+'bank[{}:0]'.format(NUMBANKBITS-1)))
503 word_signals.append((prefix+'ie'))
504 word_signals.append((prefix+'io'))
505 word_signals.append((prefix+'oe'))
506 word_signals.append((prefix+'pden'))
507 word_signals.append((prefix+'puen'))
508 single_word.append(word_signals)
509 traces.append(tuple(single_word))
510
511 traces.append({'comment': 'Multi-byte GPIO config write bus'})
512 for word in range(0, wordsize):
513 prefix = "wr_word{}__".format(word)
514 single_word = []
515 word_signals = []
516 single_word.append('Word{}'.format(word))
517 word_signals.append((prefix+'bank[{}:0]'.format(NUMBANKBITS-1)))
518 word_signals.append((prefix+'ie'))
519 word_signals.append((prefix+'io'))
520 word_signals.append((prefix+'oe'))
521 word_signals.append((prefix+'pden'))
522 word_signals.append((prefix+'puen'))
523 single_word.append(word_signals)
524 traces.append(tuple(single_word))
525
526 for gpio in range(0, n_gpios):
527 prefix = "gpio{}__".format(gpio)
528 single_gpio = []
529 gpio_signals = []
530 single_gpio.append('GPIO{} Port'.format(gpio))
531 gpio_signals.append((prefix+'bank[{}:0]'.format(NUMBANKBITS-1), 'out'))
532 gpio_signals.append( (prefix+'i', 'in') )
533 gpio_signals.append( (prefix+'o', 'out') )
534 gpio_signals.append( (prefix+'oe', 'out') )
535 gpio_signals.append( (prefix+'pden', 'out') )
536 gpio_signals.append( (prefix+'puen', 'out') )
537 single_gpio.append(gpio_signals)
538 traces.append(tuple(single_gpio))
539
540 #print(traces)
541
542 #module = "top.xics_icp"
543 module = "bench.top.xics_icp"
544 write_gtkw(filename+".gtkw", filename+".vcd", traces, style,
545 module=module)
546
547 def test_gpio():
548 filename = "test_gpio" # Doesn't include extension
549 n_gpios = 8
550 wordsize = 4 # Number of bytes in the WB data word
551 dut = SimpleGPIO(wordsize, n_gpios)
552 vl = rtlil.convert(dut, ports=dut.ports())
553 with open(filename+".il", "w") as f:
554 f.write(vl)
555
556 m = Module()
557 m.submodules.xics_icp = dut
558
559 sim = Simulator(m)
560 sim.add_clock(1e-6)
561
562 #sim.add_sync_process(wrap(sim_gpio(dut, use_random=False)))
563 sim.add_sync_process(wrap(test_gpioman(dut)))
564 sim_writer = sim.write_vcd(filename+".vcd")
565 with sim_writer:
566 sim.run()
567
568 gen_gtkw_doc(n_gpios, wordsize, filename)
569
570 def test_gpioman(dut):
571 print("------START----------------------")
572 gpios = GPIOManager(dut, csrbus_layout, dut.bus)
573 gpios.print_info()
574 #gpios._parse_gpio_arg("all")
575 #gpios._parse_gpio_arg("0")
576 #gpios._parse_gpio_arg("1-3")
577 #gpios._parse_gpio_arg("20")
578
579 oe = 1
580 ie = 0
581 puen = 0
582 pden = 1
583 outval = 0
584 bank = 3
585 yield from gpios.config("0-1", oe=1, ie=0, puen=0, pden=1, outval=0, bank=2)
586 ie = 1
587 yield from gpios.config("5-7", oe=0, ie=1, puen=0, pden=1, outval=0, bank=6)
588 yield from gpios.set_out("0-1", outval=1)
589
590 #yield from gpios.rd_all()
591 yield from gpios.sim_set_in_pad("6-7", 1)
592 print("----------------------------")
593 yield from gpios.rd_input("4-7")
594
595 gpios.rd_shadow()
596
597 if __name__ == '__main__':
598 test_gpio()
599