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):
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}
"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
#define __MODULE_H_
#include <stdint.h>
+#include <stdbool.h>
#include "pads.h"
struct interface_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
#include "modules.h"
struct session_s {
- char *sys_clk;
+ char *clk;
+ char *name;
uint32_t freq_hz;
uint16_t phase_deg;
};
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;
}
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;
}
// 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;
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) {
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)
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;
}
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) {
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);
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;
}
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;
}
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)
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);
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[])
#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();