20201210 SpinalHDL实现输入数量可变的加法树ip

最近在学习SpinalHDL,这是一门基于Scala的硬件描述语言,粒度与Verilog/VHDL相当,但是利用了scala的许多面向对象特性以及高级抽象语言的特性,可以大大提高数字IC前端设计的效率。

由于项目需要,决定从一个最简单的加法树结构入手,练习SpinalHDL。

一个典型八输入加法树的结构如下:
八输入加法器结构示意
通过观察可以知道,加法树是通过将输入两两相加得到中间部分和,再将部分和两两相加,直至获得最终输出。这在verilog中的实现不难,只需要定义一些存放中间部分和的信号,再写一些两两相加的逻辑即可。
但是,spinalHDL在这个例子中展现了一个非常不错的地方,即它可以使用高级抽象语言的一些特性,比如递归来实现加法树(加法树的树形结构非常适合用递归来做),且其对数据位宽的自动推断使得无需考虑的中间部分和信号位宽,代码如下:

package mylib
import spinal.core._

//inputNum为参数,代表加法树输入数据个数
class addtree (inputNum: Int) extends Component {
  require(isPow2(inputNum))		//本代码只支持inputNum为2的幂次情况,实际上任意数量实现也不难
  val tree_stage = log2Up(inputNum) //加法树级数
  val io = new Bundle{
    val ppen = in Bool
    val data_in = in Vec(SInt(32 bits),inputNum)  //使用Vec类型实现可变数量的port
    val sum_out = out(Reg(SInt(32+tree_stage bits)))
  }
  //函数式编程思想
  def tree(op: Vec[SInt]): Vec[SInt] = {
    val n = op.length
    if(n == 1) op
    else {
      val psum = Vec((SInt),n/2)
      for(i <- 0 until n/2){
        psum(i) := op(i) +^ op(i + n/2)  //两两相加
      }
      tree(psum) //递归
    }
  }
  when(io.ppen){
    io.sum_out := tree(io.data_in)(0).resized  //寄存器输出
  }
}

所生成的Verilog代码如下:

module addtree (
  input               io_ppen,
  input      [31:0]   io_data_in_0,
  input      [31:0]   io_data_in_1,
  input      [31:0]   io_data_in_2,
  input      [31:0]   io_data_in_3,
  input      [31:0]   io_data_in_4,
  input      [31:0]   io_data_in_5,
  input      [31:0]   io_data_in_6,
  input      [31:0]   io_data_in_7,
  output reg [34:0]   io_sum_out,
  input               clk,
  input               reset
);
  wire       [32:0]   _zz_7;
  wire       [32:0]   _zz_8;
  wire       [32:0]   _zz_9;
  wire       [32:0]   _zz_10;
  wire       [32:0]   _zz_11;
  wire       [32:0]   _zz_12;
  wire       [32:0]   _zz_13;
  wire       [32:0]   _zz_14;
  wire       [33:0]   _zz_15;
  wire       [33:0]   _zz_16;
  wire       [33:0]   _zz_17;
  wire       [33:0]   _zz_18;
  wire       [34:0]   _zz_19;
  wire       [34:0]   _zz_20;
  wire       [32:0]   _zz_1;
  wire       [32:0]   _zz_2;
  wire       [32:0]   _zz_3;
  wire       [32:0]   _zz_4;
  wire       [33:0]   _zz_5;
  wire       [33:0]   _zz_6;

  assign _zz_7 = {io_data_in_0[31],io_data_in_0};
  assign _zz_8 = {io_data_in_4[31],io_data_in_4};
  assign _zz_9 = {io_data_in_1[31],io_data_in_1};
  assign _zz_10 = {io_data_in_5[31],io_data_in_5};
  assign _zz_11 = {io_data_in_2[31],io_data_in_2};
  assign _zz_12 = {io_data_in_6[31],io_data_in_6};
  assign _zz_13 = {io_data_in_3[31],io_data_in_3};
  assign _zz_14 = {io_data_in_7[31],io_data_in_7};
  assign _zz_15 = {_zz_1[32],_zz_1};
  assign _zz_16 = {_zz_3[32],_zz_3};
  assign _zz_17 = {_zz_2[32],_zz_2};
  assign _zz_18 = {_zz_4[32],_zz_4};
  assign _zz_19 = {_zz_5[33],_zz_5};
  assign _zz_20 = {_zz_6[33],_zz_6};
  assign _zz_1 = ($signed(_zz_7) + $signed(_zz_8));
  assign _zz_2 = ($signed(_zz_9) + $signed(_zz_10));
  assign _zz_3 = ($signed(_zz_11) + $signed(_zz_12));
  assign _zz_4 = ($signed(_zz_13) + $signed(_zz_14));
  assign _zz_5 = ($signed(_zz_15) + $signed(_zz_16));
  assign _zz_6 = ($signed(_zz_17) + $signed(_zz_18));
  always @ (posedge clk) begin
    if(io_ppen)begin
      io_sum_out <= ($signed(_zz_19) + $signed(_zz_20));
    end
  end
endmodule

由于模块比较简单,就不放仿真波形啦(坏习惯

这是我使用spinalHDL写的第一个模块,模块本身虽然很简单,但是帮助我快速理解了spinalHDL相比于Verilog的一些优势。此处还要感谢Github:@RicardoNid 同学给予的帮助。
接下来还可以思考如何写:非2的幂次数量输入的加法树、每级插流水的加法树、每n级插流水的加法树。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值