Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] Async FIFO simulation problem

Status
Not open for further replies.

stark43

Member level 1
Member level 1
Joined
Oct 24, 2021
Messages
35
Helped
0
Reputation
0
Reaction score
0
Trophy points
6
Activity points
387
Hello, I'm interested in asynchronous FIFO, I understood the logic and used ready-made code. But I'm losing the first 3 data, I think it's because can_write and can_read are not stable, but I can't understand why. I looked many times and couldn't understand the problem, I wanted to consult you
1655800832878.png


SVG:
//Cross domain
module crossdomain #(parameter SIZE = 1) (
  input reset,
  input clk,
  input [SIZE-1:0] data_in,
  output reg [SIZE-1:0] data_out
);

    reg [SIZE-1:0] data_tmp;

    always @(posedge clk) begin
        if (reset) begin
            {data_out, data_tmp} <= 0;
        end else begin
            {data_out, data_tmp} <= {data_tmp, data_in};
        end
    end

endmodule

// Async FIFO implementation
module asyncfifo(
  input                       reset,
  input                       write_clk,
  input                       write,
  input      [DATA_WIDTH-1:0] write_data,
  output reg                  can_write ,
  input                       read_clk,
  input                       read,
  output reg [DATA_WIDTH-1:0] read_data,
  output reg                  can_read
);
    parameter DATA_WIDTH = 8;
    parameter BUFFER_ADDR_WIDTH = 10;
    parameter BUFFER_SIZE = 2 ** BUFFER_ADDR_WIDTH;


    reg [DATA_WIDTH-1:0] buffer[BUFFER_SIZE-1:0];

    ///// WRITE CLOCK DOMAIN /////

    reg  [BUFFER_ADDR_WIDTH:0] write_ptr;        //wrap-around bit eklenmis hali
    wire [BUFFER_ADDR_WIDTH-1:0] write_addr = write_ptr[BUFFER_ADDR_WIDTH-1:0];


    reg [BUFFER_ADDR_WIDTH:0] write_ptr_grey_w;


    wire [BUFFER_ADDR_WIDTH:0] read_ptr_grey_w;


    wire [BUFFER_ADDR_WIDTH:0] next_write_ptr = write_ptr + 1;
    wire [BUFFER_ADDR_WIDTH:0] next_write_ptr_grey_w = (next_write_ptr >> 1) ^ next_write_ptr;
/*Okuma ve yazma adresleri aynı ancak MSB'ler (sarma bitleri) farklıysa arabelleğimiz doludur. Burada gri kod sürümlerini karşılaştırıyoruz, böylece okuma işaretçisinin etki alanları arası senkronize kopyasını kullanabiliriz.*/
    wire current_can_write = write_ptr_grey_w != { ~read_ptr_grey_w[BUFFER_ADDR_WIDTH:BUFFER_ADDR_WIDTH-1], read_ptr_grey_w[BUFFER_ADDR_WIDTH-2:0] };
    wire next_can_write = next_write_ptr_grey_w != { ~read_ptr_grey_w[BUFFER_ADDR_WIDTH:BUFFER_ADDR_WIDTH-1], read_ptr_grey_w[BUFFER_ADDR_WIDTH-2:0] };
    always @(posedge write_clk or posedge reset) begin
        if (reset) begin
            write_ptr <= 0;
            write_ptr_grey_w <= 0;
            can_write <= 1;
        end else begin
            if (write && can_write) begin                       //FIFO bos degilse ve yazma izni varsa siradaki pointer degerlerini güncellemesi icin yazilmişs if blogu
                write_ptr <= next_write_ptr;
                write_ptr_grey_w <= next_write_ptr_grey_w;      //Arttirilmiş write pointer'in gray hali, amac degisen write pointer'i gray'e cevirmek
                can_write <= next_can_write;                    //can_write --> next_can_write olmasi demek eger yazilabilir sartlar tutuyorsa siradaki pointer icin yazilabilir olup olmadigini günceller (gray haline bakarak)
            end else begin
                can_write <= current_can_write;                 //write ve can_write sartlari tutmuyorsa degeri güncellemiyor
            end
        end
    end


    always @(posedge write_clk) begin
        if (write && can_write) begin
            buffer[write_addr] <= write_data;
        end
    end

    ///// READ CLOCK DOMAIN /////

    reg [BUFFER_ADDR_WIDTH:0] read_ptr = 0;
    wire [BUFFER_ADDR_WIDTH-1:0] read_addr = read_ptr[BUFFER_ADDR_WIDTH-1:0];

    reg [BUFFER_ADDR_WIDTH:0] read_ptr_grey_r;


    wire [BUFFER_ADDR_WIDTH:0] write_ptr_grey_r;


    wire [BUFFER_ADDR_WIDTH-1:0] next_read_ptr = read_ptr + 1; /*orjinali böyleydi wire [BUFFER_ADDR_WIDTH:0] next_read_ptr = read_ptr + 1;*/
    wire [BUFFER_ADDR_WIDTH:0] next_read_ptr_grey_r = (next_read_ptr >> 1) ^ next_read_ptr;

    wire current_can_read = read_ptr_grey_r != write_ptr_grey_r;
    wire next_can_read = next_read_ptr_grey_r != write_ptr_grey_r;
    always @(posedge read_clk or posedge reset) begin
        if (reset) begin
            read_ptr <= 0;
            read_ptr_grey_r <= 0;
            read_data <= 0;
            can_read <= 0;
        end else begin
            if (read) begin
                if (can_read) begin
                    read_ptr <= next_read_ptr;
                    read_ptr_grey_r <= next_read_ptr_grey_r;
                end
                can_read <= next_can_read;
                if (next_can_read) begin
                    read_data <= buffer[next_read_ptr];
                end else begin              // (next_can_read == 0)
                    read_data <= 0;
                end
            end else begin
                can_read = current_can_read;
                if (current_can_read) begin
                    read_data <= buffer[read_addr];
                end else begin
                    read_data <= 0;
                    
                end
            end
        end
    end

    ///// CROSS-DOMAIN /////

    // Synchronize read_ptr_grey_r into read_ptr_grey_w.
    crossdomain #(.SIZE(BUFFER_ADDR_WIDTH+1)) read_ptr_grey_sync (
        .reset(reset),
        .clk(write_clk),
        .data_in(read_ptr_grey_r),
        .data_out(read_ptr_grey_w)
    );

    // Synchronize write_ptr_grey_w into write_ptr_grey_r.
    crossdomain #(.SIZE(BUFFER_ADDR_WIDTH+1)) write_ptr_grey_sync (
        .reset(reset),
        .clk(read_clk),
        .data_in(write_ptr_grey_w),
        .data_out(write_ptr_grey_r)
    );

