Skip to content

Commit 64e8593

Browse files
committed
Update sim infrastructure so it all lives in chipflow-lib
1 parent 630ae08 commit 64e8593

File tree

8 files changed

+247
-79
lines changed

8 files changed

+247
-79
lines changed

chipflow_lib/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def _ensure_chipflow_root():
4444

4545
if os.environ["CHIPFLOW_ROOT"] not in sys.path:
4646
sys.path.append(os.environ["CHIPFLOW_ROOT"])
47-
_ensure_chipflow_root.root = os.environ["CHIPFLOW_ROOT"]
47+
_ensure_chipflow_root.root = Path(os.environ["CHIPFLOW_ROOT"]).absolute()
4848
return _ensure_chipflow_root.root
4949

5050

chipflow_lib/platforms/silicon.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ def pins(self):
139139
def invert(self):
140140
return self._invert
141141

142+
142143
def __len__(self):
143144
if self._direction is io.Direction.Input:
144145
return len(self._i)

chipflow_lib/platforms/sim.py

Lines changed: 102 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,144 @@
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 io
79
from amaranth.back import rtlil
10+
from amaranth.hdl._ir import PortDirection
11+
from amaranth.lib.cdc import FFSynchronizer
812

13+
from .. import ChipFlowError
14+
from .utils import load_pinlock
915

1016
__all__ = ["SimPlatform"]
1117

1218

1319
class SimPlatform:
14-
def __init__(self):
20+
21+
def __init__(self, config):
1522
self.build_dir = os.path.join(os.environ['CHIPFLOW_ROOT'], 'build', 'sim')
1623
self.extra_files = dict()
17-
self.clk = Signal()
18-
self.rst = Signal()
19-
self.buttons = Signal(2)
2024
self.sim_boxes = dict()
25+
self._ports = {}
26+
self._config = config
2127

2228
def add_file(self, filename, content):
2329
if not isinstance(content, (str, bytes)):
2430
content = content.read()
2531
self.extra_files[filename] = content
2632

27-
def add_model(self, inst_type, iface, edge_det=[]):
28-
conns = dict(i_clk=ClockSignal(), a_keep=True)
29-
30-
def is_model_out(field_name):
31-
assert field_name.endswith("_o") or field_name.endswith("_oe") or field_name.endswith("_i"), field_name
32-
return field_name.endswith("_i")
33-
for field_name in iface.signature.members:
34-
if is_model_out(field_name):
35-
conns[f"o_{field_name}"] = getattr(iface, field_name)
36-
else:
37-
conns[f"i_{field_name}"] = getattr(iface, field_name)
38-
if inst_type not in self.sim_boxes:
39-
box = 'attribute \\blackbox 1\n'
40-
box += 'attribute \\cxxrtl_blackbox 1\n'
41-
box += 'attribute \\keep 1\n'
42-
box += f'module \\{inst_type}\n'
43-
box += ' attribute \\cxxrtl_edge "a"\n'
44-
box += ' wire width 1 input 0 \\clk\n'
45-
for i, ((field_name,), _, field) in enumerate(iface.signature.flatten(iface)):
46-
field_width = Shape.cast(field.shape()).width
47-
if field_name in edge_det:
48-
box += ' attribute \\cxxrtl_edge "a"\n'
49-
box += f' wire width {field_width} {"output" if is_model_out(field_name) else "input"} {i} \\{field_name}\n' # noqa: E501
50-
box += 'end\n\n'
51-
self.sim_boxes[inst_type] = box
52-
return Instance(inst_type, **conns)
53-
54-
def add_monitor(self, inst_type, iface):
55-
conns = dict(i_clk=ClockSignal(), a_keep=True)
56-
for field_name in iface.signature.members:
57-
conns[f'i_{field_name}'] = getattr(iface, field_name)
58-
if inst_type not in self.sim_boxes:
59-
box = 'attribute \\blackbox 1\n'
60-
box += 'attribute \\cxxrtl_blackbox 1\n'
61-
box += 'attribute \\keep 1\n'
62-
box += f'module \\{inst_type}\n'
63-
box += ' attribute \\cxxrtl_edge "a"\n'
64-
box += ' wire width 1 input 0 \\clk\n'
65-
for i, ((field_name,), _, field) in enumerate(iface.signature.flatten(iface)):
66-
field_width = Shape.cast(field.shape()).width
67-
box += f' wire width {field_width} input {i + 1} \\{field_name}\n'
68-
box += 'end\n\n'
69-
self.sim_boxes[inst_type] = box
70-
return Instance(inst_type, **conns)
71-
7233
def build(self, e):
7334
Path(self.build_dir).mkdir(parents=True, exist_ok=True)
7435

75-
output = rtlil.convert(e, name="sim_top", ports=[self.clk, self.rst, self.buttons], platform=self)
36+
ports = []
37+
for port_name, port in self._ports.items():
38+
if port.direction in (io.Direction.Input, io.Direction.Bidir):
39+
ports.append((f"io${port_name}$i", port.i, PortDirection.Input))
40+
if port.direction in (io.Direction.Output, io.Direction.Bidir):
41+
ports.append((f"io${port_name}$o", port.o, PortDirection.Output))
42+
if port.direction is io.Direction.Bidir:
43+
ports.append((f"io${port_name}$oe", port.oe, PortDirection.Output))
44+
45+
output = rtlil.convert(e, name="sim_top", ports=ports, platform=self)
7646

7747
top_rtlil = Path(self.build_dir) / "sim_soc.il"
7848
with open(top_rtlil, "w") as rtlil_file:
7949
for box_content in self.sim_boxes.values():
8050
rtlil_file.write(box_content)
8151
rtlil_file.write(output)
52+
8253
top_ys = Path(self.build_dir) / "sim_soc.ys"
8354
with open(top_ys, "w") as yosys_file:
8455
for extra_filename, extra_content in self.extra_files.items():
8556
extra_path = Path(self.build_dir) / extra_filename
8657
with open(extra_path, "w") as extra_file:
8758
extra_file.write(extra_content)
8859
if extra_filename.endswith(".il"):
89-
print(f"read_rtlil {extra_filename}", file=yosys_file)
60+
print(f"read_rtlil {extra_path}", file=yosys_file)
9061
else:
9162
# FIXME: use -defer (workaround for YosysHQ/yosys#4059)
92-
print(f"read_verilog {extra_filename}", file=yosys_file)
63+
print(f"read_verilog {extra_path}", file=yosys_file)
9364
print("read_rtlil sim_soc.il", file=yosys_file)
9465
print("hierarchy -top sim_top", file=yosys_file)
95-
# FIXME: use the default -O6 (workaround for YosysHQ/yosys#4227)
96-
print("write_cxxrtl -O4 -header sim_soc.cc", file=yosys_file)
66+
print("write_cxxrtl -header sim_soc.cc", file=yosys_file)
67+
68+
def instantiate_ports(self, m: Module):
69+
if hasattr(self, "_pinlock"):
70+
return
71+
72+
pinlock = load_pinlock()
73+
for component, iface in pinlock.port_map.items():
74+
for k, v in iface.items():
75+
for name, port in v.items():
76+
self._ports[port.port_name] = io.SimulationPort(port.direction, port.width, invert=port.invert, name=f"{component}-{name}")
77+
78+
for clock, name in self._config["chipflow"]["clocks"].items():
79+
if name not in pinlock.package.clocks:
80+
raise ChipFlowError("Unable to find clock {name} in pinlock")
81+
82+
port_data = pinlock.package.clocks[name]
83+
port = io.SimulationPort(io.Direction.Input, port_data.width, invert=True, name=f"clock-{name}")
84+
self._ports[name] = port
85+
86+
if clock == 'default':
87+
clock = 'sync'
88+
setattr(m.domains, clock, ClockDomain(name=clock))
89+
clk_buffer = io.Buffer("i", port)
90+
setattr(m.submodules, "clk_buffer_" + clock, clk_buffer)
91+
m.d.comb += ClockSignal().eq(clk_buffer.i)
92+
93+
for reset, name in self._config["chipflow"]["resets"].items():
94+
port_data = pinlock.package.resets[name]
95+
port = io.SimulationPort(io.Direction.Input, port_data.width, invert=port.invert, name=f"clock-{name}", )
96+
self._ports[name] = port
97+
rst_buffer = io.Buffer("i", port)
98+
setattr(m.submodules, reset, rst_buffer)
99+
setattr(m.submodules, reset + "_sync", FFSynchronizer(rst_buffer.i, ResetSignal()))
100+
101+
self._pinlock = pinlock
102+
103+
104+
105+
106+
VARIABLES = {
107+
"OUTPUT_DIR": "./build/sim",
108+
"ZIG_CXX": f"{sys.executable} -m ziglang c++",
109+
"CXXFLAGS": "-O3 -g -std=c++17 -Wno-array-bounds -Wno-shift-count-overflow -fbracket-depth=1024",
110+
"DEFINES": "-DPROJECT_ROOT=\\\"{PROJECT_ROOT}\\\" -DBUILD_DIR=\\\"{BUILD_DIR}\\\"",
111+
"INCLUDES": "-I {OUTPUT_DIR} -I {COMMON_DIR} -I {COMMON_DIR}/vendor -I {RUNTIME_DIR}",
112+
}
113+
DOIT_CONFIG = {'action_string_formatting': 'both'}
114+
115+
BUILD_SIM = {
116+
"name": "build_sim",
117+
"actions": [
118+
"{ZIG_CXX} {CXXFLAGS} {INCLUDES} {DEFINES} -o {OUTPUT_DIR}/sim_soc{EXE} "
119+
"{OUTPUT_DIR}/sim_soc.cc {SOURCE_DIR}/main.cc {COMMON_DIR}/models.cc"
120+
],
121+
"targets": [
122+
"{OUTPUT_DIR}/sim_soc{EXE}"
123+
],
124+
"file_dep": [
125+
"{OUTPUT_DIR}/sim_soc.cc",
126+
"{OUTPUT_DIR}/sim_soc.h",
127+
"{SOURCE_DIR}/main.cc",
128+
"{COMMON_DIR}/models.cc",
129+
"{COMMON_DIR}/models.h",
130+
"{COMMON_DIR}/vendor/nlohmann/json.hpp",
131+
"{COMMON_DIR}/vendor/cxxrtl/cxxrtl_server.h",
132+
],
133+
}
134+
135+
SIM_CXXRTL = {
136+
"name": "sim_cxxrtl",
137+
"actions": ["cd {OUTPUT_DIR} && pdm run yowasp-yosys sim_soc.ys"],
138+
"targets": ["{OUTPUT_DIR}/sim_soc.cc", "{OUTPUT_DIR}/sim_soc.h"],
139+
"file_dep": ["{OUTPUT_DIR}/sim_soc.ys", "{OUTPUT_DIR}/sim_soc.il"],
140+
}
141+
142+
TASKS = [BUILD_SIM, SIM_CXXRTL]
143+
144+

