Enhance test and document for DPI and tweak trait name. - #4380
Conversation
7b82375 to
5a1799e
Compare
| final def apply() = super.call() | ||
| } | ||
|
|
||
| Hello() // Print |
There was a problem hiding this comment.
should this Hello() call be inside a Module?
There was a problem hiding this comment.
Yes. The snippet was meant to be pseudo code so I'll update with mdoc.
|
|
||
| To call this function from Chisel, we need to define a corresponding DPI object. | ||
|
|
||
| ```scala |
There was a problem hiding this comment.
can you use mdoc so that we can check the code compiles at least?
There was a problem hiding this comment.
Yes, thank you for pointing out. Totally missed there is mdoc support in chisel repo as well.
| * A vector is lowered to an *unpacked* *open* array type, e.g., `a: Vec<4, UInt>` to `byte a []`. | ||
| * A bundle is lowered to a packed struct. |
There was a problem hiding this comment.
| * A vector is lowered to an *unpacked* *open* array type, e.g., `a: Vec<4, UInt>` to `byte a []`. | |
| * A bundle is lowered to a packed struct. | |
| * A `Vec` is lowered to an *unpacked* *open* array type, e.g., `a: Vec<4, UInt>` to `byte a []`. | |
| * A `Bundle` is lowered to a packed struct. |
for vec i think you mean a chisel3.Vec not a scala Vector, right?
| * Operand and result types must be passive. | ||
| * A vector is lowered to an *unpacked* *open* array type, e.g., `a: Vec<4, UInt>` to `byte a []`. | ||
| * A bundle is lowered to a packed struct. | ||
| * Integer types are lowered into 2-state types. |
There was a problem hiding this comment.
| * Integer types are lowered into 2-state types. | |
| * `Int`, `SInt`, `Clock`, `Reset` types are lowered into 2-state types. |
? Are clock and reset lowered?
There was a problem hiding this comment.
I am not actually sure if you are talkign about Chisel Data subclasses or not
| final def apply(lhs: UInt, rhs: UInt): UInt = super.call(lhs, rhs) | ||
| } | ||
|
|
||
| val result = Add(2.U(32.W), 3.U(32.W)) |
There was a problem hiding this comment.
again would be helpful to see this with mdoc, I assume this Add function belongs inside a Module or a RawModule given that we say clocked is False
|
|
||
| * `Add` inherits from `DPINonVoidFunctionImport[UInt]` because it returns a 32-bit unsigned integer. | ||
| * `ret` specifies the return type. | ||
| * `clocked` indicates that this is a clocked function call. |
There was a problem hiding this comment.
but this is set to false so isn't NOT a clocked function call?
| object Sum extends DPINonVoidFunctionImport[UInt] { | ||
| override val functionName = "sum" | ||
| override val ret = UInt(32.W) | ||
| override val clocked = false |
There was a problem hiding this comment.
For OpenArray example both clocked and unclocked work. So I didn't explicitly describe the example. It depends on whether user wants to evaluate the value at every input value changes or clock's posedge.
This is an example for Add
chisel/src/test/scala/chiselTests/DPISpec.scala
Lines 158 to 161 in ead5fc9
| } | ||
| ``` | ||
|
|
||
| ```scala |
There was a problem hiding this comment.
again please use mdoc to check for compile if not chisel elaboration
| ``` | ||
|
|
||
| # FAQ | ||
|
|
There was a problem hiding this comment.
can you show an example where having clocked = false makes sense? Is that done in a when context...?
There was a problem hiding this comment.
Unclocked calls be useful when user wants to replace pure (but expensive) combinatorial logics with dpi call (actually Add is a good example of such use case).
An unclocked call under when(cond) is lowered into always_comb + if(cond) and DPI is conditionally invoked as well. I updated Add example to include both of them.
class AddTest extends Module {
val io = IO(new Bundle {
val a = Input(UInt(32.W))
val b = Input(UInt(32.W))
val c = Output(UInt(32.W))
val d = Output(UInt(32.W))
val en = Input(Bool())
})
// Call DPI only when `en` is true.
when (io.en) {
io.c := AddClocked(io.a, io.b)
io.d := AddUnclocked(io.a, io.b)
} .otherwise {
io.c := 0.U(32.W)
io.d := 0.U(32.W)
}
}module AddTest(
input clock,
reset,
input [31:0] io_a,
io_b,
output [31:0] io_c,
io_d,
input io_en
);
logic [31:0] _add_0;
reg [31:0] _GEN;
always @(posedge clock) begin
if (io_en) begin
add(io_a, io_b, _add_0);
_GEN <= _add_0;
end
end // always @(posedge)
reg [31:0] _GEN_0;
always_comb begin
if (io_en) begin
add(io_a, io_b, _GEN_0);
end
else
_GEN_0 = 32'bx;
end // always_comb
assign io_c = io_en ? _GEN : 32'h0;
assign io_d = io_en ? _GEN_0 : 32'h0;|
I'm not 100% sure of Chisel's documentation conventions, but quickly glancing at other This makes diffs easier to read. |
|
|
||
| ## Type ABI | ||
|
|
||
| Unlike normal Chisel compilation flow, we use a specific ABI for types to interact with DPI. |
There was a problem hiding this comment.
I'm not sure what this means. Could you elaborate?
There was a problem hiding this comment.
It may also be useful to explicitly point out that what we're doing in this section is showing how Chisel types map to C types.
| Here's an example of a DPI function that calculates the sum of two numbers: | ||
|
|
||
| ```c++ | ||
| extern "C" void add(int lhs, int rhs, int* result) |
There was a problem hiding this comment.
It surprised me to see here that the int* result is an out-parameter. Is that always the case? (If so, maybe it needs to be called out earlier. Or maybe I just missed it?)
There was a problem hiding this comment.
Thanks, I thought I clarified that but apparently it was dropped somewhere. I'll add explanation for that.
| Chisel vectors are converted into SystemVerilog open arrays when used with DPI. Since memory layout can vary between simulators, it's recommended to use `svSize` and `svGetBitArrElemVecVal` to access array elements. | ||
|
|
||
| ```c++ | ||
| extern "C" void sum(const svOpenArrayHandle array, int* result) { |
There was a problem hiding this comment.
What is svOpenArrayHandle?
I guess, more generally, what libraries does Chisel depend on in the C++ DPI? We can't simply assume that a general Chisel user will have whatever library exposes DPI.h or whatever it is.
It would be good to have a section at the very start that gives the C++ prerequisites.
There was a problem hiding this comment.
Thank you for the suggestion, that makese sense. SV Spec (Section 35) defineds svdpi.h which declares these functions and types (including svOpenArrayHandle). These are expected to be implemented by simulators. I'll create a section for that.
|
|
||
| # FAQ | ||
|
|
||
| * Can we export functions? -- No, not currently. Consider using a black box for such functionality. |
There was a problem hiding this comment.
What do you mean by "export" here?
|
|
||
| * Can we export functions? -- No, not currently. Consider using a black box for such functionality. | ||
| * Can we call a DPI function in initial block? -- No, not currently. Consider using a black box for initialization. | ||
| * Can we call two clocked DPI calls and pass the result to another within the same clock? -- No, not currently. Please merge the DPI functions into a single function. |
There was a problem hiding this comment.
I'm not sure what this last point means.
It feels like what we actually want is a light explanation of the underlying "framework" for the DPI calls, and that the above comment is actually a restriction imposed on this framework.
For example, is this true?: "All DPI calls on the same clock are dispatched at the same time. Because of this, it is not possible to use the result of one DPI call as an argument to another."
There was a problem hiding this comment.
All DPI calls on the same clock are dispatched at the same time. Because of this, it is not possible to use the result of one DPI call as an argument to another
Yes, that's correct. This is fundamental restrictions for side-effecting operations in Chisel but DPI is a first example that has both side-effect and results.
| val dpiImpl = s""" | ||
| |#include <stdint.h> | ||
| |#include <iostream> | ||
| |#include <svdpi.h> |
There was a problem hiding this comment.
To reiterate an earlier point: Let's add a prerequisites section near the top. Let's make sure to name what library(ies?) are needed for Chisel DPI, and let's be sure to include the svdpi.h header in our examples, since otherwise, the code doesn't work.
There was a problem hiding this comment.
Let's also link to the documentation so that Chisel users can quickly cross-reference the DPI primitives, such as svBitVecVal or svOpenArrayHandle.
| * A vector is lowered to an *unpacked* *open* array type, e.g., `a: Vec<4, UInt>` to `byte a []`. | ||
| * A bundle is lowered to a packed struct. |
| Here's an example of a DPI function that calculates the sum of two numbers: | ||
|
|
||
| ```c++ | ||
| extern "C" void add(int lhs, int rhs, int* result) |
There was a problem hiding this comment.
Thanks, I thought I clarified that but apparently it was dropped somewhere. I'll add explanation for that.
| Chisel vectors are converted into SystemVerilog open arrays when used with DPI. Since memory layout can vary between simulators, it's recommended to use `svSize` and `svGetBitArrElemVecVal` to access array elements. | ||
|
|
||
| ```c++ | ||
| extern "C" void sum(const svOpenArrayHandle array, int* result) { |
There was a problem hiding this comment.
Thank you for the suggestion, that makese sense. SV Spec (Section 35) defineds svdpi.h which declares these functions and types (including svOpenArrayHandle). These are expected to be implemented by simulators. I'll create a section for that.
| object Sum extends DPINonVoidFunctionImport[UInt] { | ||
| override val functionName = "sum" | ||
| override val ret = UInt(32.W) | ||
| override val clocked = false |
There was a problem hiding this comment.
For OpenArray example both clocked and unclocked work. So I didn't explicitly describe the example. It depends on whether user wants to evaluate the value at every input value changes or clock's posedge.
This is an example for Add
chisel/src/test/scala/chiselTests/DPISpec.scala
Lines 158 to 161 in ead5fc9
|
|
||
| To call this function from Chisel, we need to define a corresponding DPI object. | ||
|
|
||
| ```scala |
There was a problem hiding this comment.
Yes, thank you for pointing out. Totally missed there is mdoc support in chisel repo as well.
| final def apply() = super.call() | ||
| } | ||
|
|
||
| Hello() // Print |
There was a problem hiding this comment.
Yes. The snippet was meant to be pseudo code so I'll update with mdoc.
|
|
||
| * Can we export functions? -- No, not currently. Consider using a black box for such functionality. | ||
| * Can we call a DPI function in initial block? -- No, not currently. Consider using a black box for initialization. | ||
| * Can we call two clocked DPI calls and pass the result to another within the same clock? -- No, not currently. Please merge the DPI functions into a single function. |
There was a problem hiding this comment.
All DPI calls on the same clock are dispatched at the same time. Because of this, it is not possible to use the result of one DPI call as an argument to another
Yes, that's correct. This is fundamental restrictions for side-effecting operations in Chisel but DPI is a first example that has both side-effect and results.
| ``` | ||
|
|
||
| # FAQ | ||
|
|
There was a problem hiding this comment.
Unclocked calls be useful when user wants to replace pure (but expensive) combinatorial logics with dpi call (actually Add is a good example of such use case).
An unclocked call under when(cond) is lowered into always_comb + if(cond) and DPI is conditionally invoked as well. I updated Add example to include both of them.
class AddTest extends Module {
val io = IO(new Bundle {
val a = Input(UInt(32.W))
val b = Input(UInt(32.W))
val c = Output(UInt(32.W))
val d = Output(UInt(32.W))
val en = Input(Bool())
})
// Call DPI only when `en` is true.
when (io.en) {
io.c := AddClocked(io.a, io.b)
io.d := AddUnclocked(io.a, io.b)
} .otherwise {
io.c := 0.U(32.W)
io.d := 0.U(32.W)
}
}module AddTest(
input clock,
reset,
input [31:0] io_a,
io_b,
output [31:0] io_c,
io_d,
input io_en
);
logic [31:0] _add_0;
reg [31:0] _GEN;
always @(posedge clock) begin
if (io_en) begin
add(io_a, io_b, _add_0);
_GEN <= _add_0;
end
end // always @(posedge)
reg [31:0] _GEN_0;
always_comb begin
if (io_en) begin
add(io_a, io_b, _GEN_0);
end
else
_GEN_0 = 32'bx;
end // always_comb
assign io_c = io_en ? _GEN : 32'h0;
assign io_d = io_en ? _GEN_0 : 32'h0;d6128dc to
7b81491
Compare
This PR adds a test for open array and document
docs/src/explanations/dpi.mdwhich describes DPI API usage.This also changes DPIClockedVoidFunctionImport to DPIVoidFunctionImport for more consistent naming (this is breaking change but I believe there is no user of DPIClockedVoidFunctionImport so it should be ok).
Contributor Checklist
docs/src?Type of Improvement
Desired Merge Strategy
Release Notes
Enhance test and document for DPI and tweak trait name
Reviewer Checklist (only modified by reviewer)
3.6.x,5.x, or6.xdepending on impact, API modification or big change:7.0)?Enable auto-merge (squash), clean up the commit message, and label withPlease Merge.Create a merge commit.