-
Notifications
You must be signed in to change notification settings - Fork 38
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
Requirements for converting compressed Bundles to uncompressed Bundles #7
Comments
Hi,
Hoo yes, i remember the pain of those :) I'm asking myself if the best would not be to have a general data type for this (grosso modo): class PacketData[T <: Data](template : T, size : Int) extends Data{
val elements = LinkedHashMap[BaseType, Bits]()
//fill elements
for(e <- template.flatten){
//Fill elements cache which map a element of the template to its packed representation (e * size)
}
//Utilities to manipulate stuff
def apply(key : T => BaseType, index : Int) = {
val e = elements(key(template))
val w = widthOf(e)
e(w*index, w bits)
}
} And having utility to automaticaly convert from / to the original type T That way you would not have to manualy redefine things by hands |
Here is a working example, which isn't read for use in IO, but already as a prototype to talk about : object PlayPackedData extends App{
class PackedData[T <: Data](hardType : HardType[T], size : Int) extends MultiData {
override val elements = ArrayBuffer[(String, Data)]()
class Element(val name : String, val raw : Bits, val width : Int)
val elementsMap = mutable.LinkedHashMap[String, Element]()
packedBuild()
def packedBuild() {
val template = hardType()
for ((e, name) <- (template.flatten, template.flattenLocalName).zipped) {
println(e + " " + name)
val w = widthOf(e)
val ref = Bits(widthOf(e) * size bits)
elements += name -> ref
elementsMap(name) = new Element(name, ref, w)
ref.parent = this
if (OwnableRef.proposal(ref, this)) ref.setPartialName(name, Nameable.DATAMODEL_WEAK)
}
}
def pack(that : T, index : Int) = {
for((from, name) <- (that.flatten, that.flattenLocalName).zipped){
val to = elementsMap(name)
assert(widthOf(from) == to.width)
to.raw(to.width*index, to.width bits) := from.asBits
}
}
def unpack(index : Int) : T = {
val ret = hardType()
for((to, name) <- (ret.flatten, ret.flattenLocalName).zipped){
val from = elementsMap(name)
to.assignFromBits(from.raw(from.width*index, from.width bits))
}
ret
}
override def assignFromImpl(that: AnyRef, target: AnyRef, kind: AnyRef): Unit = {
that match {
case that: PackedData[_] =>
???
case _ => throw new Exception("Undefined assignment")
}
}
}
SpinalVerilog(new Component {
val x = new PackedData(Rgb(5,6,5), 4).assignDontCare()
val y = Rgb(5,6,5)
x.pack(y, 2)
val z = x.unpack(2)
})
} Let's me know what you think |
@Dolu1990 module UnpackedMod (
input[31:0] io_ain_aw_addr,
input[3:0] io_ain_aw_valid,
output[3:0] io_ain_aw_ready,
input[127:0] io_ain_w_data,
input[15:0] io_ain_w_strb,
input[3:0] io_ain_w_valid,
output[3:0] io_ain_w_ready,
output[7:0] io_ain_b_resp,
output[3:0] io_ain_b_valid,
input[3:0] io_ain_b_ready,
input[31:0] io_ain_ar_addr,
input[3:0] io_ain_ar_valid,
output[3:0] io_ain_ar_ready,
output[127:0] io_ain_r_data,
output[7:0] io_ain_r_resp,
output[3:0] io_ain_r_valid,
input[3:0] io_ain_r_ready,
output[7:0] io_aout_0_aw_addr,
output [0:0] io_aout_0_aw_valid,
input[0:0] io_aout_0_aw_ready,
output[31:0] io_aout_0_w_data,
output[3:0] io_aout_0_w_strb,
output [0:0] io_aout_0_w_valid,
input [0:0] io_aout_0_w_ready,
input[1:0] io_aout_0_b_resp,
input [0:0] io_aout_0_b_valid,
output [0:0] io_aout_0_b_ready,
output[7:0] io_aout_0_ar_addr,
output [0:0] io_aout_0_ar_valid,
input [0:0] io_aout_0_ar_ready,
input[31:0] io_aout_0_r_data,
input[1:0] io_aout_0_r_resp,
input [0:0] io_aout_0_r_valid,
output [0:0] io_aout_0_r_ready,
output[7:0] io_aout_1_aw_addr,
output [0:0] io_aout_1_aw_valid,
input [0:0] io_aout_1_aw_ready,
output[31:0] io_aout_1_w_data,
output[3:0] io_aout_1_w_strb,
output [0:0] io_aout_1_w_valid,
input[0:0] io_aout_1_w_ready,
input[1:0] io_aout_1_b_resp,
input[0:0] io_aout_1_b_valid,
output [0:0] io_aout_1_b_ready,
output[7:0] io_aout_1_ar_addr,
output [0:0] io_aout_1_ar_valid,
input [0:0] io_aout_1_ar_ready,
input[31:0] io_aout_1_r_data,
input[1:0] io_aout_1_r_resp,
input [0:0] io_aout_1_r_valid,
output [0:0] io_aout_1_r_ready,
output[7:0] io_aout_2_aw_addr,
output [0:0] io_aout_2_aw_valid,
input[0:0] io_aout_2_aw_ready,
output[31:0] io_aout_2_w_data,
output[3:0] io_aout_2_w_strb,
output [0:0] io_aout_2_w_valid,
input[0:0] io_aout_2_w_ready,
input[1:0] io_aout_2_b_resp,
input[0:0] io_aout_2_b_valid,
output [0:0] io_aout_2_b_ready,
output[7:0] io_aout_2_ar_addr,
output [0:0] io_aout_2_ar_valid,
input [0:0] io_aout_2_ar_ready,
input[31:0] io_aout_2_r_data,
input[1:0] io_aout_2_r_resp,
input [0:0] io_aout_2_r_valid,
output [0:0] io_aout_2_r_ready,
output[7:0] io_aout_3_aw_addr,
output [0:0] io_aout_3_aw_valid,
input[0:0] io_aout_3_aw_ready,
output[31:0] io_aout_3_w_data,
output[3:0] io_aout_3_w_strb,
output [0:0] io_aout_3_w_valid,
input[0:0] io_aout_3_w_ready,
input[1:0] io_aout_3_b_resp,
input[0:0] io_aout_3_b_valid,
output [0:0] io_aout_3_b_ready,
output[7:0] io_aout_3_ar_addr,
output [0:0] io_aout_3_ar_valid,
input [0:0] io_aout_3_ar_ready,
input[31:0] io_aout_3_r_data,
input[1:0] io_aout_3_r_resp,
input [0:0] io_aout_3_r_valid,
output[0:0] io_aout_3_r_ready
); Among them, the following interface will be connected to the wrapper of the systemverilog interface converter input[31:0] io_ain_aw_addr,
input[3:0] io_ain_aw_valid,
output[3:0] io_ain_aw_ready,
input[127:0] io_ain_w_data,
input[15:0] io_ain_w_strb,
input[3:0] io_ain_w_valid,
output[3:0] io_ain_w_ready,
output[7:0] io_ain_b_resp,
output[3:0] io_ain_b_valid,
input[3:0] io_ain_b_ready,
input[31:0] io_ain_ar_addr,
input[3:0] io_ain_ar_valid,
output[3:0] io_ain_ar_ready,
output[127:0] io_ain_r_data,
output[7:0] io_ain_r_resp,
output[3:0] io_ain_r_valid,
input[3:0] io_ain_r_ready, The ultimate intent of this is to automate the connection to the systemverilog interface. case class AxiLite4Demo(AW:Int, DW:Int, num:Int) extends Bundle with IMasterSlave {
val STRBW = DW / 8
val aw_addr = UInt(AW * num bits)
val aw_valid = UInt(num bits)
val aw_ready = UInt(num bits)
val w_data = UInt(DW * num bits)
val w_strb = UInt(STRBW * num bits)
val w_valid = UInt(num bits)
val w_ready = UInt(num bits)
val b_resp = UInt(2 * num bits)
val b_valid = UInt(num bits)
val b_ready = UInt(num bits)
val ar_addr = UInt(AW * num bits)
val ar_valid = UInt(num bits)
val ar_ready = UInt(num bits)
val r_data = UInt(DW * num bits)
val r_resp = UInt(2 * num bits)
val r_valid = UInt(num bits)
val r_ready = UInt(num bits)
override def asMaster(): Unit = {
in(b_resp, b_valid, aw_ready, w_ready, ar_ready, r_data, r_resp, r_valid)
out(aw_addr, aw_valid, w_data, w_strb, w_valid, b_ready, ar_addr, ar_valid, r_ready)
}
}
SpinalConfig().generateSystemVerilog(new Module{
val io = new Bundle{
val ain = slave( AxiLite4Demo(8,32,4))
val aout= Vec(master(AxiLite4Demo(8,32,1)), 4)
}
//todo io.ain cannot connect to tmp
val tmp = new PackedData(AxiLite4Demo(8,32,1), 4)
io.aout.zipWithIndex.map{case(i,v) => i := tmp.unpack(v)}
}.setDefinitionName("UnpackedMod"))
SpinalConfig().generateSystemVerilog(new Module{
val io = new Bundle{
val aout = master( AxiLite4Demo(8,32,4))
val ain = Vec(slave(AxiLite4Demo(8,32,1)), 4)
}
val tmp = new PackedData(AxiLite4Demo(8,32,1), 4)
io.ain.zipWithIndex.map{case(j,v)=> tmp.pack(j,v)}
//todo tmp cannot connect to io.aout
}.setDefinitionName("PackedMod"))
Still, thank you again! |
For example, the following systemverilog code, do we have a better way to automatically connect it to spinalHDL as a black box? interface AXI_LITE #(
parameter AXI_ADDR_WIDTH = -1,
parameter AXI_DATA_WIDTH = -1
);
localparam AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8;
typedef logic [AXI_ADDR_WIDTH-1:0] addr_t;
typedef logic [AXI_DATA_WIDTH-1:0] data_t;
typedef logic [AXI_STRB_WIDTH-1:0] strb_t;
// AW channel
addr_t aw_addr;
logic aw_valid;
logic aw_ready;
data_t w_data;
strb_t w_strb;
logic w_valid;
logic w_ready;
resp_t b_resp;
logic b_valid;
logic b_ready;
addr_t ar_addr;
logic ar_valid;
logic ar_ready;
data_t r_data;
resp_t r_resp;
logic r_valid;
logic r_ready;
modport Master (
output aw_addr, aw_valid, input aw_ready,
output w_data, w_strb, w_valid, input w_ready,
input b_resp, b_valid, output b_ready,
output ar_addr, ar_valid, input ar_ready,
input r_data, r_resp, r_valid, output r_ready
);
modport Slave (
input aw_addr, aw_valid, output aw_ready,
input w_data, w_strb, w_valid, output w_ready,
output b_resp, b_valid, input b_ready,
input ar_addr, ar_valid, output ar_ready,
output r_data, r_resp, r_valid, input r_ready
);
endinterface
interface AXI_ROUTING_RULES #(
/// The address width.
parameter int AXI_ADDR_WIDTH = -1,
/// The number of slaves in the routing table.
parameter int NUM_SLAVE = -1,
/// The number of rules in the routing table.
parameter int NUM_RULES = -1
);
struct packed {
logic enabled;
logic [AXI_ADDR_WIDTH-1:0] mask;
logic [AXI_ADDR_WIDTH-1:0] base;
} [NUM_RULES-1:0] rules [NUM_SLAVE];
modport xbar(input rules);
modport cfg(output rules);
endinterface
module axi_lite_xbar #(
/// The address width.
parameter int ADDR_WIDTH = -1,
/// The data width.
parameter int DATA_WIDTH = -1,
/// The number of master ports.
parameter int NUM_MASTER = 1,
/// The number of slave ports.
parameter int NUM_SLAVE = 1,
/// The number of routing rules.
parameter int NUM_RULES = -1
)(
input logic clk_i ,
input logic rst_ni ,
AXI_LITE.Slave master [NUM_MASTER] ,
AXI_LITE.Master slave [NUM_SLAVE] ,
AXI_ROUTING_RULES.xbar rules
);
endmodule
|
Hi ^^ Ahhhh, so by packing you mean putting all the signal of the interface into a single signal ? I'm not used at all to system verilog packed feature ^^ Else about AxiLite4Demo, the goal of PacketData is to not having you to do "* num" on everything in a custom bundle, but instead just use the regular AxiLite4 definition from the SpinalLib. (PackedData isn't implemented enough to be used as io yet, but that was just to show the concept) Does it make sense or i'm still off what you want to do XD ? |
Dear Dolu1990: object BlackBoxWrapperPlay extends App{
class BlackBoxWrapper extends BlackBox
case class TestIF(dw:Int) extends Bundle with IMasterSlave{
val v = Bool()
val r = Bool()
val d = UInt(dw bits)
override def asMaster(): Unit = {
in(v,d)
out(r)
}
}
class MyBox(
mn:Int = 1,
sn:Int = 1,
dw:Int = 1
) extends BlackBox{
// todo add func
// addInterfaceGenerics(
// "DW" -> dw
// )
addGenerics(
"MN" -> mn,
"SN" -> sn
)
val io = new Bundle{
val clock = in(Bool())
val reset = in(Bool())
val sif = new PackedData( slave(TestIF(dw)), mn)
val mif = new PackedData(master(TestIF(dw)), sn)
}
addRTLPath("./test.sv")
noIoPrefix()
}
val sv =
s"""systemverilog
|
|interface TestIF #(
| parameter int DW = 1
|);
| logic v;
| logic r;
| logic [DW-1:0] d;
| modport Master(
| input v,d,
| output r
| );
| modport Slave(
| output v,d,
| input r
| );
|endinterface
|
|module xbar #(
| parameter int MN = 2,
| parameter int SN = 2,
|)(
| input logic clock,
| input logic reset,
| TestIF.Master mif [MN],
| TestIF.Slave sif [SN]
|);
| ...
|endmodule
|
|""".stripMargin
// todo SpinalHDL generate systemverilog
val generateSV =
s"""module xbar_wrapper(
| ...
|)
|
| ... connect ...
| TestIF#(
| .DW(1)
| ) mif[0:1]();
| TestIF#(
| .DW(1)
| ) sif[0:1]();
|
|xbar#(
| .MN(2),
| .SN(2)
|) xbar_0(
| .clock ( clock ),
| .reset ( reset ),
| .mif ( mif ),
| .sif ( sif )
|);
|endmodule
|""".stripMargin
}
But the biggest difficulty in this is how to generate such a Systemverilog Wrapper. |
Hi :D Ahhh damned, currently PackedData with master/slave will not play well :/
I don't realy know much details about SystemVerilog, so far the aim was more about regular old Verilog / Vhdl blackboxes, but seems like the Verilog/Vhdl people are more and more migrating toward system verilog ^^ About "struct packed" if i understand well, it allow to access all bits of a structure as if they were all put together in a single bit vector right ? |
The default struct is unpacked. From the perspective of memory storage, unpacked means that the address space is not continuous; if a continuous address space is required, packed. From a design synthesis perspective, usually unpacked and packed synthesis results are the same, but there are exceptions. There are plenty of FPGA synthesis tools that will convert unpacked arrays into some kind of memory (either FFs or RAMs). |
Hi ^^ What solution did you adopted ? |
Many times, we need to integrate systemverilog/verilog (or languages that can generate them) into our SpinalHDL programs. In this process we need to define some compressed Bundles, such as:
For the convenience of use, we usually define it like this when using:
So, we need a helper that maps from (AxiLite4Demo(AW, DW, num)) to Vec((AxiLite4Demo(AW, DW, 1)), num). Of course, the more general the better. I have implemented a very low program to do this. There may be some situations that I have not considered, and the generality is not good. So, is there a better implementation?
My code is as follows:
By the way, the user-defined instruction flow has been run through, and the parameter system is also good to use. It is roughly used as follows:
Thank you very much!
The text was updated successfully, but these errors were encountered: