Skip to content

Commit 0d8ecd9

Browse files
committed
feat: add pegasus
feat: replace verilog generation with bdb
1 parent 47fe2ab commit 0d8ecd9

9 files changed

Lines changed: 222 additions & 260 deletions
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import asyncio
2+
from utils.event_common import wait_for_result
3+
from utils.path import get_buckyball_path
4+
5+
6+
config = {
7+
"type": "api",
8+
"name": "Pegasus Verilog",
9+
"description": "Generate SystemVerilog for Pegasus FPGA (PegasusHarness + ChipTop)",
10+
"path": "/pegasus/verilog",
11+
"method": "POST",
12+
"emits": ["pegasus.verilog"],
13+
"flows": ["pegasus"],
14+
}
15+
16+
17+
async def handler(req, context):
18+
bbdir = get_buckyball_path()
19+
body = req.get("body") or {}
20+
21+
# Default config for Pegasus; allow override
22+
config_name = body.get("config", "sims.pegasus.PegasusConfig")
23+
24+
data = {
25+
"config": config_name,
26+
"output_dir": body.get("output_dir", f"{bbdir}/arch/build/pegasus/"),
27+
}
28+
29+
await context.emit({"topic": "pegasus.verilog", "data": data})
30+
31+
while True:
32+
result = await wait_for_result(context)
33+
if result is not None:
34+
return result
35+
await asyncio.sleep(1)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import os
2+
import sys
3+
4+
utils_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
5+
if utils_path not in sys.path:
6+
sys.path.insert(0, utils_path)
7+
8+
from utils.path import get_buckyball_path
9+
from utils.stream_run import stream_run_logger
10+
from utils.event_common import check_result
11+
12+
13+
config = {
14+
"type": "event",
15+
"name": "make pegasus verilog",
16+
"description": "Generate SystemVerilog from Chisel using ElaboratePegasus",
17+
"subscribes": ["pegasus.verilog"],
18+
"emits": [],
19+
"flows": ["pegasus"],
20+
}
21+
22+
23+
async def handler(data, context):
24+
bbdir = get_buckyball_path()
25+
arch_dir = f"{bbdir}/arch"
26+
build_dir = data.get("output_dir", f"{bbdir}/arch/build/pegasus/")
27+
28+
config_name = data.get("config", "sims.pegasus.PegasusConfig")
29+
context.logger.info(f"[pegasus] Elaborating config: {config_name}")
30+
context.logger.info(f"[pegasus] Output directory: {build_dir}")
31+
32+
os.makedirs(build_dir, exist_ok=True)
33+
34+
# Use ElaboratePegasus (not the generic sims.verilator.Elaborate)
35+
# ElaboratePegasus instantiates PegasusHarness directly, bypassing TestHarness
36+
command = (
37+
f"mill -i __.test.runMain sims.pegasus.ElaboratePegasus "
38+
f"--disable-annotation-unknown "
39+
f"-strip-debug-info "
40+
f"-O=debug "
41+
f"--split-verilog "
42+
f"-o={build_dir}"
43+
)
44+
45+
result = stream_run_logger(
46+
cmd=command,
47+
logger=context.logger,
48+
cwd=arch_dir,
49+
stdout_prefix="pegasus verilog",
50+
stderr_prefix="pegasus verilog",
51+
)
52+
53+
# Clean up stray top-level file if emitted next to arch/
54+
for stray in ["PegasusHarness.sv", "TestHarness.sv"]:
55+
stray_path = f"{arch_dir}/{stray}"
56+
if os.path.exists(stray_path):
57+
os.remove(stray_path)
58+
59+
# Copy generated SV files to pegasus/vivado/generated/ for Vivado build
60+
vivado_gen_dir = f"{bbdir}/pegasus/vivado/generated"
61+
if result.returncode == 0 and os.path.isdir(build_dir):
62+
import shutil
63+
os.makedirs(vivado_gen_dir, exist_ok=True)
64+
sv_files = [f for f in os.listdir(build_dir) if f.endswith(".sv") or f.endswith(".v")]
65+
for f in sv_files:
66+
shutil.copy2(os.path.join(build_dir, f), os.path.join(vivado_gen_dir, f))
67+
context.logger.info(f"[pegasus] Copied {len(sv_files)} files to {vivado_gen_dir}")
68+
69+
success_result, failure_result = await check_result(
70+
context,
71+
result.returncode,
72+
continue_run=False,
73+
extra_fields={
74+
"task": "verilog",
75+
"output_dir": build_dir,
76+
"vivado_gen_dir": vivado_gen_dir,
77+
"top_module": "PegasusHarness",
78+
},
79+
)
80+
81+
return

