Skip to content

Commit d52d0b9

Browse files
committed
csr.bus.Multiplexer: fix element w_stb getting stuck.
Also, don't clear shadow; this would break e.g. reading a 64-bit CSR register through a 32-bit Wishbone bus if a code fetch happens between the halves. Instead, clear shadow enable flag driving OR-mux.
1 parent 8662815 commit d52d0b9

File tree

2 files changed

+11
-10
lines changed

2 files changed

+11
-10
lines changed

nmigen_soc/csr/bus.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -235,29 +235,30 @@ def elaborate(self, platform):
235235

236236
for elem, (elem_start, elem_end) in self._map.resources():
237237
shadow = Signal(elem.width, name="{}__shadow".format(elem.name))
238+
if elem.access.readable():
239+
shadow_en = Signal(elem_end - elem_start, name="{}__shadow_en".format(elem.name))
240+
m.d.sync += shadow_en.eq(0)
238241
if elem.access.writable():
239242
m.d.comb += elem.w_data.eq(shadow)
243+
m.d.sync += elem.w_stb.eq(0)
240244

241245
# Enumerate every address used by the register explicitly, rather than using
242246
# arithmetic comparisons, since some toolchains (e.g. Yosys) are too eager to infer
243247
# carry chains for comparisons, even with a constant. (Register sizes don't have
244248
# to be powers of 2.)
245249
with m.Switch(self.bus.addr):
246250
for chunk_offset, chunk_addr in enumerate(range(elem_start, elem_end)):
247-
with m.Case(chunk_addr):
248-
shadow_slice = shadow[chunk_offset * self.bus.data_width:
249-
(chunk_offset + 1) * self.bus.data_width]
251+
shadow_slice = shadow.word_select(chunk_offset, self.bus.data_width)
250252

253+
with m.Case(chunk_addr):
251254
if elem.access.readable():
252-
chunk_r_stb = Signal(self.bus.data_width,
253-
name="{}__r_stb_{}".format(elem.name, chunk_offset))
254-
r_data_fanin |= Mux(chunk_r_stb, shadow_slice, 0)
255+
r_data_fanin |= Mux(shadow_en[chunk_offset], shadow_slice, 0)
255256
if chunk_addr == elem_start:
256257
m.d.comb += elem.r_stb.eq(self.bus.r_stb)
257258
with m.If(self.bus.r_stb):
258259
m.d.sync += shadow.eq(elem.r_data)
259260
# Delay by 1 cycle, allowing reads to be pipelined.
260-
m.d.sync += chunk_r_stb.eq(self.bus.r_stb)
261+
m.d.sync += shadow_en.eq(self.bus.r_stb << chunk_offset)
261262

262263
if elem.access.writable():
263264
if chunk_addr == elem_end - 1:
@@ -267,9 +268,6 @@ def elaborate(self, platform):
267268
with m.If(self.bus.w_stb):
268269
m.d.sync += shadow_slice.eq(self.bus.w_data)
269270

270-
with m.Default():
271-
m.d.sync += shadow.eq(0)
272-
273271
m.d.comb += self.bus.r_data.eq(r_data_fanin)
274272

275273
return m

nmigen_soc/test/test_csr_bus.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,13 @@ def sim_test():
167167
yield bus.w_stb.eq(1)
168168
yield
169169
yield bus.w_stb.eq(0)
170+
yield bus.addr.eq(2) # change address
170171
yield
171172
self.assertEqual((yield elem_8_w.w_stb), 1)
172173
self.assertEqual((yield elem_8_w.w_data), 0x3d)
173174
self.assertEqual((yield elem_16_rw.w_stb), 0)
175+
yield
176+
self.assertEqual((yield elem_8_w.w_stb), 0)
174177

175178
yield bus.addr.eq(2)
176179
yield bus.w_data.eq(0x55)

0 commit comments

Comments
 (0)