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