Regarding asynchronous FIFO design, you must understand these 7 points

write in front

        In this article, it is assumed that you have a basic understanding of the design method of asynchronous FIFO.

        If you don't know the basic method of asynchronous FIFO design, you can refer to: Verilg implementation method of asynchronous FIFO


1. What is Gray Code?

        Gray code is a binary encoding method proposed by American scholar Frank Gray in 1947, and the latter encoding method is named after him. In fact, there are many encoding forms of Gray code. According to the definition, in the coding of a group of numbers, if any two adjacent codes differ only by one binary digit, the coding is called Gray code .

        The following table shows the different forms of Gray codes:

        Typical Gray codes in the table are representative. Unless otherwise specified, Gray code refers to typical Gray code (hereinafter referred to as Gray code) , which can be converted from natural binary code.

        Well, now I ask you, according to the nature of the Gray code, can you write 16 Gray codes with a width of 4 bits by default? I think it is generally more difficult, because it is difficult to derive from the above properties alone. For example, 0 (0000) can be written, 1 (0001) can also be written, 2 (0011) can also be written, but 3 is not easy to write, because according to the nature of only one change, it can be 0111 , or 0010, so which one is it? If it is not used often, I think it is difficult to judge.

        So next we will introduce the second property of Gray code: when the Nth bit changes from 0 to 1, the N-1 bit of the following number will be symmetrical about the first half of the axis, and the bit higher than the N bit is identical.

        Let's look at an example of the first four bits of a 4bit Gray code. The schematic diagram is as follows:

  • When 0001 jumps to the next digit, there is no doubt that the 0th digit will remain unchanged at 1, and the 1st digit will jump to 1, so the symmetry axis can be drawn accordingly.
  • The upper 2 bits (bit 3, 2) this remains the same
  • The low order (only bit 0 in this instance) is symmetric about the symmetry axis

        With the above three points of information, the remaining two Gray codes can be written.

         

        The schematic diagram of the first 8 bits of the 4bit Gray code is as follows (I will not repeat this, you must understand it):

        For Gray code, please refer to: Interconversion between Gray code and binary code implemented by Verilog

2. Why do asynchronous FIFOs use Gray code?

        What is the most critical point of asynchronous FIFO design? The answer is the judgment of "empty" and "full". Now recall how we made state judgments. Very simple, construct read pointer and write pointer separately. The write pointer always points to the next address to write, and the read pointer always points to the current address to read. Then judge the empty and full by comparing the read and write pointers.

        As shown in the figure above, the FIFO depth is 8, then its address bit width should be 3 (the power of 2 equals 8). When both the write pointer and the read pointer point to the same location (that is, the same), it may be an empty state, but it may also be a full state (the write pointer exceeds the read pointer by one circle). (A digression here, it is impossible for the read pointer to exceed the write pointer by one circle, because when the read and write pointers are equal for the first time, the empty state should be output, and then the FIFO read operation should be stopped.) So, how exactly? Determine whether the FIFO is empty or full? The answer is that it cannot be judged. It can only be judged by adding one bit to the high bit. When the other bits except the MSB of the highest bit are the same, and the highest bit is different, it means that the write pointer exceeds the read pointer by one circle at this time, and the FIFO is written. Full; when the other bits are the same except the MSB of the highest bit, and the highest bit is the same, it means that the write pointer is equal to the read pointer at this time, and the FIFO is empty.

        It is no problem to use this method to judge the synchronous FIFO, because the read and write pointers of the synchronous FIFO are signals in the same clock domain, which can be directly compared. However, the read and write pointers of the asynchronous FIFO are signals in different clock domains, and if they are directly compared, there will be a problem of metastability. In order to judge the read and write pointers of asynchronous FIFOs, we first need to synchronize them to a unified clock domain, which leads to a new problem - read and write pointers are not unique in most cases A single-bit signal, but a multi-bit signal. If the read and write pointers are directly synchronized in the form of binary, it is difficult to avoid the simultaneous change of multiple bit signals during the synchronization process. For example, 7 (0111) jumps to 8 (1000). At this time, all 4 bit signals have changed. If they are directly synchronized, due to the delay (skew) between different signals, it may lead to metastability, wrong sampling, Leakage issues, etc.

        At this point, recall the properties of the Gray code: there is only one bit change between each adjacent bit. The pointer of the FIFO is incremented, which makes the Gray code have a natural advantage when transmitting an incremented multi-bit signal. Or the example from 7 to 8, if the Gray code is used, it should be 7 (0100)--8 (1100), so that only 1 bit changes (the highest bit), so that the multi-bit signal cross-clock The domain is transformed into a cross-clock domain of a single-bit signal, and the synchronization of a single bit across the clock domain is well achieved.

