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