build/sim: improve timebase calculation (strict checks) and update modules
authorJędrzej Boczar <jboczar@antmicro.com>
Tue, 4 Aug 2020 12:00:58 +0000 (14:00 +0200)
committerJędrzej Boczar <jboczar@antmicro.com>
Tue, 4 Aug 2020 12:00:58 +0000 (14:00 +0200)
12 files changed:
litex/build/sim/config.py
litex/build/sim/core/modules.h
litex/build/sim/core/modules/clocker/clocker.c
litex/build/sim/core/modules/ethernet/ethernet.c
litex/build/sim/core/modules/jtagremote/jtagremote.c
litex/build/sim/core/modules/serial2console/serial2console.c
litex/build/sim/core/modules/serial2tcp/serial2tcp.c
litex/build/sim/core/modules/spdeeprom/spdeeprom.c
litex/build/sim/core/modules/xgmii_ethernet/xgmii_ethernet.c
litex/build/sim/core/sim.c
litex/build/sim/core/veril.cpp
litex/build/sim/core/veril.h

index 58a2d384b1c280bd372e47f364799a9f7871cd6a..43eeb1565c6f2cbcc8c0ddf74f99cf19bec79bb0 100644 (file)
@@ -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
index b6aa4b46fcee3cd1b8912090ab1556f476194ae6..96104211345fcace017da2e9ec40d4a47181f9e6 100644 (file)
@@ -4,6 +4,7 @@
 #define __MODULE_H_
 
 #include <stdint.h>
+#include <stdbool.h>
 #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
index 5a755e1e2d4fca483fba83faf5ecb197d0285e52..785d0d7197a039e4c479f1b8ac15a398637d857f 100644 (file)
@@ -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;
index 30aca3f3be2401b6f5149fb52b9da46867a0100f..3896cf9c7762b00fdc541243fcbc2ea25adb8f62 100644 (file)
@@ -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) {
index 6a42b3756536f4d6121a4945b204c395ec88b908..87c7c159842231bba2c5e94e1e054f71c5d90e36 100644 (file)
@@ -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)
index 114947f5f7587d4f0762189cd121e9bb9ad7c548..6780245252468bcbee646bd61060fdd4248944e3 100644 (file)
@@ -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;
   }
 
index 8691d175b5b3ad01db65a3b41754f93bc3425a35..9902286036ceea50ff1be939420dc1e3d9b038a9 100644 (file)
@@ -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) {
index 1d1584fee33e03e14e25262eba9f429685544a5b..0a80aeff22672c21b7d9b8f1c4c3a8efd3d6c32f 100644 (file)
@@ -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;
   }
 
index 4474e1f2a9a9a561a99f128f667619016c64bcaf..d8152e96c700807c74d4f9d7c91269c182e946f3 100644 (file)
@@ -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;
   }
index 98ffc91fb2cd8c8ffb8235407fff2532360f853b..33e6edb346348ebd19e82a78a6945bd5c4efe6e1 100644 (file)
@@ -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);
index 2637fc5b8f1dd9350c63d036824505e2d0889f8b..b65aa8b6750e52ee3b92b3a5878e4fe195a58b84 100644 (file)
@@ -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[])
index 57e710f06c6094205861172eab39db714a9cd4e0..2daac15dbaeb0c18c9075884b1cbb74bce39d56a 100644 (file)
@@ -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();