Bundle

一、描述(Description)

Bundle是一种符合类型, 它定义了一组命名了的信号(这些信号是SpinalHDL的基础类型)

Bundle类型能用来搭建数据结构, 总线和接口的模型。

二、声明(Declaration)

声明bundle的语句如下:

case class myBundle extends Bundle {
    val bundleItem0 = AnyType
    val bundleItem1 = AnyType
    val bundleItemN = AnyType
}

例如, 一个包含颜色信号的bundle可以如下定义:

case class Color(channelWidth: Int) extends Bundle {
    val r, g, b = UInt(channelWidth bits)
}

你可以在“SpinalHDL用例”当中找到APB3的定义

  1. 条件信号(Conditional signals)

    Bundle中的信号可以根据条件选择定义。如下举例, 除非datawidth大于0, mybundle中不会有data信号:

    case class myBundle(dataWidth: Int) extends Bundle {
        val data = (dataWidth > 0) generate (UInt(dataWidth bits))
    }
    

三、操作符(Operators)

下述是Bundle可用的操作符:

  1. 对比操作(Comparison)

操作符 描述 返回类型
x===y 相等 Bool
x=/=y 不相等 Bool
val color1 = Color(8)
color1.r := 0
color1.g := 0
color1.b := 0

val color2 = Color(8)
color2.r := 0
color2.g := 0
color2.b := 0

myBool := color1 === color2
  1. 类型转换(Type cast)

操作符 描述 返回类型
x.asBits 二进制转换成Bits Bits(w(x) bits)
val color1 = Color(8)
val myBits := color1.asBits

Verilog:

wire       [7:0]    color1_data;
wire       [7:0]    myBits;

assign myBits = color1_data;

bundle的元素在二进制串中按照定义的顺序排列, 因此rcolor1myBits(LSB)中的0-8, 之后按顺序依次所gb

  1. 把Bits转换成Bundle(Convert Bits back to Bundle)

    .assignFromBits操作符相当于.asBits的相反操作。

操作符 描述 返回类型
x.assignFromBits(y) 把Bits(y)转换成Bundle(x) UInt
x.assignFromBits(y, hi, lo) 把Bits(y)转换成Bundle(x), 并带有high/low的边界 UInt

下例把一个叫做CommonDataBus的Bundle存进环形buffer(3rd party memory)中, 之后读出Bits并把他们转换回CommonDataBus。

CommonDataBus

case class TestBundle () extends Component {
    val io = new Bundle {
        val we      = in    Bool()
        val addrWr  = in    UInt(7 bits)
        val dataIn  = slave (CommonDataBus())

        val addrRd  = in    UInt(7 bits)
        val dataOut = master(CommonDataBus())
    }

    val mm = Ram3rdParty_1w_1rs (
        G_DATA_WIDTH = io.dataIn.getBitsWidth,
        G_ADDR_WIDTH = io.addrWr.getBitsWidth,
        G_VENDOR     = "Intel_Arria10_M20K")

    mm.io.clk_in    := clockDomain.readClockWire
    mm.io.clk_out   := clockDomain.readClockWire
    
    mm.io.we        := io.we
    mm.io.addr_wr   := io.addrWr.asBits
    mm.io.d         := io.dataIn.asBits

    mm.io.addr_rd   := io.addrRd.asBits
    io.dataOut.assignFromBits(mm.io.q)
}

四、IO类型指导(IO Element direction)

当你在模块的IO定义中定义一个Bundle, 你需要指定它的方向。

  1. in/out

    如果bundle的所有元素方向相同, 可以使用in(MyBundle())或者out(MyBundle())

    例如:

    val io = new Bundle {
        val input  = in (Color(8))
        val output = out(Color(8))
    }
    
  2. master/slave

    如果你的接口遵从于master/slave拓扑, 你可以使用IMasterSlave特性, 之后你需要对函数def asMaster(): Unit配置来补全每个来自master方面的元素的方向, 这之后你就可以在I定义中用master(MyBundle())slave(MyBundle())语句。

    有些函数, 如同Flow类中的toStream方法, 会以toXXX的方式定义。这些函数常常由master侧调用。此外, fromXXX函数是为slave侧设计的。通常来说, master侧可用的函数比slave更多。

    例如:

    case class HandShake(payloadWidth: Int) extends Bundle with IMasterSlave {
        val valid   = Bool()
        val ready   = Bool()
        val payload = Bits(payloadWidth bits)
    
        //你需要补全这个asMaster函数
        //这个函数应该设置master侧的每个信号的方向
        override def asMaster(): Unit = {
            out(valid, payload)
            in(ready)
        }
    }
    
    val io = new Bundle {
        val input   = slave(HandShake(8))
        val output  = master(HandShake(8))
    }
    

    Verilog:

    module MyTopLevel (
        input               io_input_valid,
        output reg          io_input_ready,
        input      [7:0]    io_input_payload,
        output reg          io_output_valid,
        input               io_output_ready,
        output reg [7:0]    io_output_payload
    );
    
    wire                when_MyTopLevel_l56;
    
    always @(*) begin
        io_input_ready = 1'b0;
        if(when_MyTopLevel_l56) begin
            io_input_ready = 1'b1;
        end
    end
    
    always @(*) begin
        io_output_valid = 1'b0;
        if(when_MyTopLevel_l56) begin
            io_output_valid = 1'b1;
        end
    end
    
    always @(*) begin
        io_output_payload = 8'h0;
        if(when_MyTopLevel_l56) begin
            io_output_payload = 8'h23;
        end
    end
    
    assign when_MyTopLevel_l56 = (io_output_ready && io_input_valid);
    
    endmodule