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