Yes, you can, and I consider this the best practice β this is the smallest work, the best understanding, ease of maintenance and the cleanest design.
The trick is to declare types that are common to all of your constructs in a package (usually I call it "Common" :-) and add use work.Common.all before declaring an AND object for each client of that object. Of course, more specialized components may have corresponding names!
For example:
package Common is -- untested... type my_enum_type is (r1, r2, r3); -- (optional) useful tools function to_slv (e : my_enum_type) return std_logic_vector; function to_enum (s : std_logic_vector(my_enum'length downto 0)) return my_enum_type; end Common; package body Common is -- subprogram bodies here end Common;
Now, when you add a value to the enumeration, you ONLY change the "Common" and rebuild the design, while those who adhere to the usual recommendations are still trying to identify each port and signal where they need to increase the range of their "std_logic_vector" by 1 .
It also works well for bus interfaces, where recording in each direction hides all individual signals and acknowledgment signals.
You will have to deal with brain tools, such as the Xilinx automated test bench generator, which will help translate ALL of your port types - integer or logical, as well as custom - into std_logic (_vector) and then not compiled. Just translate them back.
You can still make the case where at the highest level, all external FPGA pins should still be based on std_logic. And if you ever need to model the post-synthesis version of your design, you will either need to live with the std_logic_vector ports, or add a simple wrapper to convert from one form to another.
Brian drummond
source share