Closed xguerin closed 7 years ago
They are used when instantiating external verilog or vhdl components within a design. If I remember correctly the syntax is
let bits = 16 in
let d = consti bits 12 in
let m = inst "some_external_module"
[ "bits" ==> ParamInt(bits) ]
[ "clk" ==> clk; "d" ==> d ]
[ "q" ==> bits ]
in
let q = m#o "q" in
(it doesn't lead to generating configurable verilog/vhdl if that was what you were hoping)
(it doesn't lead to generating configurable verilog/vhdl if that was what you were hoping)
Yes, I was hoping for something along that line :). Being able to interface with parameterized modules is already good.
Did you ever give a thought about the complexity of adding support for generating parameterized modules ?
Exposing the generic features in verilog and vhdl (signal/wire/reg sizing calculations, pure functions, for/if generates etc) would seem possible. It would need something like a separate DSL on top of hardcaml for configuring this stuff and would also complicate/extend the API - ie input_g "x" Generics.(Int "x_width" +: Int "adjust")
or something. I also expect trying to write generic designs would be about as much hassle as writing in VHDL/Verilog in the first place.
Maybe with modular implicits the API would become easier to manage, but the backend would be more complex (you would need an evaluation step when elaborating just like vhdl/verilog). This is the main reason for not doing so - HardCaml backends are reasonably simple with only about 10 or so distinct things to manage (and maybe another 10 or so distinct binary operators).
Personally, I think leaving a little bit of elaboration up to the user (be it in a build step or using ocaml - see the hierarchy stuff for an example) is worth the tradeoff of keeping the core library and it's backends reasonably simple.
Personally, I think leaving a little bit of elaboration up to the user (be it in a build step or using ocaml - see the hierarchy stuff for an example) is worth the tradeoff of keeping the core library and it's backends reasonably simple.
I'm all for simplicity :)
Maybe with modular implicits the API would become easier to manage, but the backend would be more complex.
I was thinking more along the line of functors:
module type Parameters = sig
var DATA_BITS;
end
module IN (MyParams : Parameters) = struct
type 'a t = {
data : 'a [@bits MyParams.DATA_BITS];
}
end
Parameters from that syntax could be easily deduced, and we could generate another helper function that returns a list of parameters used in the[@bits]
and [@width]
attributes:
module IN #(
.DATA_BITS
)(
input data[DATA_BITS-1:0];
)
...
endmodule
I believe this could be implemented without too much hassle as most of the wiring to do that is already in the deriving
plugin. But the devil is in the details :)
I think the devil is indeed in the internal details of the module. A generic expression will need to be derived for the width of all the internal signals as they change at various points in the implementation.
In ppx_deriving_hardcaml
you made the point that [@bits]
and [@width]
could take arbitrary expressions. How does the framework currently handle this internally ? For all intents and purposes, it should not make any difference wether that arbitrary expression is a constant available in the local scope or a return value of function taken from a functor parameter.
The value given to, say, [@bits] is known by the time the program in running.
If you look at the signal datatype it contains a width value (an int) for every hardware node and this is statically known when it is constructed.
For example,
let data_x2 = data @: gnd
This is an internal signal of width (DATA_BITS+1) and this gets calculated exactly when the above line is evaluated. This is true of every other operation as well.
Remember, you can still set the value of DATA_BITS from, say, a command line argument. That is you can generate a completely different architecture on different runs of the same program.
(I am confusing my use of static here I think. By static here I mean that the nodes look at their arguments and calculate their own width - by the time we write verilog, every internal signal width is known).
(I am confusing my use of static here I think. By static here I mean that the nodes look at their arguments and calculate their own width - by the time we write verilog, every internal signal width is known).
Gotcha. To make that possible we would need to keep track of parameter definitions and keep the width calculation in the form of expression. Too much hassle.
While perusing the code generation part of the framework I stumbled on the part handling
generic
statements. Is this related toparameter
in Verilog ? Is there any example on how to use them ?