Merge pull request #201 from antmicro/jboc/spd-read
modules/spd: save SPD data in SDRAMModule
This commit is contained in:
commit
d62fd24c81
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue