简单的实例

一、APB3

  1. 简介

    该例子将会展示定义APB3类的语句。

  2. 规范

    ARM关于APB3的端口规范如下:

信号名称 类型 驱动端 描述
PADDR UInt(位宽 bits) Master 字节为单位的地址
PSEL Bits(selWidth) Master 每个从端1bit
PENABLE Bool Master
PWRITE Bool Master
PWDATA Bits(dataWidth bits) Master
PREADY Bool Slave
PRDATA Bits(dataWidth bits) Slave
PSLVERROR Bool Slave 可选
  1. 实现

上述规范说明APB3总线有许多可能的配置。为了实现多样的配置,我们可以在Scala中定义一个配置类:

case class Apb3Config(
  addressWidth  : Int,
  dataWidth     : Int,
  selWidth      : Int     = 1,
  useSlaveError : Boolean = true
)

随后我们可以定义用来表示硬件总线的APB3类:

case class Apb3(config: Apb3Config) extends Bundle with IMasterSlave {
  val PADDR      = UInt(config.addressWidth bits)
  val PSEL       = Bits(config.selWidth bits)
  val PENABLE    = Bool()
  val PREADY     = Bool()
  val PWRITE     = Bool()
  val PWDATA     = Bits(config.dataWidth bits)
  val PRDATA     = Bits(config.dataWidth bits)
  val PSLVERROR  = if(config.useSlaveError) Bool else null

  override def asMaster(): Unit = {
    out(PADDR,PSEL,PENABLE,PWRITE,PWDATA)
    in(PREADY,PRDATA)
    if(config.useSlaveError) in(PSLVERROR)
  }
}
  1. 应用(Usage)

    val apbConfig = Apb3Config(
      addressWidth  = 16,
      dataWidth     = 32,
      selWidth      = 1,
      useSlaveError = false
    )
    
    val io = new Bundle{
      val apb = slave(Apb3(apbConfig))
    }
    
    io.apb.PREADY := True
    when(io.apb.PSEL(0) && io.apb.PENABLE){
      //...
    }
    

    上述代码比较简陋,个人编写了一个简单的APB3的Slave端总线挂一个寄存堆的代码,如下:

    package Apb3
    
    import spinal.core._
    import spinal.lib._   // 使用IMasterSlave需要调用lib库
    
    import scala.math._
    
    class Apb3Slave extends Component{
      case class APB3Config(addressWidth: Int,
                            dataWidth: Int,
                            selWidth : Int,
                            useSlaveError : Boolean) // APB3总线的参数配置
    
    
      case class APB3 (val config: APB3Config) extends Bundle with IMasterSlave {
        val PADDR      = UInt(config.addressWidth bits)
        val PSEL       = Bits(config.selWidth bits)
        val PENABLE    = Bool
        val PREADY     = Bool
        val PWRITE     = Bool
        val PWDATA     = Bits(config.dataWidth bits)
        val PRDATA     = Bits(config.dataWidth bits)
        val PSLVERROR  = if(config.useSlaveError) Bool() else null   // 只有当配置中的useSlaveError为真时才生成该端口
    
        override def asMaster() : Unit = {
          out(PADDR,PSEL,PENABLE,PWRITE,PWDATA)
          in(PREADY,PRDATA)
    
          if(config.useSlaveError) in(PSLVERROR)
        }
        // 使用asMaster的好处在于,asSlave直接就默认是Master端口的反面
    
    
    
      }
    
      val apbConfig = APB3Config(addressWidth = 4,dataWidth = 32,selWidth = 1,useSlaveError = false) // 用户配置参数
      val io = new Bundle{
        val slaveBus = slave(APB3(apbConfig)) // 定义总线的slave端
        //val masterBus = master(APB3(apbConfig))// 定义总线的master端,不知道为何,用asMater定义,无法使用io.masterBus.PADDR表述
      }
    
    
      io.slaveBus.PREADY := True
      val num        = pow(2, io.slaveBus.config.addressWidth)
      val mem        = Mem(Bits(io.slaveBus.config.dataWidth bits), num.toInt)
      mem.write(
        enable  = io.slaveBus.PSEL(0) && io.slaveBus.PWRITE,
        address = io.slaveBus.PADDR,
        data    = io.slaveBus.PWDATA
      )
      io.slaveBus.PRDATA := mem.readSync(
        enable  = io.slaveBus.PSEL(0) && io.slaveBus.PENABLE,
        address = io.slaveBus.PADDR
      )
    
    }
    object Apb3SlaveInst {
      def main(args: Array[String]) {
        SpinalVerilog(new Apb3Slave)
      }
    }
    

    生成的Verilog代码为:

    // Generator : SpinalHDL v1.6.0    git head : 73c8d8e2b86b45646e9d0b2e729291f2b65e6be3
    // Component : Apb3Slave
    module Apb3Slave (
      input      [3:0]    io_slaveBus_PADDR,
      input      [0:0]    io_slaveBus_PSEL,
      input               io_slaveBus_PENABLE,
      output              io_slaveBus_PREADY,
      input               io_slaveBus_PWRITE,
      input      [31:0]   io_slaveBus_PWDATA,
      output     [31:0]   io_slaveBus_PRDATA,
      input               clk,
      input               reset
    );
      reg        [31:0]   _zz_mem_port1;
      wire                _zz_mem_port;
      wire                _zz_io_slaveBus_PRDATA;
      reg [31:0] mem [0:15];
    
      assign _zz_mem_port = (io_slaveBus_PSEL[0] && io_slaveBus_PWRITE);
      always @(posedge clk) begin
        if(_zz_mem_port) begin
          mem[io_slaveBus_PADDR] <= io_slaveBus_PWDATA;
        end
      end
    
      always @(posedge clk) begin
        if(_zz_io_slaveBus_PRDATA) begin
          _zz_mem_port1 <= mem[io_slaveBus_PADDR];
        end
      end
    
      assign io_slaveBus_PREADY = 1'b1;
      assign _zz_io_slaveBus_PRDATA = (io_slaveBus_PSEL[0] && io_slaveBus_PENABLE);
      assign io_slaveBus_PRDATA = _zz_mem_port1;
    
    endmodule
    

二、进位加法器(Carry Adder)

该示例定义了一个具有输入ab以及result输出的组件。在任何时候,结果都是ab(组合逻辑)的和。这个求和是由进位加法器完成的。

class CarryAdder(size : Int) extends Component{
  val io = new Bundle{
    val a = in UInt(size bits)
    val b = in UInt(size bits)
    val result = out UInt(size bits)      //result = a + b
  }

  var c = False                   //Carry, like a VHDL variable
  for (i <- 0 until size) {
    //Create some intermediate value in the loop scope.
    val a = io.a(i)
    val b = io.b(i)

    //The carry adder's asynchronous logic
    io.result(i) := a ^ b ^ c
    c \= (a & b) | (a & c) | (b & c);    //variable assignment
  }
}


object CarryAdderProject {
  def main(args: Array[String]) {
    SpinalVhdl(new CarryAdder(4))
  }
}

会生成如下代码:

// Generator : SpinalHDL v1.6.0    git head : 73c8d8e2b86b45646e9d0b2e729291f2b65e6be3
// Component : CarryAdder



module CarryAdder (
  input      [3:0]    io_a,
  input      [3:0]    io_b,
  output reg [3:0]    io_result
);
  reg                 c_4;
  reg                 c_3;
  reg                 c_2;
  reg                 c_1;
  wire                c;
  wire                _zz_c_1;
  wire                _zz_c_1_1;
  wire                _zz_c_2;
  wire                _zz_c_2_1;
  wire                _zz_c_3;
  wire                _zz_c_3_1;
  wire                _zz_c_4;
  wire                _zz_c_4_1;

  always @(*) begin
    c_4 = c_3;
    c_4 = (((_zz_c_4 && _zz_c_4_1) || (_zz_c_4 && c_3)) || (_zz_c_4_1 && c_3));
  end

  always @(*) begin
    c_3 = c_2;
    c_3 = (((_zz_c_3 && _zz_c_3_1) || (_zz_c_3 && c_2)) || (_zz_c_3_1 && c_2));
  end

  always @(*) begin
    c_2 = c_1;
    c_2 = (((_zz_c_2 && _zz_c_2_1) || (_zz_c_2 && c_1)) || (_zz_c_2_1 && c_1));
  end

  always @(*) begin
    c_1 = c;
    c_1 = (((_zz_c_1 && _zz_c_1_1) || (_zz_c_1 && c)) || (_zz_c_1_1 && c));
  end

  assign c = 1'b0;
  assign _zz_c_1 = io_a[0];
  assign _zz_c_1_1 = io_b[0];
  always @(*) begin
    io_result[0] = ((_zz_c_1 ^ _zz_c_1_1) ^ c);
    io_result[1] = ((_zz_c_2 ^ _zz_c_2_1) ^ c_1);
    io_result[2] = ((_zz_c_3 ^ _zz_c_3_1) ^ c_2);
    io_result[3] = ((_zz_c_4 ^ _zz_c_4_1) ^ c_3);
  end

  assign _zz_c_2 = io_a[1];
  assign _zz_c_2_1 = io_b[1];
  assign _zz_c_3 = io_a[2];
  assign _zz_c_3_1 = io_b[2];
  assign _zz_c_4 = io_a[3];
  assign _zz_c_4_1 = io_b[3];