endmodule

TEST BENCH
SVG:
`timescale 1ns / 1ps

module tb_async_FIFO();
reg tb_reset = 0;
reg tb_read_clk = 0;
reg tb_write_clk = 0;
reg [7:0] tb_write_data = 0;
reg tb_write = 1;
reg tb_read  = 1;
reg [9:0] a = 0;


initial begin
tb_reset = 1;
#2
tb_reset = 0;
end
//pixel clock
always begin
#21 tb_write_clk = ~tb_write_clk;
end

//read clock
always begin
#24 tb_read_clk = ~tb_read_clk;
end

//Data send
always @(posedge tb_write_clk) begin

    tb_write_data = tb_write_data + 1;
end

always @(posedge tb_read_clk) begin

    a = a +1;
    if (a == 647) begin
    tb_read = 0;
    end
    
    if (a == 920) begin
    tb_read = 1;
    end
end

asyncfifo DUT(
.reset(tb_reset),
.write_clk(tb_write_clk),
.write_data(tb_write_data),
.write(tb_write),
.read(tb_read),
.read_clk(tb_read_clk)
);


endmodule
 

Hi,

generally I don´t think it´s a good idea to rely on any device after 100ns from power up.

Consider to use internal or external RESET functionality.
While I can see some "reset" in your code, I can´t see it in the simulation.

Klaus
 

Other than the reset thing, I do not understand the relationship between clk, read_clk and write_clk.
The code is written is such a way that reset signal is in sync with the clk clock domain. But it is async to the read_clk and write_clk domains.
You have not shown us how the reset looks in simulation.
All FIFO read and write side registers must be reset (bring the registers to a known state) before you can push data in to it or read out from it.
 

Thanks for the replies. I added 100 ns reset as you said. I detected the problem. In the first read_clk pulse after the reset signal, FIFO tries to read data 0, but cannot read data 0 because it is not written. However, in the first write_clk pulse after the reset, it had to write the data to the 0 address of the buffer. Because the write condition is met.
1655814758710.png

1655814953148.png

1655814976403.png
 

You should wait for reset to deassert before attempting to write. Only write when can_write is high. You also shouldn't attempt to read until can_read of the FIFO is high.

In both these cases you are writing to a FIFO in reset and not ready and reading from a FIFO that has no data written to it or is not ready.
 

You should wait for reset to deassert before attempting to write.
What do you mean by waiting? Do I need to activate write and read after a few cycles after reset? There are two conditions for FIFO to be able to write to the buffer (write and can_write). Even if I reset the parameters with reset it seems to be configured in the next cycle. Also, at the 2nd place I specified in the picture, can_read becomes 0 and I lose 1 data again, but can_read should stay at 1 unless the FIFO is empty. I'm stuck in these two places and I don't really understand, can you tell me your solution suggestions if possible?
1655971524919.png
 

What do you mean by waiting? Do I need to activate write and read after a few cycles after reset?
No, you just have to care about "can_write" and "can_read" signals.

We see that "read_clk" is active while "can_read" is LOW. This is not how it is meant to work.

What´s the meaning of "can_read"? When HIGH it tells that data is available to be read. So if no data is available it is LOW and then: don´t try to read data, don´t activate the read_clk.


Similar with "write"...

Klaus
 

No, you just have to care about "can_write" and "can_read" signals.

We see that "read_clk" is active while "can_read" is LOW. This is not how it is meant to work.

What´s the meaning of "can_read"? When HIGH it tells that data is available to be read. So if no data is available it is LOW and then: don´t try to read data, don´t activate the read_clk.


Similar with "write"...

Klaus
Hi Klaus, thank you for your interest. I've looked at many simulations for Async FIFO, this is the first time I've heard of the clock being stopped and I'll try it. But even if the clock is active, isn't the code address-controlled? If the can_read or can_write signal goes LOW it seems to control this by not increasing the write or read addresses. Am I thinking wrong sir?
--- Updated ---

If you want to look;
 

Attachments

  • async_FIFO.zip
    465.5 KB · Views: 205

Hi,

neither "read_clk" nor "write_clk" should be your system clock of your FPGA.

These signals are used to "write" one byte into the FIFO. It may be at a constant data rate or a very non constant. (One data written after 100ns, then next data written the next week) = asynchrounous. Asynchronous to everything: like asynchronous to the previous edge, to the system clock, to the read clock....

And for sure you should only read data from the FIFO after you have data written into the FIFO.

Klaus
 

Hi,

neither "read_clk" nor "write_clk" should be your system clock of your FPGA.

These signals are used to "write" one byte into the FIFO. It may be at a constant data rate or a very non constant. (One data written after 100ns, then next data written the next week) = asynchrounous. Asynchronous to everything: like asynchronous to the previous edge, to the system clock, to the read clock....

And for sure you should only read data from the FIFO after you have data written into the FIFO.

Klaus
You are right, my system clock is different from these clocks. Actually, my goal is to write the data from the camera after the frame valid signal comes and read it with a different clock. Then I will reset the FIFO when the frame valid signal comes. If can_read is active, I will enable read_clock, otherwise I will set it to zero. Similar with "write"...
Did I understand correctly, sir?
 

Then I will reset the FIFO when the frame valid signal comes.
No, you reset the FIFO (registers) only initially, at the beginning or if for any other reason you need to do system reset.
Then as data comes in, you write in to it and as soon as valid data is available, you read out the FIFO.
You should have controllers both on the write side and read side that will control the writing to and reading out of the FIFO.
 
Hi,

now it´s the first time you mention "camera" and "frame".
So basically you want to use a FIFO to buffer picture data.
Thus there should be a camera interface with a descripton of signals, their function and their timing.
I guess you rather want to adjust the FPGA to the camera than vice versa.

But the FIFO also has an output. Where does it go to? What is it´s requirement about signals and timing?

And I agree with dpaul. Usually you want to process picture data somehow. but with resetting the FIFO you rather "delete" some data ... and can´t be processed.

***
The FIFO may be a suitable solution...
...but with picture data coming from a camara using DMA or dual port memories may be an improvement.
Because that you can frame sync the data to memory location address.
just as an example: then you know that the top right corner picture data is always located at address 1023.

Often with picture data one uses two RAM buffers: let´s name them A and B.
* So first frame data from the camera is written to buffer A.
* Then with the frame sync signal the buffers are switched:
* the second frame becomes written into buffer B .. while the data in buffer A can be processed somehow.
* Then with the frame sync signal the buffers are switched:
* the third frame becomes written into buffer A .. while the data in buffer B can be processed somehow.
* and so on...

in either case_ processing the data needs to be faster than the frame period time

Klaus
 
Hi sir,
Actually, I don't need to process the data, I'm only interested in the transmit side. My interface is MIPI CSI-2, maybe you have heard of it before or you are interested.
As you said, I need to save the incoming data in asynchronous FIFO and then pass it to the serializer module by adding some packets and waiting times at the beginning and end of the payload. I don't want to lose data, so I need to deal with the unknown value problem in the simulation output, as we talked about earlier. For this you told me to disable read_clk unless you are reading. Actually I thought I could configure write, read permission instead of disabling the clock. I'll try that and give you feedback, I hope it gets resolved. Thank you for your recommendation. As a newbie I am very happy to receive support.

1656051997461.png

--- Updated ---

No, you reset the FIFO (registers) only initially, at the beginning or if for any other reason you need to do system reset.
Then as data comes in, you write in to it and as soon as valid data is available, you read out the FIFO.
You should have controllers both on the write side and read side that will control the writing to and reading out of the FIFO.
Hi Dpaul,
Actually, I have external read and write permission signals and can_write and can_read signals determined by the FIFO being empty and full. Although I am satisfied with the general operation of FIFO, I have problems at 2 points.
1) I get X value in simulation output early in timing, we discussed this above.
2) FIFO pulls can_read low after about 620 ns as I have indicated in the image. It's not supposed to do this since the FIFO is not empty.
The article where the code is taken and the logic is explained: https://log.martinatkins.me/2020/06/07/verilog-async-fifo/
If you want to examine the output, I shared the file above.
1656053836783.png
 
Last edited:

Ok, so as I understand it, can_write is akin to write_data_valid and can_read is like read_data_valid.
Then the read and write signals are akin to read_enable and write_enable.

Generally this is how you should operate a FIFO:
0. The the read_clock and write_clock be generated by the design as it should be.
1. After de-asserting the reset (done by the system at the very start), depending on the FIFO write controller logic, you can start pushing in data to the FIFO as soon as the camera sends in data.
2. You do not read immediately, but wait for a few write clock cycles, so that you have about 4 or 5 words written to the FIFO (many FIFOs have a write_data_count signal for this).
3. Only after that start reading the FIFO and continue reading, if and only if there is atleast 1 valid word written to it (but this again depends on the read controller design, one might stop reading earlier).
4. The write side controller should stop writing to the FIFO if the FIFO is full or almost full.

Are you doing the above steps?

btw - Why do you just not use a FIFO IP/Core that is supplied by most FPGA vendors? I am not discouraging you to learn about FIFO internals, but it is much easier to use.
 
Last edited:
Ok, so as I understand it, can_write is akin to write_data_valid and can_read is like read_data_valid.
Then the read and write signals are akin to read_enable and write_enable.

Generally this is how you should operate a FIFO:
0. The the read_clock and write_clock be generated by the design as it should be.
1. After de-asserting the reset (done by the system at the very start), depending on the FIFO write controller logic, you can start pushing in data to the FIFO as soon as the camera sends in data.
2. You do not read immediately, but wait for a few write clock cycles, so that you have about 4 or 5 words written to the FIFO (many FIFOs have a write_data_count signal for this).
3. Only after that start reading the FIFO and continue reading, if and only if there is atleast 1 valid word written to it (but this again depends on the read controller design, one might stop reading earlier).
4. The write side controller should stop writing to the FIFO if the FIFO is full or almost full.

Are you doing the above steps?

btw - Why do you just not use a FIFO IP/Core that is supplied by most FPGA vendors? I am not discouraging you to learn about FIFO internals, but it is much easier to use.
Thanks, I'll try your advice and give feedback. You are right IP is much easier to use, even CSI-2 has IP. But my goal is not only to do the project, but also to learn, so I will be stubborn about it. Thanks to these support groups, I do not lose my confidence, I am grateful to you for that. I guess what's under the hood makes me even more excited than the car :)
 

Hi,

2) FIFO pulls can_read low after about 620 ns as I have indicated in the image. It's not supposed to do this since the FIFO is not empty.
I guess this is the result of the "illegal read" while the "can_read" signal says "don´t read".

Imagine a post office. When nobody is in the post office the sign says "closed" i.e. "don´t write" ... but you ignore this and throw a parcel over the fence. It get´s not stored correctly. So when someone wants to pick the parcel, nobody knows where it is.

Or the more electronics way:
The FIFO is a memory with address. There are two pointers for addressing. One is the "write_pointer" the other is the "read_pointer".
* Initially both pointers are zero.
* then you fill in the first data --> WP = 1
* since WP = 1 and RP = 0 the logic knows that there is data stored.
* now you can read and RP becomes 1
* since RP = WP the logic knows there is no data stored.
(btw: usually a FIFO with n locations can only store n-1 data)

***
But in your case:
* both pointers are zero
* you ignore the "don´t read"... increment the RP to 1 (by performing the READ from addres 0)
* so WP = 0 and RP = 1 .. this is the condition for FIFO is FULL
* So when you perform the next (illegal or not) READ it wants to read from RP = 1 .. but your simulator knows that at this location there is no valid data (no data has been store into adress 1 before). Thus it shows the "X".
(A real FIFO will output random data ... also known ans "X")

Long story short: So my and ads-ee´s recommendation:
* don´t read when signal "can_read" is LOW
* don´t write when signal "can_write" is LOW
These are the rules to operate a FIFO correctly. Don´t be surprised of malfunction when not keeping on that rules.

Klaus
 
I understand asynchronous FIFO better, I'll go and review what you said in simulation step by step, thank you both, I'm closing the issue as solved, see you on other issues, have a nice day.
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top