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