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"
32 using namespace circt;
38 struct 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;
68 bool 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(builder.create<hw::AggregateConstantOp>(
80 constOp.getLoc(), constOp.getType(), cast<ArrayAttr>(field)));
82 if (!processUsers(op, constOp.getResult(), inputs))
90 SmallVector<std::pair<Value, uint64_t>> arrays;
91 for (
auto array : llvm::reverse(concatOp.getInputs())) {
92 auto ty = hw::type_cast<hw::ArrayType>(array.getType());
93 arrays.emplace_back(array, ty.getNumElements());
96 llvm::make_early_inc_range(concatOp.getResult().getUsers())) {
97 if (TypeSwitch<Operation *, bool>(user)
99 if (
auto indexAndBitWidth =
100 tryExtractIndexAndBitWidth(getOp.getIndex())) {
101 auto [indexValue, bitWidth] = *indexAndBitWidth;
103 for (const auto &[array, size] : arrays) {
104 if (indexValue >= size) {
108 OpBuilder builder(getOp);
109 getOp.getInputMutable().set(array);
110 getOp.getIndexMutable().set(
111 builder.createOrFold<hw::ConstantOp>(
112 getOp.getLoc(), APInt(bitWidth, indexValue)));
119 .Default([](
auto op) {
return false; }))
122 op.emitError(
"unsupported packed array expression");
131 SmallVector<Value> inputs(llvm::reverse(createOp.getInputs()));
132 if (!processUsers(op, createOp.getResult(), inputs))
140 auto index = getOp.getIndex();
141 if (
auto *definingOp = index.getDefiningOp())
142 if (isa<hw::ConstantOp>(definingOp))
146 auto ty = hw::type_cast<hw::ArrayType>(getOp.getInput().getType());
147 OpBuilder builder(getOp);
148 SmallVector<Value> caseValues;
149 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
150 auto loc = op.getLoc();
152 loc, APInt(llvm::Log2_64_Ceil(e), i));
155 caseValues.push_back(element);
159 auto theWire = lowerLookupToCasez(op, getOp.getInput(), index,
160 ty.getElementType(), caseValues);
163 builder.setInsertionPoint(getOp);
166 getOp.getResult().replaceAllUsesWith(readWire);
169 .Case<sv::ArrayIndexInOutOp>([&](sv::ArrayIndexInOutOp indexOp) {
171 auto index = indexOp.getIndex();
172 if (
auto *definingOp = index.getDefiningOp())
173 if (isa<hw::ConstantOp>(definingOp))
177 auto inout = indexOp.getInput().getType();
178 if (hw::type_isa<hw::UnpackedArrayType>(inout.getElementType()))
182 auto ty = hw::type_cast<hw::ArrayType>(inout.getElementType());
183 OpBuilder builder(&op);
184 SmallVector<Value> caseValues;
185 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
186 auto loc = op.getLoc();
188 loc, APInt(llvm::Log2_64_Ceil(e), i));
189 auto element = builder.
create<sv::ArrayIndexInOutOp>(
190 loc, indexOp.getInput(), index);
192 caseValues.push_back(readElement);
196 auto theWire = lowerLookupToCasez(op, indexOp.getInput(), index,
197 ty.getElementType(), caseValues);
200 indexOp.getResult().replaceAllUsesWith(theWire);
203 .Case<sv::PAssignOp>([&](sv::PAssignOp assignOp) {
206 auto inout = assignOp.getDest().getType();
207 auto ty = hw::type_dyn_cast<hw::ArrayType>(inout.getElementType());
211 OpBuilder builder(assignOp);
212 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
213 auto loc = op.getLoc();
215 loc, APInt(llvm::Log2_64_Ceil(e), i));
216 auto dstElement = builder.
create<sv::ArrayIndexInOutOp>(
217 loc, assignOp.getDest(), index);
220 builder.
create<sv::PAssignOp>(loc, dstElement, srcElement);
228 auto ty = hw::type_dyn_cast<hw::ArrayType>(regOp.getElementType());
232 OpBuilder builder(regOp);
234 SmallVector<Value> elements;
235 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
236 auto loc = op.getLoc();
238 if (
auto nameAttr = regOp->getAttrOfType<StringAttr>(name)) {
242 elements.push_back(element);
246 if (!processUsers(op, regOp.getResult(), elements))
252 .Default([&](
auto op) {
return false; });
255 Value HWLegalizeModulesPass::lowerLookupToCasez(Operation &op, Value input,
258 SmallVector<Value> caseValues) {
261 OpBuilder builder(&op);
263 builder.getStringAttr(
"casez_tmp"));
264 builder.setInsertionPoint(&op);
266 auto loc = input.getLoc();
270 if (!op.getParentOp()->hasTrait<sv::ProceduralRegion>()) {
271 auto alwaysComb = builder.create<sv::AlwaysCombOp>(loc);
272 builder.setInsertionPointToEnd(alwaysComb.getBodyBlock());
277 if (1ULL << index.getType().getIntOrFloatBitWidth() != caseValues.size()) {
278 caseValues.push_back(builder.create<sv::ConstantXOp>(
279 op.getLoc(), op.getResult(0).getType()));
282 APInt caseValue(index.getType().getIntOrFloatBitWidth(), 0);
283 auto *context = builder.getContext();
286 builder.create<sv::CaseOp>(
287 loc, CaseStmtType::CaseZStmt, index, caseValues.size(),
288 [&](
size_t caseIdx) -> std::unique_ptr<sv::CasePattern> {
293 bool isDefault = caseIdx == caseValues.size() - 1;
294 Value theValue = caseValues[caseIdx];
295 std::unique_ptr<sv::CasePattern> thePattern;
298 thePattern = std::make_unique<sv::CaseDefaultPattern>(context);
300 thePattern = std::make_unique<sv::CaseBitPattern>(caseValue, context);
302 builder.create<sv::BPAssignOp>(loc, theWire, theValue);
309 bool HWLegalizeModulesPass::processUsers(Operation &op, Value value,
310 ArrayRef<Value> mapping) {
311 for (
auto *user : llvm::make_early_inc_range(value.getUsers())) {
312 if (TypeSwitch<Operation *, bool>(user)
314 if (
auto indexAndBitWidth =
315 tryExtractIndexAndBitWidth(getOp.getIndex())) {
316 getOp.replaceAllUsesWith(mapping[indexAndBitWidth->first]);
322 .Case<sv::ArrayIndexInOutOp>([&](sv::ArrayIndexInOutOp indexOp) {
323 if (
auto indexAndBitWidth =
324 tryExtractIndexAndBitWidth(indexOp.getIndex())) {
325 indexOp.replaceAllUsesWith(mapping[indexAndBitWidth->first]);
331 .Default([](
auto op) {
return false; })) {
336 user->emitError(
"unsupported packed array expression");
344 std::optional<std::pair<uint64_t, unsigned>>
345 HWLegalizeModulesPass::tryExtractIndexAndBitWidth(Value value) {
346 if (
auto constantOp = dyn_cast<hw::ConstantOp>(value.getDefiningOp())) {
347 auto index = constantOp.getValue();
348 return std::make_optional(
349 std::make_pair(index.getZExtValue(), index.getBitWidth()));
354 void HWLegalizeModulesPass::processPostOrder(Block &body) {
359 Block::iterator it = std::prev(body.end());
360 while (it != body.end()) {
365 if (it == body.begin())
370 if (op.getNumRegions()) {
371 for (
auto ®ion : op.getRegions())
372 for (
auto ®ionBlock : region.getBlocks())
373 processPostOrder(regionBlock);
376 if (options.disallowPackedArrays) {
378 if (tryLoweringPackedArrayOp(op)) {
379 it = --Block::iterator(op);
381 anythingChanged =
true;
387 for (
auto value : op.getResults()) {
388 if (isa<hw::ArrayType>(value.getType())) {
389 op.emitError(
"unsupported packed array expression");
397 void HWLegalizeModulesPass::runOnOperation() {
398 thisHWModule = getOperation();
401 auto optionsAttr = LoweringOptions::getAttributeFrom(
402 cast<ModuleOp>(thisHWModule->getParentOp()));
403 if (optionsAttr != lastParsedOptions) {
406 thisHWModule.emitError(error);
410 lastParsedOptions = optionsAttr;
415 anythingChanged =
false;
418 processPostOrder(*thisHWModule.getBodyBlock());
421 if (!anythingChanged)
422 markAllAnalysesPreserved();
426 return std::make_unique<HWLegalizeModulesPass>();
def create(array_value, idx)
def create(data_type, value)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
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.