endmodule

三、颜色求和(Color Summing)

首先,定义一个带有加法的Color Bundle

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

  def +(that: Color): Color = {
    val result = Color(channelWidth)
    result.r := this.r + that.r
    result.g := this.g + that.g
    result.b := this.b + that.b
    return result
  }

  def clear(): Color ={
    this.r := 0
    this.g := 0
    this.b := 0
    this
  }
}

然后,定义一个带source输入(颜色向量)和输出result(source输入之和)的组件。

class ColorSumming(sourceCount: Int, channelWidth: Int) extends Component {
  val io = new Bundle {
    val sources = in Vec(Color(channelWidth), sourceCount)
    val result = out(Color(channelWidth))
  }

  var sum = Color(channelWidth)
  sum.clear()
  for (i <- 0 to sourceCount - 1) {
    sum \= sum + io.sources(i)
  }
  io.result := sum
}

总的SpinalHDL代码为:

package ColorSumming
import spinal.core._
import spinal.lib._
case class Color(channelWidth: Int) extends Bundle {
  val r = UInt(channelWidth bits)
  val g = UInt(channelWidth bits)
  val b = UInt(channelWidth bits)

  def +(that: Color): Color = {
    val result = Color(channelWidth)
    result.r := this.r + that.r
    result.g := this.g + that.g
    result.b := this.b + that.b
    return result
  }

  def clear(): Color ={
    this.r := 0
    this.g := 0
    this.b := 0
    this
  }
}
class ColorSumming(sourceCount: Int, channelWidth: Int) extends Component {
  val io = new Bundle {
    val sources = in Vec(Color(channelWidth), sourceCount)
    val result = out(Color(channelWidth))
  }

  var sum = Color(channelWidth)
  sum.clear()
  for (i <- 0 to sourceCount - 1) {
    sum \= sum + io.sources(i)
  }
  io.result := sum
}
object ColorSummingInst {
  def main(args: Array[String]) {
    SpinalVerilog(new ColorSumming(3, 4))
  }
}

生成的Verilog代码为:

// Generator : SpinalHDL v1.6.0    git head : 73c8d8e2b86b45646e9d0b2e729291f2b65e6be3
// Component : ColorSumming
module ColorSumming (
  input      [3:0]    io_sources_0_r,
  input      [3:0]    io_sources_0_g,
  input      [3:0]    io_sources_0_b,
  input      [3:0]    io_sources_1_r,
  input      [3:0]    io_sources_1_g,
  input      [3:0]    io_sources_1_b,
  input      [3:0]    io_sources_2_r,
  input      [3:0]    io_sources_2_g,
  input      [3:0]    io_sources_2_b,
  output     [3:0]    io_result_r,
  output     [3:0]    io_result_g,
  output     [3:0]    io_result_b
);
  reg        [3:0]    _zz_io_result_r;
  reg        [3:0]    _zz_io_result_g;
  reg        [3:0]    _zz_io_result_b;
  reg        [3:0]    _zz_io_result_r_1;
  reg        [3:0]    _zz_io_result_g_1;
  reg        [3:0]    _zz_io_result_b_1;
  reg        [3:0]    _zz_io_result_r_2;
  reg        [3:0]    _zz_io_result_g_2;
  reg        [3:0]    _zz_io_result_b_2;
  wire       [3:0]    sum_r;
  wire       [3:0]    sum_g;
  wire       [3:0]    sum_b;

  always @(*) begin
    _zz_io_result_r = _zz_io_result_r_1;
    _zz_io_result_r = (_zz_io_result_r_1 + io_sources_2_r);
  end

  always @(*) begin
    _zz_io_result_g = _zz_io_result_g_1;
    _zz_io_result_g = (_zz_io_result_g_1 + io_sources_2_g);
  end

  always @(*) begin
    _zz_io_result_b = _zz_io_result_b_1;
    _zz_io_result_b = (_zz_io_result_b_1 + io_sources_2_b);
  end

  always @(*) begin
    _zz_io_result_r_1 = _zz_io_result_r_2;
    _zz_io_result_r_1 = (_zz_io_result_r_2 + io_sources_1_r);
  end

  always @(*) begin
    _zz_io_result_g_1 = _zz_io_result_g_2;
    _zz_io_result_g_1 = (_zz_io_result_g_2 + io_sources_1_g);
  end

  always @(*) begin
    _zz_io_result_b_1 = _zz_io_result_b_2;
    _zz_io_result_b_1 = (_zz_io_result_b_2 + io_sources_1_b);
  end

  always @(*) begin
    _zz_io_result_r_2 = sum_r;
    _zz_io_result_r_2 = (sum_r + io_sources_0_r);
  end

  always @(*) begin
    _zz_io_result_g_2 = sum_g;
    _zz_io_result_g_2 = (sum_g + io_sources_0_g);
  end

  always @(*) begin
    _zz_io_result_b_2 = sum_b;
    _zz_io_result_b_2 = (sum_b + io_sources_0_b);
  end

  assign sum_r = 4'b0000;
  assign sum_g = 4'b0000;
  assign sum_b = 4'b0000;
  assign io_result_r = _zz_io_result_r;
  assign io_result_g = _zz_io_result_g;
  assign io_result_b = _zz_io_result_b;

endmodule

四、带清除的计数器(Counter with clear)

本例定义了一个具有clear输入和value输出的组件。每个时钟周期输出的value都是递增的,但是当clear值很高时,value被清除。

class Counter(width : Int) extends Component{
  val io = new Bundle{
    val clear = in Bool()
    val value = out UInt(width bits)
  }
  val register = Reg(UInt(width bits)) init(0)
  register := register + 1
  when(io.clear){
    register := 0
  }
  io.value := register
}

五、锁相环黑盒与复位控制器(PLL BlackBox and reset controller)

假设用户需要定义一个TopLevel组件来例化一个PLLBlackBox,并且利用它建立一个新的时钟域应用到设计中。同时用户可能需要将外部的异步复位逻辑应用到该时钟域中,与一个同步复位资源连接。

本小章中需要导入如下库:

