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