Skip to content

Commit 13f1f17

Browse files
committed
[FIRRTL] Add ReadDesignConfigInt intrinsic for post-Verilog configuration
This patch introduces a new FIRRTL intrinsic `circt.read_design_config_int` that enables post-Verilog configuration by generating a SystemVerilog package (DesignConfigPackage) containing configurable parameters. The intrinsic allows hardware designs to reference configuration values that can be modified after Verilog generation without requiring recompilation. The implementation includes three main components. First, the ReadDesignConfigIntIntrinsicOp operation takes a parameter name, default value, and optional comment, returning an integer value. Second, the LowerToHW pass converts these intrinsics to sv.localparam operations that reference the generated package using the syntax `DesignConfigPackage::param_name`. Third, the CreateDesignConfigPackage pass collects all configuration parameters from modules under the effective DUT, generates a SystemVerilog package with parameter declarations, and creates OM metadata for tooling integration. Configuration parameters are only generated for the effective DUT to avoid conflicts when compiling testbenches separately. The generated package uses standard SystemVerilog parameter syntax, allowing values to be overridden through compilation flags or configuration files without regenerating the design.
1 parent e45721d commit 13f1f17

File tree

14 files changed

+529
-0
lines changed

14 files changed

+529
-0
lines changed

include/circt/Dialect/FIRRTL/FIRRTLIntrinsics.td

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,4 +243,39 @@ def ViewIntrinsicOp : FIRRTLOp<"view", []> {
243243
let assemblyFormat = "$name `,` (`yaml` $yamlFile^ `,`)? $augmentedType (`,` $inputs^)? attr-dict (`:` type($inputs)^)?";
244244
}
245245

246+
def ReadDesignConfigIntIntrinsicOp : FIRRTLOp<"int.read_design_config_int",
247+
[HasCustomSSAName, Pure]> {
248+
let summary = "Read an integer design configuration value";
249+
let description = [{
250+
The `int.read_design_config_int` intrinsic reads an integer design
251+
configuration value that will be defined in a generated configuration package
252+
(DesignConfigPackage). This enables post-Verilog configuration where
253+
configuration values can be set after Verilog generation.
254+
255+
The intrinsic takes a parameter name, default value, comment, and
256+
a result type. It returns the default value, but the actual value can be
257+
changed via the generated configuration package.
258+
259+
Example usage in FIRRTL:
260+
```
261+
%value = firrtl.int.read_design_config_int "buffer_type", 3, "Buffer type selection" : !firrtl.uint<8>
262+
```
263+
264+
This generates Verilog like:
265+
```verilog
266+
localparam buffer_type = DesignConfigPackage::buffer_type;
267+
```
268+
}];
269+
270+
let arguments = (ins
271+
StrAttr:$paramName,
272+
I64Attr:$defaultValue,
273+
StrAttr:$comment
274+
);
275+
let results = (outs IntType:$result);
276+
let assemblyFormat = [{
277+
$paramName `,` $defaultValue `,` $comment attr-dict `:` type($result)
278+
}];
279+
}
280+
246281
#endif // CIRCT_DIALECT_FIRRTL_FIRRTLINTRINSICS_TD

include/circt/Dialect/FIRRTL/FIRRTLVisitors.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class ExprVisitor {
5757
LTLImplicationIntrinsicOp, LTLUntilIntrinsicOp,
5858
LTLEventuallyIntrinsicOp, LTLClockIntrinsicOp, Mux2CellIntrinsicOp,
5959
Mux4CellIntrinsicOp, HasBeenResetIntrinsicOp,
60+
ReadDesignConfigIntIntrinsicOp,
6061
// Miscellaneous.
6162
BitsPrimOp, HeadPrimOp, MuxPrimOp, PadPrimOp, ShlPrimOp, ShrPrimOp,
6263
TailPrimOp, VerbatimExprOp, HWStructCastOp, BitCastOp, RefSendOp,
@@ -190,6 +191,7 @@ class ExprVisitor {
190191
HANDLE(Mux4CellIntrinsicOp, Unhandled);
191192
HANDLE(Mux2CellIntrinsicOp, Unhandled);
192193
HANDLE(HasBeenResetIntrinsicOp, Unhandled);
194+
HANDLE(ReadDesignConfigIntIntrinsicOp, Unhandled);
193195

194196
// Miscellaneous.
195197
HANDLE(BitsPrimOp, Unhandled);

include/circt/Dialect/FIRRTL/Passes.td

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,5 +961,31 @@ def AnnotateInputOnlyModules : Pass<"firrtl-annotate-input-only-modules",
961961
];
962962
}
963963

