Wednesday, December 14, 2011

Immediate SV Assertions and Simulation

One of the reasons for playing around with an FPGA board was to get some experience with the full hardware development cycle (on an FPGA, at least) - something with which, despite my work as first an AE for an EDA company and now as a DV Engineer, I have had very little first hand experience. I like to say that what I know about project work I've learned through watching the light from the fire reflected on the cave wall: I've heard so many engineers describe this problem or that flow over the years, that I have a very good working knowledge about most phases of an ASIC project, but there is no substitute for first-hand experience.

One of the areas I am very well versed in is that of applying formal property verification, my bread and butter for the last eight years. And, living and working in the US, I am especially proficient in SystemVerilog assertions; my knowledge of PSL is rather rudimentary. And yet, as I was reminded the other day, there is always something new to learn.

In my experience, by far most SV assertions are concurrent as opposed to immediate. That is, they are typically standalone assertions of the form
prop_name: assert property (@(posedge clk) a |=> b);
Whether explicit or implicit, they always include some clock or other edge-triggered sampling event - which is normally not a problem as by far most blocks are not combinatorial anyway and so there is always a clock to grab. And even when I'm dealing with a combinatorial design - an ECC checker, say - I can normally get away with writing simply
corr: assert property (ec |-> data_out == data_in);
even if there is no implicit clock in the design, as I'm always using a formal property checker: Since the formal tools are always clock rather than event driven, they will typically just use their own internal clock as the implicit clock rather than error out. Effectively, this will sample the property all the time, that is, make it combinatorial which is exactly what I want.

This leaves the question of what to do if one wants to be strictly SV compliant (or just wants tool warnings to go away). I always figured that
always_comb corr: assert (!ec || data_out == data_in);
was effectively equivalent to the concurrent version above. I always preferred the former because it allows the full SVA syntax, including implication, whereas the latter only allows SV boolean expressions. In other words, I can write concurrent assertions to be more readable.

Which brings me to other day, where I had finished one of the simple BCD-related exercises in the lab exercises for my FPGA board: Given two four bit inputs, A and B, show A on the left-most LED segment (HEX3), B on the next (HEX2), and their sum on the last two (HEX1 and HEX0) - all in decimal. Plus detection of illegal (>9) input values and some other connections that aren't relevant to the point I'm making. Following the scheme above, I wrote the property
always_comb hex3_p: assert (!err || decode(HEX3) == A);
happily assuming it would capture my intent: Unless there's an input error, the value displayed on HEX3 will be equal to the value of A. Looking at the simulation trace, I'd gotten it right the first time, I just had a corner-case property problem at cycle 0 where the property was failing because all the signals were X. Reading up on always_comb, I saw that it was specifically activated at time 0 - unlike (by inference) always @*:
always @* hex3_p: assert (!err || decode(HEX3) == A);
Now the property passed cleanly. Just to double-check the setup, I tried removing the "!err" disjunct, expecting a fail. It still passed.

After some debugging, my suspicion fell on the automatically-derived sensitivity lists of always_comb (which was also passing except for time 0) and always @*. A quick scan through IEEE 1800-2005 didn't turn up any mention of assertions in that context, so it would seem it unspecified whether variables from assertions should be added to the sensitivity list or not. ModelSim, which I'm using at home, doesn't include them, while I noticed at work that both NC-Verilog and VCS do. Fair enough. Using an explicit sensitivity list solved the problem:
always @ (error,HEX3) hex3_p: assert (!err || decode(HEX3) == A);
 Notice, as I initially didn't, the lack of A in the sensitivity list.

The check for HEX2 also went through just fine, but when I got to the sum output, I was stumped: The property was randomly failing yet all the outputs looked fine. Exposing myself as a complete newbie, I had introduced a race condition which I had inadvertently avoided in the first two properties. I conceptually had two actions in my code: The computation of HEX3 from A and the comparison of HEX3 and A in the property. When A changes (and A is in the sensitivity list), both action blocks are activated, but the order of their execution is not guaranteed: If the assignment of HEX3 is computed before the assertion, the latter won't fail. If, on the other hand, the assertion is checked before HEX3 has been updated, it will. By leaving out A in the sensitivity list of the procedure containing the assertion I had neatly avoided the race condition as the property wouldn't see the intermediary step of A updating. The property checking two LED segments could not be similarly fixed as the race condition was already between the updates of the two LED segments and the property.

In my defense, none of this makes any difference in formal tools: Sensitivity lists are ignored as they are for synthesis and race conditions do not exist as properties are checked only once a consistent state has been reached, that is, once all changes have propagated. That's my story and I'm sticking to it.

After all this, it was clear that writing stand-alone immediate combinatorial assertions was not the way forward. To avoid race conditions, it seemed prudent to explicitly do exactly what the formal tools do implicitly: Introduce a clock external to the design and make the testbench synchronous to that. As it happens, I can't use concurrent assertions with the free ModelSim version available from Altera's home page, so I simply used an intermediate assertion in a synchronous always block:
always @ (posedge clk) hex3_p: assert (!err || decode(HEX3) == A);
I don't get the nice implication syntax, but at least I don't get any race conditions either and it behaves the same across all three simulators that I have access to. All I have to ensure is that the relevant signals are non-X by the time the first posedge of clk is seen.

