Merge pull request #201 from antmicro/jboc/spd-read

modules/spd: save SPD data in SDRAMModule
This commit is contained in:
enjoy-digital 2020-06-01 21:16:58 +02:00 committed by GitHub
commit d62fd24c81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 22 deletions

View File

@ -48,46 +48,49 @@ def _twos_complement(value, nbits):
def _word(msb, lsb): def _word(msb, lsb):
return (msb << 8) | lsb return (msb << 8) | lsb
# most signifficant (upper) / least signifficant (lower) nibble
def _msn(byte):
return _read_field(byte, nbits=4, shift=4)
def _lsn(byte):
return _read_field(byte, nbits=4, shift=0)
class DDR3SPDData: class DDR3SPDData:
memtype = "DDR3" memtype = "DDR3"
_speedgrades = [800, 1066, 1333, 1600, 1866, 2133]
def __init__(self, spd_data): def __init__(self, spd_data):
# Geometry --------------------------------------------------------------------------------- self.get_geometry(spd_data)
self.init_timebase(spd_data)
self.get_timings(spd_data)
def get_geometry(self, data):
bankbits = { bankbits = {
0b000: 3, 0b000: 3,
0b001: 4, 0b001: 4,
0b010: 5, 0b010: 5,
0b011: 6, 0b011: 6,
}[_read_field(spd_data[4], nbits=3, shift=4)] }[_read_field(data[4], nbits=3, shift=4)]
rowbits = { rowbits = {
0b000: 12, 0b000: 12,
0b001: 13, 0b001: 13,
0b010: 14, 0b010: 14,
0b011: 15, 0b011: 15,
0b100: 16, 0b100: 16,
}[_read_field(spd_data[5], nbits=3, shift=3)] }[_read_field(data[5], nbits=3, shift=3)]
colbits = { colbits = {
0b000: 9, 0b000: 9,
0b001: 10, 0b001: 10,
0b010: 11, 0b010: 11,
0b011: 12, 0b011: 12,
}[_read_field(spd_data[5], nbits=3, shift=0)] }[_read_field(data[5], nbits=3, shift=0)]
self.nbanks = 2**bankbits self.nbanks = 2**bankbits
self.nrows = 2**rowbits self.nrows = 2**rowbits
self.ncols = 2**colbits self.ncols = 2**colbits
# Timings ---------------------------------------------------------------------------------- def get_timings(self, spd_data):
self.init_timebase(spd_data)
# most signifficant (upper) / least signifficant (lower) nibble
def msn(byte):
return _read_field(byte, nbits=4, shift=4)
def lsn(byte):
return _read_field(byte, nbits=4, shift=0)
b = spd_data b = spd_data
tck_min = self.txx_ns(mtb=b[12], ftb=b[34]) tck_min = self.txx_ns(mtb=b[12], ftb=b[34])
taa_min = self.txx_ns(mtb=b[16], ftb=b[35]) taa_min = self.txx_ns(mtb=b[16], ftb=b[35])
@ -95,12 +98,12 @@ class DDR3SPDData:
trcd_min = self.txx_ns(mtb=b[18], ftb=b[36]) trcd_min = self.txx_ns(mtb=b[18], ftb=b[36])
trrd_min = self.txx_ns(mtb=b[19]) trrd_min = self.txx_ns(mtb=b[19])
trp_min = self.txx_ns(mtb=b[20], ftb=b[37]) trp_min = self.txx_ns(mtb=b[20], ftb=b[37])
tras_min = self.txx_ns(mtb=_word(lsn(b[21]), b[22])) tras_min = self.txx_ns(mtb=_word(_lsn(b[21]), b[22]))
trc_min = self.txx_ns(mtb=_word(msn(b[21]), b[23]), ftb=b[38]) trc_min = self.txx_ns(mtb=_word(_msn(b[21]), b[23]), ftb=b[38])
trfc_min = self.txx_ns(mtb=_word(b[25], b[24])) trfc_min = self.txx_ns(mtb=_word(b[25], b[24]))
twtr_min = self.txx_ns(mtb=b[26]) twtr_min = self.txx_ns(mtb=b[26])
trtp_min = self.txx_ns(mtb=b[27]) trtp_min = self.txx_ns(mtb=b[27])
tfaw_min = self.txx_ns(mtb=_word(lsn(b[28]), b[29])) tfaw_min = self.txx_ns(mtb=_word(_lsn(b[28]), b[29]))
technology_timings = _TechnologyTimings( technology_timings = _TechnologyTimings(
tREFI = 64e6/8192, # 64ms/8192ops tREFI = 64e6/8192, # 64ms/8192ops
@ -142,20 +145,19 @@ class DDR3SPDData:
ftb = _twos_complement(ftb, 8) ftb = _twos_complement(ftb, 8)
return mtb * self.medium_timebase_ns + ftb * self.fine_timebase_ns return mtb * self.medium_timebase_ns + ftb * self.fine_timebase_ns
@staticmethod @classmethod
def speedgrade_freq(tck_ns): def speedgrade_freq(cls, tck_ns):
# Calculate rounded speedgrade frequency from tck_min # Calculate rounded speedgrade frequency from tck_min
freq_mhz = (1 / (tck_ns * 1e-9)) / 1e6 freq_mhz = (1 / (tck_ns * 1e-9)) / 1e6
freq_mhz *= 2 # clock rate -> transfer rate (DDR) freq_mhz *= 2 # clock rate -> transfer rate (DDR)
speedgrades = [800, 1066, 1333, 1600, 1866, 2133] for f in cls._speedgrades:
for f in speedgrades:
# Due to limited tck accuracy of 1ps, calculations may yield higher # Due to limited tck accuracy of 1ps, calculations may yield higher
# frequency than in reality (e.g. for DDR3-1866: tck=1.071 ns -> # frequency than in reality (e.g. for DDR3-1866: tck=1.071 ns ->
# -> f=1867.4 MHz, while real is f=1866.6(6) MHz). # -> f=1867.4 MHz, while real is f=1866.6(6) MHz).
max_error = 2 max_error = 2
if abs(freq_mhz - f) < max_error: if abs(freq_mhz - f) < max_error:
return f return f
raise ValueError("Transfer rate = {:.2f} does not correspond to any DDR3 speedgrade" raise ValueError("Transfer rate = {:.2f} does not correspond to any speedgrade"
.format(freq_mhz)) .format(freq_mhz))
def parse_spd_hexdump(filename): def parse_spd_hexdump(filename):
@ -286,6 +288,8 @@ class SDRAMModule:
ncols = spd.ncols ncols = spd.ncols
technology_timings = spd.technology_timings technology_timings = spd.technology_timings
speedgrade_timings = spd.speedgrade_timings speedgrade_timings = spd.speedgrade_timings
# Save data for runtime verification
_spd_data = spd_data
nphases = { nphases = {
"SDR": 1, "SDR": 1,

View File

@ -43,6 +43,12 @@ class TestSPD(unittest.TestCase):
for tck, speedgrade in tck_to_speedgrade.items(): for tck, speedgrade in tck_to_speedgrade.items():
self.assertEqual(speedgrade, DDR3SPDData.speedgrade_freq(tck)) self.assertEqual(speedgrade, DDR3SPDData.speedgrade_freq(tck))
def test_spd_data(self):
# Verify that correct _spd_data is added to SDRAMModule
data = load_spd_reference("MT16KTF1G64HZ-1G6P1.csv")
module = SDRAMModule.from_spd_data(data, 125e6)
self.assertEqual(module._spd_data, data)
def compare_geometry(self, module, module_ref): def compare_geometry(self, module, module_ref):
self.assertEqual(module.nbanks, module_ref.nbanks) self.assertEqual(module.nbanks, module_ref.nbanks)
self.assertEqual(module.nrows, module_ref.nrows) self.assertEqual(module.nrows, module_ref.nrows)