Skip to content

Commit 28dcb50

Browse files
committed
wip
1 parent 630ae08 commit 28dcb50

File tree

2 files changed

+264
-15
lines changed

2 files changed

+264
-15
lines changed

chipflow_lib/platforms/sim.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
# SPDX-License-Identifier: BSD-2-Clause
22

33
import os
4+
import sys
45
from pathlib import Path
56

67
from amaranth import *
8+
from amaranth.lib import wiring, io
79
from amaranth.back import rtlil
810

11+
from .. import ChipFlowError
12+
from .silicon import SiliconPlatformPort, FFSynchronizer
13+
from .utils import load_pinlock, Port
914

1015
__all__ = ["SimPlatform"]
1116

@@ -94,3 +99,80 @@ def build(self, e):
9499
print("hierarchy -top sim_top", file=yosys_file)
95100
# FIXME: use the default -O6 (workaround for YosysHQ/yosys#4227)
96101
print("write_cxxrtl -O4 -header sim_soc.cc", file=yosys_file)
102+
103+
def instantiate_ports(self, m: Module):
104+
if hasattr(self, "_pinlock"):
105+
return
106+
107+
pinlock = load_pinlock()
108+
for component, iface in pinlock.port_map.items():
109+
for k, v in iface.items():
110+
for name, port in v.items():
111+
self._ports[port.port_name] = SiliconPlatformPort(component, name, port)
112+
113+
for clock, name in self._config["chipflow"]["clocks"].items():
114+
if name not in pinlock.package.clocks:
115+
raise ChipFlowError("Unable to find clock {name} in pinlock")
116+
117+
port_data = pinlock.package.clocks[name]
118+
port = SiliconPlatformPort(component, name, port_data, invert=True)
119+
self._ports[name] = port
120+
121+
if clock == 'default':
122+
clock = 'sync'
123+
setattr(m.domains, clock, ClockDomain(name=clock))
124+
clk_buffer = io.Buffer("i", port)
125+
setattr(m.submodules, "clk_buffer_" + clock, clk_buffer)
126+
m.d.comb += ClockSignal().eq(clk_buffer.i)
127+
128+
for reset, name in self._config["chipflow"]["resets"].items():
129+
port_data = pinlock.package.resets[name]
130+
port = SiliconPlatformPort(component, name, port_data)
131+
self._ports[name] = port
132+
rst_buffer = io.Buffer("i", port)
133+
setattr(m.submodules, reset, rst_buffer)
134+
setattr(m.submodules, reset + "_sync", FFSynchronizer(rst_buffer.i, ResetSignal()))
135+
136+
self._pinlock = pinlock
137+
138+
139+
140+
141+
VARIABLES = {
142+
"OUTPUT_DIR": "./build/sim",
143+
"ZIG_CXX": f"{sys.executable} -m ziglang c++",
144+
"CXXFLAGS": "-O3 -g -std=c++17 -Wno-array-bounds -Wno-shift-count-overflow -fbracket-depth=1024",
145+
"INCLUDES": "-I {OUTPUT_DIR} -I {COMMON_DIR} -I {COMMON_DIR}/vendor -I {RUNTIME_DIR}",
146+
}
147+
DOIT_CONFIG = {'action_string_formatting': 'both'}
148+
149+
BUILD_SIM = {
150+
"name": "build_sim",
151+
"actions": [
152+
"{ZIG_CXX} {CXXFLAGS} {INCLUDES} -o {OUTPUT_DIR}/sim_soc{EXE} "
153+
"{OUTPUT_DIR}/sim_soc.cc {SOURCE_DIR}/main.cc {COMMON_DIR}/models.cc"
154+
],
155+
"targets": [
156+
"{OUTPUT_DIR}/sim_soc{EXE}"
157+
],
158+
"file_dep": [
159+
"{OUTPUT_DIR}/sim_soc.cc",
160+
"{OUTPUT_DIR}/sim_soc.h",
161+
"{SOURCE_DIR}/main.cc",
162+
"{COMMON_DIR}/models.cc",
163+
"{COMMON_DIR}/models.h",
164+
"{COMMON_DIR}/vendor/nlohmann/json.hpp",
165+
"{COMMON_DIR}/vendor/cxxrtl/cxxrtl_server.h",
166+
],
167+
}
168+
169+
SIM_CXXRTL = {
170+
"name": "sim_cxxrtl",
171+
"actions": ["cd {OUTPUT_DIR} && pdm run yowasp-yosys sim_soc.ys"],
172+
"targets": ["{OUTPUT_DIR}/sim_soc.cc", "{OUTPUT_DIR}/sim_soc.h"],
173+
"file_dep": ["{OUTPUT_DIR}/sim_soc.ys", "{OUTPUT_DIR}/sim_soc.il"],
174+
}
175+
176+
TASKS = [BUILD_SIM, SIM_CXXRTL]
177+
178+