964+
def CreateDesignConfigPackage : Pass<"firrtl-create-design-config-package",
965+
"firrtl::CircuitOp"> {
966+
let summary = "Create design configuration package for post-Verilog configuration";
967+
let description = [{
968+
This pass accumulates all ReadDesignConfigInt intrinsics in the design
969+
and generates a SystemVerilog package (DesignConfigPackage) containing
970+
all the configuration values. It also creates an OM class with metadata
971+
about the configurations.
972+
973+
The pass only generates the package for the effective DUT (Design Under Test).
974+
Configuration values are collected from all modules instantiated under the DUT.
975+
976+
The generated package can be used for post-Verilog configuration by
977+
modifying the configuration values without regenerating the Verilog.
978+
}];
979+
let dependentDialects = [
980+
"emit::EmitDialect",
981+
"hw::HWDialect",
982+
"sv::SVDialect",
983+
"om::OMDialect"
984+
];
985+
let statistics = [
986+
Statistic<"numConfigs", "num-configs", "Number of configurations collected">
987+
];
988+
}
989+
964990

965991
#endif // CIRCT_DIALECT_FIRRTL_PASSES_TD

lib/Conversion/FIRRTLToHW/LowerToHW.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2039,6 +2039,7 @@ struct FIRRTLLowering : public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
20392039
LogicalResult visitExpr(LTLUntilIntrinsicOp op);
20402040
LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
20412041
LogicalResult visitExpr(LTLClockIntrinsicOp op);
2042+
LogicalResult visitExpr(ReadDesignConfigIntIntrinsicOp op);
20422043

20432044
template <typename TargetOp, typename IntrinsicOp>
20442045
LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
@@ -4524,6 +4525,36 @@ LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
45244525
return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
45254526
}
45264527

4528+
LogicalResult
4529+
FIRRTLLowering::visitExpr(ReadDesignConfigIntIntrinsicOp op) {
4530+
// Lower the ReadDesignConfigInt intrinsic to sv.localparam that
4531+
// references the configuration from DesignConfigPackage.
4532+
//
4533+
// Example:
4534+
// %value = firrtl.int.read_design_config_int "buffer_type", 3, "comment"
4535+
// : !firrtl.uint<8>
4536+
//
4537+
// Lowers to:
4538+
// %value = sv.localparam : i8 {
4539+
// value = #hw.param.verbatim<"DesignConfigPackage::buffer_type"> : i8
4540+
// }
4541+
4542+
auto resultType = lowerType(op.getResult().getType());
4543+
if (!resultType)
4544+
return failure();
4545+
4546+
// Create a verbatim reference to the configuration in DesignConfigPackage
4547+
std::string paramRef = "DesignConfigPackage::" + op.getParamName().str();
4548+
auto paramVerbatim = hw::ParamVerbatimAttr::get(
4549+
builder.getStringAttr(paramRef), resultType);
4550+
4551+
// Create sv.localparam operation with the configuration reference
4552+
auto localParam = builder.create<sv::LocalParamOp>(
4553+
op.getLoc(), resultType, paramVerbatim, op.getParamNameAttr());
4554+
4555+
return setLowering(op, localParam);
4556+
}
4557+
45274558
//===----------------------------------------------------------------------===//
45284559
// Other Operations
45294560
//===----------------------------------------------------------------------===//

lib/Dialect/FIRRTL/FIRRTLIntrinsics.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,48 @@ parseAugmentedType(MLIRContext *context, Location loc,
881881
return std::nullopt;
882882
}
883883

