[FIRRTL] Set the parameters for register randomization early. (#3714)

This adds a pass that collects all registers in each module, computes
how many bits of random data should be used to initialize them, and
saves this information for each module and register. In FirRegLower,
this is used to create one large random register per module, and
select out the appropriate bits for each register in the initial
block. This ensures the same large random register is created, and the
same bits are always selected for the same register, regardless of
optimizations that may remove registers.
This commit is contained in:
Mike Urbach 2022-08-15 12:22:54 -06:00 committed by GitHub
parent a3536cb8ed
commit 261c5439af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 362 additions and 83 deletions

View File

@ -136,6 +136,8 @@ std::unique_ptr<mlir::Pass> createExtractInstancesPass();
std::unique_ptr<mlir::Pass> createIMDeadCodeElimPass();
std::unique_ptr<mlir::Pass> createRandomizeRegisterInitPass();
/// Generate the code for registering passes.
#define GEN_PASS_REGISTRATION
#include "circt/Dialect/FIRRTL/Passes.h.inc"

View File

@ -585,4 +585,19 @@ def DropName : Pass<"firrtl-drop-names", "firrtl::FModuleOp"> {
];
}
def RandomizeRegisterInit :
Pass<"firrtl-randomize-register-init", "firrtl::CircuitOp"> {
let summary = "Randomize register initialization.";
let description = [{
This pass eagerly creates a large vector of randomized bits for initializing
registers, and marks each register with attributes indicating which bits to
read. If the registers survive until LowerToHW, their initialization logic
will pick up the correct bits.
This ensures a stable initialization, so registers should always see the
same initial value for the same seed, regardless of optimization levels.
}];
let constructor = "circt::firrtl::createRandomizeRegisterInitPass()";
}
#endif // CIRCT_DIALECT_FIRRTL_PASSES_TD

View File

@ -1063,6 +1063,11 @@ FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
if (auto comment = oldModule->getAttrOfType<StringAttr>("comment"))
newModule.setCommentAttr(comment);
// Pass along the number of random initialization bits needed for this module.
if (auto randomWidth =
oldModule->getAttrOfType<IntegerAttr>("firrtl.random_init_width"))
newModule->setAttr("firrtl.random_init_width", randomWidth);
// If the circuit has an entry point, set all other modules private.
// Otherwise, mark all modules as public.
SymbolTable::setSymbolVisibility(newModule,
@ -2597,6 +2602,13 @@ LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
Backedge inputEdge = backedgeBuilder.get(resultType);
auto reg = builder.create<seq::FirRegOp>(inputEdge, clockVal,
op.getNameAttr(), symName);
// Pass along the start and end random initialization bits for this register.
if (auto randomStart = op->getAttr("firrtl.random_init_start"))
reg->setAttr("firrtl.random_init_start", randomStart);
if (auto randomEnd = op->getAttr("firrtl.random_init_end"))
reg->setAttr("firrtl.random_init_end", randomEnd);
inputEdge.setValue(reg);
circuitState.used_RANDOMIZE_REG_INIT = 1;
regMapping.try_emplace(op, reg);
@ -2630,6 +2642,13 @@ LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
auto reg =
builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
resetSignal, resetValue, symName, isAsync);
// Pass along the start and end random initialization bits for this register.
if (auto randomStart = op->getAttr("firrtl.random_init_start"))
reg->setAttr("firrtl.random_init_start", randomStart);
if (auto randomEnd = op->getAttr("firrtl.random_init_end"))
reg->setAttr("firrtl.random_init_end", randomEnd);
inputEdge.setValue(reg);
circuitState.used_RANDOMIZE_REG_INIT = 1;
regMapping.try_emplace(op, reg);

View File

