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
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
= 8 # 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
)
60 ("bank_sel", NUMBANKBITS
)
62 self
.csrbus
= Record(layout
)
64 def elaborate(self
, platform
):
66 comb
, sync
= m
.d
.comb
, m
.d
.sync
69 wb_rd_data
= bus
.dat_r
70 wb_wr_data
= bus
.dat_w
73 bank_sel
= self
.bank_sel
75 gpio_oe
= self
.gpio_oe
77 gpio_ie
= self
.gpio_ie
84 gpio_addr
= Signal(log2_int(self
.n_gpio
))
85 gpio_o_list
= Array(list(gpio_o
))
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
))
94 # Flag for indicating rd/wr transactions
95 new_transaction
= Signal(1)
98 #print("gpio_addr: ", type(gpio_addr))
99 #print("gpio_o_list: ", type(gpio_o_list))
100 #print("bank_sel: ", type(bank_sel))
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
106 sync
+= gpio_addr
.eq(bus
.adr
)
107 sync
+= new_transaction
.eq(1)
108 with m
.If(bus
.we
): # write
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
)
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
])
122 sync
+= csrbus
.io
.eq(csrbus
.io
)
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
)
142 for field
in self
.bus
.fields
.values():
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
)
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
160 return csr_val
# return the config state
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
)
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))
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)
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
))
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
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
199 new_in_val
= old_in_val |
(in_val
<< gpio
)
201 temp
= (old_in_val
>> gpio
) & 1
204 new_in_val
= old_in_val
& mask
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
212 def gpio_test_in_pattern(dut
, pattern
):
213 num_gpios
= len(dut
.gpio_o
)
214 print("Test 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
])
220 temp
= yield from gpio_rd_input(dut
, gpio
)
221 print("Pattern: {0}, Reading {1}".format(pattern
[pat
], temp
))
222 assert (temp
== pattern
[pat
])
224 if pat
== len(pattern
):
228 def sim_gpio(dut
, use_random
=True):
231 num_gpios
= len(dut
.gpio_o
)
233 bank_sel
= randint(0, 2**NUMBANKBITS
)
234 print("Random bank_select: {0:b}".format(bank_sel
))
236 bank_sel
= 3 # not special, chose for testing
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
)
249 for gpio
in range(0, num_gpios
):
250 yield from gpio_set_out(dut
, gpio
, gpio_csr
[gpio
], output
)
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
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
)
263 temp
= yield from gpio_rd_input(dut
, gpio
)
266 yield from gpio_set_in_pad(dut
, gpio
, 1)
267 temp
= yield from gpio_rd_input(dut
, gpio
)
270 # TODO: not working yet
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)
277 #yield from reg_write(dut, 0, reg_val)
278 #yield from reg_write(dut, 0, reg_val)
280 print("Finished the simple GPIO block test!")
284 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
285 with
open("test_gpio.il", "w") as f
:
289 m
.submodules
.xics_icp
= dut
294 sim
.add_sync_process(wrap(sim_gpio(dut
, use_random
=False)))
295 sim_writer
= sim
.write_vcd('test_gpio.vcd')
300 if __name__
== '__main__':