phy/lpddr5: delay WCK sync FSM transition by 1 cycle

With fixed serialization logic WCK sync can be now started later
which avoids the need for special logic when tWCKENL=0.
This commit is contained in:
Jędrzej Boczar 2021-08-24 15:14:31 +02:00 committed by Alessandro Comodi
parent 32a56ffe28
commit 8c10f1405b
2 changed files with 12 additions and 32 deletions

View File

@ -300,7 +300,7 @@ class LPDDR5PHY(Module, AutoCSR):
wck_sync = TappedDelayLine( wck_sync = TappedDelayLine(
signal = self.adapter.wck_sync, signal = self.adapter.wck_sync,
ntaps = max(1, max(frange.t_wckenl_wr, frange.t_wckenl_rd) + frange.t_wckpre_static - 1), ntaps = max(1, max(frange.t_wckenl_wr, frange.t_wckenl_rd) + frange.t_wckpre_static),
) )
self.submodules += wck_sync self.submodules += wck_sync
wck_sync_taps = Array([wck_sync.input, *wck_sync.taps]) wck_sync_taps = Array([wck_sync.input, *wck_sync.taps])
@ -313,43 +313,23 @@ class LPDDR5PHY(Module, AutoCSR):
"toggle_4:1": "-_-_-_-_", "toggle_4:1": "-_-_-_-_",
} }
# When tWCKENL=0 there is a special case:
# tWCKENL=0, tWCKPRE_Static=1: instant static and jump toggle
# tWCKENL=0, tWCKPRE_Static=2: instant static and jump static
# tWCKENL=1, tWCKPRE_Static=1 (and others): jump static, jump toggle
def on_disabled(t_wckenl):
# when there is no tWCKENL we need to use static in this cycle
return If(t_wckenl == 0,
wck_pattern.eq(bitpattern(patterns["static"])),
# If there is only 1 cycle of static, we must jump directly to toggle
If(frange.t_wckpre_static == 1,
NextState("TOGGLE")
).Else(
NextState("STATIC")
)
).Else(
# tWCKENL was > 0, so we just continue to STATIC
NextState("STATIC")
),
assert frange.t_wckpre_static > 0 # The algorithm assumes it's never 0 assert frange.t_wckpre_static > 0 # The algorithm assumes it's never 0
wck_fsm = FSM() wck_fsm = FSM()
self.submodules += wck_fsm self.submodules += wck_fsm
wck_fsm.act("DISABLED", wck_fsm.act("DISABLED",
wck_pattern.eq(bitpattern(patterns["disabled"])), wck_pattern.eq(bitpattern(patterns["disabled"])),
# Avoid indexing with -1 as it wraps, we just have special logic for tWCKENL=0 If(wck_sync_taps[frange.t_wckenl_wr] == WCKSyncType.WR,
If(wck_sync_taps[max(0, frange.t_wckenl_wr - 1)] == WCKSyncType.WR, NextState("STATIC")
on_disabled(frange.t_wckenl_wr) ).Elif(wck_sync_taps[frange.t_wckenl_rd] == WCKSyncType.RD,
).Elif(wck_sync_taps[max(0, frange.t_wckenl_rd - 1)] == WCKSyncType.RD, NextState("STATIC")
on_disabled(frange.t_wckenl_rd)
) )
) )
wck_fsm.act("STATIC", wck_fsm.act("STATIC",
wck_pattern.eq(bitpattern(patterns["static"])), wck_pattern.eq(bitpattern(patterns["static"])),
If(wck_sync_taps[frange.t_wckenl_wr + frange.t_wckpre_static - 1] == WCKSyncType.WR, If(wck_sync_taps[frange.t_wckenl_wr + frange.t_wckpre_static] == WCKSyncType.WR,
NextState("TOGGLE") NextState("TOGGLE")
).Elif(wck_sync_taps[frange.t_wckenl_rd + frange.t_wckpre_static - 1] == WCKSyncType.RD, ).Elif(wck_sync_taps[frange.t_wckenl_rd + frange.t_wckpre_static] == WCKSyncType.RD,
NextState("TOGGLE") NextState("TOGGLE")
) )
) )

