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