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