题解 | #异步FIFO#

异步FIFO

http://www.nowcoder.com/practice/40246577a1a04c08b3b7f529f9a268cf

异步FIFO的原理在这里不做赘述,有需要移步知乎,说几个提交过程中遇到的问题:

  1. 指针变换为格雷码之后,此格雷码属于异步时钟域中的组合逻辑,所以第一步必须在本时钟域进行同步(此步骤是错的,会导致FIFO的空满判断错误,原则上写时钟域写指针实时。读时钟域读指针实时),然后再进行常规CDC处理,从而判断空满时也是源时钟域中的信号和目的时钟域中的同步信号进行比较。
  2. 注意题目给的双口RAM模块写和读操作是没有判断空满标志的,因此给此模块输入的时候一定是将空满判断之后的信号输入。
`timescale 1ns/1ns

/***************************************RAM*****************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
	,input [WIDTH-1:0] wdata      	//数据写入
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
	,output reg [WIDTH-1:0] rdata 		//数据输出
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
	if(wenc)
		RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
	if(renc)
		rdata <= RAM_MEM[raddr];
end 

endmodule  

/***************************************AFIFO*****************************************/
module asyn_fifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					wclk	, 
	input 					rclk	,   
	input 					wrstn	,
	input					rrstn	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output wire				wfull	,
	output wire				rempty	,
	output wire [WIDTH-1:0]	rdata
);
    //=======================================parameter
    parameter ADDR_WIDTH = $clog2(DEPTH);
    
    //=======================================defination
    reg [ADDR_WIDTH : 0] wpnt;
    reg [ADDR_WIDTH : 0] rpnt;
    
    wire [ADDR_WIDTH - 1: 0] wpnt_addr;
    wire [ADDR_WIDTH - 1: 0] rpnt_addr;
    
    wire [ADDR_WIDTH : 0] wpnt_gray;
    wire [ADDR_WIDTH : 0] rpnt_gray;
    
    reg [ADDR_WIDTH : 0] wpnt_gray_r;
    reg [ADDR_WIDTH : 0] rpnt_gray_r;
    
    reg [ADDR_WIDTH : 0] wpnt_gray_r_r;
    reg [ADDR_WIDTH : 0] rpnt_gray_r_r;
    
    wire wenc;
    wire renc;
    //=======================================output 
    //pnt
    always@(posedge wclk or negedge wrstn)begin
        if(!wrstn) wpnt <= 'd0;
        else if(!wfull && winc) wpnt <= wpnt + 1'b1;
    end
    
    always@(posedge rclk or negedge rrstn)begin
        if(!rrstn) rpnt <= 'd0;
        else if(!rempty && rinc) rpnt <= rpnt + 1'b1;
    end
    
    //gray & sync
    assign wpnt_gray = wpnt ^ (wpnt >> 1);
    assign rpnt_gray = rpnt ^ (rpnt >> 1);
    
    
    always@(posedge rclk or negedge rrstn)begin
        if(!rrstn) {wpnt_gray_r_r, wpnt_gray_r} <= 'd0;
        else {wpnt_gray_r_r, wpnt_gray_r} <= {wpnt_gray_r, wpnt_gray};
    end
    
    always@(posedge wclk or negedge wrstn)begin
        if(!wrstn) {rpnt_gray_r_r, rpnt_gray_r} <= 'd0;
        else {rpnt_gray_r_r, rpnt_gray_r} <= {rpnt_gray_r, rpnt_gray};
    end
    
    //full & empty
    assign wfull = wpnt_gray == {~rpnt_gray_r_r[ADDR_WIDTH : ADDR_WIDTH - 1], rpnt_gray_r_r[ADDR_WIDTH - 2 : 0]};
    assign rempty = rpnt_gray == wpnt_gray_r_r;
    
    //ram ctrl
    assign wpnt_addr = wpnt[ADDR_WIDTH - 1 : 0];
    assign rpnt_addr = rpnt[ADDR_WIDTH - 1 : 0];
    
    assign wenc = winc && !wfull;
    assign renc = rinc && !rempty;
    
    dual_port_RAM 
        #(
              .DEPTH (DEPTH),
              .WIDTH (WIDTH)
        )
        u_dual_port_RAM
        (
            .wclk (wclk),
            .wenc (wenc),
            .waddr (wpnt_addr), //深度对2取对数,得到地址的位宽。
            .wdata (wdata)    ,  //数据写入
            .rclk (rclk),
            .renc (renc),
            .raddr(rpnt_addr) , //深度对2取对数,得到地址的位宽。
            .rdata(rdata)//数据输出
        ); 
    
    
endmodule
全部评论
就是这样,害我modelsim跑了半天仿真都没找到问题,都怀疑人生了,就不应该打一拍。太强了老哥。这网站错的答案是真多。
点赞 回复 分享
发布于 2023-08-04 02:52 浙江
感觉代码中所有的!wfull和!rempty判断都是不必要的,因为空满判断是输出(相对外界是输入),外界只有在收到非满时才会传入写使能信号,收到非空时才会传入读使能信号,因此在有写使能/读使能时没必要再判断空满
点赞 回复 分享
发布于 2022-04-08 15:53
老哥我有两个问题: 1、二进制转格雷码是用组合逻辑得到的二进制呢还是用打一拍之后的二进制数呢? 2、判断空满是直接用组合逻辑产生的格雷码指针呢还是同步一拍之后的格雷码指针呢? 3、空满信号经组合逻辑产生后,需要对空满信号打一拍同步吗? 前两个问题我的答案是:组合逻辑、组合逻辑;第三个问题不确定
点赞 回复 分享
发布于 2022-03-24 17:25
在写时钟域将写格雷码打一拍同步,然后再得到wfull的标志位,这样不会慢一拍吗,这个时候wpnt已经自加了,但是wfull判断中的wpnt_gray_是对应上一个wpnt的格雷码这样的话wpnt不就超过rpnt了吗
点赞 回复 分享
发布于 2022-03-14 01:28

相关推荐

鸿雁于飞:1. 求职定位乱成一锅粥,直接劝退HR 你期望职位同时写了「项目经理/技术经理/交付经理」,这仨岗根本不是一个赛道!项目经理玩流程和干系人,技术经理玩架构和带技术团队,交付经理玩客户和回款,你仨全堆上,HR直接判定「这人自己都不知道自己要干啥,没核心竞争力」,直接扔简历。 ​ 2. 2年多的职业空窗期,一个字不提,纯纯自杀行为 金融行业最看重职业连贯性和背景干净,你2018年5月到2020年8月,整整2年3个月没上班,啥说明都没有!HR直接脑补你是不是有竞业限制、是不是创业失败、是不是有啥背调过不了的问题,直接不敢往下看,首轮就给你筛了,这是最致命的坑! ​ 3. 工作经历纯纯摆烂,干货全藏起来了 你每段工作就写个公司、职位、时间,干了啥、带了多大团队、出了啥核心成果、给公司赚了/省了多少钱,一个字没有,全堆到后面的项目里了。HR看简历就3秒,第一眼看不到你每段工作的价值,直接就划走了,根本不会翻你后面的项目。 ​ 4. 项目经验像个大杂烩,还全是bug 你堆了快10个项目,银行、证券、公安、政务、日本项目啥都有,跟个杂货铺一样,HR根本看不到你的核心优势在哪。而且项目连个起止时间都不写,谁知道你这是最近的标杆项目,还是10年前刚入行干的活?还有数据前后矛盾,一会说「零事故交付」,一会说「生产事故率降低50%」,HR一看就觉得你瞎包装,根本不信。 ​ 5. 15年经验的经理岗,还在写一线拧螺丝的活,层级完全错配 你都应聘经理级岗位了,简历里还在写自己写接口、写测试脚本、做前端开发这些一线执行的活,完全没写你怎么搭建管理体系、怎么带团队、怎么搞定甲方、怎么控项目风险、怎么拿经营结果,MBA的价值一点没体现出来。HR看完直接觉得:合着你干了15年,还是个高级开发,根本达不到经理岗的要求,直接pass。 ​ 6. AI风口完全没抓住,写了句空话等于没写 现在全行业都在卷AI+金融,人家招管理岗,都要能落地AI场景的人。你就写了句「深化Transformer与大模型底层技术研习」,纯纯空话,一点实际落地成果都没有,跟其他候选人比,完全没差异化优势,人家凭啥放着年轻能落地的不要,要你这个只学了理论的? 姐好好看看,然后改改简历吧,要专,要精,然后降低求职目标。希望你能早日拿到offer
点赞 评论 收藏
分享
03-26 12:00
已编辑
门头沟学院 Java
offer魅魔_oc...:100-200每天,你还要倒贴100
点赞 评论 收藏
分享
评论
5
3
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务