0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

数字设计笔试Verilog手撕代码—累加器

冬至子 来源:FPGA and ICer 作者:Vuko 2023-06-02 16:35 次阅读

已知一个加法器IP,其功能是计算两个数的和,但这个和延迟两个周期才会输出。现在有一串连续的数据输入,每个周期都不间断,试问最少需要例化几个上述的加法器IP,才可以实现累加的功能。

设计分析

实现累加器的加法器例化的个数。按照原文大佬的设计方法,因为数据连续且加法器的延迟周期是2,使用使用一个实现累加,会有一半的数据丢失。那这样设计他就将奇数偶数的数据进行了分开做一级累加,然后第二级将奇数偶数的累加结果再累加。这样做共需消耗3个加法器。

这样设计当然没问题,但是这样设计是否是最少呢?我先抛出我的思考,我认为在允许少量逻辑设计的情况下,最少需要例化两个上述的加法器IP可以实现累加。

如果比较极限的情况下,一个都可以,先把一串数据使用寄存器缓存,然后一个一个取出来慢慢算即可,但这样是不太可取的,首先,数据是连续的并没有给出数据的极限长度,也就是说不论用任何涉及存储结构进行缓存,都没法确保该次数据能完全被存储。如果题目改成一串连续数据输入,长度最大为10,那我认为用寄存器缓存这样的设计是合理的。

设计架构

回到设计思路:用两个加法器的结构如图示。

图片

设计实现

加法器设计

假设两个时钟周期延时加法器代码如下,通过例化加法器进行构建累加器。

//加法器IP
module adder
#(parameter DATA_WIDTH = 8)(
    input clk,
    input rst_n,
    input [DATA_WIDTH-1:0] a_in,
    input [DATA_WIDTH-1:0] b_in,
    output reg [DATA_WIDTH-1:0] out
  );
  reg [DATA_WIDTH-1:0] sum;
  always @(posedge clk or negedge rst_n)begin
    if(rst_n == 'd0)begin
      sum <= 'd0;
      out <= 'd0;
    end
    else begin
      sum <= a_in + b_in;
      out <= sum;
    end
  end
endmodule

累加器设计

//累加器实现
module adder_for_acc
  #(parameter DATA_WIDTH = 8)
   (
     input                        clk,
     input                      rst_n,
     input       [DATA_WIDTH-1:0] din,
     input                  din_valid,
     output reg            dout_valid,
     output reg [DATA_WIDTH-1:0] dout
   );

  reg [DATA_WIDTH-1:0]din_r0;

  //打一拍
  always @(posedge clk or negedge rst_n)begin
    if(rst_n == 'd0)begin
      din_r0 <= 'd0;
    end
    else if(din_valid==1'B1)begin
      din_r0<= din;
    end
    else begin
      din_r0<='d0;
    end
  end

  //adder0_valid信号
  reg adder0_valid;
  always @(posedge clk or negedge rst_n)begin
    if(rst_n == 'd0)begin
      adder0_valid <= 'd0;
    end
    else if(din_valid==1'B1)begin
      adder0_valid<=!adder0_valid;
    end
    else begin
      adder0_valid<='d0;
    end
  end

  wire[DATA_WIDTH-1:0] a_in = (adder0_valid && din_valid)?din:0;
  wire[DATA_WIDTH-1:0] b_in = (adder0_valid)?din_r0:0;
  wire[DATA_WIDTH-1:0] ab_sum;

  adder adder0_dut (
    .clk  (clk   ),
    .rst_n(rst_n ),
    .a_in (a_in  ),
    .b_in (b_in  ),
    .out  (ab_sum)
  );
  //第一级加法器输出有效信号
  reg [1:0]adder0_valid_dly;
  wire ab_sum_valid = adder0_valid_dly[1];
  always @(posedge clk ) begin
      adder0_valid_dly<={adder0_valid_dly[0],adder0_valid};
  end

  wire [DATA_WIDTH-1:0] sum_in;
  wire [DATA_WIDTH-1:0] ab_sum_in = (ab_sum_valid)?ab_sum:0;
  wire [DATA_WIDTH-1:0] accsum_in = (ab_sum_valid)?sum_in:dout;

  adder adder1_dut (
    .clk  (clk      ),
    .rst_n(rst_n    ),
    .a_in (ab_sum_in),
    .b_in (accsum_in),
    .out  (sum_in   )
  );
  
  //第二级加法器输出有效信号
  reg [3:0]din_valid_r0;
  reg [1:0]adder1_valid_dly;
  wire adder1_outvld = adder1_valid_dly[1];
  always @(posedge clk ) begin
    adder1_valid_dly<={adder1_valid_dly[0],ab_sum_valid};
  end
  //输出
  always @(posedge clk ) begin
    din_valid_r0<={din_valid_r0[2:0],(din_valid || adder0_valid)};
  end
  always @(posedge clk or negedge rst_n) begin
    if(rst_n == 'd0)begin
      dout <= 'd0;
      dout_valid <= 'd0;
    end
    else if(adder1_outvld == 1 && (din_valid_r0[3]==1 && din_valid_r0[2]==0))begin
      dout <= sum_in ;
      dout_valid <= 'd1;
    end
    else begin
      dout <= dout ;
      dout_valid <= 'd0;
    end
  end

endmodule

代码架构设计

  1. 打拍:先对数据用寄存器缓存一拍,输入数据暂时用in[i]表示,缓存。
  2. 第一级加法器输入选择valid:因为前级积累一拍的数据,设计valid用于指示加法器的输入数据。
  3. 第一级加法器信号输入:根据valid信号进行选择数据输入。
  4. 调用第一级加法器,同时对输入valid信号进行打两拍处理,指示有效的输出数据。
  5. 第二级加法器信号输入:根据valid信号进行选择数据输入。
  6. 调用第二级加法器,同时对输入valid信号进行打两拍处理,指示有效的输出数据。
  7. 输出结果和valid信号。

经过分析,目前设计延时是4拍,也即两级,这里dout和valid使用的是时序逻辑输出,所以在输入valid拉低后的第五个时钟周期输出正确的结果。

仿真测试

设计仿真测试代码对代码进行测试,这里使用了递增数测试代码可用性,在实际测试时,可通过改变DATA_LEN的大小测试单次递增累加后的结果,后续结果依次递增为第一次的N倍。

`timescale 1ns/1ps
module adder_for_acc_tb;

  // Parameters
  localparam  DATA_WIDTH = 8;
  localparam  DATA_LEN = 5;
  // Ports
  reg clk = 1;
  reg rst_n = 0;
  reg [DATA_WIDTH-1:0] din;
  reg din_valid = 0;
  wire  dout_valid;
  wire [DATA_WIDTH-1:0] dout;

  adder_for_acc 
  #(
    .DATA_WIDTH (
        DATA_WIDTH )
  )
  adder_for_acc_dut (
    .clk (clk ),
    .rst_n (rst_n ),
    .din (din ),
    .din_valid (din_valid ),
    .dout_valid (dout_valid ),
    .dout  ( dout)
  );
  always @(posedge clk or negedge rst_n)begin
    if(rst_n == 'd0)begin
            din <= 'd0;
      din_valid <= 'd0;
    end
    else if(dout_valid == 1)begin
      din <= 'd0;
      din_valid <= 'd1;
    end
    else if(din == DATA_LEN)begin
      din <= din;
      din_valid <= 'd0;
    end
    else if(din != DATA_LEN)begin
      din <= din + 1;
      din_valid <= 'd1;
    end
    else begin
      din <= din;
      din_valid <= 'd0;
    end
  end

  always #5  clk = ! clk ;
  initial begin
    begin
      #100;
      rst_n = 1;
      #1000;
      $finish;
    end
  end

 
endmodule

仿真截图

图片

仿真分析

在图示仿真可知,累加器功能正常,在din_valid信号拉低后第五拍可得到输出结果,功能正常。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 寄存器
    +关注

    关注

    30

    文章

    5027

    浏览量

    117710
  • Verilog
    +关注

    关注

    28

    文章

    1325

    浏览量

    109298
  • 加法器
    +关注

    关注

    6

    文章

    174

    浏览量

    29729
  • 累加器
    +关注

    关注

    0

    文章

    50

    浏览量

    9365
收藏 人收藏

    评论

    相关推荐

    基于相位累加器的任意分频原理解析

    在大部分的教科书中,都会提到如何分频,包括奇数分频,偶数分频,小数分频等。 1、DDS相位累加器 (1)DDS合成流程 首先讲述DSS(直接频率合成法)的原理。 DDS是重要的频率合成方法,在波形
    的头像 发表于 11-29 10:19 4263次阅读

    单片机 累加器A与ACC区别

    累加器A与ACC区别累加器写成A或ACC在51汇编语言指令中是有区别的。ACC在汇编后的机器码必有一个字节的操作数,即累加器的字节地址E0H,A在汇编后则隐含在指令操作码中。所以在指令中A不能
    发表于 10-16 20:38

    累加器verilog语言编程问题

    刚刚接触FPGA,编程方面也不是很擅长。今天做了一个累加器verilog编程,仿真波形图一直出错,想请大神指教一下错误的原因,十分感谢!我要实现的累加器功能是:连续输入几百个gary_value
    发表于 05-23 20:09

    FPGA实现NCO中相位累加器的输出和查找表地址什么关系?

    ,这么理解是否正确,怎样实现?Q3: NCO的性能与相位累加器的位数有关,但与正弦表的精确程度有什么关系,能否采用更小的正弦表。相位累加器和查找表的地址的关系希望有人写出来,或者写成伪代码更好,最好能按上面这个例子来解释。本人新
    发表于 03-10 20:10

    求问大佬LabVIEW做条件累加器

    在C语言中很简单:if a=1, b++; 昨天刚学Labview,只会做累加,不会加条件啊。我是把a的值作为条件结构的输入,在条件结构里加个循环一次的for循环结构,直接用循环变量i做累加器,初值是零。但是这样每次都是i=0, i=i+1=1,
    发表于 03-03 23:27

    控制累加器

    怎么由布尔控件控制开始和停止累加器的运行
    发表于 06-04 22:09

    累加器的简单使用和自定义累加器

    累加器使用的注意点及自定义累加器
    发表于 04-02 09:31

    寄存累加器、暂存,还是分不清?

    ,但一个实作 x86 指令集的 CPU 可以包含比八个更多的暂存。暂存的分类资料暂存 —— 用来储存整数数字(参考以下的浮点暂存)。
    发表于 10-14 07:18

    计算机累加器有加法器功能吗,累加器是什么_累加器的作用及原理介绍 精选资料推荐

    累加器的概念在中央处理中,累加器(accumulator)是一种寄存,用来储存计算产生的中间结果。如果没有像累加器这样的寄存
    发表于 08-30 08:57

    相位累加器原理

    相位累加器一个正弦波,虽然它的幅度不是线性的,但是它的相位却是线性增加的。DDS 正是利用了这一特点来产生正弦信号。如图 2,根据
    发表于 09-03 08:43 7799次阅读
    相位<b class='flag-5'>累加器</b>原理

    累加器是什么_累加器的作用及原理介绍

    本文开始介绍了累加器的概念和相位累加器原理,其次介绍了累加器的作用,最后介绍了流水线相位累加器的设计与累加定时器在PLC控制程序中的应用。
    发表于 04-11 11:40 7w次阅读
    <b class='flag-5'>累加器</b>是什么_<b class='flag-5'>累加器</b>的作用及原理介绍

    累加器是寄存器吗_寄存器、累加器、暂存器有什么区别

    本文首先对寄存器、累加器、暂存器做个哥介绍,其次解答了累加器是不是寄存器,最后阐述了寄存器、累加器、暂存器的区别。
    发表于 04-11 16:31 9160次阅读

    累加器A的主要作用是什么_一文解析累加器a和acc的区别

    在中央处理器中,累加器(accumulator) 是一种寄存器,用来储存计算产生的中间结果。如果没有像累加器这样的寄存器,那么在每次计算 (加法,乘法,移位等等) 后就必须要把结果写回到内存,也许马上就得读回来。然而存取主存的速度是比从算术逻辑单元到有直接路径的
    发表于 04-11 16:46 2.2w次阅读

    32位数字相位累加器的程序和工程文件免费下载

    累加器 (accumulator) 是一种寄存器,用来储存计算产生的中间结果。如果没有像累加器这样的寄存器,那么在每次计算 (加法,乘法,移位等等) 后就必须要把结果写回到 内存,也许马上就得读回来。然而存取主存的速度是比从算术逻辑单元到有直接路径的
    发表于 10-14 16:00 8次下载
    32位<b class='flag-5'>数字</b>相位<b class='flag-5'>累加器</b>的程序和工程文件免费下载

    51单片机中累加器A与ACC的区别

    累加器A与ACC区别累加器写成A或ACC在51汇编语言指令中是有区别的。ACC在汇编后的机器码必有一个字节的操作数,即累加器的字节地址E0H,A在汇编后则隐含在指令操作码中。所以在指令中A不能
    发表于 11-23 09:06 105次下载
    51单片机中<b class='flag-5'>累加器</b>A与ACC的区别