chipflow_lib/steps/sim.py

Lines changed: 182 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,194 @@
1-
# SPDX-License-Identifier: BSD-2-Clause
1+
import os
2+
import importlib.resources
3+
import logging
24

3-
from doit.cmd_base import ModuleTaskLoader
5+
from contextlib import contextmanager
6+
from pathlib import Path
7+
from pprint import pformat
8+
9+
from doit.cmd_base import TaskLoader2, loader
410
from doit.doit_cmd import DoitMain
11+
from doit.task import dict_to_task
12+
13+
from amaranth import *
14+
from amaranth.lib import wiring, io
15+
from amaranth.back import rtlil
516

17+
from .. import ChipFlowError
618
from . import StepBase
19+
from ..platforms import SiliconPlatform, top_interfaces, load_pinlock
20+
from ..platforms.silicon import SiliconPlatformPort, FFSynchronizer
21+
from ..platforms.utils import IOSignature, load_pinlock, Port
22+
from ..platforms.sim import VARIABLES, TASKS, DOIT_CONFIG
723

8-
class SimStep(StepBase):
9-
"""Simulate the design."""
1024

11-
doit_build_module = None
25+
EXE = ".exe" if os.name == "nt" else ""
26+
logger = logging.getLogger(__name__)
27+
28+
29+
@contextmanager
30+
def common():
31+
chipflow_lib = importlib.resources.files('chipflow_lib')
32+
common = chipflow_lib.joinpath('common', 'sim')
33+
with importlib.resources.as_file(common) as f:
34+
yield f
35+
36+
@contextmanager
37+
def source():
38+
sourcedir = importlib.resources.files("mcu_soc")
39+
sim_src = sourcedir.joinpath('design','sim')
40+
with importlib.resources.as_file(sim_src) as f:
41+
yield f
42+
43+
@contextmanager
44+
def runtime():
45+
yowasp = importlib.resources.files("yowasp_yosys")
46+
runtime = yowasp.joinpath('share', 'include', 'backends', 'cxxrtl', 'runtime')
47+
with importlib.resources.as_file(runtime) as f:
48+
yield f
49+
50+
51+
class ContextTaskLoader(TaskLoader2):
52+
def __init__(self, config, tasks, context):
53+
self.config = config
54+
self.tasks = tasks
55+
self.subs = context
56+
super().__init__()
57+
58+
def load_doit_config(self):
59+
return loader.load_doit_config(self.config)
60+
61+
def load_tasks(self, cmd, pos_args):
62+
task_list = []
63+
# substitute
64+
for task in self.tasks:
65+
d = {}
66+
for k,v in task.items():
67+
match v:
68+
case str():
69+
d[k.format(**self.subs)] = v.format(**self.subs)
70+
case list():
71+
d[k.format(**self.subs)] = [i.format(**self.subs) for i in v]
72+
case _:
73+
raise ChipFlowError("Unexpected task definition")
74+
print(f"adding task: {pformat(d)}")
75+
task_list.append(dict_to_task(d))
76+
return task_list
77+
78+
79+
class SimPlatform:
80+
81+
def __init__(self, config):
82+
self.build_dir = os.path.join(os.environ['CHIPFLOW_ROOT'], 'build', 'sim')
83+
self.extra_files = dict()
84+
self._ports = {}
85+
self._config = config
86+
87+
def add_file(self, filename, content):
88+
if not isinstance(content, (str, bytes)):
89+
content = content.read()
90+
self.extra_files[filename] = content
91+
92+
def build(self, e):
93+
Path(self.build_dir).mkdir(parents=True, exist_ok=True)
1294

13-
def __init__(self, config, platform):
14-
self.platform = platform
95+
output = rtlil.convert(e, name="sim_top", ports=self._ports, platform=self)
1596

