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