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