From 38054874ac1dc80f75faca9b9af590a4754b9fff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Mon, 3 Aug 2020 15:21:24 +0200 Subject: [PATCH 1/3] build/sim: use a real timebase in the simulation --- litex/build/sim/config.py | 9 +++- litex/build/sim/core/modules.h | 3 +- litex/build/sim/core/parse.c | 82 ++++++++++++++++++++++++++++++++-- litex/build/sim/core/sim.c | 5 ++- litex/build/sim/core/veril.cpp | 28 ++++++------ litex/build/sim/core/veril.h | 2 + litex/tools/litex_sim.py | 4 +- 7 files changed, 110 insertions(+), 23 deletions(-) diff --git a/litex/build/sim/config.py b/litex/build/sim/config.py index 07a2be574..1cad495e2 100644 --- a/litex/build/sim/config.py +++ b/litex/build/sim/config.py @@ -5,9 +5,10 @@ import json class SimConfig(): - def __init__(self, default_clk=None): + def __init__(self, default_clk=None, timebase_ps=1): self.modules = [] self.default_clk = default_clk + self.timebase = timebase_ps if default_clk: self.add_clocker(default_clk) @@ -23,6 +24,9 @@ class SimConfig(): new.append(obj) return new + def _format_timebase(self): + return {"timebase": int(self.timebase)} + def add_clocker(self, clk): self.add_module("clocker", [], clocks=clk, tickfirst=True) @@ -49,4 +53,5 @@ class SimConfig(): return False def get_json(self): - return json.dumps(self.modules, indent=4) + config = self.modules + [self._format_timebase()] + return json.dumps(config, indent=4) diff --git a/litex/build/sim/core/modules.h b/litex/build/sim/core/modules.h index ea83d9ebd..f324126ef 100644 --- a/litex/build/sim/core/modules.h +++ b/litex/build/sim/core/modules.h @@ -3,6 +3,7 @@ #ifndef __MODULE_H_ #define __MODULE_H_ +#include #include "pads.h" struct interface_s { @@ -33,7 +34,7 @@ struct ext_module_list_s { struct ext_module_list_s *next; }; -int litex_sim_file_to_module_list(char *filename, struct module_s **mod); +int litex_sim_file_parse(char *filename, struct module_s **mod, uint64_t *timebase); int litex_sim_load_ext_modules(struct ext_module_list_s **mlist); int litex_sim_find_ext_module(struct ext_module_list_s *first, char *name , struct ext_module_list_s **found); diff --git a/litex/build/sim/core/parse.c b/litex/build/sim/core/parse.c index 6af8a198b..5b45cb778 100644 --- a/litex/build/sim/core/parse.c +++ b/litex/build/sim/core/parse.c @@ -228,11 +228,10 @@ static int json_to_module_list(json_object *obj, struct module_s **mod) for(i = 0; i < n; i++) { tobj = json_object_array_get_idx(obj, i); + if(!json_object_object_get_ex(tobj, "module", &name)) { - ret=RC_JSERROR; - eprintf("expected \"module\" in object (%s)\n", json_object_to_json_string(tobj)); - goto out; + continue; } if(!json_object_object_get_ex(tobj, "interface", &interface)) @@ -288,6 +287,14 @@ static int json_to_module_list(json_object *obj, struct module_s **mod) m->tickfirst = json_object_get_boolean(tickfirst); } } + + if (!m) + { + ret = RC_JSERROR; + eprintf("No modules found in config file:\n%s\n", json_object_to_json_string(obj)); + goto out; + } + *mod = first; first=NULL; @@ -299,7 +306,68 @@ out: return ret; } -int litex_sim_file_to_module_list(char *filename, struct module_s **mod) +static int json_get_timebase(json_object *obj, uint64_t *timebase) +{ + json_object *tobj; + int ret=RC_OK; + int i, n; + uint64_t _timebase = 0; + json_object *json_timebase; + + if(!obj || !timebase) + { + ret = RC_INVARG; + eprintf("Wrong arguments\n"); + goto out; + } + + if(!json_object_is_type(obj, json_type_array)) + { + ret=RC_JSERROR; + eprintf("Config file must be an array\n"); + goto out; + } + + n = json_object_array_length(obj); + for(i = 0; i < n; i++) + { + tobj = json_object_array_get_idx(obj, i); + + if(!json_object_object_get_ex(tobj, "timebase", &json_timebase)) + { + continue; + } + + if (_timebase != 0) + { + ret=RC_JSERROR; + eprintf("\"timebase\" found multiple times: in object (%s)\n", json_object_to_json_string(tobj)); + goto out; + } + + _timebase = json_object_get_uint64(json_timebase); + if (_timebase == 0) + { + ret=RC_JSERROR; + eprintf("\"timebase\" cannot be zero: in object (%s)\n", json_object_to_json_string(tobj)); + goto out; + } + } + + if (_timebase == 0) + { + ret=RC_JSERROR; + eprintf("No \"timebase\" found in config:\n%s\n", json_object_to_json_string(obj)); + goto out; + } + *timebase = _timebase; + +out: + return ret; +} + + +int litex_sim_file_parse(char *filename, struct module_s **mod, uint64_t *timebase) { struct module_s *m=NULL; json_object *obj=NULL; @@ -318,6 +386,12 @@ int litex_sim_file_to_module_list(char *filename, struct module_s **mod) goto out; } + ret = json_get_timebase(obj, timebase); + if(RC_OK != ret) + { + goto out; + } + ret = json_to_module_list(obj, &m); if(RC_OK != ret) { diff --git a/litex/build/sim/core/sim.c b/litex/build/sim/core/sim.c index a4e9adeae..aec6311fd 100644 --- a/litex/build/sim/core/sim.c +++ b/litex/build/sim/core/sim.c @@ -31,6 +31,7 @@ struct session_list_s { struct session_list_s *next; }; +uint64_t timebase_ps = 1; struct session_list_s *sesslist=NULL; struct event_base *base=NULL; @@ -63,7 +64,7 @@ static int litex_sim_initialize_all(void **sim, void *base) } /* Load configuration */ - ret = litex_sim_file_to_module_list("sim_config.js", &ml); + ret = litex_sim_file_parse("sim_config.js", &ml, &timebase_ps); if(RC_OK != ret) { goto out; @@ -191,6 +192,8 @@ static void cb(int sock, short which, void *arg) s->module->tick(s->session); } + litex_sim_increment_time(timebase_ps); + if (litex_sim_got_finish()) { event_base_loopbreak(base); break; diff --git a/litex/build/sim/core/veril.cpp b/litex/build/sim/core/veril.cpp index 914743604..5f74ecc4c 100644 --- a/litex/build/sim/core/veril.cpp +++ b/litex/build/sim/core/veril.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "Vsim.h" #include "verilated.h" #ifdef TRACE_FST @@ -16,8 +17,9 @@ VerilatedFstC* tfp; #else VerilatedVcdC* tfp; #endif -long tfp_start; -long tfp_end; +uint64_t tfp_start; +uint64_t tfp_end; +uint64_t main_time = 0; extern "C" void litex_sim_eval(void *vsim) { @@ -25,6 +27,10 @@ extern "C" void litex_sim_eval(void *vsim) sim->eval(); } +extern "C" void litex_sim_increment_time(unsigned long dt_ps) { + main_time += dt_ps; +} + extern "C" void litex_sim_init_cmdargs(int argc, char *argv[]) { Verilated::commandArgs(argc, argv); @@ -34,7 +40,7 @@ extern "C" void litex_sim_init_tracer(void *vsim, long start, long end) { Vsim *sim = (Vsim*)vsim; tfp_start = start; - tfp_end = end; + tfp_end = end >= 0 ? end : UINT64_MAX; Verilated::traceEverOn(true); #ifdef TRACE_FST tfp = new VerilatedFstC; @@ -45,20 +51,15 @@ extern "C" void litex_sim_init_tracer(void *vsim, long start, long end) sim->trace(tfp, 99); tfp->open("sim.vcd"); #endif + tfp->set_time_unit("1ps"); + tfp->set_time_resolution("1ps"); } extern "C" void litex_sim_tracer_dump() { - static unsigned int ticks=0; - int dump = 1; - if (ticks < tfp_start) - dump = 0; - if (tfp_end != -1) - if (ticks > tfp_end) - dump = 0; - if (dump) - tfp->dump(ticks); - ticks++; + if (tfp_start <= main_time && main_time <= tfp_end) { + tfp->dump(main_time); + } } extern "C" int litex_sim_got_finish() @@ -73,7 +74,6 @@ extern "C" void litex_sim_coverage_dump() } #endif -vluint64_t main_time = 0; double sc_time_stamp() { return main_time; diff --git a/litex/build/sim/core/veril.h b/litex/build/sim/core/veril.h index 4944316e6..d25a3e1cb 100644 --- a/litex/build/sim/core/veril.h +++ b/litex/build/sim/core/veril.h @@ -6,6 +6,7 @@ #ifdef __cplusplus extern "C" void litex_sim_init_cmdargs(int argc, char *argv[]); extern "C" void litex_sim_eval(void *vsim); +extern "C" void litex_sim_increment_time(unsigned long dt_ps); extern "C" void litex_sim_init_tracer(void *vsim, long start, long end) extern "C" void litex_sim_tracer_dump(); extern "C" int litex_sim_got_finish(); @@ -14,6 +15,7 @@ extern "C" void litex_sim_coverage_dump(); #endif #else void litex_sim_eval(void *vsim); +void litex_sim_increment_time(unsigned long dt_ps); void litex_sim_init_tracer(void *vsim); void litex_sim_tracer_dump(); int litex_sim_got_finish(); diff --git a/litex/tools/litex_sim.py b/litex/tools/litex_sim.py index 4c33dbb72..24c8b9144 100755 --- a/litex/tools/litex_sim.py +++ b/litex/tools/litex_sim.py @@ -343,7 +343,9 @@ def main(): soc_kwargs = soc_sdram_argdict(args) builder_kwargs = builder_argdict(args) - sim_config = SimConfig(default_clk="sys_clk") + # timebase is half of the period of main simulation clock + sys_clk_freq = int(1e6) + sim_config = SimConfig(default_clk="sys_clk", timebase_ps=(1/sys_clk_freq / 2) * 1e12) # Configuration -------------------------------------------------------------------------------- From c1ae7e596c3e11db309659c8bfc197ebdeec14c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Mon, 3 Aug 2020 16:52:54 +0200 Subject: [PATCH 2/3] build/sim: allow for arbitrary clocks generation using clockers --- litex/build/sim/config.py | 41 +++++++---- litex/build/sim/core/modules.h | 2 +- .../build/sim/core/modules/clocker/clocker.c | 71 ++++++++++++++++++- litex/build/sim/core/sim.c | 9 ++- litex/build/sim/core/veril.cpp | 3 +- litex/build/sim/core/veril.h | 6 +- litex/tools/litex_sim.py | 4 +- 7 files changed, 111 insertions(+), 25 deletions(-) diff --git a/litex/build/sim/config.py b/litex/build/sim/config.py index 1cad495e2..58a2d384b 100644 --- a/litex/build/sim/config.py +++ b/litex/build/sim/config.py @@ -3,14 +3,12 @@ # License: BSD import json +import math class SimConfig(): - def __init__(self, default_clk=None, timebase_ps=1): + def __init__(self, timebase_ps=None): self.modules = [] - self.default_clk = default_clk - self.timebase = timebase_ps - if default_clk: - self.add_clocker(default_clk) + self.timebase_ps = timebase_ps def _format_interfaces(self, interfaces): if not isinstance(interfaces, list): @@ -25,17 +23,32 @@ class SimConfig(): return new def _format_timebase(self): - return {"timebase": int(self.timebase)} + timebase_ps = self.timebase_ps + if timebase_ps is None: + timebase_ps = self._get_timebase_ps() + return {"timebase": int(timebase_ps)} - def add_clocker(self, clk): - self.add_module("clocker", [], clocks=clk, tickfirst=True) + def _get_timebase_ps(self): + clockers = [m for m in self.modules if m["module"] == "clocker"] + periods_ps = [1e12 / m["args"]["freq_hz"] for m in clockers] + # timebase is half of the shortest period + for p in periods_ps: + assert round(p/2) == int(p//2), "Period cannot be represented: {}".format(p) + half_period = [int(p//2) for p in periods_ps] + # find greatest common denominator + gcd = half_period[0] + for p in half_period[1:]: + gcd = math.gcd(gcd, p) + assert gcd >= 1 + return gcd - def add_module(self, name, interfaces, clocks=None, args=None, tickfirst=False): + def add_clocker(self, clk, freq_hz, phase_deg=0): + args = {"freq_hz": freq_hz, "phase_deg": phase_deg} + self.add_module("clocker", [], clocks=clk, tickfirst=True, args=args) + + def add_module(self, name, interfaces, clocks="sys_clk", args=None, tickfirst=False): interfaces = self._format_interfaces(interfaces) - if clocks: - interfaces += self._format_interfaces(clocks) - else: - interfaces += [self.default_clk] + interfaces += self._format_interfaces(clocks) newmod = { "module": name, "interface": interfaces, @@ -53,5 +66,7 @@ class SimConfig(): return False def get_json(self): + assert "clocker" in (m["module"] for m in self.modules), \ + "No simulation clocker found! Use sim_config.add_clocker() to define one or more clockers." config = self.modules + [self._format_timebase()] return json.dumps(config, indent=4) diff --git a/litex/build/sim/core/modules.h b/litex/build/sim/core/modules.h index f324126ef..b6aa4b46f 100644 --- a/litex/build/sim/core/modules.h +++ b/litex/build/sim/core/modules.h @@ -26,7 +26,7 @@ struct ext_module_s { int (*new_sess)(void **, char *); int (*add_pads)(void *, struct pad_list_s *); int (*close)(void*); - int (*tick)(void*); + int (*tick)(void*, uint64_t); }; struct ext_module_list_s { diff --git a/litex/build/sim/core/modules/clocker/clocker.c b/litex/build/sim/core/modules/clocker/clocker.c index cfd2f1039..5a755e1e2 100644 --- a/litex/build/sim/core/modules/clocker/clocker.c +++ b/litex/build/sim/core/modules/clocker/clocker.c @@ -1,11 +1,14 @@ #include #include #include +#include #include "error.h" #include "modules.h" struct session_s { char *sys_clk; + uint32_t freq_hz; + uint16_t phase_deg; }; static int litex_sim_module_pads_get( struct pad_s *pads, char *name, void **signal) @@ -34,6 +37,55 @@ out: return ret; } +static int clocker_parse_args(struct session_s *s, const char *args) +{ + int ret = RC_OK; + json_object *args_json = NULL; + json_object *freq_json = NULL; + json_object *phase_json = NULL; + + args_json = json_tokener_parse(args); + if (!args_json) { + ret = RC_JSERROR; + fprintf(stderr, "[clocker] Could not parse args: %s\n", args); + goto out; + } + + if(!json_object_object_get_ex(args_json, "freq_hz", &freq_json)) + { + ret = RC_JSERROR; + fprintf(stderr, "[clocker] \"freq_hz\" not found in args: %s\n", json_object_to_json_string(args_json)); + goto out; + } + + if(!json_object_object_get_ex(args_json, "phase_deg", &phase_json)) + { + ret = RC_JSERROR; + fprintf(stderr, "[clocker] \"phase_deg\" not found in args: %s\n", json_object_to_json_string(args_json)); + goto out; + } + + s->freq_hz = json_object_get_uint64(freq_json); + s->phase_deg = json_object_get_uint64(phase_json); + + if (s->freq_hz == 0) { + ret = RC_JSERROR; + fprintf(stderr, "[clocker] \"freq_hz\" must be different than 0\n"); + goto out; + } + + if (s->phase_deg >= 360) { + ret = RC_JSERROR; + fprintf(stderr, "[clocker] \"phase_deg\" must be in range [0, 360)\n"); + goto out; + } + + printf("[clocker] freq_hz=%u, phase_deg=%u\n", s->freq_hz, s->phase_deg); +out: + if(args_json) json_object_put(args_json); + return ret; +} + static int clocker_start() { printf("[clocker] loaded\n"); @@ -58,6 +110,7 @@ static int clocker_new(void **sess, char *args) } memset(s, 0, sizeof(struct session_s)); + clocker_parse_args(s, args); out: *sess=(void*)s; return ret; @@ -85,10 +138,22 @@ out: return ret; } -static int clocker_tick(void *sess) +static int clocker_tick(void *sess, uint64_t time_ps) { - struct session_s *s=(struct session_s*)sess; - *s->sys_clk = ~(*s->sys_clk); + static const uint64_t ps_in_sec = 1000000000000ull; + struct session_s *s = (struct session_s*) sess; + + uint64_t period_ps = ps_in_sec / s->freq_hz; + uint64_t phase_shift_ps = period_ps * s->phase_deg / 360; + + // phase-shifted time relative to start of current period + uint64_t rel_time_ps = (time_ps - phase_shift_ps) % period_ps; + if (rel_time_ps < (period_ps/2)) { + *s->sys_clk = 1; + } else { + *s->sys_clk = 0; + } + return 0; } diff --git a/litex/build/sim/core/sim.c b/litex/build/sim/core/sim.c index aec6311fd..98ffc91fb 100644 --- a/litex/build/sim/core/sim.c +++ b/litex/build/sim/core/sim.c @@ -32,6 +32,7 @@ struct session_list_s { }; uint64_t timebase_ps = 1; +uint64_t sim_time_ps = 0; struct session_list_s *sesslist=NULL; struct event_base *base=NULL; @@ -182,17 +183,19 @@ static void cb(int sock, short which, void *arg) for(s = sesslist; s; s=s->next) { if(s->tickfirst) - s->module->tick(s->session); + s->module->tick(s->session, sim_time_ps); } + litex_sim_eval(vsim); litex_sim_dump(); + for(s = sesslist; s; s=s->next) { if(!s->tickfirst) - s->module->tick(s->session); + s->module->tick(s->session, sim_time_ps); } - litex_sim_increment_time(timebase_ps); + sim_time_ps = litex_sim_increment_time(timebase_ps); if (litex_sim_got_finish()) { event_base_loopbreak(base); diff --git a/litex/build/sim/core/veril.cpp b/litex/build/sim/core/veril.cpp index 5f74ecc4c..2637fc5b8 100644 --- a/litex/build/sim/core/veril.cpp +++ b/litex/build/sim/core/veril.cpp @@ -27,8 +27,9 @@ extern "C" void litex_sim_eval(void *vsim) sim->eval(); } -extern "C" void litex_sim_increment_time(unsigned long dt_ps) { +extern "C" uint64_t litex_sim_increment_time(unsigned long dt_ps) { main_time += dt_ps; + return main_time; } extern "C" void litex_sim_init_cmdargs(int argc, char *argv[]) diff --git a/litex/build/sim/core/veril.h b/litex/build/sim/core/veril.h index d25a3e1cb..57e710f06 100644 --- a/litex/build/sim/core/veril.h +++ b/litex/build/sim/core/veril.h @@ -3,10 +3,12 @@ #ifndef __VERIL_H_ #define __VERIL_H_ +#include + #ifdef __cplusplus extern "C" void litex_sim_init_cmdargs(int argc, char *argv[]); extern "C" void litex_sim_eval(void *vsim); -extern "C" void litex_sim_increment_time(unsigned long dt_ps); +extern "C" uint64_t litex_sim_increment_time(unsigned long dt_ps); extern "C" void litex_sim_init_tracer(void *vsim, long start, long end) extern "C" void litex_sim_tracer_dump(); extern "C" int litex_sim_got_finish(); @@ -15,7 +17,7 @@ extern "C" void litex_sim_coverage_dump(); #endif #else void litex_sim_eval(void *vsim); -void litex_sim_increment_time(unsigned long dt_ps); +uint64_t litex_sim_increment_time(unsigned long dt_ps); void litex_sim_init_tracer(void *vsim); void litex_sim_tracer_dump(); int litex_sim_got_finish(); diff --git a/litex/tools/litex_sim.py b/litex/tools/litex_sim.py index 24c8b9144..c07cb3a98 100755 --- a/litex/tools/litex_sim.py +++ b/litex/tools/litex_sim.py @@ -343,9 +343,9 @@ def main(): soc_kwargs = soc_sdram_argdict(args) builder_kwargs = builder_argdict(args) - # timebase is half of the period of main simulation clock sys_clk_freq = int(1e6) - sim_config = SimConfig(default_clk="sys_clk", timebase_ps=(1/sys_clk_freq / 2) * 1e12) + sim_config = SimConfig() + sim_config.add_clocker("sys_clk", freq_hz=sys_clk_freq) # Configuration -------------------------------------------------------------------------------- From f778ff09dcc0edcdbea34f41dc64c05b4dee1587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Tue, 4 Aug 2020 14:00:58 +0200 Subject: [PATCH 3/3] build/sim: improve timebase calculation (strict checks) and update modules --- litex/build/sim/config.py | 68 +++++++++++++------ litex/build/sim/core/modules.h | 17 +++++ .../build/sim/core/modules/clocker/clocker.c | 19 +++--- .../sim/core/modules/ethernet/ethernet.c | 6 +- .../sim/core/modules/jtagremote/jtagremote.c | 6 +- .../modules/serial2console/serial2console.c | 5 +- .../sim/core/modules/serial2tcp/serial2tcp.c | 6 +- .../sim/core/modules/spdeeprom/spdeeprom.c | 7 +- .../modules/xgmii_ethernet/xgmii_ethernet.c | 5 +- litex/build/sim/core/sim.c | 4 +- litex/build/sim/core/veril.cpp | 8 +-- litex/build/sim/core/veril.h | 8 +-- 12 files changed, 105 insertions(+), 54 deletions(-) diff --git a/litex/build/sim/config.py b/litex/build/sim/config.py index 58a2d384b..43eeb1565 100644 --- a/litex/build/sim/config.py +++ b/litex/build/sim/config.py @@ -6,9 +6,8 @@ import json import math class SimConfig(): - def __init__(self, timebase_ps=None): + def __init__(self): self.modules = [] - self.timebase_ps = timebase_ps def _format_interfaces(self, interfaces): if not isinstance(interfaces, list): @@ -23,24 +22,9 @@ class SimConfig(): return new def _format_timebase(self): - timebase_ps = self.timebase_ps - if timebase_ps is None: - timebase_ps = self._get_timebase_ps() - return {"timebase": int(timebase_ps)} - - def _get_timebase_ps(self): clockers = [m for m in self.modules if m["module"] == "clocker"] - periods_ps = [1e12 / m["args"]["freq_hz"] for m in clockers] - # timebase is half of the shortest period - for p in periods_ps: - assert round(p/2) == int(p//2), "Period cannot be represented: {}".format(p) - half_period = [int(p//2) for p in periods_ps] - # find greatest common denominator - gcd = half_period[0] - for p in half_period[1:]: - gcd = math.gcd(gcd, p) - assert gcd >= 1 - return gcd + timebase_ps = _calculate_timebase_ps(clockers) + return {"timebase": int(timebase_ps)} def add_clocker(self, clk, freq_hz, phase_deg=0): args = {"freq_hz": freq_hz, "phase_deg": phase_deg} @@ -70,3 +54,49 @@ class SimConfig(): "No simulation clocker found! Use sim_config.add_clocker() to define one or more clockers." config = self.modules + [self._format_timebase()] return json.dumps(config, indent=4) + +def _calculate_timebase_ps(clockers): + """Calculate timebase for a list of clocker modules + + Clock edges happen at time instants: + t(n) = n * T/2 + P/360 * T + where: T - clock period, P - clock phase [deg] + We must be able to represent clock edges with the timebase B: + t(n) mod B = 0, for all n + In this function checks that: + ((T/2) mod B = 0) AND ((P/360 * T) mod B = 0) + + Currently we allow only for integer periods (in ps), which it's quite restrictive. + """ + # convert to picoseconds, 1ps is our finest timebase for dumping simulation data + periods_ps = [1e12 / c["args"]["freq_hz"] for c in clockers] + phase_shifts_ps = [p * c["args"]["phase_deg"]/360 for c, p in zip(clockers, periods_ps)] + + # calculate timebase as greatest common denominator + timebase_ps = None + for period, phase_shift in zip(periods_ps, phase_shifts_ps): + if timebase_ps is None: + timebase_ps = int(period/2) + timebase_ps = math.gcd(timebase_ps, int(period/2)) + timebase_ps = math.gcd(timebase_ps, int(phase_shift)) + + # check correctness + for clocker, period, phase_shift in zip(clockers, periods_ps, phase_shifts_ps): + def error(description): + return f""" +SimConfig: +{description}: + timebase = {timebase_ps}ps, period = {period}ps, phase_shift = {phase_shift}ps, + clocker[args] = {clocker["args"]} +Adjust clock definitions so that integer multiple of 1ps can be used as a timebase. + """.strip() + + assert int(period) == period, error("Non-integer period") + assert int(phase_shift) == phase_shift, error("Non-integer phase_shift") + + assert (period/2 % timebase_ps) == 0, \ + error("Could not find an integer timebase for period") + assert (phase_shift % timebase_ps) == 0, \ + error("Could not find an integer timebase for phase shift") + + return timebase_ps diff --git a/litex/build/sim/core/modules.h b/litex/build/sim/core/modules.h index b6aa4b46f..961042113 100644 --- a/litex/build/sim/core/modules.h +++ b/litex/build/sim/core/modules.h @@ -4,6 +4,7 @@ #define __MODULE_H_ #include +#include #include "pads.h" struct interface_s { @@ -34,8 +35,24 @@ struct ext_module_list_s { struct ext_module_list_s *next; }; +struct clk_edge_t { + int last_clk; +}; + int litex_sim_file_parse(char *filename, struct module_s **mod, uint64_t *timebase); int litex_sim_load_ext_modules(struct ext_module_list_s **mlist); int litex_sim_find_ext_module(struct ext_module_list_s *first, char *name , struct ext_module_list_s **found); +inline bool clk_pos_edge(struct clk_edge_t *edge, int new_clk) { + bool is_edge = edge->last_clk == 0 && new_clk == 1; + edge->last_clk = new_clk; + return is_edge; +} + +inline bool clk_neg_edge(struct clk_edge_t *edge, int new_clk) { + bool is_edge = edge->last_clk == 1 && new_clk == 0; + edge->last_clk = new_clk; + return is_edge; +} + #endif diff --git a/litex/build/sim/core/modules/clocker/clocker.c b/litex/build/sim/core/modules/clocker/clocker.c index 5a755e1e2..785d0d719 100644 --- a/litex/build/sim/core/modules/clocker/clocker.c +++ b/litex/build/sim/core/modules/clocker/clocker.c @@ -6,7 +6,8 @@ #include "modules.h" struct session_s { - char *sys_clk; + char *clk; + char *name; uint32_t freq_hz; uint16_t phase_deg; }; @@ -79,8 +80,6 @@ static int clocker_parse_args(struct session_s *s, const char *args) fprintf(stderr, "[clocker] \"phase_deg\" must be in range [0, 360)\n"); goto out; } - - printf("[clocker] freq_hz=%u, phase_deg=%u\n", s->freq_hz, s->phase_deg); out: if(args_json) json_object_put(args_json); return ret; @@ -128,12 +127,14 @@ static int clocker_add_pads(void *sess, struct pad_list_s *plist) } pads = plist->pads; - if(!strcmp(plist->name, "sys_clk")) { - litex_sim_module_pads_get(pads, "sys_clk", (void**)&s->sys_clk); + ret = litex_sim_module_pads_get(pads, plist->name, (void**)&s->clk); + if (ret != RC_OK) { + goto out; } - *s->sys_clk=0; - + s->name = plist->name; + *s->clk=0; + printf("[clocker] %s: freq_hz=%u, phase_deg=%u\n", s->name, s->freq_hz, s->phase_deg); out: return ret; } @@ -149,9 +150,9 @@ static int clocker_tick(void *sess, uint64_t time_ps) // phase-shifted time relative to start of current period uint64_t rel_time_ps = (time_ps - phase_shift_ps) % period_ps; if (rel_time_ps < (period_ps/2)) { - *s->sys_clk = 1; + *s->clk = 1; } else { - *s->sys_clk = 0; + *s->clk = 0; } return 0; diff --git a/litex/build/sim/core/modules/ethernet/ethernet.c b/litex/build/sim/core/modules/ethernet/ethernet.c index 30aca3f3b..3896cf9c7 100644 --- a/litex/build/sim/core/modules/ethernet/ethernet.c +++ b/litex/build/sim/core/modules/ethernet/ethernet.c @@ -200,14 +200,16 @@ out: return ret; } -static int ethernet_tick(void *sess) +static int ethernet_tick(void *sess, uint64_t time_ps) { + static struct clk_edge_t edge; char c; struct session_s *s = (struct session_s*)sess; struct eth_packet_s *pep; - if(*s->sys_clk == 0) + if(!clk_pos_edge(&edge, *s->sys_clk)) { return RC_OK; + } *s->tx_ready = 1; if(*s->tx_valid == 1) { diff --git a/litex/build/sim/core/modules/jtagremote/jtagremote.c b/litex/build/sim/core/modules/jtagremote/jtagremote.c index 6a42b3756..87c7c1598 100644 --- a/litex/build/sim/core/modules/jtagremote/jtagremote.c +++ b/litex/build/sim/core/modules/jtagremote/jtagremote.c @@ -208,14 +208,16 @@ out: return ret; } -static int jtagremote_tick(void *sess) +static int jtagremote_tick(void *sess, uint64_t time_ps) { + static struct clk_edge_t edge; char c, val; int ret = RC_OK; struct session_s *s = (struct session_s*)sess; - if(*s->sys_clk == 0) + if(!clk_pos_edge(&edge, *s->sys_clk)) { return RC_OK; + } s->cntticks++; if(s->cntticks % 10) diff --git a/litex/build/sim/core/modules/serial2console/serial2console.c b/litex/build/sim/core/modules/serial2console/serial2console.c index 114947f5f..678024525 100644 --- a/litex/build/sim/core/modules/serial2console/serial2console.c +++ b/litex/build/sim/core/modules/serial2console/serial2console.c @@ -145,10 +145,11 @@ out: return ret; } -static int serial2console_tick(void *sess) { +static int serial2console_tick(void *sess, uint64_t time_ps) { + static struct clk_edge_t edge; struct session_s *s = (struct session_s*)sess; - if(*s->sys_clk == 0) { + if(!clk_pos_edge(&edge, *s->sys_clk)) { return RC_OK; } diff --git a/litex/build/sim/core/modules/serial2tcp/serial2tcp.c b/litex/build/sim/core/modules/serial2tcp/serial2tcp.c index 8691d175b..990228603 100644 --- a/litex/build/sim/core/modules/serial2tcp/serial2tcp.c +++ b/litex/build/sim/core/modules/serial2tcp/serial2tcp.c @@ -210,14 +210,16 @@ out: return ret; } -static int serial2tcp_tick(void *sess) +static int serial2tcp_tick(void *sess, uint64_t time_ps) { + static struct clk_edge_t edge; char c; int ret = RC_OK; struct session_s *s = (struct session_s*)sess; - if(*s->sys_clk == 0) + if(!clk_pos_edge(&edge, *s->sys_clk)) { return RC_OK; + } *s->tx_ready = 1; if(s->fd && *s->tx_valid) { diff --git a/litex/build/sim/core/modules/spdeeprom/spdeeprom.c b/litex/build/sim/core/modules/spdeeprom/spdeeprom.c index 1d1584fee..0a80aeff2 100644 --- a/litex/build/sim/core/modules/spdeeprom/spdeeprom.c +++ b/litex/build/sim/core/modules/spdeeprom/spdeeprom.c @@ -65,7 +65,7 @@ struct session_s { static int spdeeprom_start(); static int spdeeprom_new(void **sess, char *args); static int spdeeprom_add_pads(void *sess, struct pad_list_s *plist); -static int spdeeprom_tick(void *sess); +static int spdeeprom_tick(void *sess, uint64_t time_ps); // EEPROM simulation static void fsm_tick(struct session_s *s); static enum SerialState state_serial_next(struct session_s *s); @@ -162,15 +162,16 @@ out: return ret; } -static int spdeeprom_tick(void *sess) +static int spdeeprom_tick(void *sess, uint64_t time_ps) { + static struct clk_edge_t edge; struct session_s *s = (struct session_s*) sess; if (s->sda_in == 0 || s->sda_out == 0 || s->scl == 0) { return RC_OK; } - if(*s->sys_clk == 0) { + if(!clk_pos_edge(&edge, *s->sys_clk)) { return RC_OK; } diff --git a/litex/build/sim/core/modules/xgmii_ethernet/xgmii_ethernet.c b/litex/build/sim/core/modules/xgmii_ethernet/xgmii_ethernet.c index 4474e1f2a..d8152e96c 100644 --- a/litex/build/sim/core/modules/xgmii_ethernet/xgmii_ethernet.c +++ b/litex/build/sim/core/modules/xgmii_ethernet/xgmii_ethernet.c @@ -233,12 +233,13 @@ unsigned int g_mask = 0xff; unsigned int g_idle = 0x07070707; #endif -static int xgmii_ethernet_tick(void *sess) +static int xgmii_ethernet_tick(void *sess, uint64_t time_ps) { + static struct clk_edge_t edge; struct session_s *s = (struct session_s*)sess; struct eth_packet_s *pep; - if(*s->sys_clk == 0) { + if(!clk_pos_edge(&edge, *s->sys_clk)) { s->preamble=0; return RC_OK; } diff --git a/litex/build/sim/core/sim.c b/litex/build/sim/core/sim.c index 98ffc91fb..33e6edb34 100644 --- a/litex/build/sim/core/sim.c +++ b/litex/build/sim/core/sim.c @@ -186,7 +186,7 @@ static void cb(int sock, short which, void *arg) s->module->tick(s->session, sim_time_ps); } - litex_sim_eval(vsim); + litex_sim_eval(vsim, sim_time_ps); litex_sim_dump(); for(s = sesslist; s; s=s->next) @@ -195,7 +195,7 @@ static void cb(int sock, short which, void *arg) s->module->tick(s->session, sim_time_ps); } - sim_time_ps = litex_sim_increment_time(timebase_ps); + sim_time_ps += timebase_ps; if (litex_sim_got_finish()) { event_base_loopbreak(base); diff --git a/litex/build/sim/core/veril.cpp b/litex/build/sim/core/veril.cpp index 2637fc5b8..b65aa8b67 100644 --- a/litex/build/sim/core/veril.cpp +++ b/litex/build/sim/core/veril.cpp @@ -21,15 +21,11 @@ uint64_t tfp_start; uint64_t tfp_end; uint64_t main_time = 0; -extern "C" void litex_sim_eval(void *vsim) +extern "C" void litex_sim_eval(void *vsim, uint64_t time_ps) { Vsim *sim = (Vsim*)vsim; sim->eval(); -} - -extern "C" uint64_t litex_sim_increment_time(unsigned long dt_ps) { - main_time += dt_ps; - return main_time; + main_time = time_ps; } extern "C" void litex_sim_init_cmdargs(int argc, char *argv[]) diff --git a/litex/build/sim/core/veril.h b/litex/build/sim/core/veril.h index 57e710f06..2daac15db 100644 --- a/litex/build/sim/core/veril.h +++ b/litex/build/sim/core/veril.h @@ -7,17 +7,15 @@ #ifdef __cplusplus extern "C" void litex_sim_init_cmdargs(int argc, char *argv[]); -extern "C" void litex_sim_eval(void *vsim); -extern "C" uint64_t litex_sim_increment_time(unsigned long dt_ps); -extern "C" void litex_sim_init_tracer(void *vsim, long start, long end) +extern "C" void litex_sim_eval(void *vsim, uint64_t time_ps); +extern "C" void litex_sim_init_tracer(void *vsim, long start, long end); extern "C" void litex_sim_tracer_dump(); extern "C" int litex_sim_got_finish(); #if VM_COVERAGE extern "C" void litex_sim_coverage_dump(); #endif #else -void litex_sim_eval(void *vsim); -uint64_t litex_sim_increment_time(unsigned long dt_ps); +void litex_sim_eval(void *vsim, uint64_t time_ps); void litex_sim_init_tracer(void *vsim); void litex_sim_tracer_dump(); int litex_sim_got_finish();