Saturday, January 7, 2017

The dream of HDL standard libraries

I was recently asked why RTL code is so unportable and why there aren't any standard components to use for common blocks like RAM and ROM. As this is something that I've been thinking about for a long time, and was part of the reason why I started working on FuseSoC, I started writing a long reply, but realized it was turning into a decent-sized blog post instead, so I decided to put it here instead. So here are my thoughts on the issue, why it is an issue and some ways to make it more manageble. My view on this is largely FPGA-centric, but much of it is applicable also for ASIC.

In the RTL world we have no standard libraries. Coming from a software world, this is something that very much expected to exist. There are plenty of more or less standardized software components and interfaces. A prime example would be the C standard library (libc), which is used by pretty much to some extent by all software written in C. It's portable, and the same functions can be used from the smallest microcontrollers to the beefiest server parks. Other programming languages have their own built-in libraries, but most of those are internally using libc deep down. So why don't we have the same thing for HDL code? Both VHDL and Verilog has been around long enough for this to materialize. I'm not sure why, but my guess is that part of the reasons is that the pool of RTL engineers are much smaller than their SW counterpart and that most RTL projects has been done in isolation at companies that hasn't seen the value of agreeing with other companies on a standard interface. Another part of the problem is the general ignorance that is often seen in the digital design field. One of the most commonly used phrases for HDL code is that "it's a hardware description language, not a programming language". Over the years, this makes me more and more annoyed, and I really wish that schools and companies started telling this to new engineers. No, it's not a programming language, in the sense that it gets translated to a circuit description (which is not true for simulations), rather than machine-language code. This, however doesn't mean that RTL engineers should ignore all the best practices that has been developed for software. Most of the general ideas from software can be applied directly to HDL code as well, some need interpretation and a few doesn't apply at all. Standard libraries is one of those useful ideas that can be applied directly to HDL code, so again, why don't we have them? Surely, someone else must have thought about this before.

Yes, there has been a lot of attempts to build standard libraries but it's hard to standardize something that is already implemented in a million ways, and from what I see, no one seems to be interested in using existing solutions. Part of this is due to the fact that there hasn't been an easy way to share code, and this is a big part of why I started with FuseSoC, so that it would be easier to reuse existing cores. I'd say that the constant reimplementation is actually worse than what it first looks like. Writing these things are not necessarily that difficult. The hard part is a) proving their correctness, which means both good test cases and testing against a multitude of target devices and b) usability, in form of documentation and easy integration into other projects. Most of the standard library contenders I have found falls short in both these areas, and just contain a code dump.

Just being able to reuse code more efficiently isn't a silver bullet. There are plenty of examples from the software world where similar functionality is being reimplemented over and over again. But it does help. Another issue is also the scope. What should go into this standard library? Really small things like registers and muxes? No, those are probably best handled by the HDLs themselves. How about FIFOs, RAMs, ROMs, SERDES, clock domain synchronisers? Yes, these are things that are common enough to be used in a lot of places while still being complex enough to benefit from the increased testing that comes with more users. How about even larger components like UARTs, SPI controllers and caches? While I personally would sacrifice my left pinky (this is a big thing for an Emacs user) to never see another reimplementation of a UART or SPI master, I think they contain too much configurability to be put this into a standard library. I am however a big fan of modularity, and I would be happy to see these components being built using elements from the still fictous standard library. It should also be said that there are some examples of standardized interface in the HDL world as well, mostly related to verification, perhaps since this is an area more closely tied to software development. UVM is an example of this for SystemVerilog, and OSVVM would perhaps be its VHDL counterpart.

So let's pretend that a large enough group of developers agree on a standard library interface, the next problem is the portability. Such a library would preferrably have both Verilog and VHDL implementations. The main reson for this isn't compatibility, as it is possible in most cases to API make the API language-neutral. This of course has the drawback that many of the useful features in each language can't be used as they have no direct translation in other languages. The main reason is instead that the state of mixed-language simulations are still not very good. Many commercial tools charge a lot of money for this feature (even though the situation has improved quite a bit recently), and the Open Source tools are still not good enough for mixed-language. Where does this leave all the fancy new HDLs such as Chisel or Migen then? In the great tradition of reinventing wheels, of course they have also created their own libraries for these things instead of reusing existing Verilog libraries. To be fair though, it's not as if there was any good libraries to build upon. At least none that could be considered standardized.

If we ignore this for a while and assume we have implemented the library in several languages, or perhaps only need one, the next issue is the target technology. Different FPGA and ASIC technologies use different primitives for things like memories, FIFOs etc. The good news is that many of these constructs can be described in regular HDL code, and the EDA tools will happily convert them to the correct target implementation. This should always be used as the first option if posssible. Unfortunately, I see so many cases of engineers generating a vendor-specific IP for the simplest things. This has several drawbacks. First of all, it's not portable. Definitely not among vendors, and in most cases, not even among devices of the same family. For some truly idiotic reason, it's also usually bound to a specific version of the EDA tool it was generated by, which means it becomes a mess of upgrades or downgrades whenever someone is trying to use it with another version of the tool. Depending on the format of the IP core, it can be a pain to use together with version control systems and it often requires manual changes and regeneration whenever the smallest parameter (such as FIFO depth) change. A classic example of trying to save some time on implementation which ends up adding a large cost instead on maintenance and reuse. Trust me. I've been through this too many times.

Not everything can be described easily, efficiently or even at all with vendor neutral HDL code. Examples of things that are closely tied to target technology can be clock generation, I/O cells or special macros. The good thing here is that if these features are needed, you likely already know which chip or technology you're targeting and can afford to lose portability. These things are not supposed to be in a standard library anyway. A good practice here is also to try to keep these things close to the top-level so that the core functionality can be more easily moved between different targets. Other blocks might be possible to implement in many different technologies, but can't be expressed as pure HDL, since the EDA tools can't map them in a satisfying way. In these cases, there need to be backend-specific implementations, possibly with a pure HDL fallback for some tools. This is a common approach, but it's not really standardized how to select the correct backend. Many RTL engineers uses VHDL/Verilog generate statements for this, IP-XACT uses the "view" mechanism to switch between different files and FuseSoC currently uses something similar to IP-XACT, with a more powerful mechanism being in the works, inspired by the idea behind Gentoo's use flags.

So to sum it up, I'd love to see more standard libraries, as I think it's a good idea. Unfortunately, it's hard to make this come true, both for technical and political reasons. This doesn't mean we shouldn't try, and as FOSSi Foundation now plays a role in aiming to foster collaboration and open standards we might have a better chance than before. This won't happen automatically however, and in the meantime there are a few ways to iteratively improve the situation. Follow these short guidelines and the world will become a much better place

1. Whenever you start writing new basic functionality, take a look around to see if there is something that can be reused. It might look like more work up-front, but further down the road it will mean less maintenance, better documentation  and hopefully fewer bugs for more people.

2. Use pure HDL whenever possible instead of relying on vendor-specific IP. Again, the benefits will come from less maintenance, and in this case also improved portability

3. If you decide to write your own code, document it, add testcases and publish it through LibreCores so that other people will find it. This improves the chances that your code will be reused and improved by other people.

4. Use FuseSoC! In most cases it's quite simple to put together a FuseSoC core description file to go together with you component. This makes it easier for other people to reuse your code if they are also using FuseSoC. It also makes it easier for you to reuse other code if that code is already packaged to be used with FuseSoC

Let's show those softies that we RTL engineers also can collaborate and build awesome things together!