If you’re looking for a very quick tutorial on Verilog, check out our Quick Verilog tutorial. It has a short introduction to why you should consider Verilog as a hardware design language and then jumps into Verilog syntax and design flow.
One question that occasional pops up from customers is “What Verilog code examples do we ship with our simulator?”. VeriLogger ships with all the Verilog source code examples from two popular Verilog text books: Mano’s Digital Design and Minn’s FSM-Based Digital Design (with permission of the publishers). These examples are located under C:\SynaptiCAD\Examples\Examples_Book.
We also include some open-source examples (mainly taken from opencores.org) in C:\SynaptiCAD\Examples\VeriLogger.Â Finally, there’s some TestBencher-generated test bench examples located in C:\SynaptiCAD\Examples\TestBencher\Verilog (these last examples require a TestBencher license to fully be generated).
Because Verilog code has communicating concurrent processes, it’s much easier to accidentally write code that results in an infinite loop, and it’s harder to identify the cause of the infinite loop.
We’ve added an option to VeriLogger to automatically interrupt the simulation after a large number of delta cycles have taken place without advancing simulation time. By default, the delta cycle limit is set to 1000, but it can be changed using the simxloader option, –scd_max_delta=<limit>.
Here’s an example of an infinite loop between two zero-delayed assigns that would be interrupted automatically:
wire bw = cw === 1'bx ? b : cw+b;
assign cw = bw;
initial Â #1 b = 1;
Delta cycles occur whenever a process is scheduled. Since the delta limit cycle is finite, it’s possible that some code could hit the limit even when there’s no infinite loop. For example, the code below would trigger the limit because the #0 time delay would cause the process to be rescheduled each iteration through the loop.
for (i=0; i < 4095; i = i + 1)
#0 ram[i] <= 0;
Of course, this code is not good code for many reasons (including efficiency, since it forces unnecessary reschedulings of the process) and should be written as below (which won’t trigger the limit):
for (i=0; i < 4095; i = i + 1)
ram[i] <= 0;
When the delta limit is hit, the simulator will interrupt the simulation and place the user at the simulator’s interactive command prompt so that the source of the infinite loop can be analyzed and debugged. Simulation can be resumed from the command prompt using any of the normalÂ runÂ commands.
Designs often reuse a common set of our Verilog source files. For example, when you’re designing using a particular family of FPGAs or ASICs, you will need to compile the vendor-provided Verilog source files for those parts. Instead of recompiling these files for each new design, you can compile them once into a symbolic library (sometimes referred to as a compiled library), then just reference that symbolic library in your designs. With this approach, you only need to recompile the vendor’s source files when you update your simulator to a newer version.
Cells and Snapshots
A symbolic library contains cells and snapshots. Cells represent the definitions for objects such as modules and User-Defined Primitives (UDPs) that are created when source files are compiled into a library. Snapshots are created during simulation elaboration and represent a fully built â€œsimulationâ€ that can be executed.
Within a library, cell and snapshot names must be unique and any attempt to compile a cell into a library that already contains a cell of that name will cause the original cell to be overwritten. However, cells and snapshots of the same name can exist in different libraries.
Creating Symbolic Libraries
In most Verilog compilers, a symbolic library is given both a logical name (e.g. “work”) and a physical name that indicates it’s storage location (e.g. “c:\myproject\scd_work”). The logical name is used when referring to a symbolic library in the source code or when passing a library name as a command-line option to the verilog compiler, so that the source code and script files don’t have to depend on the library’s location on a particular computer.Â The physical name is only used to specify the location of the symbolic library (and typically only when the library is first created).Â The mapping between these two names is specified using a command-line library tool that can create and manipulate symbolic libraries and the resulting mappings are stored into a “map file”. In simx, for example, this command-line tool is called simxlib. In both Mentor Graphics ModelSim and Aldec Active HDL, the library tool is called vlib.
Work: the Default Symbolic Library
By default, sources files will be compiled into a compiled library with the symbolic name of “work“, unless a different destination library is specified via the
--work <destination_library_name> compiler option. Also by default, the work library will be mapped to a subdirectory of the current working directory called scd_work, unless a map file is passed to the compiler that specifies a different physical path name for the library. Â This default mapping will be written into a default map file called scd.lib which simx will subsequently load automatically whenever simulations are run from this directory.Â The above defaults allow simx to be used without having to worry about symbolic libraries, if you don’t plan to take advantage of them to avoid re-compiling shared Verilog code.
By contrast, some compilers require you to create at least one symbolic library using their library tool before you can compile code with these simulators, so if you use BugHunter to compile code with ModelSim or ActiveHDL, you will note that it first launches vlib to create a work library before it launches the actual Verilog or VHDL compilers (aka vlog or vsim) for these simulators.
Example Commands for Creating and Using Symbolic Libraries
To compile test1.v and add it’s contents to the default library called “work”, you can either directly specify the work library with the -work option:
simx --work work test1.v
or let the simulator just use work by default:
In the above commands, the work library will be automatically created in a subdirectory called scd_work if it doesn’t already exist.
To create a library with the logical name vendor1 located at c:\vendor1 and compile a set of files into this library, use the following commands:
simxlib --create c:\vendor1
simx --work vendor1 test1.v test2.v test3.v
The above commands also create a scd.lib file in the current directory with the following contents:
DEFINE vendor1 c:/vendor1
This line says that the logical library name vendor1 maps to the physical library located at c:\vendor1.
Whenever you’re working with a large Verilog design, there’s likely to be a significant use of params (and localparams), especially when you’re stitching together IP blocks from one or more third party vendors. Params are often defined as mathematical expressions and a param’s final compiled value can often be quite difficult to figure out just by looking at the Verilog code because the expressions are scattered throughout the design hierarchy.
A parameter with an undesired value can lead to both very obvious errors (mismatch in size of a wire to a port) or to very subtle errors (e.g. a parameter used to count a number of clock cycles before reporting an error is set too large to ever get triggered). For this reason, it’s a good idea to verify the values of parameters at the interfaces between code you write and any third party IP you’re using prior to running simulations.
The quickest way to verify the parameter values is to compile (build) the design, then navigate through the resulting graphical design hierarchy tree which shows the computed values of the parameters for each IP instance. You can search for all instances of a module in a tree by entering the name of the module prefixed by a “.” (e.g. “.foo” where foo is the name of the module) in theÂ quick search box. Under each instance node in the tree is a sub-folder called Constants that lists all the top-level parameters for that instance and their compile-computed values. You can quickly scan this list to look for any parameter values that appear to be out of whack.
One final note: scanning through the computed values for internal params in a 3rd party IP is also useful in gaining better insight into how the IP works as key design details are often abstracted into param values. This can help you avoid trying to use the IP in a way that it wasn’t coded to work properly.
Verilog PLI (Programming Language Interface) applications and SystemC simulations can now be compiled and simulated from the BugHunter graphical interface. It’s also possible to run the resulting SystemC simulations in parallel with a Verilog simulation.
It’s easy to accidentally introduced “races” into Verilog, especially when you’re working with just one simulator. Since the Verilog language purposely doesn’t specify a particular order for execution of parallel process blocks, these races will frequently lead to different simulation results when a design is simulated by simulators from different vendors. VeriLogger has always been pretty useful for detecting simulation races, because our BugHunter GUI allows you to switch back and forth between our simulator (Simx) and 3rd party simulations (e.g. ModelSim, NcSim, ActivHdl, and VCS), making it easier to find such races. But this still required you to have access to other simulators to detect the races.
Over the past few months, while working with some of our customers who were “qualifying” our simulator with their existing designs, several such races have been found in the designs. Each time we encountered a simulation difference between us and a 3rd party simulator they were using, we had to determine whether the problem was a bug in our simulator, a bug in the other simulator, or a race in the design.
To speed this process up, we added several new command-line options to enable our simulator to change our default ordering of process execution and also to handle a few cases where the Verilog Language Reference Manual was vague enough about how a situation should be handled that different 3rd party vendors have chosen different ways to handle those siutations. By default, our simulator most closely models the process ordering used by Cadence’s Ncsim, so we added options to match the ordering used by Mentor’s ModelSim and Aldec ActiveHdl. We also added an option to randomize the ordering which given a few simulation runs should generally detect a problematic race in just about any design. The new options are:
–scd_invert_queue: inverts order in which “same priority” events in the event queue are evaluated.
–scd_randomize_queue: randomizes the order in which “same priority” events are evaluated.
–scd_mtilike_queue: evaluates event queue similar to ModelSim/ActiveHdl.
–scd_immediate_sensitivity: makes event control statements at the beginning of a process immediately sensitive after simulation initialization.
–scd_mtilike_dist_functions: makes $random and $dist functions behave like ModelSim/ActiveHdl instead of like NcSim.
What all this amounts to is, although our original goal was to make it easier to qualify our simulator as being functionally correct, we’ve ended up creating a new way to quickly detect races in your designs, as well. Finding these races can save you a lot of headaches down the road, especially if you sell IP to customers who use different simulators from the ones you normally work with!
As expected, we didn’t see nearly the performance improvement on Windows that we did on Unix. Average speed improvement was probably 3% or less on our benchmark suite. However, the 64bit simulator is stillÂ useful when you’re working on a very large design that won’t compile or run within the memory limits of 32-bit Windows (either 2GB, generally).
We recently released a 64-bit Linux version of the simulation engine.Â The main reasonÂ for this port was to allow the simulator to handleÂ extremely large designs (32-bit applications are limited to 3GB on Linux and while this is enough for most designs, it’s a definite limitation for some of today’s large designs).
However, we were surprised and pleased to find that the 64bit simulator runtime executes about 30% faster than the 32bit version running on equivalent hardware.Â We also see a decent speedup during the simulation generation process (e.g. compiling the Verilog code into a simulation). 64bit mode does use a little more memory since pointers are 64bits rather than 32bits in size, and this had been one area of concern to us when starting the port, but in practice we found this had little impactÂ on memory usage by the simulator.
We’ve not had a chance yet to fully explore all the reasons for the speedup, but initial research has turned up several reasons for this. First, in 64bit mode, about twice as many registers are available to the compiler, allowing commonly used varaiables to be kept inside the CPU longer. In addition, since 64bit Linux is less register constrained, it uses a different function calling convention (known as the __fastcall convention)where most function parameters can be passed in registers rather than on the stack, reducing the overhead of function calls. And, finally, 64bit integer operations (such as time value calculations in Verilog) can be done in a single operation, of course.
If you’re like me, you’d like to know more about exactly why we see such a dramatic speedup. Well, it’s on the list for further investigation, but that work is pending right now till we finish our 64bit Windows port. We’llÂ revisit this subject then, andÂ in the meantime I hope to be able to reportÂ on what kind of improvement we see for 64bit Windows. We’re expecting less improvement here, since the 64bit Windows version of the fast-call coventionÂ only passesÂ the first four parameters of a function via registers.
We’ve made a change in the way we build simulations to eliminate multiple warnings that used to be generated by some firewall software (the most annoying being the built-in firewall that comes with Windows, because itÂ even warns about completely safe local traffic between programs on your system). We no longer create an executable called simxsim.exe each time a simulation is built that needs a new validation by the firewall (for details, see my previous post about firewalls).
Now when you build a simulation, we create a shared library called simxsim.dll (or simxsim.so on Unix), instead. To perform a simulation, an executable program called simxloader.exe is run, and this exe loads and runs the simxsim shared library that represents the actual simulation. Simxloader doesn’t change, so once it’s been validated once by your firewall, it doesn’t have be revalidated again every time you build a new simulation. Peace between VeriLogger and overly picky firewalls has finally been declared!
If you’ve read the previous post on how the various programs communicate in Verilogger, the only change is that where it previously read simxsim.exe, now it should read simxloader.exe (simxloader.exe is installed in the bin subdirectory of the SynaptiCAD installation directory with our other pre-built programs). The Simxsim shared library is built in the current working directory (the directory where your project file is located if you’re building with the BugHunter debugging GUI).