22#include "mlir/IR/Builders.h"
23#include "mlir/Pass/Pass.h"
27#define GEN_PASS_DEF_HWLEGALIZEMODULES
28#include "circt/Dialect/SV/SVPasses.h.inc"
38struct HWLegalizeModulesPass
39 :
public circt::sv::impl::HWLegalizeModulesBase<HWLegalizeModulesPass> {
40 void runOnOperation()
override;
43 void processPostOrder(
Block &block);
44 bool tryLoweringPackedArrayOp(Operation &op);
45 Value lowerLookupToCasez(Operation &op, Value input, Value index,
47 SmallVector<Value> caseValues);
48 bool processUsers(Operation &op, Value value, ArrayRef<Value> mapping);
49 std::optional<std::pair<uint64_t, unsigned>>
50 tryExtractIndexAndBitWidth(Value value);
51 bool tryLoweringClockedAssertLike(Operation &op);
65 StringAttr lastParsedOptions;
69bool HWLegalizeModulesPass::tryLoweringPackedArrayOp(Operation &op) {
70 return TypeSwitch<Operation *, bool>(&op)
71 .Case<hw::AggregateConstantOp>([&](hw::AggregateConstantOp constOp) {
73 SmallVector<Value> inputs;
74 OpBuilder builder(constOp);
75 for (
auto field :
llvm::reverse(constOp.getFields())) {
76 if (
auto intAttr = dyn_cast<IntegerAttr>(field))
80 inputs.push_back(hw::AggregateConstantOp::create(
81 builder, constOp.getLoc(), constOp.getType(),
82 cast<ArrayAttr>(field)));
84 if (!processUsers(op, constOp.getResult(), inputs))
92 SmallVector<std::pair<Value, uint64_t>> arrays;
93 for (
auto array :
llvm::reverse(concatOp.getInputs())) {
94 auto ty = hw::type_cast<hw::ArrayType>(array.getType());
95 arrays.emplace_back(array, ty.getNumElements());
98 llvm::make_early_inc_range(concatOp.getResult().getUsers())) {
99 if (TypeSwitch<Operation *, bool>(user)
101 if (
auto indexAndBitWidth =
102 tryExtractIndexAndBitWidth(getOp.getIndex())) {
103 auto [indexValue, bitWidth] = *indexAndBitWidth;
105 for (const auto &[array, size] : arrays) {
106 if (indexValue >= size) {
110 OpBuilder builder(getOp);
111 getOp.getInputMutable().set(array);
112 getOp.getIndexMutable().set(
113 builder.createOrFold<hw::ConstantOp>(
114 getOp.getLoc(), APInt(bitWidth, indexValue)));
121 .Default([](
auto op) {
return false; }))
124 op.emitError(
"unsupported packed array expression");
133 SmallVector<Value> inputs(llvm::reverse(createOp.getInputs()));
134 if (!processUsers(op, createOp.getResult(), inputs))
142 auto index = getOp.getIndex();
143 if (
auto *definingOp = index.getDefiningOp())
144 if (isa<hw::ConstantOp>(definingOp))
148 auto ty = hw::type_cast<hw::ArrayType>(getOp.getInput().getType());
149 OpBuilder builder(getOp);
150 SmallVector<Value> caseValues;
151 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
152 auto loc = op.getLoc();
154 loc, APInt(llvm::Log2_64_Ceil(e), i));
157 caseValues.push_back(element);
161 auto theWire = lowerLookupToCasez(op, getOp.getInput(), index,
162 ty.getElementType(), caseValues);
165 builder.setInsertionPoint(getOp);
168 getOp.getResult().replaceAllUsesWith(readWire);
171 .Case<sv::ArrayIndexInOutOp>([&](sv::ArrayIndexInOutOp indexOp) {
173 auto index = indexOp.getIndex();
174 if (
auto *definingOp = index.getDefiningOp())
175 if (isa<hw::ConstantOp>(definingOp))
179 auto inout = indexOp.getInput().getType();
180 if (hw::type_isa<hw::UnpackedArrayType>(inout.getElementType()))
184 auto ty = hw::type_cast<hw::ArrayType>(inout.getElementType());
185 OpBuilder builder(&op);
186 SmallVector<Value> caseValues;
187 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
188 auto loc = op.getLoc();
190 loc, APInt(llvm::Log2_64_Ceil(e), i));
191 auto element = sv::ArrayIndexInOutOp::create(
192 builder, loc, indexOp.getInput(), index);
194 caseValues.push_back(readElement);
198 auto theWire = lowerLookupToCasez(op, indexOp.getInput(), index,
199 ty.getElementType(), caseValues);
202 indexOp.getResult().replaceAllUsesWith(theWire);
205 .Case<sv::PAssignOp>([&](sv::PAssignOp assignOp) {
208 auto inout = assignOp.getDest().getType();
209 auto ty = hw::type_dyn_cast<hw::ArrayType>(inout.getElementType());
213 OpBuilder builder(assignOp);
214 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
215 auto loc = op.getLoc();
217 loc, APInt(llvm::Log2_64_Ceil(e), i));
218 auto dstElement = sv::ArrayIndexInOutOp::create(
219 builder, loc, assignOp.getDest(), index);
222 sv::PAssignOp::create(builder, loc, dstElement, srcElement);
230 auto ty = hw::type_dyn_cast<hw::ArrayType>(regOp.getElementType());
234 OpBuilder builder(regOp);
235 auto name = StringAttr::get(regOp.getContext(),
"name");
236 SmallVector<Value> elements;
237 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
238 auto loc = op.getLoc();
239 auto element = sv::RegOp::create(builder, loc, ty.getElementType());
240 if (
auto nameAttr = regOp->getAttrOfType<StringAttr>(name)) {
242 StringAttr::get(regOp.getContext(), nameAttr.getValue()));
244 elements.push_back(element);
248 if (!processUsers(op, regOp.getResult(), elements))
254 .Default([&](
auto op) {
return false; });
257Value HWLegalizeModulesPass::lowerLookupToCasez(Operation &op, Value input,
260 SmallVector<Value> caseValues) {
263 OpBuilder builder(&op);
264 auto theWire = sv::RegOp::create(builder, op.getLoc(),
elementType,
265 builder.getStringAttr(
"casez_tmp"));
266 builder.setInsertionPoint(&op);
268 auto loc = input.getLoc();
273 auto alwaysComb = sv::AlwaysCombOp::create(builder, loc);
274 builder.setInsertionPointToEnd(alwaysComb.getBodyBlock());
279 if (1ULL << index.getType().getIntOrFloatBitWidth() != caseValues.size()) {
280 caseValues.push_back(sv::ConstantXOp::create(builder, op.getLoc(),
281 op.getResult(0).getType()));
284 APInt caseValue(index.getType().getIntOrFloatBitWidth(), 0);
285 auto *context = builder.getContext();
289 builder, loc, CaseStmtType::CaseZStmt, index, caseValues.size(),
290 [&](
size_t caseIdx) -> std::unique_ptr<sv::CasePattern> {
295 bool isDefault = caseIdx == caseValues.size() - 1;
296 Value theValue = caseValues[caseIdx];
297 std::unique_ptr<sv::CasePattern> thePattern;
300 thePattern = std::make_unique<sv::CaseDefaultPattern>(context);
302 thePattern = std::make_unique<sv::CaseBitPattern>(caseValue, context);
304 sv::BPAssignOp::create(builder, loc, theWire, theValue);
311bool HWLegalizeModulesPass::processUsers(Operation &op, Value value,
312 ArrayRef<Value> mapping) {
313 for (
auto *user :
llvm::make_early_inc_range(value.getUsers())) {
314 if (TypeSwitch<Operation *, bool>(user)
316 if (
auto indexAndBitWidth =
317 tryExtractIndexAndBitWidth(getOp.getIndex())) {
318 getOp.replaceAllUsesWith(mapping[indexAndBitWidth->first]);
324 .Case<sv::ArrayIndexInOutOp>([&](sv::ArrayIndexInOutOp indexOp) {
325 if (
auto indexAndBitWidth =
326 tryExtractIndexAndBitWidth(indexOp.getIndex())) {
327 indexOp.replaceAllUsesWith(mapping[indexAndBitWidth->first]);
333 .Default([](
auto op) {
return false; })) {
338 user->emitError(
"unsupported packed array expression");
346std::optional<std::pair<uint64_t, unsigned>>
347HWLegalizeModulesPass::tryExtractIndexAndBitWidth(Value value) {
348 if (
auto constantOp = dyn_cast<hw::ConstantOp>(value.getDefiningOp())) {
349 auto index = constantOp.getValue();
350 return std::make_optional(
351 std::make_pair(index.getZExtValue(), index.getBitWidth()));
357template <
typename Op>
358bool tryLoweringClockedAssertLike(Op &op) {
359 auto event = op.getEvent();
360 if (!event.has_value())
363 OpBuilder builder(op);
365 sv::AlwaysOp::create(builder, op->getLoc(), *event, op.getClock(), [&] {
366 Op::create(builder, op.getLoc(), op.getProperty(), op.getDisable(),
373bool HWLegalizeModulesPass::tryLoweringClockedAssertLike(Operation &op) {
374 return TypeSwitch<Operation *, bool>(&op)
375 .Case<sv::AssertPropertyOp>(
376 ::tryLoweringClockedAssertLike<sv::AssertPropertyOp>)
377 .Case<sv::AssumePropertyOp>(
378 ::tryLoweringClockedAssertLike<sv::AssumePropertyOp>)
379 .Case<sv::CoverPropertyOp>(
380 ::tryLoweringClockedAssertLike<sv::CoverPropertyOp>)
381 .Default([&](
auto op) {
return false; });
384void HWLegalizeModulesPass::processPostOrder(Block &body) {
389 Block::iterator it = std::prev(body.end());
390 while (it != body.end()) {
395 if (it == body.begin())
400 if (op.getNumRegions()) {
401 for (
auto ®ion : op.getRegions())
402 for (auto ®ionBlock : region.getBlocks())
403 processPostOrder(regionBlock);
406 if (options.disallowPackedArrays) {
408 if (tryLoweringPackedArrayOp(op)) {
409 it = --Block::iterator(op);
411 anythingChanged =
true;
417 for (
auto value : op.getResults()) {
418 if (isa<hw::ArrayType>(value.getType())) {
419 op.emitError(
"unsupported packed array expression");
425 if (options.disallowClockedAssertions) {
426 if (tryLoweringClockedAssertLike(op)) {
428 anythingChanged =
true;
435void HWLegalizeModulesPass::runOnOperation() {
436 thisHWModule = getOperation();
440 cast<ModuleOp>(thisHWModule->getParentOp()));
441 if (optionsAttr != lastParsedOptions) {
444 thisHWModule.emitError(error);
448 lastParsedOptions = optionsAttr;
453 anythingChanged =
false;
456 processPostOrder(*thisHWModule.getBodyBlock());
459 if (!anythingChanged)
460 markAllAnalysesPreserved();
464 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...