build/sim: allow for arbitrary clocks generation using clockers
authorJędrzej Boczar <jboczar@antmicro.com>
Mon, 3 Aug 2020 14:52:54 +0000 (16:52 +0200)
committerJędrzej Boczar <jboczar@antmicro.com>
Mon, 3 Aug 2020 15:06:38 +0000 (17:06 +0200)
litex/build/sim/config.py
litex/build/sim/core/modules.h
litex/build/sim/core/modules/clocker/clocker.c
litex/build/sim/core/sim.c
litex/build/sim/core/veril.cpp
litex/build/sim/core/veril.h
litex/tools/litex_sim.py

index 1cad495e25e3106323ecf863720e498b931ee767..58a2d384b1c280bd372e47f364799a9f7871cd6a 100644 (file)
@@ -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)
index f324126ef2badb9d5bc8c2bece6d1149899ce57c..b6aa4b46fcee3cd1b8912090ab1556f476194ae6 100644 (file)
@@ -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 {
index cfd2f10398db04cf8c28de66825f8e6a5f279cca..5a755e1e2d4fca483fba83faf5ecb197d0285e52 100644 (file)
@@ -1,11 +1,14 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <json-c/json.h>
 #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;
 }
 
index aec6311fde63cbb42df409e7f1688e37f064b9d9..98ffc91fb2cd8c8ffb8235407fff2532360f853b 100644 (file)
@@ -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);
index 5f74ecc4c17e75d5d26fb295cda1634dd203dd17..2637fc5b8f1dd9350c63d036824505e2d0889f8b 100644 (file)
@@ -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[])
index d25a3e1cbfc6dd15ecbc08f55f1198eaf9800311..57e710f06c6094205861172eab39db714a9cd4e0 100644 (file)
@@ -3,10 +3,12 @@
 #ifndef __VERIL_H_
 #define __VERIL_H_
 
+#include <stdint.h>
+
 #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();
index 24c8b91444edf6e4253e6e2e7fd6d26d4d66a8d6..c07cb3a98f0679dc2b7d07714465165d94956f84 100755 (executable)
@@ -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 --------------------------------------------------------------------------------