本(běn)文(wén)摘自(zì)网(wǎng)絡班陳同(tóng)学博客:https://www.cnblogs.com/moluoqishi/p/9751652.html
本(běn)文(wén)設計(jì)思(sī)想(xiǎng)采用(yòng)明(míng)德揚至(zhì)簡設計(jì)法。上(shàng)一(yī)篇(piān)博文(wén)中(zhōng)定(dìng)制了(le)自(zì)定(dìng)義MAC IP的(de)結構,在(zài)用(yòng)戶側需要(yào)位宽(kuān)轉(zhuǎn)換及(jí)數據(jù)緩存。本(běn)文(wén)以(yǐ)TX方(fāng)向(xiàng)为(wèi)例,設計(jì)并验(yàn)證發(fà)送緩存模块(kuài)。这(zhè)里(lǐ)定(dìng)義該模块(kuài)可(kě)緩存4个(gè)最(zuì)大长度(dù)數據(jù)包(bāo),用(yòng)戶根(gēn)據(jù)需求改動(dòng)即可(kě)。
該模块(kuài)核心(xīn)是(shì)利用(yòng)异(yì)步FIFO進(jìn)行跨时(shí)鐘(zhōng)域处理,位宽(kuān)轉(zhuǎn)換由(yóu)VerilogHDL实現(xiàn)。需要(yào)注意(yì)的(de)是(shì)用(yòng)戶數據(jù)包(bāo)位宽(kuān)32bit,因(yīn)此(cǐ)包(bāo)尾可(kě)能(néng)有(yǒu)无效字(zì)节(jié),而(ér)轉(zhuǎn)換为(wèi)8bit位宽(kuān)數據(jù)幀後(hòu)是(shì)要(yào)丢棄无效字(zì)节(jié)的(de)。內(nèi)部(bù)邏輯非(fēi)常簡單,直(zhí)接上(shàng)代(dài)碼:
`timescale 1ns / 1ps
// Description: MAC IP TX方(fāng)向(xiàng)用(yòng)戶數據(jù)緩存及(jí)位宽(kuān)轉(zhuǎn)換模块(kuài)
// 整體(tǐ)功能(néng):将TX方(fāng)向(xiàng)用(yòng)戶32bit位宽(kuān)的(de)數據(jù)包(bāo)轉(zhuǎn)換成(chéng)8bit位宽(kuān)數據(jù)包(bāo)
//用(yòng)戶側时(shí)鐘(zhōng)100MHZ,MAC側125MHZ
//緩存深度(dù):保證能(néng)緩存4个(gè)最(zuì)长數據(jù)包(bāo),TX方(fāng)向(xiàng)用(yòng)戶數據(jù)包(bāo)包(bāo)括
//目的(de)MAC地(dì)址 源MAC地(dì)址 類(lèi)型/长度(dù) 數據(jù) 最(zuì)长1514byte
module tx_buffer#(parameter DATA_W = 32)//位宽(kuān)不(bù)能(néng)改動(dòng)
(
//全(quán)局(jú)信(xìn)号(hào)
input rst_n,//保證拉低三(sān)个(gè)时(shí)鐘(zhōng)周期(qī),否則FIF可(kě)能(néng)不(bù)会(huì)正(zhèng)确複位
//用(yòng)戶側信(xìn)号(hào)
input user_clk,
input [DATA_W-1:0] din,
input din_vld,
input din_sop,
input din_eop,
input [2-1:0] din_mod,
output rdy,
//MAC側信(xìn)号(hào)
input eth_tx_clk,
output reg [8-1:0] dout,
output reg dout_sop,
output reg dout_eop,
output reg dout_vld
);
reg wr_en = 0;
reg [DATA_W+4-1:0] fifo_din = 0;
reg [ (2-1):0] rd_cnt = 0 ;
wire add_rd_cnt ;
wire end_rd_cnt ;
wire rd_en;
wire [DATA_W+4-1:0] fifo_dout;
wire rst;
reg [ (2-1):0] rst_cnt =0 ;
wire add_rst_cnt ;
wire end_rst_cnt ;
reg rst_flag = 0;
wire [11 : 0] wr_data_count;
wire empty;
wire full;
/****************************************写側*************************************************/
always @(posedge user_clk or negedge rst_n)begin
if(rst_n==1'b0)begin
wr_en <= 0;
end
else if(rdy)
wr_en <= din_vld;
end
always @(posedge user_clk or negedge rst_n)begin
if(rst_n==1'b0)begin
fifo_din <= 0;
end
else begin//[35] din_sop [34] din_eop [33:32] din_mod [31:0] din
fifo_din <= {din_sop,din_eop,din_mod,din};
end
end
assign rdy = wr_data_count <= 1516 && !rst && !rst_flag && !full;
/****************************************读(dú)側*************************************************/
always @(posedge eth_tx_clk or negedge rst_n) begin
if (rst_n==0) begin
rd_cnt <= 0;
end
else if(add_rd_cnt) begin
if(end_rd_cnt)
rd_cnt <= 0;
else
rd_cnt <= rd_cnt+1 ;
end
end
assign add_rd_cnt = (!empty);
assign end_rd_cnt = add_rd_cnt && rd_cnt == (4)-1 ;
assign rd_en = end_rd_cnt;
always @(posedge eth_tx_clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dout <= 0;
end
else if(add_rd_cnt)begin
dout <= fifo_dout[DATA_W-1-rd_cnt*8 -:8];
end
end
always @(posedge eth_tx_clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dout_vld <= 0;
end
else if(add_rd_cnt && ((rd_cnt <= 3 - fifo_dout[33:32] && fifo_dout[34]) || !fifo_dout[34]))begin
dout_vld <= 1;
end
else
dout_vld <= 0;
end
always @(posedge eth_tx_clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dout_sop <= 0;
end
else if(add_rd_cnt && rd_cnt == 0 && fifo_dout[35])begin
dout_sop <= 1;
end
else
dout_sop <= 0 ;
end
always @(posedge eth_tx_clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dout_eop <= 0;
end
else if(add_rd_cnt && rd_cnt == 3 - fifo_dout[33:32] && fifo_dout[34])begin
dout_eop <= 1;
end
else
dout_eop <= 0;
end
/******************************FIFO複位邏輯****************************************/
assign rst = !rst_n || rst_flag;
always @(posedge user_clk or negedge rst_n)begin
if(!rst_n)begin
rst_flag <= 1;
end
else if(end_rst_cnt)
rst_flag <= 0;
end
always @(posedge user_clk or negedge rst_n) begin
if (rst_n==0) begin
rst_cnt <= 0;
end
else if(add_rst_cnt) begin
if(end_rst_cnt)
rst_cnt <= 0;
else
rst_cnt <= rst_cnt+1 ;
end
end
assign add_rst_cnt = (rst_flag);
assign end_rst_cnt = add_rst_cnt && rst_cnt == (3)-1 ;
//FIFO位宽(kuān)32bit 一(yī)幀數據(jù)最(zuì)长1514byte,即379个(gè)16bit數據(jù)
//FIFO深度(dù):379*4 = 1516 需要(yào)2048
//异(yì)步FIFO例化(huà)
fifo_generator_0 fifo (
.rst(rst), // input wire rst
.wr_clk(user_clk), // input wire wr_clk 100MHZ
.rd_clk(eth_tx_clk), // input wire rd_clk 125MHZ
.din(fifo_din), // input wire [33 : 0] din
.wr_en(wr_en), // input wire wr_en
.rd_en(rd_en), // input wire rd_en
.dout(fifo_dout), // output wire [33 : 0] dout
.full(full), // output wire full
.empty(empty), // output wire empty
.wr_data_count(wr_data_count) // output wire [11 : 0] wr_data_count
);
endmodule
tx_buffer
接下(xià)来(lái)是(shì)验(yàn)證部(bù)分(fēn),也(yě)就(jiù)是(shì)本(běn)文(wén)的(de)重(zhòng)點(diǎn)。以(yǐ)下(xià)的(de)testbench包(bāo)含了(le)最(zuì)基本(běn)的(de)测試思(sī)想(xiǎng):發(fà)送测試激勵給(gěi)UUT,将UUT輸出(chū)與(yǔ)黃金(jīn)參考值進(jìn)行比較,通(tòng)过(guò)記(jì)分(fēn)牌(pái)輸出(chū)比較結果(guǒ)。
`timescale 1ns / 1ps
module tx_buffer_tb( );
parameter USER_CLK_CYC = 10,
ETH_CLK_CYC = 8,
RST_TIM = 3;
parameter SIM_TIM = 10_000;
reg user_clk;
reg rst_n;
reg [32-1:0] din;
reg din_vld,din_sop,din_eop;
reg [2-1:0] din_mod;
wire rdy;
reg eth_tx_clk;
wire [8-1:0] dout;
wire dout_sop,dout_eop,dout_vld;
reg [8-1:0] dout_buf [0:1024-1];
reg [16-1:0] len [0:100-1];
reg [2-1:0] mod [0:100-1];
reg err_flag = 0;
tx_buffer#(.DATA_W(32))//位宽(kuān)不(bù)能(néng)改動(dòng)
dut
(
//全(quán)局(jú)信(xìn)号(hào)
.rst_n (rst_n) ,//保證拉低三(sān)个(gè)时(shí)鐘(zhōng)周期(qī),否則FIF可(kě)能(néng)不(bù)会(huì)正(zhèng)确複位
.user_clk (user_clk) ,
.din (din) ,
.din_vld (din_vld) ,
.din_sop (din_sop) ,
.din_eop (din_eop) ,
.din_mod (din_mod) ,
.rdy (rdy) ,
.eth_tx_clk (eth_tx_clk) ,
.dout (dout) ,
.dout_sop (dout_sop) ,
.dout_eop (dout_eop) ,
.dout_vld (dout_vld)
);
/***********************************时(shí)鐘(zhōng)******************************************/
initial begin
user_clk = 1;
forever #(USER_CLK_CYC/2) user_clk = ~user_clk;
end
initial begin
eth_tx_clk = 1;
forever #(ETH_CLK_CYC/2) eth_tx_clk = ~eth_tx_clk;
end
/***********************************複位邏輯******************************************/
initial begin
rst_n = 1;
#1;
rst_n = 0;
#(RST_TIM*USER_CLK_CYC);
rst_n = 1;
end
/***********************************輸入(rù)激勵******************************************/
integer gen_time = 0;
initial begin
#1;
packet_initial;
#(RST_TIM*USER_CLK_CYC);
packet_gen(20,2);
#(USER_CLK_CYC*10);
packet_gen(30,1);
end
/***********************************輸出(chū)緩存與(yǔ)檢测******************************************/
integer j = 0;
integer chk_time = 0;
initial begin
forever begin
@(posedge eth_tx_clk)
if(dout_vld)begin
if(dout_sop)begin
dout_buf[0] = dout;
j = 1;
end
else if(dout_eop)begin
dout_buf[j] = dout;
j = j+1;
packet_check;
end
else begin
dout_buf[j] = dout;
j = j+1;
end
end
end
end
/***********************************score board******************************************/
integer fid;
initial begin
fid = $fopen("test.txt");
$fdisplay(fid," Start testing
");
#SIM_TIM;
if(err_flag)
$fdisplay(fid,"Check is failed
");
else
$fdisplay(fid,"Check is successful
");
$fdisplay(fid," Testing is finished
");
$fclose(fid);
$stop;
end
/***********************************子任务******************************************/
//包(bāo)生(shēng)成(chéng)子任务
task packet_gen;
input [16-1:0] length;
input [2-1:0] invalid_byte;
integer i;
begin
len[gen_time] = length;
mod[gen_time] = invalid_byte;
for(i = 1;i<=length;i=i+1)begin
if(rdy == 1)begin
din_vld = 1;
if(i==1)
din_sop = 1;
else if(i == length)begin
din_eop = 1;
din_mod = invalid_byte;
end
else begin
din_sop = 0;
din_eop = 0;
din_mod = 0;
end
din = i ;
end
else begin
din_sop = din_sop;
din_eop = din_eop;
din_vld = 0;
din_mod = din_mod;
din = din;
i = i - 1;
end
#(USER_CLK_CYC*1);
end
packet_initial;
gen_time = gen_time + 1;
end
endtask
task packet_initial;
begin
din_sop = 0;
din_eop = 0;
din_vld = 0;
din = 0;
din_mod = 0;
end
endtask
//包(bāo)檢测子任务
task packet_check;
integer k;
integer num,packet_len;
begin
num = 1;
$fdisplay(fid,"%dth:Packet checking...
",chk_time);
packet_len = 4*len[chk_time]-mod[chk_time];
if(j != packet_len)begin
$fdisplay(fid,"Length of the packet is wrong.
");
err_flag = 1;
disable packet_check;
end
for(k=0;k<packet_len;k=k+1)begin
if(k%4 == 3)begin
if(dout_buf[k] != num)begin
$fdisplay(fid,"Data of the packet is wrong!
");
err_flag = 1;
end
num = num+1;
end
else if(dout_buf[k] != 0)begin
$fdisplay(fid,"Data of the packet is wrong,it should be zero!
");
err_flag = 1;
end
end
chk_time = chk_time + 1;
end
endtask
endmodule
tx_buffer_tb
可(kě)見(jiàn)主(zhǔ)要(yào)是(shì)task編写及(jí)文(wén)件(jiàn)读(dú)写操作幫了(le)大忙,如(rú)果(guǒ)都用(yòng)眼(yǎn)睛看(kàn)波(bō)形来(lái)验(yàn)證設計(jì)正(zhèng)确性(xìng),真(zhēn)的(de)是(shì)要(yào)搞到(dào)眼(yǎn)瞎。为(wèi)保證测試完備性(xìng),测試包(bāo)生(shēng)成(chéng)task可(kě)通(tòng)过(guò)輸入(rù)接口(kǒu)産生(shēng)不(bù)同(tóng)长度(dù)和(hé)无效字(zì)节(jié)數的(de)遞增數據(jù)包(bāo)。testbench中(zhōng)每檢测到(dào)輸出(chū)包(bāo)尾指示信(xìn)号(hào)eop即調用(yòng)packet_check task对(duì)數值進(jìn)行檢测。本(běn)文(wén)的(de)testbench結構較具通(tòng)用(yòng)性(xìng),可(kě)以(yǐ)用(yòng)来(lái)验(yàn)證任意(yì)对(duì)數據(jù)包(bāo)進(jìn)行处理的(de)邏輯單元(yuán)。
之前(qián)Modelsim独立仿真(zhēn)带(dài)有(yǒu)IP核的(de)Vivado工程时(shí)經(jīng)常報錯,只(zhī)好(hǎo)使用(yòng)Vivado自(zì)带(dài)的(de)仿真(zhēn)工具。一(yī)直(zhí)很头(tóu)痛这(zhè)个(gè)問(wèn)題(tí),这(zhè)次(cì)終(zhōng)于(yú)有(yǒu)了(le)進(jìn)展(zhǎn)!首先(xiān)按照常規流程使用(yòng)Vivado調用(yòng)Modelsim進(jìn)行行为(wèi)仿真(zhēn),啟動(dòng)後(hòu)会(huì)在(zài)工程目录(lù)下(xià)産生(shēng)些有(yǒu)用(yòng)的(de)文(wén)件(jiàn),幫助我(wǒ)们(men)脫離Vivado進(jìn)行独立仿真(zhēn)。
在(zài)新建Modelsim工程时(shí),在(zài)紅(hóng)框內(nèi)選擇Vivado工程中(zhōng)<project>.sim -> sim_1 -> behav下(xià)的(de)modelsim.ini文(wén)件(jiàn)。之後(hòu)添加文(wén)件(jiàn)包(bāo)括:待测試設計(jì)文(wén)件(jiàn)、testbench以(yǐ)及(jí)IP核可(kě)綜合文(wén)件(jiàn)。第(dì)三(sān)个(gè)文(wén)件(jiàn)在(zài)<project>.srcs -> sources_1 -> ip -> <ip_name> -> synth下(xià)。

現(xiàn)在(zài)可(kě)以(yǐ)順利啟動(dòng)仿真(zhēn)了(le)。我(wǒ)们(men)来(lái)看(kàn)下(xià)仿真(zhēn)結果(guǒ):


文(wén)件(jiàn)中(zhōng)信(xìn)息打(dǎ)印(yìn)情(qíng)況:

從波(bō)形和(hé)打(dǎ)印(yìn)信(xìn)息的(de)結果(guǒ)来(lái)看(kàn),基本(běn)可(kě)以(yǐ)證明(míng)數據(jù)緩存及(jí)位宽(kuān)轉(zhuǎn)換模块(kuài)邏輯功能(néng)无誤。为(wèi)充分(fēn)验(yàn)證要(yào)進(jìn)一(yī)步給(gěi)出(chū)覆蓋率較高(gāo)的(de)测試數據(jù)集,後(hòu)期(qī)通(tòng)过(guò)編写do文(wén)件(jiàn)批量(liàng)仿真(zhēn)实現(xiàn)。在(zài)FPGA或(huò)IC設計(jì)中(zhōng),验(yàn)證占據(jù)大半開(kāi)發(fà)周期(qī),可(kě)見(jiàn)VerilogHDL的(de)非(fēi)綜合子集也(yě)是(shì)至(zhì)關(guān)重(zhòng)要(yào)的(de),今後(hòu)会(huì)多(duō)總(zǒng)結高(gāo)效的(de)验(yàn)證方(fāng)法!








