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