3. Which clock domain should the read pointer and write pointer be synchronized to?

        The read and write pointers of the asynchronous FIFO are signals of different clock domains, so they cannot be directly compared, but need to be synchronized to the same clock domain for comparison. Now the question comes? Which clock domain should the pointer be synchronized to? Options are third-party clock domains, read clock domains, and write clock domains. Next, we may analyze it one by one.

        The first thing to note is that the synchronization mentioned here refers to the use of 2 (or 3, but there are not many such cases) FF (trigger) for synchronization (commonly known as "two beats"). This synchronization method is Delayed (timing overhead, which can be seen as two synchronous clock cycles).

        Third-party clock domain : It is not difficult to know that it takes time for a signal to synchronize from one clock domain to another clock domain (synchronized clock domain) (only from full to fast is considered here, that is, the problem of missing sampling is not considered for the time being), The time required depends on the period of the clock domains being synchronized and the number of synchronizations required. Assuming that this time is T, then after the T time, due to inconsistent read and write clocks, the original read and write clock hands increase (or may remain unchanged) by an inconsistent amount. For example, if the read and write clocks actually point to 4 (and the highest bit is the same), then this situation is actually a situation of reading empty. However, after synchronizing to the third-party clock domain, the write pointer may become 6, and the read pointer may become 8 (the read clock is faster than the write clock), then in this case, the FIFO will not report "read empty", resulting in Functional disorder. So this method is not advisable.

        Synchronization to the write clock domain : It takes time T for the read pointer to synchronize to the write clock domain. After T time, the original read pointer may increase or remain unchanged, that is to say, the synchronized read pointer must be less than or equal to the original read pointer. of. The write pointer may also change, but the write pointer is originally in this clock domain, so it does not need to be synchronized, which means that the compared write pointer is the real write pointer.

                Now come to the judgment of full write: that is, the write pointer exceeds the synchronized read pointer by one circle. However, the original read pointer is greater than or equal to the synchronized read pointer, so in fact, the write pointer does not exceed the read pointer by one circle at this time, that is to say, this situation is "false full". So is "fake full" a bad design? The answer is NO. Earlier we said that the key point of asynchronous FIFO design is to generate appropriate "full" and "read empty" signals, so what is "appropriate"? Is it appropriate to not report when it should be reported? Certainly not appropriate. Is it appropriate to report when it shouldn't be? The answer is count. Imagine if a FIFO with a depth of 100 reports "full" when the 98th data is written, what will be the consequences? The answer is that it will not cause functional errors, but will only cause performance loss (2%). The big deal is that I use a little less of the depth of the FIFO. In fact, this can be regarded as a somewhat conservative design (safety).

                Then make a judgment of reading empty: that is, the synchronized read pointer catches up with the write pointer. But the original read pointer is greater than or equal to the synchronized read pointer, so in fact, the read pointer actually exceeds the write pointer at this time. This condition means that a "read null" has occurred, but there are still erroneous data read out. So this situation creates a functional error of the FIFO.

        Synchronization to the read clock domain :  It takes time T for the write pointer to synchronize to the read clock domain. After T time, the original read pointer may increase or remain unchanged, that is to say, the synchronized write pointer must be less than or equal to the original write pointer of. The read pointer may also change, but the read pointer is originally in this clock domain, so synchronization is not required, which means that the read pointer for comparison is the real read pointer.

                Now let's judge the full write: that is, the synchronized write pointer exceeds the read pointer by one circle. However, the original write pointer is greater than or equal to the synchronized write pointer, so in fact, the write pointer has exceeded the read pointer by more than one circle at this time, which means that "full write" has occurred, but the data is still overwritten. enter. So this situation creates a functional error of the FIFO.

                Then make a judgment of reading empty: that is, the read pointer catches up with the synchronized pointer. But the original write pointer is greater than or equal to the synchronized write pointer, so in fact, the read pointer has not caught up with the write pointer at this time, that is to say, this situation is "false read null". So is "false read null" a bad design? The answer is NO. Earlier we said that the key point of asynchronous FIFO design is to generate appropriate "full" and "read empty" signals, so what is "appropriate"? Is it appropriate to not report when it should be reported? Certainly not appropriate. Is it appropriate to report when it shouldn't be? The answer is count. Imagine if a FIFO reports "empty read" when there are 2 data left, what will be the consequences? The answer is that it will not cause functional errors, but will only cause performance loss (2%). The big deal is that I will not read it first, and I will read it when there is more data. In fact, this can be regarded as a somewhat conservative design (safety).

        Now we can summarize:

  • Judgment of "full": the read pointer needs to be synchronized to the write clock domain, and then judged with the write pointer
  • Judgment of "read empty": the write pointer needs to be synchronized to the read clock domain, and then judged with the read pointer

        The false reading is indicated as follows:

        The fake full is indicated as follows:

