modules: add DDR4SPDData parser
This commit is contained in:
parent
4233f86112
commit
a8f2c044c9
|
@ -160,6 +160,100 @@ class DDR3SPDData:
|
||||||
raise ValueError("Transfer rate = {:.2f} does not correspond to any speedgrade"
|
raise ValueError("Transfer rate = {:.2f} does not correspond to any speedgrade"
|
||||||
.format(freq_mhz))
|
.format(freq_mhz))
|
||||||
|
|
||||||
|
|
||||||
|
class DDR4SPDData(DDR3SPDData):
|
||||||
|
memtype = "DDR4"
|
||||||
|
_speedgrades = [1600, 1866, 2133, 2400, 2666, 2933, 3200]
|
||||||
|
|
||||||
|
def get_geometry(self, data):
|
||||||
|
bankgroupbits = {
|
||||||
|
0b00: 0,
|
||||||
|
0b01: 1,
|
||||||
|
0b10: 2,
|
||||||
|
}[_read_field(data[4], nbits=2, shift=6)]
|
||||||
|
bankbits = {
|
||||||
|
0b00: 2,
|
||||||
|
0b01: 3,
|
||||||
|
}[_read_field(data[4], nbits=2, shift=4)]
|
||||||
|
rowbits = {
|
||||||
|
0b000: 12,
|
||||||
|
0b001: 13,
|
||||||
|
0b010: 14,
|
||||||
|
0b011: 15,
|
||||||
|
0b100: 16,
|
||||||
|
0b101: 17,
|
||||||
|
0b110: 18,
|
||||||
|
}[_read_field(data[5], nbits=3, shift=3)]
|
||||||
|
colbits = {
|
||||||
|
0b000: 9,
|
||||||
|
0b001: 10,
|
||||||
|
0b010: 11,
|
||||||
|
0b011: 12,
|
||||||
|
}[_read_field(data[5], nbits=3, shift=0)]
|
||||||
|
|
||||||
|
self.ngroups = 2**bankgroupbits
|
||||||
|
self.ngroupbanks = 2**bankbits
|
||||||
|
self.nbanks = self.ngroups * self.ngroupbanks
|
||||||
|
self.nrows = 2**rowbits
|
||||||
|
self.ncols = 2**colbits
|
||||||
|
|
||||||
|
def init_timebase(self, data):
|
||||||
|
# there is only one possible value for DDR4
|
||||||
|
self.medium_timebase_ns = {
|
||||||
|
0b00: 125e-3,
|
||||||
|
}[_read_field(data[17], nbits=2, shift=2)]
|
||||||
|
self.fine_timebase_ns = {
|
||||||
|
0b00: 1e-3,
|
||||||
|
}[_read_field(data[17], nbits=2, shift=0)]
|
||||||
|
|
||||||
|
def get_timings(self, data):
|
||||||
|
b = data
|
||||||
|
|
||||||
|
self.trefi = {"1x": 64e6/8192, "2x": (64e6/8192)/2, "4x": (64e6/8192)/4}
|
||||||
|
|
||||||
|
tckavg_min = self.txx_ns(mtb=b[18], ftb=b[125])
|
||||||
|
tckavg_max = self.txx_ns(mtb=b[19], ftb=b[124])
|
||||||
|
taa_min = self.txx_ns(mtb=b[24], ftb=b[123])
|
||||||
|
trcd_min = self.txx_ns(mtb=b[25], ftb=b[122])
|
||||||
|
trp_min = self.txx_ns(mtb=b[26], ftb=b[121])
|
||||||
|
tras_min = self.txx_ns(mtb=_word(_lsn(b[27]), b[28]))
|
||||||
|
trc_min = self.txx_ns(mtb=_word(_msn(b[27]), b[29]), ftb=b[120])
|
||||||
|
self.trfc = {
|
||||||
|
"1x": (None, self.txx_ns(mtb=_word(b[31], b[30]))),
|
||||||
|
"2x": (None, self.txx_ns(mtb=_word(b[33], b[32]))),
|
||||||
|
"4x": (None, self.txx_ns(mtb=_word(b[35], b[34]))),
|
||||||
|
}
|
||||||
|
tfaw_min = self.txx_ns(mtb=_word(_lsn(b[36]), b[37]))
|
||||||
|
trrd_s_min = self.txx_ns(mtb=b[38], ftb=b[119])
|
||||||
|
trrd_l_min = self.txx_ns(mtb=b[39], ftb=b[118])
|
||||||
|
tccd_l_min = self.txx_ns(mtb=b[40], ftb=b[117]) # min 6 cycles?
|
||||||
|
twr_min = self.txx_ns(mtb=_word(_lsn(b[41]), b[42]))
|
||||||
|
twtr_s_min = self.txx_ns(mtb=_word(_lsn(b[43]), b[44]))
|
||||||
|
twtr_l_min = self.txx_ns(mtb=_word(_msn(b[43]), b[45]))
|
||||||
|
|
||||||
|
technology_timings = _TechnologyTimings(
|
||||||
|
tREFI = self.trefi,
|
||||||
|
tWTR = (4, twtr_l_min),
|
||||||
|
tCCD = (4, tccd_l_min),
|
||||||
|
tRRD = (4, trrd_l_min),
|
||||||
|
tZQCS = (128, 80),
|
||||||
|
)
|
||||||
|
speedgrade_timings = _SpeedgradeTimings(
|
||||||
|
tRP = trp_min,
|
||||||
|
tRCD = trcd_min,
|
||||||
|
tWR = twr_min,
|
||||||
|
tRFC = self.trfc,
|
||||||
|
tFAW = (None, tfaw_min),
|
||||||
|
tRAS = tras_min,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.speedgrade = str(self.speedgrade_freq(tckavg_min))
|
||||||
|
self.technology_timings = technology_timings
|
||||||
|
self.speedgrade_timings = {
|
||||||
|
self.speedgrade: speedgrade_timings,
|
||||||
|
"default": speedgrade_timings,
|
||||||
|
}
|
||||||
|
|
||||||
def parse_spd_hexdump(filename):
|
def parse_spd_hexdump(filename):
|
||||||
"""Parse data dumped using the `spdread` command in LiteX BIOS
|
"""Parse data dumped using the `spdread` command in LiteX BIOS
|
||||||
|
|
||||||
|
@ -277,6 +371,7 @@ class SDRAMModule:
|
||||||
# set parameters from SPD data based on memory type
|
# set parameters from SPD data based on memory type
|
||||||
spd_cls = {
|
spd_cls = {
|
||||||
0x0b: DDR3SPDData,
|
0x0b: DDR3SPDData,
|
||||||
|
0x0c: DDR4SPDData,
|
||||||
}[spd_data[2]]
|
}[spd_data[2]]
|
||||||
spd = spd_cls(spd_data)
|
spd = spd_cls(spd_data)
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ def load_spd_reference(filename):
|
||||||
"""
|
"""
|
||||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
path = os.path.join(script_dir, "spd_data", filename)
|
path = os.path.join(script_dir, "spd_data", filename)
|
||||||
data = [0] * 256
|
data = [0] * 512
|
||||||
with open(path) as f:
|
with open(path) as f:
|
||||||
reader = csv.DictReader(f)
|
reader = csv.DictReader(f)
|
||||||
for row in reader:
|
for row in reader:
|
||||||
|
@ -174,3 +174,46 @@ class TestSPD(unittest.TestCase):
|
||||||
self.assertEqual(sgt.tRP, 13.125)
|
self.assertEqual(sgt.tRP, 13.125)
|
||||||
self.assertEqual(sgt.tRCD, 13.125)
|
self.assertEqual(sgt.tRCD, 13.125)
|
||||||
self.assertEqual(sgt.tRP + sgt.tRAS, 47.125)
|
self.assertEqual(sgt.tRP + sgt.tRAS, 47.125)
|
||||||
|
|
||||||
|
def test_MTA4ATF51264HZ_parsing(self):
|
||||||
|
kwargs = dict(clk_freq=100e6, rate="1:4")
|
||||||
|
|
||||||
|
with self.subTest(speedgrade="-2G3"):
|
||||||
|
data = load_spd_reference("MTA4ATF51264HZ-2G3B1.csv")
|
||||||
|
module = SDRAMModule.from_spd_data(data, kwargs["clk_freq"])
|
||||||
|
sgt = module.speedgrade_timings["2400"]
|
||||||
|
self.assertEqual(sgt.tRP, 13.75)
|
||||||
|
self.assertEqual(sgt.tRCD, 13.75)
|
||||||
|
self.assertEqual(sgt.tRP + sgt.tRAS, 45.75)
|
||||||
|
|
||||||
|
with self.subTest(speedgrade="-3G2"):
|
||||||
|
data = load_spd_reference("MTA4ATF51264HZ-3G2E1.csv")
|
||||||
|
module = SDRAMModule.from_spd_data(data, kwargs["clk_freq"])
|
||||||
|
sgt = module.speedgrade_timings["3200"]
|
||||||
|
self.assertEqual(sgt.tRP, 13.75)
|
||||||
|
self.assertEqual(sgt.tRCD, 13.75)
|
||||||
|
self.assertEqual(sgt.tRP + sgt.tRAS, 45.75)
|
||||||
|
|
||||||
|
# FIXME: when setting timings as seen in SPD, DRAM leveling fails
|
||||||
|
@unittest.skip("Using timings from SPD fails DRAM initialzation on this module")
|
||||||
|
def test_MTA4ATF51264HZ(self):
|
||||||
|
kwargs = dict(clk_freq=100e6, rate="1:4")
|
||||||
|
module_ref = litedram.modules.MTA4ATF51264HZ(**kwargs)
|
||||||
|
|
||||||
|
with self.subTest(speedgrade="-2G3"):
|
||||||
|
data = load_spd_reference("MTA4ATF51264HZ-2G3B1.csv")
|
||||||
|
module = SDRAMModule.from_spd_data(data, kwargs["clk_freq"])
|
||||||
|
self.compare_modules(module, module_ref)
|
||||||
|
sgt = module.speedgrade_timings["2400"]
|
||||||
|
self.assertEqual(sgt.tRP, 13.75)
|
||||||
|
self.assertEqual(sgt.tRCD, 13.75)
|
||||||
|
self.assertEqual(sgt.tRP + sgt.tRAS, 45.75)
|
||||||
|
|
||||||
|
with self.subTest(speedgrade="-3G2"):
|
||||||
|
data = load_spd_reference("MTA4ATF51264HZ-3G2E1.csv")
|
||||||
|
module = SDRAMModule.from_spd_data(data, kwargs["clk_freq"])
|
||||||
|
self.compare_modules(module, module_ref)
|
||||||
|
sgt = module.speedgrade_timings["3200"]
|
||||||
|
self.assertEqual(sgt.tRP, 13.75)
|
||||||
|
self.assertEqual(sgt.tRCD, 13.75)
|
||||||
|
self.assertEqual(sgt.tRP + sgt.tRAS, 45.75)
|
||||||
|
|
Loading…
Reference in New Issue