前言
本系列整理關(guān)于數(shù)字設(shè)計(jì)的筆試或面試的設(shè)計(jì)問(wèn)題,手撕代碼繼續(xù)撕,今天撕一個(gè)百度昆侖筆試題的累加器設(shè)計(jì)。
設(shè)計(jì)需求
已知一個(gè)加法器IP,其功能是計(jì)算兩個(gè)數(shù)的和,但這個(gè)和延遲兩個(gè)周期才會(huì)輸出。現(xiàn)在有一串連續(xù)的數(shù)據(jù)輸入,每個(gè)周期都不間斷,試問(wèn)最少需要例化幾個(gè)上述的加法器IP,才可以實(shí)現(xiàn)累加的功能。
設(shè)計(jì)分析
實(shí)現(xiàn)累加器的加法器例化的個(gè)數(shù)。按照原文大佬的設(shè)計(jì)方法,因?yàn)閿?shù)據(jù)連續(xù)且加法器的延遲周期是2,使用使用一個(gè)實(shí)現(xiàn)累加,會(huì)有一半的數(shù)據(jù)丟失。那這樣設(shè)計(jì)他就將奇數(shù)偶數(shù)的數(shù)據(jù)進(jìn)行了分開(kāi)做一級(jí)累加,然后第二級(jí)將奇數(shù)偶數(shù)的累加結(jié)果再累加。這樣做共需消耗3個(gè)加法器。
這樣設(shè)計(jì)當(dāng)然沒(méi)問(wèn)題,但是這樣設(shè)計(jì)是否是最少呢?我先拋出我的思考,我認(rèn)為在允許少量邏輯設(shè)計(jì)的情況下,最少需要例化兩個(gè)上述的加法器IP可以實(shí)現(xiàn)累加。
如果比較極限的情況下,一個(gè)都可以,先把一串?dāng)?shù)據(jù)使用寄存器緩存,然后一個(gè)一個(gè)取出來(lái)慢慢算即可,但這樣是不太可取的,首先,數(shù)據(jù)是連續(xù)的并沒(méi)有給出數(shù)據(jù)的極限長(zhǎng)度,也就是說(shuō)不論用任何涉及存儲(chǔ)結(jié)構(gòu)進(jìn)行緩存,都沒(méi)法確保該次數(shù)據(jù)能完全被存儲(chǔ)。如果題目改成一串連續(xù)數(shù)據(jù)輸入,長(zhǎng)度最大為10,那我認(rèn)為用寄存器緩存這樣的設(shè)計(jì)是合理的。
設(shè)計(jì)架構(gòu)
回到設(shè)計(jì)思路:用兩個(gè)加法器的結(jié)構(gòu)如圖示。
設(shè)計(jì)實(shí)現(xiàn)
加法器設(shè)計(jì)
假設(shè)兩個(gè)時(shí)鐘周期延時(shí)加法器代碼如下,通過(guò)例化加法器進(jìn)行構(gòu)建累加器。
//加法器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
累加器設(shè)計(jì)
//累加器實(shí)現(xiàn)
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信號(hào)
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)
);
//第一級(jí)加法器輸出有效信號(hào)
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 )
);
//第二級(jí)加法器輸出有效信號(hào)
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
代碼架構(gòu)設(shè)計(jì)
- 打拍:先對(duì)數(shù)據(jù)用寄存器緩存一拍,輸入數(shù)據(jù)暫時(shí)用in[i]表示,緩存。
- 第一級(jí)加法器輸入選擇valid:因?yàn)榍凹?jí)積累一拍的數(shù)據(jù),設(shè)計(jì)valid用于指示加法器的輸入數(shù)據(jù)。
- 第一級(jí)加法器信號(hào)輸入:根據(jù)valid信號(hào)進(jìn)行選擇數(shù)據(jù)輸入。
- 調(diào)用第一級(jí)加法器,同時(shí)對(duì)輸入valid信號(hào)進(jìn)行打兩拍處理,指示有效的輸出數(shù)據(jù)。
- 第二級(jí)加法器信號(hào)輸入:根據(jù)valid信號(hào)進(jìn)行選擇數(shù)據(jù)輸入。
- 調(diào)用第二級(jí)加法器,同時(shí)對(duì)輸入valid信號(hào)進(jìn)行打兩拍處理,指示有效的輸出數(shù)據(jù)。
- 輸出結(jié)果和valid信號(hào)。
經(jīng)過(guò)分析,目前設(shè)計(jì)延時(shí)是4拍,也即兩級(jí),這里dout和valid使用的是時(shí)序邏輯輸出,所以在輸入valid拉低后的第五個(gè)時(shí)鐘周期輸出正確的結(jié)果。
仿真測(cè)試
設(shè)計(jì)仿真測(cè)試代碼對(duì)代碼進(jìn)行測(cè)試,這里使用了遞增數(shù)測(cè)試代碼可用性,在實(shí)際測(cè)試時(shí),可通過(guò)改變DATA_LEN的大小測(cè)試單次遞增累加后的結(jié)果,后續(xù)結(jié)果依次遞增為第一次的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信號(hào)拉低后第五拍可得到輸出結(jié)果,功能正常。