带选通信号的同步FIFO(可综合RTL设计)
还是上次那个同步FIFO,传送门在这~
-
因为本次FIFO的输入数据位宽实际上可以看成是不固定的,每次输出的数据却都是4byte,那么很容易产生的一个问题就是,前一时刻还是未满状态,下一时刻却已经是溢出(overflow)了
-
那么为了解决上一个问题,我的想法是,当FIFO深度不足8byte时,就拉低Ready_in信号,不再写入数据。
-
有同学和我讨论过一个问题,那就是设置为深度不足4byte的时候可不可以,原本想法是当FIFO中存在足够数据时,那么每个clock都会输出4byte数据,此时输入8byte还是可以存储的,可能这个问题和仿真器有关,我们不确定到底是先读还是先写,这个会存在不确定态,所以最好还是预留8byte的空间为好。
- 我们最好把FIFO宽度设置为1byte,从交上来的作业来看,我们常犯的错误是写入数据指针的设置,有点同学直接设置一个基指针,然后就是如下代码所示:
// write pointer always @(posedge clock&nbs***bsp;negedge reset_n) begin if (!reset_n) begin wptr <= 0; end else begin if (valid_in & ready_in) begin // can write into the fifo when input is valid and fifo is ready case (size) 2'b00: begin fifo_mem[wptr[ADDR_WIDTH-1:0]] <= data_in[7:0]; wptr <= wptr + 1; end 2'b01: begin fifo_mem[wptr[ADDR_WIDTH-1:0]] <= data_in[7:0]; fifo_mem[wptr[ADDR_WIDTH-1:0]+1] <= data_in[15:8]; wptr <= wptr + 2; end 2'b10: begin fifo_mem[wptr[ADDR_WIDTH-1:0]] <= data_in[7:0]; fifo_mem[wptr[ADDR_WIDTH-1:0]+1] <= data_in[15:8]; fifo_mem[wptr[ADDR_WIDTH-1:0]+2] <= data_in[23:16]; fifo_mem[wptr[ADDR_WIDTH-1:0]+3] <= data_in[31:24]; wptr <= wptr + 4;
上述代码的主要问题是,会存在数据写飞的情况...
数据在哪会丢失呢?确实,我们把wptr写指针的数据宽度设置好了,为5位宽,因此wptr最多为31,没毛病,可是这时候wptr[ADDR_WIDTH-1:0]+3为多少呢?这个数据会写到地址为34的FIFO中,因为不存在这个地址,然后数据就丢了...丢了解决方法有好几种,比如我们多设计几个中间变量,把变量的宽度进行限制;比如我们把上述写指针取余,这样就不可能宽度溢出...还有很多方法设计FIFO的方法有很多,可以加计数器,判断FIFO内部还有多少数据,甚至可以写状态机...我设计的时候就是不想加计数器,这样可以减少硬件资源,只通过读写地址之间的关系判断是否空满...
主要思路如下(code仅供参考,欢迎讨论):
`timescale 1ns / 100ps //****************************************************************** // Author:SJTU_chen // Date: 2019/10/26 // Version: v1.0 // Module Name: fifo-dut // Project Name: SystemVerilog Lab1 //******************************************************************* module fifo_dut( clock,reset_n,valid_in,wstrb,data_in,valid_out,data_out,ready_in ); input clock,reset_n,valid_in; input [1:0] wstrb; input [63:0] data_in; output valid_out; output[31:0] data_out; output ready_in; parameter FIFO_DATA_WIDTH = 8 ; parameter FIFO_DP = 32 ; parameter FIFO_ADDR_WIDTH = clogb2(FIFO_DP) ; reg [31:0] data_out; reg valid_out; wire enough_data; wire enough_space; wire ready_in; wire [4:0] fifo_wr_addr1,fifo_wr_addr2,fifo_wr_addr3,fifo_wr_addr4,fifo_wr_addr5,fifo_wr_addr6,fifo_wr_addr7; wire [FIFO_ADDR_WIDTH-1:0] fifo_wr_addr ; //fifo write address wire [FIFO_ADDR_WIDTH-1:0] fifo_rd_addr ; //fifo read address reg [FIFO_ADDR_WIDTH:0] wr_addr_ptr; //fifo write pointer reg [FIFO_ADDR_WIDTH:0] rd_addr_ptr; //fifo read pointer reg [FIFO_DATA_WIDTH-1:0] fifo_mem [FIFO_DP-1:0] ; //fifo data width is 8 .depth is 32 assign fifo_wr_addr1=fifo_wr_addr+1; assign fifo_wr_addr2=fifo_wr_addr+2; assign fifo_wr_addr3=fifo_wr_addr+3; assign fifo_wr_addr4=fifo_wr_addr+4; assign fifo_wr_addr5=fifo_wr_addr+5; assign fifo_wr_addr6=fifo_wr_addr+6; assign fifo_wr_addr7=fifo_wr_addr+7;上述参数和中间变量设置好以后,接下来就是读写数据啦
// write data to fifo
always @(posedge clock&nbs***bsp;negedge reset_n)
if (reset_n &&valid_in && enough_space) begin
if(wstrb==2'b00) begin
fifo_mem[fifo_wr_addr] <= data_in[7:0];
end
else if (wstrb==2'b01) begin
fifo_mem[fifo_wr_addr] <= data_in[7:0];
fifo_mem[fifo_wr_addr1] <= data_in[15:8];
end
else if (wstrb==2'b10) begin
fifo_mem[fifo_wr_addr] <= data_in[7:0];
fifo_mem[fifo_wr_addr1] <= data_in[15:8];
fifo_mem[fifo_wr_addr2] <= data_in[23:16];
fifo_mem[fifo_wr_addr3] <= data_in[31:24];
end
else if (wstrb==2'b11) begin
fifo_mem[fifo_wr_addr] <= data_in[7:0];
fifo_mem[fifo_wr_addr1] <= data_in[15:8];
fifo_mem[fifo_wr_addr2] <= data_in[23:16];
fifo_mem[fifo_wr_addr3] <= data_in[31:24];
fifo_mem[fifo_wr_addr4] <= data_in[39:32];
fifo_mem[fifo_wr_addr5] <= data_in[47:40];
fifo_mem[fifo_wr_addr6] <= data_in[55:48];
fifo_mem[fifo_wr_addr7] <= data_in[63:56];
end
end
else begin
fifo_mem[fifo_wr_addr] <= fifo_mem[fifo_wr_addr];
end
// read data
always @(posedge clock&nbs***bsp;negedge reset_n)
begin
if(reset_n == 1'b0) begin
data_out <= {32{1'b0}} ;
end
else if (enough_data) begin
data_out[7:0] <= fifo_mem[fifo_rd_addr] ;
data_out[15:8] <= fifo_mem[fifo_rd_addr+1] ;
data_out[23:16] <= fifo_mem[fifo_rd_addr+2] ;
data_out[31:24] <= fifo_mem[fifo_rd_addr+3] ;
end
else begin
data_out <=data_out;
end
end 指针变化
// write pointer
always @(posedge clock&nbs***bsp;negedge reset_n)
begin
if(reset_n == 1'b0)
wr_addr_ptr <= {(FIFO_ADDR_WIDTH+1){1'b0}} ;
else if (valid_in && enough_space)begin
if(wstrb==2'b00) begin
wr_addr_ptr <= wr_addr_ptr + 1'b1 ;
end
else if(wstrb==2'b01) begin
wr_addr_ptr <= wr_addr_ptr + 2'b10 ;
end
else if(wstrb==2'b10) begin
wr_addr_ptr <= wr_addr_ptr + 3'b100 ;
end
else if(wstrb==2'b11) begin
wr_addr_ptr <= wr_addr_ptr + 4'b1000 ;
end
end
else begin
wr_addr_ptr <= wr_addr_ptr;
end
end
//read pointer
always @(posedge clock&nbs***bsp;negedge reset_n)
begin
if(reset_n == 1'b0)
rd_addr_ptr <= {(FIFO_ADDR_WIDTH+1){1'b0}} ;
else if (enough_data)
rd_addr_ptr <= rd_addr_ptr + 3'b100 ;
else
rd_addr_ptr <= rd_addr_ptr;
end 状态变化 // valid_out state always @(posedge clock&nbs***bsp;negedge reset_n) begin if(reset_n == 1'b0) begin valid_out <='0; end else if (enough_data) begin valid_out <='1; end else begin valid_out <='0; end end判断内部空间:
//ready_in assign ready_in = enough_space; assign fifo_wr_addr = wr_addr_ptr[FIFO_ADDR_WIDTH-1:0]; assign fifo_rd_addr = rd_addr_ptr[FIFO_ADDR_WIDTH-1:0]; assign enough_data = ((wr_addr_ptr >= rd_addr_ptr+4)||(rd_addr_ptr >= wr_addr_ptr+4))?1:0; wire addr_select1,addr_select2,addr_select3; assign addr_select1 =((fifo_rd_addr>fifo_wr_addr)&&( fifo_rd_addr-fifo_wr_addr<8))?1:0; assign addr_select2 =((fifo_rd_addr<fifo_wr_addr)&&( fifo_wr_addr-fifo_rd_addr>=24))?1:0; assign addr_select3 = (wr_addr_ptr[FIFO_ADDR_WIDTH]^rd_addr_ptr[FIFO_ADDR_WIDTH])?1:0; assign addr_select4 = (wr_addr_ptr[FIFO_ADDR_WIDTH]~^rd_addr_ptr[FIFO_ADDR_WIDTH])?1:0; assign enough_space =((addr_select1&addr_select3)|(addr_select2&addr_select4))?0:1;上述代码中,addr_select判断读指针和写指针的位置,1.当读指针大于写指针,两个指针位置小于8,并且wr_addr_ptr的最高位不相同时则接近满。2.当写指针大于读指针,两个指针位置大于24,并且wr_addr_ptr的最高位相同时则接近满。这个是在深度为32时的情况,当深度为其他深度n时,则数值24应该改为n-8。
function integer clogb2 (input integer size); begin size = size - 1; for (clogb2=1; size>1; clogb2=clogb2+1) size = size >> 1; end endfunction // clogb2
上述代码实现的就是一个取对数函数,用来生成地址宽度与数据深度之间的关系。
上述代码经过questa sim10.6c验证,应该是没问题,但是不保证完全正确,如有bug,欢迎私信交流~