单页网站如何制作/厦门关键词排名优化
本文从微信公众号--数字IC小站,转载,欢迎关注,微信公众号更新更多更快
带选通信号的同步FIFO(可综合RTL设计)mp.weixin.qq.com
还是上次那个同步FIFO,传送门在这~
带选通信号的同步FIFO(重发)mp.weixin.qq.com
上次讲的是用SystemVerilog去设计这个FIFO,那么如果用可综合的RTL代码怎么设计呢?
- 因为本次FIFO的输入数据位宽实际上可以看成是不固定的,每次输出的数据却都是4byte,那么很容易产生的一个问题就是,前一时刻还是未满状态,下一时刻却已经是溢出(overflow)了
- 那么为了解决上一个问题,我的想法是,当FIFO深度不足8byte时,就拉低Ready_in信号,不再写入数据。
- 有同学和我讨论过一个问题,那就是设置为深度不足4byte的时候可不可以,原本想法是当FIFO中存在足够数据时,那么每个clock都会输出4byte数据,此时输入8byte还是可以存储的,可能这个问题和仿真器有关,我们不确定到底是先读还是先写,这个会存在不确定态,所以最好还是预留8byte的空间为好。
- 我们最好把FIFO宽度设置为1byte,从交上来的作业来看,我们常犯的错误是写入数据指针的设置,有点同学直接设置一个基指针,然后就是如下代码所示:
// write pointer always @(posedge clock or negedge reset_n) beginif (!reset_n) beginwptr <= 0;end else beginif (valid_in & ready_in) begin// can write into the fifo when input is valid and fifo is readycase (size)2'b00: begin fifo_mem[wptr[ADDR_WIDTH-1:0]] <= data_in[7:0];wptr <= wptr + 1;end2'b01: beginfifo_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;end2'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 addresswire [FIFO_ADDR_WIDTH-1:0] fifo_rd_addr ; //fifo read addressreg [FIFO_ADDR_WIDTH:0] wr_addr_ptr; //fifo write pointerreg [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 fifoalways @(posedge clock or negedge reset_n)if (reset_n &&valid_in && enough_space) beginif(wstrb==2'b00) beginfifo_mem[fifo_wr_addr] <= data_in[7:0];endelse if (wstrb==2'b01) beginfifo_mem[fifo_wr_addr] <= data_in[7:0];fifo_mem[fifo_wr_addr1] <= data_in[15:8];endelse if (wstrb==2'b10) beginfifo_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];endelse if (wstrb==2'b11) beginfifo_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];endendelse beginfifo_mem[fifo_wr_addr] <= fifo_mem[fifo_wr_addr];end// read dataalways @(posedge clock or negedge reset_n)beginif(reset_n == 1'b0) begindata_out <= {32{1'b0}} ;endelse if (enough_data) begindata_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] ; endelse begindata_out <=data_out;end end
指针变化
// write pointer always @(posedge clock or negedge reset_n)beginif(reset_n == 1'b0)wr_addr_ptr <= {(FIFO_ADDR_WIDTH+1){1'b0}} ;else if (valid_in && enough_space)beginif(wstrb==2'b00) beginwr_addr_ptr <= wr_addr_ptr + 1'b1 ;endelse if(wstrb==2'b01) beginwr_addr_ptr <= wr_addr_ptr + 2'b10 ;endelse if(wstrb==2'b10) beginwr_addr_ptr <= wr_addr_ptr + 3'b100 ;endelse if(wstrb==2'b11) beginwr_addr_ptr <= wr_addr_ptr + 4'b1000 ;endendelse beginwr_addr_ptr <= wr_addr_ptr;end end
//read pointeralways @(posedge clock or negedge reset_n)beginif(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 ; elserd_addr_ptr <= rd_addr_ptr;end
状态变化
// valid_out statealways @(posedge clock or negedge reset_n)beginif(reset_n == 1'b0) beginvalid_out <='0;endelse if (enough_data) beginvalid_out <='1;endelse beginvalid_out <='0;end end
判断内部空间:
//ready_inassign 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);beginsize = size - 1;for (clogb2=1; size>1; clogb2=clogb2+1)size = size >> 1;endendfunction // clogb2
上述代码实现的就是一个取对数函数,用来生成地址宽度与数据深度之间的关系。
上述代码经过questa sim10.6c验证,应该是没问题,但是不保证完全正确,如有bug,欢迎私信交流~
欢迎关注,分享~~