1 """Simple GPIO peripheral on wishbone
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
6 Modified for use with pinmux, will probably change the class name later.
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
19 from nmigen
.sim
.cxxsim
import Simulator
, Settle
21 from nmigen
.sim
import Simulator
, Settle
23 # Layout of 8-bit configuration word:
24 # bank_select[2:0] i/o | pden puen ien oe
31 NUMBANKBITS
= 3 # only supporting 8 banks (0-7)
34 WORDSIZE
= 4 # in bytes
36 class SimpleGPIO(Elaboratable
):
38 def __init__(self
, n_gpio
=16):
45 self
.bus
= Record(make_wb_layout(spec
), name
="gpio_wb")
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
)
55 csrbus_layout
= (("oe", 1),
60 ("bank_sel", NUMBANKBITS
)
62 # self.csrbus = Record(csrbus_layout)
64 print("CSRBUS layout: ", csrbus_layout
)
66 # create array - probably a cleaner way to do this...
68 for i
in range(0, WORDSIZE
):
69 temp_str
= "word{}".format(i
)
70 temp
.append(Record(name
=temp_str
, layout
=csrbus_layout
))
71 self
.multicsrbus
= Array(temp
)
74 #gpio_layout = (("oe", 1),
79 # ("bank_sel", NUMBANKBITS),
83 #self.gpios = Array([Record(gpio_layout) for _ in range(n_gpio)])
85 def elaborate(self
, platform
):
87 comb
, sync
= m
.d
.comb
, m
.d
.sync
90 wb_rd_data
= bus
.dat_r
91 wb_wr_data
= bus
.dat_w
94 bank_sel
= self
.bank_sel
96 gpio_oe
= self
.gpio_oe
98 gpio_ie
= self
.gpio_ie
101 # csrbus = self.csrbus
102 multi
= self
.multicsrbus
107 gpio_addr
= Signal(log2_int(self
.n_gpio
))
108 gpio_o_list
= Array(list(gpio_o
))
111 gpio_oe_list
= Array(list(gpio_oe
))
112 gpio_i_list
= Array(list(gpio_i
))
113 gpio_ie_list
= Array(list(gpio_ie
))
114 pden_list
= Array(list(pden
))
115 puen_list
= Array(list(puen
))
117 # Flag for indicating rd/wr transactions
118 new_transaction
= Signal(1)
121 #print("gpio_addr: ", type(gpio_addr))
122 #print("gpio_o_list: ", type(gpio_o_list))
123 #print("bank_sel: ", type(bank_sel))
125 # One address used to configure CSR, set output, read input
126 with m
.If(bus
.cyc
& bus
.stb
):
127 comb
+= wb_ack
.eq(1) # always ack
128 sync
+= gpio_addr
.eq(bus
.adr
)
129 sync
+= new_transaction
.eq(1)
130 with m
.If(bus
.we
): # write
132 for byte
in range(0, WORDSIZE
):
133 sync
+= multi
[byte
].eq(wb_wr_data
[byte
*8:8+byte
*8])
134 with m
.Else(): # read
135 # Concatinate the GPIO configs that are on the same "row" or
138 for i
in range(0, WORDSIZE
):
139 multi_cat
.append(multi
[i
])
140 comb
+= wb_rd_data
.eq(Cat(multi_cat
))
142 sync
+= new_transaction
.eq(0)
143 # Update the state of "io" while no WB transactions
144 for byte
in range(0, WORDSIZE
):
145 with m
.If(gpio_oe_list
[gpio_addr
] & (~gpio_ie_list
[gpio_addr
])):
146 sync
+= multi
[byte
].io
.eq(gpio_o_list
[gpio_addr
+byte
])
147 with m
.If(gpio_ie_list
[gpio_addr
] & (~gpio_oe_list
[gpio_addr
])):
148 sync
+= multi
[byte
].io
.eq(gpio_i_list
[gpio_addr
+byte
])
150 sync
+= multi
[byte
].io
.eq(multi
[byte
].io
)
152 # Only update GPIOs config if a new transaction happened last cycle
153 # (read or write). Always lags from csrbus by 1 clk cycle, most
154 # sane way I could think of while using Record().
155 with m
.If(new_transaction
):
156 for byte
in range(0, WORDSIZE
):
157 sync
+= gpio_oe_list
[gpio_addr
+byte
].eq(multi
[byte
].oe
)
158 sync
+= gpio_ie_list
[gpio_addr
+byte
].eq(multi
[byte
].ie
)
159 sync
+= puen_list
[gpio_addr
+byte
].eq(multi
[byte
].puen
)
160 sync
+= pden_list
[gpio_addr
+byte
].eq(multi
[byte
].pden
)
161 # Check to prevent output being set if GPIO configured as input
162 # TODO: Is this necessary? PAD might deal with this
163 # check GPIO is in output mode and NOT input (oe high, ie low)
164 with m
.If(gpio_oe_list
[gpio_addr
] & (~gpio_ie_list
[gpio_addr
])):
165 sync
+= gpio_o_list
[gpio_addr
+byte
].eq(multi
[byte
].io
)
166 sync
+= bank_sel
[gpio_addr
+byte
].eq(multi
[byte
].bank_sel
)
170 for field
in self
.bus
.fields
.values():
177 def config_to_regval(oe
, ie
, puen
, pden
, outval
, bank_sel
):
178 csr_val
= ( (oe
<< OESHIFT
)
182 |
(outval
<< IOSHIFT
)
183 |
(bank_sel
<< BANKSHIFT
) )
184 print("Created CSR value: {1:x}".format(csr_val
))
186 return csr_val
# return the config state
189 # TODO: probably make into class (or return state in a variable)
190 def gpio_config(dut
, gpio
, oe
, ie
, puen
, pden
, outval
, bank_sel
, check
=False):
191 csr_val
= ( (oe
<< OESHIFT
)
195 |
(outval
<< IOSHIFT
)
196 |
(bank_sel
<< BANKSHIFT
) )
197 print("Configuring GPIO{0} CSR to {1:x}".format(gpio
, csr_val
))
198 yield from wb_write(dut
.bus
, gpio
, csr_val
)
199 yield # Allow one clk cycle to propagate
202 # Check the written value
203 test_csr
= yield from gpio_rd_csr(dut
, gpio
)
204 assert test_csr
== csr_val
206 return csr_val
# return the config state
208 # Not used normally - only for debug
209 def reg_write(dut
, gpio
, reg_val
):
210 print("Configuring CSR to {0:x}".format(reg_val
))
211 yield from wb_write(dut
.bus
, gpio
, reg_val
)
213 # TODO: Return the configuration states
214 def gpio_rd_csr(dut
, gpio
):
215 csr_val
= yield from wb_read(dut
.bus
, gpio
)
216 print("GPIO{0} | CSR: {1:x}".format(gpio
, csr_val
))
217 print("Output Enable: {0:b}".format((csr_val
>> OESHIFT
) & 1))
218 print("Input Enable: {0:b}".format((csr_val
>> IESHIFT
) & 1))
219 print("Pull-Up Enable: {0:b}".format((csr_val
>> PUSHIFT
) & 1))
220 print("Pull-Down Enable: {0:b}".format((csr_val
>> PDSHIFT
) & 1))
221 if ((csr_val
>> IESHIFT
) & 1):
222 print("Input: {0:b}".format((csr_val
>> IOSHIFT
) & 1))
224 print("Output: {0:b}".format((csr_val
>> IOSHIFT
) & 1))
225 bank_sel_mask
= (2**NUMBANKBITS
)-1
226 print("Bank Sel: {0:b}".format((csr_val
>> BANKSHIFT
) & bank_sel_mask
))
227 # gpio_parse_csr(csr_val)
231 def gpio_rd_input(dut
, gpio
):
232 in_val
= yield from wb_read(dut
.bus
, gpio
)
233 in_val
= (in_val
>> IOSHIFT
) & 1
234 print("GPIO{0} | Input: {1:b}".format(gpio
, in_val
))
237 def gpio_set_out(dut
, gpio
, csr_val
, output
):
238 print("Setting GPIO{0} output to {1}".format(gpio
, output
))
239 yield from wb_write(dut
.bus
, gpio
, csr_val |
(output
<<IOSHIFT
))
240 yield # Allow one clk cycle to propagate
242 # TODO: There's probably a cleaner way to clear the bit...
243 def gpio_set_in_pad(dut
, gpio
, in_val
):
244 old_in_val
= yield dut
.gpio_i
246 new_in_val
= old_in_val |
(in_val
<< gpio
)
248 temp
= (old_in_val
>> gpio
) & 1
251 new_in_val
= old_in_val
& mask
253 new_in_val
= old_in_val
254 print("Previous GPIO i: {0:b} | New GPIO i: {1:b}"
255 .format(old_in_val
, new_in_val
))
256 yield dut
.gpio_i
.eq(new_in_val
)
257 yield # Allow one clk cycle to propagate
259 def gpio_test_in_pattern(dut
, pattern
):
260 num_gpios
= len(dut
.gpio_o
)
261 print("Test pattern:")
263 for pat
in range(0, len(pattern
)):
264 for gpio
in range(0, num_gpios
):
265 yield from gpio_set_in_pad(dut
, gpio
, pattern
[pat
])
267 temp
= yield from gpio_rd_input(dut
, gpio
)
268 print("Pattern: {0}, Reading {1}".format(pattern
[pat
], temp
))
269 assert (temp
== pattern
[pat
])
271 if pat
== len(pattern
):
274 def test_gpio_single(dut
, gpio
, use_random
=True):
281 bank_sel
= randint(0, 2**NUMBANKBITS
)
282 print("Random bank_select: {0:b}".format(bank_sel
))
284 bank_sel
= 3 # not special, chose for testing
286 gpio_csr
= yield from gpio_config(dut
, gpio
, oe
, ie
, puen
, pden
, output
,
287 bank_sel
, check
=True)
290 gpio_csr
= yield from gpio_config(dut
, gpio
, oe
, ie
, puen
, pden
, output
,
291 bank_sel
, check
=True)
294 def sim_gpio(dut
, use_random
=True):
297 num_gpios
= len(dut
.gpio_o
)
299 bank_sel
= randint(0, 2**NUMBANKBITS
)
300 print("Random bank_select: {0:b}".format(bank_sel
))
302 bank_sel
= 3 # not special, chose for testing
309 gpio_csr = [0] * num_gpios
310 # Configure GPIOs for
311 for gpio in range(0, num_gpios):
312 gpio_csr[gpio] = yield from gpio_config(dut, gpio, oe, ie, puen,
313 pden, output, bank_sel)
316 for gpio in range(0, num_gpios):
317 yield from gpio_set_out(dut, gpio, gpio_csr[gpio], output)
320 for gpio in range(0, num_gpios):
321 temp_csr = yield from gpio_rd_csr(dut, gpio)
322 assert ((temp_csr>>IOSHIFT) & 1) == output
323 # Configure for input
326 for gpio in range(0, num_gpios):
327 gpio_csr[gpio] = yield from gpio_config(dut, gpio, oe, ie, puen,
328 pden, output, bank_sel)
330 temp = yield from gpio_rd_input(dut, gpio)
333 yield from gpio_set_in_pad(dut, gpio, 1)
334 temp = yield from gpio_rd_input(dut, gpio)
337 # TODO: not working yet
339 #for i in range(0, (num_gpios * 2)):
340 # test_pattern.append(randint(0,1))
341 #yield from gpio_test_in_pattern(dut, test_pattern)
344 yield from test_gpio_single(dut
, 0, use_random
=True)
346 #reg_val = 0xC56271A2
347 #reg_val = 0xFFFFFFFF
348 #yield from reg_write(dut, 0, reg_val)
349 #yield from reg_write(dut, 0, reg_val)
352 #csr_val = yield from wb_read(dut.bus, 0)
353 #print("CSR Val: {0:x}".format(csr_val))
354 print("Finished the simple GPIO block test!")
358 dut
= SimpleGPIO(num_gpio
)
359 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
360 with
open("test_gpio.il", "w") as f
:
364 m
.submodules
.xics_icp
= dut
369 sim
.add_sync_process(wrap(sim_gpio(dut
, use_random
=False)))
370 sim_writer
= sim
.write_vcd('test_gpio.vcd')
375 if __name__
== '__main__':