Loading [MathJax]/extensions/tex2jax.js
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)
912 global_id[name] = $fopen(name);
913 return global_id[name];
914 endfunction
915 endclass
916endpackage
917)");
918
919 b.create<sv::MacroDefOp>("__CIRCT_LIB_LOGGING", "");
920 });
921 });
922 }
923
924 if (state.usedPrintf) {
925 b.create<sv::MacroDeclOp>("PRINTF_FD");
926 b.create<sv::MacroDeclOp>("PRINTF_FD_");
927 b.create<emit::FragmentOp>("PRINTF_FD_FRAGMENT", [&] {
928 b.create<sv::VerbatimOp>(
929 "\n// Users can define 'PRINTF_FD' to add a specified fd to "
930 "prints.");
931 emitGuard("PRINTF_FD_", [&]() {
932 emitGuardedDefine("PRINTF_FD", "PRINTF_FD_", "(`PRINTF_FD)",
933 "32'h80000002");
934 });
935 });
936
937 b.create<sv::MacroDeclOp>("PRINTF_COND");
938 b.create<sv::MacroDeclOp>("PRINTF_COND_");
939 b.create<emit::FragmentOp>("PRINTF_COND_FRAGMENT", [&] {
940 b.create<sv::VerbatimOp>(
941 "\n// Users can define 'PRINTF_COND' to add an extra gate to "
942 "prints.");
943 emitGuard("PRINTF_COND_", [&]() {
944 emitGuardedDefine("PRINTF_COND", "PRINTF_COND_", "(`PRINTF_COND)", "1");
945 });
946 });
947 }
948
949 if (state.usedAssertVerboseCond) {
950 b.create<sv::MacroDeclOp>("ASSERT_VERBOSE_COND");
951 b.create<sv::MacroDeclOp>("ASSERT_VERBOSE_COND_");
952 b.create<emit::FragmentOp>("ASSERT_VERBOSE_COND_FRAGMENT", [&] {
953 b.create<sv::VerbatimOp>(
954 "\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
955 "gate to assert error printing.");
956 emitGuard("ASSERT_VERBOSE_COND_", [&]() {
957 emitGuardedDefine("ASSERT_VERBOSE_COND", "ASSERT_VERBOSE_COND_",
958 "(`ASSERT_VERBOSE_COND)", "1");
959 });
960 });
961 }
962
963 if (state.usedStopCond) {
964 b.create<sv::MacroDeclOp>("STOP_COND");
965 b.create<sv::MacroDeclOp>("STOP_COND_");
966 b.create<emit::FragmentOp>("STOP_COND_FRAGMENT", [&] {
967 b.create<sv::VerbatimOp>(
968 "\n// Users can define 'STOP_COND' to add an extra gate "
969 "to stop conditions.");
970 emitGuard("STOP_COND_", [&]() {
971 emitGuardedDefine("STOP_COND", "STOP_COND_", "(`STOP_COND)", "1");
972 });
973 });
974 }
975}
976
977LogicalResult
978FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
979 SmallVectorImpl<hw::PortInfo> &ports,
980 Operation *moduleOp, StringRef moduleName,
981 CircuitLoweringState &loweringState) {
982 ports.reserve(firrtlPorts.size());
983 size_t numArgs = 0;
984 size_t numResults = 0;
985 for (auto e : llvm::enumerate(firrtlPorts)) {
986 PortInfo firrtlPort = e.value();
987 size_t portNo = e.index();
988 hw::PortInfo hwPort;
989 hwPort.name = firrtlPort.name;
990 hwPort.type = loweringState.lowerType(firrtlPort.type, firrtlPort.loc);
991 if (firrtlPort.sym)
992 if (firrtlPort.sym.size() > 1 ||
993 (firrtlPort.sym.size() == 1 && !firrtlPort.sym.getSymName()))
994 return emitError(firrtlPort.loc)
995 << "cannot lower aggregate port " << firrtlPort.name
996 << " with field sensitive symbols, HW dialect does not support "
997 "per field symbols yet.";
998 hwPort.setSym(firrtlPort.sym, moduleOp->getContext());
999 bool hadDontTouch = firrtlPort.annotations.removeDontTouch();
1000 if (hadDontTouch && !hwPort.getSym()) {
1001 if (hwPort.type.isInteger(0)) {
1002 if (enableAnnotationWarning) {
1003 mlir::emitWarning(firrtlPort.loc)
1004 << "zero width port " << hwPort.name
1005 << " has dontTouch annotation, removing anyway";
1006 }
1007 continue;
1008 }
1009
1010 hwPort.setSym(
1011 hw::InnerSymAttr::get(StringAttr::get(
1012 moduleOp->getContext(),
1013 Twine("__") + moduleName + Twine("__DONTTOUCH__") +
1014 Twine(portNo) + Twine("__") + firrtlPort.name.strref())),
1015 moduleOp->getContext());
1016 }
1017
1018 // We can't lower all types, so make sure to cleanly reject them.
1019 if (!hwPort.type) {
1020 moduleOp->emitError("cannot lower this port type to HW");
1021 return failure();
1022 }
1023
1024 // If this is a zero bit port, just drop it. It doesn't matter if it is
1025 // input, output, or inout. We don't want these at the HW level.
1026 if (hwPort.type.isInteger(0)) {
1027 auto sym = hwPort.getSym();
1028 if (sym && !sym.empty()) {
1029 return mlir::emitError(firrtlPort.loc)
1030 << "zero width port " << hwPort.name
1031 << " is referenced by name [" << sym
1032 << "] (e.g. in an XMR) but must be removed";
1033 }
1034 continue;
1035 }
1036
1037 // Figure out the direction of the port.
1038 if (firrtlPort.isOutput()) {
1039 hwPort.dir = hw::ModulePort::Direction::Output;
1040 hwPort.argNum = numResults++;
1041 } else if (firrtlPort.isInput()) {
1042 hwPort.dir = hw::ModulePort::Direction::Input;
1043 hwPort.argNum = numArgs++;
1044 } else {
1045 // If the port is an inout bundle or contains an analog type, then it is
1046 // implicitly inout.
1047 hwPort.type = hw::InOutType::get(hwPort.type);
1048 hwPort.dir = hw::ModulePort::Direction::InOut;
1049 hwPort.argNum = numArgs++;
1050 }
1051 hwPort.loc = firrtlPort.loc;
1052 ports.push_back(hwPort);
1053 loweringState.processRemainingAnnotations(moduleOp, firrtlPort.annotations);
1054 }
1055 return success();
1056}
1057
1058/// Map the parameter specifier on the specified extmodule into the HWModule
1059/// representation for parameters. If `ignoreValues` is true, all the values
1060/// are dropped.
1061static ArrayAttr getHWParameters(FExtModuleOp module, bool ignoreValues) {
1062 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1063 return cast<ParamDeclAttr>(a);
1064 });
1065 if (params.empty())
1066 return {};
1067
1068 Builder builder(module);
1069
1070 // Map the attributes over from firrtl attributes to HW attributes
1071 // directly. MLIR's DictionaryAttr always stores keys in the dictionary
1072 // in sorted order which is nicely stable.
1073 SmallVector<Attribute> newParams;
1074 for (const ParamDeclAttr &entry : params) {
1075 auto name = entry.getName();
1076 auto type = entry.getType();
1077 auto value = ignoreValues ? Attribute() : entry.getValue();
1078 auto paramAttr =
1079 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1080 newParams.push_back(paramAttr);
1081 }
1082 return builder.getArrayAttr(newParams);
1083}
1084
1085bool FIRRTLModuleLowering::handleForceNameAnnos(
1086 FModuleLike oldModule, AnnotationSet &annos,
1087 CircuitLoweringState &loweringState) {
1088 bool failed = false;
1089 // Remove ForceNameAnnotations by generating verilogNames on instances.
1090 annos.removeAnnotations([&](Annotation anno) {
1091 if (!anno.isClass(forceNameAnnoClass))
1092 return false;
1093
1094 auto sym = anno.getMember<FlatSymbolRefAttr>("circt.nonlocal");
1095 // This must be a non-local annotation due to how the Chisel API is
1096 // implemented.
1097 //
1098 // TODO: handle this in some sensible way based on what the SFC does with
1099 // a local annotation.
1100 if (!sym) {
1101 auto diag = oldModule.emitOpError()
1102 << "contains a '" << forceNameAnnoClass
1103 << "' that is not a non-local annotation";
1104 diag.attachNote() << "the erroneous annotation is '" << anno.getDict()
1105 << "'\n";
1106 failed = true;
1107 return false;
1108 }
1109
1110 auto nla = loweringState.nlaTable->getNLA(sym.getAttr());
1111 // The non-local anchor must exist.
1112 //
1113 // TODO: handle this with annotation verification.
1114 if (!nla) {
1115 auto diag = oldModule.emitOpError()
1116 << "contains a '" << forceNameAnnoClass
1117 << "' whose non-local symbol, '" << sym
1118 << "' does not exist in the circuit";
1119 diag.attachNote() << "the erroneous annotation is '" << anno.getDict();
1120 failed = true;
1121 return false;
1122 }
1123
1124 // Add the forced name to global state (keyed by a pseudo-inner name ref).
1125 // Error out if this key is alredy in use.
1126 //
1127 // TODO: this error behavior can be relaxed to always overwrite with the
1128 // new forced name (the bug-compatible behavior of the Chisel
1129 // implementation) or fixed to duplicate modules such that the naming can
1130 // be applied.
1131 auto inst =
1132 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1133 auto inserted = loweringState.instanceForceNames.insert(
1134 {{inst.getModule(), inst.getName()}, anno.getMember("name")});
1135 if (!inserted.second &&
1136 (anno.getMember("name") != (inserted.first->second))) {
1137 auto diag = oldModule.emitError()
1138 << "contained multiple '" << forceNameAnnoClass
1139 << "' with different names: " << inserted.first->second
1140 << " was not " << anno.getMember("name");
1141 diag.attachNote() << "the erroneous annotation is '" << anno.getDict()
1142 << "'";
1143 failed = true;
1144 return false;
1145 }
1146 return true;
1147 });
1148 return failed;
1149}
1150
1152FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1153 Block *topLevelModule,
1154 CircuitLoweringState &loweringState) {
1155 // Map the ports over, lowering their types as we go.
1156 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1157 SmallVector<hw::PortInfo, 8> ports;
1158 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1159 loweringState)))
1160 return {};
1161
1162 StringRef verilogName;
1163 if (auto defName = oldModule.getDefname())
1164 verilogName = defName.value();
1165
1166 // Build the new hw.module op.
1167 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1168 auto nameAttr = builder.getStringAttr(oldModule.getName());
1169 // Map over parameters if present. Drop all values as we do so, so there are
1170 // no known default values in the extmodule. This ensures that the
1171 // hw.instance will print all the parameters when generating verilog.
1172 auto parameters = getHWParameters(oldModule, /*ignoreValues=*/true);
1173 auto newModule = builder.create<hw::HWModuleExternOp>(
1174 oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1175 SymbolTable::setSymbolVisibility(newModule,
1176 SymbolTable::getSymbolVisibility(oldModule));
1177
1178 bool hasOutputPort =
1179 llvm::any_of(firrtlPorts, [&](auto p) { return p.isOutput(); });
1180 if (!hasOutputPort &&
1182 loweringState.isInDUT(oldModule))
1183 newModule->setAttr("firrtl.extract.cover.extra", builder.getUnitAttr());
1184
1185 AnnotationSet annos(oldModule);
1186 if (handleForceNameAnnos(oldModule, annos, loweringState))
1187 return {};
1188
1189 loweringState.processRemainingAnnotations(oldModule, annos);
1190 return newModule;
1191}
1192
1194FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1195 Block *topLevelModule,
1196 CircuitLoweringState &loweringState) {
1197 // Map the ports over, lowering their types as we go.
1198 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1199 SmallVector<hw::PortInfo, 8> ports;
1200 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1201 loweringState)))
1202 return {};
1203
1204 // Build the new hw.module op.
1205 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1206 auto newModule = builder.create<hw::HWModuleExternOp>(
1207 oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1208 oldModule.getModuleNameAttr());
1209 loweringState.processRemainingAnnotations(oldModule,
1210 AnnotationSet(oldModule));
1211 return newModule;
1212}
1213
1214/// Run on each firrtl.module, creating a basic hw.module for the firrtl module.
1216FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1217 CircuitLoweringState &loweringState) {
1218 // Map the ports over, lowering their types as we go.
1219 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1220 SmallVector<hw::PortInfo, 8> ports;
1221 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1222 loweringState)))
1223 return {};
1224
1225 // Build the new hw.module op.
1226 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1227 auto nameAttr = builder.getStringAttr(oldModule.getName());
1228 auto newModule =
1229 builder.create<hw::HWModuleOp>(oldModule.getLoc(), nameAttr, ports);
1230
1231 if (auto comment = oldModule->getAttrOfType<StringAttr>("comment"))
1232 newModule.setCommentAttr(comment);
1233
1234 // Copy over any attributes which are not required for FModuleOp.
1235 SmallVector<StringRef, 12> attrNames = {
1236 "annotations", "convention", "layers",
1237 "portNames", "sym_name", "portDirections",
1238 "portTypes", "portAnnotations", "portSymbols",
1239 "portLocations", "parameters", SymbolTable::getVisibilityAttrName()};
1240
1241 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1242 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1243 for (auto i :
1244 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1245 return !attrSet.count(namedAttr.getName()) &&
1246 !newModule->getAttrDictionary().contains(namedAttr.getName());
1247 }))
1248 newAttrs.push_back(i);
1249
1250 newModule->setAttrs(newAttrs);
1251
1252 // If the circuit has an entry point, set all other modules private.
1253 // Otherwise, mark all modules as public.
1254 SymbolTable::setSymbolVisibility(newModule,
1255 SymbolTable::getSymbolVisibility(oldModule));
1256
1257 // Transform module annotations
1258 AnnotationSet annos(oldModule);
1259
1261 newModule->setAttr("firrtl.extract.cover.extra", builder.getUnitAttr());
1262
1263 // If this is in the test harness, make sure it goes to the test directory.
1264 // Do not update output file information if it is already present.
1265 if (auto testBenchDir = loweringState.getTestBenchDirectory())
1266 if (loweringState.isInTestHarness(oldModule)) {
1267 if (!newModule->hasAttr("output_file"))
1268 newModule->setAttr("output_file", testBenchDir);
1269 newModule->setAttr("firrtl.extract.do_not_extract",
1270 builder.getUnitAttr());
1271 newModule.setCommentAttr(
1272 builder.getStringAttr("VCS coverage exclude_file"));
1273 }
1274
1275 if (handleForceNameAnnos(oldModule, annos, loweringState))
1276 return {};
1277
1278 loweringState.processRemainingAnnotations(oldModule, annos);
1279 return newModule;
1280}
1281
1282/// Given a value of analog type, check to see the only use of it is an
1283/// attach. If so, remove the attach and return the value being attached to
1284/// it, converted to an HW inout type. If this isn't a situation we can
1285/// handle, just return null.
1287 Operation *insertPoint) {
1288 if (!value.hasOneUse())
1289 return {};
1290
1291 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1292 if (!attach || attach.getNumOperands() != 2)
1293 return {};
1294
1295 // Don't optimize zero bit analogs.
1296 auto loweredType = lowerType(value.getType());
1297 if (loweredType.isInteger(0))
1298 return {};
1299
1300 // Check to see if the attached value dominates the insertion point. If
1301 // not, just fail.
1302 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1303 auto *op = attachedValue.getDefiningOp();
1304 if (op && op->getBlock() == insertPoint->getBlock() &&
1305 !op->isBeforeInBlock(insertPoint))
1306 return {};
1307
1308 attach.erase();
1309
1310 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1311 return castFromFIRRTLType(attachedValue, hw::InOutType::get(loweredType),
1312 builder);
1313}
1314
1315/// Given a value of flip type, check to see if all of the uses of it are
1316/// connects. If so, remove the connects and return the value being connected
1317/// to it, converted to an HW type. If this isn't a situation we can handle,
1318/// just return null.
1319///
1320/// This can happen when there are no connects to the value. The 'mergePoint'
1321/// location is where a 'hw.merge' operation should be inserted if needed.
1322static Value
1323tryEliminatingConnectsToValue(Value flipValue, Operation *insertPoint,
1324 CircuitLoweringState &loweringState) {
1325 // Handle analog's separately.
1326 if (type_isa<AnalogType>(flipValue.getType()))
1327 return tryEliminatingAttachesToAnalogValue(flipValue, insertPoint);
1328
1329 Operation *connectOp = nullptr;
1330 for (auto &use : flipValue.getUses()) {
1331 // We only know how to deal with connects where this value is the
1332 // destination.
1333 if (use.getOperandNumber() != 0)
1334 return {};
1335 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1336 return {};
1337
1338 // We only support things with a single connect.
1339 if (connectOp)
1340 return {};
1341 connectOp = use.getOwner();
1342 }
1343
1344 // We don't have an HW equivalent of "poison" so just don't special case
1345 // the case where there are no connects other uses of an output.
1346 if (!connectOp)
1347 return {}; // TODO: Emit an sv.constant here since it is unconnected.
1348
1349 // Don't special case zero-bit results.
1350 auto loweredType =
1351 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1352 if (loweredType.isInteger(0))
1353 return {};
1354
1355 // Convert each connect into an extended version of its operand being
1356 // output.
1357 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1358
1359 auto connectSrc = connectOp->getOperand(1);
1360
1361 // Directly forward foreign types.
1362 if (!isa<FIRRTLType>(connectSrc.getType())) {
1363 connectOp->erase();
1364 return connectSrc;
1365 }
1366
1367 // Convert fliped sources to passive sources.
1368 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1369 connectSrc = builder
1370 .create<mlir::UnrealizedConversionCastOp>(
1371 type_cast<FIRRTLBaseType>(connectSrc.getType())
1372 .getPassiveType(),
1373 connectSrc)
1374 .getResult(0);
1375
1376 // We know it must be the destination operand due to the types, but the
1377 // source may not match the destination width.
1378 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1379
1380 if (destTy != connectSrc.getType() &&
1381 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1382 isa<BaseTypeAliasType>(destTy))) {
1383 connectSrc =
1384 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1385 }
1386 if (!destTy.isGround()) {
1387 // If types are not ground type and they don't match, we give up.
1388 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1389 return {};
1390 } else if (destTy.getBitWidthOrSentinel() !=
1391 type_cast<FIRRTLBaseType>(connectSrc.getType())
1392 .getBitWidthOrSentinel()) {
1393 // The only type mismatchs we care about is due to integer width
1394 // differences.
1395 auto destWidth = destTy.getBitWidthOrSentinel();
1396 assert(destWidth != -1 && "must know integer widths");
1397 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1398 }
1399
1400 // Remove the connect and use its source as the value for the output.
1401 connectOp->erase();
1402
1403 // Convert from FIRRTL type to builtin type.
1404 return castFromFIRRTLType(connectSrc, loweredType, builder);
1405}
1406
1407static SmallVector<SubfieldOp> getAllFieldAccesses(Value structValue,
1408 StringRef field) {
1409 SmallVector<SubfieldOp> accesses;
1410 for (auto *op : structValue.getUsers()) {
1411 assert(isa<SubfieldOp>(op));
1412 auto fieldAccess = cast<SubfieldOp>(op);
1413 auto elemIndex =
1414 fieldAccess.getInput().getType().base().getElementIndex(field);
1415 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1416 accesses.push_back(fieldAccess);
1417 }
1418 return accesses;
1419}
1420
1421/// Now that we have the operations for the hw.module's corresponding to the
1422/// firrtl.module's, we can go through and move the bodies over, updating the
1423/// ports and output op.
1424LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1425 FModuleOp oldModule, hw::HWModuleOp newModule,
1426 CircuitLoweringState &loweringState) {
1427 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1428
1429 // Use a placeholder instruction be a cursor that indicates where we want to
1430 // move the new function body to. This is important because we insert some
1431 // ops at the start of the function and some at the end, and the body is
1432 // currently empty to avoid iterator invalidation.
1433 auto cursor = bodyBuilder.create<hw::ConstantOp>(APInt(1, 1));
1434 bodyBuilder.setInsertionPoint(cursor);
1435
1436 // Insert argument casts, and re-vector users in the old body to use them.
1437 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1438 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1439 "port count mismatch");
1440
1441 SmallVector<Value, 4> outputs;
1442
1443 // This is the terminator in the new module.
1444 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1445 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1446
1447 unsigned nextHWInputArg = 0;
1448 int hwPortIndex = -1;
1449 for (auto [firrtlPortIndex, port] : llvm::enumerate(firrtlPorts)) {
1450 // Inputs and outputs are both modeled as arguments in the FIRRTL level.
1451 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1452
1453 bool isZeroWidth =
1454 type_isa<FIRRTLBaseType>(port.type) &&
1455 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1456 if (!isZeroWidth)
1457 ++hwPortIndex;
1458
1459 if (!port.isOutput() && !isZeroWidth) {
1460 // Inputs and InOuts are modeled as arguments in the result, so we can
1461 // just map them over. We model zero bit outputs as inouts.
1462 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1463
1464 // Cast the argument to the old type, reintroducing sign information in
1465 // the hw.module body.
1466 newArg = castToFIRRTLType(newArg, oldArg.getType(), bodyBuilder);
1467 // Switch all uses of the old operands to the new ones.
1468 oldArg.replaceAllUsesWith(newArg);
1469 continue;
1470 }
1471
1472 // We lower zero width inout and outputs to a wire that isn't connected to
1473 // anything outside the module. Inputs are lowered to zero.
1474 if (isZeroWidth && port.isInput()) {
1475 Value newArg = bodyBuilder
1476 .create<WireOp>(port.type, "." + port.getName().str() +
1477 ".0width_input")
1478 .getResult();
1479 oldArg.replaceAllUsesWith(newArg);
1480 continue;
1481 }
1482
1483 if (auto value =
1484 tryEliminatingConnectsToValue(oldArg, outputOp, loweringState)) {
1485 // If we were able to find the value being connected to the output,
1486 // directly use it!
1487 outputs.push_back(value);
1488 assert(oldArg.use_empty() && "should have removed all uses of oldArg");
1489 continue;
1490 }
1491
1492 // Outputs need a temporary wire so they can be connect'd to, which we
1493 // then return.
1494 auto newArg = bodyBuilder.create<WireOp>(
1495 port.type, "." + port.getName().str() + ".output");
1496
1497 // Switch all uses of the old operands to the new ones.
1498 oldArg.replaceAllUsesWith(newArg.getResult());
1499
1500 // Don't output zero bit results or inouts.
1501 auto resultHWType = loweringState.lowerType(port.type, port.loc);
1502 if (!resultHWType.isInteger(0)) {
1503 auto output =
1504 castFromFIRRTLType(newArg.getResult(), resultHWType, outputBuilder);
1505 outputs.push_back(output);
1506
1507 // If output port has symbol, move it to this wire.
1508 if (auto sym = newModule.getPort(hwPortIndex).getSym()) {
1509 newArg.setInnerSymAttr(sym);
1510 newModule.setPortSymbolAttr(hwPortIndex, {});
1511 }
1512 }
1513 }
1514
1515 // Update the hw.output terminator with the list of outputs we have.
1516 outputOp->setOperands(outputs);
1517
1518 // Finally splice the body over, don't move the old terminator over though.
1519 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1520 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1521 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1522 oldBlockInstList.begin(), oldBlockInstList.end());
1523
1524 // We are done with our cursor op.
1525 cursor.erase();
1526
1527 return success();
1528}
1529
1530/// Run on each `verif.formal` to populate its body based on the original
1531/// `firrtl.formal` operation.
1532LogicalResult
1533FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1534 CircuitLoweringState &loweringState) {
1535 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1536
1537 // Find the module targeted by the `firrtl.formal` operation. The `FormalOp`
1538 // verifier guarantees the module exists and that it is an `FModuleOp`. This
1539 // we can then translate to the corresponding `HWModuleOp`.
1540 auto oldOp = cast<FormalOp>(loweringState.getOldModule(newOp));
1541 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1542 auto oldModule = cast<FModuleOp>(
1543 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1544 auto newModule = cast<hw::HWModuleOp>(loweringState.getNewModule(oldModule));
1545
1546 // Create a symbolic input for every input of the lowered module.
1547 SmallVector<Value> symbolicInputs;
1548 for (auto arg : newModule.getBody().getArguments())
1549 symbolicInputs.push_back(
1550 builder.create<verif::SymbolicValueOp>(arg.getLoc(), arg.getType()));
1551
1552 // Instantiate the module with the given symbolic inputs.
1553 builder.create<hw::InstanceOp>(newOp.getLoc(), newModule,
1554 newModule.getNameAttr(), symbolicInputs);
1555 return success();
1556}
1557
1558/// Run on each `verif.simulation` to populate its body based on the original
1559/// `firrtl.simulation` operation.
1560LogicalResult
1561FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1562 CircuitLoweringState &loweringState) {
1563 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1564
1565 // Find the module targeted by the `firrtl.simulation` operation.
1566 auto oldOp = cast<SimulationOp>(loweringState.getOldModule(newOp));
1567 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1568 auto oldModule = cast<FModuleLike>(
1569 *loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1570 auto newModule =
1571 cast<hw::HWModuleLike>(loweringState.getNewModule(oldModule));
1572
1573 // Instantiate the module with the simulation op's block arguments as inputs,
1574 // and yield the module's outputs.
1575 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1576 newOp.getBody()->args_end());
1577 auto instOp = builder.create<hw::InstanceOp>(newOp.getLoc(), newModule,
1578 newModule.getNameAttr(), inputs);
1579 builder.create<verif::YieldOp>(newOp.getLoc(), instOp.getResults());
1580 return success();
1581}
1582
1583//===----------------------------------------------------------------------===//
1584// Module Body Lowering Pass
1585//===----------------------------------------------------------------------===//
1586
1587namespace {
1588
1589struct FIRRTLLowering : public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1590
1591 FIRRTLLowering(hw::HWModuleOp module, CircuitLoweringState &circuitState)
1592 : theModule(module), circuitState(circuitState),
1593 builder(module.getLoc(), module.getContext()), moduleNamespace(module),
1594 backedgeBuilder(builder, module.getLoc()) {}
1595
1596 LogicalResult run();
1597
1598 // Helpers.
1599 Value getOrCreateClockConstant(seq::ClockConst clock);
1600 Value getOrCreateIntConstant(const APInt &value);
1601 Value getOrCreateIntConstant(unsigned numBits, uint64_t val,
1602 bool isSigned = false) {
1603 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1604 }
1605 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1606 Value getOrCreateXConstant(unsigned numBits);
1607 Value getOrCreateZConstant(Type type);
1608 Value getPossiblyInoutLoweredValue(Value value);
1609 Value getLoweredValue(Value value);
1610 Value getLoweredNonClockValue(Value value);
1611 Value getLoweredAndExtendedValue(Value value, Type destType);
1612 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1613 LogicalResult setLowering(Value orig, Value result);
1614 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1615 template <typename ResultOpType, typename... CtorArgTypes>
1616 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1617 template <typename ResultOpType, typename... CtorArgTypes>
1618 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1619 Backedge createBackedge(Location loc, Type type);
1620 Backedge createBackedge(Value orig, Type type);
1621 bool updateIfBackedge(Value dest, Value src);
1622
1623 /// Returns true if the lowered operation requires an inner symbol on it.
1624 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1626 return true;
1627 if (!hasDroppableName(op))
1628 return true;
1629 if (auto forceable = dyn_cast<Forceable>(op.getOperation()))
1630 if (forceable.isForceable())
1631 return true;
1632 return false;
1633 }
1634
1635 /// Gets the lowered InnerSymAttr of this operation. If the operation is
1636 /// DontTouched, has a non-droppable name, or is forceable, then we will
1637 /// ensure that the InnerSymAttr has a symbol with fieldID zero.
1638 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1639 auto attr = op.getInnerSymAttr();
1640 // TODO: we should be checking for symbol collisions here and renaming as
1641 // neccessary. As well, we should record the renamings in a map so that we
1642 // can update any InnerRefAttrs that we find.
1643 if (requiresInnerSymbol(op))
1644 std::tie(attr, std::ignore) = getOrAddInnerSym(
1645 op.getContext(), attr, 0,
1646 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
1647 return attr;
1648 }
1649
1650 void runWithInsertionPointAtEndOfBlock(const std::function<void(void)> &fn,
1651 Region &region);
1652
1653 /// Return a read value for the specified inout value, auto-uniquing them.
1654 Value getReadValue(Value v);
1655 /// Return an `i1` value for the specified value, auto-uniqueing them.
1656 Value getNonClockValue(Value v);
1657
1658 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1659 sv::ResetType resetStyle, sv::EventControl resetEdge,
1660 Value reset, const std::function<void(void)> &body = {},
1661 const std::function<void(void)> &resetBody = {});
1662 void addToAlwaysBlock(Value clock,
1663 const std::function<void(void)> &body = {}) {
1664 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1665 sv::EventControl(), Value(), body,
1666 std::function<void(void)>());
1667 }
1668
1669 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1670 std::function<void(void)> emit);
1671 void addToIfDefBlock(StringRef cond, std::function<void(void)> thenCtor,
1672 std::function<void(void)> elseCtor = {});
1673 void addToInitialBlock(std::function<void(void)> body);
1674 void addIfProceduralBlock(Value cond, std::function<void(void)> thenCtor,
1675 std::function<void(void)> elseCtor = {});
1676 Value getExtOrTruncAggregateValue(Value array, FIRRTLBaseType sourceType,
1677 FIRRTLBaseType destType,
1678 bool allowTruncate);
1679 Value createArrayIndexing(Value array, Value index);
1680 Value createValueWithMuxAnnotation(Operation *op, bool isMux2);
1681
1682 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1683 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1684 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1685
1686 // Lowering hooks.
1687 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1688 UnloweredOpResult handleUnloweredOp(Operation *op);
1689 LogicalResult visitExpr(ConstantOp op);
1690 LogicalResult visitExpr(SpecialConstantOp op);
1691 LogicalResult visitExpr(SubindexOp op);
1692 LogicalResult visitExpr(SubaccessOp op);
1693 LogicalResult visitExpr(SubfieldOp op);
1694 LogicalResult visitExpr(VectorCreateOp op);
1695 LogicalResult visitExpr(BundleCreateOp op);
1696 LogicalResult visitExpr(FEnumCreateOp op);
1697 LogicalResult visitExpr(AggregateConstantOp op);
1698 LogicalResult visitExpr(IsTagOp op);
1699 LogicalResult visitExpr(SubtagOp op);
1700 LogicalResult visitUnhandledOp(Operation *op) { return failure(); }
1701 LogicalResult visitInvalidOp(Operation *op) {
1702 if (auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1703 return visitUnrealizedConversionCast(castOp);
1704 return failure();
1705 }
1706
1707 // Declarations.
1708 LogicalResult visitDecl(WireOp op);
1709 LogicalResult visitDecl(NodeOp op);
1710 LogicalResult visitDecl(RegOp op);
1711 LogicalResult visitDecl(RegResetOp op);
1712 LogicalResult visitDecl(MemOp op);
1713 LogicalResult visitDecl(InstanceOp oldInstance);
1714 LogicalResult visitDecl(VerbatimWireOp op);
1715 LogicalResult visitDecl(ContractOp op);
1716
1717 // Unary Ops.
1718 LogicalResult lowerNoopCast(Operation *op);
1719 LogicalResult visitExpr(AsSIntPrimOp op);
1720 LogicalResult visitExpr(AsUIntPrimOp op);
1721 LogicalResult visitExpr(AsClockPrimOp op);
1722 LogicalResult visitExpr(AsAsyncResetPrimOp op) { return lowerNoopCast(op); }
1723
1724 LogicalResult visitExpr(HWStructCastOp op);
1725 LogicalResult visitExpr(BitCastOp op);
1726 LogicalResult
1727 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1728 LogicalResult visitExpr(CvtPrimOp op);
1729 LogicalResult visitExpr(NotPrimOp op);
1730 LogicalResult visitExpr(NegPrimOp op);
1731 LogicalResult visitExpr(PadPrimOp op);
1732 LogicalResult visitExpr(XorRPrimOp op);
1733 LogicalResult visitExpr(AndRPrimOp op);
1734 LogicalResult visitExpr(OrRPrimOp op);
1735
1736 // Binary Ops.
1737 template <typename ResultUnsignedOpType,
1738 typename ResultSignedOpType = ResultUnsignedOpType>
1739 LogicalResult lowerBinOp(Operation *op);
1740 template <typename ResultOpType>
1741 LogicalResult lowerBinOpToVariadic(Operation *op);
1742
1743 template <typename ResultOpType>
1744 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1745
1746 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1747 ICmpPredicate unsignedOp);
1748 template <typename SignedOp, typename UnsignedOp>
1749 LogicalResult lowerDivLikeOp(Operation *op);
1750
1751 LogicalResult visitExpr(CatPrimOp op);
1752
1753 LogicalResult visitExpr(AndPrimOp op) {
1754 return lowerBinOpToVariadic<comb::AndOp>(op);
1755 }
1756 LogicalResult visitExpr(OrPrimOp op) {
1757 return lowerBinOpToVariadic<comb::OrOp>(op);
1758 }
1759 LogicalResult visitExpr(XorPrimOp op) {
1760 return lowerBinOpToVariadic<comb::XorOp>(op);
1761 }
1762 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1763 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1764 }
1765 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1766 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1767 }
1768 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1769 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1770 }
1771 LogicalResult visitExpr(AddPrimOp op) {
1772 return lowerBinOpToVariadic<comb::AddOp>(op);
1773 }
1774 LogicalResult visitExpr(EQPrimOp op) {
1775 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1776 }
1777 LogicalResult visitExpr(NEQPrimOp op) {
1778 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1779 }
1780 LogicalResult visitExpr(LTPrimOp op) {
1781 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1782 }
1783 LogicalResult visitExpr(LEQPrimOp op) {
1784 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1785 }
1786 LogicalResult visitExpr(GTPrimOp op) {
1787 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1788 }
1789 LogicalResult visitExpr(GEQPrimOp op) {
1790 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1791 }
1792
1793 LogicalResult visitExpr(SubPrimOp op) { return lowerBinOp<comb::SubOp>(op); }
1794 LogicalResult visitExpr(MulPrimOp op) {
1795 return lowerBinOpToVariadic<comb::MulOp>(op);
1796 }
1797 LogicalResult visitExpr(DivPrimOp op) {
1798 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1799 }
1800 LogicalResult visitExpr(RemPrimOp op) {
1801 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1802 }
1803
1804 // Intrinsic Operations
1805 LogicalResult visitExpr(IsXIntrinsicOp op);
1806 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1807 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1808 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1809 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1810 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1811 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1812 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1813 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1814 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1815 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1816 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1817 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1818 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1819 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1820 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1821 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1822 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1823 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1824 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1825 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1826
1827 template <typename TargetOp, typename IntrinsicOp>
1828 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1829 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1830 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1831 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1832 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
1833 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
1834 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1835 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1836
1837 // Other Operations
1838 LogicalResult visitExpr(BitsPrimOp op);
1839 LogicalResult visitExpr(InvalidValueOp op);
1840 LogicalResult visitExpr(HeadPrimOp op);
1841 LogicalResult visitExpr(ShlPrimOp op);
1842 LogicalResult visitExpr(ShrPrimOp op);
1843 LogicalResult visitExpr(DShlPrimOp op) {
1844 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1845 }
1846 LogicalResult visitExpr(DShrPrimOp op) {
1847 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1848 }
1849 LogicalResult visitExpr(DShlwPrimOp op) {
1850 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1851 }
1852 LogicalResult visitExpr(TailPrimOp op);
1853 LogicalResult visitExpr(MuxPrimOp op);
1854 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1855 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1856 LogicalResult visitExpr(MultibitMuxOp op);
1857 LogicalResult visitExpr(VerbatimExprOp op);
1858 LogicalResult visitExpr(XMRRefOp op);
1859 LogicalResult visitExpr(XMRDerefOp op);
1860
1861 // Format String Operations
1862 LogicalResult visitExpr(TimeOp op);
1863 LogicalResult visitExpr(HierarchicalModuleNameOp op);
1864
1865 // Statements
1866 LogicalResult lowerVerificationStatement(
1867 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1868 Value enable, StringAttr messageAttr, ValueRange operands,
1869 StringAttr nameAttr, bool isConcurrent, EventControl eventControl);
1870
1871 LogicalResult visitStmt(SkipOp op);
1872
1873 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
1874 LogicalResult visitStmt(ConnectOp op);
1875 LogicalResult visitStmt(MatchingConnectOp op);
1876 LogicalResult visitStmt(ForceOp op);
1877
1878 std::optional<Value> getLoweredFmtOperand(Value operand);
1879 LogicalResult loweredFmtOperands(ValueRange operands,
1880 SmallVectorImpl<Value> &loweredOperands);
1881 FailureOr<Value> callFileDescriptorLib(const FileDescriptorInfo &info);
1882 // Lower statemens that use file descriptors such as printf, fprintf and
1883 // fflush. `fn` is a function that takes a file descriptor and build an always
1884 // and if-procedural block.
1885 LogicalResult lowerStatementWithFd(
1886 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
1887 const std::function<LogicalResult(Value)> &fn, bool usePrintfCond);
1888 // Lower a printf-like operation. `fileDescriptorInfo` is a pair of the
1889 // file name and whether it requires format string substitution.
1890 template <class T>
1891 LogicalResult visitPrintfLike(T op,
1892 const FileDescriptorInfo &fileDescriptorInfo,
1893 bool usePrintfCond);
1894 LogicalResult visitStmt(PrintFOp op) { return visitPrintfLike(op, {}, true); }
1895 LogicalResult visitStmt(FPrintFOp op);
1896 LogicalResult visitStmt(FFlushOp op);
1897 LogicalResult visitStmt(StopOp op);
1898 LogicalResult visitStmt(AssertOp op);
1899 LogicalResult visitStmt(AssumeOp op);
1900 LogicalResult visitStmt(CoverOp op);
1901 LogicalResult visitStmt(AttachOp op);
1902 LogicalResult visitStmt(RefForceOp op);
1903 LogicalResult visitStmt(RefForceInitialOp op);
1904 LogicalResult visitStmt(RefReleaseOp op);
1905 LogicalResult visitStmt(RefReleaseInitialOp op);
1906 LogicalResult visitStmt(BindOp op);
1907
1908 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
1909 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
1910 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
1911
1912 LogicalResult fixupLTLOps();
1913
1914 Type lowerType(Type type) {
1915 return circuitState.lowerType(type, builder.getLoc());
1916 }
1917
1918private:
1919 /// The module we're lowering into.
1920 hw::HWModuleOp theModule;
1921
1922 /// Global state.
1923 CircuitLoweringState &circuitState;
1924
1925 /// This builder is set to the right location for each visit call.
1926 ImplicitLocOpBuilder builder;
1927
1928 /// Each value lowered (e.g. operation result) is kept track in this map.
1929 /// The key should have a FIRRTL type, the result will have an HW dialect
1930 /// type.
1931 DenseMap<Value, Value> valueMapping;
1932
1933 /// Mapping from clock values to corresponding non-clock values converted
1934 /// via a deduped `seq.from_clock` op.
1935 DenseMap<Value, Value> fromClockMapping;
1936
1937 /// This keeps track of constants that we have created so we can reuse them.
1938 /// This is populated by the getOrCreateIntConstant method.
1939 DenseMap<Attribute, Value> hwConstantMap;
1940 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1941
1942 /// This keeps track of constant X that we have created so we can reuse them.
1943 /// This is populated by the getOrCreateXConstant method.
1944 DenseMap<unsigned, Value> hwConstantXMap;
1945 DenseMap<Type, Value> hwConstantZMap;
1946
1947 /// We auto-unique "ReadInOut" ops from wires and regs, enabling
1948 /// optimizations and CSEs of the read values to be more obvious. This
1949 /// caches a known ReadInOutOp for the given value and is managed by
1950 /// `getReadValue(v)`.
1951 DenseMap<Value, Value> readInOutCreated;
1952
1953 /// This keeps track of the file descriptors for each file name.
1954 DenseMap<StringAttr, sv::RegOp> fileNameToFileDescriptor;
1955
1956 // We auto-unique graph-level blocks to reduce the amount of generated
1957 // code and ensure that side effects are properly ordered in FIRRTL.
1958 using AlwaysKeyType = std::tuple<Block *, sv::EventControl, Value,
1959 sv::ResetType, sv::EventControl, Value>;
1961 alwaysBlocks;
1964
1965 /// A namespace that can be used to generate new symbol names that are unique
1966 /// within this module.
1967 hw::InnerSymbolNamespace moduleNamespace;
1968
1969 /// A backedge builder to directly materialize values during the lowering
1970 /// without requiring temporary wires.
1971 BackedgeBuilder backedgeBuilder;
1972 /// Currently unresolved backedges. More precisely, a mapping from the
1973 /// backedge value to the value it will be replaced with. We use a MapVector
1974 /// so that a combinational cycles of backedges, the one backedge that gets
1975 /// replaced with an undriven wire is consistent.
1976 llvm::MapVector<Value, Value> backedges;
1977
1978 /// A collection of values generated by the lowering process that may have
1979 /// become obsolete through subsequent parts of the lowering. This covers the
1980 /// values of wires that may be overridden by subsequent connects; or
1981 /// subaccesses that appear only as destination of a connect, and thus gets
1982 /// obsoleted by the connect directly updating the wire or register.
1983 DenseSet<Operation *> maybeUnusedValues;
1984
1985 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1986 void maybeUnused(Value value) {
1987 if (auto *op = value.getDefiningOp())
1988 maybeUnused(op);
1989 }
1990
1991 /// A worklist of LTL operations that don't have their final type yet. The
1992 /// FIRRTL intrinsics for LTL ops all use `uint<1>` types, but the actual LTL
1993 /// ops themselves have more precise `!ltl.sequence` and `!ltl.property`
1994 /// types. After all LTL ops have been lowered, this worklist is used to
1995 /// compute their actual types (re-inferring return types) and push the
1996 /// updated types to their users. This also drops any `hw.wire`s in between
1997 /// the LTL ops, which were necessary to go from the def-before-use FIRRTL
1998 /// dialect to the graph-like HW dialect.
1999 SetVector<Operation *> ltlOpFixupWorklist;
2000
2001 /// A worklist of operation ranges to be lowered. Parnet operations can push
2002 /// their nested operations onto this worklist to be processed after the
2003 /// parent operation has handled the region, blocks, and block arguments.
2004 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2005
2006 void addToWorklist(Block &block) {
2007 worklist.push_back({block.begin(), block.end()});
2008 }
2009 void addToWorklist(Region &region) {
2010 for (auto &block : llvm::reverse(region))
2011 addToWorklist(block);
2012 }
2013};
2014} // end anonymous namespace
2015
2016LogicalResult
2017FIRRTLModuleLowering::lowerModuleBody(hw::HWModuleOp module,
2018 CircuitLoweringState &loweringState) {
2019 return FIRRTLLowering(module, loweringState).run();
2020}
2021
2022LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2023 OpBuilder b(&getContext());
2024 fileOp->walk([&](Operation *op) {
2025 if (auto bindOp = dyn_cast<BindOp>(op)) {
2026 b.setInsertionPointAfter(bindOp);
2027 b.create<sv::BindOp>(bindOp.getLoc(), bindOp.getInstanceAttr());
2028 bindOp->erase();
2029 }
2030 });
2031 return success();
2032}
2033
2034LogicalResult
2035FIRRTLModuleLowering::lowerBody(Operation *op,
2036 CircuitLoweringState &loweringState) {
2037 if (auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2038 return lowerModuleBody(moduleOp, loweringState);
2039 if (auto formalOp = dyn_cast<verif::FormalOp>(op))
2040 return lowerFormalBody(formalOp, loweringState);
2041 if (auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2042 return lowerSimulationBody(simulationOp, loweringState);
2043 if (auto fileOp = dyn_cast<emit::FileOp>(op))
2044 return lowerFileBody(fileOp);
2045 return failure();
2046}
2047
2048// This is the main entrypoint for the lowering pass.
2049LogicalResult FIRRTLLowering::run() {
2050 // Mark the module's block arguments are already lowered. This will allow
2051 // `getLoweredValue` to return the block arguments as they are.
2052 for (auto arg : theModule.getBodyBlock()->getArguments())
2053 if (failed(setLowering(arg, arg)))
2054 return failure();
2055
2056 // Add the operations in the body to the worklist and lower all operations
2057 // until the worklist is empty. Operations may push their own nested
2058 // operations onto the worklist to lower them in turn. The `builder` is
2059 // positioned ahead of each operation as it is being lowered.
2060 addToWorklist(theModule.getBody());
2061 SmallVector<Operation *, 16> opsToRemove;
2062
2063 while (!worklist.empty()) {
2064 auto &[opsIt, opsEnd] = worklist.back();
2065 if (opsIt == opsEnd) {
2066 worklist.pop_back();
2067 continue;
2068 }
2069 Operation *op = &*opsIt++;
2070
2071 builder.setInsertionPoint(op);
2072 builder.setLoc(op->getLoc());
2073 auto done = succeeded(dispatchVisitor(op));
2074 circuitState.processRemainingAnnotations(op, AnnotationSet(op));
2075 if (done)
2076 opsToRemove.push_back(op);
2077 else {
2078 switch (handleUnloweredOp(op)) {
2079 case AlreadyLowered:
2080 break; // Something like hw.output, which is already lowered.
2081 case NowLowered: // Something handleUnloweredOp removed.
2082 opsToRemove.push_back(op);
2083 break;
2084 case LoweringFailure:
2085 backedgeBuilder.abandon();
2086 return failure();
2087 }
2088 }
2089 }
2090
2091 // Replace all backedges with uses of their regular values. We process them
2092 // after the module body since the lowering table is too hard to keep up to
2093 // date. Multiple operations may be lowered to the same backedge when values
2094 // are folded, which means we would have to scan the entire lowering table to
2095 // safely replace a backedge.
2096 for (auto &[backedge, value] : backedges) {
2097 SmallVector<Location> driverLocs;
2098 // In the case where we have backedges connected to other backedges, we have
2099 // to find the value that actually drives the group.
2100 while (true) {
2101 // If we find the original backedge we have some undriven logic or
2102 // a combinatorial loop. Bail out and provide information on the nodes.
2103 if (backedge == value) {
2104 Location edgeLoc = backedge.getLoc();
2105 if (driverLocs.empty()) {
2106 mlir::emitError(edgeLoc, "sink does not have a driver");
2107 } else {
2108 auto diag = mlir::emitError(edgeLoc, "sink in combinational loop");
2109 for (auto loc : driverLocs)
2110 diag.attachNote(loc) << "through driver here";
2111 }
2112 backedgeBuilder.abandon();
2113 return failure();
2114 }
2115 // If the value is not another backedge, we have found the driver.
2116 auto *it = backedges.find(value);
2117 if (it == backedges.end())
2118 break;
2119 // Find what is driving the next backedge.
2120 driverLocs.push_back(value.getLoc());
2121 value = it->second;
2122 }
2123 if (auto *defOp = backedge.getDefiningOp())
2124 maybeUnusedValues.erase(defOp);
2125 backedge.replaceAllUsesWith(value);
2126 }
2127
2128 // Now that all of the operations that can be lowered are, remove th
2129 // original values. We know that any lowered operations will be dead (if
2130 // removed in reverse order) at this point - any users of them from
2131 // unremapped operations will be changed to use the newly lowered ops.
2132 hw::ConstantOp zeroI0;
2133 while (!opsToRemove.empty()) {
2134 auto *op = opsToRemove.pop_back_val();
2135
2136 // We remove zero-width values when lowering FIRRTL ops. We can't remove
2137 // such a value if it escapes to a foreign op. In that case, create an
2138 // `hw.constant 0 : i0` to pass along.
2139 for (auto result : op->getResults()) {
2140 if (!isZeroBitFIRRTLType(result.getType()))
2141 continue;
2142 if (!zeroI0) {
2143 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2144 zeroI0 = builder.create<hw::ConstantOp>(op->getLoc(),
2145 builder.getIntegerType(0), 0);
2146 maybeUnusedValues.insert(zeroI0);
2147 }
2148 result.replaceAllUsesWith(zeroI0);
2149 }
2150
2151 if (!op->use_empty()) {
2152 auto d = op->emitOpError(
2153 "still has uses; should remove ops in reverse order of visitation");
2154 SmallPtrSet<Operation *, 2> visited;
2155 for (auto *user : op->getUsers())
2156 if (visited.insert(user).second)
2157 d.attachNote(user->getLoc())
2158 << "used by " << user->getName() << " op";
2159 return d;
2160 }
2161 maybeUnusedValues.erase(op);
2162 op->erase();
2163 }
2164
2165 // Prune operations that may have become unused throughout the lowering.
2166 while (!maybeUnusedValues.empty()) {
2167 auto it = maybeUnusedValues.begin();
2168 auto *op = *it;
2169 maybeUnusedValues.erase(it);
2170 if (!isOpTriviallyDead(op))
2171 continue;
2172 for (auto operand : op->getOperands())
2173 if (auto *defOp = operand.getDefiningOp())
2174 maybeUnusedValues.insert(defOp);
2175 op->erase();
2176 }
2177
2178 // Determine the actual types of lowered LTL operations and remove any
2179 // intermediate wires among them.
2180 if (failed(fixupLTLOps()))
2181 return failure();
2182
2183 return backedgeBuilder.clearOrEmitError();
2184}
2185
2186//===----------------------------------------------------------------------===//
2187// Helpers
2188//===----------------------------------------------------------------------===//
2189
2190/// Create uniqued constant clocks.
2191Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2192 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2193
2194 auto &entry = hwConstantMap[attr];
2195 if (entry)
2196 return entry;
2197
2198 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2199 entry = entryBuilder.create<seq::ConstClockOp>(builder.getLoc(), attr);
2200 return entry;
2201}
2202
2203/// Check to see if we've already lowered the specified constant. If so,
2204/// return it. Otherwise create it and put it in the entry block for reuse.
2205Value FIRRTLLowering::getOrCreateIntConstant(const APInt &value) {
2206 auto attr = builder.getIntegerAttr(
2207 builder.getIntegerType(value.getBitWidth()), value);
2208
2209 auto &entry = hwConstantMap[attr];
2210 if (entry)
2211 return entry;
2212
2213 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2214 entry = entryBuilder.create<hw::ConstantOp>(builder.getLoc(), attr);
2215 return entry;
2216}
2217
2218/// Check to see if we've already created the specified aggregate constant
2219/// attribute. If so, return it. Otherwise create it.
2220Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2221 Type type) {
2222 // Base case.
2223 if (hw::type_isa<IntegerType>(type))
2224 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2225
2226 auto cache = hwAggregateConstantMap.lookup({value, type});
2227 if (cache)
2228 return cache;
2229
2230 // Recursively construct elements.
2231 SmallVector<Attribute> values;
2232 for (auto e : llvm::enumerate(cast<ArrayAttr>(value))) {
2233 Type subType;
2234 if (auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2235 subType = array.getElementType();
2236 else if (auto structType = hw::type_dyn_cast<hw::StructType>(type))
2237 subType = structType.getElements()[e.index()].type;
2238 else
2239 assert(false && "type must be either array or struct");
2240
2241 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2242 }
2243
2244 // FIRRTL and HW have a different operand ordering for arrays.
2245 if (hw::type_isa<hw::ArrayType>(type))
2246 std::reverse(values.begin(), values.end());
2247
2248 auto &entry = hwAggregateConstantMap[{value, type}];
2249 entry = builder.getArrayAttr(values);
2250 return entry;
2251}
2252
2253/// Zero bit operands end up looking like failures from getLoweredValue. This
2254/// helper function invokes the closure specified if the operand was actually
2255/// zero bit, or returns failure() if it was some other kind of failure.
2256static LogicalResult handleZeroBit(Value failedOperand,
2257 const std::function<LogicalResult()> &fn) {
2258 assert(failedOperand && "Should be called on the failed operand");
2259 if (!isZeroBitFIRRTLType(failedOperand.getType()))
2260 return failure();
2261 return fn();
2262}
2263
2264/// Check to see if we've already lowered the specified constant. If so,
2265/// return it. Otherwise create it and put it in the entry block for reuse.
2266Value FIRRTLLowering::getOrCreateXConstant(unsigned numBits) {
2267
2268 auto &entry = hwConstantXMap[numBits];
2269 if (entry)
2270 return entry;
2271
2272 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2273 entry = entryBuilder.create<sv::ConstantXOp>(
2274 builder.getLoc(), entryBuilder.getIntegerType(numBits));
2275 return entry;
2276}
2277
2278Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2279 auto &entry = hwConstantZMap[type];
2280 if (!entry) {
2281 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2282 entry = entryBuilder.create<sv::ConstantZOp>(builder.getLoc(), type);
2283 }
2284 return entry;
2285}
2286
2287/// Return the lowered HW value corresponding to the specified original value.
2288/// This returns a null value for FIRRTL values that haven't be lowered, e.g.
2289/// unknown width integers. This returns hw::inout type values if present, it
2290/// does not implicitly read from them.
2291Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2292 // If we lowered this value, then return the lowered value, otherwise fail.
2293 if (auto lowering = valueMapping.lookup(value)) {
2294 assert(!isa<FIRRTLType>(lowering.getType()) &&
2295 "Lowered value should be a non-FIRRTL value");
2296 return lowering;
2297 }
2298 return Value();
2299}
2300
2301/// Return the lowered value corresponding to the specified original value.
2302/// This returns a null value for FIRRTL values that cannot be lowered, e.g.
2303/// unknown width integers.
2304Value FIRRTLLowering::getLoweredValue(Value value) {
2305 auto result = getPossiblyInoutLoweredValue(value);
2306 if (!result)
2307 return result;
2308
2309 // If we got an inout value, implicitly read it. FIRRTL allows direct use
2310 // of wires and other things that lower to inout type.
2311 if (isa<hw::InOutType>(result.getType()))
2312 return getReadValue(result);
2313
2314 return result;
2315}
2316
2317/// Return the lowered value, converting `seq.clock` to `i1.
2318Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2319 auto result = getLoweredValue(value);
2320 if (!result)
2321 return result;
2322
2323 if (hw::type_isa<seq::ClockType>(result.getType()))
2324 return getNonClockValue(result);
2325
2326 return result;
2327}
2328
2329/// Return the lowered aggregate value whose type is converted into
2330/// `destType`. We have to care about the extension/truncation/signedness of
2331/// each element.
2332Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2333 FIRRTLBaseType sourceType,
2334 FIRRTLBaseType destType,
2335 bool allowTruncate) {
2336 SmallVector<Value> resultBuffer;
2337
2338 // Helper function to cast each element of array to dest type.
2339 auto cast = [&](Value value, FIRRTLBaseType sourceType,
2340 FIRRTLBaseType destType) {
2341 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2342 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2343 auto resultType = builder.getIntegerType(destWidth);
2344
2345 if (srcWidth == destWidth)
2346 return value;
2347
2348 if (srcWidth > destWidth) {
2349 if (allowTruncate)
2350 return builder.createOrFold<comb::ExtractOp>(resultType, value, 0);
2351
2352 builder.emitError("operand should not be a truncation");
2353 return Value();
2354 }
2355
2356 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2357 return comb::createOrFoldSExt(value, resultType, builder);
2358 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2359 return builder.createOrFold<comb::ConcatOp>(zero, value);
2360 };
2361
2362 // This recursive function constructs the output array.
2363 std::function<LogicalResult(Value, FIRRTLBaseType, FIRRTLBaseType)> recurse =
2364 [&](Value src, FIRRTLBaseType srcType,
2365 FIRRTLBaseType destType) -> LogicalResult {
2366 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2367 .Case<FVectorType>([&](auto srcVectorType) {
2368 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2369 unsigned size = resultBuffer.size();
2370 unsigned indexWidth =
2371 getBitWidthFromVectorSize(srcVectorType.getNumElements());
2372 for (size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2373 destVectorType.getNumElements());
2374 i != e; ++i) {
2375 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2376 auto arrayIndex = builder.create<hw::ArrayGetOp>(src, iIdx);
2377 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2378 destVectorType.getElementType())))
2379 return failure();
2380 }
2381 SmallVector<Value> temp(resultBuffer.begin() + size,
2382 resultBuffer.end());
2383 auto array = builder.createOrFold<hw::ArrayCreateOp>(temp);
2384 resultBuffer.resize(size);
2385 resultBuffer.push_back(array);
2386 return success();
2387 })
2388 .Case<BundleType>([&](BundleType srcStructType) {
2389 auto destStructType = firrtl::type_cast<BundleType>(destType);
2390 unsigned size = resultBuffer.size();
2391
2392 // TODO: We don't support partial connects for bundles for now.
2393 if (destStructType.getNumElements() != srcStructType.getNumElements())
2394 return failure();
2395
2396 for (auto elem : llvm::enumerate(destStructType)) {
2397 auto structExtract =
2398 builder.create<hw::StructExtractOp>(src, elem.value().name);
2399 if (failed(recurse(structExtract,
2400 srcStructType.getElementType(elem.index()),
2401 destStructType.getElementType(elem.index()))))
2402 return failure();
2403 }
2404 SmallVector<Value> temp(resultBuffer.begin() + size,
2405 resultBuffer.end());
2406 auto newStruct = builder.createOrFold<hw::StructCreateOp>(
2407 lowerType(destStructType), temp);
2408 resultBuffer.resize(size);
2409 resultBuffer.push_back(newStruct);
2410 return success();
2411 })
2412 .Case<IntType>([&](auto) {
2413 if (auto result = cast(src, srcType, destType)) {
2414 resultBuffer.push_back(result);
2415 return success();
2416 }
2417 return failure();
2418 })
2419 .Default([&](auto) { return failure(); });
2420 };
2421
2422 if (failed(recurse(array, sourceType, destType)))
2423 return Value();
2424
2425 assert(resultBuffer.size() == 1 &&
2426 "resultBuffer must only contain a result array if `success` is true");
2427 return resultBuffer[0];
2428}
2429
2430/// Return the lowered value corresponding to the specified original value and
2431/// then extend it to match the width of destType if needed.
2432///
2433/// This returns a null value for FIRRTL values that cannot be lowered, e.g.
2434/// unknown width integers.
2435Value FIRRTLLowering::getLoweredAndExtendedValue(Value value, Type destType) {
2436 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2437 type_isa<FIRRTLBaseType>(destType) &&
2438 "input/output value should be FIRRTL");
2439
2440 // We only know how to extend integer types with known width.
2441 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2442 if (destWidth == -1)
2443 return {};
2444
2445 auto result = getLoweredValue(value);
2446 if (!result) {
2447 // If this was a zero bit operand being extended, then produce a zero of
2448 // the right result type. If it is just a failure, fail.
2449 if (!isZeroBitFIRRTLType(value.getType()))
2450 return {};
2451 // Zero bit results have to be returned as null. The caller can handle
2452 // this if they want to.
2453 if (destWidth == 0)
2454 return {};
2455 // Otherwise, FIRRTL semantics is that an extension from a zero bit value
2456 // always produces a zero value in the destination width.
2457 return getOrCreateIntConstant(destWidth, 0);
2458 }
2459
2460 if (destWidth ==
2461 cast<FIRRTLBaseType>(value.getType()).getBitWidthOrSentinel()) {
2462 // Lookup the lowered type of dest.
2463 auto loweredDstType = lowerType(destType);
2464 if (result.getType() != loweredDstType &&
2465 (isa<hw::TypeAliasType>(result.getType()) ||
2466 isa<hw::TypeAliasType>(loweredDstType))) {
2467 return builder.createOrFold<hw::BitcastOp>(loweredDstType, result);
2468 }
2469 }
2470 // Aggregates values
2471 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2472 // Types already match.
2473 if (destType == value.getType())
2474 return result;
2475
2476 return getExtOrTruncAggregateValue(
2477 result, type_cast<FIRRTLBaseType>(value.getType()),
2478 type_cast<FIRRTLBaseType>(destType),
2479 /* allowTruncate */ false);
2480 }
2481
2482 if (isa<seq::ClockType>(result.getType())) {
2483 // Types already match.
2484 if (destType == value.getType())
2485 return result;
2486 builder.emitError("cannot use clock type as an integer");
2487 return {};
2488 }
2489
2490 auto intResultType = dyn_cast<IntegerType>(result.getType());
2491 if (!intResultType) {
2492 builder.emitError("operand of type ")
2493 << result.getType() << " cannot be used as an integer";
2494 return {};
2495 }
2496
2497 auto srcWidth = intResultType.getWidth();
2498 if (srcWidth == unsigned(destWidth))
2499 return result;
2500
2501 if (srcWidth > unsigned(destWidth)) {
2502 builder.emitError("operand should not be a truncation");
2503 return {};
2504 }
2505
2506 auto resultType = builder.getIntegerType(destWidth);
2507
2508 // Extension follows the sign of the source value, not the destination.
2509 auto valueFIRType =
2510 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2511 if (type_cast<IntType>(valueFIRType).isSigned())
2512 return comb::createOrFoldSExt(result, resultType, builder);
2513
2514 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2515 return builder.createOrFold<comb::ConcatOp>(zero, result);
2516}
2517
2518/// Return the lowered value corresponding to the specified original value and
2519/// then extended or truncated to match the width of destType if needed.
2520///
2521/// This returns a null value for FIRRTL values that cannot be lowered, e.g.
2522/// unknown width integers.
2523Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2524 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2525 type_isa<FIRRTLBaseType>(destType) &&
2526 "input/output value should be FIRRTL");
2527
2528 // We only know how to adjust integer types with known width.
2529 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2530 if (destWidth == -1)
2531 return {};
2532
2533 auto result = getLoweredValue(value);
2534 if (!result) {
2535 // If this was a zero bit operand being extended, then produce a zero of
2536 // the right result type. If it is just a failure, fail.
2537 if (!isZeroBitFIRRTLType(value.getType()))
2538 return {};
2539 // Zero bit results have to be returned as null. The caller can handle
2540 // this if they want to.
2541 if (destWidth == 0)
2542 return {};
2543 // Otherwise, FIRRTL semantics is that an extension from a zero bit value
2544 // always produces a zero value in the destination width.
2545 return getOrCreateIntConstant(destWidth, 0);
2546 }
2547
2548 // Aggregates values
2549 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2550 // Types already match.
2551 if (destType == value.getType())
2552 return result;
2553
2554 return getExtOrTruncAggregateValue(
2555 result, type_cast<FIRRTLBaseType>(value.getType()),
2556 type_cast<FIRRTLBaseType>(destType),
2557 /* allowTruncate */ true);
2558 }
2559
2560 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2561 if (srcWidth == unsigned(destWidth))
2562 return result;
2563
2564 if (destWidth == 0)
2565 return {};
2566
2567 if (srcWidth > unsigned(destWidth)) {
2568 auto resultType = builder.getIntegerType(destWidth);
2569 return builder.createOrFold<comb::ExtractOp>(resultType, result, 0);
2570 }
2571
2572 auto resultType = builder.getIntegerType(destWidth);
2573
2574 // Extension follows the sign of the source value, not the destination.
2575 auto valueFIRType =
2576 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2577 if (type_cast<IntType>(valueFIRType).isSigned())
2578 return comb::createOrFoldSExt(result, resultType, builder);
2579
2580 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2581 return builder.createOrFold<comb::ConcatOp>(zero, result);
2582}
2583
2584/// Return a lowered version of 'operand' suitable for use with substitution /
2585/// format strings. There are three possible results:
2586///
2587/// 1. Does not contain a value if no lowering is set. This is an error.
2588/// 2. The lowering contains an empty value. This means that the operand
2589/// should be dropped.
2590/// 3. The lowering contains a value. This means the operand should be used.
2591///
2592/// Zero bit operands are rewritten as one bit zeros and signed integers are
2593/// wrapped in $signed().
2594std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2595 // Handle special substitutions.
2596 if (type_isa<FStringType>(operand.getType())) {
2597 if (isa<TimeOp>(operand.getDefiningOp()))
2598 return builder.create<sv::TimeOp>();
2599 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2600 return {nullptr};
2601 }
2602
2603 auto loweredValue = getLoweredValue(operand);
2604 if (!loweredValue) {
2605 // If this is a zero bit operand, just pass a one bit zero.
2606 if (!isZeroBitFIRRTLType(operand.getType()))
2607 return {};
2608 loweredValue = getOrCreateIntConstant(1, 0);
2609 }
2610
2611 // If the operand was an SInt, we want to give the user the option to print
2612 // it as signed decimal and have to wrap it in $signed().
2613 if (auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2614 if (intTy.isSigned())
2615 loweredValue = builder.create<sv::SystemFunctionOp>(
2616 loweredValue.getType(), "signed", loweredValue);
2617
2618 return loweredValue;
2619}
2620
2621LogicalResult
2622FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2623 SmallVectorImpl<Value> &loweredOperands) {
2624 for (auto operand : operands) {
2625 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2626 if (!loweredValue)
2627 return failure();
2628 // Skip if the lowered value is null.
2629 if (*loweredValue)
2630 loweredOperands.push_back(*loweredValue);
2631 }
2632 return success();
2633}
2634
2635LogicalResult FIRRTLLowering::lowerStatementWithFd(
2636 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
2637 const std::function<LogicalResult(Value)> &fn, bool usePrintfCond) {
2638 // Emit an "#ifndef SYNTHESIS" guard into the always block.
2639 bool failed = false;
2640 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
2641 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
2642 addToAlwaysBlock(clock, [&]() {
2643 // TODO: This is not printf specific anymore. Replace "Printf" with "FD"
2644 // or similar but be aware that changing macro name breaks existing uses.
2645 circuitState.usedPrintf = true;
2646 if (usePrintfCond)
2647 circuitState.addFragment(theModule, "PRINTF_COND_FRAGMENT");
2648
2649 // Emit an "sv.if '`PRINTF_COND_ & cond' into the #ifndef.
2650 Value ifCond = cond;
2651 if (usePrintfCond) {
2652 ifCond =
2653 builder.create<sv::MacroRefExprOp>(cond.getType(), "PRINTF_COND_");
2654 ifCond = builder.createOrFold<comb::AndOp>(ifCond, cond, true);
2655 }
2656
2657 addIfProceduralBlock(ifCond, [&]() {
2658 // Create a value specified by `PRINTF_FD` for printf, or the file
2659 // descriptor opened using $fopen for fprintf.
2660 Value fd;
2661 if (fileDescriptor.isDefaultFd()) {
2662 fd = builder.create<sv::MacroRefExprOp>(builder.getIntegerType(32),
2663 "PRINTF_FD_");
2664 circuitState.addFragment(theModule, "PRINTF_FD_FRAGMENT");
2665 } else {
2666 // Call the library function to get the FD.
2667 auto fdOrError = callFileDescriptorLib(fileDescriptor);
2668 if (llvm::failed(fdOrError)) {
2669 failed = true;
2670 return;
2671 }
2672 fd = *fdOrError;
2673 }
2674 failed = llvm::failed(fn(fd));
2675 });
2676 });
2677 });
2678 return failure(failed);
2679}
2680
2681FailureOr<Value>
2682FIRRTLLowering::callFileDescriptorLib(const FileDescriptorInfo &info) {
2683 circuitState.usedFileDescriptorLib = true;
2684 circuitState.addFragment(theModule, "CIRCT_LIB_LOGGING_FRAGMENT");
2685
2686 Value fileName;
2687 if (info.isSubstitutionRequired()) {
2688 SmallVector<Value> fileNameOperands;
2689 if (failed(loweredFmtOperands(info.getSubstitutions(), fileNameOperands)))
2690 return failure();
2691
2692 fileName = builder
2693 .create<sv::SFormatFOp>(info.getOutputFileFormat(),
2694 fileNameOperands)
2695 .getResult();
2696 } else {
2697 // If substitution is not required, just use the output file name.
2698 fileName = builder.create<sv::ConstantStrOp>(info.getOutputFileFormat())
2699 .getResult();
2700 }
2701
2702 return builder
2703 .create<sv::FuncCallProceduralOp>(
2704 mlir::TypeRange{builder.getIntegerType(32)},
2705 builder.getStringAttr("__circt_lib_logging::FileDescriptor::get"),
2706 ValueRange{fileName})
2707 ->getResult(0);
2708}
2709
2710/// Set the lowered value of 'orig' to 'result', remembering this in a map.
2711/// This always returns success() to make it more convenient in lowering code.
2712///
2713/// Note that result may be null here if we're lowering orig to a zero-bit
2714/// value.
2715///
2716LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2717 if (auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2718 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2719 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2720
2721#ifndef NDEBUG
2722 auto baseType = getBaseType(origType);
2723 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2724
2725 // Caller should pass null value iff this was a zero bit value.
2726 if (srcWidth != -1) {
2727 if (result)
2728 assert((srcWidth != 0) &&
2729 "Lowering produced value for zero width source");
2730 else
2731 assert((srcWidth == 0) &&
2732 "Lowering produced null value but source wasn't zero width");
2733 }
2734#endif
2735 } else {
2736 assert(result && "Lowering of foreign type produced null value");
2737 }
2738
2739 auto &slot = valueMapping[orig];
2740 assert(!slot && "value lowered multiple times");
2741 slot = result;
2742 return success();
2743}
2744
2745/// Set the lowering for a value to the specified result. This came from a
2746/// possible folding, so check to see if we need to handle a constant.
2747LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2748 Value result) {
2749 // If this is a constant, check to see if we have it in our unique mapping:
2750 // it could have come from folding an operation.
2751 if (auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2752 auto &entry = hwConstantMap[cst.getValueAttr()];
2753 if (entry == cst) {
2754 // We're already using an entry in the constant map, nothing to do.
2755 } else if (entry) {
2756 // We already had this constant, reuse the one we have instead of the
2757 // one we just folded.
2758 result = entry;
2759 cst->erase();
2760 } else {
2761 // This is a new constant. Remember it!
2762 entry = cst;
2763 cst->moveBefore(&theModule.getBodyBlock()->front());
2764 }
2765 }
2766
2767 return setLowering(orig, result);
2768}
2769
2770/// Create a new operation with type ResultOpType and arguments CtorArgTypes,
2771/// then call setLowering with its result.
2772template <typename ResultOpType, typename... CtorArgTypes>
2773LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2774 CtorArgTypes... args) {
2775 auto result = builder.createOrFold<ResultOpType>(args...);
2776 if (auto *op = result.getDefiningOp())
2777 tryCopyName(op, orig);
2778 return setPossiblyFoldedLowering(orig->getResult(0), result);
2779}
2780
2781/// Create a new LTL operation with type ResultOpType and arguments
2782/// CtorArgTypes, then call setLowering with its result. Also add the operation
2783/// to the worklist of LTL ops that need to have their types fixed-up after the
2784/// lowering.
2785template <typename ResultOpType, typename... CtorArgTypes>
2786LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2787 CtorArgTypes... args) {
2788 auto result = builder.createOrFold<ResultOpType>(args...);
2789 if (auto *op = result.getDefiningOp())
2790 ltlOpFixupWorklist.insert(op);
2791 return setPossiblyFoldedLowering(orig->getResult(0), result);
2792}
2793
2794/// Creates a backedge of the specified result type. A backedge represents a
2795/// placeholder to be filled in later by a lowered value. If the backedge is not
2796/// updated with a real value by the end of the pass, it will be replaced with
2797/// an undriven wire. Backedges are allowed to be updated to other backedges.
2798/// If a chain of backedges forms a combinational loop, they will be replaced
2799/// with an undriven wire.
2800Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2801 auto backedge = backedgeBuilder.get(type, loc);
2802 backedges.insert({backedge, backedge});
2803 return backedge;
2804}
2805
2806/// Sets the lowering for a value to a backedge of the specified result type.
2807/// This is useful for lowering types which cannot pass through a wire, or to
2808/// directly materialize values in operations that violate the SSA dominance
2809/// constraint.
2810Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
2811 auto backedge = createBackedge(orig.getLoc(), type);
2812 (void)setLowering(orig, backedge);
2813 return backedge;
2814}
2815
2816/// If the `from` value is in fact a backedge, record that the backedge will
2817/// be replaced by the value. Return true if the destination is a backedge.
2818bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
2819 auto backedgeIt = backedges.find(dest);
2820 if (backedgeIt == backedges.end())
2821 return false;
2822 backedgeIt->second = src;
2823 return true;
2824}
2825
2826/// Switch the insertion point of the current builder to the end of the
2827/// specified block and run the closure. This correctly handles the case
2828/// where the closure is null, but the caller needs to make sure the block
2829/// exists.
2830void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
2831 const std::function<void(void)> &fn, Region &region) {
2832 if (!fn)
2833 return;
2834
2835 auto oldIP = builder.saveInsertionPoint();
2836
2837 builder.setInsertionPointToEnd(&region.front());
2838 fn();
2839 builder.restoreInsertionPoint(oldIP);
2840}
2841
2842/// Return a read value for the specified inout operation, auto-uniquing them.
2843Value FIRRTLLowering::getReadValue(Value v) {
2844 Value result = readInOutCreated.lookup(v);
2845 if (result)
2846 return result;
2847
2848 // Make sure to put the read value at the correct scope so it dominates all
2849 // future uses.
2850 auto oldIP = builder.saveInsertionPoint();
2851 if (auto *vOp = v.getDefiningOp()) {
2852 builder.setInsertionPointAfter(vOp);
2853 } else {
2854 // For reads of ports, just set the insertion point at the top of the
2855 // module.
2856 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
2857 }
2858
2859 // Instead of creating `ReadInOutOp` for `ArrayIndexInOutOp`, create
2860 // `ArrayGetOp` for root arrays.
2861 if (auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
2862 result = getReadValue(arrayIndexInout.getInput());
2863 result = builder.createOrFold<hw::ArrayGetOp>(result,
2864 arrayIndexInout.getIndex());
2865 } else {
2866 // Otherwise, create a read inout operation.
2867 result = builder.createOrFold<sv::ReadInOutOp>(v);
2868 }
2869 builder.restoreInsertionPoint(oldIP);
2870 readInOutCreated.insert({v, result});
2871 return result;
2872}
2873
2874Value FIRRTLLowering::getNonClockValue(Value v) {
2875 auto it = fromClockMapping.try_emplace(v, Value{});
2876 if (it.second) {
2877 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
2878 builder.setInsertionPointAfterValue(v);
2879 it.first->second = builder.create<seq::FromClockOp>(v);
2880 }
2881 return it.first->second;
2882}
2883
2884void FIRRTLLowering::addToAlwaysBlock(
2885 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
2886 sv::EventControl resetEdge, Value reset,
2887 const std::function<void(void)> &body,
2888 const std::function<void(void)> &resetBody) {
2889 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
2890 resetStyle, resetEdge, reset};
2891 sv::AlwaysOp alwaysOp;
2892 sv::IfOp insideIfOp;
2893 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
2894
2895 if (!alwaysOp) {
2896 if (reset) {
2897 assert(resetStyle != sv::ResetType::NoReset);
2898 // Here, we want to create the folloing structure with sv.always and
2899 // sv.if. If `reset` is async, we need to add `reset` to a sensitivity
2900 // list.
2901 //
2902 // sv.always @(clockEdge or reset) {
2903 // sv.if (reset) {
2904 // resetBody
2905 // } else {
2906 // body
2907 // }
2908 // }
2909
2910 auto createIfOp = [&]() {
2911 // It is weird but intended. Here we want to create an empty sv.if
2912 // with an else block.
2913 insideIfOp = builder.create<sv::IfOp>(
2914 reset, []() {}, []() {});
2915 };
2916 if (resetStyle == sv::ResetType::AsyncReset) {
2917 sv::EventControl events[] = {clockEdge, resetEdge};
2918 Value clocks[] = {clock, reset};
2919
2920 alwaysOp = builder.create<sv::AlwaysOp>(events, clocks, [&]() {
2921 if (resetEdge == sv::EventControl::AtNegEdge)
2922 llvm_unreachable("negative edge for reset is not expected");
2923 createIfOp();
2924 });
2925 } else {
2926 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
2927 }
2928 } else {
2929 assert(!resetBody);
2930 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock);
2931 insideIfOp = nullptr;
2932 }
2933 alwaysBlocks[key] = {alwaysOp, insideIfOp};
2934 }
2935
2936 if (reset) {
2937 assert(insideIfOp && "reset body must be initialized before");
2938 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
2939 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
2940 } else {
2941 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
2942 }
2943
2944 // Move the earlier always block(s) down to where the last would have been
2945 // inserted. This ensures that any values used by the always blocks are
2946 // defined ahead of the uses, which leads to better generated Verilog.
2947 alwaysOp->moveBefore(builder.getInsertionBlock(),
2948 builder.getInsertionPoint());
2949}
2950
2951LogicalResult FIRRTLLowering::emitGuards(Location loc,
2952 ArrayRef<Attribute> guards,
2953 std::function<void(void)> emit) {
2954 if (guards.empty()) {
2955 emit();
2956 return success();
2957 }
2958 auto guard = dyn_cast<StringAttr>(guards[0]);
2959 if (!guard)
2960 return mlir::emitError(loc,
2961 "elements in `guards` array must be `StringAttr`");
2962
2963 // Record the guard macro to emit a declaration for it.
2964 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
2965 LogicalResult result = LogicalResult::failure();
2966 addToIfDefBlock(guard.getValue(), [&]() {
2967 result = emitGuards(loc, guards.drop_front(), emit);
2968 });
2969 return result;
2970}
2971
2972void FIRRTLLowering::addToIfDefBlock(StringRef cond,
2973 std::function<void(void)> thenCtor,
2974 std::function<void(void)> elseCtor) {
2975 auto condAttr = builder.getStringAttr(cond);
2976 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
2977 if (op) {
2978 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
2979 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
2980
2981 // Move the earlier #ifdef block(s) down to where the last would have been
2982 // inserted. This ensures that any values used by the #ifdef blocks are
2983 // defined ahead of the uses, which leads to better generated Verilog.
2984 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2985 } else {
2986 ifdefBlocks[{builder.getBlock(), condAttr}] =
2987 builder.create<sv::IfDefOp>(condAttr, thenCtor, elseCtor);
2988 }
2989}
2990
2991void FIRRTLLowering::addToInitialBlock(std::function<void(void)> body) {
2992 auto op = initialBlocks.lookup(builder.getBlock());
2993 if (op) {
2994 runWithInsertionPointAtEndOfBlock(body, op.getBody());
2995
2996 // Move the earlier initial block(s) down to where the last would have
2997 // been inserted. This ensures that any values used by the initial blocks
2998 // are defined ahead of the uses, which leads to better generated Verilog.
2999 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3000 } else {
3001 initialBlocks[builder.getBlock()] = builder.create<sv::InitialOp>(body);
3002 }
3003}
3004
3005void FIRRTLLowering::addIfProceduralBlock(Value cond,
3006 std::function<void(void)> thenCtor,
3007 std::function<void(void)> elseCtor) {
3008 // Check to see if we already have an if on this condition immediately
3009 // before the insertion point. If so, extend it.
3010 auto insertIt = builder.getInsertionPoint();
3011 if (insertIt != builder.getBlock()->begin())
3012 if (auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3013 if (ifOp.getCond() == cond) {
3014 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3015 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3016 return;
3017 }
3018 }
3019
3020 builder.create<sv::IfOp>(cond, thenCtor, elseCtor);
3021}
3022
3023//===----------------------------------------------------------------------===//
3024// Special Operations
3025//===----------------------------------------------------------------------===//
3026
3027/// Handle the case where an operation wasn't lowered. When this happens, the
3028/// operands should just be unlowered non-FIRRTL values. If the operand was
3029/// not lowered then leave it alone, otherwise we have a problem with
3030/// lowering.
3031///
3032FIRRTLLowering::UnloweredOpResult
3033FIRRTLLowering::handleUnloweredOp(Operation *op) {
3034 // FIRRTL operations must explicitly handle their regions.
3035 if (!op->getRegions().empty() && isa<FIRRTLDialect>(op->getDialect())) {
3036 op->emitOpError("must explicitly handle its regions");
3037 return LoweringFailure;
3038 }
3039
3040 // Simply pass through non-FIRRTL operations and consider them already
3041 // lowered. This allows us to handled partially lowered inputs, and also allow
3042 // other FIRRTL operations to spawn additional already-lowered operations,
3043 // like `hw.output`.
3044 if (!isa<FIRRTLDialect>(op->getDialect())) {
3045 // Push nested operations onto the worklist such that they are lowered.
3046 for (auto &region : op->getRegions())
3047 addToWorklist(region);
3048 for (auto &operand : op->getOpOperands())
3049 if (auto lowered = getPossiblyInoutLoweredValue(operand.get()))
3050 operand.set(lowered);
3051 for (auto result : op->getResults())
3052 (void)setLowering(result, result);
3053 return AlreadyLowered;
3054 }
3055
3056 // Ok, at least one operand got lowered, so this operation is using a FIRRTL
3057 // value, but wasn't itself lowered. This is because the lowering is
3058 // incomplete. This is either a bug or incomplete implementation.
3059 //
3060 // There is one aspect of incompleteness we intentionally expect: we allow
3061 // primitive operations that produce a zero bit result to be ignored by the
3062 // lowering logic. They don't have side effects, and handling this corner
3063 // case just complicates each of the lowering hooks. Instead, we just handle
3064 // them all right here.
3065 if (op->getNumResults() == 1) {
3066 auto resultType = op->getResult(0).getType();
3067 if (type_isa<FIRRTLBaseType>(resultType) &&
3068 isZeroBitFIRRTLType(resultType) &&
3069 (isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3070 // Zero bit values lower to the null Value.
3071 (void)setLowering(op->getResult(0), Value());
3072 return NowLowered;
3073 }
3074 }
3075 op->emitOpError("LowerToHW couldn't handle this operation");
3076 return LoweringFailure;
3077}
3078
3079LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3080 // Zero width values must be lowered to nothing.
3081 if (isZeroBitFIRRTLType(op.getType()))
3082 return setLowering(op, Value());
3083
3084 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3085}
3086
3087LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3088 Value cst;
3089 if (isa<ClockType>(op.getType())) {
3090 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3091 : seq::ClockConst::Low);
3092 } else {
3093 cst = getOrCreateIntConstant(APInt(/*bitWidth*/ 1, op.getValue()));
3094 }
3095 return setLowering(op, cst);
3096}
3097
3098FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3099 auto iIdx = getOrCreateIntConstant(
3101 firrtl::type_cast<FVectorType>(op.getInput().getType())
3102 .getNumElements()),
3103 op.getIndex());
3104
3105 // If the input has an inout type, we need to lower to ArrayIndexInOutOp;
3106 // otherwise hw::ArrayGetOp.
3107 Value result;
3108 if (isa<sv::InOutType>(input.getType()))
3109 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3110 else
3111 result = builder.createOrFold<hw::ArrayGetOp>(input, iIdx);
3112 if (auto *definingOp = result.getDefiningOp())
3113 tryCopyName(definingOp, op);
3114 return result;
3115}
3116
3117FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3118 Value valueIdx = getLoweredAndExtOrTruncValue(
3119 op.getIndex(),
3120 UIntType::get(op->getContext(),
3122 firrtl::type_cast<FVectorType>(op.getInput().getType())
3123 .getNumElements())));
3124 if (!valueIdx) {
3125 op->emitError() << "input lowering failed";
3126 return failure();
3127 }
3128
3129 // If the input has an inout type, we need to lower to ArrayIndexInOutOp;
3130 // otherwise, lower the op to array indexing.
3131 Value result;
3132 if (isa<sv::InOutType>(input.getType()))
3133 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3134 else
3135 result = createArrayIndexing(input, valueIdx);
3136 if (auto *definingOp = result.getDefiningOp())
3137 tryCopyName(definingOp, op);
3138 return result;
3139}
3140
3141FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3142 auto resultType = lowerType(op->getResult(0).getType());
3143 if (!resultType || !input) {
3144 op->emitError() << "subfield type lowering failed";
3145 return failure();
3146 }
3147
3148 // If the input has an inout type, we need to lower to StructFieldInOutOp;
3149 // otherwise, StructExtractOp.
3150 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3151 .getElementName(op.getFieldIndex());
3152 Value result;
3153 if (isa<sv::InOutType>(input.getType()))
3154 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3155 else
3156 result = builder.createOrFold<hw::StructExtractOp>(input, field);
3157 if (auto *definingOp = result.getDefiningOp())
3158 tryCopyName(definingOp, op);
3159 return result;
3160}
3161
3162LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3163 if (isZeroBitFIRRTLType(op.getType()))
3164 return setLowering(op, Value());
3165
3166 auto input = getPossiblyInoutLoweredValue(op.getInput());
3167 if (!input)
3168 return op.emitError() << "input lowering failed";
3169
3170 auto result = lowerSubindex(op, input);
3171 if (failed(result))
3172 return failure();
3173 return setLowering(op, *result);
3174}
3175
3176LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3177 if (isZeroBitFIRRTLType(op.getType()))
3178 return setLowering(op, Value());
3179
3180 auto input = getPossiblyInoutLoweredValue(op.getInput());
3181 if (!input)
3182 return op.emitError() << "input lowering failed";
3183
3184 auto result = lowerSubaccess(op, input);
3185 if (failed(result))
3186 return failure();
3187 return setLowering(op, *result);
3188}
3189
3190LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3191 // firrtl.mem lowering lowers some SubfieldOps. Zero-width can leave
3192 // invalid subfield accesses
3193 if (getLoweredValue(op) || !op.getInput())
3194 return success();
3195
3196 if (isZeroBitFIRRTLType(op.getType()))
3197 return setLowering(op, Value());
3198
3199 auto input = getPossiblyInoutLoweredValue(op.getInput());
3200 if (!input)
3201 return op.emitError() << "input lowering failed";
3202
3203 auto result = lowerSubfield(op, input);
3204 if (failed(result))
3205 return failure();
3206 return setLowering(op, *result);
3207}
3208
3209LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3210 auto resultType = lowerType(op.getResult().getType());
3211 SmallVector<Value> operands;
3212 // NOTE: The operand order must be inverted.
3213 for (auto oper : llvm::reverse(op.getOperands())) {
3214 auto val = getLoweredValue(oper);
3215 if (!val)
3216 return failure();
3217 operands.push_back(val);
3218 }
3219 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3220}
3221
3222LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3223 auto resultType = lowerType(op.getResult().getType());
3224 SmallVector<Value> operands;
3225 for (auto oper : op.getOperands()) {
3226 auto val = getLoweredValue(oper);
3227 if (!val)
3228 return failure();
3229 operands.push_back(val);
3230 }
3231 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3232}
3233
3234LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3235 // Zero width values must be lowered to nothing.
3236 if (isZeroBitFIRRTLType(op.getType()))
3237 return setLowering(op, Value());
3238
3239 auto input = getLoweredValue(op.getInput());
3240 auto tagName = op.getFieldNameAttr();
3241 auto type = lowerType(op.getType());
3242
3243 if (auto structType = dyn_cast<hw::StructType>(type)) {
3244 auto enumType = structType.getFieldType("tag");
3245 auto enumAttr = hw::EnumFieldAttr::get(op.getLoc(), tagName, enumType);
3246 auto enumOp = builder.create<hw::EnumConstantOp>(enumAttr);
3247 auto unionType = structType.getFieldType("body");
3248 auto unionOp = builder.create<hw::UnionCreateOp>(unionType, tagName, input);
3249 SmallVector<Value> operands = {enumOp.getResult(), unionOp.getResult()};
3250 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3251 }
3252
3253 return setLoweringTo<hw::EnumConstantOp>(
3254 op, hw::EnumFieldAttr::get(op.getLoc(), tagName, type));
3255}
3256
3257LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3258 auto resultType = lowerType(op.getResult().getType());
3259 auto attr =
3260 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3261
3262 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3263 cast<ArrayAttr>(attr));
3264}
3265
3266LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3267 auto tagName = op.getFieldNameAttr();
3268 auto lhs = getLoweredValue(op.getInput());
3269 if (isa<hw::StructType>(lhs.getType()))
3270 lhs = builder.create<hw::StructExtractOp>(lhs, "tag");
3271 auto enumField = hw::EnumFieldAttr::get(op.getLoc(), tagName, lhs.getType());
3272 auto rhs = builder.create<hw::EnumConstantOp>(enumField);
3273 return setLoweringTo<hw::EnumCmpOp>(op, lhs, rhs);
3274}
3275
3276LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3277 // Zero width values must be lowered to nothing.
3278 if (isZeroBitFIRRTLType(op.getType()))
3279 return setLowering(op, Value());
3280
3281 auto tagName = op.getFieldNameAttr();
3282 auto input = getLoweredValue(op.getInput());
3283 auto field = builder.create<hw::StructExtractOp>(input, "body");
3284 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3285}
3286
3287//===----------------------------------------------------------------------===//
3288// Declarations
3289//===----------------------------------------------------------------------===//
3290
3291LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3292 auto origResultType = op.getResult().getType();
3293
3294 // Foreign types lower to a backedge that needs to be resolved by a later
3295 // connect op.
3296 if (!type_isa<FIRRTLType>(origResultType)) {
3297 createBackedge(op.getResult(), origResultType);
3298 return success();
3299 }
3300
3301 auto resultType = lowerType(origResultType);
3302 if (!resultType)
3303 return failure();
3304
3305 if (resultType.isInteger(0)) {
3306 if (op.getInnerSym())
3307 return op.emitError("zero width wire is referenced by name [")
3308 << *op.getInnerSym() << "] (e.g. in an XMR) but must be removed";
3309 return setLowering(op.getResult(), Value());
3310 }
3311
3312 // Name attr is required on sv.wire but optional on firrtl.wire.
3313 auto innerSym = lowerInnerSymbol(op);
3314 auto name = op.getNameAttr();
3315 // This is not a temporary wire created by the compiler, so attach a symbol
3316 // name.
3317 auto wire = builder.create<hw::WireOp>(
3318 op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3319
3320 if (auto svAttrs = sv::getSVAttributes(op))
3321 sv::setSVAttributes(wire, svAttrs);
3322
3323 return setLowering(op.getResult(), wire);
3324}
3325
3326LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3327 auto resultTy = lowerType(op.getType());
3328 if (!resultTy)
3329 return failure();
3330 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3331
3332 SmallVector<Value, 4> operands;
3333 operands.reserve(op.getSubstitutions().size());
3334 for (auto operand : op.getSubstitutions()) {
3335 auto lowered = getLoweredValue(operand);
3336 if (!lowered)
3337 return failure();
3338 operands.push_back(lowered);
3339 }
3340
3341 ArrayAttr symbols = op.getSymbolsAttr();
3342 if (!symbols)
3343 symbols = ArrayAttr::get(op.getContext(), {});
3344
3345 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3346 operands, symbols);
3347}
3348
3349LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3350 auto operand = getLoweredValue(op.getInput());
3351 if (!operand)
3352 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3353 if (op.getInnerSym())
3354 return op.emitError("zero width node is referenced by name [")
3355 << *op.getInnerSym()
3356 << "] (e.g. in an XMR) but must be "
3357 "removed";
3358 return setLowering(op.getResult(), Value());
3359 });
3360
3361 // Node operations are logical noops, but may carry annotations or be
3362 // referred to through an inner name. If a don't touch is present, ensure
3363 // that we have a symbol name so we can keep the node as a wire.
3364 auto name = op.getNameAttr();
3365 auto innerSym = lowerInnerSymbol(op);
3366
3367 if (innerSym)
3368 operand = builder.create<hw::WireOp>(operand, name, innerSym);
3369
3370 // Move SV attributes.
3371 if (auto svAttrs = sv::getSVAttributes(op)) {
3372 if (!innerSym)
3373 operand = builder.create<hw::WireOp>(operand, name);
3374 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3375 }
3376
3377 return setLowering(op.getResult(), operand);
3378}
3379
3380LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3381 auto resultType = lowerType(op.getResult().getType());
3382 if (!resultType)
3383 return failure();
3384 if (resultType.isInteger(0))
3385 return setLowering(op.getResult(), Value());
3386
3387 Value clockVal = getLoweredValue(op.getClockVal());
3388 if (!clockVal)
3389 return failure();
3390
3391 // Create a reg op, wiring itself to its input.
3392 auto innerSym = lowerInnerSymbol(op);
3393 Backedge inputEdge = backedgeBuilder.get(resultType);
3394 auto reg = builder.create<seq::FirRegOp>(inputEdge, clockVal,
3395 op.getNameAttr(), innerSym);
3396
3397 // Pass along the start and end random initialization bits for this register.
3398 if (auto randomRegister = op->getAttr("firrtl.random_init_register"))
3399 reg->setAttr("firrtl.random_init_register", randomRegister);
3400 if (auto randomStart = op->getAttr("firrtl.random_init_start"))
3401 reg->setAttr("firrtl.random_init_start", randomStart);
3402 if (auto randomEnd = op->getAttr("firrtl.random_init_end"))
3403 reg->setAttr("firrtl.random_init_end", randomEnd);
3404
3405 // Move SV attributes.
3406 if (auto svAttrs = sv::getSVAttributes(op))
3407 sv::setSVAttributes(reg, svAttrs);
3408
3409 inputEdge.setValue(reg);
3410 (void)setLowering(op.getResult(), reg);
3411 return success();
3412}
3413
3414LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3415 auto resultType = lowerType(op.getResult().getType());
3416 if (!resultType)
3417 return failure();
3418 if (resultType.isInteger(0))
3419 return setLowering(op.getResult(), Value());
3420
3421 Value clockVal = getLoweredValue(op.getClockVal());
3422 Value resetSignal = getLoweredValue(op.getResetSignal());
3423 // Reset values may be narrower than the register. Extend appropriately.
3424 Value resetValue = getLoweredAndExtOrTruncValue(
3425 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3426
3427 if (!clockVal || !resetSignal || !resetValue)
3428 return failure();
3429
3430 // Create a reg op, wiring itself to its input.
3431 auto innerSym = lowerInnerSymbol(op);
3432 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3433 Backedge inputEdge = backedgeBuilder.get(resultType);
3434 auto reg =
3435 builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
3436 resetSignal, resetValue, innerSym, isAsync);
3437
3438 // Pass along the start and end random initialization bits for this register.
3439 if (auto randomRegister = op->getAttr("firrtl.random_init_register"))
3440 reg->setAttr("firrtl.random_init_register", randomRegister);
3441 if (auto randomStart = op->getAttr("firrtl.random_init_start"))
3442 reg->setAttr("firrtl.random_init_start", randomStart);
3443 if (auto randomEnd = op->getAttr("firrtl.random_init_end"))
3444 reg->setAttr("firrtl.random_init_end", randomEnd);
3445
3446 // Move SV attributes.
3447 if (auto svAttrs = sv::getSVAttributes(op))
3448 sv::setSVAttributes(reg, svAttrs);
3449
3450 inputEdge.setValue(reg);
3451 (void)setLowering(op.getResult(), reg);
3452
3453 return success();
3454}
3455
3456LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3457 // TODO: Remove this restriction and preserve aggregates in
3458 // memories.
3459 if (type_isa<BundleType>(op.getDataType()))
3460 return op.emitOpError(
3461 "should have already been lowered from a ground type to an aggregate "
3462 "type using the LowerTypes pass. Use "
3463 "'firtool --lower-types' or 'circt-opt "
3464 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3465 "to run this.");
3466
3467 FirMemory memSummary = op.getSummary();
3468
3469 // Create the memory declaration.
3470 auto memType = seq::FirMemType::get(
3471 op.getContext(), memSummary.depth, memSummary.dataWidth,
3472 memSummary.isMasked ? std::optional<uint32_t>(memSummary.maskBits)
3473 : std::optional<uint32_t>());
3474
3475 seq::FirMemInitAttr memInit;
3476 if (auto init = op.getInitAttr())
3477 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3478 init.getIsBinary(), init.getIsInline());
3479
3480 auto memDecl = builder.create<seq::FirMemOp>(
3481 memType, memSummary.readLatency, memSummary.writeLatency,
3482 memSummary.readUnderWrite, memSummary.writeUnderWrite, op.getNameAttr(),
3483 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3484
3485 // If the module is outside the DUT, set the appropriate output directory for
3486 // the memory.
3487 if (!circuitState.isInDUT(theModule))
3488 if (auto testBenchDir = circuitState.getTestBenchDirectory())
3489 memDecl.setOutputFileAttr(testBenchDir);
3490
3491 // Memories return multiple structs, one for each port, which means we
3492 // have two layers of type to split apart.
3493 for (size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3494
3495 auto addOutput = [&](StringRef field, size_t width, Value value) {
3496 for (auto &a : getAllFieldAccesses(op.getResult(i), field)) {
3497 if (width > 0)
3498 (void)setLowering(a, value);
3499 else
3500 a->eraseOperand(0);
3501 }
3502 };
3503
3504 auto addInput = [&](StringRef field, Value backedge) {
3505 for (auto a : getAllFieldAccesses(op.getResult(i), field)) {
3506 if (cast<FIRRTLBaseType>(a.getType())
3507 .getPassiveType()
3508 .getBitWidthOrSentinel() > 0)
3509 (void)setLowering(a, backedge);
3510 else
3511 a->eraseOperand(0);
3512 }
3513 };
3514
3515 auto addInputPort = [&](StringRef field, size_t width) -> Value {
3516 // If the memory is 0-width, do not materialize any connections to it.
3517 // However, `seq.firmem` now requires a 1-bit input, so materialize
3518 // a dummy x value to provide it with.
3519 Value backedge, portValue;
3520 if (width == 0) {
3521 portValue = getOrCreateXConstant(1);
3522 } else {
3523 auto portType = IntegerType::get(op.getContext(), width);
3524 backedge = portValue = createBackedge(builder.getLoc(), portType);
3525 }
3526 addInput(field, backedge);
3527 return portValue;
3528 };
3529
3530 auto addClock = [&](StringRef field) -> Value {
3531 Type clockTy = seq::ClockType::get(op.getContext());
3532 Value portValue = createBackedge(builder.getLoc(), clockTy);
3533 addInput(field, portValue);
3534 return portValue;
3535 };
3536
3537 auto memportKind = op.getPortKind(i);
3538 if (memportKind == MemOp::PortKind::Read) {
3539 auto addr = addInputPort("addr", op.getAddrBits());
3540 auto en = addInputPort("en", 1);
3541 auto clk = addClock("clk");
3542 auto data = builder.create<seq::FirMemReadOp>(memDecl, addr, clk, en);
3543 addOutput("data", memSummary.dataWidth, data);
3544 } else if (memportKind == MemOp::PortKind::ReadWrite) {
3545 auto addr = addInputPort("addr", op.getAddrBits());
3546 auto en = addInputPort("en", 1);
3547 auto clk = addClock("clk");
3548 // If maskBits =1, then And the mask field with enable, and update the
3549 // enable. Else keep mask port.
3550 auto mode = addInputPort("wmode", 1);
3551 if (!memSummary.isMasked)
3552 mode = builder.createOrFold<comb::AndOp>(mode, addInputPort("wmask", 1),
3553 true);
3554 auto wdata = addInputPort("wdata", memSummary.dataWidth);
3555 // Ignore mask port, if maskBits =1
3556 Value mask;
3557 if (memSummary.isMasked)
3558 mask = addInputPort("wmask", memSummary.maskBits);
3559 auto rdata = builder.create<seq::FirMemReadWriteOp>(
3560 memDecl, addr, clk, en, wdata, mode, mask);
3561 addOutput("rdata", memSummary.dataWidth, rdata);
3562 } else {
3563 auto addr = addInputPort("addr", op.getAddrBits());
3564 // If maskBits =1, then And the mask field with enable, and update the
3565 // enable. Else keep mask port.
3566 auto en = addInputPort("en", 1);
3567 if (!memSummary.isMasked)
3568 en = builder.createOrFold<comb::AndOp>(en, addInputPort("mask", 1),
3569 true);
3570 auto clk = addClock("clk");
3571 auto data = addInputPort("data", memSummary.dataWidth);
3572 // Ignore mask port, if maskBits =1
3573 Value mask;
3574 if (memSummary.isMasked)
3575 mask = addInputPort("mask", memSummary.maskBits);
3576 builder.create<seq::FirMemWriteOp>(memDecl, addr, clk, en, data, mask);
3577 }
3578 }
3579
3580 return success();
3581}
3582
3583LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3584 Operation *oldModule =
3585 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3586
3587 auto *newModule = circuitState.getNewModule(oldModule);
3588 if (!newModule) {
3589 oldInstance->emitOpError("could not find module [")
3590 << oldInstance.getModuleName() << "] referenced by instance";
3591 return failure();
3592 }
3593
3594 // If this is a referenced to a parameterized extmodule, then bring the
3595 // parameters over to this instance.
3596 ArrayAttr parameters;
3597 if (auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3598 parameters = getHWParameters(oldExtModule, /*ignoreValues=*/false);
3599
3600 // Decode information about the input and output ports on the referenced
3601 // module.
3602 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3603
3604 // Build an index from the name attribute to an index into portInfo, so we
3605 // can do efficient lookups.
3607 for (unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3608 portIndicesByName[portInfo[portIdx].name] = portIdx;
3609
3610 // Ok, get ready to create the new instance operation. We need to prepare
3611 // input operands.
3612 SmallVector<Value, 8> operands;
3613 for (size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3614 auto &port = portInfo[portIndex];
3615 auto portType = lowerType(port.type);
3616 if (!portType) {
3617 oldInstance->emitOpError("could not lower type of port ") << port.name;
3618 return failure();
3619 }
3620
3621 // Drop zero bit input/inout ports.
3622 if (portType.isInteger(0))
3623 continue;
3624
3625 // We wire outputs up after creating the instance.
3626 if (port.isOutput())
3627 continue;
3628
3629 auto portResult = oldInstance.getResult(portIndex);
3630 assert(portResult && "invalid IR, couldn't find port");
3631
3632 // Replace the input port with a backedge. If it turns out that this port
3633 // is never driven, an uninitialized wire will be materialized at the end.
3634 if (port.isInput()) {
3635 operands.push_back(createBackedge(portResult, portType));
3636 continue;
3637 }
3638
3639 // If the result has an analog type and is used only by attach op, try
3640 // eliminating a temporary wire by directly using an attached value.
3641 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3642 if (auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3643 if (auto source = getSingleNonInstanceOperand(attach)) {
3644 auto loweredResult = getPossiblyInoutLoweredValue(source);
3645 operands.push_back(loweredResult);
3646 (void)setLowering(portResult, loweredResult);
3647 continue;
3648 }
3649 }
3650 }
3651
3652 // Create a wire for each inout operand, so there is something to connect
3653 // to. The instance becomes the sole driver of this wire.
3654 auto wire = builder.create<sv::WireOp>(
3655 portType, "." + port.getName().str() + ".wire");
3656
3657 // Know that the argument FIRRTL value is equal to this wire, allowing
3658 // connects to it to be lowered.
3659 (void)setLowering(portResult, wire);
3660
3661 operands.push_back(wire);
3662 }
3663
3664 // If this instance is destined to be lowered to a bind, generate a symbol
3665 // for it and generate a bind op. Enter the bind into global
3666 // CircuitLoweringState so that this can be moved outside of module once
3667 // we're guaranteed to not be a parallel context.
3668 auto innerSym = oldInstance.getInnerSymAttr();
3669 if (oldInstance.getLowerToBind()) {
3670 if (!innerSym)
3671 std::tie(innerSym, std::ignore) = getOrAddInnerSym(
3672 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3673 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
3674
3675 auto bindOp = builder.create<sv::BindOp>(theModule.getNameAttr(),
3676 innerSym.getSymName());
3677 // If the lowered op already had output file information, then use that.
3678 // Otherwise, generate some default bind information.
3679 if (auto outputFile = oldInstance->getAttr("output_file"))
3680 bindOp->setAttr("output_file", outputFile);
3681 // Add the bind to the circuit state. This will be moved outside of the
3682 // encapsulating module after all modules have been processed in parallel.
3683 circuitState.addBind(bindOp);
3684 }
3685
3686 // Create the new hw.instance operation.
3687 auto newInstance = builder.create<hw::InstanceOp>(
3688 newModule, oldInstance.getNameAttr(), operands, parameters, innerSym);
3689
3690 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
3691 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3692
3693 if (newInstance.getInnerSymAttr())
3694 if (auto forceName = circuitState.instanceForceNames.lookup(
3695 {cast<hw::HWModuleOp>(newInstance->getParentOp()).getNameAttr(),
3696 newInstance.getInnerNameAttr()}))
3697 newInstance->setAttr("hw.verilogName", forceName);
3698
3699 // Now that we have the new hw.instance, we need to remap all of the users
3700 // of the outputs/results to the values returned by the instance.
3701 unsigned resultNo = 0;
3702 for (size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3703 auto &port = portInfo[portIndex];
3704 if (!port.isOutput() || isZeroBitFIRRTLType(port.type))
3705 continue;
3706
3707 Value resultVal = newInstance.getResult(resultNo);
3708
3709 auto oldPortResult = oldInstance.getResult(portIndex);
3710 (void)setLowering(oldPortResult, resultVal);
3711 ++resultNo;
3712 }
3713 return success();
3714}
3715
3716LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
3717 SmallVector<Value> inputs;
3718 SmallVector<Type> types;
3719 for (auto input : oldOp.getInputs()) {
3720 auto lowered = getLoweredValue(input);
3721 if (!lowered)
3722 return failure();
3723 inputs.push_back(lowered);
3724 types.push_back(lowered.getType());
3725 }
3726
3727 auto newOp = builder.create<verif::ContractOp>(types, inputs);
3728 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
3729 auto &body = newOp.getBody().emplaceBlock();
3730
3731 for (auto [newResult, oldResult, oldArg] :
3732 llvm::zip(newOp.getResults(), oldOp.getResults(),
3733 oldOp.getBody().getArguments())) {
3734 if (failed(setLowering(oldResult, newResult)))
3735 return failure();
3736 if (failed(setLowering(oldArg, newResult)))
3737 return failure();
3738 }
3739
3740 body.getOperations().splice(body.end(),
3741 oldOp.getBody().front().getOperations());
3742 addToWorklist(body);
3743
3744 return success();
3745}
3746
3747//===----------------------------------------------------------------------===//
3748// Unary Operations
3749//===----------------------------------------------------------------------===//
3750
3751// Lower a cast that is a noop at the HW level.
3752LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3753 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3754 if (!operand)
3755 return failure();
3756
3757 // Noop cast.
3758 return setLowering(op->getResult(0), operand);
3759}
3760
3761LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3762 if (isa<ClockType>(op.getInput().getType()))
3763 return setLowering(op->getResult(0),
3764 getLoweredNonClockValue(op.getInput()));
3765 return lowerNoopCast(op);
3766}
3767
3768LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3769 if (isa<ClockType>(op.getInput().getType()))
3770 return setLowering(op->getResult(0),
3771 getLoweredNonClockValue(op.getInput()));
3772 return lowerNoopCast(op);
3773}
3774
3775LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3776 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3777}
3778
3779LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
3780 mlir::UnrealizedConversionCastOp op) {
3781 // General lowering for non-unary casts.
3782 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3783 return failure();
3784
3785 auto operand = op.getOperand(0);
3786 auto result = op.getResult(0);
3787
3788 // FIRRTL -> FIRRTL
3789 if (type_isa<FIRRTLType>(operand.getType()) &&
3790 type_isa<FIRRTLType>(result.getType()))
3791 return lowerNoopCast(op);
3792
3793 // other -> FIRRTL
3794 // other -> other
3795 if (!type_isa<FIRRTLType>(operand.getType())) {
3796 if (type_isa<FIRRTLType>(result.getType()))
3797 return setLowering(result, getPossiblyInoutLoweredValue(operand));
3798 return failure(); // general foreign op lowering for other -> other
3799 }
3800
3801 // FIRRTL -> other
3802 // Otherwise must be a conversion from FIRRTL type to standard type.
3803 auto loweredResult = getLoweredValue(operand);
3804 if (!loweredResult) {
3805 // If this is a conversion from a zero bit HW type to firrtl value, then
3806 // we want to successfully lower this to a null Value.
3807 if (operand.getType().isSignlessInteger(0)) {
3808 return setLowering(result, Value());
3809 }
3810 return failure();
3811 }
3812
3813 // We lower builtin.unrealized_conversion_cast converting from a firrtl type
3814 // to a standard type into the lowered operand.
3815 result.replaceAllUsesWith(loweredResult);
3816 return success();
3817}
3818
3819LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3820 // Conversions from hw struct types to FIRRTL types are lowered as the
3821 // input operand.
3822 if (auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
3823 return setLowering(op, op.getOperand());
3824
3825 // Otherwise must be a conversion from FIRRTL bundle type to hw struct
3826 // type.
3827 auto result = getLoweredValue(op.getOperand());
3828 if (!result)
3829 return failure();
3830
3831 // We lower firrtl.stdStructCast converting from a firrtl bundle to an hw
3832 // struct type into the lowered operand.
3833 op.replaceAllUsesWith(result);
3834 return success();
3835}
3836
3837LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3838 auto operand = getLoweredValue(op.getOperand());
3839 if (!operand)
3840 return failure();
3841 auto resultType = lowerType(op.getType());
3842 if (!resultType)
3843 return failure();
3844
3845 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3846}
3847
3848LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3849 auto operand = getLoweredValue(op.getOperand());
3850 if (!operand) {
3851 return handleZeroBit(op.getOperand(), [&]() {
3852 // Unsigned zero bit to Signed is 1b0.
3853 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3854 return setLowering(op, getOrCreateIntConstant(1, 0));
3855 // Signed->Signed is a zero bit value.
3856 return setLowering(op, Value());
3857 });
3858 }
3859
3860 // Signed to signed is a noop.
3861 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3862 return setLowering(op, operand);
3863
3864 // Otherwise prepend a zero bit.
3865 auto zero = getOrCreateIntConstant(1, 0);
3866 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3867}
3868
3869LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3870 auto operand = getLoweredValue(op.getInput());
3871 if (!operand)
3872 return failure();
3873 // ~x ---> x ^ 0xFF
3874 auto allOnes = getOrCreateIntConstant(
3875 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3876 return setLoweringTo<comb::XorOp>(op, operand, allOnes, true);
3877}
3878
3879LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3880 // FIRRTL negate always adds a bit.
3881 // -x ---> 0-sext(x) or 0-zext(x)
3882 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3883 if (!operand)
3884 return failure();
3885
3886 auto resultType = lowerType(op.getType());
3887
3888 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3889 return setLoweringTo<comb::SubOp>(op, zero, operand, true);
3890}
3891
3892// Pad is a noop or extension operation.
3893LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3894 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3895 if (!operand)
3896 return failure();
3897 return setLowering(op, operand);
3898}
3899
3900LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3901 auto operand = getLoweredValue(op.getInput());
3902 if (!operand) {
3903 return handleZeroBit(op.getInput(), [&]() {
3904 return setLowering(op, getOrCreateIntConstant(1, 0));
3905 });
3906 return failure();
3907 }
3908
3909 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
3910 true);
3911}
3912
3913LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3914 auto operand = getLoweredValue(op.getInput());
3915 if (!operand) {
3916 return handleZeroBit(op.getInput(), [&]() {
3917 return setLowering(op, getOrCreateIntConstant(1, 1));
3918 });
3919 }
3920
3921 // Lower AndR to == -1
3922 return setLoweringTo<comb::ICmpOp>(
3923 op, ICmpPredicate::eq, operand,
3924 getOrCreateIntConstant(
3925 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3926 true);
3927}
3928
3929LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3930 auto operand = getLoweredValue(op.getInput());
3931 if (!operand) {
3932 return handleZeroBit(op.getInput(), [&]() {
3933 return setLowering(op, getOrCreateIntConstant(1, 0));
3934 });
3935 return failure();
3936 }
3937
3938 // Lower OrR to != 0
3939 return setLoweringTo<comb::ICmpOp>(
3940 op, ICmpPredicate::ne, operand,
3941 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3942 true);
3943}
3944
3945//===----------------------------------------------------------------------===//
3946// Binary Operations
3947//===----------------------------------------------------------------------===//
3948
3949template <typename ResultOpType>
3950LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3951 auto resultType = op->getResult(0).getType();
3952 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3953 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3954 if (!lhs || !rhs)
3955 return failure();
3956
3957 return setLoweringTo<ResultOpType>(op, lhs, rhs, true);
3958}
3959
3960/// Element-wise logical operations can be lowered into bitcast and normal comb
3961/// operations. Eventually we might want to introduce elementwise operations
3962/// into HW/SV level as well.
3963template <typename ResultOpType>
3964LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3965 auto resultType = op->getResult(0).getType();
3966 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3967 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3968
3969 if (!lhs || !rhs)
3970 return failure();
3971 auto bitwidth = firrtl::getBitWidth(type_cast<FIRRTLBaseType>(resultType));
3972
3973 if (!bitwidth)
3974 return failure();
3975
3976 // TODO: Introduce elementwise operations to HW dialect instead of abusing
3977 // bitcast operations.
3978 auto intType = builder.getIntegerType(*bitwidth);
3979 auto retType = lhs.getType();
3980 lhs = builder.createOrFold<hw::BitcastOp>(intType, lhs);
3981 rhs = builder.createOrFold<hw::BitcastOp>(intType, rhs);
3982 auto result = builder.createOrFold<ResultOpType>(lhs, rhs, /*twoState=*/true);
3983 return setLoweringTo<hw::BitcastOp>(op, retType, result);
3984}
3985
3986/// lowerBinOp extends each operand to the destination type, then performs the
3987/// specified binary operator.
3988template <typename ResultUnsignedOpType, typename ResultSignedOpType>
3989LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
3990 // Extend the two operands to match the destination type.
3991 auto resultType = op->getResult(0).getType();
3992 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3993 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3994 if (!lhs || !rhs)
3995 return failure();
3996
3997 // Emit the result operation.
3998 if (type_cast<IntType>(resultType).isSigned())
3999 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs, true);
4000 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs, true);
4001}
4002
4003/// lowerCmpOp extends each operand to the longest type, then performs the
4004/// specified binary operator.
4005LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4006 ICmpPredicate unsignedOp) {
4007 // Extend the two operands to match the longest type.
4008 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4009 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4010 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4011 return failure();
4012
4013 auto cmpType = getWidestIntType(lhsIntType, rhsIntType);
4014 if (cmpType.getWidth() == 0) // Handle 0-width inputs by promoting to 1 bit.
4015 cmpType = UIntType::get(builder.getContext(), 1);
4016 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4017 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4018 if (!lhs || !rhs)
4019 return failure();
4020
4021 // Emit the result operation.
4022 Type resultType = builder.getIntegerType(1);
4023 return setLoweringTo<comb::ICmpOp>(
4024 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4025 true);
4026}
4027
4028/// Lower a divide or dynamic shift, where the operation has to be performed
4029/// in the widest type of the result and two inputs then truncated down.
4030template <typename SignedOp, typename UnsignedOp>
4031LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4032 // hw has equal types for these, firrtl doesn't. The type of the firrtl
4033 // RHS may be wider than the LHS, and we cannot truncate off the high bits
4034 // (because an overlarge amount is supposed to shift in sign or zero bits).
4035 auto opType = type_cast<IntType>(op->getResult(0).getType());
4036 if (opType.getWidth() == 0)
4037 return setLowering(op->getResult(0), Value());
4038
4039 auto resultType = getWidestIntType(opType, op->getOperand(1).getType());
4040 resultType = getWidestIntType(resultType, op->getOperand(0).getType());
4041 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4042 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4043 if (!lhs || !rhs)
4044 return failure();
4045
4046 Value result;
4047 if (opType.isSigned())
4048 result = builder.createOrFold<SignedOp>(lhs, rhs, true);
4049 else
4050 result = builder.createOrFold<UnsignedOp>(lhs, rhs, true);
4051
4052 if (auto *definingOp = result.getDefiningOp())
4053 tryCopyName(definingOp, op);
4054
4055 if (resultType == opType)
4056 return setLowering(op->getResult(0), result);
4057 return setLoweringTo<comb::ExtractOp>(op, lowerType(opType), result, 0);
4058}
4059
4060LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4061 auto lhs = getLoweredValue(op.getLhs());
4062 auto rhs = getLoweredValue(op.getRhs());
4063 if (!lhs) {
4064 return handleZeroBit(op.getLhs(), [&]() {
4065 if (rhs) // cat(0bit, x) --> x
4066 return setLowering(op, rhs);
4067 // cat(0bit, 0bit) --> 0bit
4068 return handleZeroBit(op.getRhs(),
4069 [&]() { return setLowering(op, Value()); });
4070 });
4071 }
4072
4073 if (!rhs) // cat(x, 0bit) --> x
4074 return handleZeroBit(op.getRhs(), [&]() { return setLowering(op, lhs); });
4075
4076 return setLoweringTo<comb::ConcatOp>(op, lhs, rhs);
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:212
static Block * getBodyBlock(FModuleLike mod)
std::shared_ptr< calyx::CalyxLoweringState > loweringState
Instantiate one of these and use it to build typed backedges.
void abandon()
Abandon the backedges, suppressing any diagnostics if they are still active upon destruction of the b...
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
mlir::LogicalResult clearOrEmitError()
Clear the backedges, erasing any remaining cursor ops.
Backedge is a wrapper class around a Value.
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition Namespace.h:30
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool removeAnnotation(Annotation anno)
Remove an annotation from this annotation set.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
ResultType visitInvalidOp(Operation *op, ExtraArgs... args)
visitInvalidOp is an override point for non-FIRRTL dialect operations.
ResultType visitUnhandledOp(Operation *op, ExtraArgs... args)
visitUnhandledOp is an override point for FIRRTL dialect ops that the concrete visitor didn't bother ...
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
This is the common base class between SIntType and UIntType.
This table tracks nlas and what modules participate in them.
Definition NLATable.h:29
This is an edge in the InstanceGraph.
create(*sub_arrays)
Definition hw.py:504
create(elements)
Definition hw.py:483
create(array_value, idx)
Definition hw.py:450
create(data_type, value)
Definition hw.py:433
create(dest, src)
Definition sv.py:98
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:55
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
Definition EmitOps.h:32
constexpr const char * extractCoverageAnnoClass
constexpr const char * elaborationArtefactsDirectoryAnnoClass
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
constexpr const char * extractGrandCentralClass
constexpr const char * blackBoxAnnoClass
constexpr const char * dontObfuscateModuleAnnoClass
constexpr const char * metadataDirectoryAttrName
constexpr const char * testBenchDirAnnoClass
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
constexpr const char * dutAnnoClass
constexpr const char * forceNameAnnoClass
constexpr const char * noDedupAnnoClass
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
constexpr const char * extractAssertAnnoClass
constexpr const char * verifBlackBoxAnnoClass
constexpr const char * blackBoxTargetDirAnnoClass
Type lowerType(Type type, std::optional< Location > loc={}, llvm::function_ref< hw::TypeAliasType(Type, BaseTypeAliasType, Location)> getTypeDeclFn={})
Given a type, return the corresponding lowered type for the HW dialect.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
constexpr const char * extractAssumeAnnoClass
constexpr const char * testHarnessHierAnnoClass
constexpr const char * moduleHierAnnoClass
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void 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