FIFO : Synchronous WRITE, Asynchronous READ ?

Hi

I have problem to implement a FIFO with "Synchronous WRITE, Asynchronous READ" in Xilinx device.

Since the FIFO size is large (more than 48-deep words), I would like to use BRAM or Built-in FIFO.

I tried "Synchronous WRITE, Synchronous READ" using dual-ported BRAM and it seems okay.

Problem is that

Every time we 'read', one cycle delay occurs. I want to 'immediately read' a data in the location that the "read address" points to.

I am not finding a way to implement "Asynchronous READ" in BRAM.

If anyone has this experience, please let us know. Thank you in advance

Reply to
Pasacco
Loading thread data ...

It's a FIFO, not a random read so your life is simpler. The question is whether you need first-word fall-through or not. Peter Alfke is, of course, the expert on the ins and outs of FIFOs but I'll make a quick suggestion to address your troubles.

For simpler methods to reach your goals, please specify the family of device you're using.

It's also not obvious that the clock is the same on both sides of the FIFO. For the moment I'm assuming it is. You MUST indicate if it's different domains.

One approach is to build your own FIFO controller. Synchronous FIFOs are pretty simple: you have a read pointer, a write pointer, some full and empty logic per your tastes, and controls to keep the FIFO from an overrun or underrun condition. The total design is about 5 lines of active code in RTL (ignoring all the reset states and structure for this discussion).

To get the first word there the moment your read pulse is active, you need to "prefetch" the first value. With a write pointer that puts its first word into BRAM location 0, the read has to be pointing to BRAM location 0 *before* the read pulse and the BlockRAM must be enabled to get that first location out. You have - without using a read pulse - the first word present on your FIFO output. When the read pulse indicates you're taking that word, you advance to the next value and "preload" that into your BlockRAM output for your next read pulse. If you have two consecutive reads, the combinatorial read address increase that you must code will load the next read value on the next clock. If the value isn't written yet for that new value, the empty must be asserted until the write value propagates the value to the read port.

You can affect this same behavior with a wrapper on your existing FIFO as well. The empty flag, external read and internal read need to be coordinated to perform a preload of your desired "asynchronous" value to the output. It's not that tough if you look at the logic as a preload.

Is you system so messed up that you need the old value to be present until the read pulse is active for the new value? Still not a problem! The preloaded output would work with an external register for the previously read value and the preload value. When the read pulse is not active, the register is selected. When the read pulse *is* active, the BRAM value is selected and the hold register updates at the upcoming clock edge.

FIFOs with clocks on both the input and output domains are some of the simplest control mechanisms you can deal with.

If you actually need two different clock domains for your input and output sides, life is less simple but the external wrapper logic I suggested earlier will easily provide you with asynchronous access to your next word from a standard synchronous-output FIFO when your read pulse asserts.

Think more about how your design needs depart from the FIFO structures available to you and consider how you can change the FIFO or change your system to make everything happy. The conversion should be reasonably clean if you look at it from the proper perspective.

But if you want more of a turnkey solution, go into more detail on the specifics. Family. Fall-through. Clock domains.

- John_H

Reply to
John_H

I use RAMB16_S36_S36 primitive (for the moment) and "common single clock". Prefetching is very interesting, though it is hard to understand for the moment :). Thank you for info.

Reply to
Pasacco

The BRAMs are synchronous devices; you cannot read them asynchronously. So the only solution is some sort of prefetching, as mentioned. The CLB RAMs do allow asynchronous reads, and "more than 48-deep words" is not terribly large. That means probably 4 CLBs per word bit (depending on the device used), so a 16 bit word is 64 CLBs. Not really that many. So you might want to consider just using them if you don't want to bother with prefetching.

Reply to
Duane Clark

If you use a BlockRAM as a FIFO, you have to accept that any read operation is synchronous, i.e. the result of a read-clock edge. First-word-fall-through puts the first word written into a previously empty FIFO onto the output, synchronously with the read clock, but irrespective of the read clock enable. I call it a push operation, as opposed to the pull in normal mode. Once more than one word is in the FIFO, there is no difference between the two modes, you can always get a new word on the nect Read clock tick, if that is what you want. Peter Alfke, Xilinx

Reply to
Peter Alfke

The bodgers way to do this is to use the falling edge of the clock on the read section. Nasty though! HTH, Syms.

Reply to
Symon

If there's clock in the FIFO output domain, just don't see why he needs Async read ???

