14 #include "../PassDetail.h"
25 #include "mlir/IR/Builders.h"
26 #include "mlir/IR/DialectImplementation.h"
27 #include "mlir/IR/ImplicitLocOpBuilder.h"
28 #include "mlir/IR/Threading.h"
29 #include "mlir/Pass/Pass.h"
30 #include "mlir/Transforms/DialectConversion.h"
31 #include "llvm/ADT/IntervalMap.h"
32 #include "llvm/ADT/TypeSwitch.h"
34 #define DEBUG_TYPE "lower-seq-to-sv"
36 using namespace circt;
39 using llvm::MapVector;
42 #define GEN_PASS_DEF_LOWERSEQTOSV
43 #include "circt/Conversion/Passes.h.inc"
45 struct SeqToSVPass :
public impl::LowerSeqToSVBase<SeqToSVPass> {
46 void runOnOperation()
override;
47 using LowerSeqToSVBase<SeqToSVPass>::lowerToAlwaysFF;
48 using LowerSeqToSVBase<SeqToSVPass>::disableRegRandomization;
49 using LowerSeqToSVBase<SeqToSVPass>::emitSeparateAlwaysBlocks;
50 using LowerSeqToSVBase<SeqToSVPass>::LowerSeqToSVBase;
51 using LowerSeqToSVBase<SeqToSVPass>::numSubaccessRestored;
58 template <
typename OpTy>
61 CompRegLower(TypeConverter &typeConverter, MLIRContext *context,
64 lowerToAlwaysFF(lowerToAlwaysFF) {}
69 matchAndRewrite(OpTy
reg, OpAdaptor adaptor,
70 ConversionPatternRewriter &rewriter)
const final {
71 Location loc =
reg.getLoc();
74 ConversionPattern::getTypeConverter()->convertType(
reg.getType());
76 auto svReg = rewriter.create<
sv::RegOp>(loc, regTy,
reg.getNameAttr(),
77 reg.getInnerSymAttr(),
78 reg.getPowerOnValue());
79 svReg->setDialectAttrs(
reg->getDialectAttrs());
85 auto assignValue = [&] {
86 createAssign(rewriter,
reg.getLoc(), svReg,
reg);
88 auto assignReset = [&] {
89 rewriter.create<sv::PAssignOp>(loc, svReg, adaptor.getResetValue());
92 if (adaptor.getReset() && adaptor.getResetValue()) {
93 if (lowerToAlwaysFF) {
94 rewriter.create<sv::AlwaysFFOp>(
95 loc, sv::EventControl::AtPosEdge, adaptor.getClk(),
96 ResetType::SyncReset, sv::EventControl::AtPosEdge,
97 adaptor.getReset(), assignValue, assignReset);
99 rewriter.create<sv::AlwaysOp>(
100 loc, sv::EventControl::AtPosEdge, adaptor.getClk(), [&] {
101 rewriter.create<sv::IfOp>(loc, adaptor.getReset(), assignReset,
106 if (lowerToAlwaysFF) {
107 rewriter.create<sv::AlwaysFFOp>(loc, sv::EventControl::AtPosEdge,
108 adaptor.getClk(), assignValue);
110 rewriter.create<sv::AlwaysOp>(loc, sv::EventControl::AtPosEdge,
111 adaptor.getClk(), assignValue);
115 rewriter.replaceOp(
reg, regVal);
120 void createAssign(ConversionPatternRewriter &rewriter, Location loc,
124 bool lowerToAlwaysFF;
129 void CompRegLower<CompRegOp>::createAssign(ConversionPatternRewriter &rewriter,
131 OpAdaptor
reg)
const {
132 rewriter.create<sv::PAssignOp>(loc, svReg,
reg.getInput());
136 void CompRegLower<CompRegClockEnabledOp>::createAssign(
137 ConversionPatternRewriter &rewriter, Location loc,
sv::RegOp svReg,
138 OpAdaptor
reg)
const {
139 rewriter.create<sv::IfOp>(loc,
reg.getClockEnable(), [&]() {
140 rewriter.create<sv::PAssignOp>(loc, svReg,
reg.getInput());
151 matchAndRewrite(ClockGateOp clockGate, OpAdaptor adaptor,
152 ConversionPatternRewriter &rewriter)
const final {
153 auto loc = clockGate.getLoc();
154 Value
clk = adaptor.getInput();
157 Value enable = adaptor.getEnable();
158 if (
auto te = adaptor.getTestEnable())
159 enable = rewriter.create<
comb::OrOp>(loc, enable, te);
162 Value enableLatch = rewriter.create<
sv::RegOp>(
163 loc, rewriter.getI1Type(), rewriter.getStringAttr(
"cg_en_latch"));
166 rewriter.create<sv::AlwaysOp>(
167 loc, llvm::SmallVector<sv::EventControl>{}, llvm::SmallVector<Value>{},
169 rewriter.create<sv::IfOp>(
171 rewriter.create<sv::PAssignOp>(loc, enableLatch, enable);
178 clockGate.replaceAllUsesWith(gclk);
179 rewriter.eraseOp(clockGate);
192 matchAndRewrite(ClockMuxOp clockMux, OpAdaptor adaptor,
193 ConversionPatternRewriter &rewriter)
const final {
194 rewriter.replaceOpWithNewOp<
comb::MuxOp>(clockMux, adaptor.getCond(),
195 adaptor.getTrueClock(),
196 adaptor.getFalseClock(),
true);
202 struct SeqToSVTypeConverter :
public TypeConverter {
203 SeqToSVTypeConverter() {
204 addConversion([&](Type type) {
return type; });
205 addConversion([&](seq::ClockType type) {
208 addConversion([&](hw::StructType structTy) {
209 bool changed =
false;
211 SmallVector<hw::StructType::FieldInfo> newFields;
212 for (
auto field : structTy.getElements()) {
213 auto &newField = newFields.emplace_back();
214 newField.name = field.name;
215 newField.type = convertType(field.type);
216 if (field.type != newField.type)
225 addConversion([&](hw::ArrayType arrayTy) {
226 auto elementTy = arrayTy.getElementType();
228 if (elementTy != newElementTy)
233 addTargetMaterialization(
234 [&](mlir::OpBuilder &
builder, mlir::Type resultType,
236 mlir::Location loc) -> std::optional<mlir::Value> {
242 addSourceMaterialization(
243 [&](mlir::OpBuilder &
builder, mlir::Type resultType,
245 mlir::Location loc) -> std::optional<mlir::Value> {
254 template <
typename T>
260 matchAndRewrite(T op,
typename T::Adaptor adaptor,
261 ConversionPatternRewriter &rewriter)
const final {
262 rewriter.replaceOp(op, adaptor.getInput());
275 matchAndRewrite(ConstClockOp clockConst, OpAdaptor adaptor,
276 ConversionPatternRewriter &rewriter)
const final {
278 clockConst, APInt(1, clockConst.getValue() == ClockConst::High));
291 matchAndRewrite(ClockDivider clockDiv, OpAdaptor adaptor,
292 ConversionPatternRewriter &rewriter)
const final {
293 Location loc = clockDiv.getLoc();
296 if (clockDiv.getPow2()) {
300 Value output = clockDiv.getClockIn();
302 SmallVector<Value> regs;
303 for (
unsigned i = 0; i < clockDiv.getPow2(); ++i) {
305 loc, rewriter.getI1Type(),
306 rewriter.getStringAttr(
"clock_out_" + std::to_string(i)));
309 rewriter.create<sv::AlwaysOp>(
310 loc, sv::EventControl::AtPosEdge, output, [&] {
313 rewriter.create<sv::BPAssignOp>(loc,
reg, inverted);
321 rewriter.
create<sv::InitialOp>(loc, [&] {
322 for (Value
reg : regs) {
323 rewriter.
create<sv::BPAssignOp>(loc,
reg, zero);
328 rewriter.replaceOp(clockDiv, output);
337 if (hw::type_isa<ClockType>(ty))
340 if (
auto arrayTy = hw::type_dyn_cast<hw::ArrayType>(ty))
343 if (
auto structTy = hw::type_dyn_cast<hw::StructType>(ty)) {
344 for (
auto field : structTy.getElements())
355 if (
auto module = dyn_cast<hw::HWModuleLike>(op)) {
356 return llvm::all_of(module.getPortList(), [](hw::PortInfo port) {
357 return isLegalType(port.type);
360 bool allOperandsLowered = llvm::all_of(
361 op->getOperands(), [](
auto op) { return isLegalType(op.getType()); });
362 bool allResultsLowered = llvm::all_of(op->getResults(), [](
auto result) {
363 return isLegalType(result.getType());
365 return allOperandsLowered && allResultsLowered;
368 void SeqToSVPass::runOnOperation() {
369 auto circuit = getOperation();
370 MLIRContext *context = &getContext();
372 auto modules = llvm::to_vector(circuit.getOps<
HWModuleOp>());
377 auto uniqueMems = memLowering.collectMemories(modules);
378 MapVector<HWModuleOp, SmallVector<FirMemLowering::MemoryConfig>> memsByModule;
379 for (
auto &[config, memOps] : uniqueMems) {
381 auto genOp = memLowering.createMemoryModule(config, memOps);
384 for (
auto memOp : memOps) {
385 auto parent = memOp->getParentOfType<
HWModuleOp>();
386 memsByModule[parent].emplace_back(&config, genOp, memOp);
391 mlir::parallelForEach(&getContext(), modules, [&](
HWModuleOp module) {
392 SeqToSVTypeConverter typeConverter;
393 FirRegLowering regLowering(typeConverter, module, disableRegRandomization,
394 emitSeparateAlwaysBlocks);
396 numSubaccessRestored += regLowering.numSubaccessRestored;
398 if (
auto *it = memsByModule.find(module); it != memsByModule.end())
399 memLowering.lowerMemoriesInModule(module, it->second);
403 SeqToSVTypeConverter typeConverter;
404 ConversionTarget target(*context);
405 target.addIllegalDialect<SeqDialect>();
406 target.markUnknownOpDynamicallyLegal(
isLegalOp);
408 RewritePatternSet
patterns(context);
409 patterns.add<CompRegLower<CompRegOp>>(typeConverter, context,
411 patterns.add<CompRegLower<CompRegClockEnabledOp>>(typeConverter, context,
413 patterns.add<ClockCastLowering<seq::FromClockOp>>(typeConverter, context);
414 patterns.add<ClockCastLowering<seq::ToClockOp>>(typeConverter, context);
415 patterns.add<ClockGateLowering>(typeConverter, context);
416 patterns.add<ClockMuxLowering>(typeConverter, context);
417 patterns.add<ClockDividerLowering>(typeConverter, context);
418 patterns.add<ClockConstLowering>(typeConverter, context);
421 if (failed(applyPartialConversion(circuit, target, std::move(
patterns))))
425 std::unique_ptr<Pass>
427 return std::make_unique<SeqToSVPass>(options);
static FIRRTLBaseType convertType(FIRRTLBaseType type)
Returns null type if no conversion is needed.
llvm::SmallVector< StringAttr > inputs
static bool isLegalType(Type ty)
static bool isLegalOp(Operation *op)
FIR memory lowering helper.
Lower FirRegOp to sv.reg and sv.always.
def create(data_type, value)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
void setSVAttributes(mlir::Operation *op, mlir::ArrayAttr attrs)
Set the SV attributes of an operation.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
std::unique_ptr< mlir::Pass > createLowerSeqToSVPass(const LowerSeqToSVOptions &options={})
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Generic pattern which replaces an operation by one of the same operation name, but with converted att...