884+
class CirctReadDesignConfigIntConverter : public IntrinsicConverter {
885+
public:
886+
using IntrinsicConverter::IntrinsicConverter;
887+
888+
bool check(GenericIntrinsic gi) override {
889+
// Check that we have no inputs, an integer output type, and 3 parameters
890+
if (gi.hasNInputs(0))
891+
return true;
892+
893+
if (!type_isa<IntType>(gi.op.getResult().getType())) {
894+
gi.emitError() << "result must be an integer type";
895+
return true;
896+
}
897+
898+
if (gi.namedParam("name") || gi.namedIntParam("defaultValue") ||
899+
gi.namedParam("comment"))
900+
return true;
901+
902+
if (gi.hasNParam(3))
903+
return true;
904+
905+
return false;
906+
}
907+
908+
void convert(GenericIntrinsic gi, GenericIntrinsicOpAdaptor adaptor,
909+
PatternRewriter &rewriter) override {
910+
auto paramName = gi.getParamValue<StringAttr>("name");
911+
auto defaultValueAttr = gi.getParamValue<IntegerAttr>("defaultValue");
912+
913+
// Convert the defaultValue to i64
914+
auto defaultValue = rewriter.getI64IntegerAttr(
915+
defaultValueAttr.getValue().getSExtValue());
916+
917+
auto comment = gi.getParamValue<StringAttr>("comment");
918+
919+
auto resultType = gi.op.getResult().getType();
920+
921+
rewriter.replaceOpWithNewOp<ReadDesignConfigIntIntrinsicOp>(
922+
gi.op, resultType, paramName, defaultValue, comment);
923+
}
924+
};
925+
884926
class ViewConverter : public IntrinsicConverter {
885927
public:
886928
LogicalResult checkAndConvert(GenericIntrinsic gi,

lib/Dialect/FIRRTL/FIRRTLIntrinsics.td

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,28 @@ def CirctIsX : FIRRTLIntrinsic {
6565
| found | UInt<1> | i is `x` |
6666
}];
6767
}
68+
69+
def CirctReadDesignConfigInt : FIRRTLIntrinsic {
70+
let mnemonic = "circt.read_design_config_int";
71+
let converter = "CirctReadDesignConfigIntConverter";
72+
let description = [{
73+
Reads an integer design configuration value that will be defined in a
74+
generated configuration package. This intrinsic is used for post-Verilog
75+
configuration, where configuration values can be set after Verilog generation.
76+
77+
The generated Verilog will reference the configuration from DesignConfigPackage:
78+
```verilog
79+
localparam buffer_type = DesignConfigPackage.buffer_type;
80+
```
81+
82+
| Parameter | Type | Description |
83+
| ------------ | ------ | -------------------------------------------- |
84+
| name | String | Name of the configuration |
85+
| defaultValue | Int | Default value of the configuration |
86+
| comment | String | Description of the configuration (can be empty) |
87+
88+
| Result | Type | Description |
89+
| ------ | ---- | ------------------------------------------ |
90+
| value | Int | The configuration value |
91+
}];
92+
}

lib/Dialect/FIRRTL/FIRRTLOps.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6462,6 +6462,10 @@ void AndRPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
64626462
void SizeOfIntrinsicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
64636463
genericAsmResultNames(*this, setNameFn);
64646464
}
6465+
void ReadDesignConfigIntIntrinsicOp::getAsmResultNames(
6466+
OpAsmSetValueNameFn setNameFn) {
6467+
setNameFn(getResult(), getParamName());
6468+
}
64656469
void AsAsyncResetPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
64666470
genericAsmResultNames(*this, setNameFn);
64676471
}

lib/Dialect/FIRRTL/Transforms/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_circt_dialect_library(CIRCTFIRRTLTransforms
77
CheckLayers.cpp
88
CheckRecursiveInstantiation.cpp
99
CreateSiFiveMetadata.cpp
10+
CreateDesignConfigPackage.cpp
1011
Dedup.cpp
1112
DropConst.cpp
1213
DropName.cpp

0 commit comments

Comments
 (0)