23#include "mlir/IR/Builders.h"
24#include "mlir/Pass/Pass.h"
28#define GEN_PASS_DEF_HWLEGALIZEMODULES
29#include "circt/Dialect/SV/SVPasses.h.inc"
39struct HWLegalizeModulesPass
40 :
public circt::sv::impl::HWLegalizeModulesBase<HWLegalizeModulesPass> {
41 void runOnOperation()
override;
44 void processPostOrder(
Block &block);
45 bool tryLoweringPackedArrayOp(Operation &op);
46 template <
typename ElementType>
47 SmallVector<std::pair<Value, Value>>
48 createIndexValuePairs(OpBuilder &builder, LocationAttr loc, hw::ArrayType ty,
50 Value lowerLookupToCasez(Operation &op, Value input, Value index,
52 SmallVector<Value> caseValues);
53 bool processUsers(Operation &op, Value value, ArrayRef<Value> mapping);
54 std::optional<std::pair<uint64_t, unsigned>>
55 tryExtractIndexAndBitWidth(Value value);
56 bool tryLoweringClockedAssertLike(Operation &op);
70 StringAttr lastParsedOptions;
74bool HWLegalizeModulesPass::tryLoweringPackedArrayOp(Operation &op) {
75 return TypeSwitch<Operation *, bool>(&op)
76 .Case<hw::AggregateConstantOp>([&](hw::AggregateConstantOp constOp) {
78 SmallVector<Value> inputs;
79 OpBuilder builder(constOp);
80 for (
auto field :
llvm::reverse(constOp.getFields())) {
81 if (
auto intAttr = dyn_cast<IntegerAttr>(field))
85 inputs.push_back(hw::AggregateConstantOp::create(
86 builder, constOp.getLoc(), constOp.getType(),
87 cast<ArrayAttr>(field)));
89 if (!processUsers(op, constOp.getResult(), inputs))
97 SmallVector<Value> values;
98 OpBuilder builder(concatOp);
99 for (
auto array :
llvm::reverse(concatOp.getInputs())) {
100 auto ty = hw::type_cast<hw::ArrayType>(array.getType());
101 const auto indexValues = createIndexValuePairs<hw::ArrayGetOp>(
102 builder, concatOp.getLoc(), ty, array);
103 for (
const auto &[_, value] : indexValues) {
104 values.push_back(value);
107 if (!processUsers(op, concatOp.getResult(), values))
115 SmallVector<Value> inputs(llvm::reverse(createOp.getInputs()));
116 if (!processUsers(op, createOp.getResult(), inputs))
124 auto index = getOp.getIndex();
125 if (
auto *definingOp = index.getDefiningOp())
126 if (isa<hw::ConstantOp>(definingOp))
130 auto ty = hw::type_cast<hw::ArrayType>(getOp.getInput().getType());
131 OpBuilder builder(getOp);
132 auto loc = op.getLoc();
133 const auto indexValues = createIndexValuePairs<hw::ArrayGetOp>(
134 builder, loc, ty, getOp.getInput());
135 SmallVector<Value> caseValues;
136 for (
const auto &[_, value] : indexValues)
137 caseValues.push_back(value);
140 auto theWire = lowerLookupToCasez(op, getOp.getInput(), index,
141 ty.getElementType(), caseValues);
144 builder.setInsertionPoint(getOp);
147 getOp.getResult().replaceAllUsesWith(readWire);
150 .Case<sv::ArrayIndexInOutOp>([&](sv::ArrayIndexInOutOp indexOp) {
152 auto index = indexOp.getIndex();
153 if (
auto *definingOp = index.getDefiningOp())
154 if (isa<hw::ConstantOp>(definingOp))
158 auto inout = indexOp.getInput().getType();
159 if (hw::type_isa<hw::UnpackedArrayType>(inout.getElementType()))
163 auto ty = hw::type_cast<hw::ArrayType>(inout.getElementType());
164 OpBuilder builder(&op);
165 auto loc = op.getLoc();
166 const auto indexValues = createIndexValuePairs<sv::ArrayIndexInOutOp>(
167 builder, loc, ty, indexOp.getInput());
168 SmallVector<Value> caseValues;
169 for (
const auto &[_, element] : indexValues) {
171 caseValues.push_back(readElement);
175 auto theWire = lowerLookupToCasez(op, indexOp.getInput(), index,
176 ty.getElementType(), caseValues);
179 indexOp.getResult().replaceAllUsesWith(theWire);
182 .Case<sv::PAssignOp>([&](sv::PAssignOp assignOp) {
185 auto inout = assignOp.getDest().getType();
186 auto ty = hw::type_dyn_cast<hw::ArrayType>(inout.getElementType());
190 OpBuilder builder(assignOp);
191 auto loc = op.getLoc();
192 const auto indexValues = createIndexValuePairs<hw::ArrayGetOp>(
193 builder, loc, ty, assignOp.getSrc());
194 for (
const auto &[index, srcElement] : indexValues) {
195 auto dstElement = sv::ArrayIndexInOutOp::create(
196 builder, loc, assignOp.getDest(), index);
197 sv::PAssignOp::create(builder, loc, dstElement, srcElement);
205 auto ty = hw::type_dyn_cast<hw::ArrayType>(regOp.getElementType());
209 OpBuilder builder(regOp);
210 auto name = StringAttr::get(regOp.getContext(),
"name");
211 SmallVector<Value> elements;
212 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
213 auto loc = op.getLoc();
214 auto element = sv::RegOp::create(builder, loc, ty.getElementType());
215 if (
auto nameAttr = regOp->getAttrOfType<StringAttr>(name)) {
217 StringAttr::get(regOp.getContext(), nameAttr.getValue()));
219 elements.push_back(element);
223 if (!processUsers(op, regOp.getResult(), elements))
231 auto ty = hw::type_dyn_cast<hw::ArrayType>(muxOp.getType());
235 OpBuilder builder(muxOp);
237 auto trueValues = createIndexValuePairs<hw::ArrayGetOp>(
238 builder, muxOp.getLoc(), ty, muxOp.getTrueValue());
239 auto falseValues = createIndexValuePairs<hw::ArrayGetOp>(
240 builder, muxOp.getLoc(), ty, muxOp.getFalseValue());
242 SmallVector<Value> muxedValues;
244 for (
size_t i = 0, e = trueValues.size(); i < e; i++) {
245 const auto &[trueIndex, trueValue] = trueValues[i];
246 const auto &[falseIndex, falseValue] = falseValues[i];
247 muxedValues.push_back(
248 comb::MuxOp::create(builder, muxOp.getLoc(), muxOp.getCond(),
249 trueValue, falseValue, muxOp.getTwoState()));
252 if (!processUsers(op, muxOp.getResult(), muxedValues))
258 .Default([&](
auto op) {
return false; });
261template <
typename ElementType>
262SmallVector<std::pair<Value, Value>>
263HWLegalizeModulesPass::createIndexValuePairs(OpBuilder &builder,
264 LocationAttr loc, hw::ArrayType ty,
266 SmallVector<std::pair<Value, Value>> result;
267 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
269 loc, APInt(llvm::Log2_64_Ceil(e), i));
270 auto element = ElementType::create(builder, loc, array, index);
271 result.emplace_back(index, element);
276Value HWLegalizeModulesPass::lowerLookupToCasez(Operation &op, Value input,
279 SmallVector<Value> caseValues) {
282 OpBuilder builder(&op);
283 auto theWire = sv::RegOp::create(builder, op.getLoc(),
elementType,
284 builder.getStringAttr(
"casez_tmp"));
285 builder.setInsertionPoint(&op);
287 auto loc = input.getLoc();
292 auto alwaysComb = sv::AlwaysCombOp::create(builder, loc);
293 builder.setInsertionPointToEnd(alwaysComb.getBodyBlock());
298 if (1ULL << index.getType().getIntOrFloatBitWidth() != caseValues.size()) {
299 caseValues.push_back(sv::ConstantXOp::create(builder, op.getLoc(),
300 op.getResult(0).getType()));
303 APInt caseValue(index.getType().getIntOrFloatBitWidth(), 0);
304 auto *context = builder.getContext();
308 builder, loc, CaseStmtType::CaseZStmt, index, caseValues.size(),
309 [&](
size_t caseIdx) -> std::unique_ptr<sv::CasePattern> {
314 bool isDefault = caseIdx == caseValues.size() - 1;
315 Value theValue = caseValues[caseIdx];
316 std::unique_ptr<sv::CasePattern> thePattern;
319 thePattern = std::make_unique<sv::CaseDefaultPattern>(context);
321 thePattern = std::make_unique<sv::CaseBitPattern>(caseValue, context);
323 sv::BPAssignOp::create(builder, loc, theWire, theValue);
330bool HWLegalizeModulesPass::processUsers(Operation &op, Value value,
331 ArrayRef<Value> mapping) {
332 for (
auto *user :
llvm::make_early_inc_range(value.getUsers())) {
333 if (TypeSwitch<Operation *, bool>(user)
335 if (
auto indexAndBitWidth =
336 tryExtractIndexAndBitWidth(getOp.getIndex())) {
337 getOp.replaceAllUsesWith(mapping[indexAndBitWidth->first]);
343 .Case<sv::ArrayIndexInOutOp>([&](sv::ArrayIndexInOutOp indexOp) {
344 if (
auto indexAndBitWidth =
345 tryExtractIndexAndBitWidth(indexOp.getIndex())) {
346 indexOp.replaceAllUsesWith(mapping[indexAndBitWidth->first]);
352 .Default([](
auto op) {
return false; })) {
357 user->emitError(
"unsupported packed array expression");
365std::optional<std::pair<uint64_t, unsigned>>
366HWLegalizeModulesPass::tryExtractIndexAndBitWidth(Value value) {
367 if (
auto constantOp = dyn_cast<hw::ConstantOp>(value.getDefiningOp())) {
368 auto index = constantOp.getValue();
369 return std::make_optional(
370 std::make_pair(index.getZExtValue(), index.getBitWidth()));
376template <
typename Op>
377bool tryLoweringClockedAssertLike(Op &op) {
378 auto event = op.getEvent();
379 if (!event.has_value())
382 OpBuilder builder(op);
384 sv::AlwaysOp::create(builder, op->getLoc(), *event, op.getClock(), [&] {
385 Op::create(builder, op.getLoc(), op.getProperty(), op.getDisable(),
392bool HWLegalizeModulesPass::tryLoweringClockedAssertLike(Operation &op) {
393 return TypeSwitch<Operation *, bool>(&op)
394 .Case<sv::AssertPropertyOp>(
395 ::tryLoweringClockedAssertLike<sv::AssertPropertyOp>)
396 .Case<sv::AssumePropertyOp>(
397 ::tryLoweringClockedAssertLike<sv::AssumePropertyOp>)
398 .Case<sv::CoverPropertyOp>(
399 ::tryLoweringClockedAssertLike<sv::CoverPropertyOp>)
400 .Default([&](
auto op) {
return false; });
403void HWLegalizeModulesPass::processPostOrder(Block &body) {
408 Block::iterator it = std::prev(body.end());
409 while (it != body.end()) {
414 if (it == body.begin())
419 if (op.getNumRegions()) {
420 for (
auto ®ion : op.getRegions())
421 for (auto ®ionBlock : region.getBlocks())
422 processPostOrder(regionBlock);
425 if (options.disallowPackedArrays) {
427 if (tryLoweringPackedArrayOp(op)) {
428 it = --Block::iterator(op);
430 anythingChanged =
true;
436 for (
auto value : op.getResults()) {
437 if (isa<hw::ArrayType>(value.getType())) {
438 op.emitError(
"unsupported packed array expression");
444 if (options.disallowClockedAssertions) {
445 if (tryLoweringClockedAssertLike(op)) {
447 anythingChanged =
true;
454void HWLegalizeModulesPass::runOnOperation() {
455 thisHWModule = getOperation();
459 cast<ModuleOp>(thisHWModule->getParentOp()));
460 if (optionsAttr != lastParsedOptions) {
463 thisHWModule.emitError(error);
467 lastParsedOptions = optionsAttr;
472 anythingChanged =
false;
475 processPostOrder(*thisHWModule.getBodyBlock());
478 if (!anythingChanged)
479 markAllAnalysesPreserved();
483 return std::make_unique<HWLegalizeModulesPass>();
Signals that an operations regions are procedural.
std::unique_ptr< mlir::Pass > createHWLegalizeModulesPass()
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...