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