Memories and clock domain crossing (CDC) elements are among some of the most commonly used structures in our FPGA designs. Memory elements have several applications and are used to store input data, the results of processing stages, buffer information between processing stages and of course, allowing us to share data between multiple clock domains using asynchronous FIFOs.
The most commonly used CDC element is a multi-stage synchronizer, which enables a single bit or bit vector to be synchronized from one clock domain to another, ensuring any metastability effects are mitigated.
To implement both memory and CDC functions in our Vivado design, we can follow one of two approaches:
- Inference: Using inference, we write the code in a way we hope the synthesis tool will be able to extract the desired function and map it to the necessary logic resources. Inference therefore provides the most architecture flexible method of implementing memory and CDC structures.
- Instantiation: Using instantiation, the component is directly instantiated as a component or module within the RTL. This enables the instantiating RTL to control exactly how the core is implemented in the final design. This approach is most often used with more complex memory structures such as Dual Port Memories or FIFOs.
When we create designs using Vivado we have the advantage of being able to work with the IP library and Instantiation. The IP Library provides a range of memories structures including single and dual port memories, synchronous, and asynchronous FIFOs which are implemented using the block memory generator.
However, using these block memory generated IPs does have some drawbacks, including being mapped as black boxes in synthesis. Of course, mapping these memory structures as black boxes impacts the ability of the synthesis engine to predict resources usage and timing performance.
Xilinx Parameterized Macros (XPM) provide an alternative to using the block memory generator, which enable the creation of memory structures that are both faster to simulate and lets the synthesis engine work without the black boxes. This allows better area and timing performance estimation.
To use XPM templates within our designs we use instantiation in the RTL just like we do with IP created by the block memory generator. To be able to instantiate XPM templates, we include the library XPM and package vcomponents within our source code.
library xpm; use xpm.vcomponents.all;
To ensure we use the correct XPM template we can leverage the Vivado editor language templates, which open up a number of language templates including XPM templates. We can then copy and paste these into our RTL design. If you are not working with the Vivado editor, you can also download the instantiation templates.
To demonstrate the difference in synthesis results when using XPM templates, I updated a simple design which targeted the ZedBoard and previously used a synchronous FIFO created with the FIFO generator. When the original module using the FIFO generator FIFO was synthesized, as can be seen below the FIFO was reported as a black box and the list of used primitives did not include any block RAM elements.
Updating the design to use the XPM FIFO instantiation and rerunning the design through synthesis produced the results shown below. This time, you can see the primitives include the block RAM and gates used in the FIFO and of course it reports no black boxes.
Using XPM therefore gives us better quality of synthesis results and provides the additional benefit of a reduced simulation time. Adding in XPM templates within our design is very simple to do and one that we should consider at the start of each development.
More details on XPM for 7 Series and UltraScale devices
See My FPGA / SoC Projects: Adam Taylor on Hackster.io
Get the Code: ATaylorCEngFIET (Adam Taylor)
Additional Information on Xilinx FPGA / SoC Development can be found weekly on MicroZed Chronicles.