I am looking into implementing a FIFO that can hopefully be reused in multiple devices.
The FIFO template shall expose these methods push pop and len. I want the size of the FIFO to be defined as a parameter and I want the type the FIFO holds to be any type of standard integer types (uint8, uint16, uint32, uint64, int8, int16, int32 and int64). Pushing a 64-bit integer into a 8-bit FIFO shall cause truncation. The FIFO shall also support checkpointing.
I started with this code but it does not compile:
template fifo {
param fifo_size;
saved uint64 buf[fifo_size];
}
If there are only a very small, restricted set of types you need to support, then creating a separate FIFO template for each type may be advisable (using
#ifto ensure thebufdeclaration works). Such an implementation would be the easiest to understand, while still providing the greatest number of benefits for users of the templates -- its only cost is the excessive boiler-plate needed to define the templates.However, there is an alternate approach leveraging pseudo-generics which allows you to write a template that scales up to any number of supported types, and has almost all of the same benefits as creating duplicate templates for each type. Here is how such a
fifotemplate would look using that approach:Example usage:
The greatest downside of
fifocompared to duplicated, type-specific templates is thatpushandpopare not members of the template type. However, this actually addressable -- you can create a child template offifofor a specific type, and declarepushandpopto besharedin that template -- making them members of the child template's template type.For example:
Example usage:
As strange as it may seem to have non-
sharedmethods defined by a parent template be declaredsharedby a child template, it is actually perfectly allowed by DMLC. You can do the same thing with parameters, too -- an untypedparamdeclared by a parent template can be made a typedparamof a child template. This pattern is called "late shared", in want of a better name.The one benefit the above lacks compared to FIFO templates with duplicated implementations is that the underlying implementations of
popandpusharen'tshared(even if the methods themselves are declaredsharedby a child template). What this means is that the C functions for them are generated for every object that instantiates the template -- which could inflate compilation times if a particular DML model has a lot of such objects. In contrast, with templates defined by duplicating code, you could make the implementations ofpushandpopshared (although not without a small wrapper method to gain access to thefifo_buf, whose implementation can't be shared). In theory, such a template would scale better with growing number of instances than one based on the type-genericfifo.