上周阿里面试官问ram两侧读写位宽不一样改如何解决?当时含糊了一下,事后再认真分析其中缘由。
思路
取读写位宽的最大公因数作为ram的位宽,控制读写指针的步进,并预测下一次的指针位置从而判断空满,和普通fifo一样的是指针还是要计两圈。
module sync_dff_wid_fifo#(
parameter I_WID = 15,
parameter O_WID = 10,
parameter FIFO_WID = 5,
parameter DEPTH = 8,
parameter ADDR_WID = 3,
parameter I_ADDR_STEP = 3,
parameter O_ADDR_STEP = 2
)
(
input clk,
input rst_n,
input wr_en,
input [I_WID - 1 : 0] wr_data,
input rd_en,
output [O_WID - 1 : 0] rd_data,
output empty,
output full
);
//=============defination
reg [FIFO_WID - 1 : 0] ram [0 : DEPTH - 1];
reg [ADDR_WID : 0] wr_pnt;
reg [ADDR_WID : 0] rd_pnt;
wire [ADDR_WID - 1 : 0] wr_addr;
wire [ADDR_WID - 1 : 0] rd_addr;
wire [ADDR_WID : 0] wr_pnt_next;
wire [ADDR_WID : 0] rd_pnt_next;
reg full, empty;
reg [O_WID - 1 : 0] rd_data;
//=============combinal logic
assign wr_addr = wr_pnt[ADDR_WID - 1 : 0];
assign rd_addr = rd_pnt[ADDR_WID - 1 : 0];
assign wr_pnt_next = wr_pnt + I_ADDR_STEP;
assign rd_pnt_next = rd_pnt + O_ADDR_STEP;
//=============sequential logic
always@(posedge clk or negedge rst_n)begin
if(!rst_n) wr_pnt <= 'd0;
else if(wr_en && !full) wr_pnt <= wr_pnt + I_ADDR_STEP;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n) rd_pnt <= 'd0;
else if(rd_en && !empty) rd_pnt <= rd_pnt + O_ADDR_STEP;
end
always@(posedge clk)begin
if(wr_en && !full) begin
ram[wr_addr] <= wr_data[I_WID - 1 -: FIFO_WID];
ram[wr_addr + 1] <= wr_data[I_WID - FIFO_WID - 1 -: FIFO_WID];
ram[wr_addr + 2] <= wr_data[I_WID - FIFO_WID * 2 - 1 -: FIFO_WID];
end
end
always@(*)begin
if(rd_en && !empty) rd_data = {ram[rd_addr], ram[rd_addr + 1]};
end
always@(*)begin
if((wr_pnt_next[ADDR_WID] ^ rd_pnt_next[ADDR_WID]) && wr_pnt_next[ADDR_WID - 1 : 0] > rd_pnt[ADDR_WID - 1 : 0]) full = 1'b1;
else full = 1'b0;
end
always@(*)begin
if(rd_pnt_next > wr_pnt) empty = 1'b1;
else empty = 1'b0;
end
endmodule
module tb_sync_dff_wid_fifo();
//===================parameter
parameter PERIOD = 10 / 2;
//===================defination
reg clk, rst_n;
reg wr_en, rd_en;
reg [14 : 0] wr_data;
wire [9 : 0] rd_data;
wire full, empty;
//===================output
initial begin
clk = 0;
forever
#PERIOD clk = ~clk;
end
initial begin
rst_n = 0;
#20
rst_n = 1;
end
initial begin
@(rst_n);
wr('b1);
wr('d2);
wr('d3);
rd();
rd();
rd();
//wait(done);
#5000 $finish;
end
sync_dff_wid_fifo u_sync(
.clk(clk),
.rst_n(rst_n),
.wr_en(wr_en),
.wr_data(wr_data),
.rd_en(rd_en),
.rd_data(rd_data),
.full(full),
.empty(empty)
);
task wr(input [29 : 0] data);
@(posedge clk);
#1;
wr_en = 1;
wr_data = data;
@(posedge clk);
#1;
wr_en = 0;
endtask
task rd();
@(posedge clk);
#1;
rd_en = 1;
@(posedge clk);
#1;
rd_en = 0;
endtask
`ifdef FSDB
initial begin
$fsdbDumpfile("tb_sync_dff_wid_fifo.fsdb");
$fsdbDumpvars;
$fsdbDumpMDA();
end
`endif
endmodule