4. How to judge the empty and full of asynchronous FIFO?

        In the design of the synchronous FIFO, we widened the bit width of the read and write pointers by 1 bit, in order to distinguish the problem of judging empty and full when the original read and write pointers are equal. The pointer of the synchronous FIFO is in the form of binary code, and the asynchronous FIFO uses the pointer in the form of Gray code in order to reduce the metastability problem of multi-bit signal transmission across the clock domain, so how should the pointer in the form of Gray code compare? To judge empty and full?

        The first thing to note is that it is a good way to convert the Gray code into binary and then compare, but it will consume a lot of combinational logic resources, so we will not discuss it for the time being.

        First observe the above figure, assuming that the FIFO depth we want to design is 8, then the pointer width is 4, because when the width is 3, it is impossible to distinguish between empty and full.

        Empty judgment is easy to judge, as long as all bits of the read and write pointer are consistent, it means that it is in an empty state at this time.

        The judgment of full is a little more complicated. It is assumed that the judgment method of synchronous FIFO is adopted—the highest bit is different, and the other bits are consistent. Let's use the actual value to see what's going on: when the value of the read pointer is 0100, it means that the read pointer points to the highest space 7 at this time, then if the FIFO is full, the write pointer should be 1100, then the binary corresponding to 1100 How much is it? is 8. So the read and write pointers, one points to 7, the other points to 8, is this full? Obviously not.

        If it is really full, what should be the write pointer? It should be 15, which is 1000. At this time, the write pointer exceeds the read pointer by 8, which is a circle. So what is the connection between 0100 and 1000? Obviously, the upper two bits are opposite, and the lower bits are the same. The reason for the formation of this law is that we mentioned earlier that the changes of the Gray code are all symmetrical about a certain axis of symmetry.

        Summarize:

  • When the highest bit and the second highest bit are the same, and the rest of the bits are the same, it is considered to be empty.
  • When the highest bit and the second highest bit are different, the rest of the bits are considered to be full.

5. Is the judgment of empty and full accurate?

        In point 4, we know - synchronize the read pointer to the write clock domain to judge full; synchronize the write pointer to the read clock domain to judge empty. Since it is an asynchronous FIFO, the signals of reading and writing the clock domain are inconsistent, one of which has a fast frequency, and the other has a slow frequency. Then in the two synchronization processes, it must be a slow clock to pick up a fast clock and a fast clock to pick a slow clock. There is no problem with a fast clock taking a slow clock, because this follows the sampling theorem. But slow clocks will have problems with fast clocks, because the sampling process does not conform to the sampling theorem.

        So what would be the problem? The answer is omission. Some values ​​may be missed. For example, the original continuous 0--1--2--3 signal, after synchronizing from the fast clock to the slow clock, becomes a discrete 0--3, in which 1 and 2 are missed. Then such a phenomenon will cause the judgment of emptiness and fullness to be accurate? The answer is inaccurate, but that's okay.

        Imagine two cases of slow reading and fast writing and fast reading and slow writing:

        Slow to read, fast to write:

                When making a full write judgment, the read pointer needs to be synchronized to the write clock domain. Because the read is slow and the write is fast, there will be no read pointer missing, and synchronization consumes clock cycles, so the synchronized read pointer lags (less than or equal to) the current read address, Therefore, it may be filled up in advance, and it is not really filled.

                When making a judgment of reading null, it is necessary to synchronize the write pointer to the read pointer. Because the read is slow and the write is fast, when the read clock synchronizes the write pointer, a part of the write pointer will inevitably be missed. We don't need to care about which write pointers will be missed. , what we care about is that the missing pointer will have an effect on the empty read of the FIFO? For example, the write pointer is written from 0 to 10. During the read clock domain, only the three write pointers 3, 5, and 8 are captured synchronously and other pointers are missed. When it is synchronized to the write pointer of 8, the real write pointer may have been written to 10, which is equivalent to the case that the write clock domain may have written data to the FIFO before the read clock domain has time to detect it, so as to judge whether it is empty or not When it is not really empty, the missing pointer has no effect on the logical operation of the FIFO.

        Read fast and write slow:

                When making a read null judgment, it is necessary to synchronize the write pointer to the read pointer. Because reading is fast and writing is slow, there will be no write pointer missing, and synchronization consumes clock cycles, so the synchronized write pointer lags (less than or equal to) the current write address, so It is possible that the read null will be generated in advance, not a true read.

                When making a full write judgment, it is necessary to synchronize the read pointer to the write clock domain. Because the read is fast and the write is slow, when the write clock synchronizes the read pointer, a part of the read pointer will inevitably be missed. We don't need to care about which reads will be missed. Pointers, what we care about is that missing pointers will have an impact on FIFO fullness? For example, the read pointer reads from 0 to 10, during which the write clock domain only captures the three read pointers 3, 5, and 8 synchronously and misses the other pointers. When synchronizing to the read pointer of 8, the real read pointer may have read 10, which is equivalent to the case that the read clock domain may have read data from the FIFO before the write clock domain has time to detect it, so that it is judged whether it is When it is full, there will be a situation that is not really full, and the missing pointer has no effect on the logical operation of the FIFO.

        Now we will find that the so-called empty-full signal is actually inaccurate. The empty-full signal has been output before the clock is not empty or full. Such an empty-full signal is generally called false-empty and false-full. The false empty and false full signals are essentially a conservative design. Imagine an asynchronous FIFO with a depth of 16. When it writes 14 data, it outputs the full (false full) flag, which will affect our Does the design matter? Yes, it will reduce our efficiency. The FIFO depth we actually use is 14, but will it make our design wrong? Obviously not. Similarly, outputting the read empty (false empty) flag when the FIFO depth is still 2 will not make our design error, but it will reduce the efficiency because we use 2 less FIFO depth.

