指令監測模塊實現介紹
如下圖所示,為指令監測模塊的運行框圖
將指令設置為8bytes數據,故需要一個64位寄存器進行緩存,在進行數據緩存時,數據不可以輸出至下一級模塊,故對數據和有效指示信號也應該進行相應延遲,指令緩存8周期,檢驗一周期,為了數據的穩定輸出,故延遲10周期,使用shift_ram ip實現該功能即可。
當監測到頭部8個數據不屬于指令數據后,進行有效數據輸出,而若監測到為指令數據,則在輸出有效信號置0,下一級模塊即不會進行數據緩存,避免數據文件緩存錯誤。
指令監測模塊代碼編寫
根據上述框圖的介紹,以及邏輯的梳理,本節代碼可以清晰寫出,其代碼實現如下:
module udp_cmd_check(input i_clk ,input i_rst ,input [7 :0] i_udp_data ,input i_udp_valid ,output [7 :0] o_udp_data ,output o_udp_valid ,output o_store_done ,output o_raddr_clear );reg [63:0] r_check_data ;
reg [1 :0] r_check_res ;
reg [1 :0] r_clear_res ;
reg [1 :0] ro_raddr_clear ;
wire [8 :0] w_dly_din ;
wire [8 :0] w_dly_dout ;
wire w_store_done ;
wire w_raddr_clear ;assign w_dly_din = {i_udp_data,i_udp_valid};dly_w9d10 dly_w9d10_u0 (.D (w_dly_din ),.CLK (i_clk ),.Q (w_dly_dout )
);assign w_store_done = &r_check_res ? 1'b1 : 1'b0;
assign w_raddr_clear= &r_clear_res ? 1'b1 : 1'b0;
assign o_udp_data = w_dly_dout[8 :1] ;
assign o_udp_valid = (w_store_done == 1'b1 || w_raddr_clear == 1'b1) ? 1'b0 : w_dly_dout[0];
assign o_store_done = w_store_done ;
assign o_raddr_clear= w_raddr_clear ;always @(posedge i_clk,posedge i_rst) beginif(i_rst)r_check_data <= 64'd0;else if(i_udp_valid) r_check_data <= {r_check_data[55:0],i_udp_data};
endalways @(posedge i_clk,posedge i_rst) beginif(i_rst)r_check_res <= 2'b00;else if(r_check_data[63:32] == 32'HA5A5A5A5 && r_check_data[31 :0] == 32'HBCBCBCBC) r_check_res <= 2'b11;elser_check_res <= 2'b00;
endalways @(posedge i_clk,posedge i_rst) beginif(i_rst)r_clear_res <= 2'b00;else if(r_check_data[63:32] == 32'HD5D5D5D5 && r_check_data[31 :0] == 32'HFCFCFCFC) r_clear_res <= 2'b11;else if(r_dly_valid & ~w_dly_dout[0])r_clear_res <= 2'b00;
endendmodule
實際r_check_res 和 r_clear_res 只需1bit也可,筆者之前為了降低邏輯位寬過大,曾對高32位和低32位進行分別判斷,測試來看,上述代碼也可以正常運轉,時序不存在問題。
指令監測模塊仿真
在代碼編寫完成后,便需要進行仿真測試,編寫仿真文件,因為在之后的各個模塊檢驗中,都需要進行udp數據接收的模擬,筆者便將udp數據發送、指令發送,編寫為對應的任務塊,方便進行復用,tb文件代碼如下
reg i_udp_clk = 1'b0;
reg i_udp_rst = 1'b0;
wire [7 :0] w_store_udp_data ;
wire w_store_udp_valid ;
wire w_store_done ;
wire w_raddr_clear ;
integer i = 0;
integer j = 0;
always #4 i_udp_clk = ~i_udp_clk;
initial begini_udp_rst = 1;i_sfp_rst = 1;i_ui_rst = 1;#100@(i_sfp_clk) begini_udp_rst <= 1'b0;i_sfp_rst <= 1'b0;i_ui_rst <= 1'b0;end#100/*傳輸擦除指令*/@(posedge i_udp_clk)udp_cmd(64'HD5D5D5D5_FCFCFCFC);/*傳輸32KB*/@(posedge i_udp_clk)for(i = 0;i < 32; i = i + 1) begin@(posedge i_udp_clk)udp_send(1024);#500@(posedge i_udp_clk);end/*傳輸完成指令*/@(posedge i_udp_clk)udp_cmd(64'HA5A5A5A5_BCBCBCBC);
/*指令監測,輸出監測后數據*/
udp_cmd_check udp_cmd_check_u0(.i_clk (i_udp_clk ),.i_rst (i_udp_rst ),.i_udp_data (i_udp_data ),.i_udp_valid (i_udp_valid ),.o_udp_data (w_store_udp_data ),.o_udp_valid (w_store_udp_valid ),.o_store_done (w_store_done ),.o_raddr_clear (w_raddr_clear ));
task udp_send(input [15:0] byte_len);begin : datainteger i;i_udp_data = 8'd0;i_udp_valid = 1'd0;@(posedge i_udp_clk);for(i = 0;i < byte_len ;i = i + 1)begini_udp_data <= i_udp_data + 1'b1;i_udp_valid <= 1'b1;@(posedge i_udp_clk);endi_udp_data <= 8'd0;i_udp_valid <= 1'd0;
end
endtasktask udp_cmd(input [63:0] i_cmd);begin : cmdinteger i;i_udp_data = 8'd0;i_udp_valid = 1'd0;@(posedge i_udp_clk);for(i = 0;i < 8 ;i = i + 1)begini_udp_data <= i_cmd[63:56];i_cmd <= {i_cmd[55:0],8'h0};i_udp_valid <= 1'b1;@(posedge i_udp_clk);endi_udp_data <= 8'd0;i_udp_valid <= 1'd0;
end
endtask
其模擬過程即是,首先發送擦除指令,然后進行數據發送,在數據傳輸完成后,發送數據完成指令,在仿真過程中,就這三項測試做驗證,監測模塊是否成功識別指令,阻止指令數據輸出,以及是否成功輸出有效數據。
由圖片可知,模塊成功監測到DDR擦除指令,并將清除信號輸出置高多周期,數據輸出有效信號置低,在標準以太網中幀長間隔為12bytes,在仿真測試中,并未嚴格要求該參數,但實際模塊工作可以適應這類情況。
由圖片可知,成功對數據進行了下一級模塊轉發。
觀察文件傳輸完成,發送結束碼,模塊成功監測出結束碼,拉高傳輸完成信號,并且數據未進行有效輸出,避免了錯誤緩存。
經過上述仿真,可以看出該模塊正常工作,對于該模塊的編寫是較為簡單的,但指令識別功能,在開發中,是十分常用的模塊,有必要進行掌握。關于本節代碼的問題,以及優化意見,歡迎大家在評論區指出,如果想要對應工程進行學習,歡迎大家私信。