Асинхронная система FIFOVerilog - PullRequest
0 голосов
/ 10 марта 2020

Я написал асинхронный FIFO в SystemVerilog для пересечения часового домена, и чтение / очистка работает. Когда я пытаюсь выполнить запись, пока она не будет заполнена, флаг заполнения будет иметь высокий уровень, но запись приведет к потере данных * на один раз больше, чем предполагается (перезаписывает одно место, которое не было прочитано).

Это странно, потому что, когда я смотрю на результаты моделирования, состояние, в котором находится лог записи c, заполнено, а запись низкая, но все равно пишет в оперативную память. Вот код и простой тестовый стенд.

`timescale 1ns / 1ps

module fifo_async
    #(parameter DATA_WIDTH = 8,
      parameter ADDR_WIDTH = 4,
      parameter MEM_SIZE   = 2**ADDR_WIDTH)
     (input logic clka,
      input logic clkb,
      input logic reset,
      input logic rd_en,
      input logic wr_en,
      input logic [DATA_WIDTH-1:0] wrdata,
      output logic [DATA_WIDTH-1:0] rdata,
      output logic empty,
      output logic full
    );

    // ram inputs //
    logic write;
    logic read;
    logic rd;
    logic [ADDR_WIDTH-1:0] waddr;
    logic [ADDR_WIDTH-1:0] raddr;
    logic [DATA_WIDTH-1:0] wdata;

    // top pointer in clock domain A and B //
    logic [ADDR_WIDTH-1:0] top_ptrA;
    logic [ADDR_WIDTH-1:0] top_grayA;
    logic [ADDR_WIDTH-1:0] top_grayA_tmp;
    logic [ADDR_WIDTH-1:0] top_grayB;

    // bottom pointer in clock domain B and A //
    logic [ADDR_WIDTH-1:0] bot_ptrB;
    logic [ADDR_WIDTH-1:0] bot_grayB;
    logic [ADDR_WIDTH-1:0] bot_grayB_tmp;
    logic [ADDR_WIDTH-1:0] bot_grayA;

    // used for full flag logic 
    logic [ADDR_WIDTH-1:0] top_ptr_minus_one;
    logic [ADDR_WIDTH-1:0] top_ptr_minus_one_grayA ;

    // dual port ram //
    dual_port_ram_async #(.DATA_WIDTH(DATA_WIDTH),.ADDR_WIDTH(ADDR_WIDTH))
    async_ram (.clk(clka),.write, .read(rd), .raddr, .waddr, .wdata,.rdata);

    // converts binary to gray code //
    function automatic [ADDR_WIDTH-1:0] gray(input [ADDR_WIDTH-1:0] ptr);
       gray = (ptr >> 1) ^ ptr;
    endfunction

    //====================================================================//
    //===================== CLOCK A DOMAIN (WRITE) =======================//
    //====================================================================//
    typedef enum logic [1:0] {RESETA=2'b00,IDLEA = 2'b01, WRITEA=2'b11,FULL = 2'b10} states_a;

    states_a state_a, next_a;

    // Current State Logic -- sequential logic //
    always_ff @(posedge clka) begin
        if (reset) begin
            state_a <= RESETA;
            end
        else
            state_a <= next_a;
    end

    // next state logic //
    always_comb begin
        unique case(state_a)
        RESETA:
            next_a = IDLEA;
        IDLEA:
            if (wr_en) next_a = WRITEA;
            else next_a = IDLEA;
        WRITEA:
            if (top_grayA == bot_grayA) next_a = FULL;
            else if (!write) next_a = IDLEA;
            else next_a = WRITEA;
        FULL:
            if (top_ptr_minus_one_grayA != bot_grayA) next_a = IDLEA;  
            else next_a = FULL;  
        endcase
    end

     // moore outputs logic //
    always_comb begin
        unique case(state_a)
        RESETA:
        begin
            write = 0;
            full = 0;
            wdata = '0;
            end
        IDLEA:
        begin
            write = 1'b0;
            full  = 1'b0;
        end
        WRITEA:
        begin
            write = wr_en;
            wdata = wrdata;
        end
        FULL:
        begin
            write = 1'b0;   
            full  = 1'b1;
        end
        endcase
    end

    // datapath //
    always @(*) begin
        if (reset) begin
            top_ptr_minus_one = 0;
            top_ptrA = 0;
            waddr = 0;
        end
        else if (write && !full) begin
            top_ptr_minus_one = top_ptrA;
            top_ptrA = top_ptrA + 1;
            waddr = top_ptrA;
        end      
    end

    // convert top_ptrA to graycode //
    assign top_grayA = gray(top_ptrA);
    assign top_ptr_minus_one_grayA = gray(top_ptr_minus_one);

    // Double flop top_grayA to clock domain B //
    always_ff @(posedge clkb) begin
        top_grayA_tmp <= top_grayA;
        top_grayB <= top_grayA_tmp;
    end


    //====================================================================//
    //======================== CLOCK B DOMAIN (READ) ======================//
    //====================================================================//
    typedef enum logic [1:0] {RESETB=2'b00,IDLEB = 2'b01, READB=2'b11,EMPTY = 2'b10} states_b;

    states_b state_b, next_b;

    // current state logic //
    always_ff @(posedge clkb) begin
        if (reset)
            state_b <= RESETB;
        else
            state_b <= next_b;
    end


    //next state logic //
    always_comb begin
        unique case(state_b)
        RESETB:
            next_b = EMPTY;
        EMPTY:
            if (top_grayB != bot_grayB) next_b = IDLEB;
        READB:
            if (top_grayB == bot_grayB) next_b = EMPTY;
            else next_b = IDLEB;
        IDLEB:
            if (rd_en) next_b = READB;
        endcase;   
    end

    // moore outpur logic //
    always_comb begin
        unique case(state_b)
        RESETB:
            begin
            read  = 1'b0;
            empty = 1'b0;
            end
        EMPTY:
        begin
            read  = 1'b0;
            empty = 1'b1;
        end
        READB:
        begin
            read  = 1'b1;
            empty = 1'b0;
        end
        IDLEB:
        begin
            read  = 1'b0;
            empty = 1'b0;
        end
        endcase    
    end

    // Datapath //
    always_comb begin
        if (reset) begin
            bot_ptrB = 0;
            raddr = 0;
        end
        else if (read && !empty) begin
        bot_ptrB = bot_ptrB + 1;
        raddr = bot_ptrB;
        end
    end

    // convert bot_ptrB to gray code //
    assign bot_grayB = gray(bot_ptrB);

    // double flop to cross bot_grayB to clock domain A
    always_ff @(posedge clka) begin
        bot_grayB_tmp <= bot_grayB;
        bot_grayA <= bot_grayB_tmp;
        rd <= read;
    end    

endmodule

двухпортовый модуль оперативной памяти:

`timescale 1ns / 1ps