api/steps/verilator/02_verilog_event_step.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ async def handler(data, context):
5252
f"mill -i __.test.runMain sims.verify.BallTopMain {data.get('balltype')} "
5353
)
5454
else:
55-
command = f"mill -i __.test.runMain sims.verilator.Elaborate {config_name} "
55+
command = f"mill -i __.test.runMain sims.verilator.BBSimElaborate {config_name} "
5656

5757
command += "--disable-annotation-unknown -strip-debug-info -O=debug "
5858
command += f"--split-verilog -o={build_dir}"
@@ -66,7 +66,7 @@ async def handler(data, context):
6666
)
6767

6868
# Remove unwanted file
69-
topname_file = f"{arch_dir}/TestHarness.sv"
69+
topname_file = f"{arch_dir}/BBSimHarness.sv"
7070
if os.path.exists(topname_file):
7171
os.remove(topname_file)
7272

api/steps/verilator/03_build_event_step.py

Lines changed: 66 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"name": "make build",
1818
"description": "build verilator executable",
1919
"subscribes": ["verilator.build"],
20-
"emits": ["verilator.sim", "verilator.cosim"],
20+
"emits": ["verilator.sim"],
2121
"flows": ["verilator"],
2222
}
2323

@@ -26,15 +26,11 @@ async def handler(data, context):
2626
bbdir = get_buckyball_path()
2727
arch_dir = f"{bbdir}/arch"
2828
build_dir = f"{arch_dir}/build"
29-
waveform_dir = f"{arch_dir}/waveform"
30-
log_dir = f"{arch_dir}/log"
31-
cosim = data.get("cosim", False)
3229
coverage = data.get("coverage", False)
3330

34-
# ==================================================================================
35-
# Execute operation
3631
# ==================================================================================
3732
# Find sources
33+
# ==================================================================================
3834
vsrcs = glob.glob(f"{build_dir}/**/*.v", recursive=True) + glob.glob(
3935
f"{build_dir}/**/*.sv", recursive=True
4036
)
@@ -47,33 +43,71 @@ async def handler(data, context):
4743
+ glob.glob(f"{build_dir}/**/*.cpp", recursive=True)
4844
)
4945

50-
# Setup paths: fesvr from bebop/host/spike/riscv-isa-sim (install/include, install/lib)
51-
bebop_isa_sim = f"{bbdir}/bebop/host/spike/riscv-isa-sim"
46+
# Exclude testchipip's SimDRAM.cc — our SimDRAM_bb.cc overrides memory_init
47+
csrcs = [f for f in csrcs if not f.endswith("SimDRAM.cc") or "src/csrc" in f]
48+
49+
# Patch fesvr includes out of build/mm.h and build/mm.cc.
50+
# These files are auto-copied from testchipip by Verilator as SimDRAM.v
51+
# companion sources. They reference fesvr/memif.h which we don't have
52+
# (fesvr is removed). The memif_t dependency was only used by SimDRAM.cc's
53+
# load_elf — our SimDRAM_bb.cc doesn't use it.
54+
for patch_file in [f"{build_dir}/mm.h", f"{build_dir}/mm.cc"]:
55+
if os.path.exists(patch_file):
56+
with open(patch_file, "r") as f:
57+
content = f.read()
58+
patched = "\n".join(
59+
line for line in content.splitlines()
60+
if "fesvr/memif.h" not in line and "fesvr/elfloader.h" not in line
61+
)
62+
if patched != content:
63+
with open(patch_file, "w") as f:
64+
f.write(patched)
65+
context.logger.info(f"Patched fesvr includes from {patch_file}")
66+
67+
topname = "BBSimHarness"
68+
69+
# ==================================================================================
70+
# Build flags
71+
# ==================================================================================
72+
dramsim2_dir = f"{arch_dir}/thirdparty/chipyard/tools/DRAMSim2"
73+
74+
# Find readline headers/libs in nix store (not in standard paths under nix)
75+
rl_headers = glob.glob("/nix/store/*readline*-dev/include/readline/readline.h")
76+
readline_inc = os.path.dirname(os.path.dirname(rl_headers[0])) if rl_headers else ""
77+
rl_libs = glob.glob("/nix/store/*readline*/lib/libreadline.so")
78+
readline_lib = os.path.dirname(rl_libs[0]) if rl_libs else ""
79+
80+
# Find zlib headers/libs in nix store
81+
zlib_headers = glob.glob("/nix/store/*zlib*-dev/include/zlib.h")
82+
if not zlib_headers:
83+
zlib_headers = glob.glob("/nix/store/*zlib*/include/zlib.h")
84+
zlib_inc = os.path.dirname(zlib_headers[0]) if zlib_headers else ""
85+
zlib_libs = glob.glob("/nix/store/*zlib*/lib/libz.so")
86+
zlib_lib = os.path.dirname(zlib_libs[0]) if zlib_libs else ""
87+
5288
inc_paths = [
53-
os.environ.get("RISCV", "") + "/include" if os.environ.get("RISCV") else "",
54-
f"{arch_dir}/thirdparty/chipyard/tools/DRAMSim2",
55-
f"{bebop_isa_sim}/install/include",
89+
dramsim2_dir,
5690
build_dir,
5791
f"{arch_dir}/src/csrc/include",
5892
]
93+
if readline_inc:
94+
inc_paths.append(readline_inc)
95+
if zlib_inc:
96+
inc_paths.append(zlib_inc)
5997
inc_flags = " ".join([f"-I{p}" for p in inc_paths if p])
6098

