11 #include "mlir/IR/Threading.h"
12 #include "mlir/Transforms/DialectConversion.h"
13 #include "llvm/ADT/DenseSet.h"
14 #include "llvm/Support/Debug.h"
16 using namespace circt;
19 using llvm::MapVector;
21 #define DEBUG_TYPE "lower-seq-firreg"
27 llvm::DenseSet<Operation *> &forwardSlice,
28 llvm::function_ref<
bool(Operation *)> filter =
nullptr) {
29 SmallVector<Operation *> worklist({root});
31 while (!worklist.empty()) {
32 Operation *op = worklist.pop_back_val();
37 if (filter && !filter(op))
40 for (Region ®ion : op->getRegions())
41 for (Block &block : region)
42 for (Operation &blockOp : block)
43 if (forwardSlice.insert(&blockOp).second)
44 worklist.push_back(&blockOp);
45 for (Value result : op->getResults())
46 for (Operation *userOp : result.getUsers())
47 if (forwardSlice.insert(userOp).second)
48 worklist.push_back(userOp);
50 forwardSlice.insert(op);
55 const std::function<
void()> &trueSide,
56 const std::function<
void()> &falseSide) {
57 auto op = ifCache.lookup({
builder.getBlock(), cond});
62 builder.create<sv::IfOp>(cond.getLoc(), cond, trueSide, falseSide);
63 ifCache.insert({{
builder.getBlock(), cond}, newIfOp});
65 OpBuilder::InsertionGuard guard(
builder);
66 builder.setInsertionPointToEnd(op.getThenBlock());
68 builder.setInsertionPointToEnd(op.getElseBlock());
75 auto regs = module.getOps<seq::FirRegOp>();
80 SmallVector<RegLowerInfo> randomInit, presetInit;
81 llvm::MapVector<Value, SmallVector<RegLowerInfo>> asyncResets;
82 for (
auto reg : llvm::make_early_inc_range(regs)) {
83 auto svReg = lower(
reg);
85 presetInit.push_back(svReg);
86 else if (!disableRegRandomization)
87 randomInit.push_back(svReg);
89 if (svReg.asyncResetSignal)
90 asyncResets[svReg.asyncResetSignal].emplace_back(svReg);
98 for (
auto reg : randomInit)
99 if (
reg.randStart >= 0)
100 maxBit = std::max(maxBit, (uint64_t)
reg.randStart +
reg.width);
102 for (
auto &
reg : randomInit) {
103 if (
reg.randStart == -1) {
104 reg.randStart = maxBit;
121 if (randomInit.empty() && presetInit.empty() && asyncResets.empty())
124 auto loc = module.getLoc();
125 MLIRContext *context = module.getContext();
129 ImplicitLocOpBuilder::atBlockTerminator(loc, module.getBodyBlock());
132 builder.create<sv::OrderedOutputOp>([&] {
134 builder.create<sv::VerbatimOp>(
"`FIRRTL_BEFORE_INITIAL");
137 builder.create<sv::InitialOp>([&] {
138 if (!randomInit.empty()) {
139 builder.create<sv::IfDefProceduralOp>(
"INIT_RANDOM_PROLOG_", [&] {
140 builder.create<sv::VerbatimOp>(
"`INIT_RANDOM_PROLOG_");
142 builder.create<sv::IfDefProceduralOp>(randInitRef, [&] {
144 SmallVector<Value> randValues;
145 auto numRandomCalls = (maxBit + 31) / 32;
146 auto logic =
builder.create<sv::LogicOp>(
153 auto inducionVariableWidth = llvm::Log2_64_Ceil(numRandomCalls + 1);
154 auto arrayIndexWith = llvm::Log2_64_Ceil(numRandomCalls);
156 getOrCreateConstant(loc, APInt::getZero(inducionVariableWidth));
157 auto ub = getOrCreateConstant(
158 loc, APInt(inducionVariableWidth, numRandomCalls));
160 getOrCreateConstant(loc, APInt(inducionVariableWidth, 1));
161 auto forLoop =
builder.create<sv::ForOp>(
162 loc, lb, ub, step,
"i", [&](BlockArgument iter) {
163 auto rhs =
builder.create<sv::MacroRefExprSEOp>(
164 loc,
builder.getIntegerType(32),
"RANDOM");
165 Value iterValue = iter;
166 if (!iter.getType().isInteger(arrayIndexWith))
168 loc, iterValue, 0, arrayIndexWith);
169 auto lhs =
builder.create<sv::ArrayIndexInOutOp>(loc, logic,
171 builder.create<sv::BPAssignOp>(loc, lhs, rhs);
173 builder.setInsertionPointAfter(forLoop);
174 for (uint64_t x = 0; x < numRandomCalls; ++x) {
175 auto lhs =
builder.create<sv::ArrayIndexInOutOp>(
177 getOrCreateConstant(loc, APInt(arrayIndexWith, x)));
178 randValues.push_back(lhs.getResult());
182 for (
auto &svReg : randomInit)
183 initialize(
builder, svReg, randValues);
187 if (!presetInit.empty()) {
188 for (
auto &svReg : presetInit) {
189 auto loc = svReg.reg.getLoc();
190 auto cst = getOrCreateConstant(loc, svReg.preset.getValue());
191 builder.create<sv::BPAssignOp>(loc, svReg.reg, cst);
195 if (!asyncResets.empty()) {
199 for (
auto &reset : asyncResets) {
203 builder.create<sv::IfOp>(reset.first, [&] {
204 for (
auto &
reg : reset.second)
206 reg.asyncResetValue);
213 builder.create<sv::VerbatimOp>(
"`FIRRTL_AFTER_INITIAL");
218 module->removeAttr(
"firrtl.random_init_width");
234 return c1.getType() == c2.getType() &&
235 c1.getValue() == c2.getValue() &&
245 if (!andOp || !andOp.getTwoState()) {
246 llvm::SetVector<Value> ret;
251 return llvm::SetVector<Value>(andOp.getOperands().begin(),
252 andOp.getOperands().end());
256 auto constantIndex =
value.template getDefiningOp<hw::ConstantOp>();
258 return constantIndex.getValue();
267 std::optional<std::tuple<Value, Value, Value>>
271 SmallVector<Value> muxConditions;
274 SmallVector<Value> reverseOpValues(llvm::reverse(nextRegValue.getOperands()));
275 if (!llvm::all_of(llvm::enumerate(reverseOpValues), [&](
auto idxAndValue) {
277 auto [i,
value] = idxAndValue;
278 auto mux =
value.template getDefiningOp<comb::MuxOp>();
280 if (!mux || !mux.getTwoState())
283 if (trueVal && trueVal != mux.getTrueValue())
286 trueVal = mux.getTrueValue();
287 muxConditions.push_back(mux.getCond());
291 mux.getFalseValue().template getDefiningOp<hw::ArrayGetOp>();
300 llvm::SetVector<Value> commonConditions =
302 for (
auto condition : ArrayRef(muxConditions).drop_front()) {
304 commonConditions.remove_if([&](
auto v) {
return !cond.contains(v); });
307 for (
auto [idx, condition] : llvm::enumerate(muxConditions)) {
311 extractedConditions.remove_if(
312 [&](
auto v) {
return commonConditions.contains(v); });
313 if (extractedConditions.size() != 1)
317 (*extractedConditions.begin()).getDefiningOp<comb::ICmpOp>();
318 if (!indexCompare || !indexCompare.getTwoState() ||
319 indexCompare.getPredicate() != comb::ICmpPredicate::eq)
322 if (indexValue && indexValue != indexCompare.getLhs())
325 indexValue = indexCompare.getLhs();
330 OpBuilder::InsertionGuard guard(
builder);
332 Value commonConditionValue;
333 if (commonConditions.empty())
334 commonConditionValue = getOrCreateConstant(
reg.getLoc(), APInt(1, 1));
337 reg.getLoc(),
builder.getI1Type(), commonConditions.takeVector(),
true);
338 return std::make_tuple(commonConditionValue, indexValue, trueVal);
350 auto firReg = term.getDefiningOp<seq::FirRegOp>();
351 DenseSet<Operation *> regMuxFanout;
353 return op == firReg || !isa<sv::RegOp, seq::FirRegOp, hw::InstanceOp>(op);
356 SmallVector<std::tuple<Block *, Value, Value, Value>> worklist;
357 auto addToWorklist = [&](Value
reg, Value term, Value next) {
358 worklist.push_back({
builder.getBlock(),
reg, term, next});
361 auto getArrayIndex = [&](Value
reg, Value idx) {
363 OpBuilder::InsertionGuard guard(
builder);
365 return builder.create<sv::ArrayIndexInOutOp>(
reg.getLoc(),
reg, idx);
368 SmallVector<Value, 8> opsToDelete;
369 addToWorklist(
reg, term, next);
370 while (!worklist.empty()) {
371 OpBuilder::InsertionGuard guard(
builder);
373 Value
reg, term, next;
374 std::tie(block,
reg, term, next) = worklist.pop_back_val();
375 builder.setInsertionPointToEnd(block);
382 if (mux && mux.getTwoState() && regMuxFanout.contains(mux)) {
385 [&]() { addToWorklist(reg, term, mux.getTrueValue()); },
386 [&]() { addToWorklist(reg, term, mux.getFalseValue()); });
393 if (
auto matchResultOpt =
394 tryRestoringSubaccess(
builder,
reg, term, array)) {
395 Value cond, index, trueValue;
396 std::tie(cond, index, trueValue) = *matchResultOpt;
400 Value nextReg = getArrayIndex(
reg, index);
406 opsToDelete.push_back(termElement);
407 addToWorklist(nextReg, termElement, trueValue);
410 ++numSubaccessRestored;
416 for (
auto [idx,
value] : llvm::enumerate(array.getOperands())) {
417 idx = array.getOperands().size() - idx - 1;
419 auto idxVal = getOrCreateConstant(
421 APInt(std::max(1u, llvm::Log2_64_Ceil(array.getOperands().size())),
424 auto &index = arrayIndexCache[{
reg, idx}];
426 index = getArrayIndex(
reg, idxVal);
433 opsToDelete.push_back(termElement);
434 addToWorklist(index, termElement,
value);
439 builder.create<sv::PAssignOp>(term.getLoc(),
reg, next);
442 while (!opsToDelete.empty()) {
443 auto value = opsToDelete.pop_back_val();
445 value.getDefiningOp()->erase();
450 Location loc =
reg.getLoc();
451 Type regTy = typeConverter.convertType(
reg.getType());
454 RegLowerInfo svReg{
nullptr,
reg.getPresetAttr(),
nullptr,
nullptr, -1, 0};
458 if (
auto attr =
reg->getAttrOfType<IntegerAttr>(
"firrtl.random_init_start"))
459 svReg.randStart = attr.getUInt();
462 reg->removeAttr(
"firrtl.random_init_start");
465 svReg.reg->setDialectAttrs(
reg->getDialectAttrs());
467 if (
auto innerSymAttr =
reg.getInnerSymAttr())
468 svReg.reg.setInnerSymAttr(innerSymAttr);
472 if (
reg.hasReset()) {
474 module.getBodyBlock(), sv::EventControl::AtPosEdge,
reg.getClk(),
478 if (reg.getIsAsync() && areEquivalentValues(reg, reg.getNext()))
479 b.create<sv::PAssignOp>(reg.getLoc(), svReg.reg, reg);
481 createTree(b, svReg.reg, reg, reg.getNext());
483 reg.getIsAsync() ? ResetType::AsyncReset : ResetType::SyncReset,
484 sv::EventControl::AtPosEdge,
reg.getReset(),
486 builder.create<sv::PAssignOp>(loc, svReg.reg, reg.getResetValue());
488 if (
reg.getIsAsync()) {
489 svReg.asyncResetSignal =
reg.getReset();
490 svReg.asyncResetValue =
reg.getResetValue();
494 module.getBodyBlock(), sv::EventControl::AtPosEdge,
reg.getClk(),
495 [&](OpBuilder &b) { createTree(b, svReg.reg, reg, reg.getNext()); });
498 reg.replaceAllUsesWith(regVal.getResult());
513 if (
auto intTy = hw::type_dyn_cast<IntegerType>(type)) {
515 pos -= intTy.getWidth();
518 builder.create<sv::BPAssignOp>(loc,
reg, elem);
519 }
else if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type)) {
520 for (
unsigned i = 0, e = array.getNumElements(); i < e; ++i) {
521 auto index = getOrCreateConstant(loc, APInt(llvm::Log2_64_Ceil(e), i));
522 initializeRegisterElements(
526 }
else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type)) {
527 for (
auto e : structType.getElements())
528 initializeRegisterElements(
530 builder.create<sv::StructFieldInOutOp>(loc,
reg, e.name),
533 assert(
false &&
"unsupported type");
539 ArrayRef<Value> rands) {
540 auto loc =
reg.reg.getLoc();
541 SmallVector<Value> nibbles;
546 uint64_t offset =
reg.randStart;
548 auto index = offset / 32;
549 auto start = offset % 32;
550 auto nwidth = std::min(32 - start,
width);
554 nibbles.push_back(elem);
559 unsigned pos =
reg.width;
565 Block *block, sv::EventControl clockEdge, Value clock,
566 const std::function<
void(OpBuilder &)> &body, ::ResetType resetStyle,
567 sv::EventControl resetEdge, Value reset,
568 const std::function<
void(OpBuilder &)> &resetBody) {
569 auto loc = clock.getLoc();
570 auto builder = ImplicitLocOpBuilder::atBlockTerminator(loc, block);
572 resetStyle, resetEdge, reset};
574 sv::AlwaysOp alwaysOp;
576 if (!emitSeparateAlwaysBlocks) {
577 std::tie(alwaysOp, insideIfOp) = alwaysBlocks[key];
582 assert(resetStyle != ::ResetType::NoReset);
595 auto createIfOp = [&]() {
598 insideIfOp =
builder.create<sv::IfOp>(
599 reset, []() {}, []() {});
601 if (resetStyle == ::ResetType::AsyncReset) {
602 sv::EventControl events[] = {clockEdge, resetEdge};
603 Value clocks[] = {clock, reset};
605 alwaysOp =
builder.create<sv::AlwaysOp>(events, clocks, [&]() {
606 if (resetEdge == sv::EventControl::AtNegEdge)
607 llvm_unreachable(
"negative edge for reset is not expected");
611 alwaysOp =
builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
615 alwaysOp =
builder.create<sv::AlwaysOp>(clockEdge, clock);
616 insideIfOp =
nullptr;
621 assert(insideIfOp &&
"reset body must be initialized before");
623 ImplicitLocOpBuilder::atBlockEnd(loc, insideIfOp.getThenBlock());
624 resetBody(resetBuilder);
627 ImplicitLocOpBuilder::atBlockEnd(loc, insideIfOp.getElseBlock());
631 ImplicitLocOpBuilder::atBlockEnd(loc, alwaysOp.getBodyBlock());
635 if (!emitSeparateAlwaysBlocks) {
636 alwaysBlocks[key] = {alwaysOp, insideIfOp};
assert(baseType &&"element must be base type")
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
static bool areEquivalentValues(Value term, Value next)
static llvm::SetVector< Value > extractConditions(Value value)
static void getForwardSliceSimple(Operation *root, llvm::DenseSet< Operation * > &forwardSlice, llvm::function_ref< bool(Operation *)> filter=nullptr)
static std::optional< APInt > getConstantValue(Value value)
void initialize(OpBuilder &builder, RegLowerInfo reg, ArrayRef< Value > rands)
void addToIfBlock(OpBuilder &builder, Value cond, const std::function< void()> &trueSide, const std::function< void()> &falseSide)
std::optional< std::tuple< Value, Value, Value > > tryRestoringSubaccess(OpBuilder &builder, Value reg, Value term, hw::ArrayCreateOp nextRegValue)
std::tuple< Block *, sv::EventControl, Value, ResetType, sv::EventControl, Value > AlwaysKeyType
void createTree(OpBuilder &builder, Value reg, Value term, Value next)
void initializeRegisterElements(Location loc, OpBuilder &builder, Value reg, Value rand, unsigned &pos)
void addToAlwaysBlock(Block *block, sv::EventControl clockEdge, Value clock, const std::function< void(OpBuilder &)> &body, ResetType resetStyle={}, sv::EventControl resetEdge={}, Value reset={}, const std::function< void(OpBuilder &)> &resetBody={})
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
circt::hw::InOutType InOutType
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)