【《硬件架构的艺术》读书笔记】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

仿真结果:

 

posted @ 2022-11-23 17:50  Magnolia666  阅读(96)  评论(0编辑  收藏  举报