BlackBox:Verilog封装转换接口

本文作为SpinalHDL学习笔记第一篇,记录使用SpinalHDL的BlackBox库函数封装Verilog代码的方法。

SpinalHDL学习笔记总纲链接如下:

SpinalHDL 学习笔记-CSDN博客

现有的Verilog代码是不能直接放入SpinalHDL代码里的,需要使用BlackBox库函数进行封装,打个比方,Verilog是USB接口,Scala是TypeC,就需要一个转换器才能进行数据传输,这个转换器就是BlackBox,本文以SPI总线的封装为例介绍BlackBox。

代码github路径:

GitHub - tklk610/SpinalHDL-Blackbox-for-SPI: 本文以SPI总线的封装为例介绍BlackBox

目录:

1.导入包和库

2.SpiPhyIfBundle

3.spi_v1p0

4.SpiTop

整个代码分为四部分,导入的包和库文件,1个SpiTop component,一个SpiPhyIfBundle Bundle,一个spi_v1p0 BlockBox。SpiTop是顶层component,spi_v1p0是封装了Verilog代码的BlockBox,SpiPhyIfBundle是对外的Spi接口,spi_v1p0和SpiPhyIfBundle在SpiTop里链接。

导入包和库

package parrot.perp.spi

import parrot.common.CKCELL
import spinal.lib._
import spinal.core._
import spinal.lib.bus.amba3.apb.{Apb3, Apb3Config}
import spinal.lib.io.{TriState, TriStateArray}

 SpiPhyIfBundle

case class SpiPhyIfBundle() extends Bundle with IMasterSlave {
    val csn = Vec(TriState(Bool()), 8)
    val sck = TriState(Bool())
//    val csn_m =TriState(Bool())
//    val sck_m =TriState(Bool())
    val miso = TriState(Bool())
    val mosi = TriState(Bool())

//    val csn_s = in Bool ()
//    val sck_s = in Bool ()
//    val csn_m = out Bits (8 bits)
//    val sck_m = out Bool ()
//    val miso  = in Bool ()
//    val mosi  = out Bool ()

    override def asMaster(): Unit = {
        master(csn(0), csn(1), csn(2), csn(3), csn(4), csn(5), csn(6), csn(7), sck, miso, mosi)
    }
}

SpiPhyIfBundle定义了标准Spi的对外4个接口,包括csn、sck、miso和mosi,这里csn是8位,对应8个从机。

override def asMaster()是作为主机情况下的固定写法。

spi_v1p0

case class spi_v1p0(
    APB_DWIDTH: Int = 32,
    CFG_FRAME_SIZE: Int = 32,
    CFG_FIFO_DEPTH: Int = 32,
    CFG_CLK: Int = 7,
    DEFAULT_MODE: Bits = B"16'h0400"
) extends BlackBox {
    addGeneric("APB_DWIDTH", APB_DWIDTH)
    addGeneric("CFG_FRAME_SIZE", CFG_FRAME_SIZE)
    addGeneric("CFG_FIFO_DEPTH", CFG_FIFO_DEPTH)
    addGeneric("CFG_CLK", CFG_CLK)
    addGeneric("DEFAULT_MODE", DEFAULT_MODE)
    val io = new Bundle() {
        // APB
        val PCLK    = in Bool ()
        val PRESETN = in Bool ()
        val PSEL    = in Bits (1 bit)
        val PENABLE = in Bool ()
        val PWRITE  = in Bool ()
        val PADDR   = in UInt (7 bits)
        val PWDATA  = in Bits (32 bits)
        val PRDDATA = out Bits (32 bits)
        val PREADY  = out Bool ()
        // SPI PHY
        val aresetn    = in Bool ()
        val SPISSI     = in Bool ()
        val SPISDI     = in Bool ()
        val SPICLKI    = in Bool ()
        val SPIINT     = out Bool ()
        val SPISS      = out Bits (8 bits)
        val SPISCLKO   = out Bool ()
        val SPIRXAVAIL = out Bool ()
        val SPITXRFM   = out Bool ()
        val SPIOEN     = out Bool ()
        val SPISDO     = out Bool ()
        val SPIMODE    = out Bool ()
    }

    noIoPrefix()

    addRTLPath("./hw/verilog/spi/spi_v1p0.v")
    addRTLPath("./hw/verilog/spi/spi_chanctrl.v")
    addRTLPath("./hw/verilog/spi/spi_clockmux.v")
    addRTLPath("./hw/verilog/spi/spi_control.v")
    addRTLPath("./hw/verilog/spi/spi_fifo.v")
    addRTLPath("./hw/verilog/spi/spi_rf.v")

}

