Skip to content

[FIRRTL][NotForCommit] Add ReadDesignConfigInt intrinsic for post-Verilog configuration#9586

Draft
uenoku wants to merge 1 commit intollvm:mainfrom
uenoku:dev/hidetou/config
Draft

[FIRRTL][NotForCommit] Add ReadDesignConfigInt intrinsic for post-Verilog configuration#9586
uenoku wants to merge 1 commit intollvm:mainfrom
uenoku:dev/hidetou/config

Conversation

@uenoku
Copy link
Member

@uenoku uenoku commented Feb 2, 2026

This is PoC for one of proposals to achieve verilog level configuration. Not for commit.

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 naming is intentionally "config" instead of "parameter" to reserve keyword/feature for FIRRTL parameters.

The implementation follows standard lowering flow for the intrinsic:

  • LowerToHW pass converts these intrinsics to sv.localparam operations that reference the generated package using the syntax DesignConfigPackage::param_name.
  • 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 via either modifying configuration flag or maybe via macro (it's not implemented here, but we can add ifdef CIRCT_DESIGNCONFIG_<PARAMETER_NAME> to the package).

Example:

FIRRTL version 10.0.0
circuit Top:
  extmodule Buffer:
    input data_in : UInt<8>
    input mode : UInt<2>
    output data_out : UInt<8>
    defname = Buffer
  public module Top:
    input clock: Clock
    input reset: UInt<1>
    input data_in: UInt<8>
    output data_out: UInt<8>
    output buffer_out: UInt<8>

    wire mode : UInt<2>
    wire enable_feature : UInt<1>

    connect mode, intrinsic(circt_read_design_config_int<name = "mode", defaultValue = 3, comment = "Mode selection"> : UInt<2>)
    connect enable_feature, intrinsic(circt_read_design_config_int<name = "enable_feature", defaultValue = 1, comment = "Enable feature flag"> : UInt<1>)

    inst buffer of Buffer
    connect buffer.data_in, data_in
    connect buffer.mode, mode
    connect data_out, buffer.data_out
    node selected_data = mux(enable_feature, data_in, UInt<8>(0))
    connect data_out, selected_data
    connect buffer_out, buffer.data_out

Output:

// Generated by CIRCT firtool-1.139.0-139-g13f1f1710
module Top(
  input        clock,
               reset,
  input  [7:0] data_in,
  output [7:0] data_out,
               buffer_out
);

  localparam [1:0] mode = DesignConfigPackage::mode;
  localparam [0:0] enable_feature = DesignConfigPackage::enable_feature;
  Buffer buffer (
    .data_in  (data_in),
    .mode     (mode),
    .data_out (buffer_out)
  );
  assign data_out = enable_feature ? data_in : 8'h0;
endmodule


// ----- 8< ----- FILE "DesignConfigPackage.sv" ----- 8< -----

// Generated by CIRCT firtool-1.139.0-139-g13f1f1710
package DesignConfigPackage;
  // Mode selection
  parameter mode = 3;
  // Enable feature flag
  parameter enable_feature = 1;
endpackage

…tion

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.
@uenoku uenoku force-pushed the dev/hidetou/config branch from 13f1f17 to 64514b4 Compare February 2, 2026 03:27
@sequencer
Copy link
Contributor

I think we need to consider how to parameterize the interface of module, the essential problem of parameterize verilog generation are parametrized IO, Probe, Layer.

@seldridge
Copy link
Member

This is cool!

What I think may make this more palatable is if there is some prerequisite work done first. Specifically:

  1. Add a notion of a parameter type or parameter box, e.g., Parameter<Int>.
  2. Add an operation that can convert from Parameter<Int> to UInt.
  3. Have the intrinsic return a Parameter and instead use the conversion to make this a UInt.

Assuming that we want this intrinsic, this is then building infrastructure for parameterization as opposed to encapsulating all the parameterization in an intrinsic. The intrinsic would then encapsulate the mechanism for setting the parameterization as opposed to the parameterization itself.

@sequencer: There's a couple of ideas being floated for how to enable some basic parameterization. This is one idea. I do want to explore more complete parametric capture, starting with changing external module from having parameter values set on the external module to being set on the instance.

@sequencer
Copy link
Contributor

This sounds like the meta meta programming, but apparently this feature is important for ip delivery.

Basically these are some ideas that come into my mind:

  • do we need structural type defined outside the module and parameterize it?
  • do we need to emit sv.for in the verilog generation? Or more essentially, there are two meta stages after this feature landing, one at generator level, one at sv level. This pr is apparently applied to sv level, so what meta programming features in sv do we need to support?
  • How to deal with the parameters computation?
  • how to add constraints of these parameters to avoid user configuring a design which is corner case that has never been verified.
  • About ip delivery, how about a fat binary that embeds a multiple top verilog? And user can configure it w/o sv. This leaves only one meta programming, but user can configure it w/ the fat binary.

@uenoku
Copy link
Member Author

uenoku commented Feb 2, 2026

Add a notion of a parameter type or parameter box, e.g., Parameter.

The goal of this PoC is to enable post-verilog generation with least intrusive implementation for the feature FIRRTL parameterization work. So I might not consider introducing a wrapper type to be in this PR's scope (we already have semi-finished Const<Int> type so it will be also necessary to figure out how to unify them). Also verilog cannot have output parameter (I think) so more careful design work would be needed before adding a new type.

This sounds like the meta meta programming, but apparently this feature is important for ip delivery.
Basically these are some ideas that come into my mind:

These are all good questions, and that's one the reason we'll definitely need more time for proper FIRRTL parametrization. I would expect that would be going to 6-9 month/2~3 persons project from design to actual uses optimistically. This intrinsic is more of less stop-gap. Therefore the PR introduces "design config" not a parameter.

@seldridge
Copy link
Member

@uenoku wrote:

The goal of this PoC is to enable post-verilog generation with least intrusive implementation for the feature FIRRTL parameterization work. So I might not consider introducing a wrapper type to be in this PR's scope (we already have semi-finished Const type so it will be also necessary to figure out how to unify them). Also verilog cannot have output parameter (I think) so more careful design work would be needed before adding a new type.

That's fair. This intrinsic is compartmentalized such that it can later be factored into more orthogonal pieces (parameter + cast) as those orthogonal pieces are made available.

@sequencer wrote:

This pr is apparently applied to sv level, so what meta programming features in sv do we need to support?

I'd go the other direction and ask what parameterization/meta programming to do we want to capture in Chisel/FIRRTL and what does that look like. Then how do we lower it to SV parameterization/meta programming or strip it via monomorphization. Or: I tend to think of it as SV being the target, FIRRTL being the language design component, and Chisel being the metaprogramming on FIRRTL that should have APIs for accessing the FIRRTL language features.

Similar to how layers define one kind of parameterization. Instance choice (sadly incomplete...) defines another. These are compiled to SV, but their goal isn't to represent ifdef as a first-class concept, but to expose some FIRRTL language feature that we compile to ifdef.

Trying to directly represent SV features (outside of intrinsics) has always seemed dangerous.

@sequencer wrote:

About ip delivery, how about a fat binary that embeds a multiple top verilog? And user can configure it w/o sv. This leaves only one meta programming, but user can configure it w/ the fat binary.

This is always an option, even if none of these CIRCT features are available.

These are great questions, @sequencer! My inline comments are just my ideas. We're trying to figure this out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants