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

[FIRRTL][LowerDPI] Lower FIRRTL vector to an open array type #7305

Merged
merged 1 commit into from
Aug 5, 2024
Merged
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
6 changes: 4 additions & 2 deletions docs/Dialects/FIRRTL/FIRRTLIntrinsics.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ Function Declaration:

Types:
* Operand and result types must be passive.
* Aggregate types are lowered into corresponding verilog aggregate.
* A vector is lowered to an unpacked open array type, e.g. `a: Vec<4, UInt<8>>` to `byte a []`.
* A bundle is lowered to a packed struct.
* Integer types are lowered into into 2-state types.
* Small integer types (< 64 bit) must be compatible to C-types and arguments are passed by values. Users are required to use specific integer types for small integers shown in the table below. Large integers are lowered to `bit` and passed by a reference.

Expand All @@ -267,12 +268,13 @@ Types:

Example SV output:
```firrtl
node result = intrinsic(circt_dpi_call<isClocked = 1, functionName="dpi_func"> : UInt<64>, clock, enable, uint_8_value, uint_32_value)
node result = intrinsic(circt_dpi_call<isClocked = 1, functionName="dpi_func"> : UInt<64>, clock, enable, uint_8_value, uint_32_value, uint_8_vector)
```
```verilog
import "DPI-C" function void dpi_func(
input byte in_0,
int in_1,
byte in_2[],
output longint out_0
);

Expand Down
92 changes: 77 additions & 15 deletions lib/Dialect/FIRRTL/Transforms/LowerDPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "circt/Dialect/FIRRTL/FIRRTLUtils.h"
#include "circt/Dialect/FIRRTL/Namespace.h"
#include "circt/Dialect/FIRRTL/Passes.h"
#include "circt/Dialect/SV/SVOps.h"
#include "circt/Dialect/Sim/SimOps.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Threading.h"
Expand Down Expand Up @@ -91,6 +92,71 @@ void LowerDPI::collectIntrinsics() {
funcNameToCallSites[dpi.getFunctionNameAttr()].push_back(dpi);
}

// Lower FIRRTL type to core dialect types in which array type is replaced with
// an open array type.
static Type lowerDPIArgumentType(Type type) {
auto loweredType = lowerType(type);
return loweredType.replace([](hw::ArrayType array) {
return sv::UnpackedOpenArrayType::get(array.getElementType());
});
}

static Value convertToUnpackedArray(ImplicitLocOpBuilder &builder,
Value loweredValue) {
if (isa<IntegerType>(loweredValue.getType()))
return loweredValue;

auto array = dyn_cast<hw::ArrayType>(loweredValue.getType());
if (!array) {
// TODO: Support bundle or enum types.
return {};
}

SmallVector<Value> values;
auto length = array.getNumElements();
auto width = llvm::Log2_64_Ceil(length);

// Create an unpacked array from a packed array.
for (int i = length - 1; i >= 0; --i) {
auto index = builder.create<hw::ConstantOp>(APInt(width, i));
auto elem = convertToUnpackedArray(
builder, builder.create<hw::ArrayGetOp>(loweredValue, index));
if (!elem)
return {};
values.push_back(elem);
}

return builder.create<sv::UnpackedArrayCreateOp>(
hw::UnpackedArrayType::get(lowerType(array.getElementType()), length),
values);
}

static Value getLowered(ImplicitLocOpBuilder &builder, Value value) {
// Insert an unrealized conversion to cast FIRRTL type to HW type.
if (!value)
return value;

auto type = lowerType(value.getType());
Value result = builder.create<mlir::UnrealizedConversionCastOp>(type, value)
->getResult(0);

// We may need to cast the lowered value to a DPI specific type (e.g. open
// array). Get a DPI type and check if they are same.
auto dpiType = lowerDPIArgumentType(value.getType());
if (type == dpiType)
return result;

// Cast into unpacked open array type.
result = convertToUnpackedArray(builder, result);
if (!result) {
mlir::emitError(value.getLoc())
<< "contains a type that currently not supported";
return {};
}

return builder.create<sv::UnpackedOpenArrayCastOp>(dpiType, result);
}

LogicalResult LowerDPI::lower() {
for (auto [name, calls] : funcNameToCallSites) {
auto firstDPICallop = calls.front();
Expand All @@ -103,25 +169,21 @@ LogicalResult LowerDPI::lower() {
ImplicitLocOpBuilder builder(firstDPICallop.getLoc(),
circuitOp.getOperation());
auto lowerCall = [&](DPICallIntrinsicOp dpiOp) {
auto getLowered = [&](Value value) -> Value {
// Insert an unrealized conversion to cast FIRRTL type to HW type.
if (!value)
return value;
auto type = lowerType(value.getType());
return builder.create<mlir::UnrealizedConversionCastOp>(type, value)
->getResult(0);
};
builder.setInsertionPoint(dpiOp);
auto clock = getLowered(dpiOp.getClock());
auto enable = getLowered(dpiOp.getEnable());
auto clock = getLowered(builder, dpiOp.getClock());
auto enable = getLowered(builder, dpiOp.getEnable());
SmallVector<Value, 4> inputs;
inputs.reserve(dpiOp.getInputs().size());
for (auto input : dpiOp.getInputs())
inputs.push_back(getLowered(input));
for (auto input : dpiOp.getInputs()) {
inputs.push_back(getLowered(builder, input));
if (!inputs.back())
return failure();
}

SmallVector<Type> outputTypes;
if (dpiOp.getResult())
outputTypes.push_back(lowerType(dpiOp.getResult().getType()));
outputTypes.push_back(
lowerDPIArgumentType(dpiOp.getResult().getType()));

auto call = builder.create<sim::DPICallOp>(
outputTypes, firstDPIDecl.getSymNameAttr(), clock, enable, inputs);
Expand Down Expand Up @@ -188,7 +250,7 @@ sim::DPIFuncOp LowerDPI::getOrCreateDPIFuncDecl(DPICallIntrinsicOp op) {
port.dir = hw::ModulePort::Direction::Input;
port.name = inputNames ? cast<StringAttr>(inputNames[idx])
: builder.getStringAttr(Twine("in_") + Twine(idx));
port.type = lowerType(inType);
port.type = lowerDPIArgumentType(inType);
ports.push_back(port);
}

Expand All @@ -198,7 +260,7 @@ sim::DPIFuncOp LowerDPI::getOrCreateDPIFuncDecl(DPICallIntrinsicOp op) {
port.dir = hw::ModulePort::Direction::Output;
port.name = outputName ? outputName
: builder.getStringAttr(Twine("out_") + Twine(idx));
port.type = lowerType(outType);
port.type = lowerDPIArgumentType(outType);
ports.push_back(port);
}

Expand Down
2 changes: 1 addition & 1 deletion test/Dialect/FIRRTL/lower-intrinsics.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ firrtl.circuit "Foo" {
firrtl.int.generic "circt_fpga_probe" %data, %clock : (!firrtl.uint<32>, !firrtl.clock) -> ()
}

// CHECK-LABEL: firrtl.module private @DPIIntrinsicTest(in %clock: !firrtl.clock, in %enable: !firrtl.uint<1>, in %in1: !firrtl.uint<8>, in %in2: !firrtl.uint<8>)
// CHECK-LABEL: firrtl.module private @DPIIntrinsicTest
firrtl.module private @DPIIntrinsicTest(in %clock : !firrtl.clock, in %enable : !firrtl.uint<1>, in %in1: !firrtl.uint<8>, in %in2: !firrtl.uint<8>) {
// CHECK-NEXT: %0 = firrtl.int.dpi.call "clocked_result"(%in1, %in2) clock %clock enable %enable {inputNames = ["foo", "bar"], outputName = "baz"} : (!firrtl.uint<8>, !firrtl.uint<8>) -> !firrtl.uint<8>
%0 = firrtl.int.generic "circt_dpi_call" <isClocked: ui32 = 1, functionName: none = "clocked_result", inputNames: none = "foo;bar", outputName: none = "baz"> %clock, %enable, %in1, %in2 : (!firrtl.clock, !firrtl.uint<1>, !firrtl.uint<8>, !firrtl.uint<8>) -> !firrtl.uint<8>
Expand Down
8 changes: 5 additions & 3 deletions test/firtool/dpi.fir
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ circuit DPI:

; CHECK-LABEL: import "DPI-C" context function void clocked_void(
; CHECK-NEXT: input byte in_0,
; CHECK-NEXT: in_1
; CHECK-NEXT: in_1,
; CHECK-NEXT: in_2[]
; CHECK-NEXT: );

; CHECK-LABEL: import "DPI-C" context function void unclocked_result(
Expand All @@ -22,11 +23,12 @@ circuit DPI:
; CHECK-LABEL: module DPI(
; CHECK: logic [7:0] [[TMP:_.+]];
; CHECK-NEXT: reg [7:0] [[RESULT1:_.+]];
; CHECK-NEXT: wire [7:0] [[OPEN_ARRAY:_.+]][0:1] = '{in_0, in_1};
; CHECK-NEXT: always @(posedge clock) begin
; CHECK-NEXT: if (enable) begin
; CHECK-NEXT: clocked_result(in_0, in_1, [[TMP]]);
; CHECK-NEXT: [[RESULT1]] <= [[TMP]];
; CHECK-NEXT: clocked_void(in_0, in_1);
; CHECK-NEXT: clocked_void(in_0, in_1, [[OPEN_ARRAY]]);
; CHECK-NEXT: end
; CHECK-NEXT: end // always @(posedge)
; CHECK-NEXT: reg [7:0] [[RESULT2:_.+]];
Expand All @@ -47,7 +49,7 @@ circuit DPI:
output out : UInt<8>[2]

node result1 = intrinsic(circt_dpi_call<isClocked = 1, functionName="clocked_result", inputNames="foo;bar", outputName="baz"> : UInt<8>, clock, enable, in[0], in[1])
intrinsic(circt_dpi_call<isClocked = 1, functionName="clocked_void">, clock, enable, in[0], in[1])
intrinsic(circt_dpi_call<isClocked = 1, functionName="clocked_void">, clock, enable, in[0], in[1], in)
node result2 = intrinsic(circt_dpi_call<isClocked = 0, functionName="unclocked_result"> : UInt<8>, enable, in[0], in[1])

out[0] <= result1
Expand Down
Loading