Skip to content

Commit 19b4bf5

Browse files
committed
Add experimental chisel7 support
1 parent e056889 commit 19b4bf5

18 files changed

+672
-32
lines changed

build.sbt

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Tests._
22

33
val chisel6Version = "6.7.0"
4+
val chisel7Version = "7.0.0-RC4"
45
val chiselTestVersion = "6.0.0"
56
val scalaVersionFromChisel = "2.13.16"
67

@@ -92,12 +93,17 @@ lazy val chisel6Settings = Seq(
9293
libraryDependencies ++= Seq("org.chipsalliance" %% "chisel" % chisel6Version),
9394
addCompilerPlugin("org.chipsalliance" % "chisel-plugin" % chisel6Version cross CrossVersion.full)
9495
)
96+
lazy val chisel7Settings = Seq(
97+
libraryDependencies ++= Seq("org.chipsalliance" %% "chisel" % chisel7Version),
98+
addCompilerPlugin("org.chipsalliance" % "chisel-plugin" % chisel7Version cross CrossVersion.full)
99+
)
95100
lazy val chisel3Settings = Seq(
96101
libraryDependencies ++= Seq("edu.berkeley.cs" %% "chisel3" % chisel3Version),
97102
addCompilerPlugin("edu.berkeley.cs" % "chisel3-plugin" % chisel3Version cross CrossVersion.full)
98103
)
99104