61-
if cosim:
62-
topname = "ToyBuckyball"
63-
else:
64-
topname = "TestHarness"
99+
# -DBBSIM: selects VBBSimHarness in bdb.h / main.cc
100+
cflags = f"{inc_flags} -DBBSIM -DTOP_NAME='\"V{topname}\"' -std=c++17"
65101

66-
cflags = f"{inc_flags} -DTOP_NAME='\"V{topname}\"' -std=c++17 "
67-
if cosim:
68-
cflags += " -DCOSIM"
69102
ldflags = (
70-
f"-lreadline -ldramsim -lfesvr -lstdc++ "
71-
f"-L{bebop_isa_sim}/install/lib "
103+
f"-lreadline -ldramsim -lstdc++ -lz "
72104
f"-L{bbdir}/result/lib "
73-
f"-L{arch_dir}/thirdparty/chipyard/tools/DRAMSim2 "
74-
f"-L{arch_dir}/thirdparty/chipyard/toolchains/riscv-tools/riscv-isa-sim/build "
75-
f"-L{arch_dir}/thirdparty/chipyard/toolchains/riscv-tools/riscv-isa-sim/build/lib"
105+
f"-L{dramsim2_dir} "
76106
)
107+
if readline_lib:
108+
ldflags += f"-L{readline_lib} -Wl,-rpath,{readline_lib} "
109+
if zlib_lib:
110+
ldflags += f"-L{zlib_lib} -Wl,-rpath,{zlib_lib} "
77111

78112
obj_dir = f"{build_dir}/obj_dir"
79113
subprocess.run(f"rm -rf {obj_dir}", shell=True)
@@ -82,23 +116,26 @@ async def handler(data, context):
82116
sources = " ".join(vsrcs + csrcs)
83117
jobs = data.get("jobs", "")
84118

85-
# Fix nix runtime library paths: embed rpath so binary finds nix libstdc++/glibc
119+
# Fix nix runtime library paths
86120
libstdcpp_path = subprocess.run(
87121
"g++ -print-file-name=libstdc++.so", shell=True, capture_output=True, text=True
88122
).stdout.strip()
89123
if libstdcpp_path and "/" in libstdcpp_path:
90124
nix_lib_dir = os.path.dirname(os.path.realpath(libstdcpp_path))
91125
ldflags += f" -Wl,-rpath,{nix_lib_dir}"
92126

93-
# Enable ccache for verilator C++ compilation via OBJCACHE
127+
# Enable ccache if available
94128
if subprocess.run("command -v ccache", shell=True, capture_output=True).returncode == 0:
95129
os.environ["OBJCACHE"] = "ccache"
96130

97-
# Build acceleration: lld for faster linking (available in nix env)
131+
# Use lld for faster linking if available
98132
use_lld = subprocess.run("command -v ld.lld", shell=True, capture_output=True).returncode == 0
99133
if use_lld:
100134
ldflags += " -fuse-ld=lld"
101135

136+
# ==================================================================================
137+
# Run verilator
138+
# ==================================================================================
102139
verilator_cmd = (
103140
f"verilator -MMD -cc --vpi --trace -O3 --x-assign fast --x-initial fast --noassert -Wno-fatal "
104141
f"--trace-fst --trace-threads 1 --output-split 10000 --output-split-cfuncs 100 "
@@ -148,17 +185,9 @@ async def handler(data, context):
148185
extra_fields={"task": "build"},
149186
)
150187

151-
# ==================================================================================
152-
# Continue routing
153-
# ==================================================================================
154188
if data.get("from_run_workflow"):
155-
if cosim:
156-
await context.emit(
157-
{"topic": "verilator.cosim", "data": {**data, "task": "run"}}
158-
)
159-
else:
160-
await context.emit(
161-
{"topic": "verilator.sim", "data": {**data, "task": "run"}}
162-
)
189+
await context.emit(
190+
{"topic": "verilator.sim", "data": {**data, "task": "run"}}
191+
)
163192

164193
return

api/steps/verilator/04_cosim_api_step.py

Lines changed: 0 additions & 40 deletions
This file was deleted.

0 commit comments

Comments
 (0)