6. Since there are false fullness and false emptiness, is there true fullness and vacuum?

        There is, but it doesn't make sense. Since we can synchronize the read pointer to the write clock domain to judge false full; synchronize the write pointer to the read clock domain to judge false empty. Then correspondingly, the read pointer can be synchronized to the write clock domain to judge empty; the write pointer can be synchronized to the read clock domain to judge full.

       Judge empty in the write clock domain:

                The signal from which the read pointer is synchronized (the read pointer after synchronization) is a signal that lags behind the real read pointer. When the read pointer is equal to the write pointer after synchronization, the real read pointer is actually equal to the write pointer. Empty must be empty, and it has even been empty for a while. Such an empty flag is obviously meaningless, because it will cause over-reading of the FIFO. What's the point of you reading an empty FIFO back and forth? That is to say, the vacuum can be realized, but it has no practical significance.

       Judging full in the read clock domain:

                The signal that the write pointer is synchronized (the write pointer after synchronization) is a signal that lags behind the real write pointer, but when the write pointer exceeds the read pointer by one circle after synchronization, the real write pointer actually exceeds the read pointer by one circle, that is It is said that it must be full at this time, and it has even been full for a while. Such a full flag is obviously meaningless, because it will cause an overwrite operation to the FIFO. What's the point of you writing a full FIFO back and forth? That is to say, it can be fully realized, but it also has no practical significance.

7. How to design a FIFO with a depth other than the power of 2?

        In point 1 on the properties of Gray codes, we stated:

  • In the coding of a set of numbers, if any two adjacent codes differ only by one binary digit, the coding is called Gray code
  • When the Nth bit changes from 0 to 1, the N-1 bits of the following numbers will be symmetrical about the first half of the axis, and the bits higher than the N bits will be the same.

        From these two points, we find that Gray codes can be symmetric about a certain axis of symmetry. Therefore, only when the FIFO depth is a power of 2 can the Gray code go around a circle and return to the initial position with only 1 bit of change, such as 15(1000)----0(0000). When the FIFO depth is not a power of 2, obviously jumping from the end to the beginning will change more than one bit. For example, the FIFO depth is 7. Obviously, from 13 (1011) ---- 0 (0000), the change can be more than 1 bit. In this case, the meaning of the existence of the Gray code will be lost in this jump, so you have to think of other solutions.

        As mentioned earlier, each adjacent bit of the Gray code only changes by 1 bit, and it is symmetrical about the central axis. Then we can code it like this: for a FIFO with a depth of 7, starting from 1 (0001), until (14 numbers represent 7 depths, high bits distinguish the state) 14 (1001), 1 (0001) and 14 (1001) are about The central axis is symmetrical (the high bit is the change bit), so there is only a 1bit jump. Similarly, if the FIFO has a depth of 6, it starts from 2 (0011) and goes to 13 (1011), and only jumps 1 bit.

        The empty flag is only used to judge whether the read and write pointers are all equal. However, the full flag cannot be judged by "the upper two bits are opposite, and the other bits are the same", and other rules need to be found. It can be seen from this that the Gray code, as an unweighted code, is not as flexible as the weighted binary code in terms of comparison and operation. So, let's go back to binary and compare.

        To be more verbose, in fact, no matter whether you design the FIFO to use RAM or directly call IP, the final implementation will use Block RAM resources, and the generated bit width must be a power of 2. So, it's a waste not to use it.

Guess you like

Origin blog.csdn.net/wuzhikaidetb/article/details/123570799