前言:
本專欄旨在記錄高頻筆面試手撕代碼題,以備數字前端秋招,本專欄所有文章提供原理分析、代碼及波形,所有代碼均經過本人驗證。
目錄如下:
1.數字IC手撕代碼-分頻器(任意偶數分頻)
2.數字IC手撕代碼-分頻器(任意奇數分頻)
3.數字IC手撕代碼-分頻器(任意小數分頻)
4.數字IC手撕代碼-異步複位同步釋放
5.數字IC手撕代碼-邊沿檢測(上升沿、下降沿、雙邊沿)
6.數字IC手撕代碼-序列檢測(狀态機寫法)
7.數字IC手撕代碼-序列檢測(移位寄存器寫法)
8.數字IC手撕代碼-半加器、全加器
9.數字IC手撕代碼-串轉并、并轉串
10.數字IC手撕代碼-資料位寬轉換器(寬-窄,窄-寬轉換)
11.數字IC手撕代碼-有限狀态機FSM-飲料機
12.數字IC手撕代碼-握手信号(READY-VALID)
13.數字IC手撕代碼-流水握手(利用握手解決流水線斷流、反壓問題)
14.數字IC手撕代碼-泰淩微筆試真題
15.數字IC手撕代碼-平頭哥技術終面手撕真題
16.數字IC手撕代碼-兆易創新筆試真題
17.數字IC手撕代碼-樂鑫科技筆試真題(4倍頻)
18.數字IC手撕代碼-雙端口RAM(dual-port-RAM)
...持續更新
為了友善可以收藏導覽部落格: 數字IC手撕代碼-導覽目錄
目錄
握手信号
接口解釋
代碼
testbench
波形
握手信号
握手信号,就是為了子產品之間的資料互動正确才衍生出來的信号。其無非就是三種可能
一、上遊master提供的valid 信号随資料一起拉高,但下遊slave過一段時間才準備好的valid先高為敬。
二、下遊slave一直準備好,但上遊資料過段時間才有效的ready先高為敬。
三、上遊master的valid信号和下遊slave的ready同時為高。
當我第一次接觸握手信号的時候,其實是很清楚上面的三條握手原則的,但是具體的,master和slave之間的握手信号該怎麼寫代碼卻迷迷糊糊。就利用這篇文章簡單的講一下master和slave之間是怎麼通過握手信号來解決資料互動問題的。同時也為下一篇文章《多級流水線握手》打下基礎。下一篇文章會講解如何利用握手信号,解決流水線因為上流斷流及下遊反壓導緻的流水線停滞問題。
好,回到本篇文章來。
對于master來說,master發出一個資料,如果資料有效,master就會把傳送給slave的valid信号拉高,即告訴slave資料有效可以接收!如果slave準備好了,slave就把傳送給master的ready信号拉高,告訴master我準備好了,如果你資料有效,我就可以接收!是以當master傳輸的資料有效,且slave也準備好時,資料就會在下一個周期被slave取走,master可以接着傳下一個資料。
接口解釋
對于你寫的子產品來說,如果要跟上下做資料握手,那麼對于上遊,你是一個slave,對于下遊,你又是一個master了,是以接口應該如下:
module handshake(
input clk ,
input rstn ,
input [7:0] data_i ,
input valid_i ,
input ready_o ,
output [7:0] data_o ,
input ready_i ,
output valid_o
);
data_i為輸入的資料,如果valid_i為1,則資料有效,ready_o是slave發送給上遊master的準備信号,如果ready_o準備好了,則為1。
data_o為該子產品輸出給下遊的資料,如果輸出的資料有效,則valid_o為1。下遊也會有準備好和沒準備好的時候,是以還需要一個下遊提供給本子產品的ready_i信号,來告訴我們下遊是否準備完畢。
代碼
module handshake(
input clk ,
input rstn ,
input [7:0] data_i ,
input valid_i ,
output ready_o ,
output [7:0] data_o ,
input ready_i ,
output valid_o
);
reg [7:0] data_o_r;
reg valid_o_r;
assign ready_o = ready_i; //如果下遊準備好了,那我就準備好了
always @(posedge clk)begin
if(ready_i && valid_i)begin //如果下遊準備好了,并且上遊資料有效,那就把輸入的資料乘以二輸出
data_o_r <= data_i * 2;
end
end
always @(posedge clk)begin
if(!rstn)begin
valid_o_r <= 1'b0;
end
else if(ready_o)begin
valid_o_r <= valid_i; //如果我準備好了,我就把上遊的valid傳遞給下遊。
end
end
assign data_o = data_o_r;
assign valid_o = valid_o_r;
endmodule
代碼編寫如上,關鍵就在于注釋:
1.如果下遊準備好了,并且上遊輸入的資料有效的話, 把輸入資料乘以二指派給輸出資料。
2.如果下遊準備好可以接收資料了,那本子產品就可以處理資料了。
3.如果本子產品準備好了,就把上遊的valid_i傳遞給下遊valid_o.
這裡的testbench也很重要。
testbench
module handshake_tb();
reg clk,rstn;
always #5 clk = ~clk;
reg valid_i,ready_i;
wire ready_o,valid_o;
reg [7:0] data_i;
wire [7:0] data_o;
initial begin
clk <= 1'b0;
rstn <= 1'b0;
#25
rstn <= 1'b1;
ready_i <= 1'b1; //下遊準備好了
valid_i <= 1'b0; //上遊資料無效
data_i <= 8'b0000_1000;
#10
data_i <= 8'b0111_1000;
valid_i <= 1'b1; //上遊資料有效
#10
data_i <= 8'b0100_0100;
#10
valid_i <= 1'b0;
#10
data_i <= 8'b0010_0100;
valid_i <= 1'b1; //雖然上遊資料有效,但下遊沒準備好
ready_i <= 1'b0;
#20
ready_i <= 1'b1; //上遊資料有效,下遊準備好了
#10
valid_i <= 1'b0;
#500
$stop();
end
handshake u_handshake(
.clk (clk) ,
.rstn (rstn) ,
.data_i (data_i) ,
.data_o (data_o) ,
.ready_i (ready_i) ,
.ready_o (ready_o) ,
.valid_i (valid_i) ,
.valid_o (valid_o)
);
endmodule
我們用tb來模拟本子產品的上遊master和下遊slave,用tb給本子產品提供上遊資料data_i和valid_i信号。分别模拟了上遊斷流,即valid_i中途拉低,以及下遊反壓,即ready_i中途為低的情況。都通過。
波形
當輸出資料有效的時候,本子產品輸出的valid_o為高,均符合要求。不熟悉的小夥伴,多看看波形了解一下,整個module也在上面分為兩部分貼出來了,感興趣可以自己動手打一下,上面的是完整代碼。tb省略了端口例化和變量定義。