26 #include "mlir/IR/Builders.h"
27 #include "mlir/IR/DialectImplementation.h"
28 #include "mlir/IR/ImplicitLocOpBuilder.h"
29 #include "mlir/IR/Threading.h"
30 #include "mlir/Pass/Pass.h"
31 #include "mlir/Transforms/DialectConversion.h"
32 #include "llvm/ADT/IntervalMap.h"
33 #include "llvm/ADT/TypeSwitch.h"
34 #include "llvm/Support/Mutex.h"
36 #define DEBUG_TYPE "lower-seq-to-sv"
38 using namespace circt;
41 using llvm::MapVector;
44 #define GEN_PASS_DEF_LOWERSEQTOSV
45 #include "circt/Conversion/Passes.h.inc"
47 struct SeqToSVPass :
public impl::LowerSeqToSVBase<SeqToSVPass> {
49 void runOnOperation()
override;
51 using LowerSeqToSVBase<SeqToSVPass>::lowerToAlwaysFF;
52 using LowerSeqToSVBase<SeqToSVPass>::disableRegRandomization;
53 using LowerSeqToSVBase<SeqToSVPass>::emitSeparateAlwaysBlocks;
54 using LowerSeqToSVBase<SeqToSVPass>::LowerSeqToSVBase;
55 using LowerSeqToSVBase<SeqToSVPass>::numSubaccessRestored;
62 template <
typename OpTy>
65 CompRegLower(TypeConverter &typeConverter, MLIRContext *context,
68 lowerToAlwaysFF(lowerToAlwaysFF) {}
73 matchAndRewrite(OpTy
reg, OpAdaptor adaptor,
74 ConversionPatternRewriter &rewriter)
const final {
75 Location loc =
reg.getLoc();
78 ConversionPattern::getTypeConverter()->convertType(
reg.getType());
80 auto svReg = rewriter.create<
sv::RegOp>(loc, regTy,
reg.getNameAttr(),
81 reg.getInnerSymAttr(),
82 reg.getPowerOnValue());
83 svReg->setDialectAttrs(
reg->getDialectAttrs());
89 auto assignValue = [&] {
90 createAssign(rewriter,
reg.getLoc(), svReg,
reg);
92 auto assignReset = [&] {
93 rewriter.create<sv::PAssignOp>(loc, svReg, adaptor.getResetValue());
96 if (adaptor.getReset() && adaptor.getResetValue()) {
97 if (lowerToAlwaysFF) {
98 rewriter.create<sv::AlwaysFFOp>(
99 loc, sv::EventControl::AtPosEdge, adaptor.getClk(),
100 sv::ResetType::SyncReset, sv::EventControl::AtPosEdge,
101 adaptor.getReset(), assignValue, assignReset);
103 rewriter.create<sv::AlwaysOp>(
104 loc, sv::EventControl::AtPosEdge, adaptor.getClk(), [&] {
105 rewriter.create<sv::IfOp>(loc, adaptor.getReset(), assignReset,
110 if (lowerToAlwaysFF) {
111 rewriter.create<sv::AlwaysFFOp>(loc, sv::EventControl::AtPosEdge,
112 adaptor.getClk(), assignValue);
114 rewriter.create<sv::AlwaysOp>(loc, sv::EventControl::AtPosEdge,
115 adaptor.getClk(), assignValue);
119 rewriter.replaceOp(
reg, regVal);
124 void createAssign(ConversionPatternRewriter &rewriter, Location loc,
128 bool lowerToAlwaysFF;
133 void CompRegLower<CompRegOp>::createAssign(ConversionPatternRewriter &rewriter,
135 OpAdaptor
reg)
const {
136 rewriter.create<sv::PAssignOp>(loc, svReg,
reg.getInput());
140 void CompRegLower<CompRegClockEnabledOp>::createAssign(
141 ConversionPatternRewriter &rewriter, Location loc,
sv::RegOp svReg,
142 OpAdaptor
reg)
const {
143 rewriter.create<sv::IfOp>(loc,
reg.getClockEnable(), [&]() {
144 rewriter.create<sv::PAssignOp>(loc, svReg,
reg.getInput());
155 matchAndRewrite(ClockGateOp clockGate, OpAdaptor adaptor,
156 ConversionPatternRewriter &rewriter)
const final {
157 auto loc = clockGate.getLoc();
158 Value
clk = adaptor.getInput();
161 Value enable = adaptor.getEnable();
162 if (
auto te = adaptor.getTestEnable())
163 enable = rewriter.create<
comb::OrOp>(loc, enable, te);
166 Value enableLatch = rewriter.create<
sv::RegOp>(
167 loc, rewriter.getI1Type(), rewriter.getStringAttr(
"cg_en_latch"));
170 rewriter.create<sv::AlwaysOp>(
171 loc, llvm::SmallVector<sv::EventControl>{}, llvm::SmallVector<Value>{},
173 rewriter.create<sv::IfOp>(
175 rewriter.create<sv::PAssignOp>(loc, enableLatch, enable);
193 matchAndRewrite(ClockInverterOp op, OpAdaptor adaptor,
194 ConversionPatternRewriter &rewriter)
const final {
195 auto loc = op.getLoc();
196 Value
clk = adaptor.getInput();
198 StringAttr name = op->getAttrOfType<StringAttr>(
"sv.namehint");
200 auto newOp = rewriter.replaceOpWithNewOp<
comb::XorOp>(op,
clk, one);
202 rewriter.modifyOpInPlace(newOp,
203 [&] { newOp->setAttr(
"sv.namehint", name); });
216 matchAndRewrite(ClockMuxOp clockMux, OpAdaptor adaptor,
217 ConversionPatternRewriter &rewriter)
const final {
218 rewriter.replaceOpWithNewOp<
comb::MuxOp>(clockMux, adaptor.getCond(),
219 adaptor.getTrueClock(),
220 adaptor.getFalseClock(),
true);
226 struct SeqToSVTypeConverter :
public TypeConverter {
227 SeqToSVTypeConverter() {
228 addConversion([&](Type type) {
return type; });
229 addConversion([&](seq::ClockType type) {
232 addConversion([&](hw::StructType structTy) {
233 bool changed =
false;
235 SmallVector<hw::StructType::FieldInfo> newFields;
236 for (
auto field : structTy.getElements()) {
237 auto &newField = newFields.emplace_back();
238 newField.name = field.name;
239 newField.type = convertType(field.type);
240 if (field.type != newField.type)
249 addConversion([&](hw::ArrayType arrayTy) {
250 auto elementTy = arrayTy.getElementType();
252 if (elementTy != newElementTy)
257 addTargetMaterialization(
258 [&](mlir::OpBuilder &builder, mlir::Type resultType,
259 mlir::ValueRange inputs,
260 mlir::Location loc) -> std::optional<mlir::Value> {
261 if (inputs.size() != 1)
266 addSourceMaterialization(
267 [&](mlir::OpBuilder &builder, mlir::Type resultType,
268 mlir::ValueRange inputs,
269 mlir::Location loc) -> std::optional<mlir::Value> {
270 if (inputs.size() != 1)
278 template <
typename T>
284 matchAndRewrite(T op,
typename T::Adaptor adaptor,
285 ConversionPatternRewriter &rewriter)
const final {
287 if (Operation *inputOp = adaptor.getInput().getDefiningOp())
288 if (!isa<mlir::UnrealizedConversionCastOp>(inputOp))
290 rewriter.modifyOpInPlace(
291 inputOp, [&] { inputOp->setAttr(
"sv.namehint", name); });
293 rewriter.replaceOp(op, adaptor.getInput());
306 matchAndRewrite(ConstClockOp clockConst, OpAdaptor adaptor,
307 ConversionPatternRewriter &rewriter)
const final {
309 clockConst, APInt(1, clockConst.getValue() == ClockConst::High));
322 matchAndRewrite(ClockDividerOp clockDiv, OpAdaptor adaptor,
323 ConversionPatternRewriter &rewriter)
const final {
324 Location loc = clockDiv.getLoc();
327 if (clockDiv.getPow2()) {
331 Value output = clockDiv.getInput();
333 SmallVector<Value> regs;
334 for (
unsigned i = 0; i < clockDiv.getPow2(); ++i) {
336 loc, rewriter.getI1Type(),
337 rewriter.getStringAttr(
"clock_out_" + std::to_string(i)));
340 rewriter.create<sv::AlwaysOp>(
341 loc, sv::EventControl::AtPosEdge, output, [&] {
344 rewriter.create<sv::BPAssignOp>(loc,
reg, inverted);
352 rewriter.
create<sv::InitialOp>(loc, [&] {
353 for (Value
reg : regs) {
354 rewriter.
create<sv::BPAssignOp>(loc,
reg, zero);
359 rewriter.replaceOp(clockDiv, output);
368 if (hw::type_isa<ClockType>(ty))
371 if (
auto arrayTy = hw::type_dyn_cast<hw::ArrayType>(ty))
374 if (
auto structTy = hw::type_dyn_cast<hw::StructType>(ty)) {
375 for (
auto field : structTy.getElements())
386 if (
auto module = dyn_cast<hw::HWModuleLike>(op)) {
387 for (
auto port : module.getHWModuleType().getPorts())
392 bool allOperandsLowered = llvm::all_of(
393 op->getOperands(), [](
auto op) { return isLegalType(op.getType()); });
394 bool allResultsLowered = llvm::all_of(op->getResults(), [](
auto result) {
395 return isLegalType(result.getType());
397 return allOperandsLowered && allResultsLowered;
401 auto circuit = getOperation();
402 MLIRContext *context = &getContext();
404 auto modules = llvm::to_vector(circuit.getOps<
HWModuleOp>());
410 MapVector<HWModuleOp, SmallVector<FirMemLowering::MemoryConfig>> memsByModule;
411 for (
auto &[config, memOps] : uniqueMems) {
416 for (
auto memOp : memOps) {
417 auto parent = memOp->getParentOfType<
HWModuleOp>();
418 memsByModule[parent].emplace_back(&config, genOp, memOp);
423 bool needsRegRandomization =
false;
424 bool needsMemRandomization =
false;
426 struct FragmentInfo {
427 bool needsRegFragment;
428 bool needsMemFragment;
430 DenseMap<HWModuleOp, FragmentInfo> moduleFragmentInfo;
431 llvm::sys::SmartMutex<true> fragmentsMutex;
433 mlir::parallelForEach(&getContext(), modules, [&](
HWModuleOp module) {
434 SeqToSVTypeConverter typeConverter;
435 FirRegLowering regLowering(typeConverter, module, disableRegRandomization,
436 emitSeparateAlwaysBlocks);
439 if (!disableRegRandomization) {
440 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
441 moduleFragmentInfo[module].needsRegFragment =
true;
443 needsRegRandomization =
true;
447 if (
auto *it = memsByModule.find(module); it != memsByModule.end()) {
449 if (!disableMemRandomization) {
450 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
451 moduleFragmentInfo[module].needsMemFragment =
true;
453 needsMemRandomization =
true;
457 auto randomInitFragmentName =
459 auto randomInitRegFragmentName =
461 auto randomInitMemFragmentName =
464 for (
auto &[module, info] : moduleFragmentInfo) {
465 assert((info.needsRegFragment || info.needsMemFragment) &&
466 "module should use memories or registers");
468 SmallVector<Attribute> fragmentAttrs;
471 fragmentAttrs = llvm::to_vector(others);
473 if (info.needsRegFragment)
474 fragmentAttrs.push_back(randomInitRegFragmentName);
475 if (info.needsMemFragment)
476 fragmentAttrs.push_back(randomInitMemFragmentName);
477 fragmentAttrs.push_back(randomInitFragmentName);
484 SeqToSVTypeConverter typeConverter;
485 ConversionTarget target(*context);
486 target.addIllegalDialect<SeqDialect>();
487 target.markUnknownOpDynamicallyLegal(
isLegalOp);
489 RewritePatternSet
patterns(context);
490 patterns.add<CompRegLower<CompRegOp>>(typeConverter, context,
492 patterns.add<CompRegLower<CompRegClockEnabledOp>>(typeConverter, context,
494 patterns.add<ClockCastLowering<seq::FromClockOp>>(typeConverter, context);
495 patterns.add<ClockCastLowering<seq::ToClockOp>>(typeConverter, context);
496 patterns.add<ClockGateLowering>(typeConverter, context);
497 patterns.add<ClockInverterLowering>(typeConverter, context);
498 patterns.add<ClockMuxLowering>(typeConverter, context);
499 patterns.add<ClockDividerLowering>(typeConverter, context);
500 patterns.add<ClockConstLowering>(typeConverter, context);
503 if (failed(applyPartialConversion(circuit, target, std::move(
patterns))))
507 auto b = ImplicitLocOpBuilder::atBlockBegin(loc, circuit.getBody());
508 if (needsRegRandomization || needsMemRandomization) {
509 b.create<sv::MacroDeclOp>(
"ENABLE_INITIAL_REG_");
510 b.create<sv::MacroDeclOp>(
"ENABLE_INITIAL_MEM_");
511 if (needsRegRandomization) {
512 b.create<sv::MacroDeclOp>(
"FIRRTL_BEFORE_INITIAL");
513 b.create<sv::MacroDeclOp>(
"FIRRTL_AFTER_INITIAL");
515 if (needsMemRandomization)
516 b.create<sv::MacroDeclOp>(
"RANDOMIZE_MEM_INIT");
517 b.create<sv::MacroDeclOp>(
"RANDOMIZE_REG_INIT");
518 b.create<sv::MacroDeclOp>(
"RANDOMIZE");
519 b.create<sv::MacroDeclOp>(
"RANDOMIZE_DELAY");
520 b.create<sv::MacroDeclOp>(
"RANDOM");
521 b.create<sv::MacroDeclOp>(
"INIT_RANDOM");
522 b.create<sv::MacroDeclOp>(
"INIT_RANDOM_PROLOG_");
525 bool hasRegRandomization = needsRegRandomization && !disableRegRandomization;
526 bool hasMemRandomization = needsMemRandomization && !disableMemRandomization;
527 if (!hasRegRandomization && !hasMemRandomization)
532 for (Operation &op : *circuit.getBody()) {
533 if (!isa<sv::VerbatimOp, sv::IfDefOp>(&op)) {
534 b.setInsertionPoint(&op);
542 for (
auto sym : circuit.getOps<sv::MacroDeclOp>())
543 symbols.insert(sym.getName());
544 if (!symbols.count(
"SYNTHESIS"))
545 b.create<sv::MacroDeclOp>(
"SYNTHESIS");
546 if (!symbols.count(
"VERILATOR"))
547 b.create<sv::MacroDeclOp>(
"VERILATOR");
552 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
553 StringRef defineTrue =
"",
554 StringRef defineFalse = StringRef()) {
555 if (!defineFalse.data()) {
556 assert(defineTrue.data() &&
"didn't define anything");
558 guard, [&]() { b.create<sv::MacroDefOp>(defName, defineTrue); });
563 if (defineTrue.data())
564 b.create<sv::MacroDefOp>(defName, defineTrue);
566 [&]() { b.create<sv::MacroDefOp>(defName, defineFalse); });
571 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
573 guard, []() {}, body);
576 b.create<emit::FragmentOp>(randomInitFragmentName.getAttr(), [&] {
577 b.create<sv::VerbatimOp>(
578 "// Standard header to adapt well known macros for "
579 "register randomization.");
581 b.create<sv::VerbatimOp>(
582 "\n// RANDOM may be set to an expression that produces a 32-bit "
583 "random unsigned value.");
584 emitGuardedDefine(
"RANDOM",
"RANDOM", StringRef(),
"$random");
586 b.create<sv::VerbatimOp>(
587 "\n// Users can define INIT_RANDOM as general code that gets "
589 "into the\n// initializer block for modules with registers.");
590 emitGuardedDefine(
"INIT_RANDOM",
"INIT_RANDOM", StringRef(),
"");
592 b.create<sv::VerbatimOp>(
593 "\n// If using random initialization, you can also define "
594 "RANDOMIZE_DELAY to\n// customize the delay used, otherwise 0.002 "
596 emitGuardedDefine(
"RANDOMIZE_DELAY",
"RANDOMIZE_DELAY", StringRef(),
599 b.create<sv::VerbatimOp>(
600 "\n// Define INIT_RANDOM_PROLOG_ for use in our modules below.");
601 emitGuard(
"INIT_RANDOM_PROLOG_", [&]() {
605 emitGuardedDefine(
"VERILATOR",
"INIT_RANDOM_PROLOG_",
607 "`INIT_RANDOM #`RANDOMIZE_DELAY begin end");
609 [&]() { b.create<sv::MacroDefOp>(
"INIT_RANDOM_PROLOG_",
""); });
613 if (hasMemRandomization) {
614 b.create<emit::FragmentOp>(randomInitMemFragmentName.getAttr(), [&] {
615 b.create<sv::VerbatimOp>(
"\n// Include rmemory initializers in init "
616 "blocks unless synthesis is set");
617 emitGuard(
"RANDOMIZE", [&]() {
618 emitGuardedDefine(
"RANDOMIZE_MEM_INIT",
"RANDOMIZE");
620 emitGuard(
"SYNTHESIS", [&] {
621 emitGuardedDefine(
"ENABLE_INITIAL_MEM_",
"ENABLE_INITIAL_MEM_",
624 b.create<sv::VerbatimOp>(
"");
628 if (hasRegRandomization) {
629 b.create<emit::FragmentOp>(randomInitRegFragmentName.getAttr(), [&] {
630 b.create<sv::VerbatimOp>(
"\n// Include register initializers in init "
631 "blocks unless synthesis is set");
632 emitGuard(
"RANDOMIZE", [&]() {
633 emitGuardedDefine(
"RANDOMIZE_REG_INIT",
"RANDOMIZE");
635 emitGuard(
"SYNTHESIS", [&] {
636 emitGuardedDefine(
"ENABLE_INITIAL_REG_",
"ENABLE_INITIAL_REG_",
639 b.create<sv::VerbatimOp>(
"");
644 std::unique_ptr<Pass>
646 return std::make_unique<SeqToSVPass>(options);
assert(baseType &&"element must be base type")
static FIRRTLBaseType convertType(FIRRTLBaseType type)
Returns null type if no conversion is needed.
static bool isLegalType(Type ty)
static bool isLegalOp(Operation *op)
FIR memory lowering helper.
UniqueConfigs collectMemories(ArrayRef< hw::HWModuleOp > modules)
Groups memories by their kind from the whole design.
void lowerMemoriesInModule(hw::HWModuleOp module, ArrayRef< MemoryConfig > mems)
Lowers a group of memories from the same module.
hw::HWModuleGeneratedOp createMemoryModule(FirMemConfig &mem, ArrayRef< seq::FirMemOp > memOps)
Creates the generated module for a given configuration.
Lower FirRegOp to sv.reg and sv.always.
bool needsRegRandomization() const
unsigned numSubaccessRestored
def create(data_type, value)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
void setSVAttributes(mlir::Operation *op, mlir::ArrayAttr attrs)
Set the SV attributes of an operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createLowerSeqToSVPass(const LowerSeqToSVOptions &options={})
StringRef chooseName(StringRef a, StringRef b)
Choose a good name for an item from two options.
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
void runOnOperation() override
Generic pattern which replaces an operation by one of the same operation name, but with converted att...