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