@ -28,6 +28,7 @@ add_circt_dialect_library(CIRCTFIRRTLTransforms
PrefixModules.cpp
PrintInstanceGraph.cpp
PrintNLATable.cpp
RandomizeRegisterInit.cpp
RemoveUnusedPorts.cpp
SFCCompat.cpp
WireDFT.cpp

View File

@ -0,0 +1,99 @@
//===- RandomizeRegisterInit.cpp - Randomize register initialization ------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the RandomizeRegisterInit pass.
//
//===----------------------------------------------------------------------===//
#include "PassDetails.h"
#include "circt/Dialect/FIRRTL/AnnotationDetails.h"
#include "circt/Dialect/FIRRTL/FIRRTLAnnotations.h"
#include "circt/Dialect/FIRRTL/FIRRTLInstanceGraph.h"
#include "circt/Dialect/FIRRTL/FIRRTLOps.h"
#include "circt/Dialect/FIRRTL/FIRRTLTypes.h"
#include "circt/Dialect/FIRRTL/Passes.h"
#include "mlir/IR/Builders.h"
#include "llvm/Support/Parallel.h"
using namespace mlir;
using namespace circt;
using namespace firrtl;
namespace {
struct RandomizeRegisterInitPass
: public RandomizeRegisterInitBase<RandomizeRegisterInitPass> {
void runOnOperation() override;
};
} // end anonymous namespace
std::unique_ptr<mlir::Pass> circt::firrtl::createRandomizeRegisterInitPass() {
return std::make_unique<RandomizeRegisterInitPass>();
}
/// Create attributes indicating the required size of random initialization
/// values for each register in the module, and mark which range of these values
/// each register should consume. The goal is for registers to always read the
/// same random bits for the same seed, regardless of optimizations that might
/// remove registers.
static void createRandomizationAttributes(FModuleOp mod, FModuleOp dut,
InstanceGraph &instanceGraph) {
OpBuilder builder(mod);
// If there is a DUT, and this module is not it or a child of it, return
// early.
if (dut && mod != dut && !instanceGraph.isAncestor(mod, dut))
return;
// Walk all registers.
uint64_t width = 0;
auto ui64Type = builder.getIntegerType(64, false);
mod.walk([&](Operation *op) {
if (!isa<RegOp, RegResetOp>(op))
return;
// Compute the width of all registers, and remember which bits are assigned
// to each register.
auto regType = op->getResult(0).getType().cast<FIRRTLBaseType>();
Optional<int64_t> regWidth = getBitWidth(regType);
assert(regWidth.has_value() && "register must have a valid FIRRTL width");
auto start = builder.getIntegerAttr(ui64Type, width);
auto end = builder.getIntegerAttr(ui64Type, width + regWidth.value() - 1);
op->setAttr("firrtl.random_init_start", start);
op->setAttr("firrtl.random_init_end", end);
width += regWidth.value();
});
// Remember the width of the random vector in the module's attributes so
// LowerSeqToSV can grab it to create the appropriate random register.
if (width > 0)
mod->setAttr("firrtl.random_init_width",
builder.getIntegerAttr(ui64Type, width));
}
void RandomizeRegisterInitPass::runOnOperation() {
CircuitOp circuit = getOperation();
auto &instanceGraph = getAnalysis<InstanceGraph>();
// Look for a DUT annotation.
FModuleOp dut;
for (auto mod : circuit.getOps<FModuleOp>()) {
if (AnnotationSet(mod).hasAnnotation(dutAnnoClass)) {
dut = mod;
break;
}
}
// Process each module in parallel.
auto modules = SmallVector<FModuleOp>(circuit.getOps<FModuleOp>());
llvm::parallelForEach(modules, [&](FModuleOp mod) {
createRandomizationAttributes(mod, dut, instanceGraph);
});
}

View File

@ -82,6 +82,8 @@ public:
};
} // namespace
constexpr uint64_t randomWidth = 32;
namespace {
/// Lower FirRegOp to `sv.reg` and `sv.always`.
class FirRegLower {
@ -90,11 +92,18 @@ public:
void lower();
using SymbolAndRange = std::pair<Attribute, std::pair<uint64_t, uint64_t>>;
private:
using AsyncResetSignal = std::pair<Value, Value>;
std::pair<sv::RegOp, llvm::Optional<AsyncResetSignal>> lower(FirRegOp reg);
bool isInitialisePreset(sv::RegOp);
void initialisePreset(OpBuilder &regBuilder, OpBuilder &initBuilder,
sv::RegOp reg);
void initialise(OpBuilder &regBuilder, OpBuilder &initBuilder, sv::RegOp reg);
void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
@ -113,7 +122,8 @@ private:
/// This is a map from block to a pair of a random value and its unused
/// bits. It is used to reduce the number of random value.
std::pair<Value, unsigned> randomValueAndRemain;
std::pair<Value, uint64_t> randomValueAndRemain;
sv::RegOp presetRandomValue;
};
} // namespace
@ -167,7 +177,11 @@ void FirRegLower::lower() {
builder.create<sv::IfDefProceduralOp>(randInitRef, [&] {
// Create initialisers for all registers.
for (auto &[svReg, asyncReset] : toInit) {
initialise(regBuilder, builder, svReg);
if (isInitialisePreset(svReg))
initialisePreset(regBuilder, builder, svReg);
else
initialise(regBuilder, builder, svReg);
if (asyncReset) {
auto &[resetSignal, resetValue] = *asyncReset;
resets[resetSignal].emplace_back(svReg, resetValue);
@ -203,6 +217,8 @@ void FirRegLower::lower() {
});
});
}
module->removeAttr("firrtl.random_init_width");
}
std::pair<sv::RegOp, llvm::Optional<FirRegLower::AsyncResetSignal>>
@ -249,17 +265,160 @@ FirRegLower::lower(FirRegOp reg) {
return {svReg, asyncReset};
}
void FirRegLower::initialise(OpBuilder &regBuilder, OpBuilder &initBuilder,
sv::RegOp reg) {
typedef std::pair<Attribute, std::pair<unsigned, unsigned>> SymbolAndRange;
static void emitRandomInit(
hw::HWModuleOp module, sv::RegOp reg, OpBuilder &builder,
uint64_t randomRegWidth,
llvm::function_ref<void(IntegerType,
SmallVector<FirRegLower::SymbolAndRange> &)>
getRandomValues) {
auto regDefSym =
hw::InnerRefAttr::get(module.getNameAttr(), reg.getInnerSymAttr());
// Get a random value with the specified width, combining or truncating
// 32-bit units as necessary.
auto emitRandomInit = [&](Value dest, Type type, const Twine &accessor) {
auto intType = type.cast<IntegerType>();
if (intType.getWidth() == 0)
return;
SmallVector<FirRegLower::SymbolAndRange> values;
getRandomValues(intType, values);
SmallString<32> rhs(("{{0}}" + accessor + " = ").str());
unsigned i = 1;
SmallVector<Attribute, 4> symbols({regDefSym});
if (values.size() > 1)
rhs.append("{");
for (auto [value, range] : llvm::reverse(values)) {
symbols.push_back(value);
auto [high, low] = range;
if (i > 1)
rhs.append(", ");
rhs.append(("{{" + Twine(i++) + "}}").str());
// This uses all bits of the random value. Emit without part select.
if (high == randomWidth - 1 && low == 0 && randomRegWidth == randomWidth)
continue;
// Emit a single bit part select, e.g., emit "[0]" and not "[0:0]".
if (high == low) {
rhs.append(("[" + Twine(high) + "]").str());
continue;
}
// Emit a part select, e.g., "[4:2]"
rhs.append(
("[" + Twine(range.first) + ":" + Twine(range.second) + "]").str());
}
if (values.size() > 1)
rhs.append("}");
rhs.append(";");
builder.create<sv::VerbatimOp>(reg.getLoc(), rhs, ValueRange{},
builder.getArrayAttr(symbols));
};
// Randomly initialize everything in the register. If the register
// is an aggregate type, then assign random values to all its
// constituent ground types.
auto type = reg.getType().dyn_cast<hw::InOutType>().getElementType();
std::function<void(Type, const Twine &)> recurse = [&](Type type,
const Twine &member) {
TypeSwitch<Type>(type)
.Case<hw::UnpackedArrayType, hw::ArrayType>([&](auto a) {
for (size_t i = 0, e = a.getSize(); i != e; ++i)
recurse(a.getElementType(), member + "[" + Twine(i) + "]");
})
.Case<hw::StructType>([&](hw::StructType s) {
for (auto elem : s.getElements())
recurse(elem.type, member + "." + elem.name.getValue());
})
.Default([&](auto type) { emitRandomInit(reg, type, member); });
};
recurse(type, "");
}
bool FirRegLower::isInitialisePreset(sv::RegOp reg) {
return module->hasAttr("firrtl.random_init_width") &&
reg->hasAttr("firrtl.random_init_start") &&
reg->hasAttr("firrtl.random_init_end");
}
void FirRegLower::initialisePreset(OpBuilder &regBuilder,
OpBuilder &initBuilder, sv::RegOp reg) {
// Extract the required random initialisation width and compute the number of
// `RANDOM calls required to fill it up, as well as the final register width.
auto randomInitWidthAttr =
module->getAttrOfType<IntegerAttr>("firrtl.random_init_width");
assert(randomInitWidthAttr &&
"firrtl.random_init_width required for preset initialisation");
uint64_t randomInitWidth = randomInitWidthAttr.getUInt();
assert(randomInitWidth > 0 &&
"random initialization width should be non-zero");
uint64_t numRandomSources = llvm::divideCeil(randomInitWidth, randomWidth);
uint64_t randomRegWidth = randomWidth * numRandomSources;
// Only create the random register once.
if (presetRandomValue == nullptr) {
// Declare the random register.
auto randReg = regBuilder.create<sv::RegOp>(
reg.getLoc(), regBuilder.getIntegerType(randomRegWidth),
/*name=*/regBuilder.getStringAttr("_RANDOM"),
/*inner_sym=*/
regBuilder.getStringAttr(ns.newName(Twine("_RANDOM"))));
presetRandomValue = randReg;
SmallString<32> randomRegAssign("{{0}} = {");
// Fill the random register by concatenating calls to `RANDOM in a verbatim
// string.
for (uint64_t i = 0; i < numRandomSources; ++i) {
randomRegAssign.append("`RANDOM");
if (i < numRandomSources - 1)
randomRegAssign.append(",");
// Add a line break when line length gets close to 1000 characters.
if (i > 0 && i % 125 == 0)
randomRegAssign.append("\n");
}
randomRegAssign.append("};");
// Assign the concatenated calls to the declared register.
initBuilder.create<sv::VerbatimOp>(
reg.getLoc(), initBuilder.getStringAttr(randomRegAssign), ValueRange{},
initBuilder.getArrayAttr({hw::InnerRefAttr::get(
module.getNameAttr(), randReg.getInnerSymAttr())}));
}
auto getRandomValues = [&](IntegerType type,
SmallVector<SymbolAndRange> &values) {
assert(type.getWidth() != 0 && "zero bit width's not supported");
auto randomStart =
reg->getAttrOfType<IntegerAttr>("firrtl.random_init_start").getUInt();
auto randomEnd =
reg->getAttrOfType<IntegerAttr>("firrtl.random_init_end").getUInt();
reg->removeAttr("firrtl.random_init_start");
reg->removeAttr("firrtl.random_init_end");
auto randReg = presetRandomValue;
auto symbol =
hw::InnerRefAttr::get(module.getNameAttr(), randReg.getInnerSymAttr());
values.push_back({symbol, {randomEnd, randomStart}});
};
emitRandomInit(module, reg, initBuilder, randomRegWidth, getRandomValues);
}
void FirRegLower::initialise(OpBuilder &regBuilder, OpBuilder &initBuilder,
sv::RegOp reg) {
// Construct and return a new reference to `RANDOM. It is always a 32-bit
// unsigned expression. Calls to $random have side effects, so we use
// VerbatimExprSEOp.
constexpr unsigned randomWidth = 32;
auto getRandom32Val = [&](const char *suffix = "") -> Value {
sv::RegOp randReg = regBuilder.create<sv::RegOp>(
reg.getLoc(), regBuilder.getIntegerType(randomWidth),
@ -300,68 +459,7 @@ void FirRegLower::initialise(OpBuilder &regBuilder, OpBuilder &initBuilder,
}
};
// Get a random value with the specified width, combining or truncating
// 32-bit units as necessary.
auto emitRandomInit = [&](Value dest, Type type, const Twine &accessor) {
auto intType = type.cast<IntegerType>();
if (intType.getWidth() == 0)
return;
SmallVector<SymbolAndRange> values;
getRandomValues(intType, values);
SmallString<32> rhs(("{{0}}" + accessor + " = ").str());
unsigned i = 1;
SmallVector<Attribute, 4> symbols({regDefSym});
if (values.size() > 1)
rhs.append("{");
for (auto [value, range] : llvm::reverse(values)) {
symbols.push_back(value);
auto [high, low] = range;
if (i > 1)
rhs.append(", ");
rhs.append(("{{" + Twine(i++) + "}}").str());
// This uses all bits of the random value. Emit without part select.
if (high == randomWidth - 1 && low == 0)
continue;
// Emit a single bit part select, e.g., emit "[0]" and not "[0:0]".
if (high == low) {
rhs.append(("[" + Twine(high) + "]").str());
continue;
}
// Emit a part select, e.g., "[4:2]"
rhs.append(
("[" + Twine(range.first) + ":" + Twine(range.second) + "]").str());
}
if (values.size() > 1)
rhs.append("}");
rhs.append(";");
initBuilder.create<sv::VerbatimOp>(reg.getLoc(), rhs, ValueRange{},
initBuilder.getArrayAttr(symbols));
};
// Randomly initialize everything in the register. If the register
// is an aggregate type, then assign random values to all its
// constituent ground types.
auto type = reg.getType().dyn_cast<hw::InOutType>().getElementType();
std::function<void(Type, const Twine &)> recurse = [&](Type type,
const Twine &member) {
TypeSwitch<Type>(type)
.Case<hw::UnpackedArrayType, hw::ArrayType>([&](auto a) {
for (size_t i = 0, e = a.getSize(); i != e; ++i)
recurse(a.getElementType(), member + "[" + Twine(i) + "]");
})
.Case<hw::StructType>([&](hw::StructType s) {
for (auto elem : s.getElements())
recurse(elem.type, member + "." + elem.name.getValue());
})
.Default([&](auto type) { emitRandomInit(reg, type, member); });
};
recurse(type, "");
emitRandomInit(module, reg, initBuilder, randomWidth, getRandomValues);
}
void FirRegLower::addToAlwaysBlock(sv::EventControl clockEdge, Value clock,

View File

@ -13,8 +13,8 @@ circuit Example :
output out : UInt<1>[2]
wire bar : UInt<1>
bar is invalid
; CHECK: %foo0 = firrtl.reg %clock :
; CHECK: %foo1 = firrtl.reg %clock :
; CHECK: %foo0 = firrtl.reg %clock
; CHECK: %foo1 = firrtl.reg %clock
reg foo0 : UInt<1>, clock with : (reset => (arst, bar))
reg foo1 : UInt<1>, clock with : (reset => (srst, bar))
foo0 <= in
@ -40,12 +40,12 @@ circuit Example :
bar is invalid
bar.a[1] <= UInt<1>(0)
; CHECK: %foo0_a_0 = firrtl.reg %clock :
; CHECK: %foo0_a_0 = firrtl.reg %clock
; CHECK: %foo0_a_1 = firrtl.regreset %clock, %arst,
; CHECK: %foo0_b = firrtl.reg %clock :
; CHECK: %foo1_a_0 = firrtl.reg %clock :
; CHECK: %foo0_b = firrtl.reg %clock
; CHECK: %foo1_a_0 = firrtl.reg %clock
; CHECK: %foo1_a_1 = firrtl.regreset %clock, %srst,
; CHECK: %foo1_b = firrtl.reg %clock :
; CHECK: %foo1_b = firrtl.reg %clock
reg foo0 : {a : UInt<1>[2], b : UInt<1>}, clock with : (reset => (arst, bar))
reg foo1 : {a : UInt<1>[2], b : UInt<1>}, clock with : (reset => (srst, bar))
foo0 <= in
@ -104,9 +104,9 @@ circuit Example :
arst <= asAsyncReset(UInt<1>(0))
srst <= UInt<1>(0)
; CHECK: %foo0 = firrtl.reg %clock :
; CHECK: %foo1 = firrtl.reg %clock :
; CHECK: %foo2 = firrtl.reg %clock :
; CHECK: %foo0 = firrtl.reg %clock
; CHECK: %foo1 = firrtl.reg %clock
; CHECK: %foo2 = firrtl.reg %clock
reg foo0 : UInt<2>, clock with : (reset => (rst, UInt(3)))
reg foo1 : UInt<2>, clock with : (reset => (arst, UInt(3)))
reg foo2 : UInt<2>, clock with : (reset => (srst, UInt(3)))

View File

@ -101,7 +101,7 @@ circuit test_mod : %[[{"class": "circt.testNT", "data": "a"}]]
out_multibitMux <= multibitMux.b
; MLIR-LABEL: firrtl.module @test_mod(in %clock: !firrtl.clock, in %a: !firrtl.uint<1>, in %b: !firrtl.uint<2>, out %c: !firrtl.uint<1>, in %vec_0: !firrtl.uint<1>, in %vec_1: !firrtl.uint<1>, in %vec_2: !firrtl.uint<1>, out %out_implicitTrunc: !firrtl.uint<1>, out %out_prettifyExample: !firrtl.uint<1>, out %out_multibitMux: !firrtl.uint<1>) {
; MLIR-LABEL: firrtl.module @test_mod(in %clock: !firrtl.clock, in %a: !firrtl.uint<1>, in %b: !firrtl.uint<2>, out %c: !firrtl.uint<1>, in %vec_0: !firrtl.uint<1>, in %vec_1: !firrtl.uint<1>, in %vec_2: !firrtl.uint<1>, out %out_implicitTrunc: !firrtl.uint<1>, out %out_prettifyExample: !firrtl.uint<1>, out %out_multibitMux: !firrtl.uint<1>) {{.*}}{
; MLIR-NEXT: %cat_a, %cat_b, %cat_c, %cat_d = firrtl.instance cat @Cat(in a: !firrtl.uint<2>, in b: !firrtl.uint<2>, in c: !firrtl.uint<2>, out d: !firrtl.uint<6>)
; MLIR-NEXT: firrtl.strictconnect %cat_a, %b : !firrtl.uint<2>
; MLIR-NEXT: firrtl.strictconnect %cat_b, %b : !firrtl.uint<2>

View File

@ -5,7 +5,7 @@
; CHECK-LABEL: firrtl.module @Issue794
; CHECK-SAME: (in %clock: !firrtl.clock,
; CHECK: %[[memory_0:.+]] = firrtl.reg %clock : !firrtl.uint<8>
; CHECK: %[[memory_0:.+]] = firrtl.reg %clock {{.*}}: !firrtl.uint<8>
; CHECK: firrtl.mux(%[[v14:.+]], %wData_0, %[[memory_0]])
; CHECK: firrtl.mux(%[[v19:.+]], %wData_1, %[[v5:.+]])
; CHECK: firrtl.strictconnect %[[memory_0]], %[[v22:.+]]

View File

@ -0,0 +1,38 @@
; RUN: firtool -preserve-values=all -verilog %s | FileCheck %s --check-prefix=ALL
; RUN: firtool -preserve-values=named -verilog %s | FileCheck %s --check-prefix=NAMED
; RUN: firtool -preserve-values=none -verilog %s | FileCheck %s --check-prefix=NONE
circuit Foo:
module Foo:
input clock: Clock
input d: UInt<33>
output q: UInt<33>
; ALL: reg [127:0] _RANDOM;
; ALL: _RANDOM = {`RANDOM,`RANDOM,`RANDOM,`RANDOM};
; NAMED: reg [127:0] _RANDOM;
; NAMED: _RANDOM = {`RANDOM,`RANDOM,`RANDOM,`RANDOM};
; NONE: reg [127:0] _RANDOM;
; NONE: _RANDOM = {`RANDOM,`RANDOM,`RANDOM,`RANDOM};
; ALL: _r = _RANDOM[32:0];
; NAMED-NOT: _r = {{.*}};
; NONE-NOT: _r = {{.*}};
reg _r: UInt<33>, clock
_r <= d
; ALL: r = _RANDOM[65:33];
; NAMED: r = _RANDOM[65:33];
; NONE-NOT: r = {{.*}};
reg r: UInt<33>, clock
r <= d
; ALL: s = _RANDOM[98:66];
; NAMED: s = _RANDOM[98:66];
; NONE: s = _RANDOM[98:66];
reg s: UInt<33>, clock
s <= d
q <= s

View File

@ -558,6 +558,16 @@ processBuffer(MLIRContext &context, TimingScope &ts, llvm::SourceMgr &sourceMgr,
}
}
if (inliner)
pm.nest<firrtl::CircuitOp>().addPass(firrtl::createInlinerPass());
// Preset the random initialization parameters for each module. The current
// implementation assumes it can run at a time where every register is
// currently in the final module it will be emitted in, all registers have
// been created, and no registers have yet been removed.
pm.nest<firrtl::CircuitOp>().addPass(
firrtl::createRandomizeRegisterInitPass());
if (checkCombCycles)
pm.nest<firrtl::CircuitOp>().addPass(firrtl::createCheckCombCyclesPass());
@ -578,9 +588,6 @@ processBuffer(MLIRContext &context, TimingScope &ts, llvm::SourceMgr &sourceMgr,
if (prefixModules)
pm.nest<firrtl::CircuitOp>().addPass(firrtl::createPrefixModulesPass());
if (inliner)
pm.nest<firrtl::CircuitOp>().addPass(firrtl::createInlinerPass());
if (imconstprop && !disableOptimization)
pm.nest<firrtl::CircuitOp>().addPass(firrtl::createIMConstPropPass());