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