02-22-2021 06:04 AM
I'm an experienced software developer as FPGA developer but this problem I cannot (yet) solve for FPGA/VHDL.
In SW, it is very common to use external (3rd) party libraries as source code. These libraries make possibly use of other common libraries. Depending on you SW project, it could be that component COMP is used with version A in a 3rd party source library while the same COMP component is used as version B in the main application. I.e. two versions of the same component (COMP) used in the project at different places (main application and in 3rd party source). This is a very common 'problem' as are the solutions.
This problem also exists in our FPGA projects. We develop a complex internal component (call it COMPLEX_COMP) that makes use of several company wide generic HDL components. These components have nothing to do with COMPEX_COMP and can be used in many other projects. The complex internal component (COMPLEX_COMP) is also used as a generic component in several FPGA projects (added as source to those projects).
The actual problem:
- Main project PROJECT is using version A of a generic VHDL component called COUNTER
- Main project PROJECT also makes use of the complex component COMPLEX_COMP
- Complex component COMPLEX_COMP makes use of the same generic VHDL component COUNTER but version B
How can we add both versions (A and B) of generic VHDL component COUNTER to our main project so that complex component COMPLEX_COMP uses version B and the main project uses version A?
I askes this question also in our company but there was no real solution. There was one, but I really hate it:
- Update ALL generic components (and the Project) in the project that make use of generic VDHL component COUNTER to the latest version B.
- Add the latest version of the generic VHDL component COUNTER to the Vivado project PROJECT in the correct library. All other components and the main project will use version B of the COUNTER component.
Why do I hate that solution?:
- With a lot of components, you don't know which one uses the latest version of a specific version. You have to check each and every one of them
- You would have to change all components, changing perfectly working code just to incorporate version B of the COUNTER component (which is error prone, might cause bugs, lot of effort for nothing, etc)
- If you miss one, it might still build but could cause erratic behaviour because a particular component was tested with version A of the COUNTER component, not version B
Like I mentioned before, this situation is also known in SW but there are several (good) solutions to cope with this.
How to handle this in VHDL/Vivado?
02-22-2021 06:31 AM - edited 02-22-2021 08:29 AM
This is a very confusing question, as it mixes descriptions with VHDL concepts and I dont understand your exact meaning.
When you say component, do you mean a VHDL Component declaration? or a source file with an entity/architecture pair?
If your source code is generic, why do you have two versions of the same file? Or are you talking about using a single entity with multiple architectures? Multiple architectures is rarely used in FPGA world (Ive actually never seen it done)
An example with code may be good here. We have plenty of VHDL code that is re-used in several projects in different configurations based on the setup of the generics on the entity. Use of component declarations is discouraged as it requires code to essentially be maintained twice and direct instantiation used when possible.
An example here would be a register file. For this, it is one file that simply outputs an array of registers, size determined by a generic. It is one source file by used in about 20 places in the design. Each one outputs a different set of registers at different addresses.
Please add more clarity to your question. VHDL code re-use is fairly straight forward.
02-22-2021 12:23 PM
Thank you for your answer. I think you misunderstood me. Let me clarify it in another way. First, very briefly some short answers:
- I know how to configure VHDL components (source file with an entity+architecture) using generics. We use it extensively as well
- Multiple architectures for an entity is something we also use for several components. Helps us a lot to support different types of FPGAs, Vendors, concrete implementations, etc
- We instantiate components without the component-declaration. We use: my_component_inst: entity my_lib.my_component generic map() port map();
Maybe some other useful information:
- Generic Components are Entity+Architecture source files that implement common functionality that can be used in different products with different configurations (using 'generics')
- We store generic (as in reusable) HDL components in different GIT repositories. Each generic component has a different GIT-repo
- We add the components we want to use in our project using GIT submodules. I.e., each (reusable) generic component is available as source-file in a separate sub-folder
- These reusable components in specific GIT repositories can (and are) ALSO used in other reusable components using GIT submodule
- We version those reusable components using GIT tags and give them semantic versions -> 1.0.0, 1.0.1, 2.0.0, etc
So, the diagram below might show a small project:
entity liba.do_something entity libb.my_component
entity libsome.counter (v1.0.0) entity libmy.counter (v2.0.0)
entity cnt.add_value (v1.0.0) entity cnt.add_value (v2.0.0)
So, Project instantiates two components which are compiled in their respective libraries. Component 'do_something' is compiled into 'liba' and 'my_component' is compiled into 'libb'. Let's assume component 'my_component' is a GIT submodule, developed and versioned independently of the project and the sources are in a sub-folder of the project. This 'my_component' is a reusable component that can be used in different projects of the company.
This reusable 'my_component' component includes a 'counter' component with version 2.0.0 which is compiled into 'libmy'. The same 'counter' component, BUT version 1.0.0, is compiled in the project into library 'libsome'. Everything still ok, but now comes the problem:
The 'counter' component uses another component (entity+architectur) 'add_value' which is developed as part of the 'counter' component. It is in the same GIT repository as the 'counter' component. The 'counter' component instantiates this 'add_value' component directly and is (expected to be) compiled in library 'cnt'. However, as there are two different versions used in the project, the two different versions of 'add_value' cannot be added to the same 'cnt' library. One would overwrite the other, potentially causing synthesis errors or worse, undefined behaviour.
As we want to use GIT repositories to version reusable components - and not copy/paste project reusable components per project -, the above is a real-world problem with different solutions:
a) As soon as the above happens (two different versions of a component), modify the project and potential all other reusable components that use the 'counter' component so that they all use the latest version.
b) Make sure that 'add_value' v2.0.0 is compiled and only visible for 'counter' v2.0.0. Same for v1.0.0 of the 'counter' component
I don't like A as we need to modify components and is labor intensive and error prone. In my opinion, B is the way to go. This solution B is also used for SW development as the above is also a very common real word problem.
Today I started to read about the 'work'-library and I think it is the solution to the above problem. Instead of compiling 'add_value' into a 'cnt' library, compile it into the 'work'-library. This special library places compiled files in the library at which you instantiated the toplevel entity ('counter' in this case). This means that 'add_value' v2.0.0 is compiled into 'libmy' and 'add_value' v1.0.0 is compiled into 'libsome'. If you then instantiate 'add_value' using: ... entity work.add_value in the 'counter' component, everything should work as expected.
Are there any more solutions to the above problem?
PS: Different versions of the same component is very common. The 'counter' component might have a bug or you want to add extra functionality, in which case you want to fix/use in Project A but not in Project B. This means that Project A uses version v2.0.0 of the 'counter' component while Project B still uses v1.0.0. If Project B is also used in Project A (as a reusable component), you might get the above problem.
I hope it is more clear now.
02-22-2021 02:40 PM - edited 02-22-2021 02:41 PM
Wow, you really do have a complicated. Are "counter" and "add_value" really components in your library? Unless they have some special function I would never have a library of low level components like counters or adders. The lowest level we would normally have would be a FIFO or AXI stuff. When it gets larger than this re-usability usually comes in the form of packages with useful functions. RAMs can be infered quite easily. Re-use comes alot more useful for testbench utilities.
In VHDL, everything is visible from everything, but you have to access it via the appropriate libraries. But you have to ensure you dont put items with the same name in the same library. When Ive seen a code setup like yours before, the entities would have the library version in the name - eg. cnt.add_value_1_0_0.
Have every entity as a separate git submodule seems like a real pain to me. Thats a lot of repos to manage. I think a "reusable" repo might contain one or more libraries of reusable items.
Also, dont get confused with the work library. The work library is not a library itself, but the "current working library". Work can be mapped to any available library.
02-22-2021 11:14 PM - edited 02-22-2021 11:16 PM
Of course we don't have 'counter' and 'add_value' as a reusable component :), It was just to make my point clear. We have reusable components like:
These reusable components are generic and can be configured via generics. In the above list, the 100G Audio/Video Ethernet is a reusable component - it is used in several products of ours - and it includes other reusable components (like memory, SGDMA (Scatter/Gather DMA) and the internal control bus).
Also note that:
As for you last remark about the work library. That was exactly my point. It is indeed not a library. And because of that fact, it probably solves the problem I described above.