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