chipflow_lib/platforms/utils.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import pydantic
77

88
from collections import OrderedDict, deque
9-
from collections.abc import MutableMapping
9+
from collections.abc import MutableMapping, Iterable
1010
from pprint import pformat
1111
from typing import Set, List, Dict, Optional, Union, Literal
1212

@@ -73,11 +73,16 @@ class IOSignature(wiring.Signature):
7373
7474
:param direction: Input, Output or Bidir
7575
:param width: width of port, default is 1
76-
:param all_have_oe: controls whether each output wire is associated with an individual Output Enable bit or a single OE bit will be used for entire port, the default value is False, indicating that a single OE bit controls the entire port.
76+
:param invert: Polarity inversion. If the value is a simple :class:`bool`, it specifies inversion for
77+
the entire port. If the value is an iterable of :class:`bool`, the iterable must have the
78+
same length as the width of :py:`io`, and the inversion is specified for individual wires.
79+
:param all_have_oe: controls whether each output wire is associated with an individual Output Enable bit
80+
or a single OE bit will be used for entire port, the default value is False, indicating that a
81+
single OE bit controls the entire port.
7782
:param init: a :ref:`const-castable object <lang-constcasting>` for the initial values of the port
7883
"""
7984

80-
def __init__(self, direction: io.Direction, width: int = 1, all_have_oe: bool = False, init = None):
85+
def __init__(self, direction: io.Direction, width: int = 1, invert: Union[bool,Iterable[bool]] = False, all_have_oe: bool = False, init = None):
8186
self._direction = direction
8287
self._width = width
8388
self._init = init
@@ -98,6 +103,17 @@ def __init__(self, direction: io.Direction, width: int = 1, all_have_oe: bool =
98103
"all_have_oe": all_have_oe,
99104
"init": init,
100105
}
106+
match invert:
107+
case bool():
108+
self._invert = (invert,) * self._width
109+
case Iterable():
110+
self._invert = tuple(invert)
111+
if len(self._invert) != self._width:
112+
raise ValueError(f"Length of 'invert' ({len(self._invert)}) doesn't match "
113+
f"length of 'io' ({len(self._io)})")
114+
case _:
115+
raise TypeError(f"'invert' must be a bool or iterable of bool, not {invert!r}")
116+
101117

102118
super().__init__(sig)
103119

@@ -110,6 +126,10 @@ def width(self) -> int:
110126
"The width of the IO port, in wires"
111127
return self._width
112128

129+
def invert(self) -> int:
130+
"A tuple as wide as the IO port, with a bool for the polarity inversion for each wire"
131+
return self._invert
132+
113133
def options(self) -> dict:
114134
"""
115135
Options set on the io port at construction
@@ -356,6 +376,7 @@ class Port(pydantic.BaseModel):
356376
pins: List[str]
357377
port_name: str
358378
direction: Optional[str] = None
379+
invert: Union[bool, Iterable[bool]] = False
359380
options: Optional[dict] = None
360381

361382
@property

chipflow_lib/steps/silicon.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
from .. import ChipFlowError
2121
from ..cli import log_level
2222
from ..platforms import SiliconPlatform, top_interfaces, load_pinlock
23-
from ..platforms.utils import IOSignature
2423

2524

2625
logger = logging.getLogger(__name__)

0 commit comments

Comments
 (0)