24#include "mlir/IR/Builders.h"
25#include "mlir/Pass/Pass.h"
29#define GEN_PASS_DEF_HWLEGALIZEMODULES
30#include "circt/Dialect/SV/SVPasses.h.inc"
40struct HWLegalizeModulesPass
41 :
public circt::sv::impl::HWLegalizeModulesBase<HWLegalizeModulesPass> {
42 void runOnOperation()
override;
45 void processPostOrder(
Block &block);
46 bool tryLoweringPackedArrayOp(Operation &op);
47 template <
typename ElementType>
48 SmallVector<std::pair<Value, Value>>
49 createIndexValuePairs(OpBuilder &builder, LocationAttr loc, hw::ArrayType ty,
51 Value lowerLookupToCasez(Operation &op, Value input, Value index,
53 SmallVector<Value> caseValues);
54 bool processUsers(Operation &op, Value value, ArrayRef<Value> mapping);
55 std::optional<std::pair<uint64_t, unsigned>>
56 tryExtractIndexAndBitWidth(Value value);
57 bool tryLoweringClockedAssertLike(Operation &op);
71 StringAttr lastParsedOptions;
75bool HWLegalizeModulesPass::tryLoweringPackedArrayOp(Operation &op) {
76 return TypeSwitch<Operation *, bool>(&op)
77 .Case<hw::AggregateConstantOp>([&](hw::AggregateConstantOp constOp) {
79 SmallVector<Value> inputs;
80 OpBuilder builder(constOp);
81 for (
auto field :
llvm::reverse(constOp.getFields())) {
82 if (
auto intAttr = dyn_cast<IntegerAttr>(field))
86 inputs.push_back(hw::AggregateConstantOp::create(
87 builder, constOp.getLoc(), constOp.getType(),
88 cast<ArrayAttr>(field)));
90 if (!processUsers(op, constOp.getResult(), inputs))
98 SmallVector<Value> values;
99 OpBuilder builder(concatOp);
100 for (
auto array :
llvm::reverse(concatOp.getInputs())) {
101 auto ty = hw::type_cast<hw::ArrayType>(array.getType());
102 const auto indexValues = createIndexValuePairs<hw::ArrayGetOp>(
103 builder, concatOp.getLoc(), ty, array);
104 for (
const auto &[_, value] : indexValues) {
105 values.push_back(value);
108 if (!processUsers(op, concatOp.getResult(), values))
116 SmallVector<Value> inputs(llvm::reverse(createOp.getInputs()));
117 if (!processUsers(op, createOp.getResult(), inputs))
125 auto index = getOp.getIndex();
126 if (
auto *definingOp = index.getDefiningOp())
127 if (isa<hw::ConstantOp>(definingOp))
131 auto ty = hw::type_cast<hw::ArrayType>(getOp.getInput().getType());
132 OpBuilder builder(getOp);
133 auto loc = op.getLoc();
134 const auto indexValues = createIndexValuePairs<hw::ArrayGetOp>(
135 builder, loc, ty, getOp.getInput());
136 SmallVector<Value> caseValues;
137 for (
const auto &[_, value] : indexValues)
138 caseValues.push_back(value);
141 auto theWire = lowerLookupToCasez(op, getOp.getInput(), index,
142 ty.getElementType(), caseValues);
145 builder.setInsertionPoint(getOp);
148 getOp.getResult().replaceAllUsesWith(readWire);
151 .Case<sv::ArrayIndexInOutOp>([&](sv::ArrayIndexInOutOp indexOp) {
153 auto index = indexOp.getIndex();
154 if (
auto *definingOp = index.getDefiningOp())
155 if (isa<hw::ConstantOp>(definingOp))
159 auto inout = indexOp.getInput().getType();
160 if (hw::type_isa<hw::UnpackedArrayType>(inout.getElementType()))
164 auto ty = hw::type_cast<hw::ArrayType>(inout.getElementType());
165 OpBuilder builder(&op);
166 auto loc = op.getLoc();
167 const auto indexValues = createIndexValuePairs<sv::ArrayIndexInOutOp>(
168 builder, loc, ty, indexOp.getInput());
169 SmallVector<Value> caseValues;
170 for (
const auto &[_, element] : indexValues) {
172 caseValues.push_back(readElement);
176 auto theWire = lowerLookupToCasez(op, indexOp.getInput(), index,
177 ty.getElementType(), caseValues);
180 indexOp.getResult().replaceAllUsesWith(theWire);
183 .Case<sv::PAssignOp>([&](sv::PAssignOp assignOp) {
186 auto inout = assignOp.getDest().getType();
187 auto ty = hw::type_dyn_cast<hw::ArrayType>(inout.getElementType());
191 OpBuilder builder(assignOp);
192 auto loc = op.getLoc();
193 const auto indexValues = createIndexValuePairs<hw::ArrayGetOp>(
194 builder, loc, ty, assignOp.getSrc());
195 for (
const auto &[index, srcElement] : indexValues) {
196 auto dstElement = sv::ArrayIndexInOutOp::create(
197 builder, loc, assignOp.getDest(), index);
198 sv::PAssignOp::create(builder, loc, dstElement, srcElement);
206 auto ty = hw::type_dyn_cast<hw::ArrayType>(regOp.getElementType());
210 OpBuilder builder(regOp);
211 auto name = StringAttr::get(regOp.getContext(),
"name");
212 SmallVector<Value> elements;
213 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
214 auto loc = op.getLoc();
215 auto element = sv::RegOp::create(builder, loc, ty.getElementType());
216 if (
auto nameAttr = regOp->getAttrOfType<StringAttr>(name)) {
218 StringAttr::get(regOp.getContext(), nameAttr.getValue()));
220 elements.push_back(element);
224 if (!processUsers(op, regOp.getResult(), elements))
232 auto ty = hw::type_dyn_cast<hw::ArrayType>(muxOp.getType());
236 OpBuilder builder(muxOp);
238 auto trueValues = createIndexValuePairs<hw::ArrayGetOp>(
239 builder, muxOp.getLoc(), ty, muxOp.getTrueValue());
240 auto falseValues = createIndexValuePairs<hw::ArrayGetOp>(
241 builder, muxOp.getLoc(), ty, muxOp.getFalseValue());
243 SmallVector<Value> muxedValues;
245 for (
size_t i = 0, e = trueValues.size(); i < e; i++) {
246 const auto &[trueIndex, trueValue] = trueValues[i];
247 const auto &[falseIndex, falseValue] = falseValues[i];
248 muxedValues.push_back(
249 comb::MuxOp::create(builder, muxOp.getLoc(), muxOp.getCond(),
250 trueValue, falseValue, muxOp.getTwoState()));
253 if (!processUsers(op, muxOp.getResult(), muxedValues))
259 .Default([&](
auto op) {
return false; });
262template <
typename ElementType>
263SmallVector<std::pair<Value, Value>>
264HWLegalizeModulesPass::createIndexValuePairs(OpBuilder &builder,
265 LocationAttr loc, hw::ArrayType ty,
267 SmallVector<std::pair<Value, Value>> result;
268 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
270 loc, APInt(llvm::Log2_64_Ceil(e), i));
271 auto element = ElementType::create(builder, loc, array, index);
272 result.emplace_back(index, element);
277Value HWLegalizeModulesPass::lowerLookupToCasez(Operation &op, Value input,
280 SmallVector<Value> caseValues) {
283 OpBuilder builder(&op);
284 auto theWire = sv::RegOp::create(builder, op.getLoc(),
elementType,
285 builder.getStringAttr(
"casez_tmp"));
286 builder.setInsertionPoint(&op);
288 auto loc = input.getLoc();
293 auto alwaysComb = sv::AlwaysCombOp::create(builder, loc);
294 builder.setInsertionPointToEnd(alwaysComb.getBodyBlock());
299 if (1ULL << index.getType().getIntOrFloatBitWidth() != caseValues.size()) {
300 caseValues.push_back(sv::ConstantXOp::create(builder, op.getLoc(),
301 op.getResult(0).getType()));
304 APInt caseValue(index.getType().getIntOrFloatBitWidth(), 0);
305 auto *
context = builder.getContext();
309 builder, loc, CaseStmtType::CaseZStmt, index, caseValues.size(),
310 [&](
size_t caseIdx) -> std::unique_ptr<sv::CasePattern> {
315 bool isDefault = caseIdx == caseValues.size() - 1;
316 Value theValue = caseValues[caseIdx];
317 std::unique_ptr<sv::CasePattern> thePattern;
320 thePattern = std::make_unique<sv::CaseDefaultPattern>(context);
322 thePattern = std::make_unique<sv::CaseBitPattern>(caseValue, context);
324 sv::BPAssignOp::create(builder, loc, theWire, theValue);
331bool HWLegalizeModulesPass::processUsers(Operation &op, Value value,
332 ArrayRef<Value> mapping) {
333 for (
auto *user :
llvm::make_early_inc_range(value.getUsers())) {
334 if (TypeSwitch<Operation *, bool>(user)
336 if (
auto indexAndBitWidth =
337 tryExtractIndexAndBitWidth(getOp.getIndex())) {
338 getOp.replaceAllUsesWith(mapping[indexAndBitWidth->first]);
344 .Case<sv::ArrayIndexInOutOp>([&](sv::ArrayIndexInOutOp indexOp) {
345 if (
auto indexAndBitWidth =
346 tryExtractIndexAndBitWidth(indexOp.getIndex())) {
347 indexOp.replaceAllUsesWith(mapping[indexAndBitWidth->first]);
353 .Default([](
auto op) {
return false; })) {
358 user->emitError(
"unsupported packed array expression");
366std::optional<std::pair<uint64_t, unsigned>>
367HWLegalizeModulesPass::tryExtractIndexAndBitWidth(Value value) {
368 if (
auto constantOp = dyn_cast<hw::ConstantOp>(value.getDefiningOp())) {
369 auto index = constantOp.getValue();
370 return std::make_optional(
371 std::make_pair(index.getZExtValue(), index.getBitWidth()));
377template <
typename Op>
378bool tryLoweringClockedAssertLike(Op &op) {
379 auto event = op.getEvent();
380 if (!event.has_value())
383 OpBuilder builder(op);
385 sv::AlwaysOp::create(builder, op->getLoc(), *event, op.getClock(), [&] {
386 Op::create(builder, op.getLoc(), op.getProperty(), op.getDisable(),
393bool HWLegalizeModulesPass::tryLoweringClockedAssertLike(Operation &op) {
394 return TypeSwitch<Operation *, bool>(&op)
395 .Case<sv::AssertPropertyOp>(
396 ::tryLoweringClockedAssertLike<sv::AssertPropertyOp>)
397 .Case<sv::AssumePropertyOp>(
398 ::tryLoweringClockedAssertLike<sv::AssumePropertyOp>)
399 .Case<sv::CoverPropertyOp>(
400 ::tryLoweringClockedAssertLike<sv::CoverPropertyOp>)
401 .Default([&](
auto op) {
return false; });
404void HWLegalizeModulesPass::processPostOrder(Block &body) {
409 Block::iterator it = std::prev(body.end());
410 while (it != body.end()) {
415 if (it == body.begin())
420 if (op.getNumRegions()) {
421 for (
auto ®ion : op.getRegions())
422 for (auto ®ionBlock : region.getBlocks())
423 processPostOrder(regionBlock);
426 if (options.disallowPackedArrays) {
428 if (tryLoweringPackedArrayOp(op)) {
429 it = --Block::iterator(op);
431 anythingChanged =
true;
437 for (
auto value : op.getResults()) {
438 if (isa<hw::ArrayType>(value.getType())) {
439 op.emitError(
"unsupported packed array expression");
445 if (options.disallowClockedAssertions) {
446 if (tryLoweringClockedAssertLike(op)) {
448 anythingChanged =
true;
455void HWLegalizeModulesPass::runOnOperation() {
456 thisHWModule = getOperation();
460 cast<ModuleOp>(thisHWModule->getParentOp()));
461 if (optionsAttr != lastParsedOptions) {
464 thisHWModule.emitError(error);
468 lastParsedOptions = optionsAttr;
473 anythingChanged =
false;
476 processPostOrder(*thisHWModule.getBodyBlock());
479 if (!anythingChanged)
480 markAllAnalysesPreserved();
static std::unique_ptr< Context > context
Signals that an operation's regions are procedural.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Options which control the emission from CIRCT to Verilog.
static mlir::StringAttr getAttributeFrom(mlir::ModuleOp module)
Return the value of the circt.loweringOptions in the specified module if present, or a null attribute...