100-
lazy val chiselSettings = chisel6Settings ++ Seq(
105+
// Select Chisel 7 when USE_CHISEL7 is set in the environment; default to Chisel 6.
106+
lazy val chiselSettings = (if (sys.env.contains("USE_CHISEL7")) chisel7Settings else chisel6Settings) ++ Seq(
101107
libraryDependencies ++= Seq(
102108
"org.apache.commons" % "commons-lang3" % "3.12.0",
103109
"org.apache.commons" % "commons-text" % "1.9"
@@ -115,11 +121,17 @@ lazy val scalaTestSettings = Seq(
115121

116122
// -- Rocket Chip --
117123

118-
lazy val hardfloat = freshProject("hardfloat", file("generators/hardfloat/hardfloat"))
119-
.settings(chiselSettings)
120-
.settings(commonSettings)
121-
.dependsOn(midas_target_utils)
122-
.settings(scalaTestSettings)
124+
lazy val hardfloat = {
125+
val useChisel7 = sys.env.contains("USE_CHISEL7")
126+
var hf = freshProject("hardfloat", file("generators/hardfloat/hardfloat"))
127+
.settings(chiselSettings)
128+
.settings(commonSettings)
129+
.settings(scalaTestSettings)
130+
if (!useChisel7) {
131+
hf = hf.dependsOn(midas_target_utils)
132+
}
133+
hf
134+
}
123135

124136
lazy val rocketMacros = (project in rocketChipDir / "macros")
125137
.settings(commonSettings)
@@ -154,24 +166,55 @@ lazy val testchipip = withInitCheck((project in file("generators/testchipip")),
154166
.settings(commonSettings)
155167

156168
lazy val chipyard = {
169+
val useChisel7 = sys.env.contains("USE_CHISEL7")
157170
// Base chipyard project with always-on dependencies
158171
// Use explicit Project(...) so the project id remains 'chipyard'
159-
var cy = Project(id = "chipyard", base = file("generators/chipyard"))
160-
.dependsOn(
172+
val baseProjects: Seq[ProjectReference] =
173+
Seq(
161174
testchipip, rocketchip, boom, rocketchip_blocks, rocketchip_inclusive_cache,
162-
dsptools, rocket_dsp_utils,
163175
icenet, tracegen,
164176
constellation, barf, shuttle, rerocc,
165-
firrtl2_bridge
166-
)
177+
).map(sbt.Project.projectToRef) ++
178+
(if (useChisel7) Seq() else Seq(sbt.Project.projectToRef(firrtl2_bridge))) ++
179+
(if (useChisel7) Seq() else Seq(sbt.Project.projectToRef(dsptools), sbt.Project.projectToRef(rocket_dsp_utils)))
180+
181+
val baseDeps: Seq[sbt.ClasspathDep[sbt.ProjectReference]] =
182+
baseProjects.map(pr => sbt.ClasspathDependency(pr, None))
183+
184+
// Optional settings to exclude specific sources under Chisel 7
185+
val dspExcludeSettings: Seq[Def.Setting[_]] = if (useChisel7) Seq(
186+
Compile / unmanagedSources := {
187+
val files = (Compile / unmanagedSources).value
188+
val root = (ThisBuild / baseDirectory).value
189+
val excludeList = Seq(
190+
// Directories or files relative to repo root
191+
"generators/chipyard/src/main/scala/example/dsptools",
192+
"generators/chipyard/src/main/scala/config/MMIOAcceleratorConfigs.scala",
193+
"generators/chipyard/src/main/scala/config/TutorialConfigs.scala",
194+
"generators/chipyard/src/main/scala/upf"
195+
).map(p => (root / p).getCanonicalFile)
196+
val (excludeDirs, excludeFiles) = excludeList.partition(_.isDirectory)
197+
files.filterNot { f =>
198+
val cf = f.getCanonicalFile
199+
excludeFiles.contains(cf) || excludeDirs.exists(d => cf.toPath.startsWith(d.toPath))
200+
}
201+
}
202+
) else Seq.empty
203+
204+
var cy = Project(id = "chipyard", base = file("generators/chipyard"))
205+
.dependsOn(baseDeps: _*)
167206
.settings(libraryDependencies ++= rocketLibDeps.value)
168207
.settings(
169208
libraryDependencies ++= Seq(
170209
"org.reflections" % "reflections" % "0.10.2"
171210
)
172211
)
173212
.settings(commonSettings)
174-
.settings(Compile / unmanagedSourceDirectories += file("tools/stage/src/main/scala"))
213+
.settings(Compile / unmanagedSourceDirectories += {
214+
if (useChisel7) file("tools/stage-chisel7/src/main/scala")
215+
else file("tools/stage/src/main/scala")
216+
})
217+
.settings(dspExcludeSettings: _*)
175218

176219
// Optional modules discovered via initialized submodules (no env or manifest)
177220
val optionalModules: Seq[(String, ProjectReference)] = Seq(

common.mk

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,21 @@ define require_cmd
1616
|| { echo "Error: $(1) not found in PATH. Set up your tool environment before building this target." >&2; exit 1; }
1717
endef
1818

19+
# Require minimum firtool version when building with Chisel 7
20+
define require_firtool_version
21+
@if [ -n "$(USE_CHISEL7)" ]; then \
22+
vline=`$(FIRTOOL_BIN) --version 2>/dev/null | grep -E 'CIRCT firtool-[0-9]+\.[0-9]+\.[0-9]+' | head -1`; \
23+
vstr=$${vline##*firtool-}; \
24+
if [ -z "$$vstr" ]; then \
25+
echo "Error: Unable to parse firtool version. Ensure '$(FIRTOOL_BIN) --version' prints 'CIRCT firtool-X.Y.Z'." >&2; exit 1; \
26+
fi; \
27+
maj=$${vstr%%.*}; rest=$${vstr#*.}; min=$${rest%%.*}; pat=$${rest#*.}; \
28+
if [ "$$maj" -lt 1 ] || { [ "$$maj" -eq 1 ] && [ "$$min" -lt 129 ]; }; then \
29+
echo "Error: USE_CHISEL7 requires firtool >= 1.129.0, found $$vstr. Please update CIRCT firtool." >&2; exit 1; \
30+
fi; \
31+
fi
32+
endef
33+
1934
#########################################################################################
2035
# specify user-interface variables
2136
#########################################################################################
@@ -160,6 +175,9 @@ export mfc_extra_anno_contents
160175
export sfc_extra_low_transforms_anno_contents
161176
$(FINAL_ANNO_FILE) $(MFC_EXTRA_ANNO_FILE) &: $(ANNO_FILE)
162177
echo "$$mfc_extra_anno_contents" > $(MFC_EXTRA_ANNO_FILE)
178+
ifdef USE_CHISEL7
179+
jq '. + [{"class":"firrtl.transforms.BlackBoxTargetDirAnno","targetDir":"$(GEN_COLLATERAL_DIR)/blackboxes"}]' $(MFC_EXTRA_ANNO_FILE) > $(MFC_EXTRA_ANNO_FILE).tmp && mv $(MFC_EXTRA_ANNO_FILE).tmp $(MFC_EXTRA_ANNO_FILE)
180+
endif
163181
jq -s '[.[][]]' $(ANNO_FILE) $(MFC_EXTRA_ANNO_FILE) > $(FINAL_ANNO_FILE)
164182

165183
.PHONY: firrtl
@@ -180,6 +198,12 @@ SFC_MFC_TARGETS = \
180198

181199
MFC_BASE_LOWERING_OPTIONS ?= emittedLineLength=2048,noAlwaysComb,disallowLocalVariables,verifLabels,disallowPortDeclSharing,locationInfoStyle=wrapInAtSquareBracket
182200

201+
# Extra firtool flags are only applied when building with Chisel 7
202+
FIRTOOL_EXTRA_FLAGS ?=
203+
ifdef USE_CHISEL7
204+
FIRTOOL_EXTRA_FLAGS += --verification-flavor=if-else-fatal
205+
endif
206+
183207
# DOC include start: FirrtlCompiler
184208
$(MFC_LOWERING_OPTIONS):
185209
mkdir -p $(dir $@)
@@ -191,6 +215,7 @@ endif
191215

192216
$(SFC_MFC_TARGETS) &: $(FIRRTL_FILE) $(FINAL_ANNO_FILE) $(MFC_LOWERING_OPTIONS)
193217
$(call require_cmd,$(FIRTOOL_BIN))
218+
$(require_firtool_version)
194219
rm -rf $(GEN_COLLATERAL_DIR)
195220
(set -o pipefail && $(FIRTOOL_BIN) \
196221
--format=fir \
@@ -205,12 +230,29 @@ $(SFC_MFC_TARGETS) &: $(FIRRTL_FILE) $(FINAL_ANNO_FILE) $(MFC_LOWERING_OPTIONS)
205230
--repl-seq-mem-file=$(MFC_SMEMS_CONF) \
206231
--annotation-file=$(FINAL_ANNO_FILE) \
207232
--split-verilog \
233+
$(FIRTOOL_EXTRA_FLAGS) \
208234
-o $(GEN_COLLATERAL_DIR) \
209235
$(FIRRTL_FILE) |& tee $(FIRTOOL_LOG_FILE))
210236
$(SED) $(SED_INPLACE) 's/.*/& /' $(MFC_SMEMS_CONF) # need trailing space for SFC macrocompiler
211-
touch $(MFC_BB_MODS_FILELIST) # if there are no BB's then the file might not be generated, instead always generate it
237+
ifdef USE_CHISEL7
238+
# Construct blackbox file list from files emitted into gen-collateral/blackboxes
239+
@if [ -d "$(GEN_COLLATERAL_DIR)/blackboxes" ]; then \
240+
find "$(GEN_COLLATERAL_DIR)/blackboxes" -type f \( -name '*.v' -o -name '*.sv' -o -name '*.cc' \) | \
241+
sed -e 's;^$(GEN_COLLATERAL_DIR)/;;' > "$(MFC_BB_MODS_FILELIST)"; \
242+
else \
243+
: > "$(MFC_BB_MODS_FILELIST)"; \
244+
fi
245+
else
246+
# If there are no BB's then the file might not be generated; ensure it exists
247+
touch $(MFC_BB_MODS_FILELIST)
248+
endif
212249
# DOC include end: FirrtlCompiler
213250

251+
.PHONY: run-firtool
252+
run-firtool: $(SFC_MFC_TARGETS)
253+
@echo "[run-firtool] Generated: $(SFC_MFC_TARGETS)"
254+
255+
214256
$(TOP_MODS_FILELIST) $(MODEL_MODS_FILELIST) $(ALL_MODS_FILELIST) $(BB_MODS_FILELIST) $(MFC_MODEL_HRCHY_JSON_UNIQUIFIED) &: $(MFC_MODEL_HRCHY_JSON) $(MFC_TOP_HRCHY_JSON) $(MFC_FILELIST) $(MFC_BB_MODS_FILELIST)
215257
$(base_dir)/scripts/uniquify-module-names.py \
216258
--model-hier-json $(MFC_MODEL_HRCHY_JSON) \

scripts/uniquify-module-names.py

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import shutil
66
import os
77
import sys
8+
import platform
89

910

1011
parser = argparse.ArgumentParser(description="")
@@ -23,6 +24,8 @@
2324

2425
MODEL_SFX=args.model + "_UNIQUIFIED"
2526
SED=os.environ.get("SED", "sed")
27+
# macOS/BSD sed requires an explicit suffix for -i; use empty suffix
28+
SED_INPLACE = "-i ''" if platform.system() == "Darwin" else "-i"
2629

2730

2831
def bash(cmd):
@@ -63,15 +66,17 @@ def get_modules_in_filelist(filelist, verilog_module_filename, cc_filelist):
6366
lines = fl.readlines()
6467
for line in lines:
6568
path = line.strip()
66-
basepath = os.path.basename(path)
67-
ext = basepath.split(".")[-1]
69+
# Preserve relative directories in the filelist; 'path' is relative to args.gcpath
70+
ext = os.path.basename(path).split(".")[-1]
6871

6972
if (ext == "v") or (ext == "sv"):
70-
modules = get_modules_in_verilog_file(os.path.join(args.gcpath, basepath))
73+
modules = get_modules_in_verilog_file(os.path.join(args.gcpath, path))
7174
for module in modules:
72-
verilog_module_filename[module] = basepath
75+
# Map module name to its relative path under gcpath
76+
verilog_module_filename[module] = path
7377
else:
74-
cc_filelist.append(basepath)
78+
# Track non-Verilog sidecar files with their relative paths
79+
cc_filelist.append(path)
7580
return (verilog_module_filename, cc_filelist)
7681

7782
def get_modules_under_hier(hier, child_to_ignore=None):
@@ -87,10 +92,11 @@ def write_verilog_filelist(modules, verilog_module_filename, out_filelist):
8792
with open(out_filelist, "w") as df:
8893
for module in modules:
8994
if module in existing_modules:
90-
verilog_filename = verilog_module_filename[module]
95+
verilog_filename = verilog_module_filename[module] # relative to gcpath
9196
if verilog_filename not in written_files:
9297
written_files.add(verilog_filename)
93-
if args.target_dir in verilog_filename:
98+
# Always prefix with target_dir unless the path is already absolute
99+
if os.path.isabs(verilog_filename):
94100
df.write(f"{verilog_filename}\n")
95101
else:
96102
df.write(f"{args.target_dir}/{verilog_filename}\n")
@@ -99,20 +105,31 @@ def write_verilog_filelist(modules, verilog_module_filename, out_filelist):
99105
def write_cc_filelist(filelist, out_filelist):
100106
with open(out_filelist, "a") as df:
101107
for path in filelist:
102-
file = os.path.basename(path)
103-
df.write(f"{args.target_dir}/{file}\n")
108+
# Preserve relative layout for non-Verilog files as well
109+
if os.path.isabs(path):
110+
df.write(f"{path}\n")
111+
else:
112+
df.write(f"{args.target_dir}/{path}\n")
113+
114+
def generate_copy(rel_path, sfx):
115+
# rel_path is the file path relative to args.gcpath, e.g., 'verification/assert/Foo.sv'
116+
dirname = os.path.dirname(rel_path)
117+
basename = os.path.basename(rel_path)
118+
(base_no_ext, ext) = os.path.splitext(basename)
104119

105-
def generate_copy(c, sfx):
106-
(cur_name, ext) = os.path.splitext(c)
107-
new_name = cur_name + "_" + sfx
108-
new_file = new_name + ext
120+
# New module/file name with suffix, preserving directory structure
121+
new_basename = base_no_ext + "_" + sfx
122+
new_rel_path = os.path.join(dirname, new_basename + ext) if dirname else (new_basename + ext)
109123

110-
cur_file = os.path.join(args.gcpath, c)
111-
new_file = os.path.join(args.gcpath, new_file)
124+
cur_abs = os.path.join(args.gcpath, rel_path)
125+
new_abs = os.path.join(args.gcpath, new_rel_path)
112126

113-
shutil.copy(cur_file, new_file)
114-
bash(rf"{SED} -i 's/module\( \+\){cur_name}/module\1{new_name}/' {new_file}")
115-
return new_file
127+
os.makedirs(os.path.dirname(new_abs) or args.gcpath, exist_ok=True)
128+
shutil.copy(cur_abs, new_abs)
129+
# Update module declaration inside the copied file
130+
bash(rf"{SED} {SED_INPLACE} -e 's/module\( \+\){base_no_ext}/module\1{new_basename}/' {new_abs}")
131+
# Return the new file path relative to gcpath
132+
return new_rel_path
116133

117134
def bfs_uniquify_modules(tree, common_fnames, verilog_module_filename):
118135
q = [(tree['instance_name'], tree['module_name'], tree['instances'], None)]
@@ -138,7 +155,7 @@ def bfs_uniquify_modules(tree, common_fnames, verilog_module_filename):
138155
new_file = generate_copy(cur_file, MODEL_SFX)
139156
if parent is not None and ((parent, mod) not in updated_submodule):
140157
parent_file = os.path.join(args.gcpath, verilog_module_filename[parent])
141-
bash(rf"{SED} -i 's/\( \*\){mod}\( \+\)/\1{mod}_{MODEL_SFX}\2/' {parent_file}")
158+
bash(rf"{SED} {SED_INPLACE} -e 's/\( \*\){mod}\( \+\)/\1{mod}_{MODEL_SFX}\2/' {parent_file}")
142159
updated_submodule.add((parent, mod))
143160

144161
# add the uniquified module to the verilog_modul_filename dict
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// See LICENSE for license details.
2+
// Based on Rocket Chip's stage implementation
3+
4+
package chipyard.stage
5+
6+
import chisel3.experimental.BaseModule
7+
import firrtl.annotations.{Annotation, NoTargetAnnotation}
8+
import firrtl.options.{HasShellOptions, ShellOption, Unserializable}
9+
10+
trait ChipyardOption extends Unserializable { this: Annotation => }
11+
12+
/** This hijacks the existing ConfigAnnotation to accept the legacy _-delimited format */
13+
private[stage] object UnderscoreDelimitedConfigsAnnotation extends HasShellOptions {
14+
override val options = Seq(
15+
new ShellOption[String](
16+
longOption = "legacy-configs",
17+
toAnnotationSeq = a => {
18+
val split = a.split(':')
19+
assert(split.length == 2, s"'${a}' split by ':' doesn't yield two things")
20+
val packageName = split.head
21+
val configs = split.last.split("_")
22+
Seq(new ConfigsAnnotation(configs map { config => if (config contains ".") s"${config}" else s"${packageName}.${config}" } ))
23+
},
24+
helpText = "A string of underscore-delimited configs (configs have decreasing precendence from left to right).",
25+
shortOption = Some("LC")
26+
)
27+
)
28+
}
29+
30+
/** Paths to config classes */
31+
case class ConfigsAnnotation(configNames: Seq[String]) extends NoTargetAnnotation with ChipyardOption
32+
private[stage] object ConfigsAnnotation extends HasShellOptions {
33+
override val options = Seq(
34+
new ShellOption[Seq[String]](
35+
longOption = "configs",
36+
toAnnotationSeq = a => Seq(ConfigsAnnotation(a)),
37+
helpText = "<comma-delimited configs>",
38+
shortOption = Some("C")
39+
)
40+
)
41+
}
42+
43+
case class TopModuleAnnotation(clazz: Class[_ <: Any]) extends NoTargetAnnotation with ChipyardOption
44+
private[stage] object TopModuleAnnotation extends HasShellOptions {
45+
override val options = Seq(
46+
new ShellOption[String](
47+
longOption = "top-module",
48+
toAnnotationSeq = a => Seq(TopModuleAnnotation(Class.forName(a).asInstanceOf[Class[_ <: BaseModule]])),
49+
helpText = "<top module>",
50+
shortOption = Some("T")
51+
)
52+
)
53+
}
54+
55+
/** Optional base name for generated files' filenames */
56+
case class OutputBaseNameAnnotation(outputBaseName: String) extends NoTargetAnnotation with ChipyardOption
57+
private[stage] object OutputBaseNameAnnotation extends HasShellOptions {
58+
override val options = Seq(
59+
new ShellOption[String](
60+
longOption = "name",
61+
toAnnotationSeq = a => Seq(OutputBaseNameAnnotation(a)),
62+
helpText = "<base name of output files>",
63+
shortOption = Some("n")
64+
)
65+
)
66+
}
67+
68+
/** Optionally generate legacy FIRRTL2 SFC FIRRTL generation */
69+
case class LegacySFCAnnotation() extends NoTargetAnnotation with ChipyardOption
70+
private[stage] object LegacySFCAnnotation extends HasShellOptions {
71+
override val options = Seq(
72+
new ShellOption[Unit](
73+
longOption = "emit-legacy-sfc",
74+
toAnnotationSeq = a => Seq(LegacySFCAnnotation()),
75+
helpText = "Emit a legacy FIRRTL2 SFC FIRRTL file",
76+
shortOption = Some("els")
77+
)
78+
)
79+
}

0 commit comments

Comments
 (0)