Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MLIR] Add option to emit muxes as if-then-else #1220

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions magma/backend/mlir/compile_to_mlir_opts.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ class CompileToMlirOpts:
location_info_style: str = "none"
explicit_bitcast: bool = False
disallow_expression_inlining_in_ports: bool = False
emit_muxes_as_if_then_else: bool = False
35 changes: 34 additions & 1 deletion magma/backend/mlir/hardware_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,49 @@ def get_module_interface(
return operands, results


def make_if_then_else_mux(
ctx: 'HardwareModule',
data: List[MlirValue],
select: MlirValue,
result: MlirValue,
):
wire = ctx.new_value(hw.InOutType(result.type))
sv.RegOp(results=[wire])
sv.ReadInOutOp(operands=[wire], results=[result])
counter = itertools.count()

def _process():
index = next(counter)
if index >= len(data):
return
cond = ctx.new_value(builtin.IntegerType(1))
const = ctx.new_value(select.type)
hw.ConstantOp(value=index, results=[const])
comb.ICmpOp(predicate="eq", operands=[select, const], results=[cond])
curr = sv.IfOp(operands=[cond])
with push_block(curr.then_block):
sv.BPAssignOp(operands=[wire, data[index]])
with push_block(curr.else_block):
_process()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will the lack of else logic here be problematic for downstream tools? I could imagine a potential latch inference being triggered if the tool doesn't reason about all possible values being matched. In fact, if there is a non power of two number of inputs then there is a potential for a latch (e.g. if the select logic is generated incorrectly w.r.t. to the number of mux inputs, it's possible for the select value to be equal to a constant that's greater than len(data)). IIRC, MLIR would normally emit the mux as an array create/select. Perhaps in Verilog this is okay because the "extra" select values are ignored?

Copy link
Collaborator Author

@rsetaluri rsetaluri Jan 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finalizing need with customer. Will report back what codegen is desired. For now can hold off on merging.


with push_block(sv.AlwaysCombOp().body_block):
_process()


def make_mux(
ctx: 'HardwareModule',
data: List[MlirValue],
select: MlirValue,
result: MlirValue):
result: MlirValue,
):
if ctx.opts.extend_non_power_of_two_muxes:
closest_power_of_two_size = 2 ** clog2(len(data))
if closest_power_of_two_size != len(data):
extension = [data[0]] * (closest_power_of_two_size - len(data))
data = extension + data
if ctx.opts.emit_muxes_as_if_then_else:
make_if_then_else_mux(ctx, data, select, result)
return
mlir_type = hw.ArrayType((len(data),), data[0].type)
array = ctx.new_value(mlir_type)
hw.ArrayCreateOp(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module attributes {circt.loweringOptions = "locationInfoStyle=none"} {
hw.module @aggregate_mux_wrapper(%a: !hw.struct<x: i8, y: i1>, %s: i1) -> (y: !hw.struct<x: i8, y: i1>) {
%0 = hw.struct_extract %a["x"] : !hw.struct<x: i8, y: i1>
%2 = hw.constant -1 : i8
%1 = comb.xor %2, %0 : i8
%3 = hw.struct_extract %a["y"] : !hw.struct<x: i8, y: i1>
%5 = hw.constant -1 : i1
%4 = comb.xor %5, %3 : i1
%6 = hw.struct_create (%1, %4) : !hw.struct<x: i8, y: i1>
%8 = sv.reg : !hw.inout<!hw.struct<x: i8, y: i1>>
%7 = sv.read_inout %8 : !hw.inout<!hw.struct<x: i8, y: i1>>
sv.alwayscomb {
%10 = hw.constant 0 : i1
%9 = comb.icmp eq %s, %10 : i1
sv.if %9 {
sv.bpassign %8, %6 : !hw.struct<x: i8, y: i1>
} else {
%12 = hw.constant 1 : i1
%11 = comb.icmp eq %s, %12 : i1
sv.if %11 {
sv.bpassign %8, %a : !hw.struct<x: i8, y: i1>
} else {
}
}
}
hw.output %7 : !hw.struct<x: i8, y: i1>
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Generated by CIRCT circtorg-0.0.0-1018-g3a39b339f
module aggregate_mux_wrapper(
input struct packed {logic [7:0] x; logic y; } a,
input s,
output struct packed {logic [7:0] x; logic y; } y);

struct packed {logic [7:0] x; logic y; } _GEN;
always_comb begin
if (~s)
_GEN = '{x: (~a.x), y: (~a.y)};
else if (s)
_GEN = a;
end // always_comb
assign y = _GEN;
endmodule

Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,14 @@ def test_compile_to_mlir_disallow_expression_inlining_in_ports(
}
kwargs.update({"check_verilog": False})
run_test_compile_to_mlir(ckt, **kwargs)


@pytest.mark.parametrize("ckt", (aggregate_mux_wrapper,))
def test_compile_to_mlir_emit_muxes_as_if_then_else(ckt):
gold_name = f"{ckt.name}_emit_muxes_as_if_then_else"
kwargs = {
"extend_non_power_of_two_muxes": False,
"emit_muxes_as_if_then_else": True,
"gold_name": gold_name,
}
run_test_compile_to_mlir(ckt, **kwargs)