CIRCT 21.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 LogicalResult visitDecl(ContractOp op);
1584
1585 // Unary Ops.
1586 LogicalResult lowerNoopCast(Operation *op);
1587 LogicalResult visitExpr(AsSIntPrimOp op);
1588 LogicalResult visitExpr(AsUIntPrimOp op);
1589 LogicalResult visitExpr(AsClockPrimOp op);
1590 LogicalResult visitExpr(AsAsyncResetPrimOp op) { return lowerNoopCast(op); }
1591
1592 LogicalResult visitExpr(HWStructCastOp op);
1593 LogicalResult visitExpr(BitCastOp op);
1594 LogicalResult
1595 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1596 LogicalResult visitExpr(CvtPrimOp op);
1597 LogicalResult visitExpr(NotPrimOp op);
1598 LogicalResult visitExpr(NegPrimOp op);
1599 LogicalResult visitExpr(PadPrimOp op);
1600 LogicalResult visitExpr(XorRPrimOp op);
1601 LogicalResult visitExpr(AndRPrimOp op);
1602 LogicalResult visitExpr(OrRPrimOp op);
1603
1604 // Binary Ops.
1605 template <typename ResultUnsignedOpType,
1606 typename ResultSignedOpType = ResultUnsignedOpType>
1607 LogicalResult lowerBinOp(Operation *op);
1608 template <typename ResultOpType>
1609 LogicalResult lowerBinOpToVariadic(Operation *op);
1610
1611 template <typename ResultOpType>
1612 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1613
1614 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1615 ICmpPredicate unsignedOp);
1616 template <typename SignedOp, typename UnsignedOp>
1617 LogicalResult lowerDivLikeOp(Operation *op);
1618
1619 LogicalResult visitExpr(CatPrimOp op);
1620
1621 LogicalResult visitExpr(AndPrimOp op) {
1622 return lowerBinOpToVariadic<comb::AndOp>(op);
1623 }
1624 LogicalResult visitExpr(OrPrimOp op) {
1625 return lowerBinOpToVariadic<comb::OrOp>(op);
1626 }
1627 LogicalResult visitExpr(XorPrimOp op) {
1628 return lowerBinOpToVariadic<comb::XorOp>(op);
1629 }
1630 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1631 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1632 }
1633 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1634 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1635 }
1636 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1637 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1638 }
1639 LogicalResult visitExpr(AddPrimOp op) {
1640 return lowerBinOpToVariadic<comb::AddOp>(op);
1641 }
1642 LogicalResult visitExpr(EQPrimOp op) {
1643 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1644 }
1645 LogicalResult visitExpr(NEQPrimOp op) {
1646 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1647 }
1648 LogicalResult visitExpr(LTPrimOp op) {
1649 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1650 }
1651 LogicalResult visitExpr(LEQPrimOp op) {
1652 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1653 }
1654 LogicalResult visitExpr(GTPrimOp op) {
1655 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1656 }
1657 LogicalResult visitExpr(GEQPrimOp op) {
1658 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1659 }
1660
1661 LogicalResult visitExpr(SubPrimOp op) { return lowerBinOp<comb::SubOp>(op); }
1662 LogicalResult visitExpr(MulPrimOp op) {
1663 return lowerBinOpToVariadic<comb::MulOp>(op);
1664 }
1665 LogicalResult visitExpr(DivPrimOp op) {
1666 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1667 }
1668 LogicalResult visitExpr(RemPrimOp op) {
1669 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1670 }
1671
1672 // Intrinsic Operations
1673 LogicalResult visitExpr(IsXIntrinsicOp op);
1674 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1675 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1676 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1677 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1678 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1679 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1680 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1681 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1682 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1683 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1684 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1685 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1686 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1687 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1688 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1689 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1690 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1691 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1692 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1693 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1694
1695 template <typename TargetOp, typename IntrinsicOp>
1696 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1697 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1698 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1699 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1700 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
1701 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
1702 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1703 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1704
1705 // Other Operations
1706 LogicalResult visitExpr(BitsPrimOp op);
1707 LogicalResult visitExpr(InvalidValueOp op);
1708 LogicalResult visitExpr(HeadPrimOp op);
1709 LogicalResult visitExpr(ShlPrimOp op);
1710 LogicalResult visitExpr(ShrPrimOp op);
1711 LogicalResult visitExpr(DShlPrimOp op) {
1712 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1713 }
1714 LogicalResult visitExpr(DShrPrimOp op) {
1715 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1716 }
1717 LogicalResult visitExpr(DShlwPrimOp op) {
1718 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1719 }
1720 LogicalResult visitExpr(TailPrimOp op);
1721 LogicalResult visitExpr(MuxPrimOp op);
1722 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1723 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1724 LogicalResult visitExpr(MultibitMuxOp op);
1725 LogicalResult visitExpr(VerbatimExprOp op);
1726 LogicalResult visitExpr(XMRRefOp op);
1727 LogicalResult visitExpr(XMRDerefOp op);
1728
1729 // Statements
1730 LogicalResult lowerVerificationStatement(
1731 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1732 Value enable, StringAttr messageAttr, ValueRange operands,
1733 StringAttr nameAttr, bool isConcurrent, EventControl eventControl);
1734
1735 LogicalResult visitStmt(SkipOp op);
1736
1737 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
1738 LogicalResult visitStmt(ConnectOp op);
1739 LogicalResult visitStmt(MatchingConnectOp op);
1740 LogicalResult visitStmt(ForceOp op);
1741 LogicalResult visitStmt(PrintFOp op);
1742 LogicalResult visitStmt(StopOp op);
1743 LogicalResult visitStmt(AssertOp op);
1744 LogicalResult visitStmt(AssumeOp op);
1745 LogicalResult visitStmt(CoverOp op);
1746 LogicalResult visitStmt(AttachOp op);
1747 LogicalResult visitStmt(RefForceOp op);
1748 LogicalResult visitStmt(RefForceInitialOp op);
1749 LogicalResult visitStmt(RefReleaseOp op);
1750 LogicalResult visitStmt(RefReleaseInitialOp op);
1751
1752 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
1753 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
1754 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
1755
1756 LogicalResult fixupLTLOps();
1757
1758 Type lowerType(Type type) {
1759 return circuitState.lowerType(type, builder.getLoc());
1760 }
1761
1762private:
1763 /// The module we're lowering into.
1764 hw::HWModuleOp theModule;
1765
1766 /// Global state.
1767 CircuitLoweringState &circuitState;
1768
1769 /// This builder is set to the right location for each visit call.
1770 ImplicitLocOpBuilder builder;
1771
1772 /// Each value lowered (e.g. operation result) is kept track in this map.
1773 /// The key should have a FIRRTL type, the result will have an HW dialect
1774 /// type.
1775 DenseMap<Value, Value> valueMapping;
1776
1777 /// Mapping from clock values to corresponding non-clock values converted
1778 /// via a deduped `seq.from_clock` op.
1779 DenseMap<Value, Value> fromClockMapping;
1780
1781 /// This keeps track of constants that we have created so we can reuse them.
1782 /// This is populated by the getOrCreateIntConstant method.
1783 DenseMap<Attribute, Value> hwConstantMap;
1784 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1785
1786 /// This keeps track of constant X that we have created so we can reuse them.
1787 /// This is populated by the getOrCreateXConstant method.
1788 DenseMap<unsigned, Value> hwConstantXMap;
1789 DenseMap<Type, Value> hwConstantZMap;
1790
1791 /// We auto-unique "ReadInOut" ops from wires and regs, enabling
1792 /// optimizations and CSEs of the read values to be more obvious. This
1793 /// caches a known ReadInOutOp for the given value and is managed by
1794 /// `getReadValue(v)`.
1795 DenseMap<Value, Value> readInOutCreated;
1796
1797 // We auto-unique graph-level blocks to reduce the amount of generated
1798 // code and ensure that side effects are properly ordered in FIRRTL.
1799 using AlwaysKeyType = std::tuple<Block *, sv::EventControl, Value,
1800 sv::ResetType, sv::EventControl, Value>;
1802 alwaysBlocks;
1805
1806 /// A namespace that can be used to generate new symbol names that are unique
1807 /// within this module.
1808 hw::InnerSymbolNamespace moduleNamespace;
1809
1810 /// A backedge builder to directly materialize values during the lowering
1811 /// without requiring temporary wires.
1812 BackedgeBuilder backedgeBuilder;
1813 /// Currently unresolved backedges. More precisely, a mapping from the
1814 /// backedge value to the value it will be replaced with. We use a MapVector
1815 /// so that a combinational cycles of backedges, the one backedge that gets
1816 /// replaced with an undriven wire is consistent.
1817 llvm::MapVector<Value, Value> backedges;
1818
1819 /// A collection of values generated by the lowering process that may have
1820 /// become obsolete through subsequent parts of the lowering. This covers the
1821 /// values of wires that may be overridden by subsequent connects; or
1822 /// subaccesses that appear only as destination of a connect, and thus gets
1823 /// obsoleted by the connect directly updating the wire or register.
1824 DenseSet<Operation *> maybeUnusedValues;
1825
1826 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1827 void maybeUnused(Value value) {
1828 if (auto *op = value.getDefiningOp())
1829 maybeUnused(op);
1830 }
1831
1832 /// A worklist of LTL operations that don't have their final type yet. The
1833 /// FIRRTL intrinsics for LTL ops all use `uint<1>` types, but the actual LTL
1834 /// ops themselves have more precise `!ltl.sequence` and `!ltl.property`
1835 /// types. After all LTL ops have been lowered, this worklist is used to
1836 /// compute their actual types (re-inferring return types) and push the
1837 /// updated types to their users. This also drops any `hw.wire`s in between
1838 /// the LTL ops, which were necessary to go from the def-before-use FIRRTL
1839 /// dialect to the graph-like HW dialect.
1840 SetVector<Operation *> ltlOpFixupWorklist;
1841
1842 /// A worklist of operation ranges to be lowered. Parnet operations can push
1843 /// their nested operations onto this worklist to be processed after the
1844 /// parent operation has handled the region, blocks, and block arguments.
1845 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
1846
1847 void addToWorklist(Block &block) {
1848 worklist.push_back({block.begin(), block.end()});
1849 }
1850 void addToWorklist(Region &region) {
1851 for (auto &block : llvm::reverse(region))
1852 addToWorklist(block);
1853 }
1854};
1855} // end anonymous namespace
1856
1857LogicalResult FIRRTLModuleLowering::lowerModuleOperations(
1858 hw::HWModuleOp module, CircuitLoweringState &loweringState) {
1859 return FIRRTLLowering(module, loweringState).run();
1860}
1861
1862// This is the main entrypoint for the lowering pass.
1863LogicalResult FIRRTLLowering::run() {
1864 // Mark the module's block arguments are already lowered. This will allow
1865 // `getLoweredValue` to return the block arguments as they are.
1866 for (auto arg : theModule.getBodyBlock()->getArguments())
1867 if (failed(setLowering(arg, arg)))
1868 return failure();
1869
1870 // Add the operations in the body to the worklist and lower all operations
1871 // until the worklist is empty. Operations may push their own nested
1872 // operations onto the worklist to lower them in turn. The `builder` is
1873 // positioned ahead of each operation as it is being lowered.
1874 addToWorklist(theModule.getBody());
1875 SmallVector<Operation *, 16> opsToRemove;
1876
1877 while (!worklist.empty()) {
1878 auto &[opsIt, opsEnd] = worklist.back();
1879 if (opsIt == opsEnd) {
1880 worklist.pop_back();
1881 continue;
1882 }
1883 Operation *op = &*opsIt++;
1884
1885 builder.setInsertionPoint(op);
1886 builder.setLoc(op->getLoc());
1887 auto done = succeeded(dispatchVisitor(op));
1888 circuitState.processRemainingAnnotations(op, AnnotationSet(op));
1889 if (done)
1890 opsToRemove.push_back(op);
1891 else {
1892 switch (handleUnloweredOp(op)) {
1893 case AlreadyLowered:
1894 break; // Something like hw.output, which is already lowered.
1895 case NowLowered: // Something handleUnloweredOp removed.
1896 opsToRemove.push_back(op);
1897 break;
1898 case LoweringFailure:
1899 backedgeBuilder.abandon();
1900 return failure();
1901 }
1902 }
1903 }
1904
1905 // Replace all backedges with uses of their regular values. We process them
1906 // after the module body since the lowering table is too hard to keep up to
1907 // date. Multiple operations may be lowered to the same backedge when values
1908 // are folded, which means we would have to scan the entire lowering table to
1909 // safely replace a backedge.
1910 for (auto &[backedge, value] : backedges) {
1911 SmallVector<Location> driverLocs;
1912 // In the case where we have backedges connected to other backedges, we have
1913 // to find the value that actually drives the group.
1914 while (true) {
1915 // If we find the original backedge we have some undriven logic or
1916 // a combinatorial loop. Bail out and provide information on the nodes.
1917 if (backedge == value) {
1918 Location edgeLoc = backedge.getLoc();
1919 if (driverLocs.empty()) {
1920 mlir::emitError(edgeLoc, "sink does not have a driver");
1921 } else {
1922 auto diag = mlir::emitError(edgeLoc, "sink in combinational loop");
1923 for (auto loc : driverLocs)
1924 diag.attachNote(loc) << "through driver here";
1925 }
1926 backedgeBuilder.abandon();
1927 return failure();
1928 }
1929 // If the value is not another backedge, we have found the driver.
1930 auto *it = backedges.find(value);
1931 if (it == backedges.end())
1932 break;
1933 // Find what is driving the next backedge.
1934 driverLocs.push_back(value.getLoc());
1935 value = it->second;
1936 }
1937 if (auto *defOp = backedge.getDefiningOp())
1938 maybeUnusedValues.erase(defOp);
1939 backedge.replaceAllUsesWith(value);
1940 }
1941
1942 // Now that all of the operations that can be lowered are, remove th
1943 // original values. We know that any lowered operations will be dead (if
1944 // removed in reverse order) at this point - any users of them from
1945 // unremapped operations will be changed to use the newly lowered ops.
1946 hw::ConstantOp zeroI0;
1947 while (!opsToRemove.empty()) {
1948 auto *op = opsToRemove.pop_back_val();
1949
1950 // We remove zero-width values when lowering FIRRTL ops. We can't remove
1951 // such a value if it escapes to a foreign op. In that case, create an
1952 // `hw.constant 0 : i0` to pass along.
1953 for (auto result : op->getResults()) {
1954 if (!isZeroBitFIRRTLType(result.getType()))
1955 continue;
1956 if (!zeroI0) {
1957 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
1958 zeroI0 = builder.create<hw::ConstantOp>(op->getLoc(),
1959 builder.getIntegerType(0), 0);
1960 maybeUnusedValues.insert(zeroI0);
1961 }
1962 result.replaceAllUsesWith(zeroI0);
1963 }
1964
1965 if (!op->use_empty()) {
1966 auto d = op->emitOpError(
1967 "still has uses; should remove ops in reverse order of visitation");
1968 SmallPtrSet<Operation *, 2> visited;
1969 for (auto *user : op->getUsers())
1970 if (visited.insert(user).second)
1971 d.attachNote(user->getLoc())
1972 << "used by " << user->getName() << " op";
1973 return d;
1974 }
1975 maybeUnusedValues.erase(op);
1976 op->erase();
1977 }
1978
1979 // Prune operations that may have become unused throughout the lowering.
1980 while (!maybeUnusedValues.empty()) {
1981 auto it = maybeUnusedValues.begin();
1982 auto *op = *it;
1983 maybeUnusedValues.erase(it);
1984 if (!isOpTriviallyDead(op))
1985 continue;
1986 for (auto operand : op->getOperands())
1987 if (auto *defOp = operand.getDefiningOp())
1988 maybeUnusedValues.insert(defOp);
1989 op->erase();
1990 }
1991
1992 // Determine the actual types of lowered LTL operations and remove any
1993 // intermediate wires among them.
1994 if (failed(fixupLTLOps()))
1995 return failure();
1996
1997 return backedgeBuilder.clearOrEmitError();
1998}
1999
2000//===----------------------------------------------------------------------===//
2001// Helpers
2002//===----------------------------------------------------------------------===//
2003
2004/// Create uniqued constant clocks.
2005Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2006 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2007
2008 auto &entry = hwConstantMap[attr];
2009 if (entry)
2010 return entry;
2011
2012 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2013 entry = entryBuilder.create<seq::ConstClockOp>(builder.getLoc(), attr);
2014 return entry;
2015}
2016
2017/// Check to see if we've already lowered the specified constant. If so,
2018/// return it. Otherwise create it and put it in the entry block for reuse.
2019Value FIRRTLLowering::getOrCreateIntConstant(const APInt &value) {
2020 auto attr = builder.getIntegerAttr(
2021 builder.getIntegerType(value.getBitWidth()), value);
2022
2023 auto &entry = hwConstantMap[attr];
2024 if (entry)
2025 return entry;
2026
2027 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2028 entry = entryBuilder.create<hw::ConstantOp>(builder.getLoc(), attr);
2029 return entry;
2030}
2031
2032/// Check to see if we've already created the specified aggregate constant
2033/// attribute. If so, return it. Otherwise create it.
2034Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2035 Type type) {
2036 // Base case.
2037 if (hw::type_isa<IntegerType>(type))
2038 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2039
2040 auto cache = hwAggregateConstantMap.lookup({value, type});
2041 if (cache)
2042 return cache;
2043
2044 // Recursively construct elements.
2045 SmallVector<Attribute> values;
2046 for (auto e : llvm::enumerate(cast<ArrayAttr>(value))) {
2047 Type subType;
2048 if (auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2049 subType = array.getElementType();
2050 else if (auto structType = hw::type_dyn_cast<hw::StructType>(type))
2051 subType = structType.getElements()[e.index()].type;
2052 else
2053 assert(false && "type must be either array or struct");
2054
2055 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2056 }
2057
2058 // FIRRTL and HW have a different operand ordering for arrays.
2059 if (hw::type_isa<hw::ArrayType>(type))
2060 std::reverse(values.begin(), values.end());
2061
2062 auto &entry = hwAggregateConstantMap[{value, type}];
2063 entry = builder.getArrayAttr(values);
2064 return entry;
2065}
2066
2067/// Zero bit operands end up looking like failures from getLoweredValue. This
2068/// helper function invokes the closure specified if the operand was actually
2069/// zero bit, or returns failure() if it was some other kind of failure.
2070static LogicalResult handleZeroBit(Value failedOperand,
2071 const std::function<LogicalResult()> &fn) {
2072 assert(failedOperand && "Should be called on the failed operand");
2073 if (!isZeroBitFIRRTLType(failedOperand.getType()))
2074 return failure();
2075 return fn();
2076}
2077
2078/// Check to see if we've already lowered the specified constant. If so,
2079/// return it. Otherwise create it and put it in the entry block for reuse.
2080Value FIRRTLLowering::getOrCreateXConstant(unsigned numBits) {
2081
2082 auto &entry = hwConstantXMap[numBits];
2083 if (entry)
2084 return entry;
2085
2086 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2087 entry = entryBuilder.create<sv::ConstantXOp>(
2088 builder.getLoc(), entryBuilder.getIntegerType(numBits));
2089 return entry;
2090}
2091
2092Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2093 auto &entry = hwConstantZMap[type];
2094 if (!entry) {
2095 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2096 entry = entryBuilder.create<sv::ConstantZOp>(builder.getLoc(), type);
2097 }
2098 return entry;
2099}
2100
2101/// Return the lowered HW value corresponding to the specified original value.
2102/// This returns a null value for FIRRTL values that haven't be lowered, e.g.
2103/// unknown width integers. This returns hw::inout type values if present, it
2104/// does not implicitly read from them.
2105Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2106 // If we lowered this value, then return the lowered value, otherwise fail.
2107 if (auto lowering = valueMapping.lookup(value)) {
2108 assert(!isa<FIRRTLType>(lowering.getType()) &&
2109 "Lowered value should be a non-FIRRTL value");
2110 return lowering;
2111 }
2112 return Value();
2113}
2114
2115/// Return the lowered value corresponding to the specified original value.
2116/// This returns a null value for FIRRTL values that cannot be lowered, e.g.
2117/// unknown width integers.
2118Value FIRRTLLowering::getLoweredValue(Value value) {
2119 auto result = getPossiblyInoutLoweredValue(value);
2120 if (!result)
2121 return result;
2122
2123 // If we got an inout value, implicitly read it. FIRRTL allows direct use
2124 // of wires and other things that lower to inout type.
2125 if (isa<hw::InOutType>(result.getType()))
2126 return getReadValue(result);
2127
2128 return result;
2129}
2130
2131/// Return the lowered value, converting `seq.clock` to `i1.
2132Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2133 auto result = getLoweredValue(value);
2134 if (!result)
2135 return result;
2136
2137 if (hw::type_isa<seq::ClockType>(result.getType()))
2138 return getNonClockValue(result);
2139
2140 return result;
2141}
2142
2143/// Return the lowered aggregate value whose type is converted into
2144/// `destType`. We have to care about the extension/truncation/signedness of
2145/// each element.
2146Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2147 FIRRTLBaseType sourceType,
2148 FIRRTLBaseType destType,
2149 bool allowTruncate) {
2150 SmallVector<Value> resultBuffer;
2151
2152 // Helper function to cast each element of array to dest type.
2153 auto cast = [&](Value value, FIRRTLBaseType sourceType,
2154 FIRRTLBaseType destType) {
2155 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2156 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2157 auto resultType = builder.getIntegerType(destWidth);
2158
2159 if (srcWidth == destWidth)
2160 return value;
2161
2162 if (srcWidth > destWidth) {
2163 if (allowTruncate)
2164 return builder.createOrFold<comb::ExtractOp>(resultType, value, 0);
2165
2166 builder.emitError("operand should not be a truncation");
2167 return Value();
2168 }
2169
2170 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2171 return comb::createOrFoldSExt(value, resultType, builder);
2172 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2173 return builder.createOrFold<comb::ConcatOp>(zero, value);
2174 };
2175
2176 // This recursive function constructs the output array.
2177 std::function<LogicalResult(Value, FIRRTLBaseType, FIRRTLBaseType)> recurse =
2178 [&](Value src, FIRRTLBaseType srcType,
2179 FIRRTLBaseType destType) -> LogicalResult {
2180 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2181 .Case<FVectorType>([&](auto srcVectorType) {
2182 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2183 unsigned size = resultBuffer.size();
2184 unsigned indexWidth =
2185 getBitWidthFromVectorSize(srcVectorType.getNumElements());
2186 for (size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2187 destVectorType.getNumElements());
2188 i != e; ++i) {
2189 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2190 auto arrayIndex = builder.create<hw::ArrayGetOp>(src, iIdx);
2191 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2192 destVectorType.getElementType())))
2193 return failure();
2194 }
2195 SmallVector<Value> temp(resultBuffer.begin() + size,
2196 resultBuffer.end());
2197 auto array = builder.createOrFold<hw::ArrayCreateOp>(temp);
2198 resultBuffer.resize(size);
2199 resultBuffer.push_back(array);
2200 return success();
2201 })
2202 .Case<BundleType>([&](BundleType srcStructType) {
2203 auto destStructType = firrtl::type_cast<BundleType>(destType);
2204 unsigned size = resultBuffer.size();
2205
2206 // TODO: We don't support partial connects for bundles for now.
2207 if (destStructType.getNumElements() != srcStructType.getNumElements())
2208 return failure();
2209
2210 for (auto elem : llvm::enumerate(destStructType)) {
2211 auto structExtract =
2212 builder.create<hw::StructExtractOp>(src, elem.value().name);
2213 if (failed(recurse(structExtract,
2214 srcStructType.getElementType(elem.index()),
2215 destStructType.getElementType(elem.index()))))
2216 return failure();
2217 }
2218 SmallVector<Value> temp(resultBuffer.begin() + size,
2219 resultBuffer.end());
2220 auto newStruct = builder.createOrFold<hw::StructCreateOp>(
2221 lowerType(destStructType), temp);
2222 resultBuffer.resize(size);
2223 resultBuffer.push_back(newStruct);
2224 return success();
2225 })
2226 .Case<IntType>([&](auto) {
2227 if (auto result = cast(src, srcType, destType)) {
2228 resultBuffer.push_back(result);
2229 return success();
2230 }
2231 return failure();
2232 })
2233 .Default([&](auto) { return failure(); });
2234 };
2235
2236 if (failed(recurse(array, sourceType, destType)))
2237 return Value();
2238
2239 assert(resultBuffer.size() == 1 &&
2240 "resultBuffer must only contain a result array if `success` is true");
2241 return resultBuffer[0];
2242}
2243
2244/// Return the lowered value corresponding to the specified original value and
2245/// then extend it to match the width of destType if needed.
2246///
2247/// This returns a null value for FIRRTL values that cannot be lowered, e.g.
2248/// unknown width integers.
2249Value FIRRTLLowering::getLoweredAndExtendedValue(Value value, Type destType) {
2250 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2251 type_isa<FIRRTLBaseType>(destType) &&
2252 "input/output value should be FIRRTL");
2253
2254 // We only know how to extend integer types with known width.
2255 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2256 if (destWidth == -1)
2257 return {};
2258
2259 auto result = getLoweredValue(value);
2260 if (!result) {
2261 // If this was a zero bit operand being extended, then produce a zero of
2262 // the right result type. If it is just a failure, fail.
2263 if (!isZeroBitFIRRTLType(value.getType()))
2264 return {};
2265 // Zero bit results have to be returned as null. The caller can handle
2266 // this if they want to.
2267 if (destWidth == 0)
2268 return {};
2269 // Otherwise, FIRRTL semantics is that an extension from a zero bit value
2270 // always produces a zero value in the destination width.
2271 return getOrCreateIntConstant(destWidth, 0);
2272 }
2273
2274 if (destWidth ==
2275 cast<FIRRTLBaseType>(value.getType()).getBitWidthOrSentinel()) {
2276 // Lookup the lowered type of dest.
2277 auto loweredDstType = lowerType(destType);
2278 if (result.getType() != loweredDstType &&
2279 (isa<hw::TypeAliasType>(result.getType()) ||
2280 isa<hw::TypeAliasType>(loweredDstType))) {
2281 return builder.createOrFold<hw::BitcastOp>(loweredDstType, result);
2282 }
2283 }
2284 // Aggregates values
2285 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2286 // Types already match.
2287 if (destType == value.getType())
2288 return result;
2289
2290 return getExtOrTruncAggregateValue(
2291 result, type_cast<FIRRTLBaseType>(value.getType()),
2292 type_cast<FIRRTLBaseType>(destType),
2293 /* allowTruncate */ false);
2294 }
2295
2296 if (isa<seq::ClockType>(result.getType())) {
2297 // Types already match.
2298 if (destType == value.getType())
2299 return result;
2300 builder.emitError("cannot use clock type as an integer");
2301 return {};
2302 }
2303
2304 auto intResultType = dyn_cast<IntegerType>(result.getType());
2305 if (!intResultType) {
2306 builder.emitError("operand of type ")
2307 << result.getType() << " cannot be used as an integer";
2308 return {};
2309 }
2310
2311 auto srcWidth = intResultType.getWidth();
2312 if (srcWidth == unsigned(destWidth))
2313 return result;
2314
2315 if (srcWidth > unsigned(destWidth)) {
2316 builder.emitError("operand should not be a truncation");
2317 return {};
2318 }
2319
2320 auto resultType = builder.getIntegerType(destWidth);
2321
2322 // Extension follows the sign of the source value, not the destination.
2323 auto valueFIRType =
2324 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2325 if (type_cast<IntType>(valueFIRType).isSigned())
2326 return comb::createOrFoldSExt(result, resultType, builder);
2327
2328 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2329 return builder.createOrFold<comb::ConcatOp>(zero, result);
2330}
2331
2332/// Return the lowered value corresponding to the specified original value and
2333/// then extended or truncated to match the width of destType if needed.
2334///
2335/// This returns a null value for FIRRTL values that cannot be lowered, e.g.
2336/// unknown width integers.
2337Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2338 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2339 type_isa<FIRRTLBaseType>(destType) &&
2340 "input/output value should be FIRRTL");
2341
2342 // We only know how to adjust integer types with known width.
2343 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2344 if (destWidth == -1)
2345 return {};
2346
2347 auto result = getLoweredValue(value);
2348 if (!result) {
2349 // If this was a zero bit operand being extended, then produce a zero of
2350 // the right result type. If it is just a failure, fail.
2351 if (!isZeroBitFIRRTLType(value.getType()))
2352 return {};
2353 // Zero bit results have to be returned as null. The caller can handle
2354 // this if they want to.
2355 if (destWidth == 0)
2356 return {};
2357 // Otherwise, FIRRTL semantics is that an extension from a zero bit value
2358 // always produces a zero value in the destination width.
2359 return getOrCreateIntConstant(destWidth, 0);
2360 }
2361
2362 // Aggregates values
2363 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2364 // Types already match.
2365 if (destType == value.getType())
2366 return result;
2367
2368 return getExtOrTruncAggregateValue(
2369 result, type_cast<FIRRTLBaseType>(value.getType()),
2370 type_cast<FIRRTLBaseType>(destType),
2371 /* allowTruncate */ true);
2372 }
2373
2374 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2375 if (srcWidth == unsigned(destWidth))
2376 return result;
2377
2378 if (destWidth == 0)
2379 return {};
2380
2381 if (srcWidth > unsigned(destWidth)) {
2382 auto resultType = builder.getIntegerType(destWidth);
2383 return builder.createOrFold<comb::ExtractOp>(resultType, result, 0);
2384 }
2385
2386 auto resultType = builder.getIntegerType(destWidth);
2387
2388 // Extension follows the sign of the source value, not the destination.
2389 auto valueFIRType =
2390 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2391 if (type_cast<IntType>(valueFIRType).isSigned())
2392 return comb::createOrFoldSExt(result, resultType, builder);
2393
2394 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2395 return builder.createOrFold<comb::ConcatOp>(zero, result);
2396}
2397
2398/// Return a lowered version of 'operand' suitable for use with substitution /
2399/// format strings. Zero bit operands are rewritten as one bit zeros and signed
2400/// integers are wrapped in $signed().
2401Value FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2402 auto loweredValue = getLoweredValue(operand);
2403 if (!loweredValue) {
2404 // If this is a zero bit operand, just pass a one bit zero.
2405 if (!isZeroBitFIRRTLType(operand.getType()))
2406 return nullptr;
2407 loweredValue = getOrCreateIntConstant(1, 0);
2408 }
2409
2410 // If the operand was an SInt, we want to give the user the option to print
2411 // it as signed decimal and have to wrap it in $signed().
2412 if (auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2413 if (intTy.isSigned())
2414 loweredValue = builder.create<sv::SystemFunctionOp>(
2415 loweredValue.getType(), "signed", loweredValue);
2416
2417 return loweredValue;
2418}
2419
2420/// Set the lowered value of 'orig' to 'result', remembering this in a map.
2421/// This always returns success() to make it more convenient in lowering code.
2422///
2423/// Note that result may be null here if we're lowering orig to a zero-bit
2424/// value.
2425///
2426LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2427 if (auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2428 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2429 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2430
2431#ifndef NDEBUG
2432 auto baseType = getBaseType(origType);
2433 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2434
2435 // Caller should pass null value iff this was a zero bit value.
2436 if (srcWidth != -1) {
2437 if (result)
2438 assert((srcWidth != 0) &&
2439 "Lowering produced value for zero width source");
2440 else
2441 assert((srcWidth == 0) &&
2442 "Lowering produced null value but source wasn't zero width");
2443 }
2444#endif
2445 } else {
2446 assert(result && "Lowering of foreign type produced null value");
2447 }
2448
2449 auto &slot = valueMapping[orig];
2450 assert(!slot && "value lowered multiple times");
2451 slot = result;
2452 return success();
2453}
2454
2455/// Set the lowering for a value to the specified result. This came from a
2456/// possible folding, so check to see if we need to handle a constant.
2457LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2458 Value result) {
2459 // If this is a constant, check to see if we have it in our unique mapping:
2460 // it could have come from folding an operation.
2461 if (auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2462 auto &entry = hwConstantMap[cst.getValueAttr()];
2463 if (entry == cst) {
2464 // We're already using an entry in the constant map, nothing to do.
2465 } else if (entry) {
2466 // We already had this constant, reuse the one we have instead of the
2467 // one we just folded.
2468 result = entry;
2469 cst->erase();
2470 } else {
2471 // This is a new constant. Remember it!
2472 entry = cst;
2473 cst->moveBefore(&theModule.getBodyBlock()->front());
2474 }
2475 }
2476
2477 return setLowering(orig, result);
2478}
2479
2480/// Create a new operation with type ResultOpType and arguments CtorArgTypes,
2481/// then call setLowering with its result.
2482template <typename ResultOpType, typename... CtorArgTypes>
2483LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2484 CtorArgTypes... args) {
2485 auto result = builder.createOrFold<ResultOpType>(args...);
2486 if (auto *op = result.getDefiningOp())
2487 tryCopyName(op, orig);
2488 return setPossiblyFoldedLowering(orig->getResult(0), result);
2489}
2490
2491/// Create a new LTL operation with type ResultOpType and arguments
2492/// CtorArgTypes, then call setLowering with its result. Also add the operation
2493/// to the worklist of LTL ops that need to have their types fixed-up after the
2494/// lowering.
2495template <typename ResultOpType, typename... CtorArgTypes>
2496LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2497 CtorArgTypes... args) {
2498 auto result = builder.createOrFold<ResultOpType>(args...);
2499 if (auto *op = result.getDefiningOp())
2500 ltlOpFixupWorklist.insert(op);
2501 return setPossiblyFoldedLowering(orig->getResult(0), result);
2502}
2503
2504/// Creates a backedge of the specified result type. A backedge represents a
2505/// placeholder to be filled in later by a lowered value. If the backedge is not
2506/// updated with a real value by the end of the pass, it will be replaced with
2507/// an undriven wire. Backedges are allowed to be updated to other backedges.
2508/// If a chain of backedges forms a combinational loop, they will be replaced
2509/// with an undriven wire.
2510Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2511 auto backedge = backedgeBuilder.get(type, loc);
2512 backedges.insert({backedge, backedge});
2513 return backedge;
2514}
2515
2516/// Sets the lowering for a value to a backedge of the specified result type.
2517/// This is useful for lowering types which cannot pass through a wire, or to
2518/// directly materialize values in operations that violate the SSA dominance
2519/// constraint.
2520Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
2521 auto backedge = createBackedge(orig.getLoc(), type);
2522 (void)setLowering(orig, backedge);
2523 return backedge;
2524}
2525
2526/// If the `from` value is in fact a backedge, record that the backedge will
2527/// be replaced by the value. Return true if the destination is a backedge.
2528bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
2529 auto backedgeIt = backedges.find(dest);
2530 if (backedgeIt == backedges.end())
2531 return false;
2532 backedgeIt->second = src;
2533 return true;
2534}
2535
2536/// Switch the insertion point of the current builder to the end of the
2537/// specified block and run the closure. This correctly handles the case
2538/// where the closure is null, but the caller needs to make sure the block
2539/// exists.
2540void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
2541 const std::function<void(void)> &fn, Region &region) {
2542 if (!fn)
2543 return;
2544
2545 auto oldIP = builder.saveInsertionPoint();
2546
2547 builder.setInsertionPointToEnd(&region.front());
2548 fn();
2549 builder.restoreInsertionPoint(oldIP);
2550}
2551
2552/// Return a read value for the specified inout operation, auto-uniquing them.
2553Value FIRRTLLowering::getReadValue(Value v) {
2554 Value result = readInOutCreated.lookup(v);
2555 if (result)
2556 return result;
2557
2558 // Make sure to put the read value at the correct scope so it dominates all
2559 // future uses.
2560 auto oldIP = builder.saveInsertionPoint();
2561 if (auto *vOp = v.getDefiningOp()) {
2562 builder.setInsertionPointAfter(vOp);
2563 } else {
2564 // For reads of ports, just set the insertion point at the top of the
2565 // module.
2566 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
2567 }
2568
2569 // Instead of creating `ReadInOutOp` for `ArrayIndexInOutOp`, create
2570 // `ArrayGetOp` for root arrays.
2571 if (auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
2572 result = getReadValue(arrayIndexInout.getInput());
2573 result = builder.createOrFold<hw::ArrayGetOp>(result,
2574 arrayIndexInout.getIndex());
2575 } else {
2576 // Otherwise, create a read inout operation.
2577 result = builder.createOrFold<sv::ReadInOutOp>(v);
2578 }
2579 builder.restoreInsertionPoint(oldIP);
2580 readInOutCreated.insert({v, result});
2581 return result;
2582}
2583
2584Value FIRRTLLowering::getNonClockValue(Value v) {
2585 auto it = fromClockMapping.try_emplace(v, Value{});
2586 if (it.second) {
2587 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
2588 builder.setInsertionPointAfterValue(v);
2589 it.first->second = builder.create<seq::FromClockOp>(v);
2590 }
2591 return it.first->second;
2592}
2593
2594void FIRRTLLowering::addToAlwaysBlock(
2595 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
2596 sv::EventControl resetEdge, Value reset,
2597 const std::function<void(void)> &body,
2598 const std::function<void(void)> &resetBody) {
2599 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
2600 resetStyle, resetEdge, reset};
2601 sv::AlwaysOp alwaysOp;
2602 sv::IfOp insideIfOp;
2603 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
2604
2605 if (!alwaysOp) {
2606 if (reset) {
2607 assert(resetStyle != sv::ResetType::NoReset);
2608 // Here, we want to create the folloing structure with sv.always and
2609 // sv.if. If `reset` is async, we need to add `reset` to a sensitivity
2610 // list.
2611 //
2612 // sv.always @(clockEdge or reset) {
2613 // sv.if (reset) {
2614 // resetBody
2615 // } else {
2616 // body
2617 // }
2618 // }
2619
2620 auto createIfOp = [&]() {
2621 // It is weird but intended. Here we want to create an empty sv.if
2622 // with an else block.
2623 insideIfOp = builder.create<sv::IfOp>(
2624 reset, []() {}, []() {});
2625 };
2626 if (resetStyle == sv::ResetType::AsyncReset) {
2627 sv::EventControl events[] = {clockEdge, resetEdge};
2628 Value clocks[] = {clock, reset};
2629
2630 alwaysOp = builder.create<sv::AlwaysOp>(events, clocks, [&]() {
2631 if (resetEdge == sv::EventControl::AtNegEdge)
2632 llvm_unreachable("negative edge for reset is not expected");
2633 createIfOp();
2634 });
2635 } else {
2636 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
2637 }
2638 } else {
2639 assert(!resetBody);
2640 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock);
2641 insideIfOp = nullptr;
2642 }
2643 alwaysBlocks[key] = {alwaysOp, insideIfOp};
2644 }
2645
2646 if (reset) {
2647 assert(insideIfOp && "reset body must be initialized before");
2648 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
2649 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
2650 } else {
2651 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
2652 }
2653
2654 // Move the earlier always block(s) down to where the last would have been
2655 // inserted. This ensures that any values used by the always blocks are
2656 // defined ahead of the uses, which leads to better generated Verilog.
2657 alwaysOp->moveBefore(builder.getInsertionBlock(),
2658 builder.getInsertionPoint());
2659}
2660
2661LogicalResult FIRRTLLowering::emitGuards(Location loc,
2662 ArrayRef<Attribute> guards,
2663 std::function<void(void)> emit) {
2664 if (guards.empty()) {
2665 emit();
2666 return success();
2667 }
2668 auto guard = dyn_cast<StringAttr>(guards[0]);
2669 if (!guard)
2670 return mlir::emitError(loc,
2671 "elements in `guards` array must be `StringAttr`");
2672
2673 // Record the guard macro to emit a declaration for it.
2674 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
2675 LogicalResult result = LogicalResult::failure();
2676 addToIfDefBlock(guard.getValue(), [&]() {
2677 result = emitGuards(loc, guards.drop_front(), emit);
2678 });
2679 return result;
2680}
2681
2682void FIRRTLLowering::addToIfDefBlock(StringRef cond,
2683 std::function<void(void)> thenCtor,
2684 std::function<void(void)> elseCtor) {
2685 auto condAttr = builder.getStringAttr(cond);
2686 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
2687 if (op) {
2688 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
2689 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
2690
2691 // Move the earlier #ifdef block(s) down to where the last would have been
2692 // inserted. This ensures that any values used by the #ifdef blocks are
2693 // defined ahead of the uses, which leads to better generated Verilog.
2694 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2695 } else {
2696 ifdefBlocks[{builder.getBlock(), condAttr}] =
2697 builder.create<sv::IfDefOp>(condAttr, thenCtor, elseCtor);
2698 }
2699}
2700
2701void FIRRTLLowering::addToInitialBlock(std::function<void(void)> body) {
2702 auto op = initialBlocks.lookup(builder.getBlock());
2703 if (op) {
2704 runWithInsertionPointAtEndOfBlock(body, op.getBody());
2705
2706 // Move the earlier initial block(s) down to where the last would have
2707 // been inserted. This ensures that any values used by the initial blocks
2708 // are defined ahead of the uses, which leads to better generated Verilog.
2709 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2710 } else {
2711 initialBlocks[builder.getBlock()] = builder.create<sv::InitialOp>(body);
2712 }
2713}
2714
2715void FIRRTLLowering::addIfProceduralBlock(Value cond,
2716 std::function<void(void)> thenCtor,
2717 std::function<void(void)> elseCtor) {
2718 // Check to see if we already have an if on this condition immediately
2719 // before the insertion point. If so, extend it.
2720 auto insertIt = builder.getInsertionPoint();
2721 if (insertIt != builder.getBlock()->begin())
2722 if (auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
2723 if (ifOp.getCond() == cond) {
2724 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
2725 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
2726 return;
2727 }
2728 }
2729
2730 builder.create<sv::IfOp>(cond, thenCtor, elseCtor);
2731}
2732
2733//===----------------------------------------------------------------------===//
2734// Special Operations
2735//===----------------------------------------------------------------------===//
2736
2737/// Handle the case where an operation wasn't lowered. When this happens, the
2738/// operands should just be unlowered non-FIRRTL values. If the operand was
2739/// not lowered then leave it alone, otherwise we have a problem with
2740/// lowering.
2741///
2742FIRRTLLowering::UnloweredOpResult
2743FIRRTLLowering::handleUnloweredOp(Operation *op) {
2744 // FIRRTL operations must explicitly handle their regions.
2745 if (!op->getRegions().empty() && isa<FIRRTLDialect>(op->getDialect())) {
2746 op->emitOpError("must explicitly handle its regions");
2747 return LoweringFailure;
2748 }
2749
2750 // Simply pass through non-FIRRTL operations and consider them already
2751 // lowered. This allows us to handled partially lowered inputs, and also allow
2752 // other FIRRTL operations to spawn additional already-lowered operations,
2753 // like `hw.output`.
2754 if (!isa<FIRRTLDialect>(op->getDialect())) {
2755 // Push nested operations onto the worklist such that they are lowered.
2756 for (auto &region : op->getRegions())
2757 addToWorklist(region);
2758 for (auto &operand : op->getOpOperands())
2759 if (auto lowered = getPossiblyInoutLoweredValue(operand.get()))
2760 operand.set(lowered);
2761 for (auto result : op->getResults())
2762 (void)setLowering(result, result);
2763 return AlreadyLowered;
2764 }
2765
2766 // Ok, at least one operand got lowered, so this operation is using a FIRRTL
2767 // value, but wasn't itself lowered. This is because the lowering is
2768 // incomplete. This is either a bug or incomplete implementation.
2769 //
2770 // There is one aspect of incompleteness we intentionally expect: we allow
2771 // primitive operations that produce a zero bit result to be ignored by the
2772 // lowering logic. They don't have side effects, and handling this corner
2773 // case just complicates each of the lowering hooks. Instead, we just handle
2774 // them all right here.
2775 if (op->getNumResults() == 1) {
2776 auto resultType = op->getResult(0).getType();
2777 if (type_isa<FIRRTLBaseType>(resultType) &&
2778 isZeroBitFIRRTLType(resultType) &&
2779 (isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
2780 // Zero bit values lower to the null Value.
2781 (void)setLowering(op->getResult(0), Value());
2782 return NowLowered;
2783 }
2784 }
2785 op->emitOpError("LowerToHW couldn't handle this operation");
2786 return LoweringFailure;
2787}
2788
2789LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
2790 // Zero width values must be lowered to nothing.
2791 if (isZeroBitFIRRTLType(op.getType()))
2792 return setLowering(op, Value());
2793
2794 return setLowering(op, getOrCreateIntConstant(op.getValue()));
2795}
2796
2797LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
2798 Value cst;
2799 if (isa<ClockType>(op.getType())) {
2800 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
2801 : seq::ClockConst::Low);
2802 } else {
2803 cst = getOrCreateIntConstant(APInt(/*bitWidth*/ 1, op.getValue()));
2804 }
2805 return setLowering(op, cst);
2806}
2807
2808FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
2809 auto iIdx = getOrCreateIntConstant(
2811 firrtl::type_cast<FVectorType>(op.getInput().getType())
2812 .getNumElements()),
2813 op.getIndex());
2814
2815 // If the input has an inout type, we need to lower to ArrayIndexInOutOp;
2816 // otherwise hw::ArrayGetOp.
2817 Value result;
2818 if (isa<sv::InOutType>(input.getType()))
2819 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
2820 else
2821 result = builder.createOrFold<hw::ArrayGetOp>(input, iIdx);
2822 if (auto *definingOp = result.getDefiningOp())
2823 tryCopyName(definingOp, op);
2824 return result;
2825}
2826
2827FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
2828 Value valueIdx = getLoweredAndExtOrTruncValue(
2829 op.getIndex(),
2830 UIntType::get(op->getContext(),
2832 firrtl::type_cast<FVectorType>(op.getInput().getType())
2833 .getNumElements())));
2834 if (!valueIdx) {
2835 op->emitError() << "input lowering failed";
2836 return failure();
2837 }
2838
2839 // If the input has an inout type, we need to lower to ArrayIndexInOutOp;
2840 // otherwise, lower the op to array indexing.
2841 Value result;
2842 if (isa<sv::InOutType>(input.getType()))
2843 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
2844 else
2845 result = createArrayIndexing(input, valueIdx);
2846 if (auto *definingOp = result.getDefiningOp())
2847 tryCopyName(definingOp, op);
2848 return result;
2849}
2850
2851FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
2852 auto resultType = lowerType(op->getResult(0).getType());
2853 if (!resultType || !input) {
2854 op->emitError() << "subfield type lowering failed";
2855 return failure();
2856 }
2857
2858 // If the input has an inout type, we need to lower to StructFieldInOutOp;
2859 // otherwise, StructExtractOp.
2860 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
2861 .getElementName(op.getFieldIndex());
2862 Value result;
2863 if (isa<sv::InOutType>(input.getType()))
2864 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
2865 else
2866 result = builder.createOrFold<hw::StructExtractOp>(input, field);
2867 if (auto *definingOp = result.getDefiningOp())
2868 tryCopyName(definingOp, op);
2869 return result;
2870}
2871
2872LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
2873 if (isZeroBitFIRRTLType(op.getType()))
2874 return setLowering(op, Value());
2875
2876 auto input = getPossiblyInoutLoweredValue(op.getInput());
2877 if (!input)
2878 return op.emitError() << "input lowering failed";
2879
2880 auto result = lowerSubindex(op, input);
2881 if (failed(result))
2882 return failure();
2883 return setLowering(op, *result);
2884}
2885
2886LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
2887 if (isZeroBitFIRRTLType(op.getType()))
2888 return setLowering(op, Value());
2889
2890 auto input = getPossiblyInoutLoweredValue(op.getInput());
2891 if (!input)
2892 return op.emitError() << "input lowering failed";
2893
2894 auto result = lowerSubaccess(op, input);
2895 if (failed(result))
2896 return failure();
2897 return setLowering(op, *result);
2898}
2899
2900LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
2901 // firrtl.mem lowering lowers some SubfieldOps. Zero-width can leave
2902 // invalid subfield accesses
2903 if (getLoweredValue(op) || !op.getInput())
2904 return success();
2905
2906 if (isZeroBitFIRRTLType(op.getType()))
2907 return setLowering(op, Value());
2908
2909 auto input = getPossiblyInoutLoweredValue(op.getInput());
2910 if (!input)
2911 return op.emitError() << "input lowering failed";
2912
2913 auto result = lowerSubfield(op, input);
2914 if (failed(result))
2915 return failure();
2916 return setLowering(op, *result);
2917}
2918
2919LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
2920 auto resultType = lowerType(op.getResult().getType());
2921 SmallVector<Value> operands;
2922 // NOTE: The operand order must be inverted.
2923 for (auto oper : llvm::reverse(op.getOperands())) {
2924 auto val = getLoweredValue(oper);
2925 if (!val)
2926 return failure();
2927 operands.push_back(val);
2928 }
2929 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
2930}
2931
2932LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
2933 auto resultType = lowerType(op.getResult().getType());
2934 SmallVector<Value> operands;
2935 for (auto oper : op.getOperands()) {
2936 auto val = getLoweredValue(oper);
2937 if (!val)
2938 return failure();
2939 operands.push_back(val);
2940 }
2941 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
2942}
2943
2944LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
2945 // Zero width values must be lowered to nothing.
2946 if (isZeroBitFIRRTLType(op.getType()))
2947 return setLowering(op, Value());
2948
2949 auto input = getLoweredValue(op.getInput());
2950 auto tagName = op.getFieldNameAttr();
2951 auto type = lowerType(op.getType());
2952
2953 if (auto structType = dyn_cast<hw::StructType>(type)) {
2954 auto enumType = structType.getFieldType("tag");
2955 auto enumAttr = hw::EnumFieldAttr::get(op.getLoc(), tagName, enumType);
2956 auto enumOp = builder.create<hw::EnumConstantOp>(enumAttr);
2957 auto unionType = structType.getFieldType("body");
2958 auto unionOp = builder.create<hw::UnionCreateOp>(unionType, tagName, input);
2959 SmallVector<Value> operands = {enumOp.getResult(), unionOp.getResult()};
2960 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
2961 }
2962
2963 return setLoweringTo<hw::EnumConstantOp>(
2964 op, hw::EnumFieldAttr::get(op.getLoc(), tagName, type));
2965}
2966
2967LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
2968 auto resultType = lowerType(op.getResult().getType());
2969 auto attr =
2970 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
2971
2972 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
2973 cast<ArrayAttr>(attr));
2974}
2975
2976LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
2977 auto tagName = op.getFieldNameAttr();
2978 auto lhs = getLoweredValue(op.getInput());
2979 if (isa<hw::StructType>(lhs.getType()))
2980 lhs = builder.create<hw::StructExtractOp>(lhs, "tag");
2981 auto enumField = hw::EnumFieldAttr::get(op.getLoc(), tagName, lhs.getType());
2982 auto rhs = builder.create<hw::EnumConstantOp>(enumField);
2983 return setLoweringTo<hw::EnumCmpOp>(op, lhs, rhs);
2984}
2985
2986LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
2987 // Zero width values must be lowered to nothing.
2988 if (isZeroBitFIRRTLType(op.getType()))
2989 return setLowering(op, Value());
2990
2991 auto tagName = op.getFieldNameAttr();
2992 auto input = getLoweredValue(op.getInput());
2993 auto field = builder.create<hw::StructExtractOp>(input, "body");
2994 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
2995}
2996
2997//===----------------------------------------------------------------------===//
2998// Declarations
2999//===----------------------------------------------------------------------===//
3000
3001LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3002 auto origResultType = op.getResult().getType();
3003
3004 // Foreign types lower to a backedge that needs to be resolved by a later
3005 // connect op.
3006 if (!type_isa<FIRRTLType>(origResultType)) {
3007 createBackedge(op.getResult(), origResultType);
3008 return success();
3009 }
3010
3011 auto resultType = lowerType(origResultType);
3012 if (!resultType)
3013 return failure();
3014
3015 if (resultType.isInteger(0)) {
3016 if (op.getInnerSym())
3017 return op.emitError("zero width wire is referenced by name [")
3018 << *op.getInnerSym() << "] (e.g. in an XMR) but must be removed";
3019 return setLowering(op.getResult(), Value());
3020 }
3021
3022 // Name attr is required on sv.wire but optional on firrtl.wire.
3023 auto innerSym = lowerInnerSymbol(op);
3024 auto name = op.getNameAttr();
3025 // This is not a temporary wire created by the compiler, so attach a symbol
3026 // name.
3027 auto wire = builder.create<hw::WireOp>(
3028 op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3029
3030 if (auto svAttrs = sv::getSVAttributes(op))
3031 sv::setSVAttributes(wire, svAttrs);
3032
3033 return setLowering(op.getResult(), wire);
3034}
3035
3036LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3037 auto resultTy = lowerType(op.getType());
3038 if (!resultTy)
3039 return failure();
3040 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3041
3042 SmallVector<Value, 4> operands;
3043 operands.reserve(op.getSubstitutions().size());
3044 for (auto operand : op.getSubstitutions()) {
3045 auto lowered = getLoweredValue(operand);
3046 if (!lowered)
3047 return failure();
3048 operands.push_back(lowered);
3049 }
3050
3051 ArrayAttr symbols = op.getSymbolsAttr();
3052 if (!symbols)
3053 symbols = ArrayAttr::get(op.getContext(), {});
3054
3055 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3056 operands, symbols);
3057}
3058
3059LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3060 auto operand = getLoweredValue(op.getInput());
3061 if (!operand)
3062 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3063 if (op.getInnerSym())
3064 return op.emitError("zero width node is referenced by name [")
3065 << *op.getInnerSym()
3066 << "] (e.g. in an XMR) but must be "
3067 "removed";
3068 return setLowering(op.getResult(), Value());
3069 });
3070
3071 // Node operations are logical noops, but may carry annotations or be
3072 // referred to through an inner name. If a don't touch is present, ensure
3073 // that we have a symbol name so we can keep the node as a wire.
3074 auto name = op.getNameAttr();
3075 auto innerSym = lowerInnerSymbol(op);
3076
3077 if (innerSym)
3078 operand = builder.create<hw::WireOp>(operand, name, innerSym);
3079
3080 // Move SV attributes.
3081 if (auto svAttrs = sv::getSVAttributes(op)) {
3082 if (!innerSym)
3083 operand = builder.create<hw::WireOp>(operand, name);
3084 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3085 }
3086
3087 return setLowering(op.getResult(), operand);
3088}
3089
3090LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3091 auto resultType = lowerType(op.getResult().getType());
3092 if (!resultType)
3093 return failure();
3094 if (resultType.isInteger(0))
3095 return setLowering(op.getResult(), Value());
3096
3097 Value clockVal = getLoweredValue(op.getClockVal());
3098 if (!clockVal)
3099 return failure();
3100
3101 // Create a reg op, wiring itself to its input.
3102 auto innerSym = lowerInnerSymbol(op);
3103 Backedge inputEdge = backedgeBuilder.get(resultType);
3104 auto reg = builder.create<seq::FirRegOp>(inputEdge, clockVal,
3105 op.getNameAttr(), innerSym);
3106
3107 // Pass along the start and end random initialization bits for this register.
3108 if (auto randomRegister = op->getAttr("firrtl.random_init_register"))
3109 reg->setAttr("firrtl.random_init_register", randomRegister);
3110 if (auto randomStart = op->getAttr("firrtl.random_init_start"))
3111 reg->setAttr("firrtl.random_init_start", randomStart);
3112 if (auto randomEnd = op->getAttr("firrtl.random_init_end"))
3113 reg->setAttr("firrtl.random_init_end", randomEnd);
3114
3115 // Move SV attributes.
3116 if (auto svAttrs = sv::getSVAttributes(op))
3117 sv::setSVAttributes(reg, svAttrs);
3118
3119 inputEdge.setValue(reg);
3120 (void)setLowering(op.getResult(), reg);
3121 return success();
3122}
3123
3124LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3125 auto resultType = lowerType(op.getResult().getType());
3126 if (!resultType)
3127 return failure();
3128 if (resultType.isInteger(0))
3129 return setLowering(op.getResult(), Value());
3130
3131 Value clockVal = getLoweredValue(op.getClockVal());
3132 Value resetSignal = getLoweredValue(op.getResetSignal());
3133 // Reset values may be narrower than the register. Extend appropriately.
3134 Value resetValue = getLoweredAndExtOrTruncValue(
3135 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3136
3137 if (!clockVal || !resetSignal || !resetValue)
3138 return failure();
3139
3140 // Create a reg op, wiring itself to its input.
3141 auto innerSym = lowerInnerSymbol(op);
3142 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3143 Backedge inputEdge = backedgeBuilder.get(resultType);
3144 auto reg =
3145 builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
3146 resetSignal, resetValue, innerSym, isAsync);
3147
3148 // Pass along the start and end random initialization bits for this register.
3149 if (auto randomRegister = op->getAttr("firrtl.random_init_register"))
3150 reg->setAttr("firrtl.random_init_register", randomRegister);
3151 if (auto randomStart = op->getAttr("firrtl.random_init_start"))
3152 reg->setAttr("firrtl.random_init_start", randomStart);
3153 if (auto randomEnd = op->getAttr("firrtl.random_init_end"))
3154 reg->setAttr("firrtl.random_init_end", randomEnd);
3155
3156 // Move SV attributes.
3157 if (auto svAttrs = sv::getSVAttributes(op))
3158 sv::setSVAttributes(reg, svAttrs);
3159
3160 inputEdge.setValue(reg);
3161 (void)setLowering(op.getResult(), reg);
3162
3163 return success();
3164}
3165
3166LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3167 // TODO: Remove this restriction and preserve aggregates in
3168 // memories.
3169 if (type_isa<BundleType>(op.getDataType()))
3170 return op.emitOpError(
3171 "should have already been lowered from a ground type to an aggregate "
3172 "type using the LowerTypes pass. Use "
3173 "'firtool --lower-types' or 'circt-opt "
3174 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3175 "to run this.");
3176
3177 FirMemory memSummary = op.getSummary();
3178
3179 // Create the memory declaration.
3180 auto memType = seq::FirMemType::get(
3181 op.getContext(), memSummary.depth, memSummary.dataWidth,
3182 memSummary.isMasked ? std::optional<uint32_t>(memSummary.maskBits)
3183 : std::optional<uint32_t>());
3184
3185 seq::FirMemInitAttr memInit;
3186 if (auto init = op.getInitAttr())
3187 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3188 init.getIsBinary(), init.getIsInline());
3189
3190 auto memDecl = builder.create<seq::FirMemOp>(
3191 memType, memSummary.readLatency, memSummary.writeLatency,
3192 memSummary.readUnderWrite, memSummary.writeUnderWrite, op.getNameAttr(),
3193 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3194
3195 // If the module is outside the DUT, set the appropriate output directory for
3196 // the memory.
3197 if (!circuitState.isInDUT(theModule))
3198 if (auto testBenchDir = circuitState.getTestBenchDirectory())
3199 memDecl.setOutputFileAttr(testBenchDir);
3200
3201 // Memories return multiple structs, one for each port, which means we
3202 // have two layers of type to split apart.
3203 for (size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3204
3205 auto addOutput = [&](StringRef field, size_t width, Value value) {
3206 for (auto &a : getAllFieldAccesses(op.getResult(i), field)) {
3207 if (width > 0)
3208 (void)setLowering(a, value);
3209 else
3210 a->eraseOperand(0);
3211 }
3212 };
3213
3214 auto addInput = [&](StringRef field, Value backedge) {
3215 for (auto a : getAllFieldAccesses(op.getResult(i), field)) {
3216 if (cast<FIRRTLBaseType>(a.getType())
3217 .getPassiveType()
3218 .getBitWidthOrSentinel() > 0)
3219 (void)setLowering(a, backedge);
3220 else
3221 a->eraseOperand(0);
3222 }
3223 };
3224
3225 auto addInputPort = [&](StringRef field, size_t width) -> Value {
3226 // If the memory is 0-width, do not materialize any connections to it.
3227 // However, `seq.firmem` now requires a 1-bit input, so materialize
3228 // a dummy x value to provide it with.
3229 Value backedge, portValue;
3230 if (width == 0) {
3231 portValue = getOrCreateXConstant(1);
3232 } else {
3233 auto portType = IntegerType::get(op.getContext(), width);
3234 backedge = portValue = createBackedge(builder.getLoc(), portType);
3235 }
3236 addInput(field, backedge);
3237 return portValue;
3238 };
3239
3240 auto addClock = [&](StringRef field) -> Value {
3241 Type clockTy = seq::ClockType::get(op.getContext());
3242 Value portValue = createBackedge(builder.getLoc(), clockTy);
3243 addInput(field, portValue);
3244 return portValue;
3245 };
3246
3247 auto memportKind = op.getPortKind(i);
3248 if (memportKind == MemOp::PortKind::Read) {
3249 auto addr = addInputPort("addr", op.getAddrBits());
3250 auto en = addInputPort("en", 1);
3251 auto clk = addClock("clk");
3252 auto data = builder.create<seq::FirMemReadOp>(memDecl, addr, clk, en);
3253 addOutput("data", memSummary.dataWidth, data);
3254 } else if (memportKind == MemOp::PortKind::ReadWrite) {
3255 auto addr = addInputPort("addr", op.getAddrBits());
3256 auto en = addInputPort("en", 1);
3257 auto clk = addClock("clk");
3258 // If maskBits =1, then And the mask field with enable, and update the
3259 // enable. Else keep mask port.
3260 auto mode = addInputPort("wmode", 1);
3261 if (!memSummary.isMasked)
3262 mode = builder.createOrFold<comb::AndOp>(mode, addInputPort("wmask", 1),
3263 true);
3264 auto wdata = addInputPort("wdata", memSummary.dataWidth);
3265 // Ignore mask port, if maskBits =1
3266 Value mask;
3267 if (memSummary.isMasked)
3268 mask = addInputPort("wmask", memSummary.maskBits);
3269 auto rdata = builder.create<seq::FirMemReadWriteOp>(
3270 memDecl, addr, clk, en, wdata, mode, mask);
3271 addOutput("rdata", memSummary.dataWidth, rdata);
3272 } else {
3273 auto addr = addInputPort("addr", op.getAddrBits());
3274 // If maskBits =1, then And the mask field with enable, and update the
3275 // enable. Else keep mask port.
3276 auto en = addInputPort("en", 1);
3277 if (!memSummary.isMasked)
3278 en = builder.createOrFold<comb::AndOp>(en, addInputPort("mask", 1),
3279 true);
3280 auto clk = addClock("clk");
3281 auto data = addInputPort("data", memSummary.dataWidth);
3282 // Ignore mask port, if maskBits =1
3283 Value mask;
3284 if (memSummary.isMasked)
3285 mask = addInputPort("mask", memSummary.maskBits);
3286 builder.create<seq::FirMemWriteOp>(memDecl, addr, clk, en, data, mask);
3287 }
3288 }
3289
3290 return success();
3291}
3292
3293LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3294 Operation *oldModule =
3295 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3296
3297 auto *newModule = circuitState.getNewModule(oldModule);
3298 if (!newModule) {
3299 oldInstance->emitOpError("could not find module [")
3300 << oldInstance.getModuleName() << "] referenced by instance";
3301 return failure();
3302 }
3303
3304 // If this is a referenced to a parameterized extmodule, then bring the
3305 // parameters over to this instance.
3306 ArrayAttr parameters;
3307 if (auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3308 parameters = getHWParameters(oldExtModule, /*ignoreValues=*/false);
3309
3310 // Decode information about the input and output ports on the referenced
3311 // module.
3312 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3313
3314 // Build an index from the name attribute to an index into portInfo, so we
3315 // can do efficient lookups.
3317 for (unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3318 portIndicesByName[portInfo[portIdx].name] = portIdx;
3319
3320 // Ok, get ready to create the new instance operation. We need to prepare
3321 // input operands.
3322 SmallVector<Value, 8> operands;
3323 for (size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3324 auto &port = portInfo[portIndex];
3325 auto portType = lowerType(port.type);
3326 if (!portType) {
3327 oldInstance->emitOpError("could not lower type of port ") << port.name;
3328 return failure();
3329 }
3330
3331 // Drop zero bit input/inout ports.
3332 if (portType.isInteger(0))
3333 continue;
3334
3335 // We wire outputs up after creating the instance.
3336 if (port.isOutput())
3337 continue;
3338
3339 auto portResult = oldInstance.getResult(portIndex);
3340 assert(portResult && "invalid IR, couldn't find port");
3341
3342 // Replace the input port with a backedge. If it turns out that this port
3343 // is never driven, an uninitialized wire will be materialized at the end.
3344 if (port.isInput()) {
3345 operands.push_back(createBackedge(portResult, portType));
3346 continue;
3347 }
3348
3349 // If the result has an analog type and is used only by attach op, try
3350 // eliminating a temporary wire by directly using an attached value.
3351 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3352 if (auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3353 if (auto source = getSingleNonInstanceOperand(attach)) {
3354 auto loweredResult = getPossiblyInoutLoweredValue(source);
3355 operands.push_back(loweredResult);
3356 (void)setLowering(portResult, loweredResult);
3357 continue;
3358 }
3359 }
3360 }
3361
3362 // Create a wire for each inout operand, so there is something to connect
3363 // to. The instance becomes the sole driver of this wire.
3364 auto wire = builder.create<sv::WireOp>(
3365 portType, "." + port.getName().str() + ".wire");
3366
3367 // Know that the argument FIRRTL value is equal to this wire, allowing
3368 // connects to it to be lowered.
3369 (void)setLowering(portResult, wire);
3370
3371 operands.push_back(wire);
3372 }
3373
3374 // If this instance is destined to be lowered to a bind, generate a symbol
3375 // for it and generate a bind op. Enter the bind into global
3376 // CircuitLoweringState so that this can be moved outside of module once
3377 // we're guaranteed to not be a parallel context.
3378 auto innerSym = oldInstance.getInnerSymAttr();
3379 if (oldInstance.getLowerToBind()) {
3380 if (!innerSym)
3381 std::tie(innerSym, std::ignore) = getOrAddInnerSym(
3382 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3383 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
3384
3385 auto bindOp = builder.create<sv::BindOp>(theModule.getNameAttr(),
3386 innerSym.getSymName());
3387 // If the lowered op already had output file information, then use that.
3388 // Otherwise, generate some default bind information.
3389 if (auto outputFile = oldInstance->getAttr("output_file"))
3390 bindOp->setAttr("output_file", outputFile);
3391 // Add the bind to the circuit state. This will be moved outside of the
3392 // encapsulating module after all modules have been processed in parallel.
3393 circuitState.addBind(bindOp);
3394 }
3395
3396 // Create the new hw.instance operation.
3397 auto newInstance = builder.create<hw::InstanceOp>(
3398 newModule, oldInstance.getNameAttr(), operands, parameters, innerSym);
3399
3400 if (oldInstance.getLowerToBind())
3401 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3402
3403 if (newInstance.getInnerSymAttr())
3404 if (auto forceName = circuitState.instanceForceNames.lookup(
3405 {cast<hw::HWModuleOp>(newInstance->getParentOp()).getNameAttr(),
3406 newInstance.getInnerNameAttr()}))
3407 newInstance->setAttr("hw.verilogName", forceName);
3408
3409 // Now that we have the new hw.instance, we need to remap all of the users
3410 // of the outputs/results to the values returned by the instance.
3411 unsigned resultNo = 0;
3412 for (size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3413 auto &port = portInfo[portIndex];
3414 if (!port.isOutput() || isZeroBitFIRRTLType(port.type))
3415 continue;
3416
3417 Value resultVal = newInstance.getResult(resultNo);
3418
3419 auto oldPortResult = oldInstance.getResult(portIndex);
3420 (void)setLowering(oldPortResult, resultVal);
3421 ++resultNo;
3422 }
3423 return success();
3424}
3425
3426LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
3427 SmallVector<Value> inputs;
3428 SmallVector<Type> types;
3429 for (auto input : oldOp.getInputs()) {
3430 auto lowered = getLoweredValue(input);
3431 if (!lowered)
3432 return failure();
3433 inputs.push_back(lowered);
3434 types.push_back(lowered.getType());
3435 }
3436
3437 auto newOp = builder.create<verif::ContractOp>(types, inputs);
3438 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
3439 auto &body = newOp.getBody().emplaceBlock();
3440
3441 for (auto [newResult, oldResult, oldArg] :
3442 llvm::zip(newOp.getResults(), oldOp.getResults(),
3443 oldOp.getBody().getArguments())) {
3444 if (failed(setLowering(oldResult, newResult)))
3445 return failure();
3446 if (failed(setLowering(oldArg, newResult)))
3447 return failure();
3448 }
3449
3450 body.getOperations().splice(body.end(),
3451 oldOp.getBody().front().getOperations());
3452 addToWorklist(body);
3453
3454 return success();
3455}
3456
3457//===----------------------------------------------------------------------===//
3458// Unary Operations
3459//===----------------------------------------------------------------------===//
3460
3461// Lower a cast that is a noop at the HW level.
3462LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3463 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3464 if (!operand)
3465 return failure();
3466
3467 // Noop cast.
3468 return setLowering(op->getResult(0), operand);
3469}
3470
3471LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3472 if (isa<ClockType>(op.getInput().getType()))
3473 return setLowering(op->getResult(0),
3474 getLoweredNonClockValue(op.getInput()));
3475 return lowerNoopCast(op);
3476}
3477
3478LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3479 if (isa<ClockType>(op.getInput().getType()))
3480 return setLowering(op->getResult(0),
3481 getLoweredNonClockValue(op.getInput()));
3482 return lowerNoopCast(op);
3483}
3484
3485LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3486 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3487}
3488
3489LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
3490 mlir::UnrealizedConversionCastOp op) {
3491 // General lowering for non-unary casts.
3492 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3493 return failure();
3494
3495 auto operand = op.getOperand(0);
3496 auto result = op.getResult(0);
3497
3498 // FIRRTL -> FIRRTL
3499 if (type_isa<FIRRTLType>(operand.getType()) &&
3500 type_isa<FIRRTLType>(result.getType()))
3501 return lowerNoopCast(op);
3502
3503 // other -> FIRRTL
3504 // other -> other
3505 if (!type_isa<FIRRTLType>(operand.getType())) {
3506 if (type_isa<FIRRTLType>(result.getType()))
3507 return setLowering(result, getPossiblyInoutLoweredValue(operand));
3508 return failure(); // general foreign op lowering for other -> other
3509 }
3510
3511 // FIRRTL -> other
3512 // Otherwise must be a conversion from FIRRTL type to standard type.
3513 auto loweredResult = getLoweredValue(operand);
3514 if (!loweredResult) {
3515 // If this is a conversion from a zero bit HW type to firrtl value, then
3516 // we want to successfully lower this to a null Value.
3517 if (operand.getType().isSignlessInteger(0)) {
3518 return setLowering(result, Value());
3519 }
3520 return failure();
3521 }
3522
3523 // We lower builtin.unrealized_conversion_cast converting from a firrtl type
3524 // to a standard type into the lowered operand.
3525 result.replaceAllUsesWith(loweredResult);
3526 return success();
3527}
3528
3529LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3530 // Conversions from hw struct types to FIRRTL types are lowered as the
3531 // input operand.
3532 if (auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
3533 return setLowering(op, op.getOperand());
3534
3535 // Otherwise must be a conversion from FIRRTL bundle type to hw struct
3536 // type.
3537 auto result = getLoweredValue(op.getOperand());
3538 if (!result)
3539 return failure();
3540
3541 // We lower firrtl.stdStructCast converting from a firrtl bundle to an hw
3542 // struct type into the lowered operand.
3543 op.replaceAllUsesWith(result);
3544 return success();
3545}
3546
3547LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3548 auto operand = getLoweredValue(op.getOperand());
3549 if (!operand)
3550 return failure();
3551 auto resultType = lowerType(op.getType());
3552 if (!resultType)
3553 return failure();
3554
3555 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3556}
3557
3558LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3559 auto operand = getLoweredValue(op.getOperand());
3560 if (!operand) {
3561 return handleZeroBit(op.getOperand(), [&]() {
3562 // Unsigned zero bit to Signed is 1b0.
3563 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3564 return setLowering(op, getOrCreateIntConstant(1, 0));
3565 // Signed->Signed is a zero bit value.
3566 return setLowering(op, Value());
3567 });
3568 }
3569
3570 // Signed to signed is a noop.
3571 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3572 return setLowering(op, operand);
3573
3574 // Otherwise prepend a zero bit.
3575 auto zero = getOrCreateIntConstant(1, 0);
3576 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3577}
3578
3579LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3580 auto operand = getLoweredValue(op.getInput());
3581 if (!operand)
3582 return failure();
3583 // ~x ---> x ^ 0xFF
3584 auto allOnes = getOrCreateIntConstant(
3585 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3586 return setLoweringTo<comb::XorOp>(op, operand, allOnes, true);
3587}
3588
3589LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3590 // FIRRTL negate always adds a bit.
3591 // -x ---> 0-sext(x) or 0-zext(x)
3592 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3593 if (!operand)
3594 return failure();
3595
3596 auto resultType = lowerType(op.getType());
3597
3598 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3599 return setLoweringTo<comb::SubOp>(op, zero, operand, true);
3600}
3601
3602// Pad is a noop or extension operation.
3603LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3604 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3605 if (!operand)
3606 return failure();
3607 return setLowering(op, operand);
3608}
3609
3610LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3611 auto operand = getLoweredValue(op.getInput());
3612 if (!operand) {
3613 return handleZeroBit(op.getInput(), [&]() {
3614 return setLowering(op, getOrCreateIntConstant(1, 0));
3615 });
3616 return failure();
3617 }
3618
3619 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
3620 true);
3621}
3622
3623LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3624 auto operand = getLoweredValue(op.getInput());
3625 if (!operand) {
3626 return handleZeroBit(op.getInput(), [&]() {
3627 return setLowering(op, getOrCreateIntConstant(1, 1));
3628 });
3629 }
3630
3631 // Lower AndR to == -1
3632 return setLoweringTo<comb::ICmpOp>(
3633 op, ICmpPredicate::eq, operand,
3634 getOrCreateIntConstant(
3635 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3636 true);
3637}
3638
3639LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3640 auto operand = getLoweredValue(op.getInput());
3641 if (!operand) {
3642 return handleZeroBit(op.getInput(), [&]() {
3643 return setLowering(op, getOrCreateIntConstant(1, 0));
3644 });
3645 return failure();
3646 }
3647
3648 // Lower OrR to != 0
3649 return setLoweringTo<comb::ICmpOp>(
3650 op, ICmpPredicate::ne, operand,
3651 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3652 true);
3653}
3654
3655//===----------------------------------------------------------------------===//
3656// Binary Operations
3657//===----------------------------------------------------------------------===//
3658
3659template <typename ResultOpType>
3660LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3661 auto resultType = op->getResult(0).getType();
3662 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3663 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3664 if (!lhs || !rhs)
3665 return failure();
3666
3667 return setLoweringTo<ResultOpType>(op, lhs, rhs, true);
3668}
3669
3670/// Element-wise logical operations can be lowered into bitcast and normal comb
3671/// operations. Eventually we might want to introduce elementwise operations
3672/// into HW/SV level as well.
3673template <typename ResultOpType>
3674LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3675 auto resultType = op->getResult(0).getType();
3676 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3677 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3678
3679 if (!lhs || !rhs)
3680 return failure();
3681 auto bitwidth = firrtl::getBitWidth(type_cast<FIRRTLBaseType>(resultType));
3682
3683 if (!bitwidth)
3684 return failure();
3685
3686 // TODO: Introduce elementwise operations to HW dialect instead of abusing
3687 // bitcast operations.
3688 auto intType = builder.getIntegerType(*bitwidth);
3689 auto retType = lhs.getType();
3690 lhs = builder.createOrFold<hw::BitcastOp>(intType, lhs);
3691 rhs = builder.createOrFold<hw::BitcastOp>(intType, rhs);
3692 auto result = builder.createOrFold<ResultOpType>(lhs, rhs, /*twoState=*/true);
3693 return setLoweringTo<hw::BitcastOp>(op, retType, result);
3694}
3695
3696/// lowerBinOp extends each operand to the destination type, then performs the
3697/// specified binary operator.
3698template <typename ResultUnsignedOpType, typename ResultSignedOpType>
3699LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
3700 // Extend the two operands to match the destination type.
3701 auto resultType = op->getResult(0).getType();
3702 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3703 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3704 if (!lhs || !rhs)
3705 return failure();
3706
3707 // Emit the result operation.
3708 if (type_cast<IntType>(resultType).isSigned())
3709 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs, true);
3710 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs, true);
3711}
3712
3713/// lowerCmpOp extends each operand to the longest type, then performs the
3714/// specified binary operator.
3715LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
3716 ICmpPredicate unsignedOp) {
3717 // Extend the two operands to match the longest type.
3718 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
3719 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
3720 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
3721 return failure();
3722
3723 auto cmpType = getWidestIntType(lhsIntType, rhsIntType);
3724 if (cmpType.getWidth() == 0) // Handle 0-width inputs by promoting to 1 bit.
3725 cmpType = UIntType::get(builder.getContext(), 1);
3726 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
3727 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
3728 if (!lhs || !rhs)
3729 return failure();
3730
3731 // Emit the result operation.
3732 Type resultType = builder.getIntegerType(1);
3733 return setLoweringTo<comb::ICmpOp>(
3734 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
3735 true);
3736}
3737
3738/// Lower a divide or dynamic shift, where the operation has to be performed
3739/// in the widest type of the result and two inputs then truncated down.
3740template <typename SignedOp, typename UnsignedOp>
3741LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
3742 // hw has equal types for these, firrtl doesn't. The type of the firrtl
3743 // RHS may be wider than the LHS, and we cannot truncate off the high bits
3744 // (because an overlarge amount is supposed to shift in sign or zero bits).
3745 auto opType = type_cast<IntType>(op->getResult(0).getType());
3746 if (opType.getWidth() == 0)
3747 return setLowering(op->getResult(0), Value());
3748
3749 auto resultType = getWidestIntType(opType, op->getOperand(1).getType());
3750 resultType = getWidestIntType(resultType, op->getOperand(0).getType());
3751 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3752 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3753 if (!lhs || !rhs)
3754 return failure();
3755
3756 Value result;
3757 if (opType.isSigned())
3758 result = builder.createOrFold<SignedOp>(lhs, rhs, true);
3759 else
3760 result = builder.createOrFold<UnsignedOp>(lhs, rhs, true);
3761
3762 if (auto *definingOp = result.getDefiningOp())
3763 tryCopyName(definingOp, op);
3764
3765 if (resultType == opType)
3766 return setLowering(op->getResult(0), result);
3767 return setLoweringTo<comb::ExtractOp>(op, lowerType(opType), result, 0);
3768}
3769
3770LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
3771 auto lhs = getLoweredValue(op.getLhs());
3772 auto rhs = getLoweredValue(op.getRhs());
3773 if (!lhs) {
3774 return handleZeroBit(op.getLhs(), [&]() {
3775 if (rhs) // cat(0bit, x) --> x
3776 return setLowering(op, rhs);
3777 // cat(0bit, 0bit) --> 0bit
3778 return handleZeroBit(op.getRhs(),
3779 [&]() { return setLowering(op, Value()); });
3780 });
3781 }
3782
3783 if (!rhs) // cat(x, 0bit) --> x
3784 return handleZeroBit(op.getRhs(), [&]() { return setLowering(op, lhs); });
3785
3786 return setLoweringTo<comb::ConcatOp>(op, lhs, rhs);
3787}
3788
3789//===----------------------------------------------------------------------===//
3790// Verif Operations
3791//===----------------------------------------------------------------------===//
3792
3793LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
3794 auto input = getLoweredNonClockValue(op.getArg());
3795 if (!input)
3796 return failure();
3797
3798 if (!isa<IntType>(input.getType())) {
3799 auto srcType = op.getArg().getType();
3800 auto bitwidth = firrtl::getBitWidth(type_cast<FIRRTLBaseType>(srcType));
3801 assert(bitwidth && "Unknown width");
3802 auto intType = builder.getIntegerType(*bitwidth);
3803 input = builder.createOrFold<hw::BitcastOp>(intType, input);
3804 }
3805
3806 return setLoweringTo<comb::ICmpOp>(
3807 op, ICmpPredicate::ceq, input,
3808 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()), true);
3809}
3810
3811LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
3812 auto operand = getLoweredValue(op.getInput());
3813 builder.create<hw::WireOp>(operand);
3814 return success();
3815}
3816
3817LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
3818 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
3819 op.getFormatStringAttr());
3820}
3821
3822LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
3823 auto type = lowerType(op.getResult().getType());
3824 if (!type)
3825 return failure();
3826
3827 auto valueOp = builder.create<sim::PlusArgsValueOp>(
3828 builder.getIntegerType(1), type, op.getFormatStringAttr());
3829 if (failed(setLowering(op.getResult(), valueOp.getResult())))
3830 return failure();
3831 if (failed(setLowering(op.getFound(), valueOp.getFound())))
3832 return failure();
3833 return success();
3834}
3835
3836LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
3837 op.emitError("SizeOf should have been resolved.");
3838 return failure();
3839}
3840
3841LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
3842 Value testEnable;
3843 if (op.getTestEnable())
3844 testEnable = getLoweredValue(op.getTestEnable());
3845 return setLoweringTo<seq::ClockGateOp>(
3846 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
3847 testEnable, /*inner_sym=*/hw::InnerSymAttr{});
3848}
3849
3850LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
3851 auto operand = getLoweredValue(op.getInput());
3852 return setLoweringTo<seq::ClockInverterOp>(op, operand);
3853}
3854
3855LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
3856 auto operand = getLoweredValue(op.getInput());
3857 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
3858}
3859
3860LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
3861 return setLoweringToLTL<ltl::AndOp>(
3862 op,
3863 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3864}
3865
3866LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
3867 return setLoweringToLTL<ltl::OrOp>(
3868 op,
3869 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3870}
3871
3872LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
3873 return setLoweringToLTL<ltl::IntersectOp>(
3874 op,
3875 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3876}
3877
3878LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
3879 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
3880 op.getDelayAttr(), op.getLengthAttr());
3881}
3882
3883LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
3884 return setLoweringToLTL<ltl::ConcatOp>(
3885 op,
3886 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3887}
3888
3889LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
3890 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
3891 op.getBaseAttr(), op.getMoreAttr());
3892}
3893
3894LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
3895 return setLoweringToLTL<ltl::GoToRepeatOp>(
3896 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
3897}
3898
3899LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
3900 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
3901 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
3902}
3903
3904LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
3905 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
3906}
3907
3908LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
3909 return setLoweringToLTL<ltl::ImplicationOp>(
3910 op,
3911 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3912}
3913
3914LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
3915 return setLoweringToLTL<ltl::UntilOp>(
3916 op,
3917 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3918}
3919
3920LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
3921 return setLoweringToLTL<ltl::EventuallyOp>(op,
3922 getLoweredValue(op.getInput()));
3923}
3924
3925LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
3926 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
3927 ltl::ClockEdge::Pos,
3928 getLoweredNonClockValue(op.getClock()));
3929}
3930
3931template <typename TargetOp, typename IntrinsicOp>
3932LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
3933 auto property = getLoweredValue(op.getProperty());
3934 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
3935 builder.create<TargetOp>(property, enable, op.getLabelAttr());
3936 return success();
3937}
3938
3939LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
3940 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
3941}
3942
3943LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
3944 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
3945}
3946
3947LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
3948 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
3949}
3950
3951LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
3952 if (!isa<verif::ContractOp>(op->getParentOp()))
3953 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
3954 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
3955}
3956
3957LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
3958 if (!isa<verif::ContractOp>(op->getParentOp()))
3959 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
3960 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
3961}
3962
3963LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
3964 auto clock = getLoweredNonClockValue(op.getClock());
3965 auto reset = getLoweredValue(op.getReset());
3966 if (!clock || !reset)
3967 return failure();
3968 auto resetType = op.getReset().getType();
3969 auto uintResetType = dyn_cast<UIntType>(resetType);
3970 auto isSync = uintResetType && uintResetType.getWidth() == 1;
3971 auto isAsync = isa<AsyncResetType>(resetType);
3972 if (!isAsync && !isSync) {
3973 auto d = op.emitError("uninferred reset passed to 'has_been_reset'; "
3974 "requires sync or async reset");
3975 d.attachNote() << "reset is of type " << resetType
3976 << ", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
3977 return failure();
3978 }
3979 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
3980}
3981
3982//===----------------------------------------------------------------------===//
3983// Other Operations
3984//===----------------------------------------------------------------------===//
3985
3986LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
3987 auto input = getLoweredValue(op.getInput());
3988 if (!input)
3989 return failure();
3990
3991 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
3992 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
3993}
3994
3995LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
3996 auto resultTy = lowerType(op.getType());
3997 if (!resultTy)
3998 return failure();
3999
4000 // Values of analog type always need to be lowered to something with inout
4001 // type. We do that by lowering to a wire and return that. As with the
4002 // SFC, we do not connect anything to this, because it is bidirectional.
4003 if (type_isa<AnalogType>(op.getType()))
4004 // This is a locally visible, private wire created by the compiler, so do
4005 // not attach a symbol name.
4006 return setLoweringTo<sv::WireOp>(op, resultTy, ".invalid_analog");
4007
4008 // We don't allow aggregate values which contain values of analog types.
4009 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4010 return failure();
4011
4012 // We lower invalid to 0. TODO: the FIRRTL spec mentions something about
4013 // lowering it to a random value, we should see if this is what we need to
4014 // do.
4015 if (auto bitwidth =
4016 firrtl::getBitWidth(type_cast<FIRRTLBaseType>(op.getType()))) {
4017 if (*bitwidth == 0) // Let the caller handle zero width values.
4018 return failure();
4019
4020 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4021 // If the result is an aggregate value, we have to bitcast the constant.
4022 if (!type_isa<IntegerType>(resultTy))
4023 constant = builder.create<hw::BitcastOp>(resultTy, constant);
4024 return setLowering(op, constant);
4025 }
4026
4027 // Invalid for bundles isn't supported.
4028 op.emitOpError("unsupported type");
4029 return failure();
4030}
4031
4032LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4033 auto input = getLoweredValue(op.getInput());
4034 if (!input)
4035 return failure();
4036 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4037 if (op.getAmount() == 0)
4038 return setLowering(op, Value());
4039 Type resultType = builder.getIntegerType(op.getAmount());
4040 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4041 inWidth - op.getAmount());
4042}
4043
4044LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4045 auto input = getLoweredValue(op.getInput());
4046 if (!input) {
4047 return handleZeroBit(op.getInput(), [&]() {
4048 if (op.getAmount() == 0)
4049 return failure();
4050 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4051 });
4052 }
4053
4054 // Handle the degenerate case.
4055 if (op.getAmount() == 0)
4056 return setLowering(op, input);
4057
4058 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4059 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4060}
4061
4062LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4063 auto input = getLoweredValue(op.getInput());
4064 if (!input)
4065 return failure();
4066
4067 // Handle the special degenerate cases.
4068 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4069 auto shiftAmount = op.getAmount();
4070 if (shiftAmount >= inWidth) {
4071 // Unsigned shift by full width returns a single-bit zero.
4072 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4073 return setLowering(op, {});
4074
4075 // Signed shift by full width is equivalent to extracting the sign bit.
4076 shiftAmount = inWidth - 1;
4077 }
4078
4079 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4080 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4081}
4082
4083LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4084 auto input = getLoweredValue(op.getInput());
4085 if (!input)
4086 return failure();
4087
4088 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4089 if (inWidth == op.getAmount())
4090 return setLowering(op, Value());
4091 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4092 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4093}
4094
4095LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4096 auto cond = getLoweredValue(op.getSel());
4097 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4098 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4099 if (!cond || !ifTrue || !ifFalse)
4100 return failure();
4101
4102 if (isa<ClockType>(op.getType()))
4103 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4104 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4105 true);
4106}
4107
4108LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4109 auto cond = getLoweredValue(op.getSel());
4110 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4111 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4112 if (!cond || !ifTrue || !ifFalse)
4113 return failure();
4114
4115 auto val = builder.create<comb::MuxOp>(ifTrue.getType(), cond, ifTrue,
4116 ifFalse, true);
4117 return setLowering(op, createValueWithMuxAnnotation(val, true));
4118}
4119
4120LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4121 auto sel = getLoweredValue(op.getSel());
4122 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4123 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4124 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4125 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4126 if (!sel || !v3 || !v2 || !v1 || !v0)
4127 return failure();
4128 Value array[] = {v3, v2, v1, v0};
4129 auto create = builder.create<hw::ArrayCreateOp>(array);
4130 auto val = builder.create<hw::ArrayGetOp>(create, sel);
4131 return setLowering(op, createValueWithMuxAnnotation(val, false));
4132}
4133
4134// Construct a value with vendor specific pragmas to utilize MUX cells.
4135// Specifically we annotate pragmas in the following form.
4136//
4137// For an array indexing:
4138// ```
4139// wire GEN;
4140// /* synopsys infer_mux_override */
4141// assign GEN = array[index] /* cadence map_to_mux */;
4142// ```
4143//
4144// For a mux:
4145// ```
4146// wire GEN;
4147// /* synopsys infer_mux_override */
4148// assign GEN = sel ? /* cadence map_to_mux */ high : low;
4149// ```
4150Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op, bool isMux2) {
4151 assert(op->getNumResults() == 1 && "only expect a single result");
4152 auto val = op->getResult(0);
4153 auto valWire = builder.create<sv::WireOp>(val.getType());
4154 // Use SV attributes to annotate pragmas.
4156 op, sv::SVAttributeAttr::get(builder.getContext(), "cadence map_to_mux",
4157 /*emitAsComment=*/true));
4158
4159 // For operands, create temporary wires with optimization blockers(inner
4160 // symbols) so that the AST structure will never be destoyed in the later
4161 // pipeline.
4162 {
4163 OpBuilder::InsertionGuard guard(builder);
4164 builder.setInsertionPoint(op);
4165 StringRef namehint = isMux2 ? "mux2cell_in" : "mux4cell_in";
4166 for (auto [idx, operand] : llvm::enumerate(op->getOperands())) {
4167 auto [innerSym, _] = getOrAddInnerSym(
4168 op->getContext(), /*attr=*/nullptr, 0,
4169 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
4170 auto wire =
4171 builder.create<hw::WireOp>(operand, namehint + Twine(idx), innerSym);
4172 op->setOperand(idx, wire);
4173 }
4174 }
4175
4176 auto assignOp = builder.create<sv::AssignOp>(valWire, val);
4177 sv::setSVAttributes(assignOp,
4178 sv::SVAttributeAttr::get(builder.getContext(),
4179 "synopsys infer_mux_override",
4180 /*emitAsComment=*/true));
4181 return builder.create<sv::ReadInOutOp>(valWire);
4182}
4183
4184Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4185
4186 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4187 // Extend to power of 2. FIRRTL semantics say out-of-bounds access result in
4188 // an indeterminate value. Existing chisel code depends on this behavior
4189 // being "return index 0". Ideally, we would tail extend the array to improve
4190 // optimization.
4191 if (!llvm::isPowerOf2_64(size)) {
4192 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4193 auto extValue = builder.create<hw::ArrayGetOp>(array, extElem);
4194 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4195 auto ext = builder.create<hw::ArrayCreateOp>(temp);
4196 Value temp2[] = {ext.getResult(), array};
4197 array = builder.create<hw::ArrayConcatOp>(temp2);
4198 }
4199
4200 Value inBoundsRead = builder.create<hw::ArrayGetOp>(array, index);
4201
4202 return inBoundsRead;
4203}
4204
4205LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4206 // Lower and resize to the index width.
4207 auto index = getLoweredAndExtOrTruncValue(
4208 op.getIndex(),
4209 UIntType::get(op.getContext(),
4210 getBitWidthFromVectorSize(op.getInputs().size())));
4211
4212 if (!index)
4213 return failure();
4214 SmallVector<Value> loweredInputs;
4215 loweredInputs.reserve(op.getInputs().size());
4216 for (auto input : op.getInputs()) {
4217 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4218 if (!lowered)
4219 return failure();
4220 loweredInputs.push_back(lowered);
4221 }
4222
4223 Value array = builder.create<hw::ArrayCreateOp>(loweredInputs);
4224 return setLowering(op, createArrayIndexing(array, index));
4225}
4226
4227LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4228 auto resultTy = lowerType(op.getType());
4229 if (!resultTy)
4230 return failure();
4231
4232 SmallVector<Value, 4> operands;
4233 operands.reserve(op.getSubstitutions().size());
4234 for (auto operand : op.getSubstitutions()) {
4235 auto lowered = getLoweredValue(operand);
4236 if (!lowered)
4237 return failure();
4238 operands.push_back(lowered);
4239 }
4240
4241 ArrayAttr symbols = op.getSymbolsAttr();
4242 if (!symbols)
4243 symbols = ArrayAttr::get(op.getContext(), {});
4244
4245 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4246 operands, symbols);
4247}
4248
4249LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4250 // This XMR is accessed solely by FIRRTL statements that mutate the probe.
4251 // To avoid the use of clock wires, create an `i1` wire and ensure that
4252 // all connections are also of the `i1` type.
4253 Type baseType = op.getType().getType();
4254
4255 Type xmrType;
4256 if (isa<ClockType>(baseType))
4257 xmrType = builder.getIntegerType(1);
4258 else
4259 xmrType = lowerType(baseType);
4260
4261 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4262 op.getRef(), op.getVerbatimSuffixAttr());
4263}
4264
4265LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4266 // When an XMR targets a clock wire, replace it with an `i1` wire, but
4267 // introduce a clock-typed read op into the design afterwards.
4268 Type xmrType;
4269 if (isa<ClockType>(op.getType()))
4270 xmrType = builder.getIntegerType(1);
4271 else
4272 xmrType = lowerType(op.getType());
4273
4274 auto xmr = builder.create<sv::XMRRefOp>(
4275 sv::InOutType::get(xmrType), op.getRef(), op.getVerbatimSuffixAttr());
4276 auto readXmr = getReadValue(xmr);
4277 if (!isa<ClockType>(op.getType()))
4278 return setLowering(op, readXmr);
4279 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4280}
4281
4282//===----------------------------------------------------------------------===//
4283// Statements
4284//===----------------------------------------------------------------------===//
4285
4286LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4287 // Nothing! We could emit an comment as a verbatim op if there were a
4288 // reason to.
4289 return success();
4290}
4291
4292/// Resolve a connection to `destVal`, an `hw::WireOp` or `seq::FirRegOp`, by
4293/// updating the input operand to be `srcVal`. Returns true if the update was
4294/// made and the connection can be considered lowered. Returns false if the
4295/// destination isn't a wire or register with an input operand to be updated.
4296/// Returns failure if the destination is a subaccess operation. These should be
4297/// transposed to the right-hand-side by a pre-pass.
4298FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4299 auto srcType = srcVal.getType();
4300 auto dstType = destVal.getType();
4301 if (srcType != dstType &&
4302 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4303 srcVal = builder.create<hw::BitcastOp>(destVal.getType(), srcVal);
4304 }
4305 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4306 .Case<hw::WireOp>([&](auto op) {
4307 maybeUnused(op.getInput());
4308 op.getInputMutable().assign(srcVal);
4309 return true;
4310 })
4311 .Case<seq::FirRegOp>([&](auto op) {
4312 maybeUnused(op.getNext());
4313 op.getNextMutable().assign(srcVal);
4314 return true;
4315 })
4316 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](auto op) {
4317 // NOTE: msvc thinks `return op.emitOpError(...);` is ambiguous. So
4318 // return `failure()` separately.
4319 op.emitOpError("used as connect destination");
4320 return failure();
4321 })
4322 .Default([](auto) { return false; });
4323}
4324
4325LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4326 auto dest = op.getDest();
4327 // The source can be a smaller integer, extend it as appropriate if so.
4328 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4329 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4330 if (!srcVal)
4331 return handleZeroBit(op.getSrc(), []() { return success(); });
4332
4333 auto destVal = getPossiblyInoutLoweredValue(dest);
4334 if (!destVal)
4335 return failure();
4336
4337 auto result = lowerConnect(destVal, srcVal);
4338 if (failed(result))
4339 return failure();
4340 if (*result)
4341 return success();
4342
4343 // If this connect is driving a value that is currently a backedge, record
4344 // that the source is the value of the backedge.
4345 if (updateIfBackedge(destVal, srcVal))
4346 return success();
4347
4348 if (!isa<hw::InOutType>(destVal.getType()))
4349 return op.emitError("destination isn't an inout type");
4350
4351 builder.create<sv::AssignOp>(destVal, srcVal);
4352 return success();
4353}
4354
4355LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
4356 auto dest = op.getDest();
4357 auto srcVal = getLoweredValue(op.getSrc());
4358 if (!srcVal)
4359 return handleZeroBit(op.getSrc(), []() { return success(); });
4360
4361 auto destVal = getPossiblyInoutLoweredValue(dest);
4362 if (!destVal)
4363 return failure();
4364
4365 auto result = lowerConnect(destVal, srcVal);
4366 if (failed(result))
4367 return failure();
4368 if (*result)
4369 return success();
4370
4371 // If this connect is driving a value that is currently a backedge, record
4372 // that the source is the value of the backedge.
4373 if (updateIfBackedge(destVal, srcVal))
4374 return success();
4375
4376 if (!isa<hw::InOutType>(destVal.getType()))
4377 return op.emitError("destination isn't an inout type");
4378
4379 builder.create<sv::AssignOp>(destVal, srcVal);
4380 return success();
4381}
4382
4383LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4384 auto srcVal = getLoweredValue(op.getSrc());
4385 if (!srcVal)
4386 return failure();
4387
4388 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4389 if (!destVal)
4390 return failure();
4391
4392 if (!isa<hw::InOutType>(destVal.getType()))
4393 return op.emitError("destination isn't an inout type");
4394
4395 // #ifndef SYNTHESIS
4396 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4397 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4398 addToInitialBlock([&]() { builder.create<sv::ForceOp>(destVal, srcVal); });
4399 });
4400 return success();
4401}
4402
4403LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4404 auto src = getLoweredNonClockValue(op.getSrc());
4405 auto clock = getLoweredNonClockValue(op.getClock());
4406 auto pred = getLoweredValue(op.getPredicate());
4407 if (!src || !clock || !pred)
4408 return failure();
4409
4410 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4411 if (!destVal)
4412 return failure();
4413
4414 // #ifndef SYNTHESIS
4415 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4416 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4417 addToAlwaysBlock(clock, [&]() {
4418 addIfProceduralBlock(
4419 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4420 });
4421 });
4422 return success();
4423}
4424LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4425 auto src = getLoweredNonClockValue(op.getSrc());
4426 auto pred = getLoweredValue(op.getPredicate());
4427 if (!src || !pred)
4428 return failure();
4429
4430 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4431 if (!destVal)
4432 return failure();
4433
4434 // #ifndef SYNTHESIS
4435 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4436 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4437 addToInitialBlock([&]() {
4438 addIfProceduralBlock(
4439 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4440 });
4441 });
4442 return success();
4443}
4444LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4445 auto clock = getLoweredNonClockValue(op.getClock());
4446 auto pred = getLoweredValue(op.getPredicate());
4447 if (!clock || !pred)
4448 return failure();
4449
4450 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4451 if (!destVal)
4452 return failure();
4453
4454 // #ifndef SYNTHESIS
4455 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4456 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4457 addToAlwaysBlock(clock, [&]() {
4458 addIfProceduralBlock(pred,
4459 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4460 });
4461 });
4462 return success();
4463}
4464LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
4465 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4466 auto pred = getLoweredValue(op.getPredicate());
4467 if (!destVal || !pred)
4468 return failure();
4469
4470 // #ifndef SYNTHESIS
4471 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4472 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4473 addToInitialBlock([&]() {
4474 addIfProceduralBlock(pred,
4475 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4476 });
4477 });
4478 return success();
4479}
4480
4481// Printf is a macro op that lowers to an sv.ifdef.procedural, an sv.if,
4482// and an sv.fwrite all nested together.
4483LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
4484 auto clock = getLoweredNonClockValue(op.getClock());
4485 auto cond = getLoweredValue(op.getCond());
4486 if (!clock || !cond)
4487 return failure();
4488
4489 SmallVector<Value, 4> operands;
4490 operands.reserve(op.getSubstitutions().size());
4491 for (auto operand : op.getSubstitutions()) {
4492 Value loweredValue = getLoweredFmtOperand(operand);
4493 if (!loweredValue)
4494 return failure();
4495 operands.push_back(loweredValue);
4496 }
4497
4498 // Emit an "#ifndef SYNTHESIS" guard into the always block.
4499 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4500 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
4501 addToAlwaysBlock(clock, [&]() {
4502 circuitState.usedPrintf = true;
4503 circuitState.addFragment(theModule, "PRINTF_FD_FRAGMENT");
4504 circuitState.addFragment(theModule, "PRINTF_COND_FRAGMENT");
4505
4506 // Emit an "sv.if '`PRINTF_COND_ & cond' into the #ifndef.
4507 Value ifCond =
4508 builder.create<sv::MacroRefExprOp>(cond.getType(), "PRINTF_COND_");
4509 ifCond = builder.createOrFold<comb::AndOp>(ifCond, cond, true);
4510
4511 addIfProceduralBlock(ifCond, [&]() {
4512 // Emit the sv.fwrite, writing to fd specified by `PRINTF_FD.
4513 Value fd = builder.create<sv::MacroRefExprOp>(
4514 builder.getIntegerType(32), "PRINTF_FD_");
4515 builder.create<sv::FWriteOp>(fd, op.getFormatString(), operands);
4516 });
4517 });
4518 });
4519
4520 return success();
4521}
4522
4523// Stop lowers into a nested series of behavioral statements plus $fatal
4524// or $finish.
4525LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
4526 auto clock = getLoweredValue(op.getClock());
4527 auto cond = getLoweredValue(op.getCond());
4528 if (!clock || !cond)
4529 return failure();
4530
4531 circuitState.usedStopCond = true;
4532 circuitState.addFragment(theModule, "STOP_COND_FRAGMENT");
4533
4534 Value stopCond =
4535 builder.create<sv::MacroRefExprOp>(cond.getType(), "STOP_COND_");
4536 Value exitCond = builder.createOrFold<comb::AndOp>(stopCond, cond, true);
4537
4538 if (op.getExitCode())
4539 builder.create<sim::FatalOp>(clock, exitCond);
4540 else
4541 builder.create<sim::FinishOp>(clock, exitCond);
4542
4543 return success();
4544}
4545
4546/// Helper function to build an immediate assert operation based on the
4547/// original FIRRTL operation name. This reduces code duplication in
4548/// `lowerVerificationStatement`.
4549template <typename... Args>
4550static Operation *buildImmediateVerifOp(ImplicitLocOpBuilder &builder,
4551 StringRef opName, Args &&...args) {
4552 if (opName == "assert")
4553 return builder.create<sv::AssertOp>(std::forward<Args>(args)...);
4554 if (opName == "assume")
4555 return builder.create<sv::AssumeOp>(std::forward<Args>(args)...);
4556 if (opName == "cover")
4557 return builder.create<sv::CoverOp>(std::forward<Args>(args)...);
4558 llvm_unreachable("unknown verification op");
4559}
4560
4561/// Helper function to build a concurrent assert operation based on the
4562/// original FIRRTL operation name. This reduces code duplication in
4563/// `lowerVerificationStatement`.
4564template <typename... Args>
4565static Operation *buildConcurrentVerifOp(ImplicitLocOpBuilder &builder,
4566 StringRef opName, Args &&...args) {
4567 if (opName == "assert")
4568 return builder.create<sv::AssertConcurrentOp>(std::forward<Args>(args)...);
4569 if (opName == "assume")
4570 return builder.create<sv::AssumeConcurrentOp>(std::forward<Args>(args)...);
4571 if (opName == "cover")
4572 return builder.create<sv::CoverConcurrentOp>(std::forward<Args>(args)...);
4573 llvm_unreachable("unknown verification op");
4574}
4575
4576/// Template for lowering verification statements from type A to
4577/// type B.
4578///
4579/// For example, lowering the "foo" op to the "bar" op would start
4580/// with:
4581///
4582/// foo(clock, condition, enable, "message")
4583///
4584/// This becomes a Verilog clocking block with the "bar" op guarded
4585/// by an if enable:
4586///
4587/// always @(posedge clock) begin
4588/// if (enable) begin
4589/// bar(condition);
4590/// end
4591/// end
4592/// The above can also be reduced into a concurrent verification statement
4593/// sv.assert.concurrent posedge %clock (condition && enable)
4594LogicalResult FIRRTLLowering::lowerVerificationStatement(
4595 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
4596 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
4597 StringAttr opNameAttr, bool isConcurrent, EventControl opEventControl) {
4598 StringRef opName = op->getName().stripDialect();
4599
4600 // The attribute holding the compile guards
4601 ArrayRef<Attribute> guards{};
4602 if (auto guardsAttr = op->template getAttrOfType<ArrayAttr>("guards"))
4603 guards = guardsAttr.getValue();
4604
4605 auto isCover = isa<CoverOp>(op);
4606 auto clock = getLoweredNonClockValue(opClock);
4607 auto enable = getLoweredValue(opEnable);
4608 auto predicate = getLoweredValue(opPredicate);
4609 if (!clock || !enable || !predicate)
4610 return failure();
4611
4612 StringAttr label;
4613 if (opNameAttr && !opNameAttr.getValue().empty())
4614 label = opNameAttr;
4615 StringAttr prefixedLabel;
4616 if (label)
4617 prefixedLabel =
4618 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
4619
4620 StringAttr message;
4621 SmallVector<Value> messageOps;
4622 VerificationFlavor flavor = circuitState.verificationFlavor;
4623
4624 // For non-assertion, rollback to per-op configuration.
4625 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
4626 flavor = VerificationFlavor::None;
4627
4628 if (flavor == VerificationFlavor::None) {
4629 // TODO: This should *not* be part of the op, but rather a lowering
4630 // option that the user of this pass can choose.
4631
4632 auto format = op->getAttrOfType<StringAttr>("format");
4633 // if-else-fatal iff concurrent and the format is specified.
4634 if (isConcurrent && format && format.getValue() == "ifElseFatal") {
4635 if (!isa<AssertOp>(op))
4636 return op->emitError()
4637 << "ifElseFatal format cannot be used for non-assertions";
4638 flavor = VerificationFlavor::IfElseFatal;
4639 } else if (isConcurrent)
4640 flavor = VerificationFlavor::SVA;
4641 else
4642 flavor = VerificationFlavor::Immediate;
4643 }
4644
4645 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
4646 message = opMessageAttr;
4647 for (auto operand : opOperands) {
4648 auto loweredValue = getLoweredFmtOperand(operand);
4649 if (!loweredValue)
4650 return failure();
4651
4652 // For SVA assert/assume statements, wrap any message ops in $sampled() to
4653 // guarantee that these will print with the same value as when the
4654 // assertion triggers. (See SystemVerilog 2017 spec section 16.9.3 for
4655 // more information.)
4656 if (flavor == VerificationFlavor::SVA)
4657 loweredValue = builder.create<sv::SampledOp>(loweredValue);
4658 messageOps.push_back(loweredValue);
4659 }
4660 }
4661
4662 auto emit = [&]() {
4663 switch (flavor) {
4664 case VerificationFlavor::Immediate: {
4665 // Handle the purely procedural flavor of the operation.
4666 auto deferImmediate = circt::sv::DeferAssertAttr::get(
4667 builder.getContext(), circt::sv::DeferAssert::Immediate);
4668 addToAlwaysBlock(clock, [&]() {
4669 addIfProceduralBlock(enable, [&]() {
4670 buildImmediateVerifOp(builder, opName, predicate, deferImmediate,
4671 prefixedLabel, message, messageOps);
4672 });
4673 });
4674 return;
4675 }
4676 case VerificationFlavor::IfElseFatal: {
4677 assert(isa<AssertOp>(op) && "only assert is expected");
4678 // Handle the `ifElseFatal` format, which does not emit an SVA but
4679 // rather a process that uses $error and $fatal to perform the checks.
4680 auto boolType = IntegerType::get(builder.getContext(), 1);
4681 predicate = comb::createOrFoldNot(predicate, builder, /*twoState=*/true);
4682 predicate = builder.createOrFold<comb::AndOp>(enable, predicate, true);
4683
4684 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4685 addToIfDefBlock("SYNTHESIS", {}, [&]() {
4686 addToAlwaysBlock(clock, [&]() {
4687 addIfProceduralBlock(predicate, [&]() {
4688 circuitState.usedStopCond = true;
4689 circuitState.addFragment(theModule, "STOP_COND_FRAGMENT");
4690
4691 circuitState.usedAssertVerboseCond = true;
4692 circuitState.addFragment(theModule, "ASSERT_VERBOSE_COND_FRAGMENT");
4693
4694 addIfProceduralBlock(
4695 builder.create<sv::MacroRefExprOp>(boolType,
4696 "ASSERT_VERBOSE_COND_"),
4697 [&]() { builder.create<sv::ErrorOp>(message, messageOps); });
4698 addIfProceduralBlock(
4699 builder.create<sv::MacroRefExprOp>(boolType, "STOP_COND_"),
4700 [&]() { builder.create<sv::FatalOp>(); });
4701 });
4702 });
4703 });
4704 return;
4705 }
4706 case VerificationFlavor::SVA: {
4707 // Formulate the `enable -> predicate` as `!enable | predicate`.
4708 // Except for covers, combine them: enable & predicate
4709 if (!isCover) {
4710 auto notEnable =
4711 comb::createOrFoldNot(enable, builder, /*twoState=*/true);
4712 predicate =
4713 builder.createOrFold<comb::OrOp>(notEnable, predicate, true);
4714 } else {
4715 predicate = builder.createOrFold<comb::AndOp>(enable, predicate, true);
4716 }
4717
4718 // Handle the regular SVA case.
4719 sv::EventControl event;
4720 switch (opEventControl) {
4721 case EventControl::AtPosEdge:
4722 event = circt::sv::EventControl::AtPosEdge;
4723 break;
4724 case EventControl::AtEdge:
4725 event = circt::sv::EventControl::AtEdge;
4726 break;
4727 case EventControl::AtNegEdge:
4728 event = circt::sv::EventControl::AtNegEdge;
4729 break;
4730 }
4731
4733 builder, opName,
4734 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
4735 predicate, prefixedLabel, message, messageOps);
4736 return;
4737 }
4738 case VerificationFlavor::None:
4739 llvm_unreachable(
4740 "flavor `None` must be converted into one of concreate flavors");
4741 }
4742 };
4743
4744 // Wrap the verification statement up in the optional preprocessor
4745 // guards. This is a bit awkward since we want to translate an array of
4746 // guards into a recursive call to `addToIfDefBlock`.
4747 return emitGuards(op->getLoc(), guards, emit);
4748}
4749
4750// Lower an assert to SystemVerilog.
4751LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
4752 return lowerVerificationStatement(
4753 op, "assert__", op.getClock(), op.getPredicate(), op.getEnable(),
4754 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4755 op.getIsConcurrent(), op.getEventControl());
4756}
4757
4758// Lower an assume to SystemVerilog.
4759LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
4760 return lowerVerificationStatement(
4761 op, "assume__", op.getClock(), op.getPredicate(), op.getEnable(),
4762 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4763 op.getIsConcurrent(), op.getEventControl());
4764}
4765
4766// Lower a cover to SystemVerilog.
4767LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
4768 return lowerVerificationStatement(
4769 op, "cover__", op.getClock(), op.getPredicate(), op.getEnable(),
4770 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4771 op.getIsConcurrent(), op.getEventControl());
4772}
4773
4774// Lower an UNR only assume to a specific style of SV assume.
4775LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
4776 // TODO : Need to figure out if there is a cleaner way to get the string which
4777 // indicates the assert is UNR only. Or better - not rely on this at all -
4778 // ideally there should have been some other attribute which indicated that
4779 // this assert for UNR only.
4780 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>("guards");
4781 ArrayRef<Attribute> guards =
4782 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
4783
4784 auto label = op.getNameAttr();
4785 StringAttr assumeLabel;
4786 if (label && !label.empty())
4787 assumeLabel =
4788 StringAttr::get(builder.getContext(), "assume__" + label.getValue());
4789 auto predicate = getLoweredValue(op.getPredicate());
4790 auto enable = getLoweredValue(op.getEnable());
4791 auto notEnable = comb::createOrFoldNot(enable, builder, /*twoState=*/true);
4792 predicate = builder.createOrFold<comb::OrOp>(notEnable, predicate, true);
4793
4794 SmallVector<Value> messageOps;
4795 for (auto operand : op.getSubstitutions()) {
4796 auto loweredValue = getLoweredValue(operand);
4797 if (!loweredValue) {
4798 // If this is a zero bit operand, just pass a one bit zero.
4799 if (!isZeroBitFIRRTLType(operand.getType()))
4800 return failure();
4801 loweredValue = getOrCreateIntConstant(1, 0);
4802 }
4803 messageOps.push_back(loweredValue);
4804 }
4805 return emitGuards(op.getLoc(), guards, [&]() {
4806 builder.create<sv::AlwaysOp>(
4807 ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate), [&]() {
4808 if (op.getMessageAttr().getValue().empty())
4809 buildImmediateVerifOp(
4810 builder, "assume", predicate,
4811 circt::sv::DeferAssertAttr::get(
4812 builder.getContext(), circt::sv::DeferAssert::Immediate),
4813 assumeLabel);
4814 else
4815 buildImmediateVerifOp(
4816 builder, "assume", predicate,
4817 circt::sv::DeferAssertAttr::get(
4818 builder.getContext(), circt::sv::DeferAssert::Immediate),
4819 assumeLabel, op.getMessageAttr(), messageOps);
4820 });
4821 });
4822}
4823
4824LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
4825 // Don't emit anything for a zero or one operand attach.
4826 if (op.getAttached().size() < 2)
4827 return success();
4828
4829 SmallVector<Value, 4> inoutValues;
4830 for (auto v : op.getAttached()) {
4831 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
4832 if (!inoutValues.back()) {
4833 // Ignore zero bit values.
4834 if (!isZeroBitFIRRTLType(v.getType()))
4835 return failure();
4836 inoutValues.pop_back();
4837 continue;
4838 }
4839
4840 if (!isa<hw::InOutType>(inoutValues.back().getType()))
4841 return op.emitError("operand isn't an inout type");
4842 }
4843
4844 if (inoutValues.size() < 2)
4845 return success();
4846
4847 // If the op has a single source value, the value is used as a lowering result
4848 // of other values. Therefore we can delete the attach op here.
4850 return success();
4851
4852 // If all operands of the attach are internal to this module (none of them
4853 // are ports), then they can all be replaced with a single wire, and we can
4854 // delete the attach op.
4855 bool isAttachInternalOnly =
4856 llvm::none_of(inoutValues, [](auto v) { return isa<BlockArgument>(v); });
4857
4858 if (isAttachInternalOnly) {
4859 auto v0 = inoutValues.front();
4860 for (auto v : inoutValues) {
4861 if (v == v0)
4862 continue;
4863 v.replaceAllUsesWith(v0);
4864 }
4865 return success();
4866 }
4867
4868 // If the attach operands contain a port, then we can't do anything to
4869 // simplify the attach operation.
4870 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
4871 circuitState.addMacroDecl(builder.getStringAttr("VERILATOR"));
4872 addToIfDefBlock(
4873 "SYNTHESIS",
4874 // If we're doing synthesis, we emit an all-pairs assign complex.
4875 [&]() {
4876 SmallVector<Value, 4> values;
4877 for (auto inoutValue : inoutValues)
4878 values.push_back(getReadValue(inoutValue));
4879
4880 for (size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
4881 for (size_t i2 = 0; i2 != e; ++i2)
4882 if (i1 != i2)
4883 builder.create<sv::AssignOp>(inoutValues[i1], values[i2]);
4884 }
4885 },
4886 // In the non-synthesis case, we emit a SystemVerilog alias
4887 // statement.
4888 [&]() {
4889 builder.create<sv::IfDefOp>(
4890 "VERILATOR",
4891 [&]() {
4892 builder.create<sv::VerbatimOp>(
4893 "`error \"Verilator does not support alias and thus "
4894 "cannot "
4895 "arbitrarily connect bidirectional wires and ports\"");
4896 },
4897 [&]() { builder.create<sv::AliasOp>(inoutValues); });
4898 });
4899
4900 return success();
4901}
4902
4903LogicalResult FIRRTLLowering::fixupLTLOps() {
4904 if (ltlOpFixupWorklist.empty())
4905 return success();
4906 LLVM_DEBUG(llvm::dbgs() << "Fixing up " << ltlOpFixupWorklist.size()
4907 << " LTL ops\n");
4908
4909 // Add wire users into the worklist.
4910 for (unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
4911 for (auto *user : ltlOpFixupWorklist[i]->getUsers())
4912 if (isa<hw::WireOp>(user))
4913 ltlOpFixupWorklist.insert(user);
4914
4915 // Re-infer LTL op types and remove wires.
4916 while (!ltlOpFixupWorklist.empty()) {
4917 auto *op = ltlOpFixupWorklist.pop_back_val();
4918
4919 // Update the operation's return type by re-running type inference.
4920 if (auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
4921 LLVM_DEBUG(llvm::dbgs() << "- Update " << *op << "\n");
4922 SmallVector<Type, 2> types;
4923 auto result = opIntf.inferReturnTypes(
4924 op->getContext(), op->getLoc(), op->getOperands(),
4925 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
4926 types);
4927 if (failed(result))
4928 return failure();
4929 assert(types.size() == op->getNumResults());
4930
4931 // Update the result types and add the dependent ops into the worklist if
4932 // the type changed.
4933 for (auto [result, type] : llvm::zip(op->getResults(), types)) {
4934 if (result.getType() == type)
4935 continue;
4936 LLVM_DEBUG(llvm::dbgs()
4937 << " - Result #" << result.getResultNumber() << " from "
4938 << result.getType() << " to " << type << "\n");
4939 result.setType(type);
4940 for (auto *user : result.getUsers())
4941 if (user != op)
4942 ltlOpFixupWorklist.insert(user);
4943 }
4944 }
4945
4946 // Remove LTL-typed wires.
4947 if (auto wireOp = dyn_cast<hw::WireOp>(op)) {
4948 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
4949 wireOp.replaceAllUsesWith(wireOp.getInput());
4950 LLVM_DEBUG(llvm::dbgs() << "- Remove " << wireOp << "\n");
4951 if (wireOp.use_empty())
4952 wireOp.erase();
4953 }
4954 continue;
4955 }
4956
4957 // Ensure that the operation has no users outside of LTL operations.
4958 SmallPtrSet<Operation *, 4> usersReported;
4959 for (auto *user : op->getUsers()) {
4960 if (!usersReported.insert(user).second)
4961 continue;
4962 if (isa<ltl::LTLDialect, verif::VerifDialect>(user->getDialect()))
4963 continue;
4964 if (isa<hw::WireOp>(user))
4965 continue;
4966 auto d = op->emitError(
4967 "verification operation used in a non-verification context");
4968 d.attachNote(user->getLoc())
4969 << "leaking outside verification context here";
4970 return d;
4971 }
4972 }
4973
4974 return success();
4975}
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