列表

详情


VL68. 同步FIFO

描述

请设计带有空满信号的同步FIFO,FIFO的深度和宽度可配置。双口RAM的参考代码和接口信号已给出,请在答案中添加并例化此部分代码。
电路的接口如下图所示。端口说明如下表。
接口电路图如下:

双口RAM端口说明:

端口名

I/O

描述

wclk

input

写数据时钟

wenc

input

写使能

waddr

input

写地址

wdata

input

输入数据

rclk

input

读数据时钟

renc

input

读使能

raddr

input

读地址

rdata

output

输出数据

同步FIFO端口说明:

端口名

I/O

描述

clk

input

时钟

rst_n

input

异步复位

winc

input

写使能

rinc

input

读使能

wdata

input

写数据

wfull

output

写满信号

rempty

output

读空信号

rdata

output

读数据

参考代码如下:
module dual_port_RAM #(parameter DEPTH = 16,
                       parameter WIDTH = 8)(
     input wclk
    ,input wenc
    ,input [$clog2(DEPTH)-1:0] waddr  
    ,input [WIDTH-1:0] wdata        
    ,input rclk
    ,input renc
    ,input [$clog2(DEPTH)-1:0] raddr  
    ,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 


输入描述

input clk
input rst_n ,
input winc ,
input rinc ,
input                  wdata ,

输出描述

    output reg                wfull    ,
    output reg                rempty    ,
    output wire               rdata

原站题解

Verilog 解法, 执行用时: 0ms, 内存消耗: 0KB, 提交时间: 2022-08-06

`timescale 1ns/1ns

/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  
	,input [WIDTH-1:0] wdata      	
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  
	,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  

/**********************************SFIFO************************************/
module sfifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					clk		, 
	input 					rst_n	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output reg				wfull	,
	output reg				rempty	,
	output wire [WIDTH-1:0]	rdata
);
    parameter ADDR_WIDTH = $clog2(DEPTH);   
    reg     [ADDR_WIDTH:0]    waddr;
    reg     [ADDR_WIDTH:0]    raddr;
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        waddr <= 'd0;
    end 
    else if(!wfull && winc)begin
        waddr <= waddr + 1'd1;
    end
    end
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        raddr <= 'd0;
    end 
    else if(!rempty && rinc)begin
        raddr <= raddr + 1'd1;
    end
    end     
wire        [ADDR_WIDTH : 0]    fifo_cnt;
 
assign fifo_cnt = (waddr[ADDR_WIDTH] == raddr[ADDR_WIDTH]) ? (waddr[ADDR_WIDTH:0] - raddr[ADDR_WIDTH:0]) :
                  (DEPTH + waddr[ADDR_WIDTH-1:0] - raddr[ADDR_WIDTH-1:0]);
always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        wfull <= 'd0;
        rempty <= 'd0;
    end 
    else if(fifo_cnt == 'd0)begin
        rempty <= 1'd1;
    end
    else if(fifo_cnt == DEPTH)begin
        wfull <= 1'd1;
    end
    else begin
        wfull <= 'd0;
        rempty <= 'd0;
    end 
end
        
wire     wen    ;
wire    ren    ;
wire     wren;//high write
assign wen = winc & !wfull;
assign ren = rinc & !rempty;
 
dual_port_RAM #(.DEPTH(DEPTH),
                .WIDTH(WIDTH)
)dual_port_RAM(
    .wclk (clk),  
    .wenc (wen),  
    .waddr(waddr[ADDR_WIDTH-1:0]),  //深度对2取对数,得到地址的位宽。
    .wdata(wdata),           //数据写入
    .rclk (clk), 
    .renc (ren), 
    .raddr(raddr[ADDR_WIDTH-1:0]),   //深度对2取对数,得到地址的位宽。
    .rdata(rdata)          //数据输出
);        

endmodule

Verilog 解法, 执行用时: 0ms, 内存消耗: 0KB, 提交时间: 2022-08-06

`timescale 1ns/1ns

/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  
	,input [WIDTH-1:0] wdata      	
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  
	,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  

/**********************************SFIFO************************************/
module sfifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					clk		, 
	input 					rst_n	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output reg				wfull	,
	output reg				rempty	,
	output wire [WIDTH-1:0]	rdata
);
    parameter ADDR_WIDTH = $clog2(DEPTH);
    reg [ADDR_WIDTH:0] waddr, raddr;
    wire [ADDR_WIDTH:0] waddr_next, raddr_next;
    assign waddr_next = (winc & (!wfull))?(waddr+'d1):waddr;
    assign raddr_next = (rinc & (!rempty))?(raddr+'d1):raddr;
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            {waddr,raddr} <= 'd0;
        end
        else begin
            {waddr,raddr} <= {waddr_next,raddr_next};
        end
    end
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            {wfull,rempty} <= 'd0;
        end
        else begin
            {wfull,rempty} <= {(waddr=={~raddr[ADDR_WIDTH],raddr[ADDR_WIDTH-1:0]}),(waddr==raddr)};
        end
    end
    dual_port_RAM #(.DEPTH(DEPTH),
                    .WIDTH(WIDTH))
    u_dual_port_RAM(
        .wclk(clk),
        .wenc(winc & (!wfull)),
        .waddr(waddr[ADDR_WIDTH-1:0]),  
        .wdata(wdata),      	
        .rclk(clk),
        .renc(rinc & (!rempty)),
        .raddr(raddr[ADDR_WIDTH-1:0]),  
        .rdata(rdata) 		
);
endmodule

Verilog 解法, 执行用时: 0ms, 内存消耗: 0KB, 提交时间: 2022-08-06

`timescale 1ns/1ns

/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  
	,input [WIDTH-1:0] wdata      	
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  
	,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  

/**********************************SFIFO************************************/
module sfifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					clk		, 
	input 					rst_n	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output reg				wfull	,
	output reg				rempty	,
	output wire [WIDTH-1:0]	rdata
);
reg [$clog2(DEPTH)-1:0] waddr;
reg [$clog2(DEPTH)-1:0] raddr; 
reg [$clog2(DEPTH):0] dat_cnt;//ram中的未被读走数据个数
wire   ram_wr_en;//ram写信号
wire   ram_rd_en;//ram读信号
 
assign ram_wr_en=winc&(!wfull);//不满的情况下,写ram才有效
assign ram_rd_en=rinc&(!rempty);//不空的情况,读ram才有效
 
//写地址控制
    always@(posedge clk or negedge rst_n)
   if(!rst_n)
      begin
      waddr<='d0;
      end
   else if(ram_wr_en&&(waddr==DEPTH-1'b1))
        begin
        waddr<='d0;
        end
   else if(ram_wr_en)
        begin
        waddr<=waddr+'d1;
        end
 
//读地址控制
    always@(posedge clk or negedge rst_n)
   if(!rst_n)
      begin
      raddr<='d0;
      end
   else if(ram_rd_en&&(raddr==DEPTH-1'b1))
        begin
        raddr<='d0;
        end
   else if(ram_rd_en)
        begin
        raddr<=raddr+1'b1;
        end
 
//用于统计ram中未被读走的数据个数
always@(posedge clk or negedge rst_n)
   if(!rst_n)
      begin
      dat_cnt<='d0;
      end
   else if(ram_wr_en&ram_rd_en)  //读写使能同时拉高,数据个数不变
        begin 
        dat_cnt<=dat_cnt;
        end
   else if(ram_wr_en)
        begin
        dat_cnt<=dat_cnt+1'b1;
        end
   else if(ram_rd_en)
        begin
        dat_cnt<=dat_cnt-1'b1;
        end
 
//*************************满标志位控制*************************//
 
//组合逻辑实现,该方式也是正确的,并且数据写满的下一个周期就不能写入数据了
// always @*
//        if(dat_cnt==DEPTH)
//        begin
//        wfull<='d1;
//        end
//   else 
//        begin
//        wfull<='d0;
//        end
 
//时序逻辑实现,该方式下,要在数据写满的第二个周期才能停止写入数据,当连续写入的时候,有可能出现错误,也就是多覆盖了一个数据,并且写指针也多加了一次
always@(posedge clk or negedge rst_n)
   if(!rst_n)
      begin
      wfull<='d0;
      end
   else if(dat_cnt==DEPTH)
        begin
        wfull<='d1;
        end
   else 
        begin
        wfull<='d0;
        end
 
//*************************读标志位控制*************************//
 
//组合逻辑实现,该方式也是正确的,并且数据读完的下一个周期就不能读出数据了
// always @*
//        if(dat_cnt=='d0)
//        begin
//        rempty<='d1;
//        end
//   else 
//        begin
//        rempty<='d0;
//        end
 
//时序逻辑实现,该方式下,要在数据读完的第二个周期才能停止读出数据,当连续读出的时候,有可能出现错误,也就是多读了一个数据,并且读指针也多加了一次
always@(posedge clk or negedge rst_n)
   if(!rst_n)
      begin
      rempty<='d0;
      end
   else if(dat_cnt=='d0)
        begin
        rempty<='d1;
        end
   else 
        begin
        rempty<='d0;
        end
 
dual_port_RAM #(.DEPTH(DEPTH),
                .WIDTH(WIDTH))
    inst_dual_port_RAM(
     .wclk(clk)
    ,.wenc(ram_wr_en)
    ,.waddr(waddr)  
    ,.wdata(wdata)       
    ,.rclk(clk)
    ,.renc(ram_rd_en)
    ,.raddr(raddr)  
    ,.rdata(rdata)        
);
     
endmodule

Verilog 解法, 执行用时: 0ms, 内存消耗: 0KB, 提交时间: 2022-08-06

`timescale 1ns/1ns

/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  
	,input [WIDTH-1:0] wdata      	
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  
	,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  

/**********************************SFIFO************************************/
module sfifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					clk		, 
	input 					rst_n	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output reg				wfull	,
	output reg				rempty	,
	output wire [WIDTH-1:0]	rdata
);
    reg [$clog2(DEPTH) : 0] data_cnt;
    reg [$clog2(DEPTH) - 1 : 0] w_addr, r_addr;
    wire wenc, renc;
    assign wenc = winc == 1'b1 && wfull == 1'b0 ? 1'b1 : 1'b0;
    assign renc = rinc == 1'b1 && rempty == 1'b0 ? 1'b1 : 1'b0;
    
    dual_port_RAM #(.DEPTH(DEPTH), .WIDTH(WIDTH)) u_dual_port_RAM(
        .wclk(clk),
        .wenc(wenc),
        .waddr(w_addr),  
        .wdata(wdata),     	
        .rclk(clk),
        .renc(renc),
        .raddr(r_addr),  
        .rdata(rdata)
    );
    
    always@(posedge clk or negedge rst_n) begin
        if(rst_n == 1'b0)
            data_cnt <= 0;
        else if(winc == 1'b1 && wfull == 1'b0)
            data_cnt <= data_cnt + 1;
        else if(rinc == 1'b1 && rempty == 1'b0)
            data_cnt <= data_cnt - 1;
        else
            data_cnt <= data_cnt;
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(rst_n == 1'b0)
            w_addr <= 0;
        else if(winc == 1'b1 && wfull == 1'b0)
            w_addr <= w_addr + 1;
        else
            w_addr <= w_addr;
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(rst_n == 1'b0)
            r_addr <= 0;
        else if(rinc == 1'b1 && rempty == 1'b0)
            r_addr <= r_addr + 1;
        else
            r_addr <= r_addr;
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(rst_n == 1'b0)
            wfull <= 0;
        else if(data_cnt == DEPTH)
            wfull <= 1;
        else
            wfull <= 0;
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(rst_n == 1'b0)
            rempty <= 0;
        else if(data_cnt == 0)
            rempty <= 1;
        else
            rempty <= 0;
    end
        
endmodule

Verilog 解法, 执行用时: 0ms, 内存消耗: 0KB, 提交时间: 2022-08-06

`timescale 1ns/1ns

/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  
	,input [WIDTH-1:0] wdata      	
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  
	,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  

/**********************************SFIFO************************************/
module sfifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					clk		, 
	input 					rst_n	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output reg				wfull	,
	output reg				rempty	,
	output wire [WIDTH-1:0]	rdata
);
    
    parameter ADDR_WIDTH = $clog2(DEPTH);
    reg    [ADDR_WIDTH-1:0]        r_addr;
    reg    [ADDR_WIDTH-1:0]        w_addr;
    
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            r_addr <= {(ADDR_WIDTH){1'b0}};
        else begin
            if(rinc & ~rempty)
                r_addr <= r_addr + 1'b1;
        end
    end
    
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            w_addr <= {(ADDR_WIDTH){1'b0}};
        else begin
            if(winc & ~wfull)
                w_addr <= w_addr + 1'b1;
        end
    end
    
    reg    [ADDR_WIDTH:0]        addr_gap;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            addr_gap <= {ADDR_WIDTH+1{1'b0}};
        else begin
            if(rinc & ~rempty & ~winc || rinc & wfull)
                addr_gap <= addr_gap - 1'b1;
            else if(winc & ~wfull & ~rinc || winc & rempty)
                addr_gap <= addr_gap + 1'b1;
        end
    end
    
    wire wfull_inner;
    assign wfull_inner = addr_gap == DEPTH;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            wfull <= 1'b0;
        else begin
            wfull <= wfull_inner;
        end
    end
    
    wire rempty_inner;
    assign rempty_inner = (addr_gap == {ADDR_WIDTH+1{1'b0}});
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            rempty <= 1'b0;
        else begin
            rempty <= rempty_inner;
        end
    end
    
    dual_port_RAM #(.DEPTH(DEPTH),
                    .WIDTH(WIDTH)) dual_port_RAM_u(
        .wclk(clk)
        ,.wenc(winc & ~wfull)
        ,.waddr(w_addr[ADDR_WIDTH-1:0])  
        ,.wdata(wdata)      	
        ,.rclk(clk)
        ,.renc(rinc & ~rempty)
        ,.raddr(r_addr[ADDR_WIDTH-1:0])  
        ,.rdata(rdata) 		
);
endmodule

上一题