sim: add CXXRTL integration.
[lambdasoc.git] / lambdasoc / sim / platform.py
1 import sys
2
3 from collections import OrderedDict
4 from importlib import import_module, resources
5
6 from nmigen import *
7 from nmigen.build import *
8
9
10 __all__ = ["CXXRTLPlatform"]
11
12
13 class CXXRTLPlatform(TemplatedPlatform):
14 device = "cxxrtl"
15 default_clk = "clk"
16 default_rst = "rst"
17 resources = [
18 Resource("clk", 0, Pins("clk", dir="i"), Clock(5e6)),
19 Resource("rst", 0, Pins("rst", dir="i")),
20 ]
21 connectors = []
22 toolchain = None # selected when creating platform
23
24 file_templates = {
25 **TemplatedPlatform.build_script_templates,
26 "{{name}}.il": r"""
27 # {{autogenerated}}
28 {{emit_rtlil()}}
29 """,
30 "{{name}}.ys": r"""
31 # {{autogenerated}}
32 {% for file in platform.iter_files(".v") -%}
33 read_verilog {{get_override("read_verilog_opts")|options}} {{file}}
34 {% endfor %}
35 {% for file in platform.iter_files(".sv") -%}
36 read_verilog {{get_override("read_verilog_opts")|options}} {{file}}
37 {% endfor %}
38 {% for file in platform.iter_files(".il") -%}
39 read_ilang {{file}}
40 {% endfor %}
41 read_ilang {{name}}.il
42 delete w:$verilog_initial_trigger
43 {{get_override("script_after_read")|default("# (script_after_read placeholder)")}}
44 write_cxxrtl {{get_override("write_cxxrtl_opts")|options}} -header {{name}}.cc
45 """,
46 "{{name}}.mk": r"""
47 # {{autogenerated}}
48 CC = $(CXX)
49 CPPFLAGS = -Ilambdasoc.sim/include -include {{name}}.h -MMD \
50 -I{{get_override("yosys_include_dir")|default("/usr/local/share/yosys/include")}} \
51 {{get_override("additional_cpp_flags")|default("# (additional_cpp_flags placeholder)")}}
52 CXXFLAGS = {{get_override("cxx_flags")|default("-std=c++14 -Wall -O3 -mtune=native")}}
53 LDFLAGS = {{get_override("ld_flags")|default("# (ld_flags placeholder)")}}
54 LDLIBS = {{get_override("ld_libs")|default("# (ld_libs placeholder)")}}
55
56 SRC = {{name}}.cc \
57 {% for file in platform.iter_files(".cc") %}
58 {{file}} \
59 {% endfor %}
60
61 OBJS = $(SRC:.cc=.o)
62 DEPS = $(OBJS:.o=.d)
63
64 .PHONY: all clean
65
66 all: {{name}}_driver
67
68 -include DEPS
69
70 {% set tab = ""|indent(width="\t", first=True) -%}
71
72 {% for file in platform.iter_files(".v", ".sv", ".il") %}
73 {{name}}.cc {{name}}.h: {{file}}
74 {% endfor %}
75 {{name}}.cc {{name}}.h: {{name}}.ys {{name}}.il
76 {{tab}}$(YOSYS) -s {{name}}.ys
77
78 {{name}}_driver.o: CPPFLAGS += -DCXXRTL_TOP='cxxrtl_design::p_{{name}}'
79
80 $(OBJS): {{name}}.h
81
82 {{name}}_driver: $(OBJS)
83
84 clean:
85 {{tab}}rm -f {{name}}.cc {{name}}.h
86 {{tab}}rm -f $(OBJS) $(DEPS)
87 {{tab}}rm -f {{name}}_driver
88 """,
89 }
90
91 # GCC templates
92
93 _gcc_required_tools = [
94 "yosys",
95 "g++",
96 "make",
97 ]
98 _gcc_command_templates = [
99 r"""
100 YOSYS={{invoke_tool("yosys")}}
101 CXX={{invoke_tool("g++")}}
102 {{invoke_tool("make")}} -f {{name}}.mk
103 """,
104 ]
105
106 # Clang templates
107
108 _clang_required_tools = [
109 "yosys",
110 "clang++",
111 "make",
112 ]
113 _clang_command_templates = [
114 r"""
115 YOSYS={{invoke_tool("yosys")}}
116 CXX={{invoke_tool("clang++")}}
117 {{invoke_tool("make")}} -f {{name}}.mk
118 """,
119 ]
120
121 def __init__(self, *, toolchain="clang"):
122 super().__init__()
123
124 assert toolchain in ("gcc", "clang")
125 self.toolchain = toolchain
126
127 @property
128 def required_tools(self):
129 if self.toolchain == "gcc":
130 return self._gcc_required_tools
131 if self.toolchain == "clang":
132 return self._clang_required_tools
133 assert False
134
135 @property
136 def command_templates(self):
137 if self.toolchain == "gcc":
138 return self._gcc_command_templates
139 if self.toolchain == "clang":
140 return self._clang_command_templates
141 assert False
142
143 def create_missing_domain(self, name):
144 if name == "sync":
145 m = Module()
146 clk_i = self.request(self.default_clk).i
147 rst_i = self.request(self.default_rst).i
148 m.domains.sync = ClockDomain("sync")
149 m.d.comb += [
150 ClockSignal("sync").eq(clk_i),
151 ResetSignal("sync").eq(rst_i),
152 ]
153 return m
154
155 def toolchain_prepare(self, fragment, name="top", blackboxes=None, **kwargs):
156 if blackboxes is None:
157 blackboxes = OrderedDict()
158
159 def get_cxxrtl_src(package):
160 cxxrtl_src = OrderedDict()
161 assert hasattr(package, "cxxrtl_src_files")
162 for module, subdirs, src_file in package.cxxrtl_src_files:
163 src_contents = resources.read_text(module, src_file)
164 src_path = "/".join((package.__name__, *subdirs, src_file))
165 cxxrtl_src[src_path] = src_contents
166 return cxxrtl_src
167
168 cxxrtl_src = {
169 f"{name}_driver.cc": resources.read_text(__package__, "top_driver.cc"),
170 **get_cxxrtl_src(sys.modules[__package__]),
171 }
172
173 for blackbox_name, driver_name in blackboxes.items():
174 blackbox = import_module(blackbox_name)
175 blackbox_contents = resources.read_text(blackbox, "blackbox.v")
176 blackbox_path = f"{blackbox_name}/blackbox.v"
177 self.add_file(blackbox_path, blackbox_contents)
178
179 driver = import_module(f"{blackbox_name}.drivers.{driver_name}")
180 cxxrtl_src.update(get_cxxrtl_src(driver))
181
182 for cxxrtl_src_path, cxxrtl_src_contents in cxxrtl_src.items():
183 self.add_file(cxxrtl_src_path, cxxrtl_src_contents)
184
185 return super().toolchain_prepare(fragment, name=name, **kwargs)