modules: add DDR4SPDData parser

This commit is contained in:
Jędrzej Boczar 2020-06-02 12:16:41 +02:00
parent 4233f86112
commit a8f2c044c9
2 changed files with 139 additions and 1 deletions

View File

@ -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)

View File

@ -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)