View File

@ -516,14 +516,14 @@ class LPDDR5Tests(unittest.TestCase):
# tWCKENL_WR starts counting from first command (CAS) so we add command latency, # tWCKENL_WR starts counting from first command (CAS) so we add command latency,
# then preamble, then toggle for the whole burst, then postamble for tWCKPST=2.5tCK # then preamble, then toggle for the whole burst, then postamble for tWCKPST=2.5tCK
# (but for now we assume that WCK is never disabled) # (but for now we assume that WCK is never disabled)
"wck0": "0000" + wck_preamble + "10 10" * (16//4) + "10 10 1" + "0 10" + "10 10"*2, "wck0": "0000 0000" + wck_preamble + "10 10" * (16//4) + "10 10 1" + "0 10" + "10 10"*2,
}, },
}, },
chunk_size=4, chunk_size=4,
) )
def test_lpddr5_wck_sync_4to1_write(self): def test_lpddr5_wck_sync_4to1_write(self):
# Test that correct WCK sequence is generated during WCK sync before burst write for WCK:CK=2:1 # Test that correct WCK sequence is generated during WCK sync before burst write for WCK:CK=4:1
cases = { # sys_clk_freq: timings cases = { # sys_clk_freq: timings
50e6: dict(t_wckenl_wr=0, t_wckenl_static=1, t_wckenl_toggle_wr=2), # data rate 400 MT/s 50e6: dict(t_wckenl_wr=0, t_wckenl_static=1, t_wckenl_toggle_wr=2), # data rate 400 MT/s
100e6: dict(t_wckenl_wr=0, t_wckenl_static=1, t_wckenl_toggle_wr=2), # 800 MT/s 100e6: dict(t_wckenl_wr=0, t_wckenl_static=1, t_wckenl_toggle_wr=2), # 800 MT/s
@ -559,7 +559,7 @@ class LPDDR5Tests(unittest.TestCase):
# tWCKENL_WR starts counting from first command (CAS) so we add command latency, # tWCKENL_WR starts counting from first command (CAS) so we add command latency,
# then preamble, then toggle for the whole burst, then postamble for tWCKPST=2.5tCK # then preamble, then toggle for the whole burst, then postamble for tWCKPST=2.5tCK
# (but for now we assume that WCK is never disabled) # (but for now we assume that WCK is never disabled)
"wck0": "00000000" + wck_preamble + "10101010" * (16//8) + "10101" + "0 10" + "10 10"*2, "wck0": "00000000 00000000" + wck_preamble + "10101010" * (16//8) + "10101" + "0 10" + "10 10"*2,
}, },
}, },
) )
@ -591,7 +591,7 @@ class LPDDR5Tests(unittest.TestCase):
"cs": "01100000", "cs": "01100000",
}, },
"sys4x_270": { "sys4x_270": {
"wck0": "0000" + wck_preamble + "10 10" * (16//4) + "10 10 1" + "0 10" + "10 10"*2, "wck0": "0000 0000" + wck_preamble + "10 10" * (16//4) + "10 10 1" + "0 10" + "10 10"*2,
}, },
}, },
chunk_size=4, chunk_size=4,
@ -624,7 +624,7 @@ class LPDDR5Tests(unittest.TestCase):
"cs": "01100000", "cs": "01100000",
}, },
"sys8x_270": { "sys8x_270": {
"wck0": "00000000" + wck_preamble + "10101010" * (16//8) + "10101" + "0 10" + "10 10"*2, "wck0": "00000000 00000000" + wck_preamble + "10101010" * (16//8) + "10101" + "0 10" + "10 10"*2,
}, },
}, },
) )