Merge pull request #231 from antmicro/jboc/module-timings
Allow to pass all module timings in the format (ck, ns)
This commit is contained in:
commit
83b31f4f71
|
@ -294,6 +294,12 @@ def parse_spd_hexdump(filename):
|
||||||
|
|
||||||
# SDRAMModule --------------------------------------------------------------------------------------
|
# SDRAMModule --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Frac = namedtuple("Frac", ["num", "denom"])
|
||||||
|
|
||||||
|
class Timing(namedtuple("Timing", ["ck", "ns"])):
|
||||||
|
def __add__(self, other):
|
||||||
|
return Timing(self.ck + other.ck, self.ns + other.ns)
|
||||||
|
|
||||||
class SDRAMModule:
|
class SDRAMModule:
|
||||||
"""SDRAM module geometry and timings.
|
"""SDRAM module geometry and timings.
|
||||||
|
|
||||||
|
@ -320,68 +326,82 @@ class SDRAMModule:
|
||||||
if (fine_refresh_mode is None) and (self.memtype == "DDR4"):
|
if (fine_refresh_mode is None) and (self.memtype == "DDR4"):
|
||||||
fine_refresh_mode = "1x"
|
fine_refresh_mode = "1x"
|
||||||
self.timing_settings = TimingSettings(
|
self.timing_settings = TimingSettings(
|
||||||
tRP = self.ns_to_cycles(self.get("tRP")),
|
tRP = self.ck_ns_to_cycles(self.get("tRP")),
|
||||||
tRCD = self.ns_to_cycles(self.get("tRCD")),
|
tRCD = self.ck_ns_to_cycles(self.get("tRCD")),
|
||||||
tWR = self.ns_to_cycles(self.get("tWR")),
|
tWR = self.ck_ns_to_cycles(self.get("tWR")),
|
||||||
tREFI = self.ns_to_cycles(self.get("tREFI", fine_refresh_mode), False),
|
tREFI = self.ck_ns_to_cycles(self.get("tREFI", fine_refresh_mode), margin=False),
|
||||||
tRFC = self.ck_ns_to_cycles(*self.get("tRFC", fine_refresh_mode)),
|
tRFC = self.ck_ns_to_cycles(self.get("tRFC", fine_refresh_mode)),
|
||||||
tWTR = self.ck_ns_to_cycles(*self.get("tWTR")),
|
tWTR = self.ck_ns_to_cycles(self.get("tWTR")),
|
||||||
tFAW = None if self.get("tFAW") is None else self.ck_ns_to_cycles(*self.get("tFAW")),
|
tFAW = None if self.get("tFAW") is None else self.ck_ns_to_cycles(self.get("tFAW")),
|
||||||
tCCD = None if self.get("tCCD") is None else self.ck_ns_to_cycles(*self.get("tCCD")),
|
tCCD = None if self.get("tCCD") is None else self.ck_ns_to_cycles(self.get("tCCD")),
|
||||||
tRRD = None if self.get("tRRD") is None else self.ck_ns_to_cycles(*self.get("tRRD")),
|
tRRD = None if self.get("tRRD") is None else self.ck_ns_to_cycles(self.get("tRRD")),
|
||||||
tRC = None if self.get("tRAS") is None else self.ns_to_cycles(self.get("tRP") + self.get("tRAS")),
|
tRC = None if self.get("tRAS") is None else self.ck_ns_to_cycles(self.get("tRP") + self.get("tRAS")),
|
||||||
tRAS = None if self.get("tRAS") is None else self.ns_to_cycles(self.get("tRAS")),
|
tRAS = None if self.get("tRAS") is None else self.ck_ns_to_cycles(self.get("tRAS")),
|
||||||
tZQCS = None if self.get("tZQCS") is None else self.ck_ns_to_cycles(*self.get("tZQCS"))
|
tZQCS = None if self.get("tZQCS") is None else self.ck_ns_to_cycles(self.get("tZQCS"))
|
||||||
)
|
)
|
||||||
self.timing_settings.fine_refresh_mode = fine_refresh_mode
|
self.timing_settings.fine_refresh_mode = fine_refresh_mode
|
||||||
|
|
||||||
def get(self, name, key=None):
|
def get(self, name, key=None):
|
||||||
r = None
|
assert name in _speedgrade_timings + _technology_timings, "Unknown name: {}".format(name)
|
||||||
if name in _speedgrade_timings:
|
timing = self.get_timing(name)
|
||||||
if hasattr(self, "speedgrade_timings"):
|
if timing is None:
|
||||||
speedgrade = "default" if self.speedgrade is None else self.speedgrade
|
return None
|
||||||
r = getattr(self.speedgrade_timings[speedgrade], name)
|
if (timing is not None) and (key is not None):
|
||||||
else:
|
timing = timing[key]
|
||||||
name = name + "_" + self.speedgrade if self.speedgrade is not None else name
|
if isinstance(timing, tuple):
|
||||||
try:
|
ck, ns = timing
|
||||||
r = getattr(self, name)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
if hasattr(self, "technology_timings"):
|
ck, ns = 0, timing
|
||||||
r = getattr(self.technology_timings, name)
|
ck = ck or 0
|
||||||
else:
|
ns = ns or 0
|
||||||
try:
|
return Timing(ck, ns)
|
||||||
r = getattr(self, name)
|
|
||||||
except:
|
def get_timing(self, name):
|
||||||
pass
|
if name in _speedgrade_timings:
|
||||||
if (r is not None) and (key is not None):
|
return self.get_speedgrade_timing(name)
|
||||||
r = r[key]
|
return self.get_technology_timing(name)
|
||||||
return r
|
|
||||||
|
def get_speedgrade_timing(self, name):
|
||||||
|
if hasattr(self, "speedgrade_timings"):
|
||||||
|
speedgrade = "default" if self.speedgrade is None else self.speedgrade
|
||||||
|
return getattr(self.speedgrade_timings[speedgrade], name)
|
||||||
|
name = name + "_" + self.speedgrade if self.speedgrade is not None else name
|
||||||
|
try:
|
||||||
|
return getattr(self, name)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_technology_timing(self, name):
|
||||||
|
if hasattr(self, "technology_timings"):
|
||||||
|
return getattr(self.technology_timings, name)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return getattr(self, name)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def ns_to_cycles(self, t, margin=True):
|
def ns_to_cycles(self, t, margin=True):
|
||||||
clk_period_ns = 1e9/self.clk_freq
|
clk_period_ns = 1e9/self.clk_freq
|
||||||
if margin:
|
t += self.margin if margin else 0
|
||||||
margins = {
|
|
||||||
"1:1" : 0,
|
|
||||||
"1:2" : clk_period_ns/2,
|
|
||||||
"1:4" : 3*clk_period_ns/4
|
|
||||||
}
|
|
||||||
t += margins[self.rate]
|
|
||||||
return ceil(t/clk_period_ns)
|
return ceil(t/clk_period_ns)
|
||||||
|
|
||||||
def ck_to_cycles(self, c):
|
def ck_to_cycles(self, c):
|
||||||
d = {
|
return ceil(c/self.rate_frac.denom)
|
||||||
"1:1" : 1,
|
|
||||||
"1:2" : 2,
|
|
||||||
"1:4" : 4
|
|
||||||
}
|
|
||||||
return ceil(c/d[self.rate])
|
|
||||||
|
|
||||||
def ck_ns_to_cycles(self, c, t):
|
def ck_ns_to_cycles(self, timing, **kwargs):
|
||||||
c = 0 if c is None else c
|
return max(self.ck_to_cycles(timing.ck), self.ns_to_cycles(timing.ns, **kwargs))
|
||||||
t = 0 if t is None else t
|
|
||||||
return max(self.ck_to_cycles(c), self.ns_to_cycles(t))
|
@property
|
||||||
|
def rate_frac(self):
|
||||||
|
num, denom = map(int, self.rate.split(":"))
|
||||||
|
assert num == 1, "Rate: numerator must be 1: {}".format(self.rate)
|
||||||
|
return Frac(num, denom)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def margin(self):
|
||||||
|
clk_period_ns = 1e9 / self.clk_freq
|
||||||
|
frac = self.rate_frac
|
||||||
|
return clk_period_ns * (1 - frac.num/frac.denom)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_spd_data(cls, spd_data, clk_freq, fine_refresh_mode=None):
|
def from_spd_data(cls, spd_data, clk_freq, fine_refresh_mode=None):
|
||||||
|
|
|
@ -212,10 +212,10 @@ class DFITimingsChecker(Module):
|
||||||
|
|
||||||
if val is None:
|
if val is None:
|
||||||
val = 0
|
val = 0
|
||||||
elif key in CK_NS:
|
elif key == "tCK":
|
||||||
val = self.ck_ns_to_ps(val, tck)
|
|
||||||
else:
|
|
||||||
val = self.ns_to_ps(val)
|
val = self.ns_to_ps(val)
|
||||||
|
else:
|
||||||
|
val = self.ck_ns_to_ps(val, tck)
|
||||||
|
|
||||||
new_timings[key] = val
|
new_timings[key] = val
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue