赞
踩
具体代码直接按照结构图来写,分为读控制模块,写控制模块,同步模块,及顶层模块。
- module wr_ctrl#(
- parameter DEPTH = 16,
- parameter ADDR_WIDTH = $clog2(DEPTH)
- )(
- input wire wr_clk,
- input wire wr_rst_n,
- input wire [ADDR_WIDTH : 0] rd_addr_gray,
- input wire wr_en,
-
- output wire [ADDR_WIDTH : 0] wr_addr_p_gray,
- output wire [ADDR_WIDTH - 1 : 0] wr_addr,
- output wire wr_full
- );
-
-
- reg [ADDR_WIDTH : 0] wr_addr_p;
- reg [ADDR_WIDTH : 0] rd_addr_p;
-
- always@(posedge wr_clk or negedge wr_rst_n)begin
- if(!wr_rst_n)
- wr_addr_p <= {ADDR_WIDTH+1{1'b0}};
- else if(wr_en && (~wr_full))
- wr_addr_p <= wr_addr_p + 1'b1;
- end
-
- assign wr_addr = wr_addr_p[ADDR_WIDTH - 1 : 0];
-
- assign wr_addr_p_gray = {wr_addr_p[ADDR_WIDTH],wr_addr_p[ADDR_WIDTH:1] ^ wr_addr_p[ADDR_WIDTH-1:0]};
-
- integer i ;
- always@(*)begin
- rd_addr_p[ADDR_WIDTH] = rd_addr_gray[ADDR_WIDTH];
- for(i = ADDR_WIDTH - 1 ; i >= 0 ; i = i - 1)
- rd_addr_p[i] = rd_addr_p[i+1] ^ rd_addr_gray[i];
- end
-
- assign wr_full = (wr_addr_p[ADDR_WIDTH] != rd_addr_p[ADDR_WIDTH]) && (wr_addr_p[ADDR_WIDTH-1:0] == rd_addr_p[ADDR_WIDTH-1:0]);
-
-
- endmodule

写控制模块作用包括:产生写地址指针,写地址,将读时钟域的格雷码转换,产生写满标志位。
这里我格雷码转二进制用了for循环,因为考虑到模块复用的情况,地址宽度是变化的,我没有想到更好的方法。下面读控制同理。(有好的方法欢迎提出)
- module rd_ctrl#(
- parameter DEPTH = 16,
- parameter ADDR_WIDTH = $clog2(DEPTH)
- )(
- input wire rd_clk,
- input wire rd_rst_n,
- input wire rd_en,
- input wire [ADDR_WIDTH:0] wr_addr_gray,
-
- output wire rd_empty,
- output wire [ADDR_WIDTH-1:0] rd_addr,
- output wire [ADDR_WIDTH:0] rd_addr_p_gray
- );
-
-
- reg [ADDR_WIDTH:0] rd_addr_p;
- reg [ADDR_WIDTH:0] wr_addr_p;
-
- always@(posedge rd_clk or negedge rd_rst_n)begin
- if(!rd_rst_n)
- rd_addr_p <= {ADDR_WIDTH+1{1'b0}};
- else if(rd_en && (~rd_empty))
- rd_addr_p <= rd_addr_p + 1'b1;
- end
-
- assign rd_addr = rd_addr_p[ADDR_WIDTH-1:0];
-
- assign rd_addr_p_gray = {rd_addr_p[ADDR_WIDTH],rd_addr_p[ADDR_WIDTH:1] ^ rd_addr_p[ADDR_WIDTH-1:0]};
-
- integer j ;
- always@(*)begin
- wr_addr_p[ADDR_WIDTH] = wr_addr_gray[ADDR_WIDTH];
- for(j = ADDR_WIDTH - 1 ; j >= 0 ; j = j - 1)
- wr_addr_p[j] = wr_addr_p[j+1] ^ wr_addr_gray[j];
- end
-
- assign rd_empty = wr_addr_p == rd_addr_p;
-
- endmodule

读控制模块作用包括:产生读地址指针,读地址,将写时钟域的格雷码转换,产生读空标志位。
- module sync2#(
- parameter DEPTH = 16,
- parameter ADDR_WIDTH = $clog2(DEPTH)
- )(
- input wire clk,
- input wire rst_n,
- input wire [ADDR_WIDTH : 0] addr_in,
-
- output wire [ADDR_WIDTH : 0] addr_out
- );
- reg [ADDR_WIDTH : 0] addr_d1;
- reg [ADDR_WIDTH : 0] addr_d2;
- always@(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- addr_d1 <= 0;
- addr_d2 <= 0;
- end
- else begin
- addr_d1 <= addr_in;
- addr_d2 <= addr_d1;
- end
- end
-
- assign addr_out = addr_d2;
-
- endmodule

同步模块作用:格雷码传输,相邻格雷码间只有1位变化,使用两级同步器。
- module async_fifo
- #(
- parameter DEPTH = 16,
- parameter WIDTH = 8
- )(
- input wire wr_clk,
- input wire wr_req,
- input wire wr_rst_n,
- input wire [WIDTH - 1:0] data_in,
- output wire wr_full,
-
- input wire rd_clk,
- input wire rd_req,
- input wire rd_rst_n,
- output wire rd_empty,
- output reg [WIDTH - 1:0] data_out
- );
- parameter ADDR_WIDTH = $clog2(DEPTH);
-
- reg [WIDTH - 1 : 0] regs_array [DEPTH - 1 : 0];
-
- wire [ADDR_WIDTH : 0] wptr,rptr;
- wire [ADDR_WIDTH : 0] wptr_d2,rptr_d2;
- wire [ADDR_WIDTH - 1 : 0] wr_addr,rd_addr;
-
- always@(posedge wr_clk)begin
- if(wr_req && (!wr_full))
- regs_array[wr_addr] <= data_in;
- end
-
- always@(posedge rd_clk)begin
- if(rd_req && (!rd_empty))
- data_out <= regs_array[rd_addr];
- end
-
- wr_ctrl #(
- .DEPTH (16)
- )wr_ctrl_inst(
- .wr_clk (wr_clk),
- .wr_rst_n (wr_rst_n),
- .wr_en (wr_req),
- .rd_addr_gray (rptr_d2),
-
- .wr_addr (wr_addr),
- .wr_addr_p_gray (wptr),
- .wr_full (wr_full)
- );
-
- rd_ctrl #(
- .DEPTH (16)
- )rd_ctrl_inst(
- .rd_clk (rd_clk),
- .rd_rst_n (rd_rst_n),
- .rd_en (rd_req),
- .wr_addr_gray (wptr_d2),
-
- .rd_addr (rd_addr),
- .rd_addr_p_gray (rptr),
- .rd_empty (rd_empty)
- );
-
- sync2 #(
- .DEPTH (16)
- )sync2_inst1(
- .clk (wr_clk),
- .rst_n (wr_rst_n),
- .addr_in (rptr),
- .addr_out (rptr_d2)
- );
-
- sync2#(
- .DEPTH (16)
- )sync2_inst2(
- .clk (rd_clk),
- .rst_n (rd_rst_n),
- .addr_in (wptr),
- .addr_out (wptr_d2)
- );
-
- endmodule

顶层模块作用包括:例外各个子模块,定义存储器变量(图中双口ram),读写数据控制。
- `timescale 1ns/1ns;
- module async_fifo_tb();
- parameter WIDTH = 8;
-
- reg wr_clk;
- reg wr_req;
- reg wr_rst_n;
- reg [WIDTH - 1:0] data_in;
-
- reg rd_clk;
- reg rd_req;
- reg rd_rst_n;
-
- wire wr_full;
- wire rd_empty;
- wire [WIDTH - 1:0] data_out;
-
- reg init_done;
-
- always #2 wr_clk = ~wr_clk;
- always #4 rd_clk = ~rd_clk;
-
- initial begin
- wr_rst_n = 0;
- rd_rst_n = 0;
- wr_clk = 0;
- rd_clk = 0;
- wr_req = 0;
- rd_req = 0;
- data_in = 0;
- init_done = 0;
- #25;
- wr_rst_n = 1;
- rd_rst_n = 1;
- init_done = 1;
- #10000
- $finish;
- end
-
- always@(*)begin
- if(init_done)begin
- if(wr_full)
- wr_req = 0;
- else
- wr_req = 1;
- end
- end
-
- always@(*)begin
- if(init_done)begin
- if(rd_empty)
- rd_req = 0;
- else
- rd_req = 1;
- end
- end
-
- always@(posedge wr_clk)begin
- if(init_done)begin
- if(!wr_full)
- data_in <= data_in + 1'b1;
- else
- data_in <= data_in;
- end
- else
- data_in <= 0;
- end
- async_fifo #(
- .DEPTH (16),
- .WIDTH (8)
- )async_fifo_inst(
- .wr_clk (wr_clk),
- .wr_req (wr_req),
- .wr_rst_n (wr_rst_n),
- .data_in (data_in),
- .wr_full (wr_full),
- .rd_clk (rd_clk),
- .rd_req (rd_req),
- .rd_rst_n (rd_rst_n),
- .rd_empty (rd_empty),
- .data_out (data_out)
- );
- initial begin
- $fsdbDumpfile("tb.fsdb");
- $fsdbDumpvars;
- end
- endmodule

testbench作用为:给激励,产生读写数据,控制读写请求,例化顶层模块。
仿真了边读边写的情况,可以看到写满拉高,读写时钟域分开,方便观察中间信号。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。