build/sim: allow for arbitrary clocks generation using clockers
[litex.git] / litex / build / sim / config.py
1 # This file is Copyright (c) 2017 Pierre-Olivier Vauboin <po@lambdaconcept>
2 # This file is Copyright (c) 2018 Florent Kermarrec <florent@enjoy-digital.fr>
3 # License: BSD
4
5 import json
6 import math
7
8 class SimConfig():
9 def __init__(self, timebase_ps=None):
10 self.modules = []
11 self.timebase_ps = timebase_ps
12
13 def _format_interfaces(self, interfaces):
14 if not isinstance(interfaces, list):
15 interfaces = [interfaces]
16 new = []
17 for it in interfaces:
18 obj = it
19 if isinstance(it, tuple):
20 name, index = it
21 obj = {"name": name, "index": index}
22 new.append(obj)
23 return new
24
25 def _format_timebase(self):
26 timebase_ps = self.timebase_ps
27 if timebase_ps is None:
28 timebase_ps = self._get_timebase_ps()
29 return {"timebase": int(timebase_ps)}
30
31 def _get_timebase_ps(self):
32 clockers = [m for m in self.modules if m["module"] == "clocker"]
33 periods_ps = [1e12 / m["args"]["freq_hz"] for m in clockers]
34 # timebase is half of the shortest period
35 for p in periods_ps:
36 assert round(p/2) == int(p//2), "Period cannot be represented: {}".format(p)
37 half_period = [int(p//2) for p in periods_ps]
38 # find greatest common denominator
39 gcd = half_period[0]
40 for p in half_period[1:]:
41 gcd = math.gcd(gcd, p)
42 assert gcd >= 1
43 return gcd
44
45 def add_clocker(self, clk, freq_hz, phase_deg=0):
46 args = {"freq_hz": freq_hz, "phase_deg": phase_deg}
47 self.add_module("clocker", [], clocks=clk, tickfirst=True, args=args)
48
49 def add_module(self, name, interfaces, clocks="sys_clk", args=None, tickfirst=False):
50 interfaces = self._format_interfaces(interfaces)
51 interfaces += self._format_interfaces(clocks)
52 newmod = {
53 "module": name,
54 "interface": interfaces,
55 }
56 if args:
57 newmod.update({"args": args})
58 if tickfirst:
59 newmod.update({"tickfirst": tickfirst})
60 self.modules.append(newmod)
61
62 def has_module(self, name):
63 for module in self.modules:
64 if module["module"] == name:
65 return True
66 return False
67
68 def get_json(self):
69 assert "clocker" in (m["module"] for m in self.modules), \
70 "No simulation clocker found! Use sim_config.add_clocker() to define one or more clockers."
71 config = self.modules + [self._format_timebase()]
72 return json.dumps(config, indent=4)