18 Verilog语法_FIFO设计
软件版本:无
操作系统:WIN10 64bit
硬件平台:适用所有系列FPGA
登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!
1概述
本小节主要讲解Verilog语法的FIFO设计,需要掌握FIFO的基本原理,掌握同步FIFO和异步FIFO的结构。
2同步FIFO
FIFO表示先入先出,它是一种存储器结构。同步FIFO是使用单一时钟同时进行读取和写入操作的,数据流和相关的控制逻辑在同一个时钟域内处理。
同步FIFO的接口设计如下:
fifo_clk | fifo的时钟信号 |
fifo_rst_n | fifo的复位信号 |
fifo_wren | fifo的写有效信号 |
fifo_wrdata | fifo的写数据 |
fifo_rden | fifo的读有效信号 |
fifo_rddata | fifo的读数据 |
fifo_full | fifo的状态满信号 |
fifo_empty | fifo的状态空信号 |
fifo_afull | fifo的状态将满信号 |
fifo_aempty | fifo的状态将空信号 |
fifo_room_avail | fifo的状态可写入数信号 |
fifo_data_avail | fifo的状态可读出数信号 |
同步FIFO具体的设计如下:
module synch_fifo#( parameter FIFO_AFULL_SIZE = 1, parameter FIFO_AEMPTY_SIZE = 1, parameter FIFO_ADDR_WIDTH = 4, parameter FIFO_WIDTH = 8, parameter FIFO_DEPTH = 2**FIFO_ADDR_WIDTH ) ( fifo_clk , fifo_rst_n , fifo_wren , fifo_wrdata , fifo_rden , fifo_rddata , fifo_full , fifo_empty , fifo_afull , fifo_aempty , fifo_room_avail, fifo_data_avail );
input wire fifo_clk ; input wire fifo_rst_n ; input wire fifo_wren ; input wire [FIFO_WIDTH-1:0] fifo_wrdata ; input wire fifo_rden ; output wire [FIFO_WIDTH-1:0] fifo_rddata ; output reg fifo_full ; output reg fifo_empty ; output wire fifo_afull ; output wire fifo_aempty ; output reg [FIFO_ADDR_WIDTH:0] fifo_room_avail; output wire [FIFO_ADDR_WIDTH:0] fifo_data_avail;
localparam FIFO_DEPTH_MINUS1 = FIFO_DEPTH - 1;
//****************REG************************** reg [FIFO_ADDR_WIDTH-1:0] wr_ptr,wr_ptr_nxt; reg [FIFO_ADDR_WIDTH-1:0] rd_ptr,rd_ptr_nxt; reg [FIFO_ADDR_WIDTH:0] num_entries,num_entries_nxt;
wire fifo_full_nxt; wire fifo_empty_nxt; wire [FIFO_ADDR_WIDTH:0] fifo_room_avail_nxt;
//write pointer control logic //********************************************* always@(*) begin wr_ptr_nxt = wr_ptr; if(fifo_wren) begin if(wr_ptr == FIFO_DEPTH_MINUS1) wr_ptr_nxt = 'd0; else wr_ptr_nxt = wr_ptr + 1'b1; end end
//read pointer control logic //********************************************* always@(*) begin rd_ptr_nxt = rd_ptr; if(fifo_rden) begin if(rd_ptr == FIFO_DEPTH_MINUS1) rd_ptr_nxt = 'd0; else rd_ptr_nxt = rd_ptr + 1'b1; end end
//calculate number of occupied entries in the fifo //********************************************* always@(*) begin num_entries_nxt = num_entries; if (fifo_wren && fifo_rden) begin num_entries_nxt = num_entries; end else if (fifo_wren) begin num_entries_nxt = num_entries + 1'b1; end else if (fifo_rden) begin num_entries_nxt = num_entries - 1'b1; end end
assign fifo_full_nxt = (num_entries_nxt == FIFO_DEPTH); assign fifo_empty_nxt = (num_entries_nxt == 'd0); assign fifo_data_avail = num_entries; assign fifo_room_avail_nxt = FIFO_DEPTH - num_entries_nxt; assign fifo_afull = (fifo_room_avail_nxt <= FIFO_AFULL_SIZE)? 1:0; assign fifo_aempty = (fifo_data_avail <= FIFO_AEMPTY_SIZE ) ? 1:0;
//the fifo output //********************************************* always@(posedge fifo_clk or negedge fifo_rst_n) begin if (!fifo_rst_n) begin wr_ptr <= 'd0; rd_ptr <= 'd0; num_entries <= 'd0; fifo_full <= 1'b0; fifo_empty <= 1'b1; fifo_room_avail <= FIFO_DEPTH; end else begin wr_ptr <= wr_ptr_nxt; rd_ptr <= rd_ptr_nxt; num_entries <= num_entries_nxt; fifo_full <= fifo_full_nxt; fifo_empty <= fifo_empty_nxt; fifo_room_avail <= fifo_room_avail_nxt; end end
//the ram instantiation sdp //*********************************************
sdp_ram_sync #( .DATA_W (FIFO_WIDTH ), .ADDR_WIDTH (FIFO_ADDR_WIDTH ), .REG_OUT (0 ) )u_ram ( .data_in (fifo_wrdata ), .wraddress (wr_ptr ), .wren (fifo_wren ), .clk (fifo_clk ), .data_out (fifo_rddata ), .rdaddress (rd_ptr ), .rden (fifo_rden ), .rst_n (fifo_rst_n ) );
endmodule
module sdp_ram_sync#( parameter DATA_W = 1, parameter ADDR_WIDTH= 9, parameter REG_OUT = 1, parameter U_DLY = 1 )( input [1 -1:0] clk , input [1 -1:0] rst_n , input [1 -1:0] wren , input [ADDR_WIDTH -1:0] wraddress , input [DATA_W -1:0] data_in , input [1 -1:0] rden , input [ADDR_WIDTH -1:0] rdaddress , output [DATA_W -1:0] data_out );
localparam ADDR_NUM = 2**ADDR_WIDTH;
reg [DATA_W -1:0] mem [ADDR_NUM -1:0]; reg [DATA_W -1:0] q_tmp ; reg [DATA_W -1:0] q_tmp_1d ;
always@(posedge clk or negedge rst_n) begin if(!rst_n) mem[wraddress] <= #U_DLY 'd0; else if(wren==1'b1) begin mem[wraddress] <= #U_DLY data_in; end end
always@(posedge clk or negedge rst_n) begin if(!rst_n) q_tmp <= #U_DLY 'd0; else if(rden==1'b1) begin q_tmp <= #U_DLY mem[rdaddress]; end end
always@(posedge clk ) begin q_tmp_1d <= #U_DLY q_tmp; end
assign data_out =(REG_OUT == 1) ? q_tmp_1d : q_tmp ;
endmodule |
仿真结果如图所示:
3异步FIFO
在实际的应用中常常需要多个时钟域的数据传递,需要使用异步FIFO将数据从一个时钟域传递到另一个时钟域。
从原理上来讲,同步FIFO和异步FIFO类似,但是由于异步FIFO与两个时钟相关,电路复杂度变高。对于异步FIFO的写入操作和读出操作的方式与同步FIFO类似,写入和读出操作都有自己的信号集,其复杂度在于产生FIFO的状态信号。
通过比较写入指针和读取时钟的值,可以产生空,满等状态信号,指针的跨时钟传递经过了格雷码和二进制码的转换以及打两拍进行跨时钟的处理。如下图所示
写入指针首先被转换为格雷码,并被寄存到写入时钟域的触发器中。然后,再经过读取时钟域同步,打两拍,后面进行格雷码到二进制的转换电路,进行寄存。此时写入指针已被传送到读取时钟域,用于和读取时钟域的读指针进行比较,求得空状态信号。
当读取指针被传送到写入时钟域时,相对于读取时钟域中的读取指针将会有3-4个的时钟周期延迟。这意味着可用于写入数据的位置要比显示的可能要多三到四个。这是异步FIFO操作保守的一面,如此才不会有数据上溢出。当FIFO读取数据时,读指针数据在三到四个周期内将会跟上实际的读指针值。另外FIFO具有一定的深度,其中有足够多的数据被读取,因此不会影响数据传送的性能。例:
module asynch_fifo#( parameter FIFO_AFULL_SIZE = 1, parameter FIFO_AEMPTY_SIZE = 1, parameter FIFO_PTR = 4, parameter FIFO_WIDTH = 8 // parameter FIFO_DEPTH = 16 )( fifo_wrclk , fifo_wr_rst_n , fifo_wren , fifo_wrdata , reset_wrptr , fifo_rdclk , fifo_rd_rst_n , fifo_rden , fifo_rddata , reset_rdptr , fifo_full , fifo_empty , fifo_afull , fifo_aempty , fifo_room_avail, fifo_data_avail ); input wire fifo_wrclk ; input wire fifo_wr_rst_n ; input wire fifo_wren ; input wire [FIFO_WIDTH-1:0] fifo_wrdata ; input wire reset_wrptr ; input wire fifo_rdclk ; input wire fifo_rd_rst_n ; input wire fifo_rden ; output wire [FIFO_WIDTH-1:0] fifo_rddata ; input wire reset_rdptr ; output reg fifo_full ; output reg fifo_empty ; output wire fifo_afull ; output wire fifo_aempty ; output reg [FIFO_PTR:0] fifo_room_avail; output reg [FIFO_PTR:0] fifo_data_avail;
localparam FIFO_DEPTH = (1<<FIFO_PTR); localparam FIFO_TWICEDEPTH_MINUS1 = 2*FIFO_DEPTH - 1;
//****************REG************************** reg [FIFO_PTR:0] wr_ptr_wab,wr_ptr_wab_nxt;//extra wraparound bit wire [FIFO_PTR:0] fifo_room_avail_nxt ; wire fifo_full_nxt ; wire [FIFO_PTR:0] wr_ptr ;//write ptr without wraparound bit reg [FIFO_PTR:0] rd_ptr_wab,rd_ptr_wab_nxt;//extra wraparound bit wire [FIFO_PTR:0] fifo_data_avail_nxt ; wire fifo_empty_nxt ; wire [FIFO_PTR:0] rd_ptr ;//read ptr without wraparound bit
reg [FIFO_PTR:0] wr_ptr_wab_gray ; wire [FIFO_PTR:0] wr_ptr_wab_gray_nxt ; reg [FIFO_PTR:0] wr_ptr_wab_gray_sync1 ; reg [FIFO_PTR:0] wr_ptr_wab_gray_sync2 ; reg [FIFO_PTR:0] wr_ptr_wab_rdclk ; wire [FIFO_PTR:0] wr_ptr_wab_rdclk_nxt ;
reg [FIFO_PTR:0] rd_ptr_wab_gray ; wire [FIFO_PTR:0] rd_ptr_wab_gray_nxt ; reg [FIFO_PTR:0] rd_ptr_wab_gray_sync1 ; reg [FIFO_PTR:0] rd_ptr_wab_gray_sync2 ; reg [FIFO_PTR:0] rd_ptr_wab_wrclk ; wire [FIFO_PTR:0] rd_ptr_wab_wrclk_nxt ; //write pointer control logic //********************************************* always@(*) begin wr_ptr_wab_nxt = wr_ptr_wab; if(reset_wrptr) begin wr_ptr_wab_nxt = 'd0; end else if(fifo_wren&&(wr_ptr_wab == FIFO_TWICEDEPTH_MINUS1)) begin wr_ptr_wab_nxt = 'd0; end else if(fifo_wren) begin wr_ptr_wab_nxt = wr_ptr_wab + 1'b1; end end
always@(posedge fifo_wrclk or negedge fifo_wr_rst_n) begin if(!fifo_wr_rst_n) begin wr_ptr_wab <= 'd0; end else begin wr_ptr_wab <= wr_ptr_wab_nxt; end end
//convert the binary wr_ptr to gray,flop it,and then pass it to read domain //********************************************* binary_to_gray#(.PTR(FIFO_PTR)) u_binary_to_gray_wr ( .binary_value (wr_ptr_wab_nxt ), .gray_value (wr_ptr_wab_gray_nxt ) );
always@(posedge fifo_wrclk or negedge fifo_wr_rst_n) begin if(!fifo_wr_rst_n) begin wr_ptr_wab_gray <= 'd0; end else begin wr_ptr_wab_gray <= wr_ptr_wab_gray_nxt; end end
//synchronize wr_ptr_wab_gray into read clock domain //********************************************* always@(posedge fifo_rdclk or negedge fifo_rd_rst_n) begin if(!fifo_rd_rst_n) begin wr_ptr_wab_gray_sync1 <= 'd0; wr_ptr_wab_gray_sync2 <= 'd0; end else begin wr_ptr_wab_gray_sync1 <= wr_ptr_wab_gray; wr_ptr_wab_gray_sync2 <= wr_ptr_wab_gray_sync1; end end
//convert wr_ptr_wab_gray_sync2 back to binary form //********************************************* gray_to_binary#(.PTR(FIFO_PTR)) u_gray_to_binary_wr ( .gray_value (wr_ptr_wab_gray_sync2 ), .binary_value (wr_ptr_wab_rdclk_nxt ) );
always@(posedge fifo_rdclk or negedge fifo_rd_rst_n) begin if(!fifo_rd_rst_n) begin wr_ptr_wab_rdclk <= 'd0; end else begin wr_ptr_wab_rdclk <= wr_ptr_wab_rdclk_nxt; end end
//read pointer control logic //********************************************* always@(*) begin rd_ptr_wab_nxt = rd_ptr_wab; if(reset_rdptr) begin rd_ptr_wab_nxt = 'd0; end else if(fifo_rden && (rd_ptr_wab == FIFO_TWICEDEPTH_MINUS1)) begin rd_ptr_wab_nxt = 'd0; end else if(fifo_rden) begin rd_ptr_wab_nxt = rd_ptr_wab + 1'b1; end end
always@(posedge fifo_rdclk or negedge fifo_rd_rst_n) begin if(!fifo_rd_rst_n) begin rd_ptr_wab <= 'd0; end else begin rd_ptr_wab <= rd_ptr_wab_nxt; end end
//convert the binary rd_ptr to gray and then pass it to write clock domain //********************************************* binary_to_gray#(.PTR(FIFO_PTR)) u_binary_to_gray_rd ( .binary_value (rd_ptr_wab_nxt ), .gray_value (rd_ptr_wab_gray_nxt ) );
always@(posedge fifo_rdclk or negedge fifo_rd_rst_n) begin if(!fifo_rd_rst_n) begin rd_ptr_wab_gray <= 'd0; end else begin rd_ptr_wab_gray <= rd_ptr_wab_gray_nxt; end end
//synchronize rd_ptr_wab_gray into write clock domain //********************************************* always@(posedge fifo_wrclk or negedge fifo_wr_rst_n) begin if(!fifo_wr_rst_n) begin rd_ptr_wab_gray_sync1 <= 'd0; rd_ptr_wab_gray_sync2 <= 'd0; end else begin rd_ptr_wab_gray_sync1 <= rd_ptr_wab_gray; rd_ptr_wab_gray_sync2 <= rd_ptr_wab_gray_sync1; end end
//convert rd_ptr_wab_gray_sync2 back to binary form //********************************************* gray_to_binary#(.PTR(FIFO_PTR)) u_gray_to_binary_rd ( .gray_value (rd_ptr_wab_gray_sync2 ), .binary_value (rd_ptr_wab_wrclk_nxt ) );
always@(posedge fifo_rdclk or negedge fifo_rd_rst_n) begin if(!fifo_rd_rst_n) begin rd_ptr_wab_wrclk <= 'd0; end else begin rd_ptr_wab_wrclk <= rd_ptr_wab_wrclk_nxt; end end
assign wr_ptr = wr_ptr_wab[FIFO_PTR-1:0]; assign rd_ptr = rd_ptr_wab[FIFO_PTR-1:0];
//the ram instantiation sdp //*********************************************
sdp_ram #( .DATA_W (FIFO_WIDTH ), .ADDR_WIDTH (FIFO_PTR ), .REG_OUT (0 ) )u_ram( .data_in (fifo_wrdata ), .wraddress (wr_ptr ), .wren (fifo_wren ), .clk_w (fifo_wrclk ), .data_out (fifo_rddata ), .rdaddress (rd_ptr ), .rden (fifo_rden ), .clk_r (fifo_rdclk ), .rst_n_w (fifo_wr_rst_n ), .rst_n_r (fifo_rd_rst_n ) );
//generate fifo_full:pointers equal,but the warp around bits are different //********************************************* assign fifo_full_nxt = (wr_ptr_wab_nxt[FIFO_PTR] != rd_ptr_wab_wrclk_nxt[FIFO_PTR]) && (wr_ptr_wab_nxt[FIFO_PTR-1:0] == rd_ptr_wab_wrclk_nxt[FIFO_PTR-1:0]);
assign fifo_room_avail_nxt = (wr_ptr_wab[FIFO_PTR] == rd_ptr_wab_wrclk[FIFO_PTR])? (FIFO_DEPTH - (wr_ptr_wab[FIFO_PTR-1:0] - rd_ptr_wab_wrclk[FIFO_PTR-1:0])): (rd_ptr_wab_wrclk[FIFO_PTR-1:0] - wr_ptr_wab[FIFO_PTR-1:0]);
//generate fifo_empty:pointers are equal including the warp around bits //********************************************* assign fifo_empty_nxt = (rd_ptr_wab_nxt[FIFO_PTR:0] == wr_ptr_wab_rdclk_nxt[FIFO_PTR:0]);
assign fifo_data_avail_nxt = (rd_ptr_wab[FIFO_PTR] == wr_ptr_wab_rdclk[FIFO_PTR])? (wr_ptr_wab_rdclk[FIFO_PTR-1:0] - rd_ptr_wab[FIFO_PTR-1:0]): (FIFO_DEPTH - (rd_ptr_wab[FIFO_PTR-1:0] - wr_ptr_wab_rdclk[FIFO_PTR-1:0]));
assign fifo_afull = (fifo_room_avail_nxt <= FIFO_AFULL_SIZE)? 1:0; assign fifo_aempty = (fifo_data_avail_nxt <= FIFO_AEMPTY_SIZE ) ? 1:0;
always@(posedge fifo_wrclk or negedge fifo_wr_rst_n) begin if(!fifo_wr_rst_n) begin fifo_full <= 1'b0; fifo_room_avail <= 'd0; end else begin fifo_full <= fifo_full_nxt; fifo_room_avail <= fifo_room_avail_nxt; end end
always@(posedge fifo_rdclk or negedge fifo_rd_rst_n) begin if(!fifo_rd_rst_n) begin fifo_empty <= 1'b1; fifo_data_avail <= 'd0; end else begin fifo_empty <= fifo_empty_nxt; fifo_data_avail <= fifo_data_avail_nxt; end end
endmodule
module binary_to_gray#( parameter PTR = 4 //"1","0" )( binary_value , gray_value );
input wire [PTR:0] binary_value ; output wire [PTR:0] gray_value ;
generate genvar i; for( i = 0 ; i < PTR ; i = i + 1 ) begin assign gray_value[i] = binary_value[i] ^ binary_value[ i + 1 ]; end
endgenerate
assign gray_value[PTR] = binary_value[PTR];
endmodule
module gray_to_binary#( parameter PTR = 4 //"1","0" )( binary_value , gray_value );
input wire [PTR:0] gray_value ; output wire [PTR:0] binary_value ;
generate genvar i; for( i = 0 ; i < PTR ; i = i + 1 ) begin assign binary_value[i] = binary_value[i + 1] ^ gray_value[i]; end
endgenerate
assign binary_value[PTR] = gray_value[PTR];
endmodule
module sdp_ram#( parameter DATA_W = 1, parameter ADDR_WIDTH= 9, parameter REG_OUT = 1, parameter U_DLY = 1 )( input [1 -1:0] clk_w , input [1 -1:0] rst_n_w , input [1 -1:0] wren , input [ADDR_WIDTH -1:0] wraddress , input [DATA_W -1:0] data_in , input [1 -1:0] clk_r , input [1 -1:0] rst_n_r , input [1 -1:0] rden , input [ADDR_WIDTH -1:0] rdaddress , output [DATA_W -1:0] data_out );
localparam ADDR_NUM = 2**ADDR_WIDTH;
reg [DATA_W -1:0] mem [ADDR_NUM -1:0]; reg [DATA_W -1:0] q_tmp ; reg [DATA_W -1:0] q_tmp_1d ;
always@(posedge clk_w or negedge rst_n_w) begin if(!rst_n_w) mem[wraddress] <= #U_DLY 'd0; else if(wren==1'b1) begin mem[wraddress] <= #U_DLY data_in; end end
always@(posedge clk_r or negedge rst_n_r) begin if(!rst_n_r) q_tmp <= #U_DLY 'd0; else if(rden==1'b1) begin q_tmp <= #U_DLY mem[rdaddress]; end end
always@(posedge clk_r ) begin q_tmp_1d <= #U_DLY q_tmp; end
assign data_out =(REG_OUT == 1) ? q_tmp_1d : q_tmp ;
endmodule |
仿真结果如图所示:
本文来米联客(milianke),作者:米联客(milianke),转载请注明原文链接:https://www.cnblogs.com/milianke/p/17950393