From c1ae7e596c3e11db309659c8bfc197ebdeec14c5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C4=99drzej=20Boczar?= Date: Mon, 3 Aug 2020 16:52:54 +0200 Subject: [PATCH] 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 1cad495e..58a2d384 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 f324126e..b6aa4b46 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 cfd2f103..5a755e1e 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 aec6311f..98ffc91f 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 5f74ecc4..2637fc5b 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 d25a3e1c..57e710f0 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 24c8b914..c07cb3a9 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 -------------------------------------------------------------------------------- -- 2.30.2