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