import spinal.core._
import spinal.lib._
  1. 锁相环黑盒定义(The PLL BlackBox definition)

    下述代码展示了如何定义锁相环BlackBox

    class PLL extends BlackBox{
      val io = new Bundle{
        val clkIn    = in Bool()
        val clkOut   = out Bool()
        val isLocked = out Bool()
      }
    
      noIoPrefix()
    }
    

    其会生成如下VHDL代码:

    component PLL is
      port(
        clkIn    : in std_logic;
        clkOut   : out std_logic;
        isLocked : out std_logic
      );
    end component;
    
  2. 顶层定义(TopLevel definition)

    下述例子展示了如何定义个人的TopLevel来例化锁相环,建立新clockdomain并且将异步复位的输入连接至同步复位端口:

    class TopLevel extends Component{
      val io = new Bundle {
        val aReset    = in Bool()
        val clk100Mhz = in Bool()
        val result    = out UInt(4 bits)
      }
    
      // 创建一个Area来管理所有时钟和复位
      val clkCtrl = new Area {
        //例化并驱动PLL
        val pll = new PLL
        pll.io.clkIn := io.clk100Mhz
    
        //建立新时钟域'core'
        val coreClockDomain = ClockDomain.internal(
          name = "core",
          frequency = FixedFrequency(200 MHz)  // 该频率要求可以被使用
        )                                      // 通过coreClockDomain用户来做某些计算
    
        //驱动coreClockDomain的时钟和复位
        coreClockDomain.clock := pll.io.clkOut
        coreClockDomain.reset := ResetCtrl.asyncAssertSyncDeassert(
          input = io.aReset || ! pll.io.isLocked,
          clockDomain = coreClockDomain
        )
      }
    
      //建立clkCtrl.coreClockDomain下的ClockingArea
      val core = new ClockingArea(clkCtrl.coreClockDomain){
        val counter = Reg(UInt(4 bits)) init(0)
        counter := counter + 1
        io.result := counter
      }
    }
    

    会生成如下Verilog代码:

    // Generator : SpinalHDL v1.6.0    git head : 73c8d8e2b86b45646e9d0b2e729291f2b65e6be3
    // Component : MyTopLevel
    module MyTopLevel (
      input               io_aReset,
      input               io_clk100Mhz,
      output     [3:0]    io_result
    );
      wire                clkCtrl_pll_clkOut;
      wire                clkCtrl_pll_isLocked;
      wire                bufferCC_1_io_dataOut;
      wire                core_clk;
      wire                core_reset;
      wire                _zz_1;
      reg        [3:0]    core_counter;
    
      PLL clkCtrl_pll (
        .clkIn       (io_clk100Mhz          ), //i
        .clkOut      (clkCtrl_pll_clkOut    ), //o
        .isLocked    (clkCtrl_pll_isLocked  )  //o
      );
      BufferCC bufferCC_1 (
        .io_dataIn     (1'b0                   ), //i
        .io_dataOut    (bufferCC_1_io_dataOut  ), //o
        .core_clk      (core_clk               ), //i
        ._zz_1         (_zz_1                  )  //i
      );
      assign core_clk = clkCtrl_pll_clkOut;
      assign _zz_1 = (io_aReset || (! clkCtrl_pll_isLocked));
      assign core_reset = bufferCC_1_io_dataOut;
      assign io_result = core_counter;
      always @(posedge core_clk or posedge core_reset) begin
        if(core_reset) begin
          core_counter <= 4'b0000;
        end else begin
          core_counter <= (core_counter + 4'b0001);
        end
      end
    
    
    endmodule
    
    module BufferCC (
      input               io_dataIn,
      output              io_dataOut,
      input               core_clk,
      input               _zz_1
    );
      (* async_reg = "true" *) reg                 buffers_0;
      (* async_reg = "true" *) reg                 buffers_1;
    
      assign io_dataOut = buffers_1;
      always @(posedge core_clk or posedge _zz_1) begin
        if(_zz_1) begin
          buffers_0 <= 1'b1;
          buffers_1 <= 1'b1;
        end else begin
          buffers_0 <= io_dataIn;
          buffers_1 <= buffers_0;
        end
      end
    
    
    endmodule
    

六、RGB转灰度图

假设要建立一个可以将RGB图转化成灰度图并存到外部Memory的组件。

IO名称 方向 描述
clear in 清除所有内部寄存器
r,g,b in 颜色输入
wr out 写存储
address out 存储地址,每周期增加
data out 存储数据,灰度等级
class RgbToGray extends Component{
  val io = new Bundle{
    val clear = in Bool()
    val r,g,b = in UInt(8 bits)

    val wr = out Bool()
    val address = out UInt(16 bits)
    val data = out UInt(8 bits)
  }

  def coef(value : UInt,by : Float) : UInt = (value * U((255*by).toInt,8 bits) >> 8)
  val gray = RegNext(
    coef(io.r,0.3f) +
    coef(io.g,0.4f) +
    coef(io.b,0.3f)
  )

  val address = CounterFreeRun(stateCount = 1 << 16)
  io.address := address
  io.wr := True
  io.data := gray

  when(io.clear){
    gray := 0
    address.clear()
    io.wr := False
  }
}

生成的Verilog如下:

// Generator : SpinalHDL v1.6.0    git head : 73c8d8e2b86b45646e9d0b2e729291f2b65e6be3
// Component : RgbToGray
module RgbToGray (
  input               io_clear,
  input      [7:0]    io_r,
  input      [7:0]    io_g,
  input      [7:0]    io_b,
  output reg          io_wr,
  output     [15:0]   io_address,
  output     [7:0]    io_data,
  input               clk,
  input               reset
);
  wire       [7:0]    _zz_gray;
  wire       [7:0]    _zz_gray_1;
  wire       [15:0]   _zz_gray_2;
  wire       [7:0]    _zz_gray_3;
  wire       [15:0]   _zz_gray_4;
  wire       [7:0]    _zz_gray_5;
  wire       [15:0]   _zz_gray_6;
  wire       [15:0]   _zz_address_valueNext;
  wire       [0:0]    _zz_address_valueNext_1;
  reg        [7:0]    gray;
  wire                address_willIncrement;
  reg                 address_willClear;
  reg        [15:0]   address_valueNext;
  reg        [15:0]   address_value;
  wire                address_willOverflowIfInc;
  wire                address_willOverflow;

  assign _zz_gray = (_zz_gray_1 + _zz_gray_3);
  assign _zz_gray_1 = (_zz_gray_2 >>> 8);
  assign _zz_gray_2 = (io_r * 8'h4c);
  assign _zz_gray_3 = (_zz_gray_4 >>> 8);
  assign _zz_gray_4 = (io_g * 8'h66);
  assign _zz_gray_5 = (_zz_gray_6 >>> 8);
  assign _zz_gray_6 = (io_b * 8'h4c);
  assign _zz_address_valueNext_1 = address_willIncrement;
  assign _zz_address_valueNext = {15'd0, _zz_address_valueNext_1};
  always @(*) begin
    address_willClear = 1'b0;
    if(io_clear) begin
      address_willClear = 1'b1;
    end
  end

  assign address_willOverflowIfInc = (address_value == 16'hffff);
  assign address_willOverflow = (address_willOverflowIfInc && address_willIncrement);
  always @(*) begin
    address_valueNext = (address_value + _zz_address_valueNext);
    if(address_willClear) begin
      address_valueNext = 16'h0;
    end
  end

  assign address_willIncrement = 1'b1;
  assign io_address = address_value;
  always @(*) begin
    io_wr = 1'b1;
    if(io_clear) begin
      io_wr = 1'b0;
    end
  end

  assign io_data = gray;
  always @(posedge clk) begin
    gray <= (_zz_gray + _zz_gray_5);
    if(io_clear) begin
      gray <= 8'h0;
    end
  end

  always @(posedge clk or posedge reset) begin
    if(reset) begin
      address_value <= 16'h0;
    end else begin
      address_value <= address_valueNext;
    end
  end


endmodule

七、正弦ROM(Sinus rom)

假设用户需要生成一个正弦波以及其滤波后的形式。

参数名称 类型 描述
resolutionWidth Int 表述数值的比特位宽
sampleCount Int 一个正弦周期的采样点数
IO名称 方向 类型 描述
sin out SInt(resolutionWidth bits) 描述正弦波的输出
sinFiltred out SInt(resolutionWidth bits) 描述滤波后正弦波的输出

定义一个Component

class TopLevel(resolutionWidth : Int,sampleCount : Int) extends Component {
  val io = new Bundle {
    val sin = out SInt(resolutionWidth bits)
    val sinFiltred = out SInt(resolutionWidth bits)
  }
  // 这里写逻辑实现
}

为了在sin输出端口输出正弦波,需要定义一个包含了正弦波一个周期内所有采样点的ROM。随后可以利用相位计数器读取ROM并生成正弦波。

//定义生成ROM的函数
def sinTable = for(sampleIndex <- 0 until sampleCount) yield {
  val sinValue = Math.sin(2 * Math.PI * sampleIndex / sampleCount)
  S((sinValue * ((1<<resolutionWidth)/2-1)).toInt,resolutionWidth bits)
}

val rom =  Mem(SInt(resolutionWidth bits),initialContent = sinTable)
val phase = Reg(UInt(log2Up(sampleCount) bits)) init(0)
phase := phase + 1

io.sin := rom.readSync(phase)

随后生成sinFiltered,例如可以使用一个一阶低通滤波器:

io.sinFiltred := RegNext(io.sinFiltred  - (io.sinFiltred  >> 5) + (io.sin >> 5)) init(0)

完整代码如下:

class TopLevel(resolutionWidth : Int,sampleCount : Int) extends Component {
  val io = new Bundle {
    val sin = out SInt(resolutionWidth bits)
    val sinFiltred = out SInt(resolutionWidth bits)
  }

  def sinTable = for(sampleIndex <- 0 until sampleCount) yield {
    val sinValue = Math.sin(2 * Math.PI * sampleIndex / sampleCount)
    S((sinValue * ((1<<resolutionWidth)/2-1)).toInt,resolutionWidth bits)
  }

  val rom =  Mem(SInt(resolutionWidth bits),initialContent = sinTable)
  val phase = Reg(UInt(log2Up(sampleCount) bits)) init(0)
  phase := phase + 1

  io.sin := rom.readSync(phase)
  io.sinFiltred := RegNext(io.sinFiltred  - (io.sinFiltred  >> 5) + (io.sin >> 5)) init(0)
}

设置位宽为16,采样数为32,生成的Verilog为:

// Generator : SpinalHDL v1.6.0    git head : 73c8d8e2b86b45646e9d0b2e729291f2b65e6be3
// Component : MyTopLevel



module MyTopLevel (
  output     [15:0]   io_sin,
  output     [15:0]   io_sinFiltred,
  input               clk,
  input               reset
);
  reg        [15:0]   _zz_rom_port0;
  wire                _zz_rom_port;
  wire                _zz_io_sin;
  wire       [15:0]   _zz__zz_io_sinFiltred;
  wire       [15:0]   _zz__zz_io_sinFiltred_1;
  wire       [10:0]   _zz__zz_io_sinFiltred_2;
  wire       [15:0]   _zz__zz_io_sinFiltred_3;
  wire       [10:0]   _zz__zz_io_sinFiltred_4;
  reg        [4:0]    phase;
  reg        [15:0]   _zz_io_sinFiltred;
  reg [15:0] rom [0:31];

  assign _zz__zz_io_sinFiltred = ($signed(io_sinFiltred) - $signed(_zz__zz_io_sinFiltred_1));
  assign _zz__zz_io_sinFiltred_2 = (io_sinFiltred >>> 5);
  assign _zz__zz_io_sinFiltred_1 = {{5{_zz__zz_io_sinFiltred_2[10]}}, _zz__zz_io_sinFiltred_2};
  assign _zz__zz_io_sinFiltred_4 = (io_sin >>> 5);
  assign _zz__zz_io_sinFiltred_3 = {{5{_zz__zz_io_sinFiltred_4[10]}}, _zz__zz_io_sinFiltred_4};
  assign _zz_io_sin = 1'b1;
  initial begin
    $readmemb("MyTopLevel.v_toplevel_rom.bin",rom);
  end
  always @(posedge clk) begin
    if(_zz_io_sin) begin
      _zz_rom_port0 <= rom[phase];
    end
  end

  assign io_sin = _zz_rom_port0;
  assign io_sinFiltred = _zz_io_sinFiltred;
  always @(posedge clk or posedge reset) begin
    if(reset) begin
      phase <= 5'h0;
      _zz_io_sinFiltred <= 16'h0;
    end else begin
      phase <= (phase + 5'h01);
      _zz_io_sinFiltred <= ($signed(_zz__zz_io_sinFiltred) + $signed(_zz__zz_io_sinFiltred_3));
    end
  end


endmodule

其中ROM的Binary文件为:

0000000000000000
0001100011111000
0011000011111011
0100011100011100
0101101010000001
0110101001101100
0111011001000000
0111110110001001
0111111111111111
0111110110001001
0111011001000000
0110101001101100
0101101010000001
0100011100011100
0011000011111011
0001100011111000
0000000000000000
1110011100001000
1100111100000101
1011100011100100
1010010101111111
1001010110010100
1000100111000000
1000001001110111
1000000000000001
1000001001110111
1000100111000000
1001010110010100
1010010101111111
1011100011100100
1100111100000101
1110011100001000