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);
64 StringAttr lastParsedOptions;
68bool HWLegalizeModulesPass::tryLoweringPackedArrayOp(Operation &op) {
69 return TypeSwitch<Operation *, bool>(&op)
70 .Case<hw::AggregateConstantOp>([&](hw::AggregateConstantOp constOp) {
72 SmallVector<Value> inputs;
73 OpBuilder builder(constOp);
74 for (
auto field :
llvm::reverse(constOp.getFields())) {
75 if (
auto intAttr = dyn_cast<IntegerAttr>(field))
79 inputs.push_back(hw::AggregateConstantOp::create(
80 builder, constOp.getLoc(), constOp.getType(),
81 cast<ArrayAttr>(field)));
83 if (!processUsers(op, constOp.getResult(), inputs))
91 SmallVector<std::pair<Value, uint64_t>> arrays;
92 for (
auto array :
llvm::reverse(concatOp.getInputs())) {
93 auto ty = hw::type_cast<hw::ArrayType>(array.getType());
94 arrays.emplace_back(array, ty.getNumElements());
97 llvm::make_early_inc_range(concatOp.getResult().getUsers())) {
98 if (TypeSwitch<Operation *, bool>(user)
100 if (
auto indexAndBitWidth =
101 tryExtractIndexAndBitWidth(getOp.getIndex())) {
102 auto [indexValue, bitWidth] = *indexAndBitWidth;
104 for (const auto &[array, size] : arrays) {
105 if (indexValue >= size) {
109 OpBuilder builder(getOp);
110 getOp.getInputMutable().set(array);
111 getOp.getIndexMutable().set(
112 builder.createOrFold<hw::ConstantOp>(
113 getOp.getLoc(), APInt(bitWidth, indexValue)));
120 .Default([](
auto op) {
return false; }))
123 op.emitError(
"unsupported packed array expression");
132 SmallVector<Value> inputs(llvm::reverse(createOp.getInputs()));
133 if (!processUsers(op, createOp.getResult(), inputs))
141 auto index = getOp.getIndex();
142 if (
auto *definingOp = index.getDefiningOp())
143 if (isa<hw::ConstantOp>(definingOp))
147 auto ty = hw::type_cast<hw::ArrayType>(getOp.getInput().getType());
148 OpBuilder builder(getOp);
149 SmallVector<Value> caseValues;
150 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
151 auto loc = op.getLoc();
153 loc, APInt(llvm::Log2_64_Ceil(e), i));
156 caseValues.push_back(element);
160 auto theWire = lowerLookupToCasez(op, getOp.getInput(), index,
161 ty.getElementType(), caseValues);
164 builder.setInsertionPoint(getOp);
167 getOp.getResult().replaceAllUsesWith(readWire);
170 .Case<sv::ArrayIndexInOutOp>([&](sv::ArrayIndexInOutOp indexOp) {
172 auto index = indexOp.getIndex();
173 if (
auto *definingOp = index.getDefiningOp())
174 if (isa<hw::ConstantOp>(definingOp))
178 auto inout = indexOp.getInput().getType();
179 if (hw::type_isa<hw::UnpackedArrayType>(inout.getElementType()))
183 auto ty = hw::type_cast<hw::ArrayType>(inout.getElementType());
184 OpBuilder builder(&op);
185 SmallVector<Value> caseValues;
186 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
187 auto loc = op.getLoc();
189 loc, APInt(llvm::Log2_64_Ceil(e), i));
190 auto element = sv::ArrayIndexInOutOp::create(
191 builder, loc, indexOp.getInput(), index);
193 caseValues.push_back(readElement);
197 auto theWire = lowerLookupToCasez(op, indexOp.getInput(), index,
198 ty.getElementType(), caseValues);
201 indexOp.getResult().replaceAllUsesWith(theWire);
204 .Case<sv::PAssignOp>([&](sv::PAssignOp assignOp) {
207 auto inout = assignOp.getDest().getType();
208 auto ty = hw::type_dyn_cast<hw::ArrayType>(inout.getElementType());
212 OpBuilder builder(assignOp);
213 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
214 auto loc = op.getLoc();
216 loc, APInt(llvm::Log2_64_Ceil(e), i));
217 auto dstElement = sv::ArrayIndexInOutOp::create(
218 builder, loc, assignOp.getDest(), index);
221 sv::PAssignOp::create(builder, loc, dstElement, srcElement);
229 auto ty = hw::type_dyn_cast<hw::ArrayType>(regOp.getElementType());
233 OpBuilder builder(regOp);
234 auto name = StringAttr::get(regOp.getContext(),
"name");
235 SmallVector<Value> elements;
236 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
237 auto loc = op.getLoc();
238 auto element = sv::RegOp::create(builder, loc, ty.getElementType());
239 if (
auto nameAttr = regOp->getAttrOfType<StringAttr>(name)) {
241 StringAttr::get(regOp.getContext(), nameAttr.getValue()));
243 elements.push_back(element);
247 if (!processUsers(op, regOp.getResult(), elements))
253 .Default([&](
auto op) {
return false; });
256Value HWLegalizeModulesPass::lowerLookupToCasez(Operation &op, Value input,
259 SmallVector<Value> caseValues) {
262 OpBuilder builder(&op);
263 auto theWire = sv::RegOp::create(builder, op.getLoc(),
elementType,
264 builder.getStringAttr(
"casez_tmp"));
265 builder.setInsertionPoint(&op);
267 auto loc = input.getLoc();
272 auto alwaysComb = sv::AlwaysCombOp::create(builder, loc);
273 builder.setInsertionPointToEnd(alwaysComb.getBodyBlock());
278 if (1ULL << index.getType().getIntOrFloatBitWidth() != caseValues.size()) {
279 caseValues.push_back(sv::ConstantXOp::create(builder, op.getLoc(),
280 op.getResult(0).getType()));
283 APInt caseValue(index.getType().getIntOrFloatBitWidth(), 0);
284 auto *context = builder.getContext();
288 builder, loc, CaseStmtType::CaseZStmt, index, caseValues.size(),
289 [&](
size_t caseIdx) -> std::unique_ptr<sv::CasePattern> {
294 bool isDefault = caseIdx == caseValues.size() - 1;
295 Value theValue = caseValues[caseIdx];
296 std::unique_ptr<sv::CasePattern> thePattern;
299 thePattern = std::make_unique<sv::CaseDefaultPattern>(context);
301 thePattern = std::make_unique<sv::CaseBitPattern>(caseValue, context);
303 sv::BPAssignOp::create(builder, loc, theWire, theValue);
310bool HWLegalizeModulesPass::processUsers(Operation &op, Value value,
311 ArrayRef<Value> mapping) {
312 for (
auto *user :
llvm::make_early_inc_range(value.getUsers())) {
313 if (TypeSwitch<Operation *, bool>(user)
315 if (
auto indexAndBitWidth =
316 tryExtractIndexAndBitWidth(getOp.getIndex())) {
317 getOp.replaceAllUsesWith(mapping[indexAndBitWidth->first]);
323 .Case<sv::ArrayIndexInOutOp>([&](sv::ArrayIndexInOutOp indexOp) {
324 if (
auto indexAndBitWidth =
325 tryExtractIndexAndBitWidth(indexOp.getIndex())) {
326 indexOp.replaceAllUsesWith(mapping[indexAndBitWidth->first]);
332 .Default([](
auto op) {
return false; })) {
337 user->emitError(
"unsupported packed array expression");
345std::optional<std::pair<uint64_t, unsigned>>
346HWLegalizeModulesPass::tryExtractIndexAndBitWidth(Value value) {
347 if (
auto constantOp = dyn_cast<hw::ConstantOp>(value.getDefiningOp())) {
348 auto index = constantOp.getValue();
349 return std::make_optional(
350 std::make_pair(index.getZExtValue(), index.getBitWidth()));
355void HWLegalizeModulesPass::processPostOrder(Block &body) {
360 Block::iterator it = std::prev(body.end());
361 while (it != body.end()) {
366 if (it == body.begin())
371 if (op.getNumRegions()) {
372 for (
auto ®ion : op.getRegions())
373 for (auto ®ionBlock : region.getBlocks())
374 processPostOrder(regionBlock);
377 if (options.disallowPackedArrays) {
379 if (tryLoweringPackedArrayOp(op)) {
380 it = --Block::iterator(op);
382 anythingChanged =
true;
388 for (
auto value : op.getResults()) {
389 if (isa<hw::ArrayType>(value.getType())) {
390 op.emitError(
"unsupported packed array expression");
398void HWLegalizeModulesPass::runOnOperation() {
399 thisHWModule = getOperation();
403 cast<ModuleOp>(thisHWModule->getParentOp()));
404 if (optionsAttr != lastParsedOptions) {
407 thisHWModule.emitError(error);
411 lastParsedOptions = optionsAttr;
416 anythingChanged =
false;
419 processPostOrder(*thisHWModule.getBodyBlock());
422 if (!anythingChanged)
423 markAllAnalysesPreserved();
427 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...