使用addRTLPath将Verilog代码吃进来,io定义了这些代码里的信号, noIoPrefix()规定IO信号名不加前缀。

SpiTop

case class SpiTop(apb3cfg: Apb3Config = Apb3Config(8, 32)) extends Component {
    val io = new Bundle() {
        val apb3     = slave(Apb3(apb3cfg))
        val dma_txe  = out Bool ()
        val dma_rxne = out Bool ()
        val irq      = out Bool ()
        val phy      = master(SpiPhyIfBundle())
    }
    require(apb3cfg.addressWidth >= 8, "addressWidth must over 8")
    val SpiBlackBoxInst = spi_v1p0()

    val spi_scko_buffer = Bool()
    val spi_scki_buffer = Bool()
    if (GenerationFlags.synthesis) {
        val spiClkInBuffer = CKCELL("CKBUF")
        val spiClkOutBuffer = CKCELL("CKBUF")
        spiClkOutBuffer.io.A := SpiBlackBoxInst.io.SPISCLKO
        spi_scko_buffer := spiClkOutBuffer.io.Z
        spiClkInBuffer.io.A := io.phy.sck.read
        spi_scki_buffer := spiClkInBuffer.io.Z
    } else {
        spi_scko_buffer := SpiBlackBoxInst.io.SPISCLKO
        spi_scki_buffer := io.phy.sck.read
    }

    SpiBlackBoxInst.io.PCLK    := ClockDomain.current.readClockWire
    SpiBlackBoxInst.io.PRESETN := ClockDomain.current.readResetWire
    SpiBlackBoxInst.io.PSEL    := io.apb3.PSEL
    SpiBlackBoxInst.io.PENABLE := io.apb3.PENABLE
    SpiBlackBoxInst.io.PWRITE  := io.apb3.PWRITE
    SpiBlackBoxInst.io.PADDR   := io.apb3.PADDR(6 downto 0)
    SpiBlackBoxInst.io.PWDATA  := io.apb3.PWDATA
    io.apb3.PRDATA             := SpiBlackBoxInst.io.PRDDATA
    io.apb3.PREADY             := SpiBlackBoxInst.io.PREADY
    io.apb3.PSLVERROR.clear()

    SpiBlackBoxInst.io.aresetn := ClockDomain.current.readResetWire
    SpiBlackBoxInst.io.SPISSI  := io.phy.csn(0).read
    SpiBlackBoxInst.io.SPICLKI := spi_scki_buffer
//    SpiBlackBoxInst.io.SPISDI  := io.phy.miso
    for (i <- 0 until 8) io.phy.csn(i).write := SpiBlackBoxInst.io.SPISS(i)
    io.phy.sck.write := spi_scko_buffer
    // 主机模式
    when(SpiBlackBoxInst.io.SPIMODE) {
        io.phy.sck.writeEnable.set()
        io.phy.csn.foreach(_.writeEnable.set())
        io.phy.miso.writeEnable.clear()
        io.phy.miso.write.set()
        SpiBlackBoxInst.io.SPISDI := io.phy.miso.read
        io.phy.mosi.writeEnable   := SpiBlackBoxInst.io.SPIOEN
        io.phy.mosi.write         := SpiBlackBoxInst.io.SPISDO
    } otherwise {
        io.phy.sck.writeEnable.clear()
        io.phy.csn.foreach(_.writeEnable.clear())
        io.phy.miso.writeEnable := SpiBlackBoxInst.io.SPIOEN
        io.phy.miso.write       := SpiBlackBoxInst.io.SPISDO
        io.phy.mosi.writeEnable.clear()
        io.phy.mosi.write.set()
        SpiBlackBoxInst.io.SPISDI := io.phy.mosi.read
    }


    io.dma_txe  := SpiBlackBoxInst.io.SPITXRFM
    io.dma_rxne := SpiBlackBoxInst.io.SPIRXAVAIL
    io.irq      := SpiBlackBoxInst.io.SPIINT

    //    val csn_s = in Bool ()
    //    val sck_s = in Bool ()
    //    val csn_m = out Bits (8 bits)
    //    val sck_m = out Bool ()
    //    val miso  = in Bool ()
    //    val mosi  = out Bool ()
}

