【《硬件架构的艺术》读书笔记】03 处理多个时钟(2)
3.6 握手信号方法
1)X将数放在数据总线上兵发出xreq信号,表示有效数据已经发到接收器Y的数据总线上。
2)xreq信号同步到接收器时钟域ylk上。
3)Y在识别xreq同步的信号yreq2后,锁存数据总线上信号。
4)Y发出确认信号yack,表示其已经接收了数据。
5)yack同步到发送时钟xclk上。
6)X识别到同步的xack2信号后,将下一个数据放到数据总线上。
如图,安全地将一个数据从发送器传输到接收器需要5个时钟周期。
3.6.1 握手信号的要求
数据应在发送时钟域内稳定至少两个时钟上升沿。
xreq宽度应该超过两个上升沿时钟,否则从高速时钟域到低速时钟域传递可能无法捕捉到该信号。
3.6.2 握手信号缺点
传输单个数据延迟比FIFO传输同样的数据大得多。
3.7 使用同步FIFO传输数据
3.7.1 同步FIFO架构
写指针指向下一个要写的地址,读指针指向下一个要读的地址。写使能使写指针递增,读使能使读指针递增。
根据读写指针可以产生空信号和满信号,也可以对FIFO内数据进行计数。
DPRAM可以同步读取或者异步读取。同步读时,应在FIFO输出有效前给都信号。异步读时,输出不会寄存。数据只要一写入就可用。
3.7.2 同步FIFO的工作方式
3.7.2.1 FIFO空满的产生
图中为FIFO满的情况,当读指针等于写指针加一并进行写操作,FIFO满。
同样,当读操作使两个指针在下个周期相等时,FIFO变空。
3.7.2.2 另一种方法
另一种方法使使用计数器来指示FIFO中空或满位置的个数。写入数据时计数器加一,读取数据时计数器减一。
这种方法原理上简单,但是要增加额外的硬件(比较器)。FIFO深度增加,比较器宽度也会增加,这最终会降低FIFO操作的最高频率。
`timescale 1ns / 1ps module FIFO#( parameter WIDTH=16, parameter DEPTH=8 ) ( input clk, input reset_n, input [WIDTH-1:0]wr_data, input wr_en, input rd_en, output reg[WIDTH-1:0]rd_data, output reg fifo_full, output reg fifo_empty ); localparam RAM_SIZE=(1<<DEPTH); reg [DEPTH-1:0]rd_ptr,wr_ptr; reg [WIDTH-1:0]DPRAM[RAM_SIZE-1:0]; always @(posedge clk or negedge reset_n) begin if(~reset_n) rd_data <= 'd0; else begin if(wr_en && ~fifo_full) DPRAM[wr_ptr] <= wr_data; if(rd_en && ~fifo_empty) rd_data <= DPRAM[rd_ptr]; end end always @(posedge clk or negedge reset_n) begin:fifo_full_gen if(~reset_n) fifo_full <= 1'b0; else if(wr_en && rd_en) ;//do nothing else if(rd_en) fifo_full <= 1'b0; else if(wr_en && (rd_ptr == wr_ptr +1'b1)) fifo_full <= 1'b1; end always @(posedge clk or negedge reset_n) begin:fifo_empty_gen if(~reset_n) fifo_empty <= 1'b1; else if(wr_en && rd_en) ;//do nothing else if(wr_en) fifo_empty <= 1'b0; else if(rd_en && (wr_ptr == rd_ptr +1'b1)) fifo_empty <= 1'b1; end always @(posedge clk or negedge reset_n) begin:rd_ptr_gen if(~reset_n) rd_ptr <= 'd0; else if(rd_en && ~fifo_empty) rd_ptr <= rd_ptr +1'b1; end always @(posedge clk or negedge reset_n) begin:wr_ptr_gen if(~reset_n) wr_ptr <= 'd0; else if(wr_en && ~fifo_full) wr_ptr <= wr_ptr +1'b1; end endmodule
testbench
`timescale 1ns / 1ps module tb_fifo(); reg clk,rst_n,wr_en,rd_en; wire full,empty; reg [15:0]wr_data; wire [15:0]rd_data; initial begin clk = 0; rst_n = 0; wr_en = 0; rd_en = 0; wr_data = 0; #100 rst_n = 1; while(~full) begin #20 wr_en <= 1'b1; wr_data <= wr_data + 1'b1; end repeat(100) begin @(posedge clk)begin wr_en = {$random} %2; rd_en = {$random} %2; end end $finish; end always #10 clk = ~clk; FIFO my_fifo( .clk(clk), .reset_n(rst_n), .wr_data(wr_data), .wr_en(wr_en), .rd_en(rd_en), .rd_data(rd_data), .fifo_full(full), .fifo_empty(empty) ); endmodule
仿真结果: