When building a large FPGA design, there comes a point when you have to decide where to put the dividing lines between modules. Deciding where to draw the boundaries is a bit of an art.
You don’t want too much hierarchy as then each module does only a tiny task and it’s hard to get a big picture view on the functionality. If you put everything into one big blob then you get lost amongst it all, there’s no opportunity to reuse code, and it’s a nightmare to test and debug.
Another consideration is the number of signals you have interconnecting modules. If those signals also dive deep into the hierarchy, then each time you add a signal to one of the module interfaces, you have to replicate it and wire it up in each container module on the way down.
Pushing signals around
In terms of routing signals around, records can help with that: if you want to push an extra signal down the hierarchy, you just add it to the appropriate record and recompile. However, make sure you package things together sensibly – by function is often good. Not just “all the signals to this entity go in one record” as then you can end up with all the signals in the design in one big record!
A more modern approach is presented by Sigasi who have developed the Eclipse IDE into a VHDL development environment. This (as I understand it) allows you to drill signals through the hierarchy semi-automatically. I say “as I understand it” as when I tried the tool in its beta phase, it couldn’t copewith some of the apparently unconventional (but legal) HDL I write!
Testing is a huge part of the effort of any design, and this presents quite a useful way to ascertain where to split things up. “Things that are easy to test” is a good boundary in my experience.
For example – say you want an image processing system which does an “edge detecting” function, then does a “peak detecting” function on the results of that and then a “assign (x,y) coordinates to each peak we detected” function.
You could stick all of that into one big block of code, but when you get the wrong x coordinates out of the far end, you have no idea which sub-function went wrong.
Instead you design three blocks, one for each of the functions, and test them individually. You can get each part right in isolation.
Then when you wire them all together and test it, and it doesn’t work first time (!), you only have to look at the higher-level integration to see what’s wrong.
It may sound like more work this way (writing testbenches doesn’t feel like productive work, especially when you’re starting out). But once you get proficient (which means practicing, just like playing your scales on the piano :), you can do the testing very quickly because each individual test is fairly simple. And you can push your design to all the corner cases, which is much more difficult when testing the whole thing all together.