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 emitError(builder.getLoc(), "lower-to-core does not support "
2933 "{{SimulationTime}} in printf");
2934 return failure();
2935 })
2936 .Default([&](auto) {
2937 emitError(builder.getLoc(), "has a substitution with "
2938 "an unimplemented "
2939 "lowering")
2940 .attachNote(substitution.getLoc())
2941 << "op with an unimplemented lowering is here";
2942 return failure();
2943 });
2944 if (failed(result))
2945 return failure();
2946 i += 3;
2947 break;
2948 }
2949 default:
2950 literal.push_back(c);
2951 break;
2952 }
2953 }
2954
2955 emitLiteral(literal);
2956 if (fragments.empty())
2957 return sim::FormatLiteralOp::create(builder, "").getResult();
2958 if (fragments.size() == 1)
2959 return fragments.front();
2960 return sim::FormatStringConcatOp::create(builder, fragments).getResult();
2961}
2962
2963LogicalResult FIRRTLLowering::lowerStatementWithFd(
2964 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
2965 const std::function<LogicalResult(Value)> &fn, bool usePrintfCond) {
2966 // Emit an "#ifndef SYNTHESIS" guard into the always block.
2967 bool failed = false;
2968 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
2969 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
2970 addToAlwaysBlock(clock, [&]() {
2971 // TODO: This is not printf specific anymore. Replace "Printf" with "FD"
2972 // or similar but be aware that changing macro name breaks existing uses.
2973 circuitState.usedPrintf = true;
2974 if (usePrintfCond)
2975 circuitState.addFragment(theModule, "PRINTF_COND_FRAGMENT");
2976
2977 // Emit an "sv.if '`PRINTF_COND_ & cond' into the #ifndef.
2978 Value ifCond = cond;
2979 if (usePrintfCond) {
2980 ifCond =
2981 sv::MacroRefExprOp::create(builder, cond.getType(), "PRINTF_COND_");
2982 ifCond = builder.createOrFold<comb::AndOp>(ifCond, cond, true);
2983 }
2984
2985 addIfProceduralBlock(ifCond, [&]() {
2986 // `fd`represents a file decriptor. Use the stdout or the one opened
2987 // using $fopen.
2988 Value fd;
2989 if (fileDescriptor.isDefaultFd()) {
2990 // Emit the sv.fwrite, writing to stderr by default.
2991 fd = hw::ConstantOp::create(builder, APInt(32, 0x80000002));
2992 } else {
2993 // Call the library function to get the FD.
2994 auto fdOrError = callFileDescriptorLib(fileDescriptor);
2995 if (llvm::failed(fdOrError)) {
2996 failed = true;
2997 return;
2998 }
2999 fd = *fdOrError;
3000 }
3001 failed = llvm::failed(fn(fd));
3002 });
3003 });
3004 });
3005 return failure(failed);
3006}
3007
3008FailureOr<Value>
3009FIRRTLLowering::callFileDescriptorLib(const FileDescriptorInfo &info) {
3010 circuitState.usedFileDescriptorLib = true;
3011 circuitState.addFragment(
3012 theModule, sv::getFileDescriptorFragmentRef(builder.getContext()));
3013
3014 Value fileName;
3015 if (info.isSubstitutionRequired()) {
3016 SmallVector<Value> fileNameOperands;
3017 if (failed(loweredFmtOperands(info.getSubstitutions(), fileNameOperands)))
3018 return failure();
3019
3020 fileName = sv::SFormatFOp::create(builder, info.getOutputFileFormat(),
3021 fileNameOperands)
3022 .getResult();
3023 } else {
3024 // If substitution is not required, just use the output file name.
3025 fileName = sv::ConstantStrOp::create(builder, info.getOutputFileFormat())
3026 .getResult();
3027 }
3028
3029 return sv::createProceduralFileDescriptorGetterCall(builder, builder.getLoc(),
3030 fileName);
3031}
3032
3033/// Set the lowered value of 'orig' to 'result', remembering this in a map.
3034/// This always returns success() to make it more convenient in lowering code.
3035///
3036/// Note that result may be null here if we're lowering orig to a zero-bit
3037/// value.
3038///
3039LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
3040 if (auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
3041 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
3042 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
3043
3044#ifndef NDEBUG
3045 auto baseType = getBaseType(origType);
3046 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
3047
3048 // Caller should pass null value iff this was a zero bit value.
3049 if (srcWidth != -1) {
3050 if (result)
3051 assert((srcWidth != 0) &&
3052 "Lowering produced value for zero width source");
3053 else
3054 assert((srcWidth == 0) &&
3055 "Lowering produced null value but source wasn't zero width");
3056 }
3057#endif
3058 } else {
3059 assert(result && "Lowering of foreign type produced null value");
3060 }
3061
3062 auto &slot = valueMapping[orig];
3063 assert(!slot && "value lowered multiple times");
3064 slot = result;
3065 return success();
3066}
3067
3068/// Set the lowering for a value to the specified result. This came from a
3069/// possible folding, so check to see if we need to handle a constant.
3070LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
3071 Value result) {
3072 // If this is a constant, check to see if we have it in our unique mapping:
3073 // it could have come from folding an operation.
3074 if (auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
3075 auto &entry = hwConstantMap[cst.getValueAttr()];
3076 if (entry == cst) {
3077 // We're already using an entry in the constant map, nothing to do.
3078 } else if (entry) {
3079 // We already had this constant, reuse the one we have instead of the
3080 // one we just folded.
3081 result = entry;
3082 cst->erase();
3083 } else {
3084 // This is a new constant. Remember it!
3085 entry = cst;
3086 cst->moveBefore(&theModule.getBodyBlock()->front());
3087 }
3088 }
3089
3090 return setLowering(orig, result);
3091}
3092
3093/// Create a new operation with type ResultOpType and arguments CtorArgTypes,
3094/// then call setLowering with its result.
3095template <typename ResultOpType, typename... CtorArgTypes>
3096LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
3097 CtorArgTypes... args) {
3098 auto result = builder.createOrFold<ResultOpType>(args...);
3099 if (auto *op = result.getDefiningOp())
3100 tryCopyName(op, orig);
3101 return setPossiblyFoldedLowering(orig->getResult(0), result);
3102}
3103
3104/// Create a new LTL operation with type ResultOpType and arguments
3105/// CtorArgTypes, then call setLowering with its result. Also add the operation
3106/// to the worklist of LTL ops that need to have their types fixed-up after the
3107/// lowering.
3108template <typename ResultOpType, typename... CtorArgTypes>
3109LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
3110 CtorArgTypes... args) {
3111 auto result = builder.createOrFold<ResultOpType>(args...);
3112 if (auto *op = result.getDefiningOp())
3113 ltlOpFixupWorklist.insert(op);
3114 return setPossiblyFoldedLowering(orig->getResult(0), result);
3115}
3116
3117/// Creates a backedge of the specified result type. A backedge represents a
3118/// placeholder to be filled in later by a lowered value. If the backedge is not
3119/// updated with a real value by the end of the pass, it will be replaced with
3120/// an undriven wire. Backedges are allowed to be updated to other backedges.
3121/// If a chain of backedges forms a combinational loop, they will be replaced
3122/// with an undriven wire.
3123Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
3124 auto backedge = backedgeBuilder.get(type, loc);
3125 backedges.insert({backedge, backedge});
3126 return backedge;
3127}
3128
3129/// Sets the lowering for a value to a backedge of the specified result type.
3130/// This is useful for lowering types which cannot pass through a wire, or to
3131/// directly materialize values in operations that violate the SSA dominance
3132/// constraint.
3133Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
3134 auto backedge = createBackedge(orig.getLoc(), type);
3135 (void)setLowering(orig, backedge);
3136 return backedge;
3137}
3138
3139/// If the `from` value is in fact a backedge, record that the backedge will
3140/// be replaced by the value. Return true if the destination is a backedge.
3141bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
3142 auto backedgeIt = backedges.find(dest);
3143 if (backedgeIt == backedges.end())
3144 return false;
3145 backedgeIt->second = src;
3146 return true;
3147}
3148
3149/// Switch the insertion point of the current builder to the end of the
3150/// specified block and run the closure. This correctly handles the case
3151/// where the closure is null, but the caller needs to make sure the block
3152/// exists.
3153void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
3154 const std::function<void(void)> &fn, Region &region) {
3155 if (!fn)
3156 return;
3157
3158 auto oldIP = builder.saveInsertionPoint();
3159
3160 builder.setInsertionPointToEnd(&region.front());
3161 fn();
3162 builder.restoreInsertionPoint(oldIP);
3163}
3164
3165/// Return a read value for the specified inout operation, auto-uniquing them.
3166Value FIRRTLLowering::getReadValue(Value v) {
3167 Value result = readInOutCreated.lookup(v);
3168 if (result)
3169 return result;
3170
3171 // Make sure to put the read value at the correct scope so it dominates all
3172 // future uses.
3173 auto oldIP = builder.saveInsertionPoint();
3174 if (auto *vOp = v.getDefiningOp()) {
3175 builder.setInsertionPointAfter(vOp);
3176 } else {
3177 // For reads of ports, just set the insertion point at the top of the
3178 // module.
3179 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
3180 }
3181
3182 // Instead of creating `ReadInOutOp` for `ArrayIndexInOutOp`, create
3183 // `ArrayGetOp` for root arrays.
3184 if (auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
3185 result = getReadValue(arrayIndexInout.getInput());
3186 result = builder.createOrFold<hw::ArrayGetOp>(result,
3187 arrayIndexInout.getIndex());
3188 } else {
3189 // Otherwise, create a read inout operation.
3190 result = builder.createOrFold<sv::ReadInOutOp>(v);
3191 }
3192 builder.restoreInsertionPoint(oldIP);
3193 readInOutCreated.insert({v, result});
3194 return result;
3195}
3196
3197Value FIRRTLLowering::getNonClockValue(Value v) {
3198 auto it = fromClockMapping.try_emplace(v, Value{});
3199 if (it.second) {
3200 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
3201 builder.setInsertionPointAfterValue(v);
3202 it.first->second = seq::FromClockOp::create(builder, v);
3203 }
3204 return it.first->second;
3205}
3206
3207void FIRRTLLowering::addToAlwaysBlock(
3208 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
3209 sv::EventControl resetEdge, Value reset,
3210 const std::function<void(void)> &body,
3211 const std::function<void(void)> &resetBody) {
3212 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
3213 resetStyle, resetEdge, reset};
3214 sv::AlwaysOp alwaysOp;
3215 sv::IfOp insideIfOp;
3216 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
3217
3218 if (!alwaysOp) {
3219 if (reset) {
3220 assert(resetStyle != sv::ResetType::NoReset);
3221 // Here, we want to create the folloing structure with sv.always and
3222 // sv.if. If `reset` is async, we need to add `reset` to a sensitivity
3223 // list.
3224 //
3225 // sv.always @(clockEdge or reset) {
3226 // sv.if (reset) {
3227 // resetBody
3228 // } else {
3229 // body
3230 // }
3231 // }
3232
3233 auto createIfOp = [&]() {
3234 // It is weird but intended. Here we want to create an empty sv.if
3235 // with an else block.
3236 insideIfOp = sv::IfOp::create(
3237 builder, reset, [] {}, [] {});
3238 };
3239 if (resetStyle == sv::ResetType::AsyncReset) {
3240 sv::EventControl events[] = {clockEdge, resetEdge};
3241 Value clocks[] = {clock, reset};
3242
3243 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
3244 if (resetEdge == sv::EventControl::AtNegEdge)
3245 llvm_unreachable("negative edge for reset is not expected");
3246 createIfOp();
3247 });
3248 } else {
3249 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
3250 }
3251 } else {
3252 assert(!resetBody);
3253 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
3254 insideIfOp = nullptr;
3255 }
3256 alwaysBlocks[key] = {alwaysOp, insideIfOp};
3257 }
3258
3259 if (reset) {
3260 assert(insideIfOp && "reset body must be initialized before");
3261 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
3262 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
3263 } else {
3264 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
3265 }
3266
3267 // Move the earlier always block(s) down to where the last would have been
3268 // inserted. This ensures that any values used by the always blocks are
3269 // defined ahead of the uses, which leads to better generated Verilog.
3270 alwaysOp->moveBefore(builder.getInsertionBlock(),
3271 builder.getInsertionPoint());
3272}
3273
3274LogicalResult FIRRTLLowering::emitGuards(Location loc,
3275 ArrayRef<Attribute> guards,
3276 std::function<void(void)> emit) {
3277 if (guards.empty()) {
3278 emit();
3279 return success();
3280 }
3281 auto guard = dyn_cast<StringAttr>(guards[0]);
3282 if (!guard)
3283 return mlir::emitError(loc,
3284 "elements in `guards` array must be `StringAttr`");
3285
3286 // Record the guard macro to emit a declaration for it.
3287 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
3288 LogicalResult result = LogicalResult::failure();
3289 addToIfDefBlock(guard.getValue(), [&]() {
3290 result = emitGuards(loc, guards.drop_front(), emit);
3291 });
3292 return result;
3293}
3294
3295void FIRRTLLowering::addToIfDefBlock(StringRef cond,
3296 std::function<void(void)> thenCtor,
3297 std::function<void(void)> elseCtor) {
3298 auto condAttr = builder.getStringAttr(cond);
3299 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
3300 if (op) {
3301 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
3302 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
3303
3304 // Move the earlier #ifdef block(s) down to where the last would have been
3305 // inserted. This ensures that any values used by the #ifdef blocks are
3306 // defined ahead of the uses, which leads to better generated Verilog.
3307 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3308 } else {
3309 ifdefBlocks[{builder.getBlock(), condAttr}] =
3310 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
3311 }
3312}
3313
3314void FIRRTLLowering::addToInitialBlock(std::function<void(void)> body) {
3315 auto op = initialBlocks.lookup(builder.getBlock());
3316 if (op) {
3317 runWithInsertionPointAtEndOfBlock(body, op.getBody());
3318
3319 // Move the earlier initial block(s) down to where the last would have
3320 // been inserted. This ensures that any values used by the initial blocks
3321 // are defined ahead of the uses, which leads to better generated Verilog.
3322 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3323 } else {
3324 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
3325 }
3326}
3327
3328void FIRRTLLowering::addIfProceduralBlock(Value cond,
3329 std::function<void(void)> thenCtor,
3330 std::function<void(void)> elseCtor) {
3331 // Check to see if we already have an if on this condition immediately
3332 // before the insertion point. If so, extend it.
3333 auto insertIt = builder.getInsertionPoint();
3334 if (insertIt != builder.getBlock()->begin())
3335 if (auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3336 if (ifOp.getCond() == cond) {
3337 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3338 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3339 return;
3340 }
3341 }
3342
3343 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3344}
3345
3346//===----------------------------------------------------------------------===//
3347// Special Operations
3348//===----------------------------------------------------------------------===//
3349
3350/// Handle the case where an operation wasn't lowered. When this happens, the
3351/// operands should just be unlowered non-FIRRTL values. If the operand was
3352/// not lowered then leave it alone, otherwise we have a problem with
3353/// lowering.
3354///
3355FIRRTLLowering::UnloweredOpResult
3356FIRRTLLowering::handleUnloweredOp(Operation *op) {
3357 // FIRRTL operations must explicitly handle their regions.
3358 if (!op->getRegions().empty() &&
3359 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3360 op->emitOpError("must explicitly handle its regions");
3361 return LoweringFailure;
3362 }
3363
3364 // Simply pass through non-FIRRTL operations and consider them already
3365 // lowered. This allows us to handled partially lowered inputs, and also allow
3366 // other FIRRTL operations to spawn additional already-lowered operations,
3367 // like `hw.output`.
3368 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3369 // Push nested operations onto the worklist such that they are lowered.
3370 for (auto &region : op->getRegions())
3371 addToWorklist(region);
3372 for (auto &operand : op->getOpOperands())
3373 if (auto lowered = getPossiblyInoutLoweredValue(operand.get()))
3374 operand.set(lowered);
3375 for (auto result : op->getResults())
3376 (void)setLowering(result, result);
3377 return AlreadyLowered;
3378 }
3379
3380 // Ok, at least one operand got lowered, so this operation is using a FIRRTL
3381 // value, but wasn't itself lowered. This is because the lowering is
3382 // incomplete. This is either a bug or incomplete implementation.
3383 //
3384 // There is one aspect of incompleteness we intentionally expect: we allow
3385 // primitive operations that produce a zero bit result to be ignored by the
3386 // lowering logic. They don't have side effects, and handling this corner
3387 // case just complicates each of the lowering hooks. Instead, we just handle
3388 // them all right here.
3389 if (op->getNumResults() == 1) {
3390 auto resultType = op->getResult(0).getType();
3391 if (type_isa<FIRRTLBaseType>(resultType) &&
3392 isZeroBitFIRRTLType(resultType) &&
3393 (isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3394 // Zero bit values lower to the null Value.
3395 (void)setLowering(op->getResult(0), Value());
3396 return NowLowered;
3397 }
3398 }
3399 op->emitOpError("LowerToHW couldn't handle this operation");
3400 return LoweringFailure;
3401}
3402
3403LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3404 // Zero width values must be lowered to nothing.
3405 if (isZeroBitFIRRTLType(op.getType()))
3406 return setLowering(op, Value());
3407
3408 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3409}
3410
3411LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3412 Value cst;
3413 if (isa<ClockType>(op.getType())) {
3414 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3415 : seq::ClockConst::Low);
3416 } else {
3417 cst = getOrCreateIntConstant(APInt(/*bitWidth*/ 1, op.getValue()));
3418 }
3419 return setLowering(op, cst);
3420}
3421
3422FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3423 auto iIdx = getOrCreateIntConstant(
3425 firrtl::type_cast<FVectorType>(op.getInput().getType())
3426 .getNumElements()),
3427 op.getIndex());
3428
3429 // If the input has an inout type, we need to lower to ArrayIndexInOutOp;
3430 // otherwise hw::ArrayGetOp.
3431 Value result;
3432 if (isa<sv::InOutType>(input.getType()))
3433 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3434 else
3435 result = builder.createOrFold<hw::ArrayGetOp>(input, iIdx);
3436 if (auto *definingOp = result.getDefiningOp())
3437 tryCopyName(definingOp, op);
3438 return result;
3439}
3440
3441FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3442 Value valueIdx = getLoweredAndExtOrTruncValue(
3443 op.getIndex(),
3444 UIntType::get(op->getContext(),
3446 firrtl::type_cast<FVectorType>(op.getInput().getType())
3447 .getNumElements())));
3448 if (!valueIdx) {
3449 op->emitError() << "input lowering failed";
3450 return failure();
3451 }
3452
3453 // If the input has an inout type, we need to lower to ArrayIndexInOutOp;
3454 // otherwise, lower the op to array indexing.
3455 Value result;
3456 if (isa<sv::InOutType>(input.getType()))
3457 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3458 else
3459 result = createArrayIndexing(input, valueIdx);
3460 if (auto *definingOp = result.getDefiningOp())
3461 tryCopyName(definingOp, op);
3462 return result;
3463}
3464
3465FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3466 auto resultType = lowerType(op->getResult(0).getType());
3467 if (!resultType || !input) {
3468 op->emitError() << "subfield type lowering failed";
3469 return failure();
3470 }
3471
3472 // If the input has an inout type, we need to lower to StructFieldInOutOp;
3473 // otherwise, StructExtractOp.
3474 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3475 .getElementName(op.getFieldIndex());
3476 Value result;
3477 if (isa<sv::InOutType>(input.getType()))
3478 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3479 else
3480 result = builder.createOrFold<hw::StructExtractOp>(input, field);
3481 if (auto *definingOp = result.getDefiningOp())
3482 tryCopyName(definingOp, op);
3483 return result;
3484}
3485
3486LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3487 if (isZeroBitFIRRTLType(op.getType()))
3488 return setLowering(op, Value());
3489
3490 auto input = getPossiblyInoutLoweredValue(op.getInput());
3491 if (!input)
3492 return op.emitError() << "input lowering failed";
3493
3494 auto result = lowerSubindex(op, input);
3495 if (failed(result))
3496 return failure();
3497 return setLowering(op, *result);
3498}
3499
3500LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3501 if (isZeroBitFIRRTLType(op.getType()))
3502 return setLowering(op, Value());
3503
3504 auto input = getPossiblyInoutLoweredValue(op.getInput());
3505 if (!input)
3506 return op.emitError() << "input lowering failed";
3507
3508 auto result = lowerSubaccess(op, input);
3509 if (failed(result))
3510 return failure();
3511 return setLowering(op, *result);
3512}
3513
3514LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3515 // firrtl.mem lowering lowers some SubfieldOps. Zero-width can leave
3516 // invalid subfield accesses
3517 if (getLoweredValue(op) || !op.getInput())
3518 return success();
3519
3520 if (isZeroBitFIRRTLType(op.getType()))
3521 return setLowering(op, Value());
3522
3523 auto input = getPossiblyInoutLoweredValue(op.getInput());
3524 if (!input)
3525 return op.emitError() << "input lowering failed";
3526
3527 auto result = lowerSubfield(op, input);
3528 if (failed(result))
3529 return failure();
3530 return setLowering(op, *result);
3531}
3532
3533LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3534 auto resultType = lowerType(op.getResult().getType());
3535 SmallVector<Value> operands;
3536 // NOTE: The operand order must be inverted.
3537 for (auto oper : llvm::reverse(op.getOperands())) {
3538 auto val = getLoweredValue(oper);
3539 if (!val)
3540 return failure();
3541 operands.push_back(val);
3542 }
3543 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3544}
3545
3546LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3547 auto resultType = lowerType(op.getResult().getType());
3548 SmallVector<Value> operands;
3549 for (auto oper : op.getOperands()) {
3550 auto val = getLoweredValue(oper);
3551 if (!val)
3552 return failure();
3553 operands.push_back(val);
3554 }
3555 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3556}
3557
3558LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3559 // Zero width values must be lowered to nothing.
3560 if (isZeroBitFIRRTLType(op.getType()))
3561 return setLowering(op, Value());
3562
3563 auto input = getLoweredValue(op.getInput());
3564 auto tagName = op.getFieldNameAttr();
3565 auto oldType = op.getType().base();
3566 auto newType = lowerType(oldType);
3567 auto element = *oldType.getElement(op.getFieldNameAttr());
3568
3569 if (auto structType = dyn_cast<hw::StructType>(newType)) {
3570 auto tagType = structType.getFieldType("tag");
3571 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3572 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3573 tagName);
3574 auto bodyType = structType.getFieldType("body");
3575 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3576 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3577 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3578 }
3579 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3580 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3581}
3582
3583LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3584 auto resultType = lowerType(op.getResult().getType());
3585 auto attr =
3586 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3587
3588 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3589 cast<ArrayAttr>(attr));
3590}
3591
3592LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3593
3594 auto tagName = op.getFieldNameAttr();
3595 auto lhs = getLoweredValue(op.getInput());
3596 if (isa<hw::StructType>(lhs.getType()))
3597 lhs = hw::StructExtractOp::create(builder, lhs, "tag");
3598
3599 auto index = op.getFieldIndex();
3600 auto enumType = op.getInput().getType().base();
3601 auto tagValue = enumType.getElementValueAttr(index);
3602 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3603 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3604 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3605 loweredTagValue, tagName);
3606
3607 Type resultType = builder.getIntegerType(1);
3608 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3609 rhs, true);
3610}
3611
3612LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3613 // Zero width values must be lowered to nothing.
3614 if (isZeroBitFIRRTLType(op.getType()))
3615 return setLowering(op, Value());
3616
3617 auto tagName = op.getFieldNameAttr();
3618 auto input = getLoweredValue(op.getInput());
3619 auto field = hw::StructExtractOp::create(builder, input, "body");
3620 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3621}
3622
3623LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3624 // Zero width values must be lowered to nothing.
3625 if (isZeroBitFIRRTLType(op.getType()))
3626 return setLowering(op, Value());
3627
3628 auto input = getLoweredValue(op.getInput());
3629 if (!input)
3630 return failure();
3631
3632 // If the lowered enum is a struct (has both tag and body), extract the tag
3633 // field.
3634 if (isa<hw::StructType>(input.getType())) {
3635 return setLoweringTo<hw::StructExtractOp>(op, input, "tag");
3636 }
3637
3638 // If the lowered enum is just the tag (simple enum with no data), return it
3639 // directly.
3640 return setLowering(op, input);
3641}
3642
3643//===----------------------------------------------------------------------===//
3644// Declarations
3645//===----------------------------------------------------------------------===//
3646
3647LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3648 auto origResultType = op.getResult().getType();
3649
3650 // Foreign types lower to a backedge that needs to be resolved by a later
3651 // connect op.
3652 if (!type_isa<FIRRTLType>(origResultType)) {
3653 createBackedge(op.getResult(), origResultType);
3654 return success();
3655 }
3656
3657 auto resultType = lowerType(origResultType);
3658 if (!resultType)
3659 return failure();
3660
3661 if (resultType.isInteger(0)) {
3662 if (op.getInnerSym())
3663 return op.emitError("zero width wire is referenced by name [")
3664 << *op.getInnerSym() << "] (e.g. in an XMR) but must be removed";
3665 return setLowering(op.getResult(), Value());
3666 }
3667
3668 // Name attr is required on sv.wire but optional on firrtl.wire.
3669 auto innerSym = lowerInnerSymbol(op);
3670 auto name = op.getNameAttr();
3671 // This is not a temporary wire created by the compiler, so attach a symbol
3672 // name.
3673 auto wire = hw::WireOp::create(
3674 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3675
3676 if (auto svAttrs = sv::getSVAttributes(op))
3677 sv::setSVAttributes(wire, svAttrs);
3678
3679 return setLowering(op.getResult(), wire);
3680}
3681
3682LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3683 auto resultTy = lowerType(op.getType());
3684 if (!resultTy)
3685 return failure();
3686 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3687
3688 SmallVector<Value, 4> operands;
3689 operands.reserve(op.getSubstitutions().size());
3690 for (auto operand : op.getSubstitutions()) {
3691 auto lowered = getLoweredValue(operand);
3692 if (!lowered)
3693 return failure();
3694 operands.push_back(lowered);
3695 }
3696
3697 ArrayAttr symbols = op.getSymbolsAttr();
3698 if (!symbols)
3699 symbols = ArrayAttr::get(op.getContext(), {});
3700
3701 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3702 operands, symbols);
3703}
3704
3705LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3706 auto operand = getLoweredValue(op.getInput());
3707 if (!operand)
3708 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3709 if (op.getInnerSym())
3710 return op.emitError("zero width node is referenced by name [")
3711 << *op.getInnerSym()
3712 << "] (e.g. in an XMR) but must be "
3713 "removed";
3714 return setLowering(op.getResult(), Value());
3715 });
3716
3717 // Node operations are logical noops, but may carry annotations or be
3718 // referred to through an inner name. If a don't touch is present, ensure
3719 // that we have a symbol name so we can keep the node as a wire.
3720 auto name = op.getNameAttr();
3721 auto innerSym = lowerInnerSymbol(op);
3722
3723 if (innerSym)
3724 operand = hw::WireOp::create(builder, operand, name, innerSym);
3725
3726 // Move SV attributes.
3727 if (auto svAttrs = sv::getSVAttributes(op)) {
3728 if (!innerSym)
3729 operand = hw::WireOp::create(builder, operand, name);
3730 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3731 }
3732
3733 return setLowering(op.getResult(), operand);
3734}
3735
3736LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3737 auto resultType = lowerType(op.getResult().getType());
3738 if (!resultType)
3739 return failure();
3740 if (resultType.isInteger(0))
3741 return setLowering(op.getResult(), Value());
3742
3743 Value clockVal = getLoweredValue(op.getClockVal());
3744 if (!clockVal)
3745 return failure();
3746
3747 // Create a reg op, wiring itself to its input.
3748 auto innerSym = lowerInnerSymbol(op);
3749 Backedge inputEdge = backedgeBuilder.get(resultType);
3750 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3751 op.getNameAttr(), innerSym);
3752
3753 // Pass along the start and end random initialization bits for this register.
3754 if (auto randomRegister = op->getAttr("firrtl.random_init_register"))
3755 reg->setAttr("firrtl.random_init_register", randomRegister);
3756 if (auto randomStart = op->getAttr("firrtl.random_init_start"))
3757 reg->setAttr("firrtl.random_init_start", randomStart);
3758 if (auto randomEnd = op->getAttr("firrtl.random_init_end"))
3759 reg->setAttr("firrtl.random_init_end", randomEnd);
3760
3761 // Move SV attributes.
3762 if (auto svAttrs = sv::getSVAttributes(op))
3763 sv::setSVAttributes(reg, svAttrs);
3764
3765 inputEdge.setValue(reg);
3766 (void)setLowering(op.getResult(), reg);
3767 return success();
3768}
3769
3770LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3771 auto resultType = lowerType(op.getResult().getType());
3772 if (!resultType)
3773 return failure();
3774 if (resultType.isInteger(0))
3775 return setLowering(op.getResult(), Value());
3776
3777 Value clockVal = getLoweredValue(op.getClockVal());
3778 Value resetSignal = getLoweredValue(op.getResetSignal());
3779 // Reset values may be narrower than the register. Extend appropriately.
3780 Value resetValue = getLoweredAndExtOrTruncValue(
3781 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3782
3783 if (!clockVal || !resetSignal || !resetValue)
3784 return failure();
3785
3786 // Create a reg op, wiring itself to its input.
3787 auto innerSym = lowerInnerSymbol(op);
3788 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3789 Backedge inputEdge = backedgeBuilder.get(resultType);
3790 auto reg =
3791 seq::FirRegOp::create(builder, inputEdge, clockVal, op.getNameAttr(),
3792 resetSignal, resetValue, innerSym, isAsync);
3793
3794 // Pass along the start and end random initialization bits for this register.
3795 if (auto randomRegister = op->getAttr("firrtl.random_init_register"))
3796 reg->setAttr("firrtl.random_init_register", randomRegister);
3797 if (auto randomStart = op->getAttr("firrtl.random_init_start"))
3798 reg->setAttr("firrtl.random_init_start", randomStart);
3799 if (auto randomEnd = op->getAttr("firrtl.random_init_end"))
3800 reg->setAttr("firrtl.random_init_end", randomEnd);
3801
3802 // Move SV attributes.
3803 if (auto svAttrs = sv::getSVAttributes(op))
3804 sv::setSVAttributes(reg, svAttrs);
3805
3806 inputEdge.setValue(reg);
3807 (void)setLowering(op.getResult(), reg);
3808
3809 return success();
3810}
3811
3812LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3813 // TODO: Remove this restriction and preserve aggregates in
3814 // memories.
3815 if (type_isa<BundleType>(op.getDataType()))
3816 return op.emitOpError(
3817 "should have already been lowered from a ground type to an aggregate "
3818 "type using the LowerTypes pass. Use "
3819 "'firtool --lower-types' or 'circt-opt "
3820 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3821 "to run this.");
3822
3823 FirMemory memSummary = op.getSummary();
3824
3825 // Create the memory declaration.
3826 auto memType = seq::FirMemType::get(
3827 op.getContext(), memSummary.depth, memSummary.dataWidth,
3828 memSummary.isMasked ? std::optional<uint32_t>(memSummary.maskBits)
3829 : std::optional<uint32_t>());
3830
3831 seq::FirMemInitAttr memInit;
3832 if (auto init = op.getInitAttr())
3833 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3834 init.getIsBinary(), init.getIsInline());
3835
3836 auto memDecl = seq::FirMemOp::create(
3837 builder, memType, memSummary.readLatency, memSummary.writeLatency,
3838 memSummary.readUnderWrite, memSummary.writeUnderWrite, op.getNameAttr(),
3839 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3840
3841 if (auto parent = op->getParentOfType<hw::HWModuleOp>()) {
3842 if (auto file = parent->getAttrOfType<hw::OutputFileAttr>("output_file")) {
3843 auto dir = file;
3844 if (!file.isDirectory())
3845 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3846 file.getDirectory());
3847 memDecl.setOutputFileAttr(dir);
3848 }
3849 }
3850
3851 // Memories return multiple structs, one for each port, which means we
3852 // have two layers of type to split apart.
3853 for (size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3854
3855 auto addOutput = [&](StringRef field, size_t width, Value value) {
3856 for (auto &a : getAllFieldAccesses(op.getResult(i), field)) {
3857 if (width > 0)
3858 (void)setLowering(a, value);
3859 else
3860 a->eraseOperand(0);
3861 }
3862 };
3863
3864 auto addInput = [&](StringRef field, Value backedge) {
3865 for (auto a : getAllFieldAccesses(op.getResult(i), field)) {
3866 if (cast<FIRRTLBaseType>(a.getType())
3867 .getPassiveType()
3868 .getBitWidthOrSentinel() > 0)
3869 (void)setLowering(a, backedge);
3870 else
3871 a->eraseOperand(0);
3872 }
3873 };
3874
3875 auto addInputPort = [&](StringRef field, size_t width) -> Value {
3876 // If the memory is 0-width, do not materialize any connections to it.
3877 // However, `seq.firmem` now requires a 1-bit input, so materialize
3878 // a dummy x value to provide it with.
3879 Value backedge, portValue;
3880 if (width == 0) {
3881 portValue = getOrCreateXConstant(1);
3882 } else {
3883 auto portType = IntegerType::get(op.getContext(), width);
3884 backedge = portValue = createBackedge(builder.getLoc(), portType);
3885 }
3886 addInput(field, backedge);
3887 return portValue;
3888 };
3889
3890 auto addClock = [&](StringRef field) -> Value {
3891 Type clockTy = seq::ClockType::get(op.getContext());
3892 Value portValue = createBackedge(builder.getLoc(), clockTy);
3893 addInput(field, portValue);
3894 return portValue;
3895 };
3896
3897 auto memportKind = op.getPortKind(i);
3898 if (memportKind == MemOp::PortKind::Read) {
3899 auto addr = addInputPort("addr", op.getAddrBits());
3900 auto en = addInputPort("en", 1);
3901 auto clk = addClock("clk");
3902 auto data = seq::FirMemReadOp::create(builder, memDecl, addr, clk, en);
3903 addOutput("data", memSummary.dataWidth, data);
3904 } else if (memportKind == MemOp::PortKind::ReadWrite) {
3905 auto addr = addInputPort("addr", op.getAddrBits());
3906 auto en = addInputPort("en", 1);
3907 auto clk = addClock("clk");
3908 // If maskBits =1, then And the mask field with enable, and update the
3909 // enable. Else keep mask port.
3910 auto mode = addInputPort("wmode", 1);
3911 if (!memSummary.isMasked)
3912 mode = builder.createOrFold<comb::AndOp>(mode, addInputPort("wmask", 1),
3913 true);
3914 auto wdata = addInputPort("wdata", memSummary.dataWidth);
3915 // Ignore mask port, if maskBits =1
3916 Value mask;
3917 if (memSummary.isMasked)
3918 mask = addInputPort("wmask", memSummary.maskBits);
3919 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl, addr, clk,
3920 en, wdata, mode, mask);
3921 addOutput("rdata", memSummary.dataWidth, rdata);
3922 } else {
3923 auto addr = addInputPort("addr", op.getAddrBits());
3924 // If maskBits =1, then And the mask field with enable, and update the
3925 // enable. Else keep mask port.
3926 auto en = addInputPort("en", 1);
3927 if (!memSummary.isMasked)
3928 en = builder.createOrFold<comb::AndOp>(en, addInputPort("mask", 1),
3929 true);
3930 auto clk = addClock("clk");
3931 auto data = addInputPort("data", memSummary.dataWidth);
3932 // Ignore mask port, if maskBits =1
3933 Value mask;
3934 if (memSummary.isMasked)
3935 mask = addInputPort("mask", memSummary.maskBits);
3936 seq::FirMemWriteOp::create(builder, memDecl, addr, clk, en, data, mask);
3937 }
3938 }
3939
3940 return success();
3941}
3942
3943LogicalResult
3944FIRRTLLowering::prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
3945 Operation *instanceOp,
3946 SmallVectorImpl<Value> &inputOperands) {
3947
3948 for (size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3949 auto &port = portInfo[portIndex];
3950 auto portType = lowerType(port.type);
3951 if (!portType) {
3952 instanceOp->emitOpError("could not lower type of port ") << port.name;
3953 return failure();
3954 }
3955
3956 // Drop zero bit input/inout ports.
3957 if (portType.isInteger(0))
3958 continue;
3959
3960 // We wire outputs up after creating the instance.
3961 if (port.isOutput())
3962 continue;
3963
3964 auto portResult = instanceOp->getResult(portIndex);
3965 assert(portResult && "invalid IR, couldn't find port");
3966
3967 // Replace the input port with a backedge. If it turns out that this port
3968 // is never driven, an uninitialized wire will be materialized at the end.
3969 if (port.isInput()) {
3970 inputOperands.push_back(createBackedge(portResult, portType));
3971 continue;
3972 }
3973
3974 // If the result has an analog type and is used only by attach op, try
3975 // eliminating a temporary wire by directly using an attached value.
3976 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3977 if (auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3978 if (auto source = getSingleNonInstanceOperand(attach)) {
3979 auto loweredResult = getPossiblyInoutLoweredValue(source);
3980 inputOperands.push_back(loweredResult);
3981 (void)setLowering(portResult, loweredResult);
3982 continue;
3983 }
3984 }
3985 }
3986
3987 // Create a wire for each inout operand, so there is something to connect
3988 // to. The instance becomes the sole driver of this wire.
3989 auto wire = sv::WireOp::create(builder, portType,
3990 "." + port.getName().str() + ".wire");
3991
3992 // Know that the argument FIRRTL value is equal to this wire, allowing
3993 // connects to it to be lowered.
3994 (void)setLowering(portResult, wire);
3995 inputOperands.push_back(wire);
3996 }
3997
3998 return success();
3999}
4000
4001LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
4002 Operation *oldModule =
4003 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
4004
4005 auto *newModule = circuitState.getNewModule(oldModule);
4006 if (!newModule) {
4007 oldInstance->emitOpError("could not find module [")
4008 << oldInstance.getModuleName() << "] referenced by instance";
4009 return failure();
4010 }
4011
4012 // If this is a referenced to a parameterized extmodule, then bring the
4013 // parameters over to this instance.
4014 ArrayAttr parameters;
4015 if (auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
4016 parameters = getHWParameters(oldExtModule, /*ignoreValues=*/false);
4017
4018 // Decode information about the input and output ports on the referenced
4019 // module.
4020 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
4021
4022 // Ok, get ready to create the new instance operation. We need to prepare
4023 // input operands.
4024 SmallVector<Value, 8> operands;
4025 if (failed(prepareInstanceOperands(portInfo, oldInstance, operands)))
4026 return failure();
4027
4028 // If this instance is destined to be lowered to a bind, generate a symbol
4029 // for it and generate a bind op. Enter the bind into global
4030 // CircuitLoweringState so that this can be moved outside of module once
4031 // we're guaranteed to not be a parallel context.
4032 auto innerSym = oldInstance.getInnerSymAttr();
4033 if (oldInstance.getLowerToBind()) {
4034 if (!innerSym)
4035 std::tie(innerSym, std::ignore) = getOrAddInnerSym(
4036 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
4037 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
4038
4039 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
4040 innerSym.getSymName());
4041 // If the lowered op already had output file information, then use that.
4042 // Otherwise, generate some default bind information.
4043 if (auto outputFile = oldInstance->getAttr("output_file"))
4044 bindOp->setAttr("output_file", outputFile);
4045 // Add the bind to the circuit state. This will be moved outside of the
4046 // encapsulating module after all modules have been processed in parallel.
4047 circuitState.addBind(bindOp);
4048 }
4049
4050 // Create the new hw.instance operation.
4051 auto newInstance =
4052 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
4053 operands, parameters, innerSym);
4054
4055 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
4056 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
4057
4058 if (newInstance.getInnerSymAttr())
4059 if (auto forceName = circuitState.instanceForceNames.lookup(
4060 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
4061 newInstance.getInnerNameAttr()}))
4062 newInstance->setAttr("hw.verilogName", forceName);
4063
4064 // Now that we have the new hw.instance, we need to remap all of the users
4065 // of the outputs/results to the values returned by the instance.
4066 unsigned resultNo = 0;
4067 for (size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4068 auto &port = portInfo[portIndex];
4069 if (!port.isOutput() || isZeroBitFIRRTLType(port.type))
4070 continue;
4071
4072 Value resultVal = newInstance.getResult(resultNo);
4073
4074 auto oldPortResult = oldInstance.getResult(portIndex);
4075 (void)setLowering(oldPortResult, resultVal);
4076 ++resultNo;
4077 }
4078 return success();
4079}
4080
4081LogicalResult FIRRTLLowering::visitDecl(InstanceChoiceOp oldInstanceChoice) {
4082 if (oldInstanceChoice.getInnerSymAttr()) {
4083 oldInstanceChoice->emitOpError(
4084 "instance choice with inner sym cannot be lowered");
4085 return failure();
4086 }
4087
4088 // Require instance_macro to be set before lowering
4089 FlatSymbolRefAttr instanceMacro = oldInstanceChoice.getInstanceMacroAttr();
4090 if (!instanceMacro)
4091 return oldInstanceChoice->emitOpError(
4092 "must have instance_macro attribute set before "
4093 "lowering");
4094
4095 // Get all the target modules
4096 auto moduleNames = oldInstanceChoice.getModuleNamesAttr();
4097 auto caseNames = oldInstanceChoice.getCaseNamesAttr();
4098
4099 // Get the default module.
4100 auto defaultModuleName = oldInstanceChoice.getDefaultTargetAttr();
4101 auto *defaultModuleNode =
4102 circuitState.getInstanceGraph().lookup(defaultModuleName.getAttr());
4103
4104 Operation *defaultModule = defaultModuleNode->getModule();
4105
4106 // Get port information from the default module (all alternatives must have
4107 // same ports).
4108 SmallVector<PortInfo, 8> portInfo =
4109 cast<FModuleLike>(defaultModule).getPorts();
4110
4111 // Prepare input operands.
4112 SmallVector<Value, 8> inputOperands;
4113 if (failed(
4114 prepareInstanceOperands(portInfo, oldInstanceChoice, inputOperands)))
4115 return failure();
4116
4117 // Create wires for output ports.
4118 SmallVector<sv::WireOp, 8> outputWires;
4119 StringRef wirePrefix = oldInstanceChoice.getInstanceName();
4120 for (size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4121 auto &port = portInfo[portIndex];
4122 if (port.isInput())
4123 continue;
4124 auto portType = lowerType(port.type);
4125 if (!portType || portType.isInteger(0))
4126 continue;
4127 auto wire = sv::WireOp::create(
4128 builder, portType, wirePrefix.str() + "." + port.getName().str());
4129 outputWires.push_back(wire);
4130 if (failed(setLowering(oldInstanceChoice.getResult(portIndex), wire)))
4131 return failure();
4132 }
4133
4134 auto optionName = oldInstanceChoice.getOptionNameAttr();
4135
4136 // Lambda to create an instance for a given module and assign outputs to wires
4137 auto createInstanceAndAssign = [&](Operation *oldMod,
4138 StringRef suffix) -> hw::InstanceOp {
4139 auto *newMod = circuitState.getNewModule(oldMod);
4140
4141 ArrayAttr parameters;
4142 if (auto oldExtModule = dyn_cast<FExtModuleOp>(oldMod))
4143 parameters = getHWParameters(oldExtModule, /*ignoreValues=*/false);
4144
4145 // Create instance name with suffix
4146 SmallString<64> instName;
4147 instName = oldInstanceChoice.getInstanceName();
4148 if (!suffix.empty()) {
4149 instName += "_";
4150 instName += suffix;
4151 }
4152
4153 auto inst =
4154 hw::InstanceOp::create(builder, newMod, builder.getStringAttr(instName),
4155 inputOperands, parameters, nullptr);
4156 (void)getOrAddInnerSym(
4157 hw::InnerSymTarget(inst.getOperation()),
4158 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
4159
4160 // Assign instance outputs to the wires
4161 for (unsigned i = 0; i < inst.getNumResults(); ++i)
4162 sv::AssignOp::create(builder, outputWires[i], inst.getResult(i));
4163
4164 return inst;
4165 };
4166
4167 // Build macro names and module list for nested ifdefs.
4168 SmallVector<StringAttr> macroNames;
4169 SmallVector<Operation *> altModules;
4170 for (size_t i = 0, e = caseNames.size(); i < e; ++i) {
4171 altModules.push_back(
4172 circuitState.getInstanceGraph()
4173 .lookup(cast<FlatSymbolRefAttr>(moduleNames[i + 1]).getAttr())
4174 ->getModule());
4175
4176 // Get the macro name for this option case using InstanceChoiceMacroTable.
4177 auto optionCaseMacroRef = circuitState.macroTable.getMacro(
4178 optionName, cast<SymbolRefAttr>(caseNames[i]).getLeafReference());
4179 if (!optionCaseMacroRef)
4180 return oldInstanceChoice->emitOpError(
4181 "failed to get macro for option case");
4182 macroNames.push_back(optionCaseMacroRef.getAttr());
4183 }
4184
4185 // Use the helper function to create nested ifdefs.
4186 sv::createNestedIfDefs(
4187 macroNames,
4188 /*ifdefCtor=*/
4189 [&](StringRef macro, std::function<void()> thenCtor,
4190 std::function<void()> elseCtor) {
4191 addToIfDefBlock(macro, std::move(thenCtor), std::move(elseCtor));
4192 },
4193 [&](size_t index) {
4194 // Add mutual exclusion checks for all other options
4195 for (size_t i = index + 1; i < macroNames.size(); ++i) {
4196 sv::IfDefOp::create(
4197 builder, oldInstanceChoice.getLoc(), macroNames[i],
4198 [&]() {
4199 SmallString<256> errorMessage;
4200 llvm::raw_svector_ostream os(errorMessage);
4201 os << "Multiple instance choice options defined for option '"
4202 << optionName.getValue() << "': '"
4203 << macroNames[index].getValue() << "' and '"
4204 << macroNames[i].getValue() << "'";
4205 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4206 builder.getStringAttr(errorMessage));
4207 },
4208 [&]() {});
4209 }
4210
4211 auto caseSymRef =
4212 cast<SymbolRefAttr>(caseNames[index]).getLeafReference();
4213 auto inst =
4214 createInstanceAndAssign(altModules[index], caseSymRef.getValue());
4215 // Define the instance macro for this case.
4216 sv::MacroDefOp::create(builder, inst.getLoc(), instanceMacro,
4217 builder.getStringAttr("{{0}}"),
4218 builder.getArrayAttr({hw::InnerRefAttr::get(
4219 theModule.getNameAttr(),
4220 inst.getInnerSymAttr().getSymName())}));
4221 },
4222 [&]() {
4223 // Generate an error when no instance choice option is selected.
4224 SmallString<256> errorMessage;
4225 llvm::raw_svector_ostream os(errorMessage);
4226 os << "Required instance choice option '" << optionName.getValue()
4227 << "' not selected, must define one of: ";
4228 llvm::interleaveComma(macroNames, os, [&](StringAttr macro) {
4229 os << "'" << macro.getValue() << "'";
4230 });
4231 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4232 builder.getStringAttr(errorMessage));
4233 });
4234
4235 return success();
4236}
4237
4238LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
4239 SmallVector<Value> inputs;
4240 SmallVector<Type> types;
4241 for (auto input : oldOp.getInputs()) {
4242 auto lowered = getLoweredValue(input);
4243 if (!lowered)
4244 return failure();
4245 inputs.push_back(lowered);
4246 types.push_back(lowered.getType());
4247 }
4248
4249 auto newOp = verif::ContractOp::create(builder, types, inputs);
4250 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
4251 auto &body = newOp.getBody().emplaceBlock();
4252
4253 for (auto [newResult, oldResult, oldArg] :
4254 llvm::zip(newOp.getResults(), oldOp.getResults(),
4255 oldOp.getBody().getArguments())) {
4256 if (failed(setLowering(oldResult, newResult)))
4257 return failure();
4258 if (failed(setLowering(oldArg, newResult)))
4259 return failure();
4260 }
4261
4262 body.getOperations().splice(body.end(),
4263 oldOp.getBody().front().getOperations());
4264 addToWorklist(body);
4265
4266 return success();
4267}
4268
4269//===----------------------------------------------------------------------===//
4270// Unary Operations
4271//===----------------------------------------------------------------------===//
4272
4273// Lower a cast that is a noop at the HW level.
4274LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
4275 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
4276 if (!operand)
4277 return failure();
4278
4279 // Noop cast.
4280 return setLowering(op->getResult(0), operand);
4281}
4282
4283LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
4284 if (isa<ClockType>(op.getInput().getType()))
4285 return setLowering(op->getResult(0),
4286 getLoweredNonClockValue(op.getInput()));
4287 return lowerNoopCast(op);
4288}
4289
4290LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
4291 if (isa<ClockType>(op.getInput().getType()))
4292 return setLowering(op->getResult(0),
4293 getLoweredNonClockValue(op.getInput()));
4294 return lowerNoopCast(op);
4295}
4296
4297LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
4298 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
4299}
4300
4301LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
4302 mlir::UnrealizedConversionCastOp op) {
4303 // General lowering for non-unary casts.
4304 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
4305 return failure();
4306
4307 auto operand = op.getOperand(0);
4308 auto result = op.getResult(0);
4309
4310 // FIRRTL -> FIRRTL
4311 if (type_isa<FIRRTLType>(operand.getType()) &&
4312 type_isa<FIRRTLType>(result.getType()))
4313 return lowerNoopCast(op);
4314
4315 // other -> FIRRTL
4316 // other -> other
4317 if (!type_isa<FIRRTLType>(operand.getType())) {
4318 if (type_isa<FIRRTLType>(result.getType()))
4319 return setLowering(result, getPossiblyInoutLoweredValue(operand));
4320 return failure(); // general foreign op lowering for other -> other
4321 }
4322
4323 // FIRRTL -> other
4324 // Otherwise must be a conversion from FIRRTL type to standard type.
4325 auto loweredResult = getLoweredValue(operand);
4326 if (!loweredResult) {
4327 // If this is a conversion from a zero bit HW type to firrtl value, then
4328 // we want to successfully lower this to a null Value.
4329 if (operand.getType().isSignlessInteger(0)) {
4330 return setLowering(result, Value());
4331 }
4332 return failure();
4333 }
4334
4335 // We lower builtin.unrealized_conversion_cast converting from a firrtl type
4336 // to a standard type into the lowered operand.
4337 result.replaceAllUsesWith(loweredResult);
4338 return success();
4339}
4340
4341LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
4342 // Conversions from hw struct types to FIRRTL types are lowered as the
4343 // input operand.
4344 if (auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
4345 return setLowering(op, op.getOperand());
4346
4347 // Otherwise must be a conversion from FIRRTL bundle type to hw struct
4348 // type.
4349 auto result = getLoweredValue(op.getOperand());
4350 if (!result)
4351 return failure();
4352
4353 // We lower firrtl.stdStructCast converting from a firrtl bundle to an hw
4354 // struct type into the lowered operand.
4355 op.replaceAllUsesWith(result);
4356 return success();
4357}
4358
4359LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
4360 auto operand = getLoweredValue(op.getOperand());
4361 if (!operand)
4362 return failure();
4363 auto resultType = lowerType(op.getType());
4364 if (!resultType)
4365 return failure();
4366
4367 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
4368}
4369
4370LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
4371 auto operand = getLoweredValue(op.getOperand());
4372 if (!operand) {
4373 return handleZeroBit(op.getOperand(), [&]() {
4374 // Unsigned zero bit to Signed is 1b0.
4375 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
4376 return setLowering(op, getOrCreateIntConstant(1, 0));
4377 // Signed->Signed is a zero bit value.
4378 return setLowering(op, Value());
4379 });
4380 }
4381
4382 // Signed to signed is a noop.
4383 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
4384 return setLowering(op, operand);
4385
4386 // Otherwise prepend a zero bit.
4387 auto zero = getOrCreateIntConstant(1, 0);
4388 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
4389}
4390
4391LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
4392 auto operand = getLoweredValue(op.getInput());
4393 if (!operand)
4394 return failure();
4395 // ~x ---> x ^ 0xFF
4396 auto allOnes = getOrCreateIntConstant(
4397 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
4398 return setLoweringTo<comb::XorOp>(op, operand, allOnes, true);
4399}
4400
4401LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
4402 // FIRRTL negate always adds a bit.
4403 // -x ---> 0-sext(x) or 0-zext(x)
4404 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4405 if (!operand)
4406 return failure();
4407
4408 auto resultType = lowerType(op.getType());
4409
4410 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
4411 return setLoweringTo<comb::SubOp>(op, zero, operand, true);
4412}
4413
4414// Pad is a noop or extension operation.
4415LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
4416 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4417 if (!operand)
4418 return failure();
4419 return setLowering(op, operand);
4420}
4421
4422LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
4423 auto operand = getLoweredValue(op.getInput());
4424 if (!operand) {
4425 return handleZeroBit(op.getInput(), [&]() {
4426 return setLowering(op, getOrCreateIntConstant(1, 0));
4427 });
4428 return failure();
4429 }
4430
4431 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
4432 true);
4433}
4434
4435LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
4436 auto operand = getLoweredValue(op.getInput());
4437 if (!operand) {
4438 return handleZeroBit(op.getInput(), [&]() {
4439 return setLowering(op, getOrCreateIntConstant(1, 1));
4440 });
4441 }
4442
4443 // Lower AndR to == -1
4444 return setLoweringTo<comb::ICmpOp>(
4445 op, ICmpPredicate::eq, operand,
4446 getOrCreateIntConstant(
4447 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
4448 true);
4449}
4450
4451LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
4452 auto operand = getLoweredValue(op.getInput());
4453 if (!operand) {
4454 return handleZeroBit(op.getInput(), [&]() {
4455 return setLowering(op, getOrCreateIntConstant(1, 0));
4456 });
4457 return failure();
4458 }
4459
4460 // Lower OrR to != 0
4461 return setLoweringTo<comb::ICmpOp>(
4462 op, ICmpPredicate::ne, operand,
4463 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
4464 true);
4465}
4466
4467//===----------------------------------------------------------------------===//
4468// Binary Operations
4469//===----------------------------------------------------------------------===//
4470
4471template <typename ResultOpType>
4472LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
4473 auto resultType = op->getResult(0).getType();
4474 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4475 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4476 if (!lhs || !rhs)
4477 return failure();
4478
4479 return setLoweringTo<ResultOpType>(op, lhs, rhs, true);
4480}
4481
4482/// Element-wise logical operations can be lowered into bitcast and normal comb
4483/// operations. Eventually we might want to introduce elementwise operations
4484/// into HW/SV level as well.
4485template <typename ResultOpType>
4486LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
4487 auto resultType = op->getResult(0).getType();
4488 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4489 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4490
4491 if (!lhs || !rhs)
4492 return failure();
4493 auto bitwidth = firrtl::getBitWidth(type_cast<FIRRTLBaseType>(resultType));
4494
4495 if (!bitwidth)
4496 return failure();
4497
4498 // TODO: Introduce elementwise operations to HW dialect instead of abusing
4499 // bitcast operations.
4500 auto intType = builder.getIntegerType(*bitwidth);
4501 auto retType = lhs.getType();
4502 lhs = builder.createOrFold<hw::BitcastOp>(intType, lhs);
4503 rhs = builder.createOrFold<hw::BitcastOp>(intType, rhs);
4504 auto result = builder.createOrFold<ResultOpType>(lhs, rhs, /*twoState=*/true);
4505 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4506}
4507
4508/// lowerBinOp extends each operand to the destination type, then performs the
4509/// specified binary operator.
4510template <typename ResultUnsignedOpType, typename ResultSignedOpType>
4511LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4512 // Extend the two operands to match the destination type.
4513 auto resultType = op->getResult(0).getType();
4514 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4515 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4516 if (!lhs || !rhs)
4517 return failure();
4518
4519 // Emit the result operation.
4520 if (type_cast<IntType>(resultType).isSigned())
4521 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs, true);
4522 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs, true);
4523}
4524
4525/// lowerCmpOp extends each operand to the longest type, then performs the
4526/// specified binary operator.
4527LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4528 ICmpPredicate unsignedOp) {
4529 // Extend the two operands to match the longest type.
4530 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4531 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4532 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4533 return failure();
4534
4535 auto cmpType = getWidestIntType(lhsIntType, rhsIntType);
4536 if (cmpType.getWidth() == 0) // Handle 0-width inputs by promoting to 1 bit.
4537 cmpType = UIntType::get(builder.getContext(), 1);
4538 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4539 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4540 if (!lhs || !rhs)
4541 return failure();
4542
4543 // Emit the result operation.
4544 Type resultType = builder.getIntegerType(1);
4545 return setLoweringTo<comb::ICmpOp>(
4546 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4547 true);
4548}
4549
4550/// Lower a divide or dynamic shift, where the operation has to be performed
4551/// in the widest type of the result and two inputs then truncated down.
4552template <typename SignedOp, typename UnsignedOp>
4553LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4554 // hw has equal types for these, firrtl doesn't. The type of the firrtl
4555 // RHS may be wider than the LHS, and we cannot truncate off the high bits
4556 // (because an overlarge amount is supposed to shift in sign or zero bits).
4557 auto opType = type_cast<IntType>(op->getResult(0).getType());
4558 if (opType.getWidth() == 0)
4559 return setLowering(op->getResult(0), Value());
4560
4561 auto resultType = getWidestIntType(opType, op->getOperand(1).getType());
4562 resultType = getWidestIntType(resultType, op->getOperand(0).getType());
4563 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4564 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4565 if (!lhs || !rhs)
4566 return failure();
4567
4568 Value result;
4569 if (opType.isSigned())
4570 result = builder.createOrFold<SignedOp>(lhs, rhs, true);
4571 else
4572 result = builder.createOrFold<UnsignedOp>(lhs, rhs, true);
4573
4574 if (auto *definingOp = result.getDefiningOp())
4575 tryCopyName(definingOp, op);
4576
4577 if (resultType == opType)
4578 return setLowering(op->getResult(0), result);
4579 return setLoweringTo<comb::ExtractOp>(op, lowerType(opType), result, 0);
4580}
4581
4582LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4583 // Handle the case of no operands - should result in a 0-bit value
4584 if (op.getInputs().empty())
4585 return setLowering(op, Value());
4586
4587 SmallVector<Value> loweredOperands;
4588
4589 // Lower all operands, filtering out zero-bit values
4590 for (auto operand : op.getInputs()) {
4591 auto loweredOperand = getLoweredValue(operand);
4592 if (loweredOperand) {
4593 loweredOperands.push_back(loweredOperand);
4594 } else {
4595 // Check if this is a zero-bit operand, which we can skip
4596 auto result = handleZeroBit(operand, [&]() { return success(); });
4597 if (failed(result))
4598 return failure();
4599 // Zero-bit operands are skipped (not added to loweredOperands)
4600 }
4601 }
4602
4603 // If no non-zero operands, return 0-bit value
4604 if (loweredOperands.empty())
4605 return setLowering(op, Value());
4606
4607 // Use comb.concat
4608 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4609}
4610
4611//===----------------------------------------------------------------------===//
4612// Verif Operations
4613//===----------------------------------------------------------------------===//
4614
4615LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4616 auto input = getLoweredNonClockValue(op.getArg());
4617 if (!input)
4618 return failure();
4619
4620 if (!isa<IntType>(input.getType())) {
4621 auto srcType = op.getArg().getType();
4622 auto bitwidth = firrtl::getBitWidth(type_cast<FIRRTLBaseType>(srcType));
4623 assert(bitwidth && "Unknown width");
4624 auto intType = builder.getIntegerType(*bitwidth);
4625 input = builder.createOrFold<hw::BitcastOp>(intType, input);
4626 }
4627
4628 return setLoweringTo<comb::ICmpOp>(
4629 op, ICmpPredicate::ceq, input,
4630 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()), true);
4631}
4632
4633LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4634 auto operand = getLoweredValue(op.getInput());
4635 hw::WireOp::create(builder, operand);
4636 return success();
4637}
4638
4639LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4640 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4641 op.getFormatStringAttr());
4642}
4643
4644LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4645 auto type = lowerType(op.getResult().getType());
4646 if (!type)
4647 return failure();
4648
4649 auto valueOp = sim::PlusArgsValueOp::create(
4650 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4651 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4652 return failure();
4653 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4654 return failure();
4655 return success();
4656}
4657
4658LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4659 op.emitError("SizeOf should have been resolved.");
4660 return failure();
4661}
4662
4663LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4664 Value testEnable;
4665 if (op.getTestEnable())
4666 testEnable = getLoweredValue(op.getTestEnable());
4667 return setLoweringTo<seq::ClockGateOp>(
4668 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4669 testEnable, /*inner_sym=*/hw::InnerSymAttr{});
4670}
4671
4672LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4673 auto operand = getLoweredValue(op.getInput());
4674 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4675}
4676
4677LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4678 auto operand = getLoweredValue(op.getInput());
4679 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4680}
4681
4682LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4683 return setLoweringToLTL<ltl::AndOp>(
4684 op,
4685 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4686}
4687
4688LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4689 return setLoweringToLTL<ltl::OrOp>(
4690 op,
4691 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4692}
4693
4694LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4695 return setLoweringToLTL<ltl::IntersectOp>(
4696 op,
4697 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4698}
4699
4700LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4701 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4702 op.getDelayAttr(), op.getLengthAttr());
4703}
4704
4705LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4706 return setLoweringToLTL<ltl::ConcatOp>(
4707 op,
4708 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4709}
4710
4711LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4712 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4713 op.getBaseAttr(), op.getMoreAttr());
4714}
4715
4716LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4717 return setLoweringToLTL<ltl::GoToRepeatOp>(
4718 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4719}
4720
4721LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4722 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4723 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4724}
4725
4726LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4727 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4728}
4729
4730LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4731 return setLoweringToLTL<ltl::ImplicationOp>(
4732 op,
4733 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4734}
4735
4736LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4737 return setLoweringToLTL<ltl::UntilOp>(
4738 op,
4739 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4740}
4741
4742LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4743 return setLoweringToLTL<ltl::EventuallyOp>(op,
4744 getLoweredValue(op.getInput()));
4745}
4746
4747LogicalResult FIRRTLLowering::visitExpr(LTLPastIntrinsicOp op) {
4748 Value clk = getLoweredNonClockValue(op.getClock());
4749 return setLoweringToLTL<ltl::PastOp>(op, getLoweredValue(op.getInput()),
4750 op.getDelayAttr(), clk);
4751}
4752
4753LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4754 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4755 ltl::ClockEdge::Pos,
4756 getLoweredNonClockValue(op.getClock()));
4757}
4758
4759template <typename TargetOp, typename IntrinsicOp>
4760LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4761 auto property = getLoweredValue(op.getProperty());
4762 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4763 TargetOp::create(builder, property, enable, op.getLabelAttr());
4764 return success();
4765}
4766
4767LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4768 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4769}
4770
4771LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4772 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4773}
4774
4775LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4776 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4777}
4778
4779LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4780 if (!isa<verif::ContractOp>(op->getParentOp()))
4781 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4782 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4783}
4784
4785LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4786 if (!isa<verif::ContractOp>(op->getParentOp()))
4787 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4788 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4789}
4790
4791LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4792 auto clock = getLoweredNonClockValue(op.getClock());
4793 auto reset = getLoweredValue(op.getReset());
4794 if (!clock || !reset)
4795 return failure();
4796 auto resetType = op.getReset().getType();
4797 auto uintResetType = dyn_cast<UIntType>(resetType);
4798 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4799 auto isAsync = isa<AsyncResetType>(resetType);
4800 if (!isAsync && !isSync) {
4801 auto d = op.emitError("uninferred reset passed to 'has_been_reset'; "
4802 "requires sync or async reset");
4803 d.attachNote() << "reset is of type " << resetType
4804 << ", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4805 return failure();
4806 }
4807 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4808}
4809
4810//===----------------------------------------------------------------------===//
4811// Other Operations
4812//===----------------------------------------------------------------------===//
4813
4814LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4815 auto input = getLoweredValue(op.getInput());
4816 if (!input)
4817 return failure();
4818
4819 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4820 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4821}
4822
4823LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4824 auto resultTy = lowerType(op.getType());
4825 if (!resultTy)
4826 return failure();
4827
4828 // Values of analog type always need to be lowered to something with inout
4829 // type. We do that by lowering to a wire and return that. As with the
4830 // SFC, we do not connect anything to this, because it is bidirectional.
4831 if (type_isa<AnalogType>(op.getType()))
4832 // This is a locally visible, private wire created by the compiler, so do
4833 // not attach a symbol name.
4834 return setLoweringTo<sv::WireOp>(op, resultTy, ".invalid_analog");
4835
4836 // We don't allow aggregate values which contain values of analog types.
4837 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4838 return failure();
4839
4840 // We lower invalid to 0. TODO: the FIRRTL spec mentions something about
4841 // lowering it to a random value, we should see if this is what we need to
4842 // do.
4843 if (auto bitwidth =
4844 firrtl::getBitWidth(type_cast<FIRRTLBaseType>(op.getType()))) {
4845 if (*bitwidth == 0) // Let the caller handle zero width values.
4846 return failure();
4847
4848 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4849 // If the result is an aggregate value, we have to bitcast the constant.
4850 if (!type_isa<IntegerType>(resultTy))
4851 constant = hw::BitcastOp::create(builder, resultTy, constant);
4852 return setLowering(op, constant);
4853 }
4854
4855 // Invalid for bundles isn't supported.
4856 op.emitOpError("unsupported type");
4857 return failure();
4858}
4859
4860LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4861 auto input = getLoweredValue(op.getInput());
4862 if (!input)
4863 return failure();
4864 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4865 if (op.getAmount() == 0)
4866 return setLowering(op, Value());
4867 Type resultType = builder.getIntegerType(op.getAmount());
4868 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4869 inWidth - op.getAmount());
4870}
4871
4872LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4873 auto input = getLoweredValue(op.getInput());
4874 if (!input) {
4875 return handleZeroBit(op.getInput(), [&]() {
4876 if (op.getAmount() == 0)
4877 return failure();
4878 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4879 });
4880 }
4881
4882 // Handle the degenerate case.
4883 if (op.getAmount() == 0)
4884 return setLowering(op, input);
4885
4886 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4887 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4888}
4889
4890LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4891 auto input = getLoweredValue(op.getInput());
4892 if (!input)
4893 return failure();
4894
4895 // Handle the special degenerate cases.
4896 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4897 auto shiftAmount = op.getAmount();
4898 if (shiftAmount >= inWidth) {
4899 // Unsigned shift by full width returns a single-bit zero.
4900 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4901 return setLowering(op, {});
4902
4903 // Signed shift by full width is equivalent to extracting the sign bit.
4904 shiftAmount = inWidth - 1;
4905 }
4906
4907 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4908 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4909}
4910
4911LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4912 auto input = getLoweredValue(op.getInput());
4913 if (!input)
4914 return failure();
4915
4916 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4917 if (inWidth == op.getAmount())
4918 return setLowering(op, Value());
4919 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4920 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4921}
4922
4923LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4924 auto cond = getLoweredValue(op.getSel());
4925 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4926 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4927 if (!cond || !ifTrue || !ifFalse)
4928 return failure();
4929
4930 if (isa<ClockType>(op.getType()))
4931 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4932 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4933 true);
4934}
4935
4936LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4937 auto cond = getLoweredValue(op.getSel());
4938 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4939 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4940 if (!cond || !ifTrue || !ifFalse)
4941 return failure();
4942
4943 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
4944 ifFalse, true);
4945 return setLowering(op, createValueWithMuxAnnotation(val, true));
4946}
4947
4948LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4949 auto sel = getLoweredValue(op.getSel());
4950 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4951 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4952 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4953 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4954 if (!sel || !v3 || !v2 || !v1 || !v0)
4955 return failure();
4956 Value array[] = {v3, v2, v1, v0};
4957 auto create = hw::ArrayCreateOp::create(builder, array);
4958 auto val = hw::ArrayGetOp::create(builder, create, sel);
4959 return setLowering(op, createValueWithMuxAnnotation(val, false));
4960}
4961
4962// Construct a value with vendor specific pragmas to utilize MUX cells.
4963// Specifically we annotate pragmas in the following form.
4964//
4965// For an array indexing:
4966// ```
4967// wire GEN;
4968// /* synopsys infer_mux_override */
4969// assign GEN = array[index] /* cadence map_to_mux */;
4970// ```
4971//
4972// For a mux:
4973// ```
4974// wire GEN;
4975// /* synopsys infer_mux_override */
4976// assign GEN = sel ? /* cadence map_to_mux */ high : low;
4977// ```
4978Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op, bool isMux2) {
4979 assert(op->getNumResults() == 1 && "only expect a single result");
4980 auto val = op->getResult(0);
4981 auto valWire = sv::WireOp::create(builder, val.getType());
4982 // Use SV attributes to annotate pragmas.
4984 op, sv::SVAttributeAttr::get(builder.getContext(), "cadence map_to_mux",
4985 /*emitAsComment=*/true));
4986
4987 // For operands, create temporary wires with optimization blockers(inner
4988 // symbols) so that the AST structure will never be destoyed in the later
4989 // pipeline.
4990 {
4991 OpBuilder::InsertionGuard guard(builder);
4992 builder.setInsertionPoint(op);
4993 StringRef namehint = isMux2 ? "mux2cell_in" : "mux4cell_in";
4994 for (auto [idx, operand] : llvm::enumerate(op->getOperands())) {
4995 auto [innerSym, _] = getOrAddInnerSym(
4996 op->getContext(), /*attr=*/nullptr, 0,
4997 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
4998 auto wire =
4999 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
5000 op->setOperand(idx, wire);
5001 }
5002 }
5003
5004 auto assignOp = sv::AssignOp::create(builder, valWire, val);
5005 sv::setSVAttributes(assignOp,
5006 sv::SVAttributeAttr::get(builder.getContext(),
5007 "synopsys infer_mux_override",
5008 /*emitAsComment=*/true));
5009 return sv::ReadInOutOp::create(builder, valWire);
5010}
5011
5012Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
5013
5014 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
5015 // Extend to power of 2. FIRRTL semantics say out-of-bounds access result in
5016 // an indeterminate value. Existing chisel code depends on this behavior
5017 // being "return index 0". Ideally, we would tail extend the array to improve
5018 // optimization.
5019 if (!llvm::isPowerOf2_64(size)) {
5020 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
5021 auto extValue = hw::ArrayGetOp::create(builder, array, extElem);
5022 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
5023 auto ext = hw::ArrayCreateOp::create(builder, temp);
5024 Value temp2[] = {ext.getResult(), array};
5025 array = hw::ArrayConcatOp::create(builder, temp2);
5026 }
5027
5028 Value inBoundsRead = hw::ArrayGetOp::create(builder, array, index);
5029
5030 return inBoundsRead;
5031}
5032
5033LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
5034 // Lower and resize to the index width.
5035 auto index = getLoweredAndExtOrTruncValue(
5036 op.getIndex(),
5037 UIntType::get(op.getContext(),
5038 getBitWidthFromVectorSize(op.getInputs().size())));
5039
5040 if (!index)
5041 return failure();
5042 SmallVector<Value> loweredInputs;
5043 loweredInputs.reserve(op.getInputs().size());
5044 for (auto input : op.getInputs()) {
5045 auto lowered = getLoweredAndExtendedValue(input, op.getType());
5046 if (!lowered)
5047 return failure();
5048 loweredInputs.push_back(lowered);
5049 }
5050
5051 Value array = hw::ArrayCreateOp::create(builder, loweredInputs);
5052 return setLowering(op, createArrayIndexing(array, index));
5053}
5054
5055LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
5056 auto resultTy = lowerType(op.getType());
5057 if (!resultTy)
5058 return failure();
5059
5060 SmallVector<Value, 4> operands;
5061 operands.reserve(op.getSubstitutions().size());
5062 for (auto operand : op.getSubstitutions()) {
5063 auto lowered = getLoweredValue(operand);
5064 if (!lowered)
5065 return failure();
5066 operands.push_back(lowered);
5067 }
5068
5069 ArrayAttr symbols = op.getSymbolsAttr();
5070 if (!symbols)
5071 symbols = ArrayAttr::get(op.getContext(), {});
5072
5073 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
5074 operands, symbols);
5075}
5076
5077LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
5078 // This XMR is accessed solely by FIRRTL statements that mutate the probe.
5079 // To avoid the use of clock wires, create an `i1` wire and ensure that
5080 // all connections are also of the `i1` type.
5081 Type baseType = op.getType().getType();
5082
5083 Type xmrType;
5084 if (isa<ClockType>(baseType))
5085 xmrType = builder.getIntegerType(1);
5086 else
5087 xmrType = lowerType(baseType);
5088
5089 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
5090 op.getRef(), op.getVerbatimSuffixAttr());
5091}
5092
5093LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
5094 // When an XMR targets a clock wire, replace it with an `i1` wire, but
5095 // introduce a clock-typed read op into the design afterwards.
5096 Type xmrType;
5097 if (isa<ClockType>(op.getType()))
5098 xmrType = builder.getIntegerType(1);
5099 else
5100 xmrType = lowerType(op.getType());
5101
5102 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
5103 op.getRef(), op.getVerbatimSuffixAttr());
5104 auto readXmr = getReadValue(xmr);
5105 if (!isa<ClockType>(op.getType()))
5106 return setLowering(op, readXmr);
5107 return setLoweringTo<seq::ToClockOp>(op, readXmr);
5108}
5109
5110// Do nothing when lowering fstring operations. These need to be handled at
5111// their usage sites (at the PrintfOps).
5112LogicalResult FIRRTLLowering::visitExpr(TimeOp op) { return success(); }
5113LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
5114 return success();
5115}
5116
5117//===----------------------------------------------------------------------===//
5118// Statements
5119//===----------------------------------------------------------------------===//
5120
5121LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
5122 // Nothing! We could emit an comment as a verbatim op if there were a
5123 // reason to.
5124 return success();
5125}
5126
5127/// Resolve a connection to `destVal`, an `hw::WireOp` or `seq::FirRegOp`, by
5128/// updating the input operand to be `srcVal`. Returns true if the update was
5129/// made and the connection can be considered lowered. Returns false if the
5130/// destination isn't a wire or register with an input operand to be updated.
5131/// Returns failure if the destination is a subaccess operation. These should be
5132/// transposed to the right-hand-side by a pre-pass.
5133FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
5134 auto srcType = srcVal.getType();
5135 auto dstType = destVal.getType();
5136 if (srcType != dstType &&
5137 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
5138 srcVal = hw::BitcastOp::create(builder, destVal.getType(), srcVal);
5139 }
5140 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
5141 .Case<hw::WireOp>([&](auto op) {
5142 maybeUnused(op.getInput());
5143 op.getInputMutable().assign(srcVal);
5144 return true;
5145 })
5146 .Case<seq::FirRegOp>([&](auto op) {
5147 maybeUnused(op.getNext());
5148 op.getNextMutable().assign(srcVal);
5149 return true;
5150 })
5151 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](auto op) {
5152 // NOTE: msvc thinks `return op.emitOpError(...);` is ambiguous. So
5153 // return `failure()` separately.
5154 op.emitOpError("used as connect destination");
5155 return failure();
5156 })
5157 .Default([](auto) { return false; });
5158}
5159
5160LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
5161 auto dest = op.getDest();
5162 // The source can be a smaller integer, extend it as appropriate if so.
5163 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
5164 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
5165 if (!srcVal)
5166 return handleZeroBit(op.getSrc(), []() { return success(); });
5167
5168 auto destVal = getPossiblyInoutLoweredValue(dest);
5169 if (!destVal)
5170 return failure();
5171
5172 auto result = lowerConnect(destVal, srcVal);
5173 if (failed(result))
5174 return failure();
5175 if (*result)
5176 return success();
5177
5178 // If this connect is driving a value that is currently a backedge, record
5179 // that the source is the value of the backedge.
5180 if (updateIfBackedge(destVal, srcVal))
5181 return success();
5182
5183 if (!isa<hw::InOutType>(destVal.getType()))
5184 return op.emitError("destination isn't an inout type");
5185
5186 sv::AssignOp::create(builder, destVal, srcVal);
5187 return success();
5188}
5189
5190LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
5191 auto dest = op.getDest();
5192 auto srcVal = getLoweredValue(op.getSrc());
5193 if (!srcVal)
5194 return handleZeroBit(op.getSrc(), []() { return success(); });
5195
5196 auto destVal = getPossiblyInoutLoweredValue(dest);
5197 if (!destVal)
5198 return failure();
5199
5200 auto result = lowerConnect(destVal, srcVal);
5201 if (failed(result))
5202 return failure();
5203 if (*result)
5204 return success();
5205
5206 // If this connect is driving a value that is currently a backedge, record
5207 // that the source is the value of the backedge.
5208 if (updateIfBackedge(destVal, srcVal))
5209 return success();
5210
5211 if (!isa<hw::InOutType>(destVal.getType()))
5212 return op.emitError("destination isn't an inout type");
5213
5214 sv::AssignOp::create(builder, destVal, srcVal);
5215 return success();
5216}
5217
5218LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
5219 if (circuitState.lowerToCore)
5220 return op.emitOpError("lower-to-core does not support firrtl.force");
5221
5222 auto srcVal = getLoweredValue(op.getSrc());
5223 if (!srcVal)
5224 return failure();
5225
5226 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5227 if (!destVal)
5228 return failure();
5229
5230 if (!isa<hw::InOutType>(destVal.getType()))
5231 return op.emitError("destination isn't an inout type");
5232
5233 // #ifndef SYNTHESIS
5234 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
5235 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
5236 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
5237 });
5238 return success();
5239}
5240
5241LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
5242 if (circuitState.lowerToCore)
5243 return op.emitOpError("lower-to-core does not support firrtl.ref.force");
5244
5245 auto src = getLoweredNonClockValue(op.getSrc());
5246 auto clock = getLoweredNonClockValue(op.getClock());
5247 auto pred = getLoweredValue(op.getPredicate());
5248 if (!src || !clock || !pred)
5249 return failure();
5250
5251 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5252 if (!destVal)
5253 return failure();
5254
5255 // #ifndef SYNTHESIS
5256 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
5257 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
5258 addToAlwaysBlock(clock, [&]() {
5259 addIfProceduralBlock(
5260 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5261 });
5262 });
5263 return success();
5264}
5265LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
5266 if (circuitState.lowerToCore)
5267 return op.emitOpError(
5268 "lower-to-core does not support firrtl.ref.force_initial");
5269
5270 auto src = getLoweredNonClockValue(op.getSrc());
5271 auto pred = getLoweredValue(op.getPredicate());
5272 if (!src || !pred)
5273 return failure();
5274
5275 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5276 if (!destVal)
5277 return failure();
5278
5279 // #ifndef SYNTHESIS
5280 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
5281 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
5282 addToInitialBlock([&]() {
5283 addIfProceduralBlock(
5284 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5285 });
5286 });
5287 return success();
5288}
5289LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
5290 if (circuitState.lowerToCore)
5291 return op.emitOpError("lower-to-core does not support firrtl.ref.release");
5292
5293 auto clock = getLoweredNonClockValue(op.getClock());
5294 auto pred = getLoweredValue(op.getPredicate());
5295 if (!clock || !pred)
5296 return failure();
5297
5298 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5299 if (!destVal)
5300 return failure();
5301
5302 // #ifndef SYNTHESIS
5303 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
5304 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
5305 addToAlwaysBlock(clock, [&]() {
5306 addIfProceduralBlock(pred,
5307 [&]() { sv::ReleaseOp::create(builder, destVal); });
5308 });
5309 });
5310 return success();
5311}
5312LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5313 if (circuitState.lowerToCore)
5314 return op.emitOpError(
5315 "lower-to-core does not support firrtl.ref.release_initial");
5316
5317 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5318 auto pred = getLoweredValue(op.getPredicate());
5319 if (!destVal || !pred)
5320 return failure();
5321
5322 // #ifndef SYNTHESIS
5323 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
5324 addToIfDefBlock("SYNTHESIS", std::function<void()>(), [&]() {
5325 addToInitialBlock([&]() {
5326 addIfProceduralBlock(pred,
5327 [&]() { sv::ReleaseOp::create(builder, destVal); });
5328 });
5329 });
5330 return success();
5331}
5332
5333// Replace FIRRTL "special" substitutions {{..}} with verilog equivalents.
5334static LogicalResult resolveFormatString(Location loc,
5335 StringRef originalFormatString,
5336 ValueRange operands,
5337 StringAttr &result) {
5338 // Update the format string to replace "special" substitutions based on
5339 // substitution type and lower normal substitusion.
5340 SmallString<32> formatString;
5341 for (size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5342 char c = originalFormatString[i];
5343 switch (c) {
5344 // Maybe a "%?" normal substitution.
5345 case '%': {
5346 formatString.push_back(c);
5347
5348 // Parse the width specifier.
5349 SmallString<6> width;
5350 c = originalFormatString[++i];
5351 while (isdigit(c)) {
5352 width.push_back(c);
5353 c = originalFormatString[++i];
5354 }
5355
5356 // Parse the radix.
5357 switch (c) {
5358 // A normal substitution. If this is a radix specifier, include the width
5359 // if one exists.
5360 case 'b':
5361 case 'd':
5362 case 'x':
5363 if (!width.empty())
5364 formatString.append(width);
5365 [[fallthrough]];
5366 case 'c':
5367 ++subIdx;
5368 [[fallthrough]];
5369 default:
5370 formatString.push_back(c);
5371 }
5372 break;
5373 }
5374 // Maybe a "{{}}" special substitution.
5375 case '{': {
5376 // Not a special substituion.
5377 if (originalFormatString.slice(i, i + 4) != "{{}}") {
5378 formatString.push_back(c);
5379 break;
5380 }
5381 // Special substitution. Look at the defining op to know how to lower it.
5382 auto substitution = operands[subIdx++];
5383 assert(type_isa<FStringType>(substitution.getType()) &&
5384 "the operand for a '{{}}' substitution must be an 'fstring' type");
5385 auto result =
5386 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5387 .template Case<TimeOp>([&](auto) {
5388 formatString.append("%0t");
5389 return success();
5390 })
5391 .template Case<HierarchicalModuleNameOp>([&](auto) {
5392 formatString.append("%m");
5393 return success();
5394 })
5395 .Default([&](auto) {
5396 emitError(loc, "has a substitution with an unimplemented "
5397 "lowering")
5398 .attachNote(substitution.getLoc())
5399 << "op with an unimplemented lowering is here";
5400 return failure();
5401 });
5402 if (failed(result))
5403 return failure();
5404 i += 3;
5405 break;
5406 }
5407 // Default is to let characters through.
5408 default:
5409 formatString.push_back(c);
5410 }
5411 }
5412
5413 result = StringAttr::get(loc->getContext(), formatString);
5414 return success();
5415}
5416
5417// Printf/FPrintf is a macro op that lowers to an sv.ifdef.procedural, an sv.if,
5418// and an sv.fwrite all nested together.
5419template <class T>
5420LogicalResult FIRRTLLowering::visitPrintfLike(
5421 T op, const FileDescriptorInfo &fileDescriptorInfo, bool usePrintfCond) {
5422 auto clock = getLoweredNonClockValue(op.getClock());
5423 auto cond = getLoweredValue(op.getCond());
5424 if (!clock || !cond)
5425 return failure();
5426
5427 StringAttr formatString;
5428 if (failed(resolveFormatString(op.getLoc(), op.getFormatString(),
5429 op.getSubstitutions(), formatString)))
5430 return failure();
5431
5432 auto fn = [&](Value fd) {
5433 SmallVector<Value> operands;
5434 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5435 return failure();
5436 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5437 return success();
5438 };
5439
5440 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5441 usePrintfCond);
5442}
5443
5444LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
5445 if (!circuitState.lowerToCore)
5446 return visitPrintfLike(op, {}, true);
5447
5448 auto clock = getLoweredValue(op.getClock());
5449 auto cond = getLoweredValue(op.getCond());
5450 if (!clock || !cond)
5451 return failure();
5452
5453 auto formatString =
5454 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5455 if (failed(formatString))
5456 return failure();
5457
5458 sim::TriggeredOp::create(builder, clock, cond, [&] {
5459 sim::PrintFormattedProcOp::create(builder, *formatString);
5460 });
5461 return success();
5462}
5463
5464LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5465 if (circuitState.lowerToCore)
5466 return op.emitOpError("lower-to-core does not support firrtl.fprintf yet");
5467
5468 StringAttr outputFileAttr;
5469 if (failed(resolveFormatString(op.getLoc(), op.getOutputFileAttr(),
5470 op.getOutputFileSubstitutions(),
5471 outputFileAttr)))
5472 return failure();
5473
5474 FileDescriptorInfo outputFile(outputFileAttr,
5475 op.getOutputFileSubstitutions());
5476 return visitPrintfLike(op, outputFile, false);
5477}
5478
5479// FFlush lowers into $fflush statement.
5480LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5481 if (circuitState.lowerToCore)
5482 return op.emitOpError("lower-to-core does not support firrtl.fflush yet");
5483
5484 auto clock = getLoweredNonClockValue(op.getClock());
5485 auto cond = getLoweredValue(op.getCond());
5486 if (!clock || !cond)
5487 return failure();
5488
5489 auto fn = [&](Value fd) {
5490 sv::FFlushOp::create(builder, op.getLoc(), fd);
5491 return success();
5492 };
5493
5494 if (!op.getOutputFileAttr())
5495 return lowerStatementWithFd({}, clock, cond, fn, false);
5496
5497 // If output file is specified, resolve the format string and lower it with a
5498 // file descriptor associated with the output file.
5499 StringAttr outputFileAttr;
5500 if (failed(resolveFormatString(op.getLoc(), op.getOutputFileAttr(),
5501 op.getOutputFileSubstitutions(),
5502 outputFileAttr)))
5503 return failure();
5504
5505 return lowerStatementWithFd(
5506 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5507 clock, cond, fn, false);
5508}
5509
5510// Stop lowers into a nested series of behavioral statements plus $fatal
5511// or $finish.
5512LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5513 auto clock = getLoweredValue(op.getClock());
5514 auto cond = getLoweredValue(op.getCond());
5515 if (!clock || !cond)
5516 return failure();
5517
5518 circuitState.usedStopCond = true;
5519 circuitState.addFragment(theModule, "STOP_COND_FRAGMENT");
5520
5521 Value stopCond =
5522 sv::MacroRefExprOp::create(builder, cond.getType(), "STOP_COND_");
5523 Value exitCond = builder.createOrFold<comb::AndOp>(stopCond, cond, true);
5524
5525 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5526 /*success=*/op.getExitCode() == 0,
5527 /*verbose=*/true);
5528
5529 return success();
5530}
5531
5532/// Helper function to build an immediate assert operation based on the
5533/// original FIRRTL operation name. This reduces code duplication in
5534/// `lowerVerificationStatement`.
5535template <typename... Args>
5536static Operation *buildImmediateVerifOp(ImplicitLocOpBuilder &builder,
5537 StringRef opName, Args &&...args) {
5538 if (opName == "assert")
5539 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5540 if (opName == "assume")
5541 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5542 if (opName == "cover")
5543 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5544 llvm_unreachable("unknown verification op");
5545}
5546
5547/// Helper function to build a concurrent assert operation based on the
5548/// original FIRRTL operation name. This reduces code duplication in
5549/// `lowerVerificationStatement`.
5550template <typename... Args>
5551static Operation *buildConcurrentVerifOp(ImplicitLocOpBuilder &builder,
5552 StringRef opName, Args &&...args) {
5553 if (opName == "assert")
5554 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5555 if (opName == "assume")
5556 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5557 if (opName == "cover")
5558 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5559 llvm_unreachable("unknown verification op");
5560}
5561
5562static verif::ClockEdge firrtlToVerifClockEdge(EventControl eventControl) {
5563 switch (eventControl) {
5564 case EventControl::AtPosEdge:
5565 return verif::ClockEdge::Pos;
5566 case EventControl::AtEdge:
5567 return verif::ClockEdge::Both;
5568 case EventControl::AtNegEdge:
5569 return verif::ClockEdge::Neg;
5570 }
5571 llvm_unreachable("unknown FIRRTL event control");
5572}
5573
5574LogicalResult FIRRTLLowering::lowerVerificationStatementToCore(
5575 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5576 Value opEnable, StringAttr opNameAttr, EventControl opEventControl) {
5577 auto guardsAttr = op->getAttrOfType<ArrayAttr>("guards");
5578 if (guardsAttr && !guardsAttr.empty())
5579 return op->emitOpError(
5580 "lower-to-core does not support guarded verification statements");
5581
5582 auto clock = getLoweredNonClockValue(opClock);
5583 auto enable = getLoweredValue(opEnable);
5584 auto predicate = getLoweredValue(opPredicate);
5585 if (!clock || !enable || !predicate)
5586 return failure();
5587
5588 StringAttr label;
5589 if (opNameAttr && !opNameAttr.getValue().empty())
5590 label = StringAttr::get(builder.getContext(),
5591 labelPrefix + opNameAttr.getValue());
5592
5593 auto edge = firrtlToVerifClockEdge(opEventControl);
5594 auto opName = op->getName().stripDialect();
5595 if (opName == "assert") {
5596 verif::ClockedAssertOp::create(builder, predicate, edge, clock, enable,
5597 label);
5598 return success();
5599 }
5600 if (opName == "assume") {
5601 verif::ClockedAssumeOp::create(builder, predicate, edge, clock, enable,
5602 label);
5603 return success();
5604 }
5605 if (opName == "cover") {
5606 verif::ClockedCoverOp::create(builder, predicate, edge, clock, enable,
5607 label);
5608 return success();
5609 }
5610 llvm_unreachable("unknown verification op");
5611}
5612
5613/// Template for lowering verification statements from type A to
5614/// type B.
5615///
5616/// For example, lowering the "foo" op to the "bar" op would start
5617/// with:
5618///
5619/// foo(clock, condition, enable, "message")
5620///
5621/// This becomes a Verilog clocking block with the "bar" op guarded
5622/// by an if enable:
5623///
5624/// always @(posedge clock) begin
5625/// if (enable) begin
5626/// bar(condition);
5627/// end
5628/// end
5629/// The above can also be reduced into a concurrent verification statement
5630/// sv.assert.concurrent posedge %clock (condition && enable)
5631LogicalResult FIRRTLLowering::lowerVerificationStatement(
5632 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5633 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5634 StringAttr opNameAttr, bool isConcurrent, EventControl opEventControl) {
5635 if (circuitState.lowerToCore)
5636 return lowerVerificationStatementToCore(op, labelPrefix, opClock,
5637 opPredicate, opEnable, opNameAttr,
5638 opEventControl);
5639
5640 StringRef opName = op->getName().stripDialect();
5641
5642 // The attribute holding the compile guards
5643 ArrayRef<Attribute> guards{};
5644 if (auto guardsAttr = op->template getAttrOfType<ArrayAttr>("guards"))
5645 guards = guardsAttr.getValue();
5646
5647 auto isCover = isa<CoverOp>(op);
5648 auto clock = getLoweredNonClockValue(opClock);
5649 auto enable = getLoweredValue(opEnable);
5650 auto predicate = getLoweredValue(opPredicate);
5651 if (!clock || !enable || !predicate)
5652 return failure();
5653
5654 StringAttr label;
5655 if (opNameAttr && !opNameAttr.getValue().empty())
5656 label = opNameAttr;
5657 StringAttr prefixedLabel;
5658 if (label)
5659 prefixedLabel =
5660 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5661
5662 StringAttr message;
5663 SmallVector<Value> messageOps;
5664 VerificationFlavor flavor = circuitState.verificationFlavor;
5665
5666 // For non-assertion, rollback to per-op configuration.
5667 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5668 flavor = VerificationFlavor::None;
5669
5670 if (flavor == VerificationFlavor::None) {
5671 // TODO: This should *not* be part of the op, but rather a lowering
5672 // option that the user of this pass can choose.
5673
5674 auto format = op->getAttrOfType<StringAttr>("format");
5675 // if-else-fatal iff concurrent and the format is specified.
5676 if (isConcurrent && format && format.getValue() == "ifElseFatal") {
5677 if (!isa<AssertOp>(op))
5678 return op->emitError()
5679 << "ifElseFatal format cannot be used for non-assertions";
5680 flavor = VerificationFlavor::IfElseFatal;
5681 } else if (isConcurrent)
5682 flavor = VerificationFlavor::SVA;
5683 else
5684 flavor = VerificationFlavor::Immediate;
5685 }
5686
5687 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5688 // Resolve format string to handle special substitutions like
5689 // {{HierarchicalModuleName}} which should be replaced with %m.
5690 if (failed(resolveFormatString(op->getLoc(), opMessageAttr.getValue(),
5691 opOperands, message)))
5692 return failure();
5693
5694 if (failed(loweredFmtOperands(opOperands, messageOps)))
5695 return failure();
5696
5697 if (flavor == VerificationFlavor::SVA) {
5698 // For SVA assert/assume statements, wrap any message ops in $sampled() to
5699 // guarantee that these will print with the same value as when the
5700 // assertion triggers. (See SystemVerilog 2017 spec section 16.9.3 for
5701 // more information.)
5702 for (auto &loweredValue : messageOps)
5703 loweredValue = sv::SampledOp::create(builder, loweredValue);
5704 }
5705 }
5706
5707 auto emit = [&]() {
5708 switch (flavor) {
5709 case VerificationFlavor::Immediate: {
5710 // Handle the purely procedural flavor of the operation.
5711 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5712 builder.getContext(), circt::sv::DeferAssert::Immediate);
5713 addToAlwaysBlock(clock, [&]() {
5714 addIfProceduralBlock(enable, [&]() {
5715 buildImmediateVerifOp(builder, opName, predicate, deferImmediate,
5716 prefixedLabel, message, messageOps);
5717 });
5718 });
5719 return;
5720 }
5721 case VerificationFlavor::IfElseFatal: {
5722 assert(isa<AssertOp>(op) && "only assert is expected");
5723 // Handle the `ifElseFatal` format, which does not emit an SVA but
5724 // rather a process that uses $error and $fatal to perform the checks.
5725 auto boolType = IntegerType::get(builder.getContext(), 1);
5726 predicate = comb::createOrFoldNot(builder, predicate, /*twoState=*/true);
5727 predicate = builder.createOrFold<comb::AndOp>(enable, predicate, true);
5728
5729 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
5730 addToIfDefBlock("SYNTHESIS", {}, [&]() {
5731 addToAlwaysBlock(clock, [&]() {
5732 addIfProceduralBlock(predicate, [&]() {
5733 circuitState.usedStopCond = true;
5734 circuitState.addFragment(theModule, "STOP_COND_FRAGMENT");
5735
5736 circuitState.usedAssertVerboseCond = true;
5737 circuitState.addFragment(theModule, "ASSERT_VERBOSE_COND_FRAGMENT");
5738
5739 addIfProceduralBlock(
5740 sv::MacroRefExprOp::create(builder, boolType,
5741 "ASSERT_VERBOSE_COND_"),
5742 [&]() {
5743 sv::ErrorProceduralOp::create(builder, message, messageOps);
5744 });
5745 addIfProceduralBlock(
5746 sv::MacroRefExprOp::create(builder, boolType, "STOP_COND_"),
5747 [&]() { sv::FatalProceduralOp::create(builder); });
5748 });
5749 });
5750 });
5751 return;
5752 }
5753 case VerificationFlavor::SVA: {
5754 // Formulate the `enable -> predicate` as `!enable | predicate`.
5755 // Except for covers, combine them: enable & predicate
5756 if (!isCover) {
5757 auto notEnable =
5758 comb::createOrFoldNot(builder, enable, /*twoState=*/true);
5759 predicate =
5760 builder.createOrFold<comb::OrOp>(notEnable, predicate, true);
5761 } else {
5762 predicate = builder.createOrFold<comb::AndOp>(enable, predicate, true);
5763 }
5764
5765 // Handle the regular SVA case.
5766 sv::EventControl event;
5767 switch (opEventControl) {
5768 case EventControl::AtPosEdge:
5769 event = circt::sv::EventControl::AtPosEdge;
5770 break;
5771 case EventControl::AtEdge:
5772 event = circt::sv::EventControl::AtEdge;
5773 break;
5774 case EventControl::AtNegEdge:
5775 event = circt::sv::EventControl::AtNegEdge;
5776 break;
5777 }
5778
5780 builder, opName,
5781 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5782 predicate, prefixedLabel, message, messageOps);
5783 return;
5784 }
5785 case VerificationFlavor::None:
5786 llvm_unreachable(
5787 "flavor `None` must be converted into one of concreate flavors");
5788 }
5789 };
5790
5791 // Wrap the verification statement up in the optional preprocessor
5792 // guards. This is a bit awkward since we want to translate an array of
5793 // guards into a recursive call to `addToIfDefBlock`.
5794 return emitGuards(op->getLoc(), guards, emit);
5795}
5796
5797// Lower an assert to SystemVerilog.
5798LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5799 return lowerVerificationStatement(
5800 op, "assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5801 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5802 op.getIsConcurrent(), op.getEventControl());
5803}
5804
5805// Lower an assume to SystemVerilog.
5806LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5807 return lowerVerificationStatement(
5808 op, "assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5809 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5810 op.getIsConcurrent(), op.getEventControl());
5811}
5812
5813// Lower a cover to SystemVerilog.
5814LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5815 return lowerVerificationStatement(
5816 op, "cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5817 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5818 op.getIsConcurrent(), op.getEventControl());
5819}
5820
5821// Lower an UNR only assume to a specific style of SV assume.
5822LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5823 if (circuitState.lowerToCore) {
5824 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>("guards");
5825 if (guardsAttr && !guardsAttr.empty())
5826 return op.emitOpError(
5827 "lower-to-core does not support guarded verification statements");
5828
5829 auto predicate = getLoweredValue(op.getPredicate());
5830 auto enable = getLoweredValue(op.getEnable());
5831 if (!predicate || !enable)
5832 return failure();
5833
5834 auto label = op.getNameAttr();
5835 StringAttr assumeLabel;
5836 if (label && !label.empty())
5837 assumeLabel =
5838 StringAttr::get(builder.getContext(), "assume__" + label.getValue());
5839 verif::AssumeOp::create(builder, predicate, enable, assumeLabel);
5840 return success();
5841 }
5842
5843 // TODO : Need to figure out if there is a cleaner way to get the string which
5844 // indicates the assert is UNR only. Or better - not rely on this at all -
5845 // ideally there should have been some other attribute which indicated that
5846 // this assert for UNR only.
5847 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>("guards");
5848 ArrayRef<Attribute> guards =
5849 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5850
5851 auto label = op.getNameAttr();
5852 StringAttr assumeLabel;
5853 if (label && !label.empty())
5854 assumeLabel =
5855 StringAttr::get(builder.getContext(), "assume__" + label.getValue());
5856 auto predicate = getLoweredValue(op.getPredicate());
5857 auto enable = getLoweredValue(op.getEnable());
5858 auto notEnable = comb::createOrFoldNot(builder, enable, /*twoState=*/true);
5859 predicate = builder.createOrFold<comb::OrOp>(notEnable, predicate, true);
5860
5861 SmallVector<Value> messageOps;
5862 for (auto operand : op.getSubstitutions()) {
5863 auto loweredValue = getLoweredValue(operand);
5864 if (!loweredValue) {
5865 // If this is a zero bit operand, just pass a one bit zero.
5866 if (!isZeroBitFIRRTLType(operand.getType()))
5867 return failure();
5868 loweredValue = getOrCreateIntConstant(1, 0);
5869 }
5870 messageOps.push_back(loweredValue);
5871 }
5872 return emitGuards(op.getLoc(), guards, [&]() {
5873 sv::AlwaysOp::create(
5874 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5875 [&]() {
5876 if (op.getMessageAttr().getValue().empty())
5877 buildImmediateVerifOp(
5878 builder, "assume", predicate,
5879 circt::sv::DeferAssertAttr::get(
5880 builder.getContext(), circt::sv::DeferAssert::Immediate),
5881 assumeLabel);
5882 else
5883 buildImmediateVerifOp(
5884 builder, "assume", predicate,
5885 circt::sv::DeferAssertAttr::get(
5886 builder.getContext(), circt::sv::DeferAssert::Immediate),
5887 assumeLabel, op.getMessageAttr(), messageOps);
5888 });
5889 });
5890}
5891
5892LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5893 // Don't emit anything for a zero or one operand attach.
5894 if (op.getAttached().size() < 2)
5895 return success();
5896
5897 SmallVector<Value, 4> inoutValues;
5898 for (auto v : op.getAttached()) {
5899 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5900 if (!inoutValues.back()) {
5901 // Ignore zero bit values.
5902 if (!isZeroBitFIRRTLType(v.getType()))
5903 return failure();
5904 inoutValues.pop_back();
5905 continue;
5906 }
5907
5908 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5909 return op.emitError("operand isn't an inout type");
5910 }
5911
5912 if (inoutValues.size() < 2)
5913 return success();
5914
5915 // If the op has a single source value, the value is used as a lowering result
5916 // of other values. Therefore we can delete the attach op here.
5918 return success();
5919
5920 if (circuitState.lowerToCore)
5921 return op.emitOpError(
5922 "lower-to-core does not support firrtl.attach that requires SV "
5923 "lowering");
5924
5925 // If all operands of the attach are internal to this module (none of them
5926 // are ports), then they can all be replaced with a single wire, and we can
5927 // delete the attach op.
5928 bool isAttachInternalOnly =
5929 llvm::none_of(inoutValues, [](auto v) { return isa<BlockArgument>(v); });
5930
5931 if (isAttachInternalOnly) {
5932 auto v0 = inoutValues.front();
5933 for (auto v : inoutValues) {
5934 if (v == v0)
5935 continue;
5936 v.replaceAllUsesWith(v0);
5937 }
5938 return success();
5939 }
5940
5941 // If the attach operands contain a port, then we can't do anything to
5942 // simplify the attach operation.
5943 circuitState.addMacroDecl(builder.getStringAttr("SYNTHESIS"));
5944 circuitState.addMacroDecl(builder.getStringAttr("VERILATOR"));
5945 addToIfDefBlock(
5946 "SYNTHESIS",
5947 // If we're doing synthesis, we emit an all-pairs assign complex.
5948 [&]() {
5949 SmallVector<Value, 4> values;
5950 for (auto inoutValue : inoutValues)
5951 values.push_back(getReadValue(inoutValue));
5952
5953 for (size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
5954 for (size_t i2 = 0; i2 != e; ++i2)
5955 if (i1 != i2)
5956 sv::AssignOp::create(builder, inoutValues[i1], values[i2]);
5957 }
5958 },
5959 // In the non-synthesis case, we emit a SystemVerilog alias
5960 // statement.
5961 [&]() {
5962 sv::IfDefOp::create(
5963 builder, "VERILATOR",
5964 [&]() {
5965 sv::VerbatimOp::create(
5966 builder,
5967 "`error \"Verilator does not support alias and thus "
5968 "cannot "
5969 "arbitrarily connect bidirectional wires and ports\"");
5970 },
5971 [&]() { sv::AliasOp::create(builder, inoutValues); });
5972 });
5973
5974 return success();
5975}
5976
5977LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
5978 sv::BindOp::create(builder, op.getInstanceAttr());
5979 return success();
5980}
5981
5982LogicalResult FIRRTLLowering::fixupLTLOps() {
5983 if (ltlOpFixupWorklist.empty())
5984 return success();
5985 LLVM_DEBUG(llvm::dbgs() << "Fixing up " << ltlOpFixupWorklist.size()
5986 << " LTL ops\n");
5987
5988 // Add wire users into the worklist.
5989 for (unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
5990 for (auto *user : ltlOpFixupWorklist[i]->getUsers())
5991 if (isa<hw::WireOp>(user))
5992 ltlOpFixupWorklist.insert(user);
5993
5994 // Re-infer LTL op types and remove wires.
5995 while (!ltlOpFixupWorklist.empty()) {
5996 auto *op = ltlOpFixupWorklist.pop_back_val();
5997
5998 // Update the operation's return type by re-running type inference.
5999 if (auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
6000 LLVM_DEBUG(llvm::dbgs() << "- Update " << *op << "\n");
6001 SmallVector<Type, 2> types;
6002 auto result = opIntf.inferReturnTypes(
6003 op->getContext(), op->getLoc(), op->getOperands(),
6004 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
6005 types);
6006 if (failed(result))
6007 return failure();
6008 assert(types.size() == op->getNumResults());
6009
6010 // Update the result types and add the dependent ops into the worklist if
6011 // the type changed.
6012 for (auto [result, type] : llvm::zip(op->getResults(), types)) {
6013 if (result.getType() == type)
6014 continue;
6015 LLVM_DEBUG(llvm::dbgs()
6016 << " - Result #" << result.getResultNumber() << " from "
6017 << result.getType() << " to " << type << "\n");
6018 result.setType(type);
6019 for (auto *user : result.getUsers())
6020 if (user != op)
6021 ltlOpFixupWorklist.insert(user);
6022 }
6023 }
6024
6025 // Remove LTL-typed wires.
6026 if (auto wireOp = dyn_cast<hw::WireOp>(op)) {
6027 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
6028 wireOp.replaceAllUsesWith(wireOp.getInput());
6029 LLVM_DEBUG(llvm::dbgs() << "- Remove " << wireOp << "\n");
6030 if (wireOp.use_empty())
6031 wireOp.erase();
6032 }
6033 continue;
6034 }
6035
6036 // Ensure that the operation has no users outside of LTL operations.
6037 SmallPtrSet<Operation *, 4> usersReported;
6038 for (auto *user : op->getUsers()) {
6039 if (!usersReported.insert(user).second)
6040 continue;
6041 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
6042 user->getDialect()))
6043 continue;
6044 if (isa<hw::WireOp>(user))
6045 continue;
6046 auto d = op->emitError(
6047 "verification operation used in a non-verification context");
6048 d.attachNote(user->getLoc())
6049 << "leaking outside verification context here";
6050 return d;
6051 }
6052 }
6053
6054 return success();
6055}
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:218
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:1716
Definition hw.py:1
Definition seq.py:1
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition seq.py:21
Definition sv.py:1
Definition verif.py:1
The namespace of a CircuitOp, generally inhabited by modules.
Definition Namespace.h:24
This holds the name and type that describes the module's ports.
bool isOutput() const
Return true if this is a simple output-only port.
bool isInput() const
Return true if this is a simple input-only port.
mlir::Type type
Definition HWTypes.h:31
mlir::StringAttr name
Definition HWTypes.h:30
This holds the name, type, direction of a module's ports.
size_t argNum
This is the argument index or the result index depending on the direction.
void setSym(InnerSymAttr sym, MLIRContext *ctx)
InnerSymAttr getSym() const