|
| 1 | +# |
| 2 | +# This file is part of LiteDRAM. |
| 3 | +# |
| 4 | +# Copyright (c) 2026 Florent Kermarrec <florent@enjoy-digital.fr> |
| 5 | +# SPDX-License-Identifier: BSD-2-Clause |
| 6 | + |
| 7 | +import unittest |
| 8 | +from types import SimpleNamespace |
| 9 | + |
| 10 | +from migen import * |
| 11 | + |
| 12 | +from litedram.common import get_default_cl |
| 13 | +from litedram.init import get_ddr3_phy_init_sequence |
| 14 | +from litedram.phy.s7ddrphy import A7DDRPHY, K7DDRPHY |
| 15 | + |
| 16 | + |
| 17 | +class TestS7DDRPHYSettings(unittest.TestCase): |
| 18 | + sys_clk_freqs = [50e6, 100e6, 125e6, 150e6, 175e6, 225e6] |
| 19 | + ddr3_cls = range(5, 15) |
| 20 | + read_pipeline_cycles = 6 |
| 21 | + |
| 22 | + @staticmethod |
| 23 | + def get_pads(): |
| 24 | + return Record([ |
| 25 | + ("a", 15), |
| 26 | + ("ba", 3), |
| 27 | + ("ras_n", 1), |
| 28 | + ("cas_n", 1), |
| 29 | + ("we_n", 1), |
| 30 | + ("clk_p", 1), |
| 31 | + ("clk_n", 1), |
| 32 | + ("dq", 8), |
| 33 | + ("dm", 1), |
| 34 | + ("dqs_p", 1), |
| 35 | + ("dqs_n", 1), |
| 36 | + ]) |
| 37 | + |
| 38 | + @staticmethod |
| 39 | + def get_rdphase(phy): |
| 40 | + return phy.settings.rdphase.reset.value |
| 41 | + |
| 42 | + @staticmethod |
| 43 | + def get_mr0_cl(phy): |
| 44 | + init_sequence, _ = get_ddr3_phy_init_sequence( |
| 45 | + phy_settings = phy.settings, |
| 46 | + timing_settings = SimpleNamespace(tWTR=2), |
| 47 | + ) |
| 48 | + mr0 = next(address for comment, address, _, _, _ in init_sequence |
| 49 | + if comment.startswith("Load Mode Register 0")) |
| 50 | + mr0_to_cl = { |
| 51 | + 0b0010 : 5, |
| 52 | + 0b0100 : 6, |
| 53 | + 0b0110 : 7, |
| 54 | + 0b1000 : 8, |
| 55 | + 0b1010 : 9, |
| 56 | + 0b1100 : 10, |
| 57 | + 0b1110 : 11, |
| 58 | + 0b0001 : 12, |
| 59 | + 0b0011 : 13, |
| 60 | + 0b0101 : 14, |
| 61 | + } |
| 62 | + mr0_cl = ((mr0 >> 2) & 0b1) | (((mr0 >> 4) & 0b111) << 1) |
| 63 | + return mr0_to_cl[mr0_cl] |
| 64 | + |
| 65 | + def test_a7_default_cl_is_preserved(self): |
| 66 | + for sys_clk_freq in self.sys_clk_freqs: |
| 67 | + with self.subTest(sys_clk_freq=sys_clk_freq): |
| 68 | + phy = A7DDRPHY(self.get_pads(), sys_clk_freq=sys_clk_freq) |
| 69 | + tck = 1/(4*sys_clk_freq) |
| 70 | + expected_cl = get_default_cl("DDR3", tck) |
| 71 | + |
| 72 | + self.assertEqual(phy.settings.cl, expected_cl) |
| 73 | + self.assertEqual(self.get_mr0_cl(phy), expected_cl) |
| 74 | + |
| 75 | + def test_a7_read_alignment(self): |
| 76 | + for cl in self.ddr3_cls: |
| 77 | + for cmd_latency in range(3): |
| 78 | + with self.subTest(cl=cl, cmd_latency=cmd_latency): |
| 79 | + phy = A7DDRPHY(self.get_pads(), |
| 80 | + sys_clk_freq = 100e6, |
| 81 | + cl = cl, |
| 82 | + cmd_latency = cmd_latency, |
| 83 | + ) |
| 84 | + rdphase = self.get_rdphase(phy) |
| 85 | + read_sys_latency = phy.settings.read_latency - self.read_pipeline_cycles |
| 86 | + |
| 87 | + self.assertEqual(phy.settings.cl, cl) |
| 88 | + self.assertEqual(self.get_mr0_cl(phy), cl) |
| 89 | + self.assertEqual( |
| 90 | + rdphase + cl + cmd_latency, |
| 91 | + read_sys_latency*phy.settings.nphases + 1, |
| 92 | + ) |
| 93 | + |
| 94 | + def test_k7_read_alignment_is_unchanged(self): |
| 95 | + for cl in self.ddr3_cls: |
| 96 | + with self.subTest(cl=cl): |
| 97 | + phy = K7DDRPHY(self.get_pads(), sys_clk_freq=100e6, cl=cl) |
| 98 | + rdphase = self.get_rdphase(phy) |
| 99 | + read_sys_latency = phy.settings.read_latency - self.read_pipeline_cycles |
| 100 | + |
| 101 | + self.assertEqual(phy.settings.cl, cl) |
| 102 | + self.assertEqual(self.get_mr0_cl(phy), cl) |
| 103 | + self.assertEqual( |
| 104 | + rdphase + cl, |
| 105 | + read_sys_latency*phy.settings.nphases, |
| 106 | + ) |
0 commit comments