SpiTop里定义了接口io,包括APB总线信号,DMA相关信号和外部接口信号phy。

val phy = master(SpiPhyIfBundle()):将SpiPhyIfBundle作为master的端口信号定义phy;

val SpiBlackBoxInst = spi_v1p0():将spi_v1p0作为BlockBox进行声明;

require(apb3cfg.addressWidth >= 8, "addressWidth must over 8"):类似断言

将SpiTop里的io和spi_v1p0里的信号做链接。

基于SpinalHDL BlackBox的CANFD实现,结构大体相同:

GitHub - tklk610/SpinalHDL-BlackBox-for-CANFD

  • 30
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是一个简单的 Verilog 封装 Axis 接口的例程: ``` module axis_wrapper ( input clk, input reset, input [DATA_WIDTH-1:0] data_in, input tlast_in, input tvalid_in, output [DATA_WIDTH-1:0] data_out, output tlast_out, output tvalid_out ); // 定义 AXI-Stream 接口 wire axis_tready; wire axis_tvalid; wire [DATA_WIDTH-1:0] axis_tdata; wire axis_tlast; // 实例化 AXI-Stream 接口 axis_interface #( .DATA_WIDTH(DATA_WIDTH) ) axis_inst ( .clk(clk), .reset(reset), .tready(axis_tready), .tvalid(axis_tvalid), .tdata(axis_tdata), .tlast(axis_tlast) ); // 将输入信号连接到 AXI-Stream 接口 assign axis_tdata = data_in; assign axis_tlast = tlast_in; assign axis_tvalid = tvalid_in; // 将输出信号连接到 AXI-Stream 接口 assign data_out = axis_tdata; assign tlast_out = axis_tlast; assign tvalid_out = axis_tvalid && axis_tready; endmodule module axis_interface #( parameter DATA_WIDTH = 32 )( input clk, input reset, output reg tready, input tvalid, input [DATA_WIDTH-1:0] tdata, input tlast ); // 定义内部计数器 reg [7:0] count = 0; always @(posedge clk or posedge reset) begin if (reset) begin count <= 0; tready <= 0; end else begin // 当 tvalid 信号为高电平时计数器递增 if (tvalid) begin count <= count + 1; tready <= 1; end else begin tready <= 0; end // 当计数器递增到 8 时,将计数器清零,表示已经传输完一个数据包 if (count == 8) begin count <= 0; end end end endmodule ``` 在这个例程中,我们首先定义了一个 Axis 接口封装模块 `axis_wrapper`,该模块包含了输入和输出的数据、TLAST 和 TVALID 信号。然后我们实例化了一个 Axis 接口模块 `axis_interface`,并将输入信号连接到 AXI-Stream 接口,将输出信号连接到 AXI-Stream 接口。在 `axis_interface` 模块中,我们使用了一个计数器来计算传输的数据包的数量,当计数器递增到 8 时,将计数器清零,表示已经传输完一个数据包。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千穹凌帝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值