CIRCT 20.0.0git
Loading...
Searching...
No Matches
LowerToHW.cpp
Go to the documentation of this file.
1//===- LowerToHW.cpp - FIRRTL to HW/SV Lowering Pass ----------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This is the main FIRRTL to HW/SV Lowering Pass Implementation.
10//
11//===----------------------------------------------------------------------===//
12
35#include "mlir/IR/BuiltinOps.h"
36#include "mlir/IR/BuiltinTypes.h"
37#include "mlir/IR/ImplicitLocOpBuilder.h"
38#include "mlir/IR/Threading.h"
39#include "mlir/Pass/Pass.h"
40#include "llvm/Support/Debug.h"
41#include "llvm/Support/Mutex.h"
42
43#define DEBUG_TYPE "lower-to-hw"
44
45namespace circt {
46#define GEN_PASS_DEF_LOWERFIRRTLTOHW
47#include "circt/Conversion/Passes.h.inc"
48} // namespace circt
49
50using namespace circt;
51using namespace firrtl;
52using circt::comb::ICmpPredicate;
53
54/// Attribute that indicates that the module hierarchy starting at the
55/// annotated module should be dumped to a file.
56static const char moduleHierarchyFileAttrName[] = "firrtl.moduleHierarchyFile";
57
58/// Return true if the specified type is a sized FIRRTL type (Int or Analog)
59/// with zero bits.
60static bool isZeroBitFIRRTLType(Type type) {
61 auto ftype = dyn_cast<FIRRTLBaseType>(type);
62 return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
63}
64
65// Return a single source value in the operands of the given attach op if
66// exists.
67static Value getSingleNonInstanceOperand(AttachOp op) {
68 Value singleSource;
69 for (auto operand : op.getAttached()) {
70 if (isZeroBitFIRRTLType(operand.getType()) ||
71 operand.getDefiningOp<InstanceOp>())
72 continue;
73 // If it is used by other than attach op or there is already a source
74 // value, bail out.
75 if (!operand.hasOneUse() || singleSource)
76 return {};
77 singleSource = operand;
78 }
79 return singleSource;
80}
81
82/// This verifies that the target operation has been lowered to a legal
83/// operation. This checks that the operation recursively has no FIRRTL
84/// operations or types.
85static LogicalResult verifyOpLegality(Operation *op) {
86 auto checkTypes = [](Operation *op) -> WalkResult {
87 // Check that this operation is not a FIRRTL op.
88 if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
89 return op->emitError("Found unhandled FIRRTL operation '")
90 << op->getName() << "'";
91
92 // Helper to check a TypeRange for any FIRRTL types.
93 auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
94 if (llvm::any_of(types, [](Type type) {
95 return isa<FIRRTLDialect>(type.getDialect());
96 }))
97 return op->emitOpError("found unhandled FIRRTL type");
98 return success();
99 };
100
101 // Check operand and result types.
102 if (failed(checkTypeRange(op->getOperandTypes())) ||
103 failed(checkTypeRange(op->getResultTypes())))
104 return WalkResult::interrupt();
105
106 // Check the block argument types.
107 for (auto &region : op->getRegions())
108 for (auto &block : region)
109 if (failed(checkTypeRange(block.getArgumentTypes())))
110 return WalkResult::interrupt();
111
112 // Continue to the next operation.
113 return WalkResult::advance();
114 };
115
116 if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
117 return failure();
118 return success();
119}
120
121/// Given two FIRRTL integer types, return the widest one.
122static IntType getWidestIntType(Type t1, Type t2) {
123 auto t1c = type_cast<IntType>(t1), t2c = type_cast<IntType>(t2);
124 return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
125}
126
127/// Cast a value to a desired target type. This will insert struct casts and
128/// unrealized conversion casts as necessary.
129static Value castToFIRRTLType(Value val, Type type,
130 ImplicitLocOpBuilder &builder) {
131 // Use HWStructCastOp for a bundle type.
132 if (BundleType bundle = dyn_cast<BundleType>(type))
133 val = builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
134
135 if (type != val.getType())
136 val = builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(
137 0);
138
139 return val;
140}
141
142/// Cast from a FIRRTL type (potentially with a flip) to a standard type.
143static Value castFromFIRRTLType(Value val, Type type,
144 ImplicitLocOpBuilder &builder) {
145
146 if (hw::StructType structTy = dyn_cast<hw::StructType>(type)) {
147 // Strip off Flip type if needed.
148 val =
149 builder
150 .create<mlir::UnrealizedConversionCastOp>(
151 type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
152 .getResult(0);
153 val = builder.createOrFold<HWStructCastOp>(type, val);
154 return val;
155 }
156
157 val =
158 builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(0);
159
160 return val;
161}
162
163/// Move a ExtractTestCode related annotation from annotations to an attribute.
164static void moveVerifAnno(ModuleOp top, AnnotationSet &annos,
165 StringRef annoClass, StringRef attrBase) {
166 auto anno = annos.getAnnotation(annoClass);
167 auto *ctx = top.getContext();
168 if (!anno)
169 return;
170 if (auto dir = anno.getMember<StringAttr>("directory")) {
171 SmallVector<NamedAttribute> old;
172 for (auto i : top->getAttrs())
173 old.push_back(i);
174 old.emplace_back(
175 StringAttr::get(ctx, attrBase),
176 hw::OutputFileAttr::getAsDirectory(ctx, dir.getValue(), true, true));
177 top->setAttrs(old);
178 }
179 if (auto file = anno.getMember<StringAttr>("filename")) {
180 SmallVector<NamedAttribute> old;
181 for (auto i : top->getAttrs())
182 old.push_back(i);
183 old.emplace_back(StringAttr::get(ctx, attrBase + ".bindfile"),
184 hw::OutputFileAttr::getFromFilename(
185 ctx, file.getValue(), /*excludeFromFileList=*/true));
186 top->setAttrs(old);
187 }
188}
189
190static unsigned getBitWidthFromVectorSize(unsigned size) {
191 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
192}
193
194// Try moving a name from an firrtl expression to a hw expression as a name
195// hint. Dont' overwrite an existing name.
196static void tryCopyName(Operation *dst, Operation *src) {
197 if (auto attr = src->getAttrOfType<StringAttr>("name"))
198 if (!dst->hasAttr("sv.namehint") && !dst->hasAttr("name"))
199 dst->setAttr("sv.namehint", attr);
200}
201
202//===----------------------------------------------------------------------===//
203// firrtl.module Lowering Pass
204//===----------------------------------------------------------------------===//
205namespace {
206
207struct FIRRTLModuleLowering;
208
209/// This is state shared across the parallel module lowering logic.
210struct CircuitLoweringState {
211 // Flags indicating whether the circuit uses certain header fragments.
212 std::atomic<bool> usedPrintf{false};
213 std::atomic<bool> usedAssertVerboseCond{false};
214 std::atomic<bool> usedStopCond{false};
215
216 CircuitLoweringState(CircuitOp circuitOp, bool enableAnnotationWarning,
217 firrtl::VerificationFlavor verificationFlavor,
218 InstanceGraph &instanceGraph, NLATable *nlaTable)
219 : circuitOp(circuitOp), instanceGraph(instanceGraph),
220 enableAnnotationWarning(enableAnnotationWarning),
221 verificationFlavor(verificationFlavor), nlaTable(nlaTable) {
222 auto *context = circuitOp.getContext();
223
224 // Get the testbench output directory.
225 if (auto tbAnno =
226 AnnotationSet(circuitOp).getAnnotation(testBenchDirAnnoClass)) {
227 auto dirName = tbAnno.getMember<StringAttr>("dirname");
228 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
229 context, dirName.getValue(), false, true);
230 }
231
232 for (auto &op : *circuitOp.getBodyBlock()) {
233 if (auto module = dyn_cast<FModuleLike>(op))
235 dut = module;
236 }
237
238 // Figure out which module is the DUT and TestHarness. If there is no
239 // module marked as the DUT, the top module is the DUT. If the DUT and the
240 // test harness are the same, then there is no test harness.
241 testHarness = instanceGraph.getTopLevelModule();
242 if (!dut) {
243 dut = testHarness;
244 testHarness = nullptr;
245 } else if (dut == testHarness) {
246 testHarness = nullptr;
247 }
248
249 // Pre-populate the dutModules member with a list of all modules that are
250 // determined to be under the DUT.
251 auto inDUT = [&](igraph::ModuleOpInterface child) {
252 auto isBind = [](igraph::InstanceRecord *instRec) {
253 auto inst = instRec->getInstance();
254 if (auto *finst = dyn_cast<InstanceOp>(&inst))
255 return finst->getLowerToBind();
256 return false;
257 };
258 if (auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
259 return getInstanceGraph().isAncestor(child, parent, isBind);
260 return dut == child;
261 };
262 circuitOp->walk([&](FModuleLike moduleOp) {
263 if (inDUT(moduleOp))
264 dutModules.insert(moduleOp);
265 });
266 }
267
268 Operation *getNewModule(Operation *oldModule) {
269 auto it = oldToNewModuleMap.find(oldModule);
270 return it != oldToNewModuleMap.end() ? it->second : nullptr;
271 }
272
273 Operation *getOldModule(Operation *newModule) {
274 auto it = newToOldModuleMap.find(newModule);
275 return it != newToOldModuleMap.end() ? it->second : nullptr;
276 }
277
278 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
279 oldToNewModuleMap[oldFMod] = newHWMod;
280 newToOldModuleMap[newHWMod] = oldFMod;
281 }
282
283 // Process remaining annotations and emit warnings on unprocessed annotations
284 // still remaining in the annoSet.
285 void processRemainingAnnotations(Operation *op, const AnnotationSet &annoSet);
286
287 CircuitOp circuitOp;
288
289 // Safely add a BindOp to global mutable state. This will acquire a lock to
290 // do this safely.
291 void addBind(sv::BindOp op) {
292 std::lock_guard<std::mutex> lock(bindsMutex);
293 binds.push_back(op);
294 }
295
296 /// For a given Type Alias, return the corresponding AliasType. Create and
297 /// record the AliasType, if it doesn't exist.
298 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
299 Location typeLoc) {
300
301 auto hwAlias = typeAliases.getTypedecl(firAliasType);
302 if (hwAlias)
303 return hwAlias;
304 assert(!typeAliases.isFrozen() &&
305 "type aliases cannot be generated after its frozen");
306 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
307 }
308
309 FModuleLike getDut() { return dut; }
310 FModuleLike getTestHarness() { return testHarness; }
311
312 // Return true if this module is the DUT or is instantiated by the DUT.
313 // Returns false if the module is not instantiated by the DUT or is
314 // instantiated under a bind. This will accept either an old FIRRTL module or
315 // a new HW module.
316 bool isInDUT(igraph::ModuleOpInterface child) {
317 if (auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
318 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
319 return dutModules.contains(child);
320 }
321
322 hw::OutputFileAttr getTestBenchDirectory() { return testBenchDirectory; }
323
324 // Return true if this module is instantiated by the Test Harness. Returns
325 // false if the module is not instantiated by the Test Harness or if the Test
326 // Harness is not known.
327 bool isInTestHarness(igraph::ModuleOpInterface mod) { return !isInDUT(mod); }
328
329 InstanceGraph &getInstanceGraph() { return instanceGraph; }
330
331 /// Given a type, return the corresponding lowered type for the HW dialect.
332 /// A wrapper to the FIRRTLUtils::lowerType, required to ensure safe addition
333 /// of TypeScopeOp for all the TypeDecls.
334 Type lowerType(Type type, Location loc) {
335 return ::lowerType(type, loc,
336 [&](Type rawType, BaseTypeAliasType firrtlType,
337 Location typeLoc) -> hw::TypeAliasType {
338 return getTypeAlias(rawType, firrtlType, typeLoc);
339 });
340 }
341
342private:
343 friend struct FIRRTLModuleLowering;
344 friend struct FIRRTLLowering;
345 CircuitLoweringState(const CircuitLoweringState &) = delete;
346 void operator=(const CircuitLoweringState &) = delete;
347
348 /// Mapping of FModuleOp to HWModuleOp
349 DenseMap<Operation *, Operation *> oldToNewModuleMap;
350
351 /// Mapping of HWModuleOp to FModuleOp
352 DenseMap<Operation *, Operation *> newToOldModuleMap;
353
354 /// Cache of module symbols. We need to test hirarchy-based properties to
355 /// lower annotaitons.
356 InstanceGraph &instanceGraph;
357
358 /// The set of old FIRRTL modules that are instantiated under the DUT. This
359 /// is precomputed as a module being under the DUT may rely on knowledge of
360 /// properties of the instance and is not suitable for querying in the
361 /// parallel execution region of this pass when the backing instances may
362 /// already be erased.
363 DenseSet<igraph::ModuleOpInterface> dutModules;
364
365 // Record the set of remaining annotation classes. This is used to warn only
366 // once about any annotation class.
367 StringSet<> pendingAnnotations;
368 const bool enableAnnotationWarning;
369 std::mutex annotationPrintingMtx;
370
371 const firrtl::VerificationFlavor verificationFlavor;
372
373 // Records any sv::BindOps that are found during the course of execution.
374 // This is unsafe to access directly and should only be used through addBind.
375 SmallVector<sv::BindOp> binds;
376
377 // Control access to binds.
378 std::mutex bindsMutex;
379
380 // The design-under-test (DUT), if it is found. This will be set if a
381 // "sifive.enterprise.firrtl.MarkDUTAnnotation" exists.
382 FModuleLike dut;
383
384 // If there is a module marked as the DUT and it is not the top level module,
385 // this will be set.
386 FModuleLike testHarness;
387
388 // If there is a testbench output directory, this will be set.
389 hw::OutputFileAttr testBenchDirectory;
390
391 /// A mapping of instances to their forced instantiation names (if
392 /// applicable).
393 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
394
395 /// The set of guard macros to emit declarations for.
396 SetVector<StringAttr> macroDeclNames;
397 std::mutex macroDeclMutex;
398
399 void addMacroDecl(StringAttr name) {
400 std::unique_lock<std::mutex> lock(macroDeclMutex);
401 macroDeclNames.insert(name);
402 }
403
404 /// The list of fragments on which the modules rely. Must be set outside the
405 /// parallelized module lowering since module type reads access it.
406 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
407 llvm::sys::SmartMutex<true> fragmentsMutex;
408
409 void addFragment(hw::HWModuleOp module, StringRef fragment) {
410 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
411 fragments[module].insert(
412 FlatSymbolRefAttr::get(circuitOp.getContext(), fragment));
413 }
414
415 /// Cached nla table analysis.
416 NLATable *nlaTable = nullptr;
417
418 /// FIRRTL::BaseTypeAliasType is lowered to hw::TypeAliasType, which requires
419 /// TypedeclOp inside a single global TypeScopeOp. This structure
420 /// maintains a map of FIRRTL alias types to HW alias type, which is populated
421 /// in the sequential phase and accessed during the read-only phase when its
422 /// frozen.
423 /// This structure ensures that
424 /// all TypeAliases are lowered as a prepass, before lowering all the modules
425 /// in parallel. Lowering of TypeAliases must be done sequentially to ensure
426 /// deteministic TypeDecls inside the global TypeScopeOp.
427 struct RecordTypeAlias {
428
429 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
430
431 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias) const {
432 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
433 if (iter != firrtlTypeToAliasTypeMap.end())
434 return iter->second;
435 return {};
436 }
437
438 bool isFrozen() { return frozen; }
439
440 void freeze() { frozen = true; }
441
442 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
443 Location typeLoc) {
444 assert(!frozen && "Record already frozen, cannot be updated");
445
446 if (!typeScope) {
447 auto b = ImplicitLocOpBuilder::atBlockBegin(
448 circuitOp.getLoc(),
449 &circuitOp->getParentRegion()->getBlocks().back());
450 typeScope = b.create<hw::TypeScopeOp>(
451 b.getStringAttr(circuitOp.getName() + "__TYPESCOPE_"));
452 typeScope.getBodyRegion().push_back(new Block());
453 }
454 auto typeName = firAlias.getName();
455 // Get a unique typedecl name.
456 // The bundleName can conflict with other symbols, but must be unique
457 // within the TypeScopeOp.
458 typeName =
459 StringAttr::get(typeName.getContext(),
460 typeDeclNamespace.newName(typeName.getValue()));
461
462 auto typeScopeBuilder =
463 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
464 auto typeDecl = typeScopeBuilder.create<hw::TypedeclOp>(typeLoc, typeName,
465 rawType, nullptr);
466 auto hwAlias = hw::TypeAliasType::get(
467 SymbolRefAttr::get(typeScope.getSymNameAttr(),
468 {FlatSymbolRefAttr::get(typeDecl)}),
469 rawType);
470 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
471 assert(insert.second && "Entry already exists, insert failed");
472 return insert.first->second;
473 }
474
475 private:
476 bool frozen = false;
477 /// Global typescope for all the typedecls in this module.
478 hw::TypeScopeOp typeScope;
479
480 /// Map of FIRRTL type to the lowered AliasType.
481 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
482
483 /// Set to keep track of unique typedecl names.
484 Namespace typeDeclNamespace;
485
486 CircuitOp circuitOp;
487 };
488
489 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
490};
491
492void CircuitLoweringState::processRemainingAnnotations(
493 Operation *op, const AnnotationSet &annoSet) {
494 if (!enableAnnotationWarning || annoSet.empty())
495 return;
496 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
497
498 for (auto a : annoSet) {
499 auto inserted = pendingAnnotations.insert(a.getClass());
500 if (!inserted.second)
501 continue;
502
503 // The following annotations are okay to be silently dropped at this point.
504 // This can occur for example if an annotation marks something in the IR as
505 // not to be processed by a pass, but that pass hasn't run anyway.
506 if (a.isClass(
507 // If the class is `circt.nonlocal`, it's not really an annotation,
508 // but part of a path specifier for another annotation which is
509 // non-local. We can ignore these path specifiers since there will
510 // be a warning produced for the real annotation.
511 "circt.nonlocal",
512 // The following are either consumed by a pass running before
513 // LowerToHW, or they have no effect if the pass doesn't run at all.
514 // If the accompanying pass runs on the HW dialect, then LowerToHW
515 // should have consumed and processed these into an attribute on the
516 // output.
518 // The following are inspected (but not consumed) by FIRRTL/GCT
519 // passes that have all run by now. Since no one is responsible for
520 // consuming these, they will linger around and can be ignored.
523 // This annotation is used to mark which external modules are
524 // imported blackboxes from the BlackBoxReader pass.
526 // This annotation is used by several GrandCentral passes.
528 // The following will be handled while lowering the verification
529 // ops.
532 // The following will be handled after lowering FModule ops, since
533 // they are still needed on the circuit until after lowering
534 // FModules.
537 continue;
538
539 mlir::emitWarning(op->getLoc(), "unprocessed annotation:'" + a.getClass() +
540 "' still remaining after LowerToHW");
541 }
542}
543} // end anonymous namespace
544
545namespace {
546struct FIRRTLModuleLowering
547 : public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
548
549 void runOnOperation() override;
550 void setEnableAnnotationWarning() { enableAnnotationWarning = true; }
551
552 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
553
554private:
555 void lowerFileHeader(CircuitOp op, CircuitLoweringState &loweringState);
556 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
557 SmallVectorImpl<hw::PortInfo> &ports,
558 Operation *moduleOp, StringRef moduleName,
559 CircuitLoweringState &loweringState);
560 bool handleForceNameAnnos(FModuleLike oldModule, AnnotationSet &annos,
561 CircuitLoweringState &loweringState);
562 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
563 CircuitLoweringState &loweringState);
564 hw::HWModuleExternOp lowerExtModule(FExtModuleOp oldModule,
565 Block *topLevelModule,
566 CircuitLoweringState &loweringState);
567 hw::HWModuleExternOp lowerMemModule(FMemModuleOp oldModule,
568 Block *topLevelModule,
569 CircuitLoweringState &loweringState);
570
571 LogicalResult
572 lowerModulePortsAndMoveBody(FModuleOp oldModule, hw::HWModuleOp newModule,
573 CircuitLoweringState &loweringState);
574 LogicalResult lowerModuleOperations(hw::HWModuleOp module,
575 CircuitLoweringState &loweringState);
576 LogicalResult lowerFormalBody(verif::FormalOp formalOp,
577 CircuitLoweringState &loweringState);
578};
579
580} // end anonymous namespace
581
582/// This is the pass constructor.
583std::unique_ptr<mlir::Pass> circt::createLowerFIRRTLToHWPass(
584 bool enableAnnotationWarning,
585 firrtl::VerificationFlavor verificationFlavor) {
586 auto pass = std::make_unique<FIRRTLModuleLowering>();
587 if (enableAnnotationWarning)
588 pass->setEnableAnnotationWarning();
589 pass->verificationFlavor = verificationFlavor;
590 return pass;
591}
592
593/// Run on the firrtl.circuit operation, lowering any firrtl.module operations
594/// it contains.
595void FIRRTLModuleLowering::runOnOperation() {
596
597 // We run on the top level modules in the IR blob. Start by finding the
598 // firrtl.circuit within it. If there is none, then there is nothing to do.
599 auto *topLevelModule = getOperation().getBody();
600
601 // Find the single firrtl.circuit in the module.
602 CircuitOp circuit;
603 for (auto &op : *topLevelModule) {
604 if ((circuit = dyn_cast<CircuitOp>(&op)))
605 break;
606 }
607
608 if (!circuit)
609 return;
610
611 auto *circuitBody = circuit.getBodyBlock();
612
613 // Keep track of the mapping from old to new modules. The result may be null
614 // if lowering failed.
615 CircuitLoweringState state(circuit, enableAnnotationWarning,
616 verificationFlavor, getAnalysis<InstanceGraph>(),
617 &getAnalysis<NLATable>());
618
619 SmallVector<hw::HWModuleOp, 32> modulesToProcess;
620 SmallVector<verif::FormalOp> formalOpsToProcess;
621
622 AnnotationSet circuitAnno(circuit);
623 moveVerifAnno(getOperation(), circuitAnno, extractAssertAnnoClass,
624 "firrtl.extract.assert");
625 moveVerifAnno(getOperation(), circuitAnno, extractAssumeAnnoClass,
626 "firrtl.extract.assume");
627 moveVerifAnno(getOperation(), circuitAnno, extractCoverageAnnoClass,
628 "firrtl.extract.cover");
629 circuitAnno.removeAnnotationsWithClass(
631
632 state.processRemainingAnnotations(circuit, circuitAnno);
633 // Iterate through each operation in the circuit body, transforming any
634 // FModule's we come across. If any module fails to lower, return early.
635 for (auto &op : make_early_inc_range(circuitBody->getOperations())) {
636 auto result =
637 TypeSwitch<Operation *, LogicalResult>(&op)
638 .Case<FModuleOp>([&](auto module) {
639 auto loweredMod = lowerModule(module, topLevelModule, state);
640 if (!loweredMod)
641 return failure();
642
643 state.recordModuleMapping(&op, loweredMod);
644 modulesToProcess.push_back(loweredMod);
645 // Lower all the alias types.
646 module.walk([&](Operation *op) {
647 for (auto res : op->getResults()) {
648 if (auto aliasType =
649 type_dyn_cast<BaseTypeAliasType>(res.getType()))
650 state.lowerType(aliasType, op->getLoc());
651 }
652 });
653 return lowerModulePortsAndMoveBody(module, loweredMod, state);
654 })
655 .Case<FExtModuleOp>([&](auto extModule) {
656 auto loweredMod =
657 lowerExtModule(extModule, topLevelModule, state);
658 if (!loweredMod)
659 return failure();
660 state.recordModuleMapping(&op, loweredMod);
661 return success();
662 })
663 .Case<FMemModuleOp>([&](auto memModule) {
664 auto loweredMod =
665 lowerMemModule(memModule, topLevelModule, state);
666 if (!loweredMod)
667 return failure();
668 state.recordModuleMapping(&op, loweredMod);
669 return success();
670 })
671 .Case<FormalOp>([&](auto oldFormalOp) {
672 auto builder = OpBuilder::atBlockEnd(topLevelModule);
673 auto newFormalOp = builder.create<verif::FormalOp>(
674 oldFormalOp.getLoc(), oldFormalOp.getNameAttr(),
675 oldFormalOp.getParametersAttr());
676 newFormalOp.getBody().emplaceBlock();
677 state.recordModuleMapping(oldFormalOp, newFormalOp);
678 formalOpsToProcess.push_back(newFormalOp);
679 return success();
680 })
681 .Default([&](Operation *op) {
682 // We don't know what this op is. If it has no illegal FIRRTL
683 // types, we can forward the operation. Otherwise, we emit an
684 // error and drop the operation from the circuit.
685 if (succeeded(verifyOpLegality(op)))
686 op->moveBefore(topLevelModule, topLevelModule->end());
687 else
688 return failure();
689 return success();
690 });
691 if (failed(result))
692 return signalPassFailure();
693 }
694 // Ensure no more TypeDecl can be added to the global TypeScope.
695 state.typeAliases.freeze();
696 // Handle the creation of the module hierarchy metadata.
697
698 // Collect the two sets of hierarchy files from the circuit. Some of them will
699 // be rooted at the test harness, the others will be rooted at the DUT.
700 SmallVector<Attribute> dutHierarchyFiles;
701 SmallVector<Attribute> testHarnessHierarchyFiles;
702 circuitAnno.removeAnnotations([&](Annotation annotation) {
703 if (annotation.isClass(moduleHierAnnoClass)) {
704 auto file = hw::OutputFileAttr::getFromFilename(
705 &getContext(),
706 annotation.getMember<StringAttr>("filename").getValue(),
707 /*excludeFromFileList=*/true);
708 dutHierarchyFiles.push_back(file);
709 return true;
710 }
711 if (annotation.isClass(testHarnessHierAnnoClass)) {
712 auto file = hw::OutputFileAttr::getFromFilename(
713 &getContext(),
714 annotation.getMember<StringAttr>("filename").getValue(),
715 /*excludeFromFileList=*/true);
716 // If there is no testHarness, we print the hiearchy for this file
717 // starting at the DUT.
718 if (state.getTestHarness())
719 testHarnessHierarchyFiles.push_back(file);
720 else
721 dutHierarchyFiles.push_back(file);
722 return true;
723 }
724 return false;
725 });
726 // Attach the lowered form of these annotations.
727 if (!dutHierarchyFiles.empty())
728 state.getNewModule(state.getDut())
730 ArrayAttr::get(&getContext(), dutHierarchyFiles));
731 if (!testHarnessHierarchyFiles.empty())
732 state.getNewModule(state.getTestHarness())
734 ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
735
736 // Lower all module bodies.
737 auto result = mlir::failableParallelForEachN(
738 &getContext(), 0, modulesToProcess.size(), [&](auto index) {
739 return lowerModuleOperations(modulesToProcess[index], state);
740 });
741 if (failed(result))
742 return signalPassFailure();
743
744 // Lower all formal op bodies.
745 result = mlir::failableParallelForEach(
746 &getContext(), formalOpsToProcess,
747 [&](auto op) { return lowerFormalBody(op, state); });
748 if (failed(result))
749 return signalPassFailure();
750
751 // Move binds from inside modules to outside modules.
752 for (auto bind : state.binds) {
753 bind->moveBefore(bind->getParentOfType<hw::HWModuleOp>());
754 }
755
756 // Fix up fragment attributes.
757 for (auto &[module, fragments] : state.fragments)
758 module->setAttr(emit::getFragmentsAttrName(),
759 ArrayAttr::get(&getContext(), fragments.getArrayRef()));
760
761 // Finally delete all the old modules.
762 for (auto oldNew : state.oldToNewModuleMap)
763 oldNew.first->erase();
764
765 if (!state.macroDeclNames.empty()) {
766 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), circuit);
767 for (auto name : state.macroDeclNames) {
768 b.create<sv::MacroDeclOp>(name);
769 }
770 }
771
772 // Emit all the macros and preprocessor gunk at the start of the file.
773 lowerFileHeader(circuit, state);
774
775 // Now that the modules are moved over, remove the Circuit.
776 circuit.erase();
777}
778
779/// Emit the file header that defines a bunch of macros.
780void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
781 CircuitLoweringState &state) {
782 // Intentionally pass an UnknownLoc here so we don't get line number
783 // comments on the output of this boilerplate in generated Verilog.
784 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), op);
785
786 // Helper function to emit a "#ifdef guard" with a `define in the then and
787 // optionally in the else branch.
788 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
789 StringRef defineTrue = "",
790 StringRef defineFalse = StringRef()) {
791 if (!defineFalse.data()) {
792 assert(defineTrue.data() && "didn't define anything");
793 b.create<sv::IfDefOp>(
794 guard, [&]() { b.create<sv::MacroDefOp>(defName, defineTrue); });
795 } else {
796 b.create<sv::IfDefOp>(
797 guard,
798 [&]() {
799 if (defineTrue.data())
800 b.create<sv::MacroDefOp>(defName, defineTrue);
801 },
802 [&]() { b.create<sv::MacroDefOp>(defName, defineFalse); });
803 }
804 };
805
806 // Helper function to emit #ifndef guard.
807 auto emitGuard = [&](const char *guard, llvm::function_ref<void(void)> body) {
808 b.create<sv::IfDefOp>(
809 guard, []() {}, body);
810 };
811
812 if (state.usedPrintf) {
813 b.create<sv::MacroDeclOp>("PRINTF_FD");
814 b.create<sv::MacroDeclOp>("PRINTF_FD_");
815 b.create<emit::FragmentOp>("PRINTF_FD_FRAGMENT", [&] {
816 b.create<sv::VerbatimOp>(
817 "\n// Users can define 'PRINTF_FD' to add a specified fd to "
818 "prints.");
819 emitGuard("PRINTF_FD_", [&]() {
820 emitGuardedDefine("PRINTF_FD", "PRINTF_FD_", "(`PRINTF_FD)",
821 "32'h80000002");
822 });
823 });
824
825 b.create<sv::MacroDeclOp>("PRINTF_COND");
826 b.create<sv::MacroDeclOp>("PRINTF_COND_");
827 b.create<emit::FragmentOp>("PRINTF_COND_FRAGMENT", [&] {
828 b.create<sv::VerbatimOp>(
829 "\n// Users can define 'PRINTF_COND' to add an extra gate to "
830 "prints.");
831 emitGuard("PRINTF_COND_", [&]() {
832 emitGuardedDefine("PRINTF_COND", "PRINTF_COND_", "(`PRINTF_COND)", "1");
833 });
834 });
835 }
836
837 if (state.usedAssertVerboseCond) {
838 b.create<sv::MacroDeclOp>("ASSERT_VERBOSE_COND");
839 b.create<sv::MacroDeclOp>("ASSERT_VERBOSE_COND_");
840 b.create<emit::FragmentOp>("ASSERT_VERBOSE_COND_FRAGMENT", [&] {
841 b.create<sv::VerbatimOp>(
842 "\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
843 "gate to assert error printing.");
844 emitGuard("ASSERT_VERBOSE_COND_", [&]() {
845 emitGuardedDefine("ASSERT_VERBOSE_COND", "ASSERT_VERBOSE_COND_",
846 "(`ASSERT_VERBOSE_COND)", "1");
847 });
848 });
849 }
850
851 if (state.usedStopCond) {
852 b.create<sv::MacroDeclOp>("STOP_COND");
853 b.create<sv::MacroDeclOp>("STOP_COND_");
854 b.create<emit::FragmentOp>("STOP_COND_FRAGMENT", [&] {
855 b.create<sv::VerbatimOp>(
856 "\n// Users can define 'STOP_COND' to add an extra gate "
857 "to stop conditions.");
858 emitGuard("STOP_COND_", [&]() {
859 emitGuardedDefine("STOP_COND", "STOP_COND_", "(`STOP_COND)", "1");
860 });
861 });
862 }
863}
864
865LogicalResult
866FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
867 SmallVectorImpl<hw::PortInfo> &ports,
868 Operation *moduleOp, StringRef moduleName,
869 CircuitLoweringState &loweringState) {
870 ports.reserve(firrtlPorts.size());
871 size_t numArgs = 0;
872 size_t numResults = 0;
873 for (auto e : llvm::enumerate(firrtlPorts)) {
874 PortInfo firrtlPort = e.value();
875 size_t portNo = e.index();
876 hw::PortInfo hwPort;
877 hwPort.name = firrtlPort.name;
878 hwPort.type = loweringState.lowerType(firrtlPort.type, firrtlPort.loc);
879 if (firrtlPort.sym)
880 if (firrtlPort.sym.size() > 1 ||
881 (firrtlPort.sym.size() == 1 && !firrtlPort.sym.getSymName()))
882 return emitError(firrtlPort.loc)
883 << "cannot lower aggregate port " << firrtlPort.name
884 << " with field sensitive symbols, HW dialect does not support "
885 "per field symbols yet.";
886 hwPort.setSym(firrtlPort.sym, moduleOp->getContext());
887 bool hadDontTouch = firrtlPort.annotations.removeDontTouch();
888 if (hadDontTouch && !hwPort.getSym()) {
889 if (hwPort.type.isInteger(0)) {
890 if (enableAnnotationWarning) {
891 mlir::emitWarning(firrtlPort.loc)
892 << "zero width port " << hwPort.name
893 << " has dontTouch annotation, removing anyway";
894 }
895 continue;
896 }
897
898 hwPort.setSym(
899 hw::InnerSymAttr::get(StringAttr::get(
900 moduleOp->getContext(),
901 Twine("__") + moduleName + Twine("__DONTTOUCH__") +
902 Twine(portNo) + Twine("__") + firrtlPort.name.strref())),
903 moduleOp->getContext());
904 }
905
906 // We can't lower all types, so make sure to cleanly reject them.
907 if (!hwPort.type) {
908 moduleOp->emitError("cannot lower this port type to HW");
909 return failure();
910 }
911
912 // If this is a zero bit port, just drop it. It doesn't matter if it is
913 // input, output, or inout. We don't want these at the HW level.
914 if (hwPort.type.isInteger(0)) {
915 auto sym = hwPort.getSym();
916 if (sym && !sym.empty()) {
917 return mlir::emitError(firrtlPort.loc)
918 << "zero width port " << hwPort.name
919 << " is referenced by name [" << sym
920 << "] (e.g. in an XMR) but must be removed";
921 }
922 continue;
923 }
924
925 // Figure out the direction of the port.
926 if (firrtlPort.isOutput()) {
927 hwPort.dir = hw::ModulePort::Direction::Output;
928 hwPort.argNum = numResults++;
929 } else if (firrtlPort.isInput()) {
930 hwPort.dir = hw::ModulePort::Direction::Input;
931 hwPort.argNum = numArgs++;
932 } else {
933 // If the port is an inout bundle or contains an analog type, then it is
934 // implicitly inout.
935 hwPort.type = hw::InOutType::get(hwPort.type);
936 hwPort.dir = hw::ModulePort::Direction::InOut;
937 hwPort.argNum = numArgs++;
938 }
939 hwPort.loc = firrtlPort.loc;
940 ports.push_back(hwPort);
941 loweringState.processRemainingAnnotations(moduleOp, firrtlPort.annotations);
942 }
943 return success();
944}
945
946/// Map the parameter specifier on the specified extmodule into the HWModule
947/// representation for parameters. If `ignoreValues` is true, all the values
948/// are dropped.
949static ArrayAttr getHWParameters(FExtModuleOp module, bool ignoreValues) {
950 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
951 return cast<ParamDeclAttr>(a);
952 });
953 if (params.empty())
954 return {};
955
956 Builder builder(module);
957
958 // Map the attributes over from firrtl attributes to HW attributes
959 // directly. MLIR's DictionaryAttr always stores keys in the dictionary
960 // in sorted order which is nicely stable.
961 SmallVector<Attribute> newParams;
962 for (const ParamDeclAttr &entry : params) {
963 auto name = entry.getName();
964 auto type = entry.getType();
965 auto value = ignoreValues ? Attribute() : entry.getValue();
966 auto paramAttr =
967 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
968 newParams.push_back(paramAttr);
969 }
970 return builder.getArrayAttr(newParams);
971}
972
973bool FIRRTLModuleLowering::handleForceNameAnnos(
974 FModuleLike oldModule, AnnotationSet &annos,
975 CircuitLoweringState &loweringState) {
976 bool failed = false;
977 // Remove ForceNameAnnotations by generating verilogNames on instances.
978 annos.removeAnnotations([&](Annotation anno) {
979 if (!anno.isClass(forceNameAnnoClass))
980 return false;
981
982 auto sym = anno.getMember<FlatSymbolRefAttr>("circt.nonlocal");
983 // This must be a non-local annotation due to how the Chisel API is
984 // implemented.
985 //
986 // TODO: handle this in some sensible way based on what the SFC does with
987 // a local annotation.
988 if (!sym) {
989 auto diag = oldModule.emitOpError()
990 << "contains a '" << forceNameAnnoClass
991 << "' that is not a non-local annotation";
992 diag.attachNote() << "the erroneous annotation is '" << anno.getDict()
993 << "'\n";
994 failed = true;
995 return false;
996 }
997
998 auto nla = loweringState.nlaTable->getNLA(sym.getAttr());
999 // The non-local anchor must exist.
1000 //
1001 // TODO: handle this with annotation verification.
1002 if (!nla) {
1003 auto diag = oldModule.emitOpError()
1004 << "contains a '" << forceNameAnnoClass
1005 << "' whose non-local symbol, '" << sym
1006 << "' does not exist in the circuit";
1007 diag.attachNote() << "the erroneous annotation is '" << anno.getDict();
1008 failed = true;
1009 return false;
1010 }
1011
1012 // Add the forced name to global state (keyed by a pseudo-inner name ref).
1013 // Error out if this key is alredy in use.
1014 //
1015 // TODO: this error behavior can be relaxed to always overwrite with the
1016 // new forced name (the bug-compatible behavior of the Chisel
1017 // implementation) or fixed to duplicate modules such that the naming can
1018 // be applied.
1019 auto inst =
1020 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1021 auto inserted = loweringState.instanceForceNames.insert(
1022 {{inst.getModule(), inst.getName()}, anno.getMember("name")});
1023 if (!inserted.second &&
1024 (anno.getMember("name") != (inserted.first->second))) {
1025 auto diag = oldModule.emitError()
1026 << "contained multiple '" << forceNameAnnoClass
1027 << "' with different names: " << inserted.first->second
1028 << " was not " << anno.getMember("name");
1029 diag.attachNote() << "the erroneous annotation is '" << anno.getDict()
1030 << "'";
1031 failed = true;
1032 return false;
1033 }
1034 return true;
1035 });
1036 return failed;
1037}
1038
1040FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1041 Block *topLevelModule,
1042 CircuitLoweringState &loweringState) {
1043 // Map the ports over, lowering their types as we go.
1044 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1045 SmallVector<hw::PortInfo, 8> ports;
1046 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1047 loweringState)))
1048 return {};
1049
1050 StringRef verilogName;
1051 if (auto defName = oldModule.getDefname())
1052 verilogName = defName.value();
1053
1054 // Build the new hw.module op.
1055 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1056 auto nameAttr = builder.getStringAttr(oldModule.getName());
1057 // Map over parameters if present. Drop all values as we do so, so there are
1058 // no known default values in the extmodule. This ensures that the
1059 // hw.instance will print all the parameters when generating verilog.
1060 auto parameters = getHWParameters(oldModule, /*ignoreValues=*/true);
1061 auto newModule = builder.create<hw::HWModuleExternOp>(
1062 oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1063 SymbolTable::setSymbolVisibility(newModule,
1064 SymbolTable::getSymbolVisibility(oldModule));
1065
1066 bool hasOutputPort =
1067 llvm::any_of(firrtlPorts, [&](auto p) { return p.isOutput(); });
1068 if (!hasOutputPort &&
1070 loweringState.isInDUT(oldModule))
1071 newModule->setAttr("firrtl.extract.cover.extra", builder.getUnitAttr());
1072
1073 AnnotationSet annos(oldModule);
1074 if (handleForceNameAnnos(oldModule, annos, loweringState))
1075 return {};
1076
1077 loweringState.processRemainingAnnotations(oldModule, annos);
1078 return newModule;
1079}
1080
1082FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1083 Block *topLevelModule,
1084 CircuitLoweringState &loweringState) {
1085 // Map the ports over, lowering their types as we go.
1086 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1087 SmallVector<hw::PortInfo, 8> ports;
1088 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1089 loweringState)))
1090 return {};
1091
1092 // Build the new hw.module op.
1093 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1094 auto newModule = builder.create<hw::HWModuleExternOp>(
1095 oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1096 oldModule.getModuleNameAttr());
1097 loweringState.processRemainingAnnotations(oldModule,
1098 AnnotationSet(oldModule));
1099 return newModule;
1100}
1101
1102/// Run on each firrtl.module, creating a basic hw.module for the firrtl module.
1104FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1105 CircuitLoweringState &loweringState) {
1106 // Map the ports over, lowering their types as we go.
1107 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1108 SmallVector<hw::PortInfo, 8> ports;
1109 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1110 loweringState)))
1111 return {};
1112
1113 // Build the new hw.module op.
1114 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1115 auto nameAttr = builder.getStringAttr(oldModule.getName());
1116 auto newModule =
1117 builder.create<hw::HWModuleOp>(oldModule.getLoc(), nameAttr, ports);
1118
1119 if (auto comment = oldModule->getAttrOfType<StringAttr>("comment"))
1120 newModule.setCommentAttr(comment);
1121
1122 // Copy over any attributes which are not required for FModuleOp.
1123 SmallVector<StringRef, 12> attrNames = {
1124 "annotations", "convention", "layers",
1125 "portNames", "sym_name", "portDirections",
1126 "portTypes", "portAnnotations", "portSymbols",
1127 "portLocations", "parameters", SymbolTable::getVisibilityAttrName()};
1128
1129 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1130 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1131 for (auto i :
1132 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1133 return !attrSet.count(namedAttr.getName()) &&
1134 !newModule->getAttrDictionary().contains(namedAttr.getName());
1135 }))
1136 newAttrs.push_back(i);
1137
1138 newModule->setAttrs(newAttrs);
1139
1140 // If the circuit has an entry point, set all other modules private.
1141 // Otherwise, mark all modules as public.
1142 SymbolTable::setSymbolVisibility(newModule,
1143 SymbolTable::getSymbolVisibility(oldModule));
1144
1145 // Transform module annotations
1146 AnnotationSet annos(oldModule);
1147
1149 newModule->setAttr("firrtl.extract.cover.extra", builder.getUnitAttr());
1150
1151 // If this is in the test harness, make sure it goes to the test directory.
1152 // Do not update output file information if it is already present.
1153 if (auto testBenchDir = loweringState.getTestBenchDirectory())
1154 if (loweringState.isInTestHarness(oldModule)) {
1155 if (!newModule->hasAttr("output_file"))
1156 newModule->setAttr("output_file", testBenchDir);
1157 newModule->setAttr("firrtl.extract.do_not_extract",
1158 builder.getUnitAttr());
1159 newModule.setCommentAttr(
1160 builder.getStringAttr("VCS coverage exclude_file"));
1161 }
1162
1163 if (handleForceNameAnnos(oldModule, annos, loweringState))
1164 return {};
1165
1166 loweringState.processRemainingAnnotations(oldModule, annos);
1167 return newModule;
1168}
1169
1170/// Given a value of analog type, check to see the only use of it is an
1171/// attach. If so, remove the attach and return the value being attached to
1172/// it, converted to an HW inout type. If this isn't a situation we can
1173/// handle, just return null.
1175 Operation *insertPoint) {
1176 if (!value.hasOneUse())
1177 return {};
1178
1179 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1180 if (!attach || attach.getNumOperands() != 2)
1181 return {};
1182
1183 // Don't optimize zero bit analogs.
1184 auto loweredType = lowerType(value.getType());
1185 if (loweredType.isInteger(0))
1186 return {};
1187
1188 // Check to see if the attached value dominates the insertion point. If
1189 // not, just fail.
1190 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1191 auto *op = attachedValue.getDefiningOp();
1192 if (op && op->getBlock() == insertPoint->getBlock() &&
1193 !op->isBeforeInBlock(insertPoint))
1194 return {};
1195
1196 attach.erase();
1197
1198 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1199 return castFromFIRRTLType(attachedValue, hw::InOutType::get(loweredType),
1200 builder);
1201}
1202
1203/// Given a value of flip type, check to see if all of the uses of it are
1204/// connects. If so, remove the connects and return the value being connected
1205/// to it, converted to an HW type. If this isn't a situation we can handle,
1206/// just return null.
1207///
1208/// This can happen when there are no connects to the value. The 'mergePoint'
1209/// location is where a 'hw.merge' operation should be inserted if needed.
1210static Value
1211tryEliminatingConnectsToValue(Value flipValue, Operation *insertPoint,
1212 CircuitLoweringState &loweringState) {
1213 // Handle analog's separately.
1214 if (type_isa<AnalogType>(flipValue.getType()))
1215 return tryEliminatingAttachesToAnalogValue(flipValue, insertPoint);
1216
1217 Operation *connectOp = nullptr;
1218 for (auto &use : flipValue.getUses()) {
1219 // We only know how to deal with connects where this value is the
1220 // destination.
1221 if (use.getOperandNumber() != 0)
1222 return {};
1223 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1224 return {};
1225
1226 // We only support things with a single connect.
1227 if (connectOp)
1228 return {};
1229 connectOp = use.getOwner();
1230 }
1231
1232 // We don't have an HW equivalent of "poison" so just don't special case
1233 // the case where there are no connects other uses of an output.
1234 if (!connectOp)
1235 return {}; // TODO: Emit an sv.constant here since it is unconnected.
1236
1237 // Don't special case zero-bit results.
1238 auto loweredType =
1239 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1240 if (loweredType.isInteger(0))
1241 return {};
1242
1243 // Convert each connect into an extended version of its operand being
1244 // output.
1245 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1246
1247 auto connectSrc = connectOp->getOperand(1);
1248
1249 // Directly forward foreign types.
1250 if (!isa<FIRRTLType>(connectSrc.getType())) {
1251 connectOp->erase();
1252 return connectSrc;
1253 }
1254
1255 // Convert fliped sources to passive sources.
1256 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1257 connectSrc = builder
1258 .create<mlir::UnrealizedConversionCastOp>(
1259 type_cast<FIRRTLBaseType>(connectSrc.getType())
1260 .getPassiveType(),
1261 connectSrc)
1262 .getResult(0);
1263
1264 // We know it must be the destination operand due to the types, but the
1265 // source may not match the destination width.
1266 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1267
1268 if (destTy != connectSrc.getType() &&
1269 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1270 isa<BaseTypeAliasType>(destTy))) {
1271 connectSrc =
1272 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1273 }
1274 if (!destTy.isGround()) {
1275 // If types are not ground type and they don't match, we give up.
1276 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1277 return {};
1278 } else if (destTy.getBitWidthOrSentinel() !=
1279 type_cast<FIRRTLBaseType>(connectSrc.getType())
1280 .getBitWidthOrSentinel()) {
1281 // The only type mismatchs we care about is due to integer width
1282 // differences.
1283 auto destWidth = destTy.getBitWidthOrSentinel();
1284 assert(destWidth != -1 && "must know integer widths");
1285 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1286 }
1287
1288 // Remove the connect and use its source as the value for the output.
1289 connectOp->erase();
1290
1291 // Convert from FIRRTL type to builtin type.
1292 return castFromFIRRTLType(connectSrc, loweredType, builder);
1293}
1294
1295static SmallVector<SubfieldOp> getAllFieldAccesses(Value structValue,
1296 StringRef field) {
1297 SmallVector<SubfieldOp> accesses;
1298 for (auto *op : structValue.getUsers()) {
1299 assert(isa<SubfieldOp>(op));
1300 auto fieldAccess = cast<SubfieldOp>(op);
1301 auto elemIndex =
1302 fieldAccess.getInput().getType().base().getElementIndex(field);
1303 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1304 accesses.push_back(fieldAccess);
1305 }
1306 return accesses;
1307}
1308
1309/// Now that we have the operations for the hw.module's corresponding to the
1310/// firrtl.module's, we can go through and move the bodies over, updating the
1311/// ports and output op.
1312LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1313 FModuleOp oldModule, hw::HWModuleOp newModule,
1314 CircuitLoweringState &loweringState) {
1315 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1316
1317 // Use a placeholder instruction be a cursor that indicates where we want to
1318 // move the new function body to. This is important because we insert some
1319 // ops at the start of the function and some at the end, and the body is
1320 // currently empty to avoid iterator invalidation.
1321 auto cursor = bodyBuilder.create<hw::ConstantOp>(APInt(1, 1));
1322 bodyBuilder.setInsertionPoint(cursor);
1323
1324 // Insert argument casts, and re-vector users in the old body to use them.
1325 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1326 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1327 "port count mismatch");
1328
1329 SmallVector<Value, 4> outputs;
1330
1331 // This is the terminator in the new module.
1332 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1333 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1334
1335 unsigned nextHWInputArg = 0;
1336 int hwPortIndex = -1;
1337 for (auto [firrtlPortIndex, port] : llvm::enumerate(firrtlPorts)) {
1338 // Inputs and outputs are both modeled as arguments in the FIRRTL level.
1339 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1340
1341 bool isZeroWidth =
1342 type_isa<FIRRTLBaseType>(port.type) &&
1343 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1344 if (!isZeroWidth)
1345 ++hwPortIndex;
1346
1347 if (!port.isOutput() && !isZeroWidth) {
1348 // Inputs and InOuts are modeled as arguments in the result, so we can
1349 // just map them over. We model zero bit outputs as inouts.
1350 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1351
1352 // Cast the argument to the old type, reintroducing sign information in
1353 // the hw.module body.
1354 newArg = castToFIRRTLType(newArg, oldArg.getType(), bodyBuilder);
1355 // Switch all uses of the old operands to the new ones.
1356 oldArg.replaceAllUsesWith(newArg);
1357 continue;
1358 }
1359
1360 // We lower zero width inout and outputs to a wire that isn't connected to
1361 // anything outside the module. Inputs are lowered to zero.
1362 if (isZeroWidth && port.isInput()) {
1363 Value newArg = bodyBuilder
1364 .create<WireOp>(port.type, "." + port.getName().str() +
1365 ".0width_input")
1366 .getResult();
1367 oldArg.replaceAllUsesWith(newArg);
1368 continue;
1369 }
1370
1371 if (auto value =
1372 tryEliminatingConnectsToValue(oldArg, outputOp, loweringState)) {
1373 // If we were able to find the value being connected to the output,
1374 // directly use it!
1375 outputs.push_back(value);
1376 assert(oldArg.use_empty() && "should have removed all uses of oldArg");
1377 continue;
1378 }
1379
1380 // Outputs need a temporary wire so they can be connect'd to, which we
1381 // then return.
1382 auto newArg = bodyBuilder.create<WireOp>(
1383 port.type, "." + port.getName().str() + ".output");
1384
1385 // Switch all uses of the old operands to the new ones.
1386 oldArg.replaceAllUsesWith(newArg.getResult());
1387
1388 // Don't output zero bit results or inouts.
1389 auto resultHWType = loweringState.lowerType(port.type, port.loc);
1390 if (!resultHWType.isInteger(0)) {
1391 auto output =
1392 castFromFIRRTLType(newArg.getResult(), resultHWType, outputBuilder);
1393 outputs.push_back(output);
1394
1395 // If output port has symbol, move it to this wire.
1396 if (auto sym = newModule.getPort(hwPortIndex).getSym()) {
1397 newArg.setInnerSymAttr(sym);
1398 newModule.setPortSymbolAttr(hwPortIndex, {});
1399 }
1400 }
1401 }
1402
1403 // Update the hw.output terminator with the list of outputs we have.
1404 outputOp->setOperands(outputs);
1405
1406 // Finally splice the body over, don't move the old terminator over though.
1407 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1408 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1409 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1410 oldBlockInstList.begin(), oldBlockInstList.end());
1411
1412 // We are done with our cursor op.
1413 cursor.erase();
1414
1415 return success();
1416}
1417
1418/// Run on each `verif.formal` to populate its body based on the original
1419/// `firrtl.formal` operation.
1420LogicalResult
1421FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp formalOp,
1422 CircuitLoweringState &loweringState) {
1423 auto builder = OpBuilder::atBlockEnd(&formalOp.getBody().front());
1424
1425 // Find the module targeted by the `firrtl.formal` operation. The `FormalOp`
1426 // verifier guarantees the module exists and that it is an `FModuleOp`. This
1427 // we can then translate to the corresponding `HWModuleOp`.
1428 auto oldFormalOp = cast<FormalOp>(loweringState.getOldModule(formalOp));
1429 auto moduleName = oldFormalOp.getModuleNameAttr().getAttr();
1430 auto oldModule = cast<FModuleOp>(
1431 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1432 auto newModule =
1433 dyn_cast_or_null<hw::HWModuleOp>(loweringState.getNewModule(oldModule));
1434 if (!newModule)
1435 return oldFormalOp->emitOpError()
1436 << "could not find module " << oldModule.getSymNameAttr();
1437
1438 // Create a symbolic input for every input of the lowered module.
1439 SmallVector<Value> symbolicInputs;
1440 for (auto arg : newModule.getBody().getArguments())
1441 symbolicInputs.push_back(
1442 builder.create<verif::SymbolicValueOp>(arg.getLoc(), arg.getType()));
1443
1444 // Instantiate the module with the given symbolic inputs.
1445 builder.create<hw::InstanceOp>(formalOp.getLoc(), newModule,
1446 newModule.getNameAttr(), symbolicInputs);
1447 return success();
1448}
1449
1450//===----------------------------------------------------------------------===//
1451// Module Body Lowering Pass
1452//===----------------------------------------------------------------------===//
1453
1454namespace {
1455
1456struct FIRRTLLowering : public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1457
1458 FIRRTLLowering(hw::HWModuleOp module, CircuitLoweringState &circuitState)
1459 : theModule(module), circuitState(circuitState),
1460 builder(module.getLoc(), module.getContext()), moduleNamespace(module),
1461 backedgeBuilder(builder, module.getLoc()) {}
1462
1463 LogicalResult run();
1464
1465 // Helpers.
1466 Value getOrCreateClockConstant(seq::ClockConst clock);
1467 Value getOrCreateIntConstant(const APInt &value);
1468 Value getOrCreateIntConstant(unsigned numBits, uint64_t val,
1469 bool isSigned = false) {
1470 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1471 }
1472 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1473 Value getOrCreateXConstant(unsigned numBits);
1474 Value getOrCreateZConstant(Type type);
1475 Value getPossiblyInoutLoweredValue(Value value);
1476 Value getLoweredValue(Value value);
1477 Value getLoweredNonClockValue(Value value);
1478 Value getLoweredAndExtendedValue(Value value, Type destType);
1479 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1480 Value getLoweredFmtOperand(Value operand);
1481 LogicalResult setLowering(Value orig, Value result);
1482 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1483 template <typename ResultOpType, typename... CtorArgTypes>
1484 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1485 template <typename ResultOpType, typename... CtorArgTypes>
1486 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1487 Backedge createBackedge(Location loc, Type type);
1488 Backedge createBackedge(Value orig, Type type);
1489 bool updateIfBackedge(Value dest, Value src);
1490
1491 /// Returns true if the lowered operation requires an inner symbol on it.
1492 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1494 return true;
1495 if (!hasDroppableName(op))
1496 return true;
1497 if (auto forceable = dyn_cast<Forceable>(op.getOperation()))
1498 if (forceable.isForceable())
1499 return true;
1500 return false;
1501 }
1502
1503 /// Gets the lowered InnerSymAttr of this operation. If the operation is
1504 /// DontTouched, has a non-droppable name, or is forceable, then we will
1505 /// ensure that the InnerSymAttr has a symbol with fieldID zero.
1506 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1507 auto attr = op.getInnerSymAttr();
1508 // TODO: we should be checking for symbol collisions here and renaming as
1509 // neccessary. As well, we should record the renamings in a map so that we
1510 // can update any InnerRefAttrs that we find.
1511 if (requiresInnerSymbol(op))
1512 std::tie(attr, std::ignore) = getOrAddInnerSym(
1513 op.getContext(), attr, 0,
1514 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
1515 return attr;
1516 }
1517
1518 void runWithInsertionPointAtEndOfBlock(const std::function<void(void)> &fn,
1519 Region &region);
1520
1521 /// Return a read value for the specified inout value, auto-uniquing them.
1522 Value getReadValue(Value v);
1523 /// Return an `i1` value for the specified value, auto-uniqueing them.
1524 Value getNonClockValue(Value v);
1525
1526 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1527 sv::ResetType resetStyle, sv::EventControl resetEdge,
1528 Value reset, const std::function<void(void)> &body = {},
1529 const std::function<void(void)> &resetBody = {});
1530 void addToAlwaysBlock(Value clock,
1531 const std::function<void(void)> &body = {}) {
1532 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1533 sv::EventControl(), Value(), body,
1534 std::function<void(void)>());
1535 }
1536
1537 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1538 std::function<void(void)> emit);
1539 void addToIfDefBlock(StringRef cond, std::function<void(void)> thenCtor,
1540 std::function<void(void)> elseCtor = {});
1541 void addToInitialBlock(std::function<void(void)> body);
1542 void addIfProceduralBlock(Value cond, std::function<void(void)> thenCtor,
1543 std::function<void(void)> elseCtor = {});
1544 Value getExtOrTruncAggregateValue(Value array, FIRRTLBaseType sourceType,
1545 FIRRTLBaseType destType,
1546 bool allowTruncate);
1547 Value createArrayIndexing(Value array, Value index);
1548 Value createValueWithMuxAnnotation(Operation *op, bool isMux2);
1549
1550 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1551 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1552 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1553
1554 // Lowering hooks.
1555 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1556 UnloweredOpResult handleUnloweredOp(Operation *op);
1557 LogicalResult visitExpr(ConstantOp op);
1558 LogicalResult visitExpr(SpecialConstantOp op);
1559 LogicalResult visitExpr(SubindexOp op);
1560 LogicalResult visitExpr(SubaccessOp op);
1561 LogicalResult visitExpr(SubfieldOp op);
1562 LogicalResult visitExpr(VectorCreateOp op);
1563 LogicalResult visitExpr(BundleCreateOp op);
1564 LogicalResult visitExpr(FEnumCreateOp op);
1565 LogicalResult visitExpr(AggregateConstantOp op);
1566 LogicalResult visitExpr(IsTagOp op);
1567 LogicalResult visitExpr(SubtagOp op);
1568 LogicalResult visitUnhandledOp(Operation *op) { return failure(); }
1569 LogicalResult visitInvalidOp(Operation *op) {
1570 if (auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1571 return visitUnrealizedConversionCast(castOp);
1572 return failure();
1573 }
1574
1575 // Declarations.
1576 LogicalResult visitDecl(WireOp op);
1577 LogicalResult visitDecl(NodeOp op);
1578 LogicalResult visitDecl(RegOp op);
1579 LogicalResult visitDecl(RegResetOp op);
1580 LogicalResult visitDecl(MemOp op);
1581 LogicalResult visitDecl(InstanceOp oldInstance);
1582 LogicalResult visitDecl(VerbatimWireOp op);
1583
1584 // Unary Ops.
1585 LogicalResult lowerNoopCast(Operation *op);
1586 LogicalResult visitExpr(AsSIntPrimOp op);
1587 LogicalResult visitExpr(AsUIntPrimOp op);
1588 LogicalResult visitExpr(AsClockPrimOp op);
1589 LogicalResult visitExpr(AsAsyncResetPrimOp op) { return lowerNoopCast(op); }
1590
1591 LogicalResult visitExpr(HWStructCastOp op);
1592 LogicalResult visitExpr(BitCastOp op);
1593 LogicalResult
1594 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1595 LogicalResult visitExpr(CvtPrimOp op);
1596 LogicalResult visitExpr(NotPrimOp op);
1597 LogicalResult visitExpr(NegPrimOp op);
1598 LogicalResult visitExpr(PadPrimOp op);
1599 LogicalResult visitExpr(XorRPrimOp op);
1600 LogicalResult visitExpr(AndRPrimOp op);
1601 LogicalResult visitExpr(OrRPrimOp op);
1602
1603 // Binary Ops.
1604 template <typename ResultUnsignedOpType,
1605 typename ResultSignedOpType = ResultUnsignedOpType>
1606 LogicalResult lowerBinOp(Operation *op);
1607 template <typename ResultOpType>
1608 LogicalResult lowerBinOpToVariadic(Operation *op);
1609
1610 template <typename ResultOpType>
1611 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1612
1613 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1614 ICmpPredicate unsignedOp);
1615 template <typename SignedOp, typename UnsignedOp>
1616 LogicalResult lowerDivLikeOp(Operation *op);
1617
1618 LogicalResult visitExpr(CatPrimOp op);
1619
1620 LogicalResult visitExpr(AndPrimOp op) {
1621 return lowerBinOpToVariadic<comb::AndOp>(op);
1622 }
1623 LogicalResult visitExpr(OrPrimOp op) {
1624 return lowerBinOpToVariadic<comb::OrOp>(op);
1625 }
1626 LogicalResult visitExpr(XorPrimOp op) {
1627 return lowerBinOpToVariadic<comb::XorOp>(op);
1628 }
1629 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1630 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1631 }
1632 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1633 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1634 }
1635 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1636 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1637 }
1638 LogicalResult visitExpr(AddPrimOp op) {
1639 return lowerBinOpToVariadic<comb::AddOp>(op);
1640 }
1641 LogicalResult visitExpr(EQPrimOp op) {
1642 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1643 }
1644 LogicalResult visitExpr(NEQPrimOp op) {
1645 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1646 }
1647 LogicalResult visitExpr(LTPrimOp op) {
1648 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1649 }
1650 LogicalResult visitExpr(LEQPrimOp op) {
1651 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1652 }
1653 LogicalResult visitExpr(GTPrimOp op) {
1654 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1655 }
1656 LogicalResult visitExpr(GEQPrimOp op) {
1657 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1658 }
1659
1660 LogicalResult visitExpr(SubPrimOp op) { return lowerBinOp<comb::SubOp>(op); }
1661 LogicalResult visitExpr(MulPrimOp op) {
1662 return lowerBinOpToVariadic<comb::MulOp>(op);
1663 }
1664 LogicalResult visitExpr(DivPrimOp op) {
1665 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1666 }
1667 LogicalResult visitExpr(RemPrimOp op) {
1668 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1669 }
1670
1671 // Intrinsic Operations
1672 LogicalResult visitExpr(IsXIntrinsicOp op);
1673 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1674 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1675 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1676 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1677 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1678 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1679 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1680 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1681 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1682 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1683 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1684 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1685 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1686 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1687 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1688 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1689 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1690 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1691 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1692 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1693
1694 template <typename TargetOp, typename IntrinsicOp>
1695 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1696 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1697 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1698 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1699 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1700 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1701
1702 // Other Operations
1703 LogicalResult visitExpr(BitsPrimOp op);
1704 LogicalResult visitExpr(InvalidValueOp op);
1705 LogicalResult visitExpr(HeadPrimOp op);
1706 LogicalResult visitExpr(ShlPrimOp op);
1707 LogicalResult visitExpr(ShrPrimOp op);
1708 LogicalResult visitExpr(DShlPrimOp op) {
1709 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1710 }
1711 LogicalResult visitExpr(DShrPrimOp op) {
1712 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1713 }
1714 LogicalResult visitExpr(DShlwPrimOp op) {
1715 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1716 }
1717 LogicalResult visitExpr(TailPrimOp op);
1718 LogicalResult visitExpr(MuxPrimOp op);
1719 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1720 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1721 LogicalResult visitExpr(MultibitMuxOp op);
1722 LogicalResult visitExpr(VerbatimExprOp op);
1723 LogicalResult visitExpr(XMRRefOp op);
1724 LogicalResult visitExpr(XMRDerefOp op);
1725
1726 // Statements
1727 LogicalResult lowerVerificationStatement(
1728 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1729 Value enable, StringAttr messageAttr, ValueRange operands,
1730 StringAttr nameAttr, bool isConcurrent, EventControl eventControl);
1731
1732 LogicalResult visitStmt(SkipOp op);
1733
1734 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
1735 LogicalResult visitStmt(ConnectOp op);
1736 LogicalResult visitStmt(MatchingConnectOp op);
1737 LogicalResult visitStmt(ForceOp op);
1738 LogicalResult visitStmt(PrintFOp op);
1739 LogicalResult visitStmt(StopOp op);
1740 LogicalResult visitStmt(AssertOp op);
1741 LogicalResult visitStmt(AssumeOp op);
1742 LogicalResult visitStmt(CoverOp op);
1743 LogicalResult visitStmt(AttachOp op);
1744 LogicalResult visitStmt(RefForceOp op);
1745 LogicalResult visitStmt(RefForceInitialOp op);
1746 LogicalResult visitStmt(RefReleaseOp op);
1747 LogicalResult visitStmt(RefReleaseInitialOp op);
1748
1749 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
1750 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
1751 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
1752
1753 LogicalResult fixupLTLOps();
1754
1755 Type lowerType(Type type) {
1756 return circuitState.lowerType(type, builder.getLoc());
1757 }
1758
1759private:
1760 /// The module we're lowering into.
1761 hw::HWModuleOp theModule;
1762
1763 /// Global state.
1764 CircuitLoweringState &circuitState;
1765
1766 /// This builder is set to the right location for each visit call.
1767 ImplicitLocOpBuilder builder;
1768
1769 /// Each value lowered (e.g. operation result) is kept track in this map.
1770 /// The key should have a FIRRTL type, the result will have an HW dialect
1771 /// type.
1772 DenseMap<Value, Value> valueMapping;
1773
1774 /// Mapping from clock values to corresponding non-clock values converted
1775 /// via a deduped `seq.from_clock` op.
1776 DenseMap<Value, Value> fromClockMapping;
1777
1778 /// This keeps track of constants that we have created so we can reuse them.
1779 /// This is populated by the getOrCreateIntConstant method.
1780 DenseMap<Attribute, Value> hwConstantMap;
1781 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1782
1783 /// This keeps track of constant X that we have created so we can reuse them.
1784 /// This is populated by the getOrCreateXConstant method.
1785 DenseMap<unsigned, Value> hwConstantXMap;
1786 DenseMap<Type, Value> hwConstantZMap;
1787
1788 /// We auto-unique "ReadInOut" ops from wires and regs, enabling
1789 /// optimizations and CSEs of the read values to be more obvious. This
1790 /// caches a known ReadInOutOp for the given value and is managed by
1791 /// `getReadValue(v)`.
1792 DenseMap<Value, Value> readInOutCreated;
1793
1794 // We auto-unique graph-level blocks to reduce the amount of generated
1795 // code and ensure that side effects are properly ordered in FIRRTL.
1796 using AlwaysKeyType = std::tuple<Block *, sv::EventControl, Value,
1797 sv::ResetType, sv::EventControl, Value>;
1799 alwaysBlocks;
1802
1803 /// A namespace that can be used to generate new symbol names that are unique
1804 /// within this module.
1805 hw::InnerSymbolNamespace moduleNamespace;
1806
1807 /// A backedge builder to directly materialize values during the lowering
1808 /// without requiring temporary wires.
1809 BackedgeBuilder backedgeBuilder;
1810 /// Currently unresolved backedges. More precisely, a mapping from the
1811 /// backedge value to the value it will be replaced with. We use a MapVector
1812 /// so that a combinational cycles of backedges, the one backedge that gets
1813 /// replaced with an undriven wire is consistent.
1814 llvm::MapVector<Value, Value> backedges;
1815
1816 /// A collection of values generated by the lowering process that may have
1817 /// become obsolete through subsequent parts of the lowering. This covers the
1818 /// values of wires that may be overridden by subsequent connects; or
1819 /// subaccesses that appear only as destination of a connect, and thus gets
1820 /// obsoleted by the connect directly updating the wire or register.
1821 DenseSet<Operation *> maybeUnusedValues;
1822
1823 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1824 void maybeUnused(Value value) {
1825 if (auto *op = value.getDefiningOp())
1826 maybeUnused(op);
1827 }
1828
1829 /// A worklist of LTL operations that don't have their final type yet. The
1830 /// FIRRTL intrinsics for LTL ops all use `uint<1>` types, but the actual LTL
1831 /// ops themselves have more precise `!ltl.sequence` and `!ltl.property`
1832 /// types. After all LTL ops have been lowered, this worklist is used to
1833 /// compute their actual types (re-inferring return types) and push the
1834 /// updated types to their users. This also drops any `hw.wire`s in between
1835 /// the LTL ops, which were necessary to go from the def-before-use FIRRTL
1836 /// dialect to the graph-like HW dialect.
1837 SetVector<Operation *> ltlOpFixupWorklist;
1838};
1839} // end anonymous namespace
1840
1841LogicalResult FIRRTLModuleLowering::lowerModuleOperations(
1842 hw::HWModuleOp module, CircuitLoweringState &loweringState) {
1843 return FIRRTLLowering(module, loweringState).run();
1844}
1845
1846// This is the main entrypoint for the lowering pass.
1847LogicalResult FIRRTLLowering::run() {
1848 // FIRRTL FModule is a single block because FIRRTL ops are a DAG. Walk
1849 // through each operation, lowering each in turn if we can, introducing
1850 // casts if we cannot.
1851 auto &body = theModule.getBody();
1852
1853 SmallVector<Operation *, 16> opsToRemove;
1854
1855 // Iterate through each operation in the module body, attempting to lower
1856 // each of them. We maintain 'builder' for each invocation.
1857 auto result = theModule.walk([&](Operation *op) {
1858 builder.setInsertionPoint(op);
1859 builder.setLoc(op->getLoc());
1860 auto done = succeeded(dispatchVisitor(op));
1861 circuitState.processRemainingAnnotations(op, AnnotationSet(op));
1862 if (done)
1863 opsToRemove.push_back(op);
1864 else {
1865 switch (handleUnloweredOp(op)) {
1866 case AlreadyLowered:
1867 break; // Something like hw.output, which is already lowered.
1868 case NowLowered: // Something handleUnloweredOp removed.
1869 opsToRemove.push_back(op);
1870 break;
1871 case LoweringFailure:
1872 backedgeBuilder.abandon();
1873 return WalkResult::interrupt();
1874 }
1875 }
1876 return WalkResult::advance();
1877 });
1878
1879 if (result.wasInterrupted())
1880 return failure();
1881
1882 // Replace all backedges with uses of their regular values. We process them
1883 // after the module body since the lowering table is too hard to keep up to
1884 // date. Multiple operations may be lowered to the same backedge when values
1885 // are folded, which means we would have to scan the entire lowering table to
1886 // safely replace a backedge.
1887 for (auto &[backedge, value] : backedges) {
1888 SmallVector<Location> driverLocs;
1889 // In the case where we have backedges connected to other backedges, we have
1890 // to find the value that actually drives the group.
1891 while (true) {
1892 // If we find the original backedge we have some undriven logic or
1893 // a combinatorial loop. Bail out and provide information on the nodes.
1894 if (backedge == value) {
1895 Location edgeLoc = backedge.getLoc();
1896 if (driverLocs.empty()) {
1897 mlir::emitError(edgeLoc, "sink does not have a driver");
1898 } else {
1899 auto diag = mlir::emitError(edgeLoc, "sink in combinational loop");
1900 for (auto loc : driverLocs)
1901 diag.attachNote(loc) << "through driver here";
1902 }
1903 backedgeBuilder.abandon();
1904 return failure();
1905 }
1906 // If the value is not another backedge, we have found the driver.
1907 auto *it = backedges.find(value);
1908 if (it == backedges.end())
1909 break;
1910 // Find what is driving the next backedge.
1911 driverLocs.push_back(value.getLoc());
1912 value = it->second;
1913 }
1914 if (auto *defOp = backedge.getDefiningOp())
1915 maybeUnusedValues.erase(defOp);
1916 backedge.replaceAllUsesWith(value);
1917 }
1918
1919 // Now that all of the operations that can be lowered are, remove th
1920 // original values. We know that any lowered operations will be dead (if
1921 // removed in reverse order) at this point - any users of them from
1922 // unremapped operations will be changed to use the newly lowered ops.
1923 hw::ConstantOp zeroI0;
1924 while (!opsToRemove.empty()) {
1925 auto *op = opsToRemove.pop_back_val();
1926
1927 // We remove zero-width values when lowering FIRRTL ops. We can't remove
1928 // such a value if it escapes to a foreign op. In that case, create an
1929 // `hw.constant 0 : i0` to pass along.
1930 for (auto result : op->getResults()) {
1931 if (!isZeroBitFIRRTLType(result.getType()))
1932 continue;
1933 if (!zeroI0) {
1934 auto builder = OpBuilder::atBlockBegin(&body.front());
1935 zeroI0 = builder.create<hw::ConstantOp>(op->getLoc(),
1936 builder.getIntegerType(0), 0);
1937 maybeUnusedValues.insert(zeroI0);
1938 }
1939 result.replaceAllUsesWith(zeroI0);
1940 }
1941
1942 if (!op->use_empty()) {
1943 auto d = op->emitOpError(
1944 "still has uses; should remove ops in reverse order of visitation");
1945 SmallPtrSet<Operation *, 2> visited;
1946 for (auto *user : op->getUsers())
1947 if (visited.insert(user).second)
1948 d.attachNote(user->getLoc())
1949 << "used by " << user->getName() << " op";
1950 return d;
1951 }
1952 maybeUnusedValues.erase(op);
1953 op->erase();
1954 }
1955
1956 // Prune operations that may have become unused throughout the lowering.
1957 while (!maybeUnusedValues.empty()) {
1958 auto it = maybeUnusedValues.begin();
1959 auto *op = *it;
1960 maybeUnusedValues.erase(it);
1961 if (!isOpTriviallyDead(op))
1962 continue;
1963 for (auto operand : op->getOperands())
1964 if (auto *defOp = operand.getDefiningOp())
1965 maybeUnusedValues.insert(defOp);
1966 op->erase();
1967 }
1968
1969 // Determine the actual types of lowered LTL operations and remove any
1970 // intermediate wires among them.
1971 if (failed(fixupLTLOps()))
1972 return failure();
1973
1974 return backedgeBuilder.clearOrEmitError();
1975}
1976
1977//===----------------------------------------------------------------------===//
1978// Helpers
1979//===----------------------------------------------------------------------===//
1980
1981/// Create uniqued constant clocks.
1982Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
1983 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
1984
1985 auto &entry = hwConstantMap[attr];
1986 if (entry)
1987 return entry;
1988
1989 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1990 entry = entryBuilder.create<seq::ConstClockOp>(builder.getLoc(), attr);
1991 return entry;
1992}
1993
1994/// Check to see if we've already lowered the specified constant. If so,
1995/// return it. Otherwise create it and put it in the entry block for reuse.
1996Value FIRRTLLowering::getOrCreateIntConstant(const APInt &value) {
1997 auto attr = builder.getIntegerAttr(
1998 builder.getIntegerType(value.getBitWidth()), value);
1999
2000 auto &entry = hwConstantMap[attr];
2001 if (entry)
2002 return entry;
2003
2004 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2005 entry = entryBuilder.create<hw::ConstantOp>(builder.getLoc(), attr);
2006 return entry;
2007}
2008
2009/// Check to see if we've already created the specified aggregate constant
2010/// attribute. If so, return it. Otherwise create it.
2011Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2012 Type type) {
2013 // Base case.
2014 if (hw::type_isa<IntegerType>(type))
2015 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2016
2017 auto cache = hwAggregateConstantMap.lookup({value, type});
2018 if (cache)
2019 return cache;
2020
2021 // Recursively construct elements.
2022 SmallVector<Attribute> values;
2023 for (auto e : llvm::enumerate(cast<ArrayAttr>(value))) {
2024 Type subType;
2025 if (auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2026 subType = array.getElementType();
2027 else if (auto structType = hw::type_dyn_cast<hw::StructType>(type))
2028 subType = structType.getElements()[e.index()].type;
2029 else
2030 assert(false && "type must be either array or struct");
2031
2032 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2033 }
2034
2035 // FIRRTL and HW have a different operand ordering for arrays.
2036 if (hw::type_isa<hw::ArrayType>(type))
2037 std::reverse(values.begin(), values.end());
2038
2039 auto &entry = hwAggregateConstantMap[{value, type}];
2040 entry = builder.getArrayAttr(values);
2041 return entry;
2042}
2043
2044/// Zero bit operands end up looking like failures from getLoweredValue. This
2045/// helper function invokes the closure specified if the operand was actually
2046/// zero bit, or returns failure() if it was some other kind of failure.
2047static LogicalResult handleZeroBit(Value failedOperand,
2048 const std::function<LogicalResult()> &fn) {
2049 assert(failedOperand && "Should be called on the failed operand");
2050 if (!isZeroBitFIRRTLType(failedOperand.getType()))
2051 return failure();
2052 return fn();
2053}
2054
2055/// Check to see if we've already lowered the specified constant. If so,
2056/// return it. Otherwise create it and put it in the entry block for reuse.
2057Value FIRRTLLowering::getOrCreateXConstant(unsigned numBits) {
2058
2059 auto &entry = hwConstantXMap[numBits];
2060 if (entry)
2061 return entry;
2062
2063 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2064 entry = entryBuilder.create<sv::ConstantXOp>(
2065 builder.getLoc(), entryBuilder.getIntegerType(numBits));
2066 return entry;
2067}
2068
2069Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2070 auto &entry = hwConstantZMap[type];
2071 if (!entry) {
2072 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2073 entry = entryBuilder.create<sv::ConstantZOp>(builder.getLoc(), type);
2074 }
2075 return entry;
2076}
2077
2078/// Return the lowered HW value corresponding to the specified original value.
2079/// This returns a null value for FIRRTL values that haven't be lowered, e.g.
2080/// unknown width integers. This returns hw::inout type values if present, it
2081/// does not implicitly read from them.
2082Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2083 // Block arguments are considered lowered.
2084 if (isa<BlockArgument>(value))
2085 return value;
2086
2087 // If we lowered this value, then return the lowered value, otherwise fail.
2088 if (auto lowering = valueMapping.lookup(value)) {
2089 assert(!isa<FIRRTLType>(lowering.getType()) &&
2090 "Lowered value should be a non-FIRRTL value");
2091 return lowering;
2092 }
2093 return Value();
2094}
2095
2096/// Return the lowered value corresponding to the specified original value.
2097/// This returns a null value for FIRRTL values that cannot be lowered, e.g.
2098/// unknown width integers.
2099Value FIRRTLLowering::getLoweredValue(Value value) {
2100 auto result = getPossiblyInoutLoweredValue(value);
2101 if (!result)
2102 return result;
2103
2104 // If we got an inout value, implicitly read it. FIRRTL allows direct use
2105 // of wires and other things that lower to inout type.
2106 if (isa<hw::InOutType>(result.getType()))
2107 return getReadValue(result);
2108
2109 return result;
2110}
2111
2112/// Return the lowered value, converting `seq.clock` to `i1.
2113Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2114 auto result = getLoweredValue(value);
2115 if (!result)
2116 return result;
2117
2118 if (hw::type_isa<seq::ClockType>(result.getType()))
2119 return getNonClockValue(result);
2120
2121 return result;
2122}
2123
2124/// Return the lowered aggregate value whose type is converted into
2125/// `destType`. We have to care about the extension/truncation/signedness of
2126/// each element.
2127Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2128 FIRRTLBaseType sourceType,
2129 FIRRTLBaseType destType,
2130 bool allowTruncate) {
2131 SmallVector<Value> resultBuffer;
2132
2133 // Helper function to cast each element of array to dest type.
2134 auto cast = [&](Value value, FIRRTLBaseType sourceType,
2135 FIRRTLBaseType destType) {
2136 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2137 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2138 auto resultType = builder.getIntegerType(destWidth);
2139
2140 if (srcWidth == destWidth)
2141 return value;
2142
2143 if (srcWidth > destWidth) {
2144 if (allowTruncate)
2145 return builder.createOrFold<comb::ExtractOp>(resultType, value, 0);
2146
2147 builder.emitError("operand should not be a truncation");
2148 return Value();
2149 }
2150
2151 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2152 return comb::createOrFoldSExt(value, resultType, builder);
2153 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2154 return builder.createOrFold<comb::ConcatOp>(zero, value);
2155 };
2156
2157 // This recursive function constructs the output array.
2158 std::function<LogicalResult(Value, FIRRTLBaseType, FIRRTLBaseType)> recurse =
2159 [&](Value src, FIRRTLBaseType srcType,
2160 FIRRTLBaseType destType) -> LogicalResult {
2161 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2162 .Case<FVectorType>([&](auto srcVectorType) {
2163 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2164 unsigned size = resultBuffer.size();
2165 unsigned indexWidth =
2166 getBitWidthFromVectorSize(srcVectorType.getNumElements());
2167 for (size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2168 destVectorType.getNumElements());
2169 i != e; ++i) {
2170 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2171 auto arrayIndex = builder.create<hw::ArrayGetOp>(src, iIdx);
2172 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2173 destVectorType.getElementType())))
2174 return failure();
2175 }
2176 SmallVector<Value> temp(resultBuffer.begin() + size,
2177 resultBuffer.end());
2178 auto array = builder.createOrFold<hw::ArrayCreateOp>(temp);
2179 resultBuffer.resize(size);
2180 resultBuffer.push_back(array);
2181 return success();
2182 })
2183 .Case<BundleType>([&](BundleType srcStructType) {
2184 auto destStructType = firrtl::type_cast<BundleType>(destType);
2185 unsigned size = resultBuffer.size();
2186
2187 // TODO: We don't support partial connects for bundles for now.
2188 if (destStructType.getNumElements() != srcStructType.getNumElements())
2189 return failure();
2190
2191 for (auto elem : llvm::enumerate(destStructType)) {
2192 auto structExtract =
2193 builder.create<hw::StructExtractOp>(src, elem.value().name);
2194 if (failed(recurse(structExtract,
2195 srcStructType.getElementType(elem.index()),
2196 destStructType.getElementType(elem.index()))))
2197 return failure();
2198 }
2199 SmallVector<Value> temp(resultBuffer.begin() + size,
2200 resultBuffer.end());
2201 auto newStruct = builder.createOrFold<hw::StructCreateOp>(
2202 lowerType(destStructType), temp);
2203 resultBuffer.resize(size);
2204 resultBuffer.push_back(newStruct);
2205 return success();
2206 })
2207 .Case<IntType>([&](auto) {
2208 if (auto result = cast(src, srcType, destType)) {
2209 resultBuffer.push_back(result);
2210 return success();
2211 }
2212 return failure();
2213 })
2214 .Default([&](auto) { return failure(); });
2215 };
2216
2217 if (failed(recurse(array, sourceType, destType)))
2218 return Value();
2219
2220 assert(resultBuffer.size() == 1 &&
2221 "resultBuffer must only contain a result array if `success` is true");
2222 return resultBuffer[0];
2223}
2224
2225/// Return the lowered value corresponding to the specified original value and
2226/// then extend it to match the width of destType if needed.
2227///
2228/// This returns a null value for FIRRTL values that cannot be lowered, e.g.
2229/// unknown width integers.
2230Value FIRRTLLowering::getLoweredAndExtendedValue(Value value, Type destType) {
2231 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2232 type_isa<FIRRTLBaseType>(destType) &&
2233 "input/output value should be FIRRTL");
2234
2235 // We only know how to extend integer types with known width.
2236 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2237 if (destWidth == -1)
2238 return {};
2239
2240 auto result = getLoweredValue(value);
2241 if (!result) {
2242 // If this was a zero bit operand being extended, then produce a zero of
2243 // the right result type. If it is just a failure, fail.
2244 if (!isZeroBitFIRRTLType(value.getType()))
2245 return {};
2246 // Zero bit results have to be returned as null. The caller can handle
2247 // this if they want to.
2248 if (destWidth == 0)
2249 return {};
2250 // Otherwise, FIRRTL semantics is that an extension from a zero bit value
2251 // always produces a zero value in the destination width.
2252 return getOrCreateIntConstant(destWidth, 0);
2253 }
2254
2255 if (destWidth ==
2256 cast<FIRRTLBaseType>(value.getType()).getBitWidthOrSentinel()) {
2257 // Lookup the lowered type of dest.
2258 auto loweredDstType = lowerType(destType);
2259 if (result.getType() != loweredDstType &&
2260 (isa<hw::TypeAliasType>(result.getType()) ||
2261 isa<hw::TypeAliasType>(loweredDstType))) {
2262 return builder.createOrFold<hw::BitcastOp>(loweredDstType, result);
2263 }
2264 }
2265 // Aggregates values
2266 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2267 // Types already match.
2268 if (destType == value.getType())
2269 return result;
2270
2271 return getExtOrTruncAggregateValue(
2272 result, type_cast<FIRRTLBaseType>(value.getType()),
2273 type_cast<FIRRTLBaseType>(destType),
2274 /* allowTruncate */ false);
2275 }
2276
2277 if (isa<seq::ClockType>(result.getType())) {
2278 // Types already match.
2279 if (destType == value.getType())
2280 return result;
2281 builder.emitError("cannot use clock type as an integer");
2282 return {};
2283 }
2284
2285 auto intResultType = dyn_cast<IntegerType>(result.getType());
2286 if (!intResultType) {
2287 builder.emitError("operand of type ")
2288 << result.getType() << " cannot be used as an integer";
2289 return {};
2290 }
2291
2292 auto srcWidth = intResultType.getWidth();
2293 if (srcWidth == unsigned(destWidth))
2294 return result;
2295
2296 if (srcWidth > unsigned(destWidth)) {
2297 builder.emitError("operand should not be a truncation");
2298 return {};
2299 }
2300
2301 auto resultType = builder.getIntegerType(destWidth);
2302
2303 // Extension follows the sign of the source value, not the destination.
2304 auto valueFIRType =
2305 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2306 if (type_cast<IntType>(valueFIRType).isSigned())
2307 return comb::createOrFoldSExt(result, resultType, builder);
2308
2309 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2310 return builder.createOrFold<comb::ConcatOp>(zero, result);
2311}
2312
2313/// Return the lowered value corresponding to the specified original value and
2314/// then extended or truncated to match the width of destType if needed.
2315///
2316/// This returns a null value for FIRRTL values that cannot be lowered, e.g.
2317/// unknown width integers.
2318Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2319 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2320 type_isa<FIRRTLBaseType>(destType) &&
2321 "input/output value should be FIRRTL");
2322
2323 // We only know how to adjust integer types with known width.
2324 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2325 if (destWidth == -1)
2326 return {};
2327
2328 auto result = getLoweredValue(value);
2329 if (!result) {
2330 // If this was a zero bit operand being extended, then produce a zero of
2331 // the right result type. If it is just a failure, fail.
2332 if (!isZeroBitFIRRTLType(value.getType()))
2333 return {};
2334 // Zero bit results have to be returned as null. The caller can handle
2335 // this if they want to.
2336 if (destWidth == 0)
2337 return {};
2338 // Otherwise, FIRRTL semantics is that an extension from a zero bit value
2339 // always produces a zero value in the destination width.
2340 return getOrCreateIntConstant(destWidth, 0);
2341 }
2342
2343 // Aggregates values
2344 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2345 // Types already match.
2346 if (destType == value.getType())
2347 return result;
2348
2349 return getExtOrTruncAggregateValue(
2350 result, type_cast<FIRRTLBaseType>(value.getType()),
2351 type_cast<FIRRTLBaseType>(destType),
2352 /* allowTruncate */ true);
2353 }
2354
2355 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2356 if (srcWidth == unsigned(destWidth))
2357 return result;
2358
2359 if (destWidth == 0)
2360 return {};
2361
2362 if (srcWidth > unsigned(destWidth)) {
2363 auto resultType = builder.getIntegerType(destWidth);
2364 return builder.createOrFold<comb::ExtractOp>(resultType, result, 0);
2365 }
2366
2367 auto resultType = builder.getIntegerType(destWidth);
2368
2369 // Extension follows the sign of the source value, not the destination.
2370 auto valueFIRType =
2371 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2372 if (type_cast<IntType>(valueFIRType).isSigned())
2373 return comb::createOrFoldSExt(result, resultType, builder);
2374
2375 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2376 return builder.createOrFold<comb::ConcatOp>(zero, result);
2377}
2378
2379/// Return a lowered version of 'operand' suitable for use with substitution /
2380/// format strings. Zero bit operands are rewritten as one bit zeros and signed
2381/// integers are wrapped in $signed().
2382Value FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2383 auto loweredValue = getLoweredValue(operand);
2384 if (!loweredValue) {
2385 // If this is a zero bit operand, just pass a one bit zero.
2386 if (!isZeroBitFIRRTLType(operand.getType()))
2387 return nullptr;
2388 loweredValue = getOrCreateIntConstant(1, 0);
2389 }
2390
2391 // If the operand was an SInt, we want to give the user the option to print
2392 // it as signed decimal and have to wrap it in $signed().
2393 if (auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2394 if (intTy.isSigned())
2395 loweredValue = builder.create<sv::SystemFunctionOp>(
2396 loweredValue.getType(), "signed", loweredValue);
2397
2398 return loweredValue;
2399}
2400
2401/// Set the lowered value of 'orig' to 'result', remembering this in a map.
2402/// This always returns success() to make it more convenient in lowering code.
2403///
2404/// Note that result may be null here if we're lowering orig to a zero-bit
2405/// value.
2406///
2407LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2408 if (auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2409 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2410 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2411
2412#ifndef NDEBUG
2413 auto baseType = getBaseType(origType);
2414 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2415
2416 // Caller should pass null value iff this was a zero bit value.
2417 if (srcWidth != -1) {
2418 if (result)
2419 assert((srcWidth != 0) &&
2420 "Lowering produced value for zero width source");
2421 else
2422 assert((srcWidth == 0) &&
2423 "Lowering produced null value but source wasn't zero width");
2424 }
2425#endif
2426 } else {
2427 assert(result && "Lowering of foreign type produced null value");
2428 }
2429
2430 auto &slot = valueMapping[orig];
2431 assert(!slot && "value lowered multiple times");
2432 slot = result;
2433 return success();
2434}
2435
2436/// Set the lowering for a value to the specified result. This came from a
2437/// possible folding, so check to see if we need to handle a constant.
2438LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2439 Value result) {
2440 // If this is a constant, check to see if we have it in our unique mapping:
2441 // it could have come from folding an operation.
2442 if (auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2443 auto &entry = hwConstantMap[cst.getValueAttr()];
2444 if (entry == cst) {
2445 // We're already using an entry in the constant map, nothing to do.
2446 } else if (entry) {
2447 // We already had this constant, reuse the one we have instead of the
2448 // one we just folded.
2449 result = entry;
2450 cst->erase();
2451 } else {
2452 // This is a new constant. Remember it!
2453 entry = cst;
2454 cst->moveBefore(&theModule.getBodyBlock()->front());
2455 }
2456 }
2457
2458 return setLowering(orig, result);
2459}
2460
2461/// Create a new operation with type ResultOpType and arguments CtorArgTypes,
2462/// then call setLowering with its result.
2463template <typename ResultOpType, typename... CtorArgTypes>
2464LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2465 CtorArgTypes... args) {
2466 auto result = builder.createOrFold<ResultOpType>(args...);
2467 if (auto *op = result.getDefiningOp())
2468 tryCopyName(op, orig);
2469 return setPossiblyFoldedLowering(orig->getResult(0), result);
2470}
2471
2472/// Create a new LTL operation with type ResultOpType and arguments
2473/// CtorArgTypes, then call setLowering with its result. Also add the operation
2474/// to the worklist of LTL ops that need to have their types fixed-up after the
2475/// lowering.
2476template <typename ResultOpType, typename... CtorArgTypes>
2477LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2478 CtorArgTypes... args) {
2479 auto result = builder.createOrFold<ResultOpType>(args...);
2480 if (auto *op = result.getDefiningOp())
2481 ltlOpFixupWorklist.insert(op);
2482 return setPossiblyFoldedLowering(orig->getResult(0), result);
2483}
2484
2485/// Creates a backedge of the specified result type. A backedge represents a
2486/// placeholder to be filled in later by a lowered value. If the backedge is not
2487/// updated with a real value by the end of the pass, it will be replaced with
2488/// an undriven wire. Backedges are allowed to be updated to other backedges.
2489/// If a chain of backedges forms a combinational loop, they will be replaced
2490/// with an undriven wire.
2491Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2492 auto backedge = backedgeBuilder.get(type, loc);
2493 backedges.insert({backedge, backedge});
2494 return backedge;
2495}
2496
2497/// Sets the lowering for a value to a backedge of the specified result type.
2498/// This is useful for lowering types which cannot pass through a wire, or to
2499/// directly materialize values in operations that violate the SSA dominance
2500/// constraint.
2501Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
2502 auto backedge = createBackedge(orig.getLoc(), type);
2503 (void)setLowering(orig, backedge);
2504 return backedge;
2505}
2506
2507/// If the `from` value is in fact a backedge, record that the backedge will
2508/// be replaced by the value. Return true if the destination is a backedge.
2509bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
2510 auto backedgeIt = backedges.find(dest);
2511 if (backedgeIt == backedges.end())
2512 return false;
2513 backedgeIt->second = src;
2514 return true;
2515}
2516
2517/// Switch the insertion point of the current builder to the end of the
2518/// specified block and run the closure. This correctly handles the case
2519/// where the closure is null, but the caller needs to make sure the block
2520/// exists.
2521void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
2522 const std::function<void(void)> &fn, Region &region) {
2523 if (!fn)
2524 return;
2525
2526 auto oldIP = builder.saveInsertionPoint();
2527
2528 builder.setInsertionPointToEnd(&region.front());
2529 fn();
2530 builder.restoreInsertionPoint(oldIP);
2531}
2532
2533/// Return a read value for the specified inout operation, auto-uniquing them.
2534Value FIRRTLLowering::getReadValue(Value v) {
2535 Value result = readInOutCreated.lookup(v);
2536 if (result)
2537 return result;
2538
2539 // Make sure to put the read value at the correct scope so it dominates all
2540 // future uses.
2541 auto oldIP = builder.saveInsertionPoint();
2542 if (auto *vOp = v.getDefiningOp()) {
2543 builder.setInsertionPointAfter(vOp);
2544 } else {
2545 // For reads of ports, just set the insertion point at the top of the
2546 // module.
2547 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
2548 }
2549
2550 // Instead of creating `ReadInOutOp` for `ArrayIndexInOutOp`, create
2551 // `ArrayGetOp` for root arrays.
2552 if (auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
2553 result = getReadValue(arrayIndexInout.getInput());
2554 result = builder.createOrFold<hw::ArrayGetOp>(result,
2555 arrayIndexInout.getIndex());
2556 } else {
2557 // Otherwise, create a read inout operation.
2558 result = builder.createOrFold<sv::ReadInOutOp>(v);
2559 }
2560 builder.restoreInsertionPoint(oldIP);
2561 readInOutCreated.insert({v, result});
2562 return result;
2563}
2564
2565Value FIRRTLLowering::getNonClockValue(Value v) {
2566 auto it = fromClockMapping.try_emplace(v, Value{});
2567 if (it.second) {
2568 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
2569 builder.setInsertionPointAfterValue(v);
2570 it.first->second = builder.create<seq::FromClockOp>(v);
2571 }
2572 return it.first->second;
2573}
2574
2575void FIRRTLLowering::addToAlwaysBlock(
2576 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
2577 sv::EventControl resetEdge, Value reset,
2578 const std::function<void(void)> &body,
2579 const std::function<void(void)> &resetBody) {
2580 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
2581 resetStyle, resetEdge, reset};
2582 sv::AlwaysOp alwaysOp;
2583 sv::IfOp insideIfOp;
2584 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
2585
2586 if (!alwaysOp) {
2587 if (reset) {
2588 assert(resetStyle != sv::ResetType::NoReset);
2589 // Here, we want to create the folloing structure with sv.always and
2590 // sv.if. If `reset` is async, we need to add `reset` to a sensitivity
2591 // list.
2592 //
2593 // sv.always @(clockEdge or reset) {
2594 // sv.if (reset) {
2595 // resetBody
2596 // } else {
2597 // body
2598 // }
2599 // }
2600
2601 auto createIfOp = [&]() {
2602 // It is weird but intended. Here we want to create an empty sv.if
2603 // with an else block.
2604 insideIfOp = builder.create<sv::IfOp>(
2605 reset, []() {}, []() {});
2606 };
2607 if (resetStyle == sv::ResetType::AsyncReset) {
2608 sv::EventControl events[] = {clockEdge, resetEdge};
2609 Value clocks[] = {clock, reset};
2610
2611 alwaysOp = builder.create<sv::AlwaysOp>(events, clocks, [&]() {
2612 if (resetEdge == sv::EventControl::AtNegEdge)
2613 llvm_unreachable("negative edge for reset is not expected");
2614 createIfOp();
2615 });
2616 } else {
2617 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
2618 }
2619 } else {
2620 assert(!resetBody);
2621 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock);
2622 insideIfOp = nullptr;
2623 }
2624 alwaysBlocks[key] = {alwaysOp, insideIfOp};
2625 }
2626
2627 if (reset) {
2628 assert(insideIfOp && "reset body must be initialized before");
2629 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
2630 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
2631 } else {
2632 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
2633 }
2634
2635 // Move the earlier always block(s) down to where the last would have been
2636 // inserted. This ensures that any values used by the always blocks are
2637 // defined ahead of the uses, which leads to better generated Verilog.
2638 alwaysOp->moveBefore(builder.getInsertionBlock(),
2639 builder.getInsertionPoint());
2640}
2641
2642LogicalResult FIRRTLLowering::emitGuards(Location loc,
2643 ArrayRef<Attribute> guards,
2644 std::function<void(void)> emit) {
2645 if (guards.empty()) {
2646 emit();
2647 return success();
2648 }
2649 auto guard = dyn_cast<StringAttr>(guards[0]);
2650 if (!guard)
2651 return mlir::emitError(loc,
2652 "elements in `guards` array must be `StringAttr`");
2653
2654 // Record the guard macro to emit a declaration for it.
2655 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
2656 LogicalResult result = LogicalResult::failure();
2657 addToIfDefBlock(guard.getValue(), [&]() {
2658 result = emitGuards(loc, guards.drop_front(), emit);
2659 });
2660 return result;
2661}
2662
2663void FIRRTLLowering::addToIfDefBlock(StringRef cond,
2664 std::function<void(void)> thenCtor,
2665 std::function<void(void)> elseCtor) {
2666 auto condAttr = builder.getStringAttr(cond);
2667 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
2668 if (op) {
2669 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
2670 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
2671
2672 // Move the earlier #ifdef block(s) down to where the last would have been
2673 // inserted. This ensures that any values used by the #ifdef blocks are
2674 // defined ahead of the uses, which leads to better generated Verilog.
2675 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2676 } else {
2677 ifdefBlocks[{builder.getBlock(), condAttr}] =
2678 builder.create<sv::IfDefOp>(condAttr, thenCtor, elseCtor);
2679 }
2680}
2681
2682void FIRRTLLowering::addToInitialBlock(std::function<void(void)> body) {
2683 auto op = initialBlocks.lookup(builder.getBlock());
2684 if (op) {
2685 runWithInsertionPointAtEndOfBlock(body, op.getBody());
2686
2687 // Move the earlier initial block(s) down to where the last would have
2688 // been inserted. This ensures that any values used by the initial blocks
2689 // are defined ahead of the uses, which leads to better generated Verilog.
2690 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2691 } else {
2692 initialBlocks[builder.getBlock()] = builder.create<sv::InitialOp>(body);
2693 }
2694}
2695
2696void FIRRTLLowering::addIfProceduralBlock(Value cond,
2697 std::function<void(void)> thenCtor,
2698 std::function<void(void)> elseCtor) {
2699 // Check to see if we already have an if on this condition immediately
2700 // before the insertion point. If so, extend it.
2701 auto insertIt = builder.getInsertionPoint();
2702 if (insertIt != builder.getBlock()->begin())
2703 if (auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
2704 if (ifOp.getCond() == cond) {
2705 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
2706 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
2707 return;
2708 }
2709 }
2710
2711 builder.create<sv::IfOp>(cond, thenCtor, elseCtor);
2712}
2713
2714//===----------------------------------------------------------------------===//
2715// Special Operations
2716//===----------------------------------------------------------------------===//
2717
2718/// Handle the case where an operation wasn't lowered. When this happens, the
2719/// operands should just be unlowered non-FIRRTL values. If the operand was
2720/// not lowered then leave it alone, otherwise we have a problem with
2721/// lowering.
2722///
2723FIRRTLLowering::UnloweredOpResult
2724FIRRTLLowering::handleUnloweredOp(Operation *op) {
2725 // Simply pass through non-FIRRTL operations and consider them already
2726 // lowered. This allows us to handled partially lowered inputs, and also allow
2727 // other FIRRTL operations to spawn additional already-lowered operations,
2728 // like `hw.output`.
2729 if (!isa<FIRRTLDialect>(op->getDialect())) {
2730 for (auto &operand : op->getOpOperands())
2731 if (auto lowered = getPossiblyInoutLoweredValue(operand.get()))
2732 operand.set(lowered);
2733 for (auto result : op->getResults())
2734 (void)setLowering(result, result);
2735 return AlreadyLowered;
2736 }
2737
2738 // Ok, at least one operand got lowered, so this operation is using a FIRRTL
2739 // value, but wasn't itself lowered. This is because the lowering is
2740 // incomplete. This is either a bug or incomplete implementation.
2741 //
2742 // There is one aspect of incompleteness we intentionally expect: we allow
2743 // primitive operations that produce a zero bit result to be ignored by the
2744 // lowering logic. They don't have side effects, and handling this corner
2745 // case just complicates each of the lowering hooks. Instead, we just handle
2746 // them all right here.
2747 if (op->getNumResults() == 1) {
2748 auto resultType = op->getResult(0).getType();
2749 if (type_isa<FIRRTLBaseType>(resultType) &&
2750 isZeroBitFIRRTLType(resultType) &&
2751 (isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
2752 // Zero bit values lower to the null Value.
2753 (void)setLowering(op->getResult(0), Value());
2754 return NowLowered;
2755 }
2756 }
2757 op->emitOpError("LowerToHW couldn't handle this operation");
2758 return LoweringFailure;
2759}
2760
2761LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
2762 // Zero width values must be lowered to nothing.
2763 if (isZeroBitFIRRTLType(op.getType()))
2764 return setLowering(op, Value());
2765
2766 return setLowering(op, getOrCreateIntConstant(op.getValue()));
2767}
2768
2769LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
2770 Value cst;
2771 if (isa<ClockType>(op.getType())) {
2772 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
2773 : seq::ClockConst::Low);
2774 } else {
2775 cst = getOrCreateIntConstant(APInt(/*bitWidth*/ 1, op.getValue()));
2776 }
2777 return setLowering(op, cst);
2778}
2779
2780FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
2781 auto iIdx = getOrCreateIntConstant(
2783 firrtl::type_cast<FVectorType>(op.getInput().getType())
2784 .getNumElements()),
2785 op.getIndex());
2786
2787 // If the input has an inout type, we need to lower to ArrayIndexInOutOp;
2788 // otherwise hw::ArrayGetOp.
2789 Value result;
2790 if (isa<sv::InOutType>(input.getType()))
2791 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
2792 else
2793 result = builder.createOrFold<hw::ArrayGetOp>(input, iIdx);
2794 if (auto *definingOp = result.getDefiningOp())
2795 tryCopyName(definingOp, op);
2796 return result;
2797}
2798
2799FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
2800 Value valueIdx = getLoweredAndExtOrTruncValue(
2801 op.getIndex(),
2802 UIntType::get(op->getContext(),
2804 firrtl::type_cast<FVectorType>(op.getInput().getType())
2805 .getNumElements())));
2806 if (!valueIdx) {
2807 op->emitError() << "input lowering failed";
2808 return failure();
2809 }
2810
2811 // If the input has an inout type, we need to lower to ArrayIndexInOutOp;
2812 // otherwise, lower the op to array indexing.
2813 Value result;
2814 if (isa<sv::InOutType>(input.getType()))
2815 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
2816 else
2817 result = createArrayIndexing(input, valueIdx);
2818 if (auto *definingOp = result.getDefiningOp())
2819 tryCopyName(definingOp, op);
2820 return result;
2821}
2822
2823FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
2824 auto resultType = lowerType(op->getResult(0).getType());
2825 if (!resultType || !input) {
2826 op->emitError() << "subfield type lowering failed";
2827 return failure();
2828 }
2829
2830 // If the input has an inout type, we need to lower to StructFieldInOutOp;
2831 // otherwise, StructExtractOp.
2832 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
2833 .getElementName(op.getFieldIndex());
2834 Value result;
2835 if (isa<sv::InOutType>(input.getType()))
2836 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
2837 else
2838 result = builder.createOrFold<hw::StructExtractOp>(input, field);
2839 if (auto *definingOp = result.getDefiningOp())
2840 tryCopyName(definingOp, op);
2841 return result;
2842}
2843
2844LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
2845 if (isZeroBitFIRRTLType(op.getType()))
2846 return setLowering(op, Value());
2847
2848 auto input = getPossiblyInoutLoweredValue(op.getInput());
2849 if (!input)
2850 return op.emitError() << "input lowering failed";
2851
2852 auto result = lowerSubindex(op, input);
2853 if (failed(result))
2854 return failure();
2855 return setLowering(op, *result);
2856}
2857
2858LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
2859 if (isZeroBitFIRRTLType(op.getType()))
2860 return setLowering(op, Value());
2861
2862 auto input = getPossiblyInoutLoweredValue(op.getInput());
2863 if (!input)
2864 return op.emitError() << "input lowering failed";
2865
2866 auto result = lowerSubaccess(op, input);
2867 if (failed(result))
2868 return failure();
2869 return setLowering(op, *result);
2870}
2871
2872LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
2873 // firrtl.mem lowering lowers some SubfieldOps. Zero-width can leave
2874 // invalid subfield accesses
2875 if (getLoweredValue(op) || !op.getInput())
2876 return success();
2877
2878 if (isZeroBitFIRRTLType(op.getType()))
2879 return setLowering(op, Value());
2880
2881 auto input = getPossiblyInoutLoweredValue(op.getInput());
2882 if (!input)
2883 return op.emitError() << "input lowering failed";
2884
2885 auto result = lowerSubfield(op, input);
2886 if (failed(result))
2887 return failure();
2888 return setLowering(op, *result);
2889}
2890
2891LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
2892 auto resultType = lowerType(op.getResult().getType());
2893 SmallVector<Value> operands;
2894 // NOTE: The operand order must be inverted.
2895 for (auto oper : llvm::reverse(op.getOperands())) {
2896 auto val = getLoweredValue(oper);
2897 if (!val)
2898 return failure();
2899 operands.push_back(val);
2900 }
2901 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
2902}
2903
2904LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
2905 auto resultType = lowerType(op.getResult().getType());
2906 SmallVector<Value> operands;
2907 for (auto oper : op.getOperands()) {
2908 auto val = getLoweredValue(oper);
2909 if (!val)
2910 return failure();
2911 operands.push_back(val);
2912 }
2913 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
2914}
2915
2916LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
2917 // Zero width values must be lowered to nothing.
2918 if (isZeroBitFIRRTLType(op.getType()))
2919 return setLowering(op, Value());
2920
2921 auto input = getLoweredValue(op.getInput());
2922 auto tagName = op.getFieldNameAttr();
2923 auto type = lowerType(op.getType());
2924
2925 if (auto structType = dyn_cast<hw::StructType>(type)) {
2926 auto enumType = structType.getFieldType("tag");
2927 auto enumAttr = hw::EnumFieldAttr::get(op.getLoc(), tagName, enumType);
2928 auto enumOp = builder.create<hw::EnumConstantOp>(enumAttr);
2929 auto unionType = structType.getFieldType("body");
2930 auto unionOp = builder.create<hw::UnionCreateOp>(unionType, tagName, input);
2931 SmallVector<Value> operands = {enumOp.getResult(), unionOp.getResult()};
2932 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
2933 }
2934
2935 return setLoweringTo<hw::EnumConstantOp>(
2936 op, hw::EnumFieldAttr::get(op.getLoc(), tagName, type));
2937}
2938
2939LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
2940 auto resultType = lowerType(op.getResult().getType());
2941 auto attr =
2942 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
2943
2944 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
2945 cast<ArrayAttr>(attr));
2946}
2947
2948LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
2949 auto tagName = op.getFieldNameAttr();
2950 auto lhs = getLoweredValue(op.getInput());
2951 if (isa<hw::StructType>(lhs.getType()))
2952 lhs = builder.create<hw::StructExtractOp>(lhs, "tag");
2953 auto enumField = hw::EnumFieldAttr::get(op.getLoc(), tagName, lhs.getType());
2954 auto rhs = builder.create<hw::EnumConstantOp>(enumField);
2955 return setLoweringTo<hw::EnumCmpOp>(op, lhs, rhs);
2956}
2957
2958LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
2959 // Zero width values must be lowered to nothing.
2960 if (isZeroBitFIRRTLType(op.getType()))
2961 return setLowering(op, Value());
2962
2963 auto tagName = op.getFieldNameAttr();
2964 auto input = getLoweredValue(op.getInput());
2965 auto field = builder.create<hw::StructExtractOp>(input, "body");
2966 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
2967}
2968
2969//===----------------------------------------------------------------------===//
2970// Declarations
2971//===----------------------------------------------------------------------===//
2972
2973LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
2974 auto origResultType = op.getResult().getType();
2975
2976 // Foreign types lower to a backedge that needs to be resolved by a later
2977 // connect op.
2978 if (!type_isa<FIRRTLType>(origResultType)) {
2979 createBackedge(op.getResult(), origResultType);
2980 return success();
2981 }
2982
2983 auto resultType = lowerType(origResultType);
2984 if (!resultType)
2985 return failure();
2986
2987 if (resultType.isInteger(0)) {
2988 if (op.getInnerSym())
2989 return op.emitError("zero width wire is referenced by name [")
2990 << *op.getInnerSym() << "] (e.g. in an XMR) but must be removed";
2991 return setLowering(op.getResult(), Value());
2992 }
2993
2994 // Name attr is required on sv.wire but optional on firrtl.wire.
2995 auto innerSym = lowerInnerSymbol(op);
2996 auto name = op.getNameAttr();
2997 // This is not a temporary wire created by the compiler, so attach a symbol
2998 // name.
2999 auto wire = builder.create<hw::WireOp>(
3000 op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3001
3002 if (auto svAttrs = sv::getSVAttributes(op))
3003 sv::setSVAttributes(wire, svAttrs);
3004
3005 return setLowering(op.getResult(), wire);
3006}
3007
3008LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3009 auto resultTy = lowerType(op.getType());
3010 if (!resultTy)
3011 return failure();
3012 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3013
3014 SmallVector<Value, 4> operands;
3015 operands.reserve(op.getSubstitutions().size());
3016 for (auto operand : op.getSubstitutions()) {
3017 auto lowered = getLoweredValue(operand);
3018 if (!lowered)
3019 return failure();
3020 operands.push_back(lowered);
3021 }
3022
3023 ArrayAttr symbols = op.getSymbolsAttr();
3024 if (!symbols)
3025 symbols = ArrayAttr::get(op.getContext(), {});
3026
3027 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3028 operands, symbols);
3029}
3030
3031LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3032 auto operand = getLoweredValue(op.getInput());
3033 if (!operand)
3034 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3035 if (op.getInnerSym())
3036 return op.emitError("zero width node is referenced by name [")
3037 << *op.getInnerSym()
3038 << "] (e.g. in an XMR) but must be "
3039 "removed";
3040 return setLowering(op.getResult(), Value());
3041 });
3042
3043 // Node operations are logical noops, but may carry annotations or be
3044 // referred to through an inner name. If a don't touch is present, ensure
3045 // that we have a symbol name so we can keep the node as a wire.
3046 auto name = op.getNameAttr();
3047 auto innerSym = lowerInnerSymbol(op);
3048
3049 if (innerSym)
3050 operand = builder.create<hw::WireOp>(operand, name, innerSym);
3051
3052 // Move SV attributes.
3053 if (auto svAttrs = sv::getSVAttributes(op)) {
3054 if (!innerSym)
3055 operand = builder.create<hw::WireOp>(operand, name);
3056 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3057 }
3058
3059 return setLowering(op.getResult(), operand);
3060}
3061
3062LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3063 auto resultType = lowerType(op.getResult().getType());
3064 if (!resultType)
3065 return failure();
3066 if (resultType.isInteger(0))
3067 return setLowering(op.getResult(), Value());
3068
3069 Value clockVal = getLoweredValue(op.getClockVal());
3070 if (!clockVal)
3071 return failure();
3072
3073 // Create a reg op, wiring itself to its input.
3074 auto innerSym = lowerInnerSymbol(op);
3075 Backedge inputEdge = backedgeBuilder.get(resultType);
3076 auto reg = builder.create<seq::FirRegOp>(inputEdge, clockVal,
3077 op.getNameAttr(), innerSym);
3078
3079 // Pass along the start and end random initialization bits for this register.
3080 if (auto randomRegister = op->getAttr("firrtl.random_init_register"))
3081 reg->setAttr("firrtl.random_init_register", randomRegister);
3082 if (auto randomStart = op->getAttr("firrtl.random_init_start"))
3083 reg->setAttr("firrtl.random_init_start", randomStart);
3084 if (auto randomEnd = op->getAttr("firrtl.random_init_end"))
3085 reg->setAttr("firrtl.random_init_end", randomEnd);
3086
3087 // Move SV attributes.
3088 if (auto svAttrs = sv::getSVAttributes(op))
3089 sv::setSVAttributes(reg, svAttrs);
3090
3091 inputEdge.setValue(reg);
3092 (void)setLowering(op.getResult(), reg);
3093 return success();
3094}
3095
3096LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3097 auto resultType = lowerType(op.getResult().getType());
3098 if (!resultType)
3099 return failure();
3100 if (resultType.isInteger(0))
3101 return setLowering(op.getResult(), Value());
3102
3103 Value clockVal = getLoweredValue(op.getClockVal());
3104 Value resetSignal = getLoweredValue(op.getResetSignal());
3105 // Reset values may be narrower than the register. Extend appropriately.
3106 Value resetValue = getLoweredAndExtOrTruncValue(
3107 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3108
3109 if (!clockVal || !resetSignal || !resetValue)
3110 return failure();
3111
3112 // Create a reg op, wiring itself to its input.
3113 auto innerSym = lowerInnerSymbol(op);
3114 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3115 Backedge inputEdge = backedgeBuilder.get(resultType);
3116 auto reg =
3117 builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
3118 resetSignal, resetValue, innerSym, isAsync);
3119
3120 // Pass along the start and end random initialization bits for this register.
3121 if (auto randomRegister = op->getAttr("firrtl.random_init_register"))
3122 reg->setAttr("firrtl.random_init_register", randomRegister);
3123 if (auto randomStart = op->getAttr("firrtl.random_init_start"))
3124 reg->setAttr("firrtl.random_init_start", randomStart);
3125 if (auto randomEnd = op->getAttr("firrtl.random_init_end"))
3126 reg->setAttr("firrtl.random_init_end", randomEnd);
3127
3128 // Move SV attributes.
3129 if (auto svAttrs = sv::getSVAttributes(op))
3130 sv::setSVAttributes(reg, svAttrs);
3131
3132 inputEdge.setValue(reg);
3133 (void)setLowering(op.getResult(), reg);
3134
3135 return success();
3136}
3137
3138LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3139 // TODO: Remove this restriction and preserve aggregates in
3140 // memories.
3141 if (type_isa<BundleType>(op.getDataType()))
3142 return op.emitOpError(
3143 "should have already been lowered from a ground type to an aggregate "
3144 "type using the LowerTypes pass. Use "
3145 "'firtool --lower-types' or 'circt-opt "
3146 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3147 "to run this.");
3148
3149 FirMemory memSummary = op.getSummary();
3150
3151 // Create the memory declaration.
3152 auto memType = seq::FirMemType::get(
3153 op.getContext(), memSummary.depth, memSummary.dataWidth,
3154 memSummary.isMasked ? std::optional<uint32_t>(memSummary.maskBits)
3155 : std::optional<uint32_t>());
3156
3157 seq::FirMemInitAttr memInit;
3158 if (auto init = op.getInitAttr())
3159 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3160 init.getIsBinary(), init.getIsInline());
3161
3162 auto memDecl = builder.create<seq::FirMemOp>(
3163 memType, memSummary.readLatency, memSummary.writeLatency,
3164 memSummary.readUnderWrite, memSummary.writeUnderWrite, op.getNameAttr(),
3165 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3166
3167 // If the module is outside the DUT, set the appropriate output directory for
3168 // the memory.
3169 if (!circuitState.isInDUT(theModule))
3170 if (auto testBenchDir = circuitState.getTestBenchDirectory())
3171 memDecl.setOutputFileAttr(testBenchDir);
3172
3173 // Memories return multiple structs, one for each port, which means we
3174 // have two layers of type to split apart.
3175 for (size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3176
3177 auto addOutput = [&](StringRef field, size_t width, Value value) {
3178 for (auto &a : getAllFieldAccesses(op.getResult(i), field)) {
3179 if (width > 0)
3180 (void)setLowering(a, value);
3181 else
3182 a->eraseOperand(0);
3183 }
3184 };
3185
3186 auto addInput = [&](StringRef field, Value backedge) {
3187 for (auto a : getAllFieldAccesses(op.getResult(i), field)) {
3188 if (cast<FIRRTLBaseType>(a.getType())
3189 .getPassiveType()
3190 .getBitWidthOrSentinel() > 0)
3191 (void)setLowering(a, backedge);
3192 else
3193 a->eraseOperand(0);
3194 }
3195 };
3196
3197 auto addInputPort = [&](StringRef field, size_t width) -> Value {
3198 // If the memory is 0-width, do not materialize any connections to it.
3199 // However, `seq.firmem` now requires a 1-bit input, so materialize
3200 // a dummy x value to provide it with.
3201 Value backedge, portValue;
3202 if (width == 0) {
3203 portValue = getOrCreateXConstant(1);
3204 } else {
3205 auto portType = IntegerType::get(op.getContext(), width);
3206 backedge = portValue = createBackedge(builder.getLoc(), portType);
3207 }
3208 addInput(field, backedge);
3209 return portValue;
3210 };
3211
3212 auto addClock = [&](StringRef field) -> Value {
3213 Type clockTy = seq::ClockType::get(op.getContext());
3214 Value portValue = createBackedge(builder.getLoc(), clockTy);
3215 addInput(field, portValue);
3216 return portValue;
3217 };
3218
3219 auto memportKind = op.getPortKind(i);
3220 if (memportKind == MemOp::PortKind::Read) {
3221 auto addr = addInputPort("addr", op.getAddrBits());
3222 auto en = addInputPort("en", 1);
3223 auto clk = addClock("clk");
3224 auto data = builder.create<seq::FirMemReadOp>(memDecl, addr, clk, en);
3225 addOutput("data", memSummary.dataWidth, data);
3226 } else if (memportKind == MemOp::PortKind::ReadWrite) {
3227 auto addr = addInputPort("addr", op.getAddrBits());
3228 auto en = addInputPort("en", 1);
3229 auto clk = addClock("clk");
3230 // If maskBits =1, then And the mask field with enable, and update the
3231 // enable. Else keep mask port.
3232 auto mode = addInputPort("wmode", 1);
3233 if (!memSummary.isMasked)
3234 mode = builder.createOrFold<comb::AndOp>(mode, addInputPort("wmask", 1),
3235 true);
3236 auto wdata = addInputPort("wdata", memSummary.dataWidth);
3237 // Ignore mask port, if maskBits =1
3238 Value mask;
3239 if (memSummary.isMasked)
3240 mask = addInputPort("wmask", memSummary.maskBits);
3241 auto rdata = builder.create<seq::FirMemReadWriteOp>(
3242 memDecl, addr, clk, en, wdata, mode, mask);
3243 addOutput("rdata", memSummary.dataWidth, rdata);
3244 } else {
3245 auto addr = addInputPort("addr", op.getAddrBits());
3246 // If maskBits =1, then And the mask field with enable, and update the
3247 // enable. Else keep mask port.
3248 auto en = addInputPort("en", 1);
3249 if (!memSummary.isMasked)
3250 en = builder.createOrFold<comb::AndOp>(en, addInputPort("mask", 1),
3251 true);
3252 auto clk = addClock("clk");
3253 auto data = addInputPort("data", memSummary.dataWidth);
3254 // Ignore mask port, if maskBits =1
3255 Value mask;
3256 if (memSummary.isMasked)
3257 mask = addInputPort("mask", memSummary.maskBits);
3258 builder.create<seq::FirMemWriteOp>(memDecl, addr, clk, en, data, mask);
3259 }
3260 }
3261
3262 return success();
3263}
3264
3265LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3266 Operation *oldModule =
3267 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3268
3269 auto *newModule = circuitState.getNewModule(oldModule);
3270 if (!newModule) {
3271 oldInstance->emitOpError("could not find module [")
3272 << oldInstance.getModuleName() << "] referenced by instance";
3273 return failure();
3274 }
3275
3276 // If this is a referenced to a parameterized extmodule, then bring the
3277 // parameters over to this instance.
3278 ArrayAttr parameters;
3279 if (auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3280 parameters = getHWParameters(oldExtModule, /*ignoreValues=*/false);
3281
3282 // Decode information about the input and output ports on the referenced
3283 // module.
3284 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3285
3286 // Build an index from the name attribute to an index into portInfo, so we
3287 // can do efficient lookups.
3289 for (unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3290 portIndicesByName[portInfo[portIdx].name] = portIdx;
3291
3292 // Ok, get ready to create the new instance operation. We need to prepare
3293 // input operands.
3294 SmallVector<Value, 8> operands;
3295 for (size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3296 auto &port = portInfo[portIndex];
3297 auto portType = lowerType(port.type);
3298 if (!portType) {
3299 oldInstance->emitOpError("could not lower type of port ") << port.name;
3300 return failure();
3301 }
3302
3303 // Drop zero bit input/inout ports.
3304 if (portType.isInteger(0))
3305 continue;
3306
3307 // We wire outputs up after creating the instance.
3308 if (port.isOutput())
3309 continue;
3310
3311 auto portResult = oldInstance.getResult(portIndex);
3312 assert(portResult && "invalid IR, couldn't find port");
3313
3314 // Replace the input port with a backedge. If it turns out that this port
3315 // is never driven, an uninitialized wire will be materialized at the end.
3316 if (port.isInput()) {
3317 operands.push_back(createBackedge(portResult, portType));
3318 continue;
3319 }
3320
3321 // If the result has an analog type and is used only by attach op, try
3322 // eliminating a temporary wire by directly using an attached value.
3323 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3324 if (auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3325 if (auto source = getSingleNonInstanceOperand(attach)) {
3326 auto loweredResult = getPossiblyInoutLoweredValue(source);
3327 operands.push_back(loweredResult);
3328 (void)setLowering(portResult, loweredResult);
3329 continue;
3330 }
3331 }
3332 }
3333
3334 // Create a wire for each inout operand, so there is something to connect
3335 // to. The instance becomes the sole driver of this wire.
3336 auto wire = builder.create<sv::WireOp>(
3337 portType, "." + port.getName().str() + ".wire");
3338
3339 // Know that the argument FIRRTL value is equal to this wire, allowing
3340 // connects to it to be lowered.
3341 (void)setLowering(portResult, wire);
3342
3343 operands.push_back(wire);
3344 }
3345
3346 // If this instance is destined to be lowered to a bind, generate a symbol
3347 // for it and generate a bind op. Enter the bind into global
3348 // CircuitLoweringState so that this can be moved outside of module once
3349 // we're guaranteed to not be a parallel context.
3350 auto innerSym = oldInstance.getInnerSymAttr();
3351 if (oldInstance.getLowerToBind()) {
3352 if (!innerSym)
3353 std::tie(innerSym, std::ignore) = getOrAddInnerSym(
3354 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3355 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
3356
3357 auto bindOp = builder.create<sv::BindOp>(theModule.getNameAttr(),
3358 innerSym.getSymName());
3359 // If the lowered op already had output file information, then use that.
3360 // Otherwise, generate some default bind information.
3361 if (auto outputFile = oldInstance->getAttr("output_file"))
3362 bindOp->setAttr("output_file", outputFile);
3363 // Add the bind to the circuit state. This will be moved outside of the
3364 // encapsulating module after all modules have been processed in parallel.
3365 circuitState.addBind(bindOp);
3366 }
3367
3368 // Create the new hw.instance operation.
3369 auto newInstance = builder.create<hw::InstanceOp>(
3370 newModule, oldInstance.getNameAttr(), operands, parameters, innerSym);
3371
3372 if (oldInstance.getLowerToBind())
3373 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3374
3375 if (newInstance.getInnerSymAttr())
3376 if (auto forceName = circuitState.instanceForceNames.lookup(
3377 {cast<hw::HWModuleOp>(newInstance->getParentOp()).getNameAttr(),
3378 newInstance.getInnerNameAttr()}))
3379 newInstance->setAttr("hw.verilogName", forceName);
3380
3381 // Now that we have the new hw.instance, we need to remap all of the users
3382 // of the outputs/results to the values returned by the instance.
3383 unsigned resultNo = 0;
3384 for (size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3385 auto &port = portInfo[portIndex];
3386 if (!port.isOutput() || isZeroBitFIRRTLType(port.type))
3387 continue;
3388
3389 Value resultVal = newInstance.getResult(resultNo);
3390
3391 auto oldPortResult = oldInstance.getResult(portIndex);
3392 (void)setLowering(oldPortResult, resultVal);
3393 ++resultNo;
3394 }
3395 return success();
3396}
3397
3398//===----------------------------------------------------------------------===//
3399// Unary Operations
3400//===----------------------------------------------------------------------===//
3401
3402// Lower a cast that is a noop at the HW level.
3403LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3404 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3405 if (!operand)
3406 return failure();
3407
3408 // Noop cast.
3409 return setLowering(op->getResult(0), operand);
3410}
3411
3412LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3413 if (isa<ClockType>(op.getInput().getType()))
3414 return setLowering(op->getResult(0),
3415 getLoweredNonClockValue(op.getInput()));
3416 return lowerNoopCast(op);
3417}
3418
3419LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3420 if (isa<ClockType>(op.getInput().getType()))
3421 return setLowering(op->getResult(0),
3422 getLoweredNonClockValue(op.getInput()));
3423 return lowerNoopCast(op);
3424}
3425
3426LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3427 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3428}
3429
3430LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
3431 mlir::UnrealizedConversionCastOp op) {
3432 // General lowering for non-unary casts.
3433 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3434 return failure();
3435
3436 auto operand = op.getOperand(0);
3437 auto result = op.getResult(0);
3438
3439 // FIRRTL -> FIRRTL
3440 if (type_isa<FIRRTLType>(operand.getType()) &&
3441 type_isa<FIRRTLType>(result.getType()))
3442 return lowerNoopCast(op);
3443
3444 // other -> FIRRTL
3445 // other -> other
3446 if (!type_isa<FIRRTLType>(operand.getType())) {
3447 if (type_isa<FIRRTLType>(result.getType()))
3448 return setLowering(result, getPossiblyInoutLoweredValue(operand));
3449 return failure(); // general foreign op lowering for other -> other
3450 }
3451
3452 // FIRRTL -> other
3453 // Otherwise must be a conversion from FIRRTL type to standard type.
3454 auto loweredResult = getLoweredValue(operand);
3455 if (!loweredResult) {
3456 // If this is a conversion from a zero bit HW type to firrtl value, then
3457 // we want to successfully lower this to a null Value.
3458 if (operand.getType().isSignlessInteger(0)) {
3459 return setLowering(result, Value());
3460 }
3461 return failure();
3462 }
3463
3464 // We lower builtin.unrealized_conversion_cast converting from a firrtl type
3465 // to a standard type into the lowered operand.
3466 result.replaceAllUsesWith(loweredResult);
3467 return success();
3468}
3469
3470LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3471 // Conversions from hw struct types to FIRRTL types are lowered as the
3472 // input operand.
3473 if (auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
3474 return setLowering(op, op.getOperand());
3475
3476 // Otherwise must be a conversion from FIRRTL bundle type to hw struct
3477 // type.
3478 auto result = getLoweredValue(op.getOperand());
3479 if (!result)
3480 return failure();
3481
3482 // We lower firrtl.stdStructCast converting from a firrtl bundle to an hw
3483 // struct type into the lowered operand.
3484 op.replaceAllUsesWith(result);
3485 return success();
3486}
3487
3488LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3489 auto operand = getLoweredValue(op.getOperand());
3490 if (!operand)
3491 return failure();
3492 auto resultType = lowerType(op.getType());
3493 if (!resultType)
3494 return failure();
3495
3496 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3497}
3498
3499LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3500 auto operand = getLoweredValue(op.getOperand());
3501 if (!operand) {
3502 return handleZeroBit(op.getOperand(), [&]() {
3503 // Unsigned zero bit to Signed is 1b0.
3504 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3505 return setLowering(op, getOrCreateIntConstant(1, 0));
3506 // Signed->Signed is a zero bit value.
3507 return setLowering(op, Value());
3508 });
3509 }
3510
3511 // Signed to signed is a noop.
3512 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3513 return setLowering(op, operand);
3514
3515 // Otherwise prepend a zero bit.
3516 auto zero = getOrCreateIntConstant(1, 0);
3517 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3518}
3519
3520LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3521 auto operand = getLoweredValue(op.getInput());
3522 if (!operand)
3523 return failure();
3524 // ~x ---> x ^ 0xFF
3525 auto allOnes = getOrCreateIntConstant(
3526 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3527 return setLoweringTo<comb::XorOp>(op, operand, allOnes, true);
3528}
3529
3530LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3531 // FIRRTL negate always adds a bit.
3532 // -x ---> 0-sext(x) or 0-zext(x)
3533 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3534 if (!operand)
3535 return failure();
3536
3537 auto resultType = lowerType(op.getType());
3538
3539 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3540 return setLoweringTo<comb::SubOp>(op, zero, operand, true);
3541}
3542
3543// Pad is a noop or extension operation.
3544LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3545 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3546 if (!operand)
3547 return failure();
3548 return setLowering(op, operand);
3549}
3550
3551LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3552 auto operand = getLoweredValue(op.getInput());
3553 if (!operand) {
3554 return handleZeroBit(op.getInput(), [&]() {
3555 return setLowering(op, getOrCreateIntConstant(1, 0));
3556 });
3557 return failure();
3558 }
3559
3560 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
3561 true);
3562}
3563
3564LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3565 auto operand = getLoweredValue(op.getInput());
3566 if (!operand) {
3567 return handleZeroBit(op.getInput(), [&]() {
3568 return setLowering(op, getOrCreateIntConstant(1, 1));
3569 });
3570 }
3571
3572 // Lower AndR to == -1
3573 return setLoweringTo<comb::ICmpOp>(
3574 op, ICmpPredicate::eq, operand,
3575 getOrCreateIntConstant(
3576 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3577 true);
3578}
3579
3580LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3581 auto operand = getLoweredValue(op.getInput());
3582 if (!operand) {
3583 return handleZeroBit(op.getInput(), [&]() {
3584 return setLowering(op, getOrCreateIntConstant(1, 0));
3585 });
3586 return failure();
3587 }
3588
3589 // Lower OrR to != 0
3590 return setLoweringTo<comb::ICmpOp>(
3591 op, ICmpPredicate::ne, operand,
3592 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3593 true);
3594}
3595
3596//===----------------------------------------------------------------------===//
3597// Binary Operations
3598//===----------------------------------------------------------------------===//
3599
3600template <typename ResultOpType>
3601LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3602 auto resultType = op->getResult(0).getType();
3603 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3604 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3605 if (!lhs || !rhs)
3606 return failure();
3607
3608 return setLoweringTo<ResultOpType>(op, lhs, rhs, true);
3609}
3610
3611/// Element-wise logical operations can be lowered into bitcast and normal comb
3612/// operations. Eventually we might want to introduce elementwise operations
3613/// into HW/SV level as well.
3614template <typename ResultOpType>
3615LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3616 auto resultType = op->getResult(0).getType();
3617 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3618 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3619
3620 if (!lhs || !rhs)
3621 return failure();
3622 auto bitwidth = firrtl::getBitWidth(type_cast<FIRRTLBaseType>(resultType));
3623
3624 if (!bitwidth)
3625 return failure();
3626
3627 // TODO: Introduce elementwise operations to HW dialect instead of abusing
3628 // bitcast operations.
3629 auto intType = builder.getIntegerType(*bitwidth);
3630 auto retType = lhs.getType();
3631 lhs = builder.createOrFold<hw::BitcastOp>(intType, lhs);
3632 rhs = builder.createOrFold<hw::BitcastOp>(intType, rhs);
3633 auto result = builder.createOrFold<ResultOpType>(lhs, rhs, /*twoState=*/true);
3634 return setLoweringTo<hw::BitcastOp>(op, retType, result);
3635}
3636
3637/// lowerBinOp extends each operand to the destination type, then performs the
3638/// specified binary operator.
3639template <typename ResultUnsignedOpType, typename ResultSignedOpType>
3640LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
3641 // Extend the two operands to match the destination type.
3642 auto resultType = op->getResult(0).getType();
3643 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3644 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3645 if (!lhs || !rhs)
3646 return failure();
3647
3648 // Emit the result operation.
3649 if (type_cast<IntType>(resultType).isSigned())
3650 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs, true);
3651 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs, true);
3652}
3653
3654/// lowerCmpOp extends each operand to the longest type, then performs the
3655/// specified binary operator.
3656LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
3657 ICmpPredicate unsignedOp) {
3658 // Extend the two operands to match the longest type.
3659 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
3660 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
3661 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
3662 return failure();
3663
3664 auto cmpType = getWidestIntType(lhsIntType, rhsIntType);
3665 if (cmpType.getWidth() == 0) // Handle 0-width inputs by promoting to 1 bit.
3666 cmpType = UIntType::get(builder.getContext(), 1);
3667 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
3668 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
3669 if (!lhs || !rhs)
3670 return failure();
3671
3672 // Emit the result operation.
3673 Type resultType = builder.getIntegerType(1);
3674 return setLoweringTo<comb::ICmpOp>(
3675 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
3676 true);
3677}
3678
3679/// Lower a divide or dynamic shift, where the operation has to be performed
3680/// in the widest type of the result and two inputs then truncated down.
3681template <typename SignedOp, typename UnsignedOp>
3682LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
3683 // hw has equal types for these, firrtl doesn't. The type of the firrtl
3684 // RHS may be wider than the LHS, and we cannot truncate off the high bits
3685 // (because an overlarge amount is supposed to shift in sign or zero bits).
3686 auto opType = type_cast<IntType>(op->getResult(0).getType());
3687 if (opType.getWidth() == 0)
3688 return setLowering(op->getResult(0), Value());
3689
3690 auto resultType = getWidestIntType(opType, op->getOperand(1).getType());
3691 resultType = getWidestIntType(resultType, op->getOperand(0).getType());
3692 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3693 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3694 if (!lhs || !rhs)
3695 return failure();
3696
3697 Value result;
3698 if (opType.isSigned())
3699 result = builder.createOrFold<SignedOp>(lhs, rhs, true);
3700 else
3701 result = builder.createOrFold<UnsignedOp>(lhs, rhs, true);
3702
3703 if (auto *definingOp = result.getDefiningOp())
3704 tryCopyName(definingOp, op);
3705
3706 if (resultType == opType)
3707 return setLowering(op->getResult(0), result);
3708 return setLoweringTo<comb::ExtractOp>(op, lowerType(opType), result, 0);
3709}
3710
3711LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
3712 auto lhs = getLoweredValue(op.getLhs());
3713 auto rhs = getLoweredValue(op.getRhs());
3714 if (!lhs) {
3715 return handleZeroBit(op.getLhs(), [&]() {
3716 if (rhs) // cat(0bit, x) --> x
3717 return setLowering(op, rhs);
3718 // cat(0bit, 0bit) --> 0bit
3719 return handleZeroBit(op.getRhs(),
3720 [&]() { return setLowering(op, Value()); });
3721 });
3722 }
3723
3724 if (!rhs) // cat(x, 0bit) --> x
3725 return handleZeroBit(op.getRhs(), [&]() { return setLowering(op, lhs); });
3726
3727 return setLoweringTo<comb::ConcatOp>(op, lhs, rhs);
3728}
3729
3730//===----------------------------------------------------------------------===//
3731// Verif Operations
3732//===----------------------------------------------------------------------===//
3733
3734LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
3735 auto input = getLoweredNonClockValue(op.getArg());
3736 if (!input)
3737 return failure();
3738
3739 if (!isa<IntType>(input.getType())) {
3740 auto srcType = op.getArg().getType();
3741 auto bitwidth = firrtl::getBitWidth(type_cast<FIRRTLBaseType>(srcType));
3742 assert(bitwidth && "Unknown width");
3743 auto intType = builder.getIntegerType(*bitwidth);
3744 input = builder.createOrFold<hw::BitcastOp>(intType, input);
3745 }
3746
3747 return setLoweringTo<comb::ICmpOp>(
3748 op, ICmpPredicate::ceq, input,
3749 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()), true);
3750}
3751
3752LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
3753 auto operand = getLoweredValue(op.getInput());
3754 builder.create<hw::WireOp>(operand);
3755 return success();
3756}
3757
3758LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
3759 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
3760 op.getFormatStringAttr());
3761}
3762
3763LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
3764 auto type = lowerType(op.getResult().getType());
3765 if (!type)
3766 return failure();
3767
3768 auto valueOp = builder.create<sim::PlusArgsValueOp>(
3769 builder.getIntegerType(1), type, op.getFormatStringAttr());
3770 if (failed(setLowering(op.getResult(), valueOp.getResult())))
3771 return failure();
3772 if (failed(setLowering(op.getFound(), valueOp.getFound())))
3773 return failure();
3774 return success();
3775}
3776
3777LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
3778 op.emitError("SizeOf should have been resolved.");
3779 return failure();
3780}
3781
3782LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
3783 Value testEnable;
3784 if (op.getTestEnable())
3785 testEnable = getLoweredValue(op.getTestEnable());
3786 return setLoweringTo<seq::ClockGateOp>(
3787 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
3788 testEnable, /*inner_sym=*/hw::InnerSymAttr{});
3789}
3790
3791LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
3792 auto operand = getLoweredValue(op.getInput());
3793 return setLoweringTo<seq::ClockInverterOp>(op, operand);
3794}
3795
3796LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
3797 auto operand = getLoweredValue(op.getInput());
3798 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
3799}
3800
3801LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
3802 return setLoweringToLTL<ltl::AndOp>(
3803 op,
3804 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3805}
3806
3807LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
3808 return setLoweringToLTL<ltl::OrOp>(
3809 op,
3810 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3811}
3812
3813LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
3814 return setLoweringToLTL<ltl::IntersectOp>(
3815 op,
3816 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3817}
3818
3819LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
3820 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
3821 op.getDelayAttr(), op.getLengthAttr());
3822}
3823
3824LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
3825 return setLoweringToLTL<ltl::ConcatOp>(
3826 op,
3827 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3828}
3829
3830LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
3831 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
3832 op.getBaseAttr(), op.getMoreAttr());
3833}
3834
3835LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
3836 return setLoweringToLTL<ltl::GoToRepeatOp>(
3837 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
3838}
3839
3840LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
3841 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
3842 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
3843}
3844
3845LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
3846 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
3847}
3848
3849LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
3850 return setLoweringToLTL<ltl::ImplicationOp>(
3851 op,
3852 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3853}
3854
3855LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
3856 return setLoweringToLTL<ltl::UntilOp>(
3857 op,
3858 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3859}
3860
3861LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
3862 return setLoweringToLTL<ltl::EventuallyOp>(op,
3863 getLoweredValue(op.getInput()));
3864}
3865
3866LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
3867 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
3868 ltl::ClockEdge::Pos,
3869 getLoweredNonClockValue(op.getClock()));
3870}
3871
3872template <typename TargetOp, typename IntrinsicOp>
3873LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
3874 auto property = getLoweredValue(op.getProperty());
3875 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
3876 builder.create<TargetOp>(property, enable, op.getLabelAttr());
3877 return success();
3878}
3879
3880LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
3881 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
3882}
3883
3884LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
3885 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
3886}
3887
3888LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
3889 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
3890}
3891
3892LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
3893 auto clock = getLoweredNonClockValue(op.getClock());
3894 auto reset = getLoweredValue(op.getReset());
3895 if (!clock || !reset)
3896 return failure();
3897 auto resetType = op.getReset().getType();
3898 auto uintResetType = dyn_cast<UIntType>(resetType);
3899 auto isSync = uintResetType && uintResetType.getWidth() == 1;
3900 auto isAsync = isa<AsyncResetType>(resetType);
3901 if (!isAsync && !isSync) {
3902 auto d = op.emitError("uninferred reset passed to 'has_been_reset'; "
3903 "requires sync or async reset");
3904 d.attachNote() << "reset is of type " << resetType
3905 << ", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
3906 return failure();
3907 }
3908 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
3909}
3910
3911//===----------------------------------------------------------------------===//
3912// Other Operations
3913//===----------------------------------------------------------------------===//
3914
3915LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
3916 auto input = getLoweredValue(op.getInput());
3917 if (!input)
3918 return failure();
3919
3920 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
3921 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
3922}
3923
3924LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
3925 auto resultTy = lowerType(op.getType());
3926 if (!resultTy)
3927 return failure();
3928
3929 // Values of analog type always need to be lowered to something with inout
3930 // type. We do that by lowering to a wire and return that. As with the
3931 // SFC, we do not connect anything to this, because it is bidirectional.
3932 if (type_isa<AnalogType>(op.getType()))
3933 // This is a locally visible, private wire created by the compiler, so do
3934 // not attach a symbol name.
3935 return setLoweringTo<sv::WireOp>(op, resultTy, ".invalid_analog");
3936
3937 // We don't allow aggregate values which contain values of analog types.
3938 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
3939 return failure();
3940
3941 // We lower invalid to 0. TODO: the FIRRTL spec mentions something about
3942 // lowering it to a random value, we should see if this is what we need to
3943 // do.
3944 if (auto bitwidth =
3945 firrtl::getBitWidth(type_cast<FIRRTLBaseType>(op.getType()))) {
3946 if (*bitwidth == 0) // Let the caller handle zero width values.
3947 return failure();
3948
3949 auto constant = getOrCreateIntConstant(*bitwidth, 0);
3950 // If the result is an aggregate value, we have to bitcast the constant.
3951 if (!type_isa<IntegerType>(resultTy))
3952 constant = builder.create<hw::BitcastOp>(resultTy, constant);
3953 return setLowering(op, constant);
3954 }
3955
3956 // Invalid for bundles isn't supported.
3957 op.emitOpError("unsupported type");
3958 return failure();
3959}
3960
3961LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
3962 auto input = getLoweredValue(op.getInput());
3963 if (!input)
3964 return failure();
3965 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3966 if (op.getAmount() == 0)
3967 return setLowering(op, Value());
3968 Type resultType = builder.getIntegerType(op.getAmount());
3969 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
3970 inWidth - op.getAmount());
3971}
3972
3973LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
3974 auto input = getLoweredValue(op.getInput());
3975 if (!input) {
3976 return handleZeroBit(op.getInput(), [&]() {
3977 if (op.getAmount() == 0)
3978 return failure();
3979 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
3980 });
3981 }
3982
3983 // Handle the degenerate case.
3984 if (op.getAmount() == 0)
3985 return setLowering(op, input);
3986
3987 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
3988 return setLoweringTo<comb::ConcatOp>(op, input, zero);
3989}
3990
3991LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
3992 auto input = getLoweredValue(op.getInput());
3993 if (!input)
3994 return failure();
3995
3996 // Handle the special degenerate cases.
3997 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3998 auto shiftAmount = op.getAmount();
3999 if (shiftAmount >= inWidth) {
4000 // Unsigned shift by full width returns a single-bit zero.
4001 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4002 return setLowering(op, {});
4003
4004 // Signed shift by full width is equivalent to extracting the sign bit.
4005 shiftAmount = inWidth - 1;
4006 }
4007
4008 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4009 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4010}
4011
4012LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4013 auto input = getLoweredValue(op.getInput());
4014 if (!input)
4015 return failure();
4016
4017 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4018 if (inWidth == op.getAmount())
4019 return setLowering(op, Value());
4020 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4021 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4022}
4023
4024LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4025 auto cond = getLoweredValue(op.getSel());
4026 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4027 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4028 if (!cond || !ifTrue || !ifFalse)
4029 return failure();
4030
4031 if (isa<ClockType>(op.getType()))
4032 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4033 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4034 true);
4035}
4036
4037LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4038 auto cond = getLoweredValue(op.getSel());
4039 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4040 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4041 if (!cond || !ifTrue || !ifFalse)
4042 return failure();
4043
4044 auto val = builder.create<comb::MuxOp>(ifTrue.getType(), cond, ifTrue,
4045 ifFalse, true);
4046 return setLowering(op, createValueWithMuxAnnotation(val, true));
4047}
4048
4049LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4050 auto sel = getLoweredValue(op.getSel());
4051 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4052 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4053 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4054 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4055 if (!sel || !v3 || !v2 || !v1 || !v0)
4056 return failure();
4057 Value array[] = {v3, v2, v1, v0};
4058 auto create = builder.create<hw::ArrayCreateOp>(array);
4059 auto val = builder.create<hw::ArrayGetOp>(create, sel);
4060 return setLowering(op, createValueWithMuxAnnotation(val, false));
4061}
4062
4063// Construct a value with vendor specific pragmas to utilize MUX cells.
4064// Specifically we annotate pragmas in the following form.
4065//
4066// For an array indexing:
4067// ```
4068// wire GEN;
4069// /* synopsys infer_mux_override */
4070// assign GEN = array[index] /* cadence map_to_mux */;
4071// ```
4072//
4073// For a mux:
4074// ```
4075// wire GEN;
4076// /* synopsys infer_mux_override */
4077// assign GEN = sel ? /* cadence map_to_mux */ high : low;
4078// ```
4079Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op, bool isMux2) {
4080 assert(op->getNumResults() == 1 && "only expect a single result");
4081 auto val = op->getResult(0);
4082 auto valWire = builder.create<sv::WireOp>(val.getType());
4083 // Use SV attributes to annotate pragmas.
4085 op, sv::SVAttributeAttr::get(builder.getContext(), "cadence map_to_mux",
4086 /*emitAsComment=*/true));
4087
4088 // For operands, create temporary wires with optimization blockers(inner
4089 // symbols) so that the AST structure will never be destoyed in the later
4090 // pipeline.
4091 {
4092 OpBuilder::InsertionGuard guard(builder);
4093 builder.setInsertionPoint(op);
4094 StringRef namehint = isMux2 ? "mux2cell_in" : "mux4cell_in";
4095 for (auto [idx, operand] : llvm::enumerate(op->getOperands())) {
4096 auto [innerSym, _] = getOrAddInnerSym(
4097 op->getContext(), /*attr=*/nullptr, 0,
4098 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
4099 auto wire =
4100 builder.create<hw::WireOp>(operand, namehint + Twine(idx), innerSym);
4101 op->setOperand(idx, wire);
4102 }
4103 }
4104
4105 auto assignOp = builder.create<sv::AssignOp>(valWire, val);
4106 sv::setSVAttributes(assignOp,
4107 sv::SVAttributeAttr::get(builder.getContext(),
4108 "synopsys infer_mux_override",
4109 /*emitAsComment=*/true));
4110 return builder.create<sv::ReadInOutOp>(valWire);
4111}
4112
4113Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4114
4115 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4116 // Extend to power of 2. FIRRTL semantics say out-of-bounds access result in
4117 // an indeterminate value. Existing chisel code depends on this behavior
4118 // being "return index 0". Ideally, we would tail extend the array to improve
4119 // optimization.
4120 if (!llvm::isPowerOf2_64(size)) {
4121 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4122 auto extValue = builder.create<hw::ArrayGetOp>(array, extElem);
4123 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4124 auto ext = builder.create<hw::ArrayCreateOp>(temp);
4125 Value temp2[] = {ext.getResult(), array};
4126 array = builder.create<hw::ArrayConcatOp>(temp2);
4127 }
4128
4129 Value inBoundsRead = builder.create<hw::ArrayGetOp>(array, index);
4130
4131 return inBoundsRead;
4132}
4133
4134LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4135 // Lower and resize to the index width.
4136 auto index = getLoweredAndExtOrTruncValue(
4137 op.getIndex(),
4138 UIntType::get(op.getContext(),
4139 getBitWidthFromVectorSize(op.getInputs().size())));
4140
4141 if (!index)
4142 return failure();
4143 SmallVector<Value> loweredInputs;
4144 loweredInputs.reserve(op.getInputs().size());
4145 for (auto input : op.getInputs()) {
4146 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4147 if (!lowered)
4148 return failure();
4149 loweredInputs.push_back(lowered);
4150 }
4151
4152 Value array = builder.create<hw::ArrayCreateOp>(loweredInputs);
4153 return setLowering(op, createArrayIndexing(array, index));
4154}
4155
4156LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4157 auto resultTy = lowerType(op.getType());
4158 if (!resultTy)
4159 return failure();
4160
4161 SmallVector<Value, 4> operands;
4162 operands.reserve(op.getSubstitutions().size());
4163 for (auto operand : op.getSubstitutions()) {
4164 auto lowered = getLoweredValue(operand);
4165 if (!lowered)
4166 return failure();
4167 operands.push_back(lowered);
4168 }
4169
4170 ArrayAttr symbols = op.getSymbolsAttr();
4171 if (!symbols)
4172 symbols = ArrayAttr::get(op.getContext(), {});
4173
4174 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4175 operands, symbols);
4176}
4177
4178LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4179 // This XMR is accessed solely by FIRRTL statements that mutate the probe.
4180 // To avoid the use of clock wires, create an `i1` wire and ensure that
4181 // all connections are also of the `i1` type.
4182 Type baseType = op.getType().getType();
4183
4184 Type xmrType;
4185 if (isa<ClockType>(baseType))
4186 xmrType = builder.getIntegerType(1);
4187 else
4188 xmrType = lowerType(baseType);
4189
4190 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4191 op.getRef(), op.getVerbatimSuffixAttr());
4192}
4193
4194LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4195 // When an XMR targets a clock wire, replace it with an `i1` wire, but
4196 // introduce a clock-typed read op into the design afterwards.
4197 Type xmrType;
4198 if (isa<ClockType>(op.getType()))
4199 xmrType = builder.getIntegerType(1);
4200 else
4201 xmrType = lowerType(op.getType());
4202
4203 auto xmr = builder.create<sv::XMRRefOp>(
4204 sv::InOutType::get(xmrType), op.getRef(), op.getVerbatimSuffixAttr());
4205 auto readXmr = getReadValue(xmr);
4206 if (!isa<ClockType>(op.getType()))
4207 return setLowering(op, readXmr);
4208 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4209}
4210
4211//===----------------------------------------------------------------------===//
4212// Statements
4213//===----------------------------------------------------------------------===//
4214
4215LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4216 // Nothing! We could emit an comment as a verbatim op if there were a
4217 // reason to.
4218 return success();
4219}
4220
4221/// Resolve a connection to `destVal`, an `hw::WireOp` or `seq::FirRegOp`, by
4222/// updating the input operand to be `srcVal`. Returns true if the update was
4223/// made and the connection can be considered lowered. Returns false if the
4224/// destination isn't a wire or register with an input operand to be updated.
4225/// Returns failure if the destination is a subaccess operation. These should be
4226/// transposed to the right-hand-side by a pre-pass.
4227FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4228 auto srcType = srcVal.getType();
4229 auto dstType = destVal.getType();
4230 if (srcType != dstType &&
4231 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4232 srcVal = builder.create<hw::BitcastOp>(destVal.getType(), srcVal);
4233 }
4234 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4235 .Case<hw::WireOp>([&](auto op) {
4236 maybeUnused(op.getInput());
4237 op.getInputMutable().assign(srcVal);
4238 return true;
4239 })
4240 .Case<seq::FirRegOp>([&](auto op) {
4241 maybeUnused(op.getNext());
4242 op.getNextMutable().assign(srcVal);
4243 return true;
4244 })
4245 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](auto op) {
4246 // NOTE: msvc thinks `return op.emitOpError(...);` is ambiguous. So
4247 // return `failure()` separately.
4248 op.emitOpError("used as connect destination");
4249 return failure();
4250 })
4251 .Default([](auto) { return false; });
4252}
4253
4254LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4255 auto dest = op.getDest();
4256 // The source can be a smaller integer, extend it as appropriate if so.
4257 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4258 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4259 if (!srcVal)
4260 return handleZeroBit(op.getSrc(), []() { return success(); });
4261
4262 auto destVal = getPossiblyInoutLoweredValue(dest);
4263 if (!destVal)
4264 return failure();
4265
4266 auto result = lowerConnect(destVal, srcVal);
4267 if (failed(result))
4268 return failure();
4269 if (*result)
4270 return success();
4271
4272 // If this connect is driving a value that is currently a backedge, record
4273 // that the source is the value of the backedge.
4274 if (updateIfBackedge(destVal, srcVal))
4275 return success();
4276
4277 if (!isa<hw::InOutType>(destVal.getType()))
4278 return op.emitError("destination isn't an inout type");
4279
4280 builder.create<sv::AssignOp>(destVal, srcVal);
4281 return success();
4282}
4283
4284LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
4285 auto dest = op.getDest();
4286 auto srcVal = getLoweredValue(op.getSrc());
4287 if (!srcVal)
4288 return handleZeroBit(op.getSrc(), []() { return success(); });
4289
4290 auto destVal = getPossiblyInoutLoweredValue(dest);
4291 if (!destVal)
4292 return failure();
4293
4294 auto result = lowerConnect(destVal, srcVal);
4295 if (failed(result))
4296 return failure();
4297 if (*result)
4298 return success();
4299
4300 // If this connect is driving a value that is currently a backedge, record
4301 // that the source is the value of the backedge.
4302 if (updateIfBackedge(destVal, srcVal))
4303 return success();
4304
4305 if (!isa<hw::InOutType>(destVal.getType()))
4306 return op.emitError("destination isn't an inout type");
4307
4308 builder.create<sv::AssignOp>(destVal, srcVal);
4309 return success();
4310}
4311
4312LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4313 auto srcVal = getLoweredValue(op.getSrc());
4314 if (!srcVal)
4315 return failure();
4316
4317 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4318 if (!destVal)
4319 return failure();
4320
4321 if (!isa<hw::InOutType>(destVal.getType()))
4322 return op.emitError("destination isn't an inout type");
4323
4324 // #ifndef SYNTHESIS
4325 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4326 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4327 addToInitialBlock([&]() { builder.create<sv::ForceOp>(destVal, srcVal); });
4328 });
4329 return success();
4330}
4331
4332LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4333 auto src = getLoweredNonClockValue(op.getSrc());
4334 auto clock = getLoweredNonClockValue(op.getClock());
4335 auto pred = getLoweredValue(op.getPredicate());
4336 if (!src || !clock || !pred)
4337 return failure();
4338
4339 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4340 if (!destVal)
4341 return failure();
4342
4343 // #ifndef SYNTHESIS
4344 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4345 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4346 addToAlwaysBlock(clock, [&]() {
4347 addIfProceduralBlock(
4348 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4349 });
4350 });
4351 return success();
4352}
4353LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4354 auto src = getLoweredNonClockValue(op.getSrc());
4355 auto pred = getLoweredValue(op.getPredicate());
4356 if (!src || !pred)
4357 return failure();
4358
4359 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4360 if (!destVal)
4361 return failure();
4362
4363 // #ifndef SYNTHESIS
4364 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4365 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4366 addToInitialBlock([&]() {
4367 addIfProceduralBlock(
4368 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4369 });
4370 });
4371 return success();
4372}
4373LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4374 auto clock = getLoweredNonClockValue(op.getClock());
4375 auto pred = getLoweredValue(op.getPredicate());
4376 if (!clock || !pred)
4377 return failure();
4378
4379 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4380 if (!destVal)
4381 return failure();
4382
4383 // #ifndef SYNTHESIS
4384 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4385 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4386 addToAlwaysBlock(clock, [&]() {
4387 addIfProceduralBlock(pred,
4388 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4389 });
4390 });
4391 return success();
4392}
4393LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
4394 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4395 auto pred = getLoweredValue(op.getPredicate());
4396 if (!destVal || !pred)
4397 return failure();
4398
4399 // #ifndef SYNTHESIS
4400 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4401 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4402 addToInitialBlock([&]() {
4403 addIfProceduralBlock(pred,
4404 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4405 });
4406 });
4407 return success();
4408}
4409
4410// Printf is a macro op that lowers to an sv.ifdef.procedural, an sv.if,
4411// and an sv.fwrite all nested together.
4412LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
4413 auto clock = getLoweredNonClockValue(op.getClock());
4414 auto cond = getLoweredValue(op.getCond());
4415 if (!clock || !cond)
4416 return failure();
4417
4418 SmallVector<Value, 4> operands;
4419 operands.reserve(op.getSubstitutions().size());
4420 for (auto operand : op.getSubstitutions()) {
4421 Value loweredValue = getLoweredFmtOperand(operand);
4422 if (!loweredValue)
4423 return failure();
4424 operands.push_back(loweredValue);
4425 }
4426
4427 // Emit an "#ifndef SYNTHESIS" guard into the always block.
4428 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4429 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4430 addToAlwaysBlock(clock, [&]() {
4431 circuitState.usedPrintf = true;
4432 circuitState.addFragment(theModule, "PRINTF_FD_FRAGMENT");
4433 circuitState.addFragment(theModule, "PRINTF_COND_FRAGMENT");
4434
4435 // Emit an "sv.if '`PRINTF_COND_ & cond' into the #ifndef.
4436 Value ifCond =
4437 builder.create<sv::MacroRefExprOp>(cond.getType(), "PRINTF_COND_");
4438 ifCond = builder.createOrFold<comb::AndOp>(ifCond, cond, true);
4439
4440 addIfProceduralBlock(ifCond, [&]() {
4441 // Emit the sv.fwrite, writing to fd specified by `PRINTF_FD.
4442 Value fd = builder.create<sv::MacroRefExprOp>(
4443 builder.getIntegerType(32), "PRINTF_FD_");
4444 builder.create<sv::FWriteOp>(fd, op.getFormatString(), operands);
4445 });
4446 });
4447 });
4448
4449 return success();
4450}
4451
4452// Stop lowers into a nested series of behavioral statements plus $fatal
4453// or $finish.
4454LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
4455 auto clock = getLoweredValue(op.getClock());
4456 auto cond = getLoweredValue(op.getCond());
4457 if (!clock || !cond)
4458 return failure();
4459
4460 circuitState.usedStopCond = true;
4461 circuitState.addFragment(theModule, "STOP_COND_FRAGMENT");
4462
4463 Value stopCond =
4464 builder.create<sv::MacroRefExprOp>(cond.getType(), "STOP_COND_");
4465 Value exitCond = builder.createOrFold<comb::AndOp>(stopCond, cond, true);
4466
4467 if (op.getExitCode())
4468 builder.create<sim::FatalOp>(clock, exitCond);
4469 else
4470 builder.create<sim::FinishOp>(clock, exitCond);
4471
4472 return success();
4473}
4474
4475/// Helper function to build an immediate assert operation based on the
4476/// original FIRRTL operation name. This reduces code duplication in
4477/// `lowerVerificationStatement`.
4478template <typename... Args>
4479static Operation *buildImmediateVerifOp(ImplicitLocOpBuilder &builder,
4480 StringRef opName, Args &&...args) {
4481 if (opName == "assert")
4482 return builder.create<sv::AssertOp>(std::forward<Args>(args)...);
4483 if (opName == "assume")
4484 return builder.create<sv::AssumeOp>(std::forward<Args>(args)...);
4485 if (opName == "cover")
4486 return builder.create<sv::CoverOp>(std::forward<Args>(args)...);
4487 llvm_unreachable("unknown verification op");
4488}
4489
4490/// Helper function to build a concurrent assert operation based on the
4491/// original FIRRTL operation name. This reduces code duplication in
4492/// `lowerVerificationStatement`.
4493template <typename... Args>
4494static Operation *buildConcurrentVerifOp(ImplicitLocOpBuilder &builder,
4495 StringRef opName, Args &&...args) {
4496 if (opName == "assert")
4497 return builder.create<sv::AssertConcurrentOp>(std::forward<Args>(args)...);
4498 if (opName == "assume")
4499 return builder.create<sv::AssumeConcurrentOp>(std::forward<Args>(args)...);
4500 if (opName == "cover")
4501 return builder.create<sv::CoverConcurrentOp>(std::forward<Args>(args)...);
4502 llvm_unreachable("unknown verification op");
4503}
4504
4505/// Template for lowering verification statements from type A to
4506/// type B.
4507///
4508/// For example, lowering the "foo" op to the "bar" op would start
4509/// with:
4510///
4511/// foo(clock, condition, enable, "message")
4512///
4513/// This becomes a Verilog clocking block with the "bar" op guarded
4514/// by an if enable:
4515///
4516/// always @(posedge clock) begin
4517/// if (enable) begin
4518/// bar(condition);
4519/// end
4520/// end
4521/// The above can also be reduced into a concurrent verification statement
4522/// sv.assert.concurrent posedge %clock (condition && enable)
4523LogicalResult FIRRTLLowering::lowerVerificationStatement(
4524 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
4525 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
4526 StringAttr opNameAttr, bool isConcurrent, EventControl opEventControl) {
4527 StringRef opName = op->getName().stripDialect();
4528
4529 // The attribute holding the compile guards
4530 ArrayRef<Attribute> guards{};
4531 if (auto guardsAttr = op->template getAttrOfType<ArrayAttr>("guards"))
4532 guards = guardsAttr.getValue();
4533
4534 auto isCover = isa<CoverOp>(op);
4535 auto clock = getLoweredNonClockValue(opClock);
4536 auto enable = getLoweredValue(opEnable);
4537 auto predicate = getLoweredValue(opPredicate);
4538 if (!clock || !enable || !predicate)
4539 return failure();
4540
4541 StringAttr label;
4542 if (opNameAttr && !opNameAttr.getValue().empty())
4543 label = opNameAttr;
4544 StringAttr prefixedLabel;
4545 if (label)
4546 prefixedLabel =
4547 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
4548
4549 StringAttr message;
4550 SmallVector<Value> messageOps;
4551 VerificationFlavor flavor = circuitState.verificationFlavor;
4552
4553 // For non-assertion, rollback to per-op configuration.
4554 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
4555 flavor = VerificationFlavor::None;
4556
4557 if (flavor == VerificationFlavor::None) {
4558 // TODO: This should *not* be part of the op, but rather a lowering
4559 // option that the user of this pass can choose.
4560
4561 auto format = op->getAttrOfType<StringAttr>("format");
4562 // if-else-fatal iff concurrent and the format is specified.
4563 if (isConcurrent && format && format.getValue() == "ifElseFatal") {
4564 if (!isa<AssertOp>(op))
4565 return op->emitError()
4566 << "ifElseFatal format cannot be used for non-assertions";
4567 flavor = VerificationFlavor::IfElseFatal;
4568 } else if (isConcurrent)
4569 flavor = VerificationFlavor::SVA;
4570 else
4571 flavor = VerificationFlavor::Immediate;
4572 }
4573
4574 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
4575 message = opMessageAttr;
4576 for (auto operand : opOperands) {
4577 auto loweredValue = getLoweredFmtOperand(operand);
4578 if (!loweredValue)
4579 return failure();
4580
4581 // For SVA assert/assume statements, wrap any message ops in $sampled() to
4582 // guarantee that these will print with the same value as when the
4583 // assertion triggers. (See SystemVerilog 2017 spec section 16.9.3 for
4584 // more information.)
4585 if (flavor == VerificationFlavor::SVA)
4586 loweredValue = builder.create<sv::SampledOp>(loweredValue);
4587 messageOps.push_back(loweredValue);
4588 }
4589 }
4590
4591 auto emit = [&]() {
4592 switch (flavor) {
4593 case VerificationFlavor::Immediate: {
4594 // Handle the purely procedural flavor of the operation.
4595 auto deferImmediate = circt::sv::DeferAssertAttr::get(
4596 builder.getContext(), circt::sv::DeferAssert::Immediate);
4597 addToAlwaysBlock(clock, [&]() {
4598 addIfProceduralBlock(enable, [&]() {
4599 buildImmediateVerifOp(builder, opName, predicate, deferImmediate,
4600 prefixedLabel, message, messageOps);
4601 });
4602 });
4603 return;
4604 }
4605 case VerificationFlavor::IfElseFatal: {
4606 assert(isa<AssertOp>(op) && "only assert is expected");
4607 // Handle the `ifElseFatal` format, which does not emit an SVA but
4608 // rather a process that uses $error and $fatal to perform the checks.
4609 auto boolType = IntegerType::get(builder.getContext(), 1);
4610 predicate = comb::createOrFoldNot(predicate, builder, /*twoState=*/true);
4611 predicate = builder.createOrFold<comb::AndOp>(enable, predicate, true);
4612
4613 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4614 addToIfDefBlock("SYNTHESIS", {}, [&]() {
4615 addToAlwaysBlock(clock, [&]() {
4616 addIfProceduralBlock(predicate, [&]() {
4617 circuitState.usedStopCond = true;
4618 circuitState.addFragment(theModule, "STOP_COND_FRAGMENT");
4619
4620 circuitState.usedAssertVerboseCond = true;
4621 circuitState.addFragment(theModule, "ASSERT_VERBOSE_COND_FRAGMENT");
4622
4623 addIfProceduralBlock(
4624 builder.create<sv::MacroRefExprOp>(boolType,
4625 "ASSERT_VERBOSE_COND_"),
4626 [&]() { builder.create<sv::ErrorOp>(message, messageOps); });
4627 addIfProceduralBlock(
4628 builder.create<sv::MacroRefExprOp>(boolType, "STOP_COND_"),
4629 [&]() { builder.create<sv::FatalOp>(); });
4630 });
4631 });
4632 });
4633 return;
4634 }
4635 case VerificationFlavor::SVA: {
4636 // Formulate the `enable -> predicate` as `!enable | predicate`.
4637 // Except for covers, combine them: enable & predicate
4638 if (!isCover) {
4639 auto notEnable =
4640 comb::createOrFoldNot(enable, builder, /*twoState=*/true);
4641 predicate =
4642 builder.createOrFold<comb::OrOp>(notEnable, predicate, true);
4643 } else {
4644 predicate = builder.createOrFold<comb::AndOp>(enable, predicate, true);
4645 }
4646
4647 // Handle the regular SVA case.
4648 sv::EventControl event;
4649 switch (opEventControl) {
4650 case EventControl::AtPosEdge:
4651 event = circt::sv::EventControl::AtPosEdge;
4652 break;
4653 case EventControl::AtEdge:
4654 event = circt::sv::EventControl::AtEdge;
4655 break;
4656 case EventControl::AtNegEdge:
4657 event = circt::sv::EventControl::AtNegEdge;
4658 break;
4659 }
4660
4662 builder, opName,
4663 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
4664 predicate, prefixedLabel, message, messageOps);
4665 return;
4666 }
4667 case VerificationFlavor::None:
4668 llvm_unreachable(
4669 "flavor `None` must be converted into one of concreate flavors");
4670 }
4671 };
4672
4673 // Wrap the verification statement up in the optional preprocessor
4674 // guards. This is a bit awkward since we want to translate an array of
4675 // guards into a recursive call to `addToIfDefBlock`.
4676 return emitGuards(op->getLoc(), guards, emit);
4677}
4678
4679// Lower an assert to SystemVerilog.
4680LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
4681 return lowerVerificationStatement(
4682 op, "assert__", op.getClock(), op.getPredicate(), op.getEnable(),
4683 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4684 op.getIsConcurrent(), op.getEventControl());
4685}
4686
4687// Lower an assume to SystemVerilog.
4688LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
4689 return lowerVerificationStatement(
4690 op, "assume__", op.getClock(), op.getPredicate(), op.getEnable(),
4691 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4692 op.getIsConcurrent(), op.getEventControl());
4693}
4694
4695// Lower a cover to SystemVerilog.
4696LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
4697 return lowerVerificationStatement(
4698 op, "cover__", op.getClock(), op.getPredicate(), op.getEnable(),
4699 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4700 op.getIsConcurrent(), op.getEventControl());
4701}
4702
4703// Lower an UNR only assume to a specific style of SV assume.
4704LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
4705 // TODO : Need to figure out if there is a cleaner way to get the string which
4706 // indicates the assert is UNR only. Or better - not rely on this at all -
4707 // ideally there should have been some other attribute which indicated that
4708 // this assert for UNR only.
4709 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>("guards");
4710 ArrayRef<Attribute> guards =
4711 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
4712
4713 auto label = op.getNameAttr();
4714 StringAttr assumeLabel;
4715 if (label && !label.empty())
4716 assumeLabel =
4717 StringAttr::get(builder.getContext(), "assume__" + label.getValue());
4718 auto predicate = getLoweredValue(op.getPredicate());
4719 auto enable = getLoweredValue(op.getEnable());
4720 auto notEnable = comb::createOrFoldNot(enable, builder, /*twoState=*/true);
4721 predicate = builder.createOrFold<comb::OrOp>(notEnable, predicate, true);
4722
4723 SmallVector<Value> messageOps;
4724 for (auto operand : op.getSubstitutions()) {
4725 auto loweredValue = getLoweredValue(operand);
4726 if (!loweredValue) {
4727 // If this is a zero bit operand, just pass a one bit zero.
4728 if (!isZeroBitFIRRTLType(operand.getType()))
4729 return failure();
4730 loweredValue = getOrCreateIntConstant(1, 0);
4731 }
4732 messageOps.push_back(loweredValue);
4733 }
4734 return emitGuards(op.getLoc(), guards, [&]() {
4735 builder.create<sv::AlwaysOp>(
4736 ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate), [&]() {
4737 if (op.getMessageAttr().getValue().empty())
4738 buildImmediateVerifOp(
4739 builder, "assume", predicate,
4740 circt::sv::DeferAssertAttr::get(
4741 builder.getContext(), circt::sv::DeferAssert::Immediate),
4742 assumeLabel);
4743 else
4744 buildImmediateVerifOp(
4745 builder, "assume", predicate,
4746 circt::sv::DeferAssertAttr::get(
4747 builder.getContext(), circt::sv::DeferAssert::Immediate),
4748 assumeLabel, op.getMessageAttr(), messageOps);
4749 });
4750 });
4751}
4752
4753LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
4754 // Don't emit anything for a zero or one operand attach.
4755 if (op.getAttached().size() < 2)
4756 return success();
4757
4758 SmallVector<Value, 4> inoutValues;
4759 for (auto v : op.getAttached()) {
4760 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
4761 if (!inoutValues.back()) {
4762 // Ignore zero bit values.
4763 if (!isZeroBitFIRRTLType(v.getType()))
4764 return failure();
4765 inoutValues.pop_back();
4766 continue;
4767 }
4768
4769 if (!isa<hw::InOutType>(inoutValues.back().getType()))
4770 return op.emitError("operand isn't an inout type");
4771 }
4772
4773 if (inoutValues.size() < 2)
4774 return success();
4775
4776 // If the op has a single source value, the value is used as a lowering result
4777 // of other values. Therefore we can delete the attach op here.
4779 return success();
4780
4781 // If all operands of the attach are internal to this module (none of them
4782 // are ports), then they can all be replaced with a single wire, and we can
4783 // delete the attach op.
4784 bool isAttachInternalOnly =
4785 llvm::none_of(inoutValues, [](auto v) { return isa<BlockArgument>(v); });
4786
4787 if (isAttachInternalOnly) {
4788 auto v0 = inoutValues.front();
4789 for (auto v : inoutValues) {
4790 if (v == v0)
4791 continue;
4792 v.replaceAllUsesWith(v0);
4793 }
4794 return success();
4795 }
4796
4797 // If the attach operands contain a port, then we can't do anything to
4798 // simplify the attach operation.
4799 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4800 circuitState.addMacroDecl(builder.getStringAttr("VERILATOR"));
4801 addToIfDefBlock(
4802 "SYNTHESIS",
4803 // If we're doing synthesis, we emit an all-pairs assign complex.
4804 [&]() {
4805 SmallVector<Value, 4> values;
4806 for (auto inoutValue : inoutValues)
4807 values.push_back(getReadValue(inoutValue));
4808
4809 for (size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
4810 for (size_t i2 = 0; i2 != e; ++i2)
4811 if (i1 != i2)
4812 builder.create<sv::AssignOp>(inoutValues[i1], values[i2]);
4813 }
4814 },
4815 // In the non-synthesis case, we emit a SystemVerilog alias
4816 // statement.
4817 [&]() {
4818 builder.create<sv::IfDefOp>(
4819 "VERILATOR",
4820 [&]() {
4821 builder.create<sv::VerbatimOp>(
4822 "`error \"Verilator does not support alias and thus "
4823 "cannot "
4824 "arbitrarily connect bidirectional wires and ports\"");
4825 },
4826 [&]() { builder.create<sv::AliasOp>(inoutValues); });
4827 });
4828
4829 return success();
4830}
4831
4832LogicalResult FIRRTLLowering::fixupLTLOps() {
4833 if (ltlOpFixupWorklist.empty())
4834 return success();
4835 LLVM_DEBUG(llvm::dbgs() << "Fixing up " << ltlOpFixupWorklist.size()
4836 << " LTL ops\n");
4837
4838 // Add wire users into the worklist.
4839 for (unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
4840 for (auto *user : ltlOpFixupWorklist[i]->getUsers())
4841 if (isa<hw::WireOp>(user))
4842 ltlOpFixupWorklist.insert(user);
4843
4844 // Re-infer LTL op types and remove wires.
4845 while (!ltlOpFixupWorklist.empty()) {
4846 auto *op = ltlOpFixupWorklist.pop_back_val();
4847
4848 // Update the operation's return type by re-running type inference.
4849 if (auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
4850 LLVM_DEBUG(llvm::dbgs() << "- Update " << *op << "\n");
4851 SmallVector<Type, 2> types;
4852 auto result = opIntf.inferReturnTypes(
4853 op->getContext(), op->getLoc(), op->getOperands(),
4854 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
4855 types);
4856 if (failed(result))
4857 return failure();
4858 assert(types.size() == op->getNumResults());
4859
4860 // Update the result types and add the dependent ops into the worklist if
4861 // the type changed.
4862 for (auto [result, type] : llvm::zip(op->getResults(), types)) {
4863 if (result.getType() == type)
4864 continue;
4865 LLVM_DEBUG(llvm::dbgs()
4866 << " - Result #" << result.getResultNumber() << " from "
4867 << result.getType() << " to " << type << "\n");
4868 result.setType(type);
4869 for (auto *user : result.getUsers())
4870 if (user != op)
4871 ltlOpFixupWorklist.insert(user);
4872 }
4873 }
4874
4875 // Remove LTL-typed wires.
4876 if (auto wireOp = dyn_cast<hw::WireOp>(op)) {
4877 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
4878 wireOp.replaceAllUsesWith(wireOp.getInput());
4879 LLVM_DEBUG(llvm::dbgs() << "- Remove " << wireOp << "\n");
4880 if (wireOp.use_empty())
4881 wireOp.erase();
4882 }
4883 continue;
4884 }
4885
4886 // Ensure that the operation has no users outside of LTL operations.
4887 SmallPtrSet<Operation *, 4> usersReported;
4888 for (auto *user : op->getUsers()) {
4889 if (!usersReported.insert(user).second)
4890 continue;
4891 if (isa<ltl::LTLDialect, verif::VerifDialect>(user->getDialect()))
4892 continue;
4893 if (isa<hw::WireOp>(user))
4894 continue;
4895 auto d = op->emitError(
4896 "verification operation used in a non-verification context");
4897 d.attachNote(user->getLoc())
4898 << "leaking outside verification context here";
4899 return d;
4900 }
4901 }
4902
4903 return success();
4904}
assert(baseType &&"element must be base type")
static Operation * buildImmediateVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build an immediate assert operation based on the original FIRRTL operation name.
static Operation * buildConcurrentVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build a concurrent assert operation based on the original FIRRTL operation name.
static unsigned getBitWidthFromVectorSize(unsigned size)
static void moveVerifAnno(ModuleOp top, AnnotationSet &annos, StringRef annoClass, StringRef attrBase)
Move a ExtractTestCode related annotation from annotations to an attribute.
static Value castToFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast a value to a desired target type.
static ArrayAttr getHWParameters(FExtModuleOp module, bool ignoreValues)
Map the parameter specifier on the specified extmodule into the HWModule representation for parameter...
static bool isZeroBitFIRRTLType(Type type)
Return true if the specified type is a sized FIRRTL type (Int or Analog) with zero bits.
Definition LowerToHW.cpp:60
static Value tryEliminatingAttachesToAnalogValue(Value value, Operation *insertPoint)
Given a value of analog type, check to see the only use of it is an attach.
static LogicalResult handleZeroBit(Value failedOperand, const std::function< LogicalResult()> &fn)
Zero bit operands end up looking like failures from getLoweredValue.
static const char moduleHierarchyFileAttrName[]
Attribute that indicates that the module hierarchy starting at the annotated module should be dumped ...
Definition LowerToHW.cpp:56
static void tryCopyName(Operation *dst, Operation *src)
static LogicalResult verifyOpLegality(Operation *op)
This verifies that the target operation has been lowered to a legal operation.
Definition LowerToHW.cpp:85
static Value castFromFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast from a FIRRTL type (potentially with a flip) to a standard type.
static SmallVector< SubfieldOp > getAllFieldAccesses(Value structValue, StringRef field)
static Value tryEliminatingConnectsToValue(Value flipValue, Operation *insertPoint, CircuitLoweringState &loweringState)
Given a value of flip type, check to see if all of the uses of it are connects.
static Value getSingleNonInstanceOperand(AttachOp op)
Definition LowerToHW.cpp:67
static IntType getWidestIntType(Type t1, Type t2)
Given two FIRRTL integer types, return the widest one.
static Block * getBodyBlock(FModuleLike mod)
std::shared_ptr< calyx::CalyxLoweringState > loweringState
Instantiate one of these and use it to build typed backedges.
void abandon()
Abandon the backedges, suppressing any diagnostics if they are still active upon destruction of the b...
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
mlir::LogicalResult clearOrEmitError()
Clear the backedges, erasing any remaining cursor ops.
Backedge is a wrapper class around a Value.
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition Namespace.h:30
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool removeAnnotation(Annotation anno)
Remove an annotation from this annotation set.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
ResultType visitInvalidOp(Operation *op, ExtraArgs... args)
visitInvalidOp is an override point for non-FIRRTL dialect operations.
ResultType visitUnhandledOp(Operation *op, ExtraArgs... args)
visitUnhandledOp is an override point for FIRRTL dialect ops that the concrete visitor didn't bother ...
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
This is the common base class between SIntType and UIntType.
This table tracks nlas and what modules participate in them.
Definition NLATable.h:29
This is an edge in the InstanceGraph.
create(*sub_arrays)
Definition hw.py:504
create(elements)
Definition hw.py:483
create(array_value, idx)
Definition hw.py:450
create(data_type, value)
Definition hw.py:433
create(dest, src)
Definition sv.py:98
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:55
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
Definition EmitOps.h:32
constexpr const char * extractCoverageAnnoClass
constexpr const char * elaborationArtefactsDirectoryAnnoClass
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
constexpr const char * extractGrandCentralClass
constexpr const char * blackBoxAnnoClass
constexpr const char * dontObfuscateModuleAnnoClass
constexpr const char * metadataDirectoryAttrName
constexpr const char * testBenchDirAnnoClass
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
constexpr const char * dutAnnoClass
constexpr const char * forceNameAnnoClass
constexpr const char * noDedupAnnoClass
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
constexpr const char * extractAssertAnnoClass
constexpr const char * verifBlackBoxAnnoClass
constexpr const char * blackBoxTargetDirAnnoClass
Type lowerType(Type type, std::optional< Location > loc={}, llvm::function_ref< hw::TypeAliasType(Type, BaseTypeAliasType, Location)> getTypeDeclFn={})
Given a type, return the corresponding lowered type for the HW dialect.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
constexpr const char * extractAssumeAnnoClass
constexpr const char * testHarnessHierAnnoClass
constexpr const char * moduleHierAnnoClass
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
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 > createLowerFIRRTLToHWPass(bool enableAnnotationWarning=false, firrtl::VerificationFlavor assertionFlavor=firrtl::VerificationFlavor::None)
This is the pass constructor.
Definition emit.py:1
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
Definition codegen.py:121
Definition hw.py:1
Definition seq.py:1
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition seq.py:21
Definition verif.py:1
This holds the name and type that describes the module's ports.
bool isOutput() const
Return true if this is a simple output-only port.
bool isInput() const
Return true if this is a simple input-only port.
mlir::Type type
Definition HWTypes.h:31
mlir::StringAttr name
Definition HWTypes.h:30
This holds the name, type, direction of a module's ports.
size_t argNum
This is the argument index or the result index depending on the direction.
void setSym(InnerSymAttr sym, MLIRContext *ctx)
InnerSymAttr getSym() const