91视频免费?看_蜜芽MY188精品TV在线观看_国产免费无遮挡在线观看视频_深夜国产_亚洲精品欧洲精品_欧美黑人粗暴多交

數字設計筆試Verilog手撕代碼 - 累加器

前言

本系列整理關于數字設計的筆試或面試的設計問題,手撕代碼繼續撕,今天撕一個百度昆侖筆試題的累加器設計。

設計需求

已知一個加法器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信號拉低后第五拍可得到輸出結果,功能正常。

聲明:本內容為作者獨立觀點,不代表電子星球立場。未經允許不得轉載。授權事宜與稿件投訴,請聯系:editor@netbroad.com
覺得內容不錯的朋友,別忘了一鍵三連哦!
贊 2
收藏 3
關注 14
成為作者 賺取收益
全部留言
0/200
成為第一個和作者交流的人吧
主站蜘蛛池模板: 太原市| 濉溪县| 栾川县| 瑞安市| 吐鲁番市| 昌邑市| 松桃| 集贤县| 马山县| 信宜市| 西畴县| 毕节市| 双牌县| 仁怀市| 菏泽市| 峨眉山市| 景泰县| 禹城市| 开原市| 五大连池市| 大同市| 洛南县| 洪洞县| 盐边县| 多伦县| 安义县| 彭阳县| 娱乐| 肇东市| 武乡县| 淳化县| 泊头市| 徐州市| 凤冈县| 武平县| 洛隆县| 开鲁县| 依兰县| 衡阳县| 乌兰察布市| 广宗县|