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