CIRCT  20.0.0git
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"
20 #include "circt/Dialect/HW/HWOps.h"
23 #include "circt/Dialect/SV/SVOps.h"
25 #include "circt/Support/Naming.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 
39 using namespace circt;
40 using namespace seq;
41 using hw::HWModuleOp;
42 using llvm::MapVector;
43 
44 namespace circt {
45 #define GEN_PASS_DEF_LOWERSEQTOSV
46 #include "circt/Conversion/Passes.h.inc"
47 
48 struct 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 
60 namespace {
61 struct 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 
96 LogicalResult 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 
108 LogicalResult
109 ModuleLoweringState::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.
145 template <typename OpTy>
146 class CompRegLower : public OpConversionPattern<OpTy> {
147 public:
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 
238 private:
239  bool lowerToAlwaysFF;
240  const MapVector<StringAttr, ModuleLoweringState> &moduleLoweringStates;
241 };
242 
243 /// Create the assign.
244 template <>
245 void 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.
251 template <>
252 void 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`.
261 class FromImmutableLowering : public OpConversionPattern<FromImmutableOp> {
262 public:
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 
300 private:
301  const MapVector<StringAttr, ModuleLoweringState> &moduleLoweringStates;
302 };
303 // Lower seq.clock_gate to a fairly standard clock gate implementation.
304 //
305 class ClockGateLowering : public OpConversionPattern<ClockGateOp> {
306 public:
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 //
343 class ClockInverterLowering : public OpConversionPattern<ClockInverterOp> {
344 public:
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 //
365 class ClockMuxLowering : public OpConversionPattern<ClockMuxOp> {
366 public:
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`.
381 struct 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.
436 template <typename T>
437 class ClockCastLowering : public OpConversionPattern<T> {
438 public:
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 //
458 class ClockConstLowering : public OpConversionPattern<ConstClockOp> {
459 public:
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 
472 class AggregateConstantPattern
473  : public OpConversionPattern<hw::AggregateConstantOp> {
474 public:
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 ///
496 class ClockDividerLowering : public OpConversionPattern<ClockDividerOp> {
497 public:
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)
547 static 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 
565 static 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 
844 std::unique_ptr<Pass>
845 circt::createLowerSeqToSVPass(const LowerSeqToSVOptions &options) {
846  return std::make_unique<SeqToSVPass>(options);
847 }
assert(baseType &&"element must be base type")
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
def create(data_type, value)
Definition: hw.py:433
Definition: sv.py:15
def create(value)
Definition: sv.py:106
Definition: sv.py:68
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
Definition: CombOps.cpp:48
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.
Definition: DebugAnalysis.h:21
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
def 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...