1f42dee88dc385fe8395a2de115dc17991dc3698
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 self
.multicsrbus
= Array([Record(csrbus_layout
), Record(csrbus_layout
), Record(csrbus_layout
), Record(csrbus_layout
)])
69 #gpio_layout = (("oe", 1),
74 # ("bank_sel", NUMBANKBITS),
78 #self.gpios = Array([Record(gpio_layout) for _ in range(n_gpio)])
80 def elaborate(self
, platform
):
82 comb
, sync
= m
.d
.comb
, m
.d
.sync
85 wb_rd_data
= bus
.dat_r
86 wb_wr_data
= bus
.dat_w
89 bank_sel
= self
.bank_sel
91 gpio_oe
= self
.gpio_oe
93 gpio_ie
= self
.gpio_ie
96 # csrbus = self.csrbus
97 multi
= self
.multicsrbus
102 gpio_addr
= Signal(log2_int(self
.n_gpio
))
103 gpio_o_list
= Array(list(gpio_o
))
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
))
112 # Flag for indicating rd/wr transactions
113 new_transaction
= Signal(1)
116 #print("gpio_addr: ", type(gpio_addr))
117 #print("gpio_o_list: ", type(gpio_o_list))
118 #print("bank_sel: ", type(bank_sel))
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
124 sync
+= gpio_addr
.eq(bus
.adr
)
125 sync
+= new_transaction
.eq(1)
126 with m
.If(bus
.we
): # write
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
134 for i
in range(0, WORDSIZE
):
135 multi_cat
.append(multi
[i
])
136 comb
+= wb_rd_data
.eq(Cat(multi_cat
))
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
])
146 sync
+= multi
[byte
].io
.eq(multi
[byte
].io
)
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
)
166 for field
in self
.bus
.fields
.values():
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
)
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
186 # Check the written value
187 test_csr
= yield from gpio_rd_csr(dut
, gpio
)
188 assert temp_csr
== csi_val
190 return csr_val
# return the config state
192 def gpio_create_csrval(dut
, oe
, ie
, puen
, pden
, outval
, bank_sel
):
193 csr_val
= ( (oe
<< OESHIFT
)
197 |
(outval
<< IOSHIFT
)
198 |
(bank_sel
<< BANKSHIFT
) )
199 print("Created CSR value to write: {1:x}".format(csr_val
))
201 return csr_val
# return the config state
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
)
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))
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)
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
))
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
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
240 new_in_val
= old_in_val |
(in_val
<< gpio
)
242 temp
= (old_in_val
>> gpio
) & 1
245 new_in_val
= old_in_val
& mask
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
253 def gpio_test_in_pattern(dut
, pattern
):
254 num_gpios
= len(dut
.gpio_o
)
255 print("Test 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
])
261 temp
= yield from gpio_rd_input(dut
, gpio
)
262 print("Pattern: {0}, Reading {1}".format(pattern
[pat
], temp
))
263 assert (temp
== pattern
[pat
])
265 if pat
== len(pattern
):
268 def test_gpio_single(dut
, gpio
, use_random
=True):
275 bank_sel
= randint(0, 2**NUMBANKBITS
)
276 print("Random bank_select: {0:b}".format(bank_sel
))
278 bank_sel
= 3 # not special, chose for testing
280 gpio_csr
= yield from gpio_config(dut
, gpio
, oe
, ie
, puen
, pden
, output
,
281 bank_sel
, check
=True)
284 gpio_csr
= yield from gpio_config(dut
, gpio
, oe
, ie
, puen
, pden
, output
,
285 bank_sel
, check
=True)
288 def sim_gpio(dut
, use_random
=True):
291 num_gpios
= len(dut
.gpio_o
)
293 bank_sel
= randint(0, 2**NUMBANKBITS
)
294 print("Random bank_select: {0:b}".format(bank_sel
))
296 bank_sel
= 3 # not special, chose for testing
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)
310 for gpio in range(0, num_gpios):
311 yield from gpio_set_out(dut, gpio, gpio_csr[gpio], output)
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
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)
324 temp = yield from gpio_rd_input(dut, gpio)
327 yield from gpio_set_in_pad(dut, gpio, 1)
328 temp = yield from gpio_rd_input(dut, gpio)
331 # TODO: not working yet
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)
338 #reg_val = 0xFFFFFFFF
339 yield from reg_write(dut
, 0, reg_val
)
340 #yield from reg_write(dut, 0, reg_val)
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!")
349 dut
= SimpleGPIO(num_gpio
)
350 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
351 with
open("test_gpio.il", "w") as f
:
355 m
.submodules
.xics_icp
= dut
360 sim
.add_sync_process(wrap(sim_gpio(dut
, use_random
=False)))
361 sim_writer
= sim
.write_vcd('test_gpio.vcd')
366 if __name__
== '__main__':