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