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