CIRCT 20.0.0git
Loading...
Searching...
No Matches
SeqToSV.cpp
Go to the documentation of this file.
1//===- LowerSeqToSV.cpp - Seq to SV lowering ------------------------------===//
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 transform translate Seq ops to SV.
10//
11//===----------------------------------------------------------------------===//
12
14#include "FirMemLowering.h"
15#include "FirRegLowering.h"
26#include "mlir/Analysis/TopologicalSortUtils.h"
27#include "mlir/IR/Builders.h"
28#include "mlir/IR/DialectImplementation.h"
29#include "mlir/IR/ImplicitLocOpBuilder.h"
30#include "mlir/IR/Threading.h"
31#include "mlir/Pass/Pass.h"
32#include "mlir/Transforms/DialectConversion.h"
33#include "llvm/ADT/IntervalMap.h"
34#include "llvm/ADT/TypeSwitch.h"
35#include "llvm/Support/Mutex.h"
36
37#define DEBUG_TYPE "lower-seq-to-sv"
38
39using namespace circt;
40using namespace seq;
41using hw::HWModuleOp;
42using llvm::MapVector;
43
44namespace circt {
45#define GEN_PASS_DEF_LOWERSEQTOSV
46#include "circt/Conversion/Passes.h.inc"
47
48struct SeqToSVPass : public impl::LowerSeqToSVBase<SeqToSVPass> {
49
50 void runOnOperation() override;
51
52 using LowerSeqToSVBase<SeqToSVPass>::lowerToAlwaysFF;
53 using LowerSeqToSVBase<SeqToSVPass>::disableRegRandomization;
54 using LowerSeqToSVBase<SeqToSVPass>::emitSeparateAlwaysBlocks;
55 using LowerSeqToSVBase<SeqToSVPass>::LowerSeqToSVBase;
56 using LowerSeqToSVBase<SeqToSVPass>::numSubaccessRestored;
57};
58} // namespace circt
59
60namespace {
61struct ModuleLoweringState {
62 ModuleLoweringState(HWModuleOp module)
63 : immutableValueLowering(module), module(module) {}
64
65 struct ImmutableValueLowering {
66 ImmutableValueLowering(hw::HWModuleOp module) : module(module) {}
67
68 // Lower initial ops.
69 LogicalResult lower();
70 LogicalResult lower(seq::InitialOp initialOp);
71
72 Value
73 lookupImmutableValue(mlir::TypedValue<seq::ImmutableType> immut) const {
74 return mapping.lookup(immut);
75 }
76
77 sv::InitialOp getSVInitial() const { return svInitialOp; }
78
79 private:
80 sv::InitialOp svInitialOp = {};
81 // A mapping from a dummy immutable value to the actual initial value
82 // defined in SV initial op.
83 MapVector<mlir::TypedValue<seq::ImmutableType>, Value> mapping;
84
85 hw::HWModuleOp module;
86 } immutableValueLowering;
87
88 struct FragmentInfo {
89 bool needsRegFragment = false;
90 bool needsMemFragment = false;
91 } fragment;
92
93 HWModuleOp module;
94};
95
96LogicalResult ModuleLoweringState::ImmutableValueLowering::lower() {
97 auto result = mergeInitialOps(module.getBodyBlock());
98 if (failed(result))
99 return failure();
100
101 auto initialOp = *result;
102 if (!initialOp)
103 return success();
104
105 return lower(initialOp);
106}
107
108LogicalResult
109ModuleLoweringState::ImmutableValueLowering::lower(seq::InitialOp initialOp) {
110 OpBuilder builder = OpBuilder::atBlockBegin(module.getBodyBlock());
111 if (!svInitialOp)
112 svInitialOp = builder.create<sv::InitialOp>(initialOp->getLoc());
113 // Initial ops are merged to single one and must not have operands.
114 assert(initialOp.getNumOperands() == 0 &&
115 "initial op should have no operands");
116
117 auto loc = initialOp.getLoc();
118 llvm::SmallVector<Value> results;
119
120 auto yieldOp = cast<seq::YieldOp>(initialOp.getBodyBlock()->getTerminator());
121
122 for (auto [result, operand] :
123 llvm::zip(initialOp.getResults(), yieldOp->getOperands())) {
124 auto placeholder =
125 builder
126 .create<mlir::UnrealizedConversionCastOp>(
127 loc, ArrayRef<Type>{result.getType()}, ArrayRef<Value>{})
128 ->getResult(0);
129 result.replaceAllUsesWith(placeholder);
130 mapping.insert(
131 {cast<mlir::TypedValue<seq ::ImmutableType>>(placeholder), operand});
132 }
133
134 svInitialOp.getBodyBlock()->getOperations().splice(
135 svInitialOp.end(), initialOp.getBodyBlock()->getOperations());
136
137 assert(initialOp->use_empty());
138 initialOp.erase();
139 yieldOp->erase();
140 return success();
141}
142
143/// Lower CompRegOp to `sv.reg` and `sv.alwaysff`. Use a posedge clock and
144/// synchronous reset.
145template <typename OpTy>
146class CompRegLower : public OpConversionPattern<OpTy> {
147public:
148 CompRegLower(
149 TypeConverter &typeConverter, MLIRContext *context, bool lowerToAlwaysFF,
150 const MapVector<StringAttr, ModuleLoweringState> &moduleLoweringStates)
151 : OpConversionPattern<OpTy>(typeConverter, context),
152 lowerToAlwaysFF(lowerToAlwaysFF),
153 moduleLoweringStates(moduleLoweringStates) {}
154
155 using OpAdaptor = typename OpConversionPattern<OpTy>::OpAdaptor;
156
157 LogicalResult
158 matchAndRewrite(OpTy reg, OpAdaptor adaptor,
159 ConversionPatternRewriter &rewriter) const final {
160 Location loc = reg.getLoc();
161
162 auto regTy =
163 ConversionPattern::getTypeConverter()->convertType(reg.getType());
164 auto svReg = rewriter.create<sv::RegOp>(loc, regTy, reg.getNameAttr(),
165 reg.getInnerSymAttr());
166
167 svReg->setDialectAttrs(reg->getDialectAttrs());
168
170
171 auto regVal = rewriter.create<sv::ReadInOutOp>(loc, svReg);
172
173 auto assignValue = [&] {
174 createAssign(rewriter, reg.getLoc(), svReg, reg);
175 };
176 auto assignReset = [&] {
177 rewriter.create<sv::PAssignOp>(loc, svReg, adaptor.getResetValue());
178 };
179
180 // Registers written in an `always_ff` process may not have any assignments
181 // outside of that process.
182 // For some tools this also prohibits inititalization.
183 bool mayLowerToAlwaysFF = lowerToAlwaysFF && !reg.getInitialValue();
184
185 if (adaptor.getReset() && adaptor.getResetValue()) {
186 if (mayLowerToAlwaysFF) {
187 rewriter.create<sv::AlwaysFFOp>(
188 loc, sv::EventControl::AtPosEdge, adaptor.getClk(),
189 sv::ResetType::SyncReset, sv::EventControl::AtPosEdge,
190 adaptor.getReset(), assignValue, assignReset);
191 } else {
192 rewriter.create<sv::AlwaysOp>(
193 loc, sv::EventControl::AtPosEdge, adaptor.getClk(), [&] {
194 rewriter.create<sv::IfOp>(loc, adaptor.getReset(), assignReset,
195 assignValue);
196 });
197 }
198 } else {
199 if (mayLowerToAlwaysFF) {
200 rewriter.create<sv::AlwaysFFOp>(loc, sv::EventControl::AtPosEdge,
201 adaptor.getClk(), assignValue);
202 } else {
203 rewriter.create<sv::AlwaysOp>(loc, sv::EventControl::AtPosEdge,
204 adaptor.getClk(), assignValue);
205 }
206 }
207
208 // Lower initial values.
209 if (auto init = reg.getInitialValue()) {
210 auto module = reg->template getParentOfType<hw::HWModuleOp>();
211 const auto &initial =
212 moduleLoweringStates.find(module.getModuleNameAttr())
213 ->second.immutableValueLowering;
214
215 Value initialValue = initial.lookupImmutableValue(init);
216
217 if (auto op = initialValue.getDefiningOp();
218 op && op->hasTrait<mlir::OpTrait::ConstantLike>()) {
219 auto clonedConstant = rewriter.clone(*op);
220 rewriter.moveOpBefore(clonedConstant, svReg);
221 svReg.getInitMutable().assign(clonedConstant->getResult(0));
222 } else {
223 OpBuilder::InsertionGuard guard(rewriter);
224 auto in = initial.getSVInitial();
225 rewriter.setInsertionPointToEnd(in.getBodyBlock());
226 rewriter.create<sv::BPAssignOp>(reg->getLoc(), svReg, initialValue);
227 }
228 }
229
230 rewriter.replaceOp(reg, regVal);
231 return success();
232 }
233
234 // Helper to create an assignment based on the register type.
235 void createAssign(ConversionPatternRewriter &rewriter, Location loc,
236 sv::RegOp svReg, OpAdaptor reg) const;
237
238private:
239 bool lowerToAlwaysFF;
240 const MapVector<StringAttr, ModuleLoweringState> &moduleLoweringStates;
241};
242
243/// Create the assign.
244template <>
245void CompRegLower<CompRegOp>::createAssign(ConversionPatternRewriter &rewriter,
246 Location loc, sv::RegOp svReg,
247 OpAdaptor reg) const {
248 rewriter.create<sv::PAssignOp>(loc, svReg, reg.getInput());
249}
250/// Create the assign inside of an if block.
251template <>
252void CompRegLower<CompRegClockEnabledOp>::createAssign(
253 ConversionPatternRewriter &rewriter, Location loc, sv::RegOp svReg,
254 OpAdaptor reg) const {
255 rewriter.create<sv::IfOp>(loc, reg.getClockEnable(), [&]() {
256 rewriter.create<sv::PAssignOp>(loc, svReg, reg.getInput());
257 });
258}
259
260/// Lower FromImmutable to `sv.reg` and `sv.initial`.
261class FromImmutableLowering : public OpConversionPattern<FromImmutableOp> {
262public:
263 FromImmutableLowering(
264 TypeConverter &typeConverter, MLIRContext *context,
265 const MapVector<StringAttr, ModuleLoweringState> &moduleLoweringStates)
266 : OpConversionPattern<FromImmutableOp>(typeConverter, context),
267 moduleLoweringStates(moduleLoweringStates) {}
268
269 using OpAdaptor = typename OpConversionPattern<FromImmutableOp>::OpAdaptor;
270
271 LogicalResult
272 matchAndRewrite(FromImmutableOp fromImmutableOp, OpAdaptor adaptor,
273 ConversionPatternRewriter &rewriter) const final {
274 Location loc = fromImmutableOp.getLoc();
275
276 auto regTy = ConversionPattern::getTypeConverter()->convertType(
277 fromImmutableOp.getType());
278 auto svReg = rewriter.create<sv::RegOp>(loc, regTy);
279
280 auto regVal = rewriter.create<sv::ReadInOutOp>(loc, svReg);
281
282 // Lower initial values.
283 auto module = fromImmutableOp->template getParentOfType<hw::HWModuleOp>();
284 const auto &initial = moduleLoweringStates.find(module.getModuleNameAttr())
285 ->second.immutableValueLowering;
286
287 Value initialValue =
288 initial.lookupImmutableValue(fromImmutableOp.getInput());
289
290 OpBuilder::InsertionGuard guard(rewriter);
291 auto in = initial.getSVInitial();
292 rewriter.setInsertionPointToEnd(in.getBodyBlock());
293 rewriter.create<sv::BPAssignOp>(fromImmutableOp->getLoc(), svReg,
294 initialValue);
295
296 rewriter.replaceOp(fromImmutableOp, regVal);
297 return success();
298 }
299
300private:
301 const MapVector<StringAttr, ModuleLoweringState> &moduleLoweringStates;
302};
303// Lower seq.clock_gate to a fairly standard clock gate implementation.
304//
305class ClockGateLowering : public OpConversionPattern<ClockGateOp> {
306public:
308 using OpAdaptor = typename OpConversionPattern<ClockGateOp>::OpAdaptor;
309 LogicalResult
310 matchAndRewrite(ClockGateOp clockGate, OpAdaptor adaptor,
311 ConversionPatternRewriter &rewriter) const final {
312 auto loc = clockGate.getLoc();
313 Value clk = adaptor.getInput();
314
315 // enable in
316 Value enable = adaptor.getEnable();
317 if (auto te = adaptor.getTestEnable())
318 enable = rewriter.create<comb::OrOp>(loc, enable, te);
319
320 // Enable latch.
321 Value enableLatch = rewriter.create<sv::RegOp>(
322 loc, rewriter.getI1Type(), rewriter.getStringAttr("cg_en_latch"));
323
324 // Latch the enable signal using an always @* block.
325 rewriter.create<sv::AlwaysOp>(
326 loc, llvm::SmallVector<sv::EventControl>{}, llvm::SmallVector<Value>{},
327 [&]() {
328 rewriter.create<sv::IfOp>(
329 loc, comb::createOrFoldNot(loc, clk, rewriter), [&]() {
330 rewriter.create<sv::PAssignOp>(loc, enableLatch, enable);
331 });
332 });
333
334 // Create the gated clock signal.
335 rewriter.replaceOpWithNewOp<comb::AndOp>(
336 clockGate, clk, rewriter.create<sv::ReadInOutOp>(loc, enableLatch));
337 return success();
338 }
339};
340
341// Lower seq.clock_inv to a regular inverter.
342//
343class ClockInverterLowering : public OpConversionPattern<ClockInverterOp> {
344public:
345 using OpConversionPattern<ClockInverterOp>::OpConversionPattern;
346
347 LogicalResult
348 matchAndRewrite(ClockInverterOp op, OpAdaptor adaptor,
349 ConversionPatternRewriter &rewriter) const final {
350 auto loc = op.getLoc();
351 Value clk = adaptor.getInput();
352
353 StringAttr name = op->getAttrOfType<StringAttr>("sv.namehint");
354 Value one = rewriter.create<hw::ConstantOp>(loc, APInt(1, 1));
355 auto newOp = rewriter.replaceOpWithNewOp<comb::XorOp>(op, clk, one);
356 if (name)
357 rewriter.modifyOpInPlace(newOp,
358 [&] { newOp->setAttr("sv.namehint", name); });
359 return success();
360 }
361};
362
363// Lower seq.clock_mux to a `comb.mux` op
364//
365class ClockMuxLowering : public OpConversionPattern<ClockMuxOp> {
366public:
368 using OpConversionPattern<ClockMuxOp>::OpAdaptor;
369
370 LogicalResult
371 matchAndRewrite(ClockMuxOp clockMux, OpAdaptor adaptor,
372 ConversionPatternRewriter &rewriter) const final {
373 rewriter.replaceOpWithNewOp<comb::MuxOp>(clockMux, adaptor.getCond(),
374 adaptor.getTrueClock(),
375 adaptor.getFalseClock(), true);
376 return success();
377 }
378};
379
380/// Map `seq.clock` to `i1`.
381struct SeqToSVTypeConverter : public TypeConverter {
382 SeqToSVTypeConverter() {
383 addConversion([&](Type type) { return type; });
384 addConversion([&](seq::ImmutableType type) { return type.getInnerType(); });
385 addConversion([&](seq::ClockType type) {
386 return IntegerType::get(type.getContext(), 1);
387 });
388 addConversion([&](hw::StructType structTy) {
389 bool changed = false;
390
391 SmallVector<hw::StructType::FieldInfo> newFields;
392 for (auto field : structTy.getElements()) {
393 auto &newField = newFields.emplace_back();
394 newField.name = field.name;
395 newField.type = convertType(field.type);
396 if (field.type != newField.type)
397 changed = true;
398 }
399
400 if (!changed)
401 return structTy;
402
403 return hw::StructType::get(structTy.getContext(), newFields);
404 });
405 addConversion([&](hw::ArrayType arrayTy) {
406 auto elementTy = arrayTy.getElementType();
407 auto newElementTy = convertType(elementTy);
408 if (elementTy != newElementTy)
409 return hw::ArrayType::get(newElementTy, arrayTy.getNumElements());
410 return arrayTy;
411 });
412
413 addTargetMaterialization([&](mlir::OpBuilder &builder,
414 mlir::Type resultType, mlir::ValueRange inputs,
415 mlir::Location loc) -> mlir::Value {
416 if (inputs.size() != 1)
417 return Value();
418 return builder
419 .create<mlir::UnrealizedConversionCastOp>(loc, resultType, inputs[0])
420 ->getResult(0);
421 });
422
423 addSourceMaterialization([&](mlir::OpBuilder &builder,
424 mlir::Type resultType, mlir::ValueRange inputs,
425 mlir::Location loc) -> mlir::Value {
426 if (inputs.size() != 1)
427 return Value();
428 return builder
429 .create<mlir::UnrealizedConversionCastOp>(loc, resultType, inputs[0])
430 ->getResult(0);
431 });
432 }
433};
434
435/// Eliminate no-op clock casts.
436template <typename T>
437class ClockCastLowering : public OpConversionPattern<T> {
438public:
440
441 LogicalResult
442 matchAndRewrite(T op, typename T::Adaptor adaptor,
443 ConversionPatternRewriter &rewriter) const final {
444 // If the cast had a better name than its input, propagate it.
445 if (Operation *inputOp = adaptor.getInput().getDefiningOp())
446 if (!isa<mlir::UnrealizedConversionCastOp>(inputOp))
447 if (auto name = chooseName(op, inputOp))
448 rewriter.modifyOpInPlace(
449 inputOp, [&] { inputOp->setAttr("sv.namehint", name); });
450
451 rewriter.replaceOp(op, adaptor.getInput());
452 return success();
453 }
454};
455
456// Lower seq.const_clock to `hw.constant`
457//
458class ClockConstLowering : public OpConversionPattern<ConstClockOp> {
459public:
461 using OpConversionPattern<ConstClockOp>::OpAdaptor;
462
463 LogicalResult
464 matchAndRewrite(ConstClockOp clockConst, OpAdaptor adaptor,
465 ConversionPatternRewriter &rewriter) const final {
466 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
467 clockConst, APInt(1, clockConst.getValue() == ClockConst::High));
468 return success();
469 }
470};
471
472class AggregateConstantPattern
473 : public OpConversionPattern<hw::AggregateConstantOp> {
474public:
475 using OpConversionPattern<hw::AggregateConstantOp>::OpConversionPattern;
476 using OpConversionPattern<hw::AggregateConstantOp>::OpAdaptor;
477
478 LogicalResult
479 matchAndRewrite(hw::AggregateConstantOp aggregateConstant, OpAdaptor adaptor,
480 ConversionPatternRewriter &rewriter) const final {
481 auto newType = typeConverter->convertType(aggregateConstant.getType());
482 auto newAttr = aggregateConstant.getFieldsAttr().replace(
483 [](seq::ClockConstAttr clockConst) {
484 return mlir::IntegerAttr::get(
485 mlir::IntegerType::get(clockConst.getContext(), 1),
486 APInt(1, clockConst.getValue() == ClockConst::High));
487 });
488 rewriter.replaceOpWithNewOp<hw::AggregateConstantOp>(
489 aggregateConstant, newType, cast<ArrayAttr>(newAttr));
490 return success();
491 }
492};
493
494/// Lower `seq.clock_div` to a behavioural clock divider
495///
496class ClockDividerLowering : public OpConversionPattern<ClockDividerOp> {
497public:
498 using OpConversionPattern<ClockDividerOp>::OpConversionPattern;
499 using OpConversionPattern<ClockDividerOp>::OpAdaptor;
500
501 LogicalResult
502 matchAndRewrite(ClockDividerOp clockDiv, OpAdaptor adaptor,
503 ConversionPatternRewriter &rewriter) const final {
504 Location loc = clockDiv.getLoc();
505
506 Value one;
507 if (clockDiv.getPow2()) {
508 one = rewriter.create<hw::ConstantOp>(loc, APInt(1, 1));
509 }
510
511 Value output = clockDiv.getInput();
512
513 SmallVector<Value> regs;
514 for (unsigned i = 0; i < clockDiv.getPow2(); ++i) {
515 Value reg = rewriter.create<sv::RegOp>(
516 loc, rewriter.getI1Type(),
517 rewriter.getStringAttr("clock_out_" + std::to_string(i)));
518 regs.push_back(reg);
519
520 rewriter.create<sv::AlwaysOp>(
521 loc, sv::EventControl::AtPosEdge, output, [&] {
522 Value outputVal = rewriter.create<sv::ReadInOutOp>(loc, reg);
523 Value inverted = rewriter.create<comb::XorOp>(loc, outputVal, one);
524 rewriter.create<sv::BPAssignOp>(loc, reg, inverted);
525 });
526
527 output = rewriter.create<sv::ReadInOutOp>(loc, reg);
528 }
529
530 if (!regs.empty()) {
531 Value zero = rewriter.create<hw::ConstantOp>(loc, APInt(1, 0));
532 rewriter.create<sv::InitialOp>(loc, [&] {
533 for (Value reg : regs) {
534 rewriter.create<sv::BPAssignOp>(loc, reg, zero);
535 }
536 });
537 }
538
539 rewriter.replaceOp(clockDiv, output);
540 return success();
541 }
542};
543
544} // namespace
545
546// NOLINTBEGIN(misc-no-recursion)
547static bool isLegalType(Type ty) {
548 if (hw::type_isa<ClockType>(ty))
549 return false;
550
551 if (auto arrayTy = hw::type_dyn_cast<hw::ArrayType>(ty))
552 return isLegalType(arrayTy.getElementType());
553
554 if (auto structTy = hw::type_dyn_cast<hw::StructType>(ty)) {
555 for (auto field : structTy.getElements())
556 if (!isLegalType(field.type))
557 return false;
558 return true;
559 }
560
561 return true;
562}
563// NOLINTEND(misc-no-recursion)
564
565static bool isLegalOp(Operation *op) {
566 if (auto module = dyn_cast<hw::HWModuleLike>(op)) {
567 for (auto port : module.getHWModuleType().getPorts())
568 if (!isLegalType(port.type))
569 return false;
570 return true;
571 }
572
573 if (auto hwAggregateConstantOp = dyn_cast<hw::AggregateConstantOp>(op)) {
574 bool foundClockAttr = false;
575 hwAggregateConstantOp.getFieldsAttr().walk(
576 [&](seq::ClockConstAttr attr) { foundClockAttr = true; });
577 if (foundClockAttr)
578 return false;
579 }
580
581 bool allOperandsLowered = llvm::all_of(
582 op->getOperands(), [](auto op) { return isLegalType(op.getType()); });
583 bool allResultsLowered = llvm::all_of(op->getResults(), [](auto result) {
584 return isLegalType(result.getType());
585 });
586 return allOperandsLowered && allResultsLowered;
587}
588
590 auto circuit = getOperation();
591 MLIRContext *context = &getContext();
592
593 auto modules = llvm::to_vector(circuit.getOps<HWModuleOp>());
594
595 FirMemLowering memLowering(circuit);
596
597 // Identify memories and group them by module.
598 auto uniqueMems = memLowering.collectMemories(modules);
599 MapVector<HWModuleOp, SmallVector<FirMemLowering::MemoryConfig>> memsByModule;
600 for (auto &[config, memOps] : uniqueMems) {
601 // Create the `HWModuleGeneratedOp`s for each unique configuration.
602 auto genOp = memLowering.createMemoryModule(config, memOps);
603
604 // Group memories by their parent module for parallelism.
605 for (auto memOp : memOps) {
606 auto parent = memOp->getParentOfType<HWModuleOp>();
607 memsByModule[parent].emplace_back(&config, genOp, memOp);
608 }
609 }
610
611 // Lower memories and registers in modules in parallel.
612 std::atomic<bool> needsRegRandomization = false;
613 std::atomic<bool> needsMemRandomization = false;
614
615 MapVector<StringAttr, ModuleLoweringState> moduleLoweringStates;
616 for (auto module : circuit.getOps<HWModuleOp>())
617 moduleLoweringStates.try_emplace(module.getModuleNameAttr(),
618 ModuleLoweringState(module));
619
620 auto result = mlir::failableParallelForEach(
621 &getContext(), moduleLoweringStates, [&](auto &moduleAndState) {
622 auto &state = moduleAndState.second;
623 auto module = state.module;
624 SeqToSVTypeConverter typeConverter;
625 FirRegLowering regLowering(typeConverter, module,
626 disableRegRandomization,
627 emitSeparateAlwaysBlocks);
628 regLowering.lower();
629 if (regLowering.needsRegRandomization()) {
630 if (!disableRegRandomization) {
631 state.fragment.needsRegFragment = true;
632 }
633 needsRegRandomization = true;
634 }
635 numSubaccessRestored += regLowering.numSubaccessRestored;
636
637 if (auto *it = memsByModule.find(module); it != memsByModule.end()) {
638 memLowering.lowerMemoriesInModule(module, it->second);
639 if (!disableMemRandomization) {
640 state.fragment.needsMemFragment = true;
641 }
642 needsMemRandomization = true;
643 }
644 return state.immutableValueLowering.lower();
645 });
646
647 if (failed(result))
648 return signalPassFailure();
649
650 auto randomInitFragmentName =
651 FlatSymbolRefAttr::get(context, "RANDOM_INIT_FRAGMENT");
652 auto randomInitRegFragmentName =
653 FlatSymbolRefAttr::get(context, "RANDOM_INIT_REG_FRAGMENT");
654 auto randomInitMemFragmentName =
655 FlatSymbolRefAttr::get(context, "RANDOM_INIT_MEM_FRAGMENT");
656
657 for (auto &[_, state] : moduleLoweringStates) {
658 const auto &info = state.fragment;
659 if (!info.needsRegFragment && !info.needsMemFragment) {
660 // If neither is emitted, just skip it.
661 continue;
662 }
663
664 SmallVector<Attribute> fragmentAttrs;
665 auto module = state.module;
666 if (auto others =
667 module->getAttrOfType<ArrayAttr>(emit::getFragmentsAttrName()))
668 fragmentAttrs = llvm::to_vector(others);
669
670 if (info.needsRegFragment)
671 fragmentAttrs.push_back(randomInitRegFragmentName);
672 if (info.needsMemFragment)
673 fragmentAttrs.push_back(randomInitMemFragmentName);
674 fragmentAttrs.push_back(randomInitFragmentName);
675
676 module->setAttr(emit::getFragmentsAttrName(),
677 ArrayAttr::get(context, fragmentAttrs));
678 }
679
680 // Mark all ops which can have clock types as illegal.
681 SeqToSVTypeConverter typeConverter;
682 ConversionTarget target(*context);
683 target.addIllegalDialect<SeqDialect>();
684 target.markUnknownOpDynamicallyLegal(isLegalOp);
685
686 RewritePatternSet patterns(context);
687 patterns.add<CompRegLower<CompRegOp>>(typeConverter, context, lowerToAlwaysFF,
688 moduleLoweringStates);
689 patterns.add<CompRegLower<CompRegClockEnabledOp>>(
690 typeConverter, context, lowerToAlwaysFF, moduleLoweringStates);
691 patterns.add<FromImmutableLowering>(typeConverter, context,
692 moduleLoweringStates);
693 patterns.add<ClockCastLowering<seq::FromClockOp>>(typeConverter, context);
694 patterns.add<ClockCastLowering<seq::ToClockOp>>(typeConverter, context);
695 patterns.add<ClockGateLowering>(typeConverter, context);
696 patterns.add<ClockInverterLowering>(typeConverter, context);
697 patterns.add<ClockMuxLowering>(typeConverter, context);
698 patterns.add<ClockDividerLowering>(typeConverter, context);
699 patterns.add<ClockConstLowering>(typeConverter, context);
700 patterns.add<TypeConversionPattern>(typeConverter, context);
701 patterns.add<AggregateConstantPattern>(typeConverter, context);
702
703 if (failed(applyPartialConversion(circuit, target, std::move(patterns))))
704 signalPassFailure();
705
706 auto loc = UnknownLoc::get(context);
707 auto b = ImplicitLocOpBuilder::atBlockBegin(loc, circuit.getBody());
708 if (needsRegRandomization || needsMemRandomization) {
709 b.create<sv::MacroDeclOp>("ENABLE_INITIAL_REG_");
710 b.create<sv::MacroDeclOp>("ENABLE_INITIAL_MEM_");
711 if (needsRegRandomization) {
712 b.create<sv::MacroDeclOp>("FIRRTL_BEFORE_INITIAL");
713 b.create<sv::MacroDeclOp>("FIRRTL_AFTER_INITIAL");
714 }
715 if (needsMemRandomization)
716 b.create<sv::MacroDeclOp>("RANDOMIZE_MEM_INIT");
717 b.create<sv::MacroDeclOp>("RANDOMIZE_REG_INIT");
718 b.create<sv::MacroDeclOp>("RANDOMIZE");
719 b.create<sv::MacroDeclOp>("RANDOMIZE_DELAY");
720 b.create<sv::MacroDeclOp>("RANDOM");
721 b.create<sv::MacroDeclOp>("INIT_RANDOM");
722 b.create<sv::MacroDeclOp>("INIT_RANDOM_PROLOG_");
723 }
724
725 bool hasRegRandomization = needsRegRandomization && !disableRegRandomization;
726 bool hasMemRandomization = needsMemRandomization && !disableMemRandomization;
727 if (!hasRegRandomization && !hasMemRandomization)
728 return;
729
730 // Build macros for FIRRTL-style register and memory initialization.
731 // Insert them at the start of the module, after any other verbatims.
732 for (Operation &op : *circuit.getBody()) {
733 if (!isa<sv::VerbatimOp, sv::IfDefOp>(&op)) {
734 b.setInsertionPoint(&op);
735 break;
736 }
737 }
738
739 // Create SYNTHESIS/VERILATOR macros if other passes have not done so already.
740 {
741 StringSet<> symbols;
742 for (auto sym : circuit.getOps<sv::MacroDeclOp>())
743 symbols.insert(sym.getName());
744 if (!symbols.count("SYNTHESIS"))
745 b.create<sv::MacroDeclOp>("SYNTHESIS");
746 if (!symbols.count("VERILATOR"))
747 b.create<sv::MacroDeclOp>("VERILATOR");
748 }
749
750 // TODO: We could have an operation for macros and uses of them, and
751 // even turn them into symbols so we can DCE unused macro definitions.
752 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
753 StringRef defineTrue = "",
754 StringRef defineFalse = StringRef()) {
755 if (!defineFalse.data()) {
756 assert(defineTrue.data() && "didn't define anything");
757 b.create<sv::IfDefOp>(
758 guard, [&]() { b.create<sv::MacroDefOp>(defName, defineTrue); });
759 } else {
760 b.create<sv::IfDefOp>(
761 guard,
762 [&]() {
763 if (defineTrue.data())
764 b.create<sv::MacroDefOp>(defName, defineTrue);
765 },
766 [&]() { b.create<sv::MacroDefOp>(defName, defineFalse); });
767 }
768 };
769
770 // Helper function to emit #ifndef guard.
771 auto emitGuard = [&](const char *guard, llvm::function_ref<void(void)> body) {
772 b.create<sv::IfDefOp>(
773 guard, []() {}, body);
774 };
775
776 b.create<emit::FragmentOp>(randomInitFragmentName.getAttr(), [&] {
777 b.create<sv::VerbatimOp>(
778 "// Standard header to adapt well known macros for "
779 "register randomization.");
780
781 b.create<sv::VerbatimOp>(
782 "\n// RANDOM may be set to an expression that produces a 32-bit "
783 "random unsigned value.");
784 emitGuardedDefine("RANDOM", "RANDOM", StringRef(), "$random");
785
786 b.create<sv::VerbatimOp>(
787 "\n// Users can define INIT_RANDOM as general code that gets "
788 "injected "
789 "into the\n// initializer block for modules with registers.");
790 emitGuardedDefine("INIT_RANDOM", "INIT_RANDOM", StringRef(), "");
791
792 b.create<sv::VerbatimOp>(
793 "\n// If using random initialization, you can also define "
794 "RANDOMIZE_DELAY to\n// customize the delay used, otherwise 0.002 "
795 "is used.");
796 emitGuardedDefine("RANDOMIZE_DELAY", "RANDOMIZE_DELAY", StringRef(),
797 "0.002");
798
799 b.create<sv::VerbatimOp>(
800 "\n// Define INIT_RANDOM_PROLOG_ for use in our modules below.");
801 emitGuard("INIT_RANDOM_PROLOG_", [&]() {
802 b.create<sv::IfDefOp>(
803 "RANDOMIZE",
804 [&]() {
805 emitGuardedDefine("VERILATOR", "INIT_RANDOM_PROLOG_",
806 "`INIT_RANDOM",
807 "`INIT_RANDOM #`RANDOMIZE_DELAY begin end");
808 },
809 [&]() { b.create<sv::MacroDefOp>("INIT_RANDOM_PROLOG_", ""); });
810 });
811 });
812
813 if (hasMemRandomization) {
814 b.create<emit::FragmentOp>(randomInitMemFragmentName.getAttr(), [&] {
815 b.create<sv::VerbatimOp>("\n// Include rmemory initializers in init "
816 "blocks unless synthesis is set");
817 emitGuard("RANDOMIZE", [&]() {
818 emitGuardedDefine("RANDOMIZE_MEM_INIT", "RANDOMIZE");
819 });
820 emitGuard("SYNTHESIS", [&] {
821 emitGuardedDefine("ENABLE_INITIAL_MEM_", "ENABLE_INITIAL_MEM_",
822 StringRef(), "");
823 });
824 b.create<sv::VerbatimOp>("");
825 });
826 }
827
828 if (hasRegRandomization) {
829 b.create<emit::FragmentOp>(randomInitRegFragmentName.getAttr(), [&] {
830 b.create<sv::VerbatimOp>("\n// Include register initializers in init "
831 "blocks unless synthesis is set");
832 emitGuard("RANDOMIZE", [&]() {
833 emitGuardedDefine("RANDOMIZE_REG_INIT", "RANDOMIZE");
834 });
835 emitGuard("SYNTHESIS", [&] {
836 emitGuardedDefine("ENABLE_INITIAL_REG_", "ENABLE_INITIAL_REG_",
837 StringRef(), "");
838 });
839 b.create<sv::VerbatimOp>("");
840 });
841 }
842}
843
844std::unique_ptr<Pass>
845circt::createLowerSeqToSVPass(const LowerSeqToSVOptions &options) {
846 return std::make_unique<SeqToSVPass>(options);
847}
assert(baseType &&"element must be base type")
static bool isLegalOp(Operation *op)
Returns true if the given op is considered as legal - i.e.
Definition DCToHW.cpp:844
static FIRRTLBaseType convertType(FIRRTLBaseType type)
Returns null type if no conversion is needed.
Definition DropConst.cpp:32
static bool isLegalType(Type ty)
Definition SeqToSV.cpp:547
static bool isLegalOp(Operation *op)
Definition SeqToSV.cpp:565
FIR memory lowering helper.
UniqueConfigs collectMemories(ArrayRef< hw::HWModuleOp > modules)
Groups memories by their kind from the whole design.
void lowerMemoriesInModule(hw::HWModuleOp module, ArrayRef< MemoryConfig > mems)
Lowers a group of memories from the same module.
hw::HWModuleGeneratedOp createMemoryModule(FirMemConfig &mem, ArrayRef< seq::FirMemOp > memOps)
Creates the generated module for a given configuration.
Lower FirRegOp to sv.reg and sv.always.
bool needsRegRandomization() const
create(data_type, value)
Definition hw.py:433
create(value)
Definition sv.py:106
Definition sv.py:68
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
Definition EmitOps.h:32
FailureOr< seq::InitialOp > mergeInitialOps(Block *block)
Definition SeqOps.cpp:1093
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.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createLowerSeqToSVPass(const LowerSeqToSVOptions &options={})
Definition SeqToSV.cpp:845
StringRef chooseName(StringRef a, StringRef b)
Choose a good name for an item from two options.
Definition Naming.cpp:47
Definition seq.py:1
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition seq.py:21
void runOnOperation() override
Definition SeqToSV.cpp:589
Generic pattern which replaces an operation by one of the same operation name, but with converted att...