Reusable VHDL IP in the Real World
By Matt Bridle, RF Engines Ltd.
Reuse has been an industry buzzword for years now. It is hardly a new idea, and probably goes back as far as the time when man first realised he could use the same fire both for keeping warm and for roasting his sabre‐tooth tiger ribs. When it comes to IP, reuse can be an extremely powerful way of saving resources and shortening project timescales.
At RF Engines, we find that reusing existing IP is very desirable. Not only does it save us development time and help us fulfil challenging delivery requirements, but making use of pre‐proven components also helps give customers confidence in our designs.
Another dimension to this is that multi‐FPGA designs are becoming increasingly widespread, and there are obvious advantages to having a chip‐level infrastructure that is reusable within the project.
Reuse, then, is clearly A Good Thing. However, in practice it has often proven surprisingly difficult to achieve. So it is worth bearing in mind a few principles and techniques that can be applied to make it more straightforward. Though it would be foolish to say that creating reusable VHDL doesnt require any extra effort, it frequently pays significant dividends in the longer term.
At the beginning of implementation, thinking about what else your component might be useful for in the future may not be the first thing on your mind. Sometimes its not obvious that your component has multiple applications, and that reuse should be one of your design goals. But even when you dont know what the component might be useful for in the future, if you take a step back, its often easy to see a few aspects of the design which can be written in a flexible way without too much of an overhead.
Wisdom from Kylie
You may spot an existing piece of your IP that you can re‐use with some adjustments for a new application, but that wasnt designed for reuse in the first place. However, as that great Australian philosopher once said, its never too late weve still got time. There is a temptation to simply copy the old design and change it, and sometimes if the changes are complex and pervasive this is a good option. But often, you can take the existing IP and revise it to make it more flexible though of course some regression testing is needed to make sure you dont break it in its original setting.
The nitty gritty
One issue that often arises in design for reuse is that many of the language features which make design for reuse easier are software‐style constructs. Hardware engineers may be wary of these constructs as they can obscure the relationship between the VHDL and the resulting implementation, but if used carefully they can be a great help. Here are a few examples.
Using a generic to set parameters such as the width of entity ports is common practice. It not only makes the component easier to reuse in other circumstances, but helps to document the code, since a meaningful generic name can be used instead of an apparently random number.
Any code which depends on the width of such ports or signals then also needs to be parameterised on the generic, e.g. to produce a reduction‐and of data_in above:
Generics are also useful to make different variants of a component. You can add a boolean generic and use an if generate statement to implement optional code, rather than writing a different variant of an entity.
Another useful trick is to gather collections of signals together using arrays. Signal processing applications often operate on blocks of data at a time, and the operations often have a strong regularly structure. Using arrays in this context can help extensibility and readability. For example, say you want to instance several copies of an entity to perform a signal processing operation, each working with one of a number of parallel data streams. You could write this as:
The width of the data vectors, or the number of vectors in a collection, can be changed simply by altering the relevant constant. Notice also the use of the range attribute, which yields the range of an array type, variable or signal. You could alternatively just use the explicit range 1 to collection_size instead of collection_typerange, but its a little more restrictive as it assumes that collection_types range starts at 1. Commonly in a signal processing application, once you have operated on each element of your data block, you will want to perform another operation on the results. For example, you could sum the results of from all the operations above using a process such as:
This is again very flexible as it doesnt rely on knowing the data width or number of results. The range attribute has been used again, this time to help us create an unsigned vector of the same dimensions as data_vec (which is a std_logic_vector). Of course, the code above could produce a very large adder chain and so in practice a more sophisticated pipelined architecture would almost certainly be necessary.
There are a couple of pitfalls to watch out for in this example. One is that VHDL requires the element type (data_vec) of the array (collection_type) to be globally static. That unfortunately means you cant use a generic to parameterise both the width of data_vec and the size of collection_type (hence the use of constants instead). In fact, collection_size could be a generic in the example, but data_width could not. There are various ways to get around this but none are perfect. One is to use a twodimensional array, which many synthesis tools now support. In this case, if we wrote:
then both collection_size and data_width could be generics. This can make it a little messier to access the data rows, so it is a trade off of flexibility against simplicity.
One other issue in this area is that, if data_collection_in/out had been entity ports, many synthesis tools would have broken them up into separate vectors, which could cause incompatibilities when doing gate level simulation. If you are unwilling to take this hit, you could create a wrapper which assigns the array elements to individual ports at the top‐level.
A final tip for making design reuse easier is to put a component declaration for your block in a package. Usually, you would have to place a component declaration in every architecture where your block is to be used, but if you create a package which contains a component declaration, then you save yourself and subsequent users the hassle:
Users of the block then simply need to reference the package, instead of redeclaring the component:
Is it worth it?
So is design or redesign for reuse really worth the effort? It may seem like a lot of work at the outset, but it can bring significant benefits in the long term. Or, looking at it the other way round, if you dont have reusable components, you could end up wishing you had. Remember, a stitch in time saves nine. A bird in the hand is worth two in the bush. Many hands make light work but too many cooks spoil the broth... well, you get the idea.
About the author
Matt Bridle holds an MA in Computer Science from the University of Cambridge. He has worked for RF Engines Ltd. since June 2008, as part of their digital design team creating high performance FPGA‐based signal processing solutions using VHDL. Prior to this, Matt was a Consultant at Doulos Ltd., where he taught and co‐authored training courses for the industry, specialising in VHDL design and verification as well as scripting in Tcl/Tk. He was also previously a Staff EDA Engineer at ARM Ltd., developing modelling tools and verification environments.