Don't let me guess it correct, the reason he wanna do this because there's no read clock at all. If that's true, another nasty way to do this is to generate a "glitch" from any changing in the address bits. Make sure it meets the BRAM setup time then the glitch can be used as the read clock

Reply to
Marlboro

By "Async read", he means that he doesn't want to wait one clock after applying the read address before getting the data out.

Reply to
Duane Clark

But can the BRAM FIFO be made to work with back-to-back reads? Since the BRAM registers the address, it won't work if you just increment the read address pointer when read enable is true - the output data is a clock behind the read address pointer, so you need an idle clock cycle between each read. Barry Brown

Reply to
Barry Brown

If the read address changes combinatorially with the read enable, the clock edge will provide the data for the next address in the back-to-back reads for the BlockRAM. No problem.

Reply to
John_H

Hmm... that is an interesting approach I had not thought of. I did not carefully read through your longer discussion, and missed that point. I'll definitely have to consider that in future things, thanks.

Reply to
Duane Clark

PLEASE HELP: I also have a question on Asynch. RAMs:

In my design I have a bank of memory made up of 64 reg[31:0]'s. They get synthesized into latches, and kill my timing because of a huge 64 X 32 sensitivity list.(See below).

How else can I synthesize a 64X32 block of Memory with Asynch Read, Asynch Write, and where the Data_In shows up immediately on the Data_Out?

Can I do it with Xilinx CoreGen Modules?

always @ (rd_address or q0 or q1 or q2 or q3 or q4 or q5 or q6 or q7 or q8 or q9 or qa or qb or qc or qd or qe or qf or q10 or q11 or q12 or q13 or q14 or q15 or q16 or q17 or q18 or q19 or q1a or q1b or q1c or q1d or q1e or q1f or q20 or q21 or q22 or q23 or q24 or q25 or q26 or q27 or q28 or q29 or q2a or q2b or q2c or q2d or q2e or q2f or q30 or q31 or q32 or q33 or q34 or q35 or q36 or q37 or q38 or q39 or q3a or q3b or q3c or q3d or q3e or q3f) begin case (1'b1) // synopsys full_case parallel_case rd_address[0]: DO = q0; rd_address[1]: DO = q1; rd_address[2]: DO = q2; rd_address[3]: DO = q3; rd_address[4]: DO = q4; rd_address[5]: DO = q5; rd_address[6]: DO = q6; rd_address[7]: DO = q7; rd_address[8]: DO = q8; rd_address[9]: DO = q9; rd_address[10]: DO = qa; rd_address[11]: DO = qb; rd_address[12]: DO = qc; rd_address[13]: DO = qd; rd_address[14]: DO = qe; rd_address[15]: DO = qf; rd_address[16]: DO = q10; rd_address[17]: DO = q11; rd_address[18]: DO = q12; rd_address[19]: DO = q13; rd_address[20]: DO = q14; rd_address[21]: DO = q15; rd_address[22]: DO = q16; rd_address[23]: DO = q17; rd_address[24]: DO = q18; rd_address[25]: DO = q19; rd_address[26]: DO = q1a; rd_address[27]: DO = q1b; rd_address[28]: DO = q1c; rd_address[29]: DO = q1d; rd_address[30]: DO = q1e; rd_address[31]: DO = q1f; rd_address[32]: DO = q20; rd_address[33]: DO = q21; rd_address[34]: DO = q22; rd_address[35]: DO = q23; rd_address[36]: DO = q24; rd_address[37]: DO = q25; rd_address[38]: DO = q26; rd_address[39]: DO = q27; rd_address[40]: DO = q28; rd_address[41]: DO = q29; rd_address[42]: DO = q2a; rd_address[43]: DO = q2b; rd_address[44]: DO = q2c; rd_address[45]: DO = q2d; rd_address[46]: DO = q2e; rd_address[47]: DO = q2f; rd_address[48]: DO = q30; rd_address[49]: DO = q31; rd_address[50]: DO = q32; rd_address[51]: DO = q33; rd_address[52]: DO = q34; rd_address[53]: DO = q35; rd_address[54]: DO = q36; rd_address[55]: DO = q37; rd_address[56]: DO = q38; rd_address[57]: DO = q39; rd_address[58]: DO = q3a; rd_address[59]: DO = q3b; rd_address[60]: DO = q3c; rd_address[61]: DO = q3d; rd_address[62]: DO = q3e; rd_address[63]: DO = q3f;

Reply to
Frank

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.