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);
60 hw::HWModuleOp thisHWModule;
66 LoweringOptions options;
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))
96 .Case<hw::ArrayConcatOp>([&](hw::ArrayConcatOp concatOp) {
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))
114 .Case<hw::ArrayCreateOp>([&](hw::ArrayCreateOp createOp) {
116 SmallVector<Value> inputs(llvm::reverse(createOp.getInputs()));
117 if (!processUsers(op, createOp.getResult(), inputs))
123 .Case<hw::ArrayGetOp>([&](hw::ArrayGetOp getOp) {
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 handle = indexOp.getInput().getType();
160 Type
elementType = sv::getHandleElementType(handle);
161 if (hw::type_isa<hw::UnpackedArrayType>(
elementType))
165 auto ty = hw::type_cast<hw::ArrayType>(
elementType);
166 OpBuilder builder(&op);
167 auto loc = op.getLoc();
168 const auto indexValues = createIndexValuePairs<sv::ArrayIndexInOutOp>(
169 builder, loc, ty, indexOp.getInput());
170 SmallVector<Value> caseValues;
171 for (
const auto &[_, element] : indexValues) {
173 caseValues.push_back(readElement);
177 auto theWire = lowerLookupToCasez(op, indexOp.getInput(), index,
178 ty.getElementType(), caseValues);
181 indexOp.getResult().replaceAllUsesWith(theWire);
184 .Case<sv::PAssignOp>([&](sv::PAssignOp assignOp) {
187 auto destElementType =
188 sv::getHandleElementType(assignOp.getDest().getType());
189 auto ty = hw::type_dyn_cast<hw::ArrayType>(destElementType);
193 OpBuilder builder(assignOp);
194 auto loc = op.getLoc();
195 const auto indexValues = createIndexValuePairs<hw::ArrayGetOp>(
196 builder, loc, ty, assignOp.getSrc());
197 for (
const auto &[index, srcElement] : indexValues) {
198 auto dstElement = sv::ArrayIndexInOutOp::create(
199 builder, loc, assignOp.getDest(), index);
200 sv::PAssignOp::create(builder, loc, dstElement, srcElement);
206 .Case<sv::RegOp>([&](sv::RegOp regOp) {
208 auto ty = hw::type_dyn_cast<hw::ArrayType>(regOp.getElementType());
212 OpBuilder builder(regOp);
213 auto name = StringAttr::get(regOp.getContext(),
"name");
214 SmallVector<Value> elements;
215 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
216 auto loc = op.getLoc();
217 auto element = sv::RegOp::create(builder, loc, ty.getElementType());
218 if (
auto nameAttr = regOp->getAttrOfType<StringAttr>(name)) {
220 StringAttr::get(regOp.getContext(), nameAttr.getValue()));
222 elements.push_back(element);
226 if (!processUsers(op, regOp.getResult(), elements))
232 .Case<comb::MuxOp>([&](comb::MuxOp muxOp) {
234 auto ty = hw::type_dyn_cast<hw::ArrayType>(muxOp.getType());
238 OpBuilder builder(muxOp);
240 auto trueValues = createIndexValuePairs<hw::ArrayGetOp>(
241 builder, muxOp.getLoc(), ty, muxOp.getTrueValue());
242 auto falseValues = createIndexValuePairs<hw::ArrayGetOp>(
243 builder, muxOp.getLoc(), ty, muxOp.getFalseValue());
245 SmallVector<Value> muxedValues;
247 for (
size_t i = 0, e = trueValues.size(); i < e; i++) {
248 const auto &[trueIndex, trueValue] = trueValues[i];
249 const auto &[falseIndex, falseValue] = falseValues[i];
250 muxedValues.push_back(
251 comb::MuxOp::create(builder, muxOp.getLoc(), muxOp.getCond(),
252 trueValue, falseValue, muxOp.getTwoState()));
255 if (!processUsers(op, muxOp.getResult(), muxedValues))
261 .Default([&](
auto op) {
return false; });
264template <
typename ElementType>
265SmallVector<std::pair<Value, Value>>
266HWLegalizeModulesPass::createIndexValuePairs(OpBuilder &builder,
267 LocationAttr loc, hw::ArrayType ty,
269 SmallVector<std::pair<Value, Value>> result;
270 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
271 auto index = builder.createOrFold<hw::ConstantOp>(
272 loc, APInt(llvm::Log2_64_Ceil(e), i));
273 auto element = ElementType::create(builder, loc, array, index);
274 result.emplace_back(index, element);
279Value HWLegalizeModulesPass::lowerLookupToCasez(Operation &op, Value input,
282 SmallVector<Value> caseValues) {
285 OpBuilder builder(&op);
286 auto theWire = sv::RegOp::create(builder, op.getLoc(),
elementType,
287 builder.getStringAttr(
"casez_tmp"));
288 builder.setInsertionPoint(&op);
290 auto loc = input.getLoc();
294 if (!op.getParentOp()->hasTrait<ProceduralRegion>()) {
295 auto alwaysComb = sv::AlwaysCombOp::create(builder, loc);
296 builder.setInsertionPointToEnd(alwaysComb.getBodyBlock());
301 if (1ULL << index.getType().getIntOrFloatBitWidth() != caseValues.size()) {
302 caseValues.push_back(sv::ConstantXOp::create(builder, op.getLoc(),
303 op.getResult(0).getType()));
306 APInt caseValue(index.getType().getIntOrFloatBitWidth(), 0);
307 auto *
context = builder.getContext();
311 builder, loc, CaseStmtType::CaseZStmt, index, caseValues.size(),
312 [&](
size_t caseIdx) -> std::unique_ptr<sv::CasePattern> {
317 bool isDefault = caseIdx == caseValues.size() - 1;
318 Value theValue = caseValues[caseIdx];
319 std::unique_ptr<sv::CasePattern> thePattern;
322 thePattern = std::make_unique<sv::CaseDefaultPattern>(context);
324 thePattern = std::make_unique<sv::CaseBitPattern>(caseValue, context);
326 sv::BPAssignOp::create(builder, loc, theWire, theValue);
333bool HWLegalizeModulesPass::processUsers(Operation &op, Value value,
334 ArrayRef<Value> mapping) {
335 for (
auto *user : llvm::make_early_inc_range(value.getUsers())) {
336 if (TypeSwitch<Operation *, bool>(user)
337 .Case<hw::ArrayGetOp>([&](hw::ArrayGetOp getOp) {
338 if (
auto indexAndBitWidth =
339 tryExtractIndexAndBitWidth(getOp.getIndex())) {
340 getOp.replaceAllUsesWith(mapping[indexAndBitWidth->first]);
346 .Case<sv::ArrayIndexInOutOp>([&](sv::ArrayIndexInOutOp indexOp) {
347 if (
auto indexAndBitWidth =
348 tryExtractIndexAndBitWidth(indexOp.getIndex())) {
349 indexOp.replaceAllUsesWith(mapping[indexAndBitWidth->first]);
355 .Default([](
auto op) {
return false; })) {
360 user->emitError(
"unsupported packed array expression");
368std::optional<std::pair<uint64_t, unsigned>>
369HWLegalizeModulesPass::tryExtractIndexAndBitWidth(Value value) {
370 if (
auto constantOp = dyn_cast<hw::ConstantOp>(value.getDefiningOp())) {
371 auto index = constantOp.getValue();
372 return std::make_optional(
373 std::make_pair(index.getZExtValue(), index.getBitWidth()));
379template <
typename Op>
380bool tryLoweringClockedAssertLike(Op &op) {
381 auto event = op.getEvent();
382 if (!event.has_value())
385 OpBuilder builder(op);
387 sv::AlwaysOp::create(builder, op->getLoc(), *event, op.getClock(), [&] {
388 Op::create(builder, op.getLoc(), op.getProperty(), op.getDisable(),
395bool HWLegalizeModulesPass::tryLoweringClockedAssertLike(Operation &op) {
396 return TypeSwitch<Operation *, bool>(&op)
397 .Case<sv::AssertPropertyOp>(
398 ::tryLoweringClockedAssertLike<sv::AssertPropertyOp>)
399 .Case<sv::AssumePropertyOp>(
400 ::tryLoweringClockedAssertLike<sv::AssumePropertyOp>)
401 .Case<sv::CoverPropertyOp>(
402 ::tryLoweringClockedAssertLike<sv::CoverPropertyOp>)
403 .Default([&](
auto op) {
return false; });
406void HWLegalizeModulesPass::processPostOrder(Block &body) {
411 Block::iterator it = std::prev(body.end());
412 while (it != body.end()) {
417 if (it == body.begin())
422 if (op.getNumRegions()) {
423 for (
auto ®ion : op.getRegions())
424 for (
auto ®ionBlock : region.getBlocks())
425 processPostOrder(regionBlock);
430 if (tryLoweringPackedArrayOp(op)) {
431 it = --Block::iterator(op);
433 anythingChanged =
true;
439 for (
auto value : op.getResults()) {
440 if (isa<hw::ArrayType>(value.getType())) {
441 op.emitError(
"unsupported packed array expression");
448 if (tryLoweringClockedAssertLike(op)) {
450 anythingChanged =
true;
457void HWLegalizeModulesPass::runOnOperation() {
458 thisHWModule = getOperation();
462 cast<ModuleOp>(thisHWModule->getParentOp()));
463 if (optionsAttr != lastParsedOptions) {
465 options = LoweringOptions(optionsAttr.getValue(), [&](Twine error) {
466 thisHWModule.emitError(error);
469 options = LoweringOptions();
470 lastParsedOptions = optionsAttr;
475 anythingChanged =
false;
478 processPostOrder(*thisHWModule.getBodyBlock());
481 if (!anythingChanged)
482 markAllAnalysesPreserved();
static std::unique_ptr< Context > context
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
bool disallowPackedArrays
If true, eliminate packed arrays for tools that don't support them (e.g.
static mlir::StringAttr getAttributeFrom(mlir::ModuleOp module)
Return the value of the circt.loweringOptions in the specified module if present, or a null attribute...
bool disallowClockedAssertions