Josh
Josh

Reputation: 3655

How to make the library work work?

There has been some concern among my peers in using the name of the current working library as work (an aliased name pointing to the current library) instead of explicitly by name (e.g. mylib).

For example, if you are referencing another design unit in the same library, you could do:

my_inst : entity work.my_design

or

my_inst : entity mylib.my_design

I feel that using work is more flexible since it is not dependent on what you name the library that the design is compiled into. You could rename mylib to myawesomelib and it would still work.

One clear example that I can think of the advantage of work is if you typically compile design units and testbenches into the same library. In your testbench, then, it would always be safe to reference your UUT (design) through work.

One disadvantage is that the design will stop working if (as in example above) my_design is no longer co-located in the same library; if either my_design or any design that uses my_design through work is separated, the design will be broken and references must be updated.

Are there other significant disadvantages to using work over an explicit library name? In cases of hierarchical use of work is there possible source of confusion for which library is the "current working library"?

For example:

mylib1
   (top_design.vhd)
   ...
   my_inst1 : entity mylib2.my_design
   ...

mylib2
   (my_design.vhd)
   ...
   my_inst2 : entity work.my_sub_design
   ...

When compiling top_design.vhd, is there any confusion about work reference within the included design from mylib2? Or since my_design.vhd has already been compiled into mylib2 there is no confusion?

Footnote: Never name your libraries work.

Upvotes: 4

Views: 7499

Answers (4)

Paebbels
Paebbels

Reputation: 16231

I'm one of the authors of the PoC-Library. We decided to use PoC as a hopefully unique VHDL library name. All entities and packages are compiled into this single library. Because PoC comes with quite many files (~ 100) we decided to divide PoC into sub-namespace (maybe VHDL will support such a feature one day ...).

Example:
A divider component is categorized into the sub-namespace arith for arithmetic modules. The entity is (virtually) referenced as PoC.arith.div in our documentation. The entity is named arith_div and located in a file called arith_div.vhdl. One package for components, types, functions and constants is provided per sub-namespace: e.g. arith.pkg.vhdl. An entity can be instantiated in two ways:

  1. Using the PoC.arith package from arith.pkg.vhdl
    myDiv : arith_div

  2. Using the entity keyword
    myDiv : entity PoC.arith_div

The sub-namespace hierarchy can have up to 2 levels. An example is our On-Chip RAM PoC.mem.ocram.* with different port variations: sp, sdp, esdp, tdp. To shorten the entity name, we only use the last sub-namespace as a prefix in the name, e.g. ocram_tdp. A sub-namespace name must be unique.

Mostly all instance references use the entity syntax: myInst : entity PoC.xxx_yyy.

So yes, we expect our users to compile all sources to PoC :).

How do we organize testbenches, docs and so on?

In contrast to @Russell's schema, PoC uses the same directory structure for each facility:

o-netlist (precompiled netlists for a specific FPGA device)
|   o-<FPGA-NAME>
|     o-mem
|       o-ocram
o-sim (waveform configuration files: *.wcfg, *.wdo, *.gtkw)
| o-io
| | o-iic
| | o-uart
| o-mem
| | o-ocram
o-src (sources as *.vhdl or *.v files)
| o-io
| | o-iic
| | o-uart
| o-mem
| | o-ocram
o-tb (testbenches as *.vhdl or *.v files)
| o-io
| | o-iic
| | o-uart
| o-mem
|   o-ocram
o-xst (synthesis instruction to generate netlists of components)
| o-mem
|   o-ocram

Upvotes: 0

lasplund
lasplund

Reputation: 1440

I think this is one of those questions which are interesting because there is no clear answer. It depends. To me this depends on project context but also personal preferences.

@Josh. I don't think the risk that you may have to change work references when restructuring libraries is an argument against using work. This is also true for named references.

@Russel. Using packages instead of direct instantiation still leaves you with the question what x in use x.uart_pkg.all; should be. You'll have fewer references though (at the expense of more code in the package).

@Kevin. I agree that mylib.foo is more explicit but I don't think I've been in a situation where I know which foo that is compiled into mylib but I'm unaware of that the referring file is also part of mylib, i.e. I would have been confused by what foo in work.foo is referring to. I'm not saying it can't be like this just that I've never experienced it.

By now you have probably guessed that I prefer using work. One reason is that I think modularity is good design and that implies avoiding dependencies. If I can avoid being dependent on the name of the library I'm compiled into I reduce the risk of code changes if library names need to be changed. I agree that library name problems are not very common if you use good descriptive names of your libraries but it still happens, at least to me:

  • Name collision with external libraries do happen and when they do it's much better if you can handle the situation without changing code. Especially if you decide to remove named references from their code. They may make new releases such that you have to do it all over again.
  • Sometimes there is no real collision but the external library is named with a non-established abbreviation and you want a more descriptive name. Sometimes that abbreviation collides with one of your own with a completely different meaning.
  • I even had my vunit library hijacked by VHDL 2008 when that standard made vunit a reserved word.

There are also all the projects where your strategy doesn't really matter. For example, small projects where you do all the coding yourself and compile everything into a single library because it's simple. However, I would still use work since that takes away some library statements and makes things even more simple.

Upvotes: 3

Kevin Thibedeau
Kevin Thibedeau

Reputation: 3421

I avoid using work because it hides the context you're expecting identifiers to be found in. Having the the library name included such as with mylib.foo makes it explicitly clear what foo you're referring to as opposed to work.foo which is somewhere within the current library, whatever that is. If you want to refer to foo from outside of its library you cannot use work anyway so you could end up with the same object referred to by different names depending on where you are in the hierarchy.

The flexibility of being to change library names at will isn't particularly useful in the real world and just creates needless complexity. This is further compounded by the practice of having the default library named work and the different ways tools deal with that. Pick a good library name and stick with it. I find work ends up being a crutch for those too lazy to partition reusable code into logical divisions. i.e. Just dump it all into work and hope for no conflicts.

Upvotes: 2

Russell
Russell

Reputation: 3465

Oh work. How do I hate thee? Let me count the ways.

  1. Code is compile order dependent when everything is in work. The last thing compiled is what sticks.
  2. Code lends itself to redundancies. Every single constant must be named something totally different, which is nearly impossible in large designs.
  3. Lends itself for components to be declared in multiple places. These component declarations often conflict!
  4. I've seen the same constants and types defined in multiple package files, over and over again, again with conflicts.
  5. NO ABILITY FOR LEVERAGE

Please, please, please USE LIBRARIES! They're there for a reason. It allows your code to be much more organized, flexible, and portable.

Organization

A library should live in its own folder. I like to have everything under one top level folder, e.g. UART. Under that I'll have the 4 directories: build, source, sim, docs. This way, your simulations and your documentation all travels with the library code! How sweet is that!? You don't need to worry about where to find that silly UART sim, you'll always know where it is. BTW, this means that self-checking Testbenches are mandatory! I stand for nothing less.

Flexible and Portable

If you're writing code for one project, you'll make assumptions. "Oh my clock is 25 MHz, so I can make this a constant." But if you write your code for a library, you realize that you're making bad assumptions. In the UART example, you need to make your baud rate a generic not a constant. This makes your code more flexible and easier for others to use.

Package File

There should be one package file per library. All of your components should be contained in this package file. This way, if your entity changes, you only need to update the component in the package file. Package files should contain all constants, functions, and procedures that are used in that library. Again this allows you to change something in 1 place.

Conclusion

Libraries are the way to make thoughtful, reusable, easily portable code. I cringe when I see all code compiled into work.

Upvotes: 5

Related Questions