gram.phy.ecp5ddrphy: Fix DQSBUFM's pause signal (fixes #51)
authorJean THOMAS <git0@pub.jeanthomas.me>
Thu, 6 Aug 2020 10:28:13 +0000 (12:28 +0200)
committerJean THOMAS <git0@pub.jeanthomas.me>
Thu, 6 Aug 2020 10:28:13 +0000 (12:28 +0200)
gram/phy/ecp5ddrphy.py
gram/test/test_phy_ecp5ddrphy.py [new file with mode: 0644]

index 9bbccbd8841cc4bc2f1dc2ca0a7f0deafcd67f8e..c1c2f170bb519aa7e8b820b79ff6567aae743172 100644 (file)
@@ -75,6 +75,32 @@ class ECP5DDRPHYInit(Elaboratable):
         return m
 
 
+class _DQSBUFMSettingManager(Elaboratable):
+    def __init__(self, rdly_csr):
+        self.rdly_csr = rdly_csr
+
+        self.pause = Signal()
+        self.readclksel = Signal(3)
+
+    def elaborate(self, platform):
+        m = Module()
+
+        with m.FSM():
+            with m.State("Idle"):
+                with m.If(self.rdly_csr.w_stb):
+                    m.d.sync += self.pause.eq(1)
+                    m.next = "RdlyUpdateRequested"
+
+            with m.State("RdlyUpdateRequested"):
+                m.d.sync += self.readclksel.eq(self.rdly_csr.w_data)
+                m.next = "ResetPause"
+
+            with m.State("ResetPause"):
+                m.d.sync += self.pause.eq(0)
+                m.next = "Idle"
+
+        return m
+
 
 class ECP5DDRPHY(Peripheral, Elaboratable):
     def __init__(self, pads, sys_clk_freq=100e6):
@@ -234,6 +260,9 @@ class ECP5DDRPHY(Peripheral, Elaboratable):
             datavalid_prev = Signal()
             m.d.sync += datavalid_prev.eq(datavalid)
 
+            dqsbufm_manager = _DQSBUFMSettingManager(self.rdly[i])
+            setattr(m.submodules, f"dqsbufm_manager{i}", dqsbufm_manager)
+
             m.submodules += Instance("DQSBUFM",
                 p_DQS_LI_DEL_ADJ="MINUS",
                 p_DQS_LI_DEL_VAL=1,
@@ -255,7 +284,7 @@ class ECP5DDRPHY(Peripheral, Elaboratable):
                 i_ECLK=ClockSignal("sync2x"),
                 i_RST=ResetSignal("dramsync"),
                 i_DDRDEL=init.delay,
-                i_PAUSE=init.pause | self.rdly[i].w_stb,
+                i_PAUSE=init.pause | dqsbufm_manager.pause,
 
                 # Control
                 # Assert LOADNs to use DDRDEL control
@@ -269,9 +298,9 @@ class ECP5DDRPHY(Peripheral, Elaboratable):
                 # Reads (generate shifted DQS clock for reads)
                 i_READ0=dqs_re,
                 i_READ1=dqs_re,
-                i_READCLKSEL0=self.rdly[i].w_data[0],
-                i_READCLKSEL1=self.rdly[i].w_data[1],
-                i_READCLKSEL2=self.rdly[i].w_data[2],
+                i_READCLKSEL0=dqsbufm_manager.readclksel[0],
+                i_READCLKSEL1=dqsbufm_manager.readclksel[1],
+                i_READCLKSEL2=dqsbufm_manager.readclksel[2],
                 i_DQSI=dqs_i,
                 o_DQSR90=dqsr90,
                 o_RDPNTR0=rdpntr[0],
diff --git a/gram/test/test_phy_ecp5ddrphy.py b/gram/test/test_phy_ecp5ddrphy.py
new file mode 100644 (file)
index 0000000..dc2bd36
--- /dev/null
@@ -0,0 +1,53 @@
+from nmigen import *
+
+from gram.phy.ecp5ddrphy import _DQSBUFMSettingManager
+from gram.test.utils import *
+
+class DQSBUFMSettingManagerTestCase(FHDLTestCase):
+    class MockCSR:
+        def __init__(self):
+            self.w_stb = Signal()
+            self.w_data = Signal(3)
+
+    def test_pause_timing(self):
+        csr = self.MockCSR()
+        dut = _DQSBUFMSettingManager(csr)
+
+        def process():
+            self.assertFalse((yield dut.pause))
+
+            yield csr.w_stb.eq(1)
+            yield
+            yield csr.w_stb.eq(0)
+            yield
+
+            self.assertTrue((yield dut.pause))
+            yield
+            self.assertTrue((yield dut.pause))
+            yield
+            self.assertFalse((yield dut.pause))
+
+        runSimulation(dut, process, "test_phy_ecp5ddrphy.vcd")
+
+    def test_value(self):
+        csr = self.MockCSR()
+        dut = _DQSBUFMSettingManager(csr)
+
+        def process():
+            # Check default value
+            self.assertEqual((yield dut.readclksel), 0)
+
+            yield csr.w_data.eq(0b101)
+            yield csr.w_stb.eq(1)
+            yield
+            yield csr.w_stb.eq(0)
+            yield 
+
+            # Ensure value isn't being changed at that point
+            self.assertEqual((yield dut.readclksel), 0)
+            yield; yield Delay(1e-9)
+
+            # Ensure value is changed after the second clock cycle
+            self.assertEqual((yield dut.readclksel), 0b101)
+
+        runSimulation(dut, process, "test_phy_ecp5ddrphy.vcd")