推荐答案
测试一下
题目
用verilog/vhdl写一个fifo控制器(包括空,满,半满信号)
参考答案与知识点
参考答案
FIFO控制器核心是管理读写指针,产生空、满、半满标志。以下以同步FIFO为例,深度为16(4位指针,实际用5位区分满状态)。
Verilog代码:
```verilog
module fifo_ctrl #(parameter DEPTH=16, PTR_WID=4) (
input clk, rst_n, wr_en, rd_en,
output reg full, empty, half_full
);
localparam PTR = PTR_WID + 1; // 多一位用于满判断
reg [PTR-1:0] wr_ptr, rd_ptr;
wire [PTR_WID-1:0] wr_addr = wr_ptr[PTR_WID-1:0];
wire [PTR_WID-1:0] rd_addr = rd_ptr[PTR_WID-1:0];
wire [PTR-1:0] wr_next = wr_ptr + 1;
wire [PTR-1:0] rd_next = rd_ptr + 1;
// 写指针递增
always @(posedge clk or negedge rst_n)
if (!rst_n) wr_ptr <= 0;
else if (wr_en && !full) wr_ptr <= wr_next;
// 读指针递增
always @(posedge clk or negedge rst_n)
if (!rst_n) rd_ptr <= 0;
else if (rd_en && !empty) rd_ptr <= rd_next;
// 空标志:复位时空,或读写指针相等且未写(实际复位后指针相等,但需要小心)
wire same_ptr = (wr_ptr == rd_ptr);
always @(posedge clk or negedge rst_n)
if (!rst_n) empty <= 1;
else if (wr_en && rd_en) empty <= empty; // 同时读写,空不变
else if (wr_en) empty <= 0;
else if (rd_en) empty <= (wr_ptr == rd_next); // 读后指针追上写指针
// 满标志:写入后写指针追上读指针(高一比特不同,其余相同)
wire almost_full = (wr_next == rd_ptr) && (wr_ptr[PTR-1] != rd_ptr[PTR-1]);
always @(posedge clk or negedge rst_n)
if (!rst_n) full <= 0;
else if (wr_en && rd_en) full <= full;
else if (wr_en) full <= almost_full;
else if (rd_en) full <= 0;
// 半满:数据量 > DEPTH/2 (读写指针差 > DEPTH/2)
// 用减法器或比较,这里用差值
wire signed [PTR:0] diff = {1'b0, wr_ptr} - {1'b0, rd_ptr}; // 注意位宽
always @(posedge clk or negedge rst_n)
if (!rst_n) half_full <= 0;
else half_full <= (diff >= (DEPTH/2));
endmodule
```
解析:
- 使用双指针(wr_ptr, rd_ptr),位宽比地址宽度多1位以区分满和空。
- 空:复位后空,且读写指针相等且无写操作时为空。实际电路用读写指针相等且最近一次操作是读(或复位)判定空。更严谨做法:设置empty_reg由两个条件决定:指针相等且没有写操作(或写使能无效)。先写后读逻辑,常见写法用wr_en和rd_en的时序关系。
- 满:写指针的下一个值等于读指针,且两指针最高位相反(表明写指针绕了一圈)。
- 半满:读写指针差值大于等于深度一半。简单减法实现,注意有符号处理。
易错点:
1. 空满判断必须考虑指针绕回,不能仅靠地址相等。
2. 半满信号若用组合逻辑,需注意毛刺;通常用寄存器打拍。
3. 同时读写时状态不变。
4. 复位时指针清零,空信号置1,满信号置0。
涉及知识点
- verilog
- Verilog
- 复位