同步FIFO学习

需要了解的一些事情

如何区分同步与异步

区分方式描述
狭义理解电路中只有一个时钟, 为同步电路;有多个时钟为异步电路
广义理解电路中只有多个时钟,但时钟之间具有固定的相位关系为同步,否则为异步

同步FIFO的作用

先入先出的数据交互方式,用于做数据的速率匹配,其本质为存储器。例如A,B两个模块传递数据,A发送数据的能力为3组,B接收数据的能力为1组,为了保证A发送的数据不至于丢失,设计FIFO作为缓冲。而同步异步则取决于A和B模块的时钟关系。
在这里插入图片描述

同步FIFO的组成

(1)FIFO写控制逻辑----产生写地址、写有效信号、FIFO写满、写错等状态信号
(2)FIFO读控制逻辑----产生读地址、读有效信号、FIFO读空、读错等状态信号
(3)FIFO存储实体(如REG\MEM)

FIFO的基本概念

如图所示的FIFO由8个位宽为8的REG组成,因此FIFO宽度与深度均为8。FIFO正常情况下写指针在读指针前,当写指针清零,从头开始写数据时,会出现读指针在写之前的情况。FIFO可以被写满,被读空,因此,满信号是在写逻辑中产生,空信号是在读逻辑中产生。
在这里插入图片描述

计数器判断模式下同步FIFO代码

计数器判断逻辑

本部分代码为读写速率相同时的同步FIFO计数器逻辑

always @(posedge clk or negedge rstn)begin
	if(!rstn) begin  //复位状态下计数器清零,当深度为8时,计数器需要4个bit位宽
		cnt <= {$clog2(DEPTH){1'b0},{1'b0}}; //$clog2(DEPTH)为取深度的对数,8个字节深度则为3位
	end
	else begin  //复位无效状态
		if((rd_en && !empty) && (wr_en && !full)) begin  //同时读写的状态,此时计数器不变
			cnt <= cnt;
		end
		else if(rd_en && !empty) begin  //读使能,且FIFO非空,计数器减一
			cnt <= cnt-1'b1;  
	    end
		else if(wr_en && !full) begin  //写使能,且FIFO未满,计数器加一
			cnt <= cnt+1'b1;
		end
	end
end

写逻辑

integer i
always @(posedge clk or negedge rstn)begin
	if(!rstn) begin  //复位状态下FIFO清零
		for(i = 0;i < DEPTH; i=i+1) begin
			mem[i] <= {WIDTH{1'b0}};
		end
	end
	else begin  //复位无效状态
		if(wr_en && !full) begin  //写使能且未满,将输入信号wr_data写入FIFO
			mem[wr_ptr] <= wr_data;
		end
	end
end

写指针逻辑

always @(posedge clk or negedge rstn)begin
	if(!rstn) begin  //复位状态下FIFO清零
		wr_ptr <= {($clog2(DEPTH)){1'b0}}; //3'b0
	end
	else begin  //复位无效状态
		if(wr_en && !full) begin  //写使能且未满,写指针加1
			wr_ptr <= wr_ptr+{{($clog2(DEPTH)-1){1'b0}},{1'b1}}; //3'b001
		end
	end
end

写满判断逻辑

assign full = (cnt == DEPTH);  //当计数器数值等于FIFO深度时,FIFO满

读逻辑

always @(posedge clk or negedge rstn)begin
	if(!rstn) begin  //复位状态下读寄存器清零
		rd_data_r <= {(WIDTH){1'b0}}; //8'b0
	end
	else begin
		if(rd_en && !empty) begin
			rd_data_r <= mem[rd_ptr];  //读使能且非空,返回正确的值
		end
		else if(rd_en && empty) begin
			rd_data_r <= bad_beef;   //读使能且空,返回错误
		end
		else begin  //读禁能,复位读寄存器
			rd_data_r <= {(WIDTH){1'b0}}; //8'b0
		end
	end

end

读指针逻辑

always @(posedge clk or negedge rstn)begin
	if(!rstn) begin  //复位状态下FIFO清零
		rd_ptr <= {($clog2(DEPTH)){1'b0}}; //3'b0
	end
	else begin  //复位无效状态
		if(rd_en && !empty) begin  //写使能且未满,读指针加1
			rd_ptr <= rd_ptr+{{($clog2(DEPTH)-1){1'b0}},{1'b1}}; //3'b001
		end
	end
end

读空判断逻辑

assign empty = (cnt == 0);  //计数器值为0,读空

计数器判断模式下同步FIFO测试代码

module sync_fifo_tb;
	parameter  DW = 16;  
	parameter  FD = 8;
	reg          clk,rstn,wr_en,rd_en;
	reg[DW-1:0]  wr_data;
	reg[DW-1:0]  rd_data;
	wire         full,empty;
	wire[($clog2(FD)):0]   cnt;

	sync_fifo #(.WIDTH(DW),.DEPTH(FD)) sync_fifo_inst(   //例化
				.clk(clk),
				.rstn(rstn),
				.wr_en(wr_en),
				.rd_en(rd_en),
				.wr_data(wr_data),
				.rd_data(rd_data),
				.full(full),
				.empty(empty),
				.cnt(cnt)
				);
				
	always #10 clk = ~clk;  //时钟,每10个时间单位翻转一次电平,周期为20个时间单位

	reg[7:0]   tempdata;
	initial begin  //激励
		clk = 0;      //信号复位
		rstn = 0;
		wr_en = 0;
		rd_en = 0;
		wr_data = 0;
		#15  //延时15个时间单位
		rstn = 1;  //第15个时间单位,复位拉高
		push(1);  //入栈,数据为1,第15个时间单位写使能拉高,第35个时间单位写使能拉低
		fork   //并行块,35timer写使能拉高,入栈数据2,读使能高,出栈数据1,53timer tempdata数据给出,读使能拉低
			push(2);       
			pop(tempdata);
		join
		push(10);  //55timer,入栈10
		push(20);
		push(30);
		push(40);
		push(50);
		push(60);
		push(70); //此时FIFO写满,不再执行PUSH
		push(80);
		push(90); 
		push(100);
		push(110);
		push(120);
		push(130);
		pop(tempdata); //读出第三个数据10
		push(tempdata); //将10入栈
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		push(140);  //140入栈
		pop(tempdata);
		push(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		push(5);
		pop(tempdata);
		#100
		$finish;  //仿真结束
	end
	
	task push(input[7:0] data);
		if(full)
			$display("---Cannot push %d:Full---",data);
		else begin
			$display("Push",data);
			wr_data = data;  //入栈任务,将数据线上的数据送入写寄存器
			wr_en = 1;  //使能,FIFO入栈
			@(posedge clk);  //等待上升沿到来
			#5  //延时5个timer
			wr_en = 0; //拉低写使能,本次入栈结束
		end
	endtask
	task pop(output[7:0] data);
		if(empty)
			$display("---Cannot Pop:Empty---")
		else begin
			rd_en = 1;  //读使能,FIFO出栈
			@(posedge clk); //等待上升沿到来
			#3 //延时3个时间单位
			rd_en = 0; //拉低读使能,结束出栈
			data = rd_data; //将出栈数据给出
			$display("---Poped",,data);
		end
	endtask
endmodule
		

测试结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值