From a2b6c8bdf54e14f53e5fe74865aaf5f6103e7a33 Mon Sep 17 00:00:00 2001 From: Jean THOMAS Date: Thu, 6 Aug 2020 12:28:13 +0200 Subject: [PATCH] gram.phy.ecp5ddrphy: Fix DQSBUFM's pause signal (fixes #51) --- gram/phy/ecp5ddrphy.py | 37 +++++++++++++++++++--- gram/test/test_phy_ecp5ddrphy.py | 53 ++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 gram/test/test_phy_ecp5ddrphy.py diff --git a/gram/phy/ecp5ddrphy.py b/gram/phy/ecp5ddrphy.py index 9bbccbd..c1c2f17 100644 --- a/gram/phy/ecp5ddrphy.py +++ b/gram/phy/ecp5ddrphy.py @@ -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 index 0000000..dc2bd36 --- /dev/null +++ b/gram/test/test_phy_ecp5ddrphy.py @@ -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") -- 2.30.2