Or to cast the std_logic_vector into a record in the top-level where the FIFO is instantiated? - The way I see it, to have a rather generic FIFO of records, we would have to declare the record type in an external package and change that according to the design.
That's one way. The other way is to convert to/from std_logic_vector right near where the record is first generated and then used. That makes those conversion functions part of a package that is local to the design and the FIFO is completely oblivious to the records.
As soon as you talk about wanting a 'generic' widget of any sort, the interface to that widget almost immediately becomes std_logic_vector because that is the common denominator, 'specially for something is inherently generic like memory. If your widget happens to perform some form of mathematical function your interface might be 'signed/unsigned' or 'sfixed/ufixed' depending on what you're computing. But store those results in memory someplace and even those types will get converted to/from slv. It's still a good idea to define a widget with signal interface types that are appropriate for what function is being performed, but don't gack just because it needs to be converted to/from std_logic_vectors at some point. Re-using a component is usually a 'good' thing.
An example of the way I do this with records is shown in the example at the end of the post. If the problem you're working doesn't lend itself to fixed sized fields (i.e. the bit numbers in the field definitions vary depending on usage), then the solution can get a bit more complicated but in general I've found that a particular design 'usually' gets by just fine with a fixed record definition so the solution below works.
Kevin Jennings
--- Example of a record type and to/from std_logic_vector functions
type t_CNTL_PORT is record
Reserved: std_ulogic_vector(7 downto 3);
Hcard_Empty: std_ulogic_vector(2 downto 2);
Hopper_Empty: std_ulogic_vector(1 downto 1);
Feeder_Exit: std_ulogic_vector(0 downto 0);
end record t_CNTL_PORT;
function To_Std_ULogic_Vector(L: t_CNTL_PORT) return std_ulogic_vector is
variable RetVal: std_ulogic_vector(31 downto 0);
begin
RetVal(L.Reserved'range) := L.Reserved;
RetVal(L.Hcard_Empty'range) := L.Hcard_Empty;
RetVal(L.Hopper_Empty'range) := L.Hopper_Empty;
RetVal(L.Feeder_Exit'range) := L.Feeder_Exit;
return(RetVal);
end function To_Std_ULogic_Vector;
function From_Std_ULogic_Vector(L: std_ulogic_vector) return t_CNTL_PORT is
variable Lx: std_ulogic_vector(L'length - 1 downto 0);
variable RetVal: t_CNTL_PORT;
begin
Lx := L;
RetVal.Reserved := Lx(RetVal.Reserved'range);
RetVal.Hcard_Empty := Lx(RetVal.Hcard_Empty'range);
RetVal.Hopper_Empty := Lx(RetVal.Hopper_Empty'range);
RetVal.Feeder_Exit := Lx(RetVal.Feeder_Exit'range);
return(RetVal);
end function From_Std_ULogic_Vector;