Please outline, at least in general terms, the functional differences between:
a) the FIFO18E2 and FIFO36E2 primitives as discussed in UG573 and UG974;
b) IP-generated primitive-based FIFOs; and
c) the XPM_FIFO macros described in UG974.
Regarding a) and c) differences, the latter appears to generally offer a subset of the capabilities of the former (e.g., the macros provide no cascading capability), however the XPM_FIFO_ASYNC does offers a CDC_SYNC_STAGES attribute which the primitives do not. This makes it not exactly clear whether the macros do in fact provide only just a subset of the primitives' functionality, or not.
My comment on the XPM_FIFO macros not providing explicit cascading should be ignored, as I see by the FIFO_WRITE_DEPTH, WRITE_DATA_WIDTH and READ_DATA_WIDTH attributes that the macros apparently make implicit use of the primitives' cascading capability.
So, given the macro support for the CDC_SYNC_STAGES attribute, it seems that the macros actually implement a superset of what the primitives offer.
Some of the basic differences:
FIFO18E2 and FIFO36E2 are just the built in FIFO primitives. User will have to manually cascade them for larger depths/widths.
IP allows the user to generate BRAM/built in FIFO/distributed RAM based FIFO's of any supported widths and depths. It also has additional features like status signals, data counts, soft ECC, error injection etc.
XPM is parameterizable macro. You can simple instantiate the macro, set different attribute values to generate different types of FIFO's. This removes the extra effort of IP generation and useful when you want to use multiple different configurations of FIFO. The XPM_FIFO supports Block RAM, URAM, distributed RAM based FIFO's.