csr.bus.Multiplexer: fix element w_stb getting stuck.
authorwhitequark <whitequark@whitequark.org>
Sat, 26 Oct 2019 00:58:27 +0000 (00:58 +0000)
committerwhitequark <whitequark@whitequark.org>
Sat, 26 Oct 2019 01:03:59 +0000 (01:03 +0000)
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.

nmigen_soc/csr/bus.py
nmigen_soc/test/test_csr_bus.py

index 3e3e992d920223137896d13f87b106cc79098982..ffb8c3b99fc2d6235cfab79e32c8034af502a981 100644 (file)
@@ -235,8 +235,12 @@ class Multiplexer(Elaboratable):
 
         for elem, (elem_start, elem_end) in self._map.resources():
             shadow = Signal(elem.width, name="{}__shadow".format(elem.name))
+            if elem.access.readable():
+                shadow_en = Signal(elem_end - elem_start, name="{}__shadow_en".format(elem.name))
+                m.d.sync += shadow_en.eq(0)
             if elem.access.writable():
                 m.d.comb += elem.w_data.eq(shadow)
+                m.d.sync += elem.w_stb.eq(0)
 
             # Enumerate every address used by the register explicitly, rather than using
             # arithmetic comparisons, since some toolchains (e.g. Yosys) are too eager to infer
@@ -244,20 +248,17 @@ class Multiplexer(Elaboratable):
             # to be powers of 2.)
             with m.Switch(self.bus.addr):
                 for chunk_offset, chunk_addr in enumerate(range(elem_start, elem_end)):
-                    with m.Case(chunk_addr):
-                        shadow_slice = shadow[chunk_offset * self.bus.data_width:
-                                              (chunk_offset + 1) * self.bus.data_width]
+                    shadow_slice = shadow.word_select(chunk_offset, self.bus.data_width)
 
+                    with m.Case(chunk_addr):
                         if elem.access.readable():
-                            chunk_r_stb = Signal(self.bus.data_width,
-                                name="{}__r_stb_{}".format(elem.name, chunk_offset))
-                            r_data_fanin |= Mux(chunk_r_stb, shadow_slice, 0)
+                            r_data_fanin |= Mux(shadow_en[chunk_offset], shadow_slice, 0)
                             if chunk_addr == elem_start:
                                 m.d.comb += elem.r_stb.eq(self.bus.r_stb)
                                 with m.If(self.bus.r_stb):
                                     m.d.sync += shadow.eq(elem.r_data)
                             # Delay by 1 cycle, allowing reads to be pipelined.
-                            m.d.sync += chunk_r_stb.eq(self.bus.r_stb)
+                            m.d.sync += shadow_en.eq(self.bus.r_stb << chunk_offset)
 
                         if elem.access.writable():
                             if chunk_addr == elem_end - 1:
@@ -267,9 +268,6 @@ class Multiplexer(Elaboratable):
                             with m.If(self.bus.w_stb):
                                 m.d.sync += shadow_slice.eq(self.bus.w_data)
 
-                with m.Default():
-                    m.d.sync += shadow.eq(0)
-
         m.d.comb += self.bus.r_data.eq(r_data_fanin)
 
         return m
index d2ce624113256c0d56208e850b9927356c60c1bf..d88912868074148434e135e4f75292394c331007 100644 (file)
@@ -167,10 +167,13 @@ class MultiplexerTestCase(unittest.TestCase):
             yield bus.w_stb.eq(1)
             yield
             yield bus.w_stb.eq(0)
+            yield bus.addr.eq(2) # change address
             yield
             self.assertEqual((yield elem_8_w.w_stb), 1)
             self.assertEqual((yield elem_8_w.w_data), 0x3d)
             self.assertEqual((yield elem_16_rw.w_stb), 0)
+            yield
+            self.assertEqual((yield elem_8_w.w_stb), 0)
 
             yield bus.addr.eq(2)
             yield bus.w_data.eq(0x55)