module dual_port_ram_async

    #(parameter DATA_WIDTH = 8,
      parameter ADDR_WIDTH = 3,
      parameter MEM_SIZE   = 2**ADDR_WIDTH
    )(
    input logic clk,
    input logic write,
    input logic read,
    input logic  [ADDR_WIDTH-1:0] raddr,
    input logic  [ADDR_WIDTH-1:0] waddr,
    input logic  [DATA_WIDTH-1:0] wdata,
    output logic [DATA_WIDTH-1:0] rdata
    );

    bit [DATA_WIDTH-1:0] mem [0:MEM_SIZE-1];

    always @(write) begin
        if (write) begin mem[waddr] = wdata; end
        else begin mem[waddr] = mem[waddr]; end
    end

    assign rdata = (read)?mem[raddr]:'z;

endmodule

Простой тестовый стенд:

`timescale 1ns / 1ps
module fifo_async_tb;

    localparam DATA_WIDTH = 8;
    localparam ADDR_WIDTH = 8;   

    localparam Ta = 10;
    localparam Tb = 20;

    //inputs
    logic clka = 0;
    logic clkb = 0;
    logic reset;

    logic rd_en;
    logic wr_en;
    logic [DATA_WIDTH-1:0] wrdata;

    // outputs
    logic [DATA_WIDTH-1:0] rdata;
    logic empty;
    logic full;

    fifo_async fifo(.*);

    always #(Ta/2) clka = ~clka;
    always #(Tb/2) clkb = ~clkb;

    initial begin

    rd_en = 0;
    wr_en = 0;
    wrdata = '0;
    reset = 1;
    #40
    reset = 0;
    #15

    #10
    rd_en = 1;
    #50;   
    rd_en = 0;
        // start test
    repeat(18) begin
    wr_en = 1;
    wrdata++;
    #(Ta/2);
    wr_en = 0;
    #(Ta/2);
    end 

    $stop;
    end

endmodule

Ниже приведены результаты. Как вы можете видеть на желтом маркере, write является низким как для оперативной памяти, так и для асин c fifo (wr_en является высоким от ТБ), но mem (сигнал в самом низу) все еще записывается в

результаты тестового стенда

1 Ответ

0 голосов
/ 11 марта 2020

Нет никакой гарантии на порядок этих процессов:

    always @(*) begin
        if (reset) begin
            top_ptr_minus_one = 0;
            top_ptrA = 0;
            waddr = 0;
        end
        else if (write && !full) begin
            top_ptr_minus_one = top_ptrA;
            top_ptrA = top_ptrA + 1;
            waddr = top_ptrA;
        end      
    end

    assign top_grayA = gray(top_ptrA);

assign может произойти до или после always, поэтому top_grayA может закончиться со значением до или после после приращения.

Помимо этого, вы использовали always @* вместо always_comb, поэтому, возможно, вы намеревались создать защелки. Но это обычно не рекомендуется для ПЛИС.

...