Sunday, December 4, 2011

Setting up the Altera FPGA environment on a Mac

Here's a quick description of how I got everything set up on my Macbook Pro to be able to program my Altera FPGA board. Everything is reasonably well documented online, but since I still had to do some amount of debugging and searching I thought I'd document my, as far as I can tell, successful installation.

First off, all I wanted was to be able to do the FPGA development on my laptop with a minimum of fuss and money. Since Altera's Quartus software runs on just two platforms - Windows and Linux - it was pretty clear that I was going to run some virtualization software. At my old job we used VirtualBox and it seemed to work fine, so that's what I went with.

Between installing Windows or Linux there didn't seem to be much of a contest, but just to spell it out:

  • Convenience. Linux comes with all the tools that I'm used to, out of the box. With Windows I would have to go through at least some hassle to find and install shells, editors, revision control software, etc.
  • Performance. Occasionally I had to run Windows at my old work, on a Macbook comparable to the one I have now, and while Windows 7 worked flawlessly per se in VirtualBox, it had some clear performance problems. I never saw significant issues with the Linux image I ran.
  • Cost. As I have no other purpose for Windows, it would seem unnecessary to shell out money for it when Linux is supported by the Altera software and is often free.

So, Linux it was. According to Altera's OS requirements, I now had the choice between RHEL, SUSE, or CentOS. This time I really had little preference, but CentOS is free, and I remember using it some jobs ago with no particular problems. Only caveat is that the ModelSim version distributed by Altera is not officially supported on CentOS. I'm not sure what that is about as my understanding is that CentOS is RHEL without the trademarks, and ModelSim runs just fine in my setup. At least, the GUI starts fine and I'm getting no warnings or errors in the x/term. I guess I'll know better once I actually put a test bench through it.

As for the versions, I just picked the most recent releases, except for CentOS where I stuck with CentOS 5 as it was explicitly supported. At the end, I installed

  • VirtualBox 4.1.6
  • CentOS 5.7 i386
  • Quartus II 11.1 Web Edition
  • ModelSim-Altera Starter Edition 10.0c

After the initial VirtualBox installation, I thought I'd save some bandwidth by using the CentOS net installer, but for some reason it barfed in the middle of the process, so I decided to download the DVD release instead (fewer files than the CD release), and that did the trick - I asked for a Desktop setup with all the default settings and got exactly that without further complaints.

At this point, I had my system running at a paltry 800x600 resolution, and no way to extend it. Enter the  VirtualBox Guest Additions. Installing them from the menu mounts a CD in CentOS, switch to root and run the VBoxLinuxAdditions.run script. Voila. I bumped up the allocated Video Memory to 64MB in the VirtualBox settings for good measure and haven't had a resolution problem since.

Next, I downloaded the ACDS (Altera Complete Design Suite) installer and had it rip at installing the Quartus and ModelSim software - everything went smooth until the very end where it told me it had had some issues with a couple of steps and asked me to manually do them after the install. Trying to do so resulted in some mysterious

quartus: error while loading shared libraries: .../altera/11.1/linux/libccl_err.so: cannot restore segment prot after reloc: Permission denied

error message. As it turned out, the solution was either to turn off SELinux (against the recommendation of CentOS) or change the context of Quartus' shared libraries (probably also against the recommendation of CentOS). I chose the latter at it was at least just a local digression:
chcon -t textrel_shlib_t .../altera/11.1/linux/*.so
I could now run Quartus without incident, look at tutorials and all that. The only thing missing at this point was the ability to actually program the device. As the board is configured through a USB cable it meant that I first needed to get USB devices fully connected from the Mac to CentOS. To this end I first installed the VirtualBox Extension Pack. Next, I shut down CentOS and went to the Ports -> USB section of the VirtualBox settings, selected "Enable USB 2.0 (EHCI) Controller" and added the "Altera USB-Blaster" device to the filter. Back in CentOS, I switched to root and added
none /vbsusfs usbfs rw,devgid=500,devmode=664 0 0
to /etc/fstab. I simply hardcoded my personal group ID (500), I'm going to be the only user anyway.

After starting Quartus again, I was still unable to connect to the device, being told that I had "insufficient permissions on some ports". Again, the answer was to be found at Altera: As root, I created a new file, /etc/udev/rules.d/51-usbblaster.rules, with the following content:

BUS=="usb", SYSFS{idVendor}=="09fb", SYSFS{idProduct}=="6001", MODE="0666"
BUS=="usb", SYSFS{idVendor}=="09fb", SYSFS{idProduct}=="6002", MODE="0666"
BUS=="usb", SYSFS{idVendor}=="09fb", SYSFS{idProduct}=="6003", MODE="0666"
BUS=="usb", SYSFS{idVendor}=="09fb", SYSFS{idProduct}=="6010", MODE="0666"
BUS=="usb", SYSFS{idVendor}=="09fb", SYSFS{idProduct}=="6810", MODE="0666"
After that, I was able to successfully program the device.