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