16-
def build_cli_parser(self, parser):
17-
pass
97+
top_rtlil = Path(self.build_dir) / "sim_soc.il"
98+
with open(top_rtlil, "w") as rtlil_file:
99+
rtlil_file.write(output)
100+
top_ys = Path(self.build_dir) / "sim_soc.ys"
101+
with open(top_ys, "w") as yosys_file:
102+
for extra_filename, extra_content in self.extra_files.items():
103+
extra_path = Path(self.build_dir) / extra_filename
104+
with open(extra_path, "w") as extra_file:
105+
extra_file.write(extra_content)
106+
if extra_filename.endswith(".il"):
107+
print(f"read_rtlil {extra_path}", file=yosys_file)
108+
else:
109+
# FIXME: use -defer (workaround for YosysHQ/yosys#4059)
110+
print(f"read_verilog {extra_path}", file=yosys_file)
111+
print("read_rtlil sim_soc.il", file=yosys_file)
112+
print("hierarchy -top sim_top", file=yosys_file)
113+
print("write_cxxrtl -header sim_soc.cc", file=yosys_file)
18114

19-
def run_cli(self, args):
20-
self.build()
115+
def instantiate_ports(self, m: Module):
116+
if hasattr(self, "_pinlock"):
117+
return
21118

22-
def doit_build(self):
23-
DoitMain(ModuleTaskLoader(self.doit_build_module)).run(["build_sim"])
119+
pinlock = load_pinlock()
120+
for component, iface in pinlock.port_map.items():
121+
for k, v in iface.items():
122+
for name, port in v.items():
123+
self._ports[port.port_name] = SiliconPlatformPort(component, name, port)
124+
125+
for clock, name in self._config["chipflow"]["clocks"].items():
126+
if name not in pinlock.package.clocks:
127+
raise ChipFlowError("Unable to find clock {name} in pinlock")
128+
129+
port_data = pinlock.package.clocks[name]
130+
port = SiliconPlatformPort(component, name, port_data, invert=True)
131+
self._ports[name] = port
132+
133+
if clock == 'default':
134+
clock = 'sync'
135+
setattr(m.domains, clock, ClockDomain(name=clock))
136+
clk_buffer = io.Buffer("i", port)
137+
setattr(m.submodules, "clk_buffer_" + clock, clk_buffer)
138+
m.d.comb += ClockSignal().eq(clk_buffer.i)
139+
140+
for reset, name in self._config["chipflow"]["resets"].items():
141+
port_data = pinlock.package.resets[name]
142+
port = SiliconPlatformPort(component, name, port_data)
143+
self._ports[name] = port
144+
rst_buffer = io.Buffer("i", port)
145+
setattr(m.submodules, reset, rst_buffer)
146+
setattr(m.submodules, reset + "_sync", FFSynchronizer(rst_buffer.i, ResetSignal()))
147+
148+
self._pinlock = pinlock
149+
150+
151+
152+
class SimStep(StepBase):
153+
def __init__(self, config):
154+
self._platform = SimPlatform(config)
155+
self._config = config
24156

25157
def build(self):
26-
self.platform.build()
27-
self.doit_build()
158+
m = Module()
159+
self._platform.instantiate_ports(m)
160+
161+
## heartbeat led (to confirm clock/reset alive)
162+
#if ("debug" in self._config["chipflow"]["silicon"] and
163+
# self._config["chipflow"]["silicon"]["debug"]["heartbeat"]):
164+
# heartbeat_ctr = Signal(23)
165+
# m.d.sync += heartbeat_ctr.eq(heartbeat_ctr + 1)
166+
# m.d.comb += platform.request("heartbeat").o.eq(heartbeat_ctr[-1])
167+
168+
top, interfaces = top_interfaces(self._config)
169+
logger.debug(f"SiliconTop top = {top}, interfaces={interfaces}")
170+
171+
for n, t in top.items():
172+
setattr(m.submodules, n, t)
173+
174+
for component, iface in self._platform._pinlock.port_map.items():
175+
for iface_name, member, in iface.items():
176+
for name, port in member.items():
177+
iface = getattr(top[component], iface_name)
178+
wire = (iface if isinstance(iface.signature, IOSignature)
179+
else getattr(iface, name))
180+
self._platform._ports[port.port_name].wire(m, wire)
181+
182+
183+
self._platform.build(m)
184+
with common() as common_dir, source() as source_dir, runtime() as runtime_dir:
185+
context = {
186+
"COMMON_DIR": common_dir,
187+
"SOURCE_DIR": source_dir,
188+
"RUNTIME_DIR": runtime_dir,
189+
"EXE": EXE,
190+
}
191+
for k,v in VARIABLES.items():
192+
context[k] = v.format(**context)
193+
print(f"substituting:\n{pformat(context)}")
194+
DoitMain(ContextTaskLoader(DOIT_CONFIG, TASKS, context)).run(["build_sim"])

0 commit comments

Comments
 (0)