October 22, 1998
C modeling accelerates HDL-system design
C modeling's software models and benchmarks make it a powerful tool for
enhancing HDL design and verification. You can use C models to evaluate early
architectures and to verify intermediate and final system-simulation results.
Tom Balph And Pat O'Malley, Motorola Semiconductor
Verification is a major issue in any large Verilog-based chip design. How do you know
that the results of your simulations are correct? And, if the results are incorrect, how
do you know where the problem is? You'll find the design process even more complicated if
the chip's pins give little visibility when you input data, the chip processes this data,
and then you see only the results. An excellent approach is to compare the design's
results with a good known reference model.
A C program is often an excellent choice for the reference model. Designers still do
most initial system modeling and algorithm development in C, despite new tools and
languages targeting these tasks. A C model is likely to be the first version of any new
standard, and you frequently have good known C programs already available for use as a
reference core.
It usually takes less time to develop a working C program than it does to develop good
HDL code. The C language has more powerful math capability. Furthermore, the C programmer
worries about only the data results rather than HDL-design concerns of timing and actual
hardware implementation. Regardless of the starting point, you can modify C-model programs
to verify intermediate results as well as functional simulation results.
C programs for design definition and verification fall into three major categories:
algorithmic, bit accurate, and cycle accurate. An algorithmic, or traditional, program is
most valuable for early function development and higher level design issues. Because you
can write C models quickly, they can provide an effective means for early decision making
that affects how you develop HDL code. Examples of tasks that you can evaluate in C before
committing them to HDL are hardware partitioning, intermediate-storage requirements,
arithmetic precision, and algorithm development. You can also easily iterate design tasks
to speed system development. However, possible mismatches between C arithmetic and
hardware arithmetic generally make the algorithmic C model a poor choice for an
HDL-verification reference.
The bit-accurate C model is the preferred reference for HDL verification. You have to
hand-code all of the arithmetic functions in the C model so that the C and HDL arithmetic
results are exactly the same. The primary issues a C model must address to achieve bit
accuracy are the order of operations, the number of bits, rounding, and saturation.
Bit-accurate C models require more development time than traditional C models and often
use the algorithmic model as the starting point. However, the initial traditional model
still provides a valuable check against which you can compare the bit-accurate model.
Cycle-accurate C models have output values that agree exactly with the HDL model for
every clock cycle. Cycle-accurate models have little advantage over bit-accurate models in
verifying results. Turning a bit-accurate model into a cycle-accurate model can be trivial
or a great deal of work, depending on your design. As you plan the C-model architecture
and think about how routines have to run, the impact of cycle accuracy on your model will
be obvious.
Usable algorithmic models
If you base your design on standards such as JPEG or MPEG, C models already exist and
are readily available. They aren't exactly what you need, but they provide valuable
starting points that significantly cut C development time. The Internet is a good place to
start looking. Granted, there is much questionable code on the Net, but there is also some
good code from reputable sources. Sometimes you can get C code from the organization that
wrote the specification. For example, Dolby Laboratories (www.dolby.com)
provides a C model as part of a Dolby-digital development agreement. Developers write many
of these C models to promote implementation of a standard, so they are available free or
for a nominal charge. Good designers write trustworthy code well enough to make
modification practical. If the C code you obtain is written or documented so poorly that
modification seems impractical, reconsider whether you can trust it as a reference.
Regardless of where you obtain the starting C code, you will end up modifying it. C
programs written as software implementations of a particular function or algorithm seldom
make good verification reference models without significant changes. The modifications
depend heavily on the way someone has written the HDL and reflect the HDL structure, so
the C programmer should be a member of the design team. Realize that once you start
modifying the C code, you must be prepared to take ownership of the code. You can expect
little or no support from the original authors for code you modify.
Another issue to consider before starting on the C model is how many ways you will use
the model. Consider a design with an embedded processor. Will you use the C model to
change the design functionality by adding new code for the embedded processor? In this
case, you may want to emulate the embedded processor within the C model so you can try the
new processor code on the C model. In other cases, you may want to have the C model and
the embedded-processor code work in different ways, as long as they both produce the same
results.
Regardless of where you start, use the first models developed to evaluate the
algorithms, arithmetic requirements, and design structure. You can do this task totally in
a C environment before you write any HDL code, with the results affecting the
specification for the design to be implemented in an HDL. You should verify the modified C
model against the original C model you obtained. You can often achieve verification by
substituting one bit-accurate routine at a time into the original code to keep the
differences between the two C models small and easy to inspect.
Modifying the model
Early design evaluation is important, but the primary benefit of a C model is to use it
as a reference that you can compare with the HDL design during simulations. As an HDL
testbench exercises your HDL design, it compares the results it sees with reference
results generated by a bit-accurate C model and automatically flags any differences.
Constructing a relatively simple, self-checking testbench by using the C model as a
reference is straightforward. Such a testbench makes verification more efficient and much
easier to automate for regression testing. Someone still has to analyze simulations that
fail, but you can automate the decision on whether a simulation passes or fails.
The people who write the HDL code should not generate the bit-accurate C model. A
separate effort on the C model provides a second opinion on the interpretation of the
design specification. You should tightly couple the C-model and HDL teams, with frequent
dialogue between them. C-code development normally occurs much faster than HDL development
does. You can check out the C model before system-level HDL simulations; C- and HDL-model
mismatches indicate HDL problems.
The bit-accurate model is normally derived from the algorithmic model and may require
twice as much code as a traditional C-code implementation. Arithmetic functions and code
partitioning are two primary considerations when writing the bit-accurate model.
Arithmetic functions in the C model must be hand-coded so that both the C- and HDL-
arithmetic results are exactly the same (for example, the same bit widths, rounding, and
saturation). Listing 1 shows an example of rounding for
the bit-accurate model versus that for traditional code. It is also a good idea to
partition C-code functionality to match HDL-architecture partitioning.
Consider a JPEG function as a partitioning example. Simple JPEG encoding consists of a
2-D forward discrete-cosine transform (DCT), quantization, and Huffman encoding. The
forward DCT is math-intensive. HDL arithmetic and standard C arithmetic may differ
slightly, depending on whether the C program uses fixed- or floating-point math. One of
the JPEG conformance tests is that the hardware forward-DCT results should be no worse
than 51 LSB compared with a C double-precision, floating-point implementation. However,
the Huffman step that occurs later in the JPEG-encoding process produces radically
different output values given minor differences in input values. It is impractical to
construct an HDL testbench that can verify the Huffman output results of an HDL model
against a C model unless the Huffman input values are the same for both implementations.
Again considering the JPEG example, it is desirable to check the intermediate results
of simulation at these functional boundaries: DCT, quantization, and Huffman encoding. If
you partition the bit-accurate C-model code to reflect this structure, the testbench can
isolate problems specific to the functional block causing the error. You can greatly speed
debugging by identifying the flawed block instead of just checking final results.
Figure 1 shows a basic HDL-verification flow diagram
for the JPEG example. The same set of input data stimulates the C and HDL models. The
verification compares the simulation results of the C model with those of the HDL model
and flags differences between the models. With this approach, the HDL testbench is
self-checking and relatively simple. (Self-checking means that the testbench either
reports that the simulation executed without error or reports what errors did occur.) The
primary task in the testbench is to compare HDL- design results with the files that the C
model generates and to report any differences.
It is easier to run the C model independent of the HDL simulation and to store results
in files for use by the HDL testbench than it is to have the C model execute as part of
the testbench. In addition to data results, the C model can also generate control data for
the HDL design and store that data in a file. If you construct the bit-accurate C model to
match the HDL architecture, you can then generate intermediate-results files and isolate
simulation errors at the module level.
As previously mentioned, it is easiest to run the C model as a separate first step,
store the results files, and then use the results files in an actual HDL simulation. The
results files can provide a stimulus, such as control data, if required, and the actual
results you use for checking. You can import the stimulus and results-file data directly
into the HDL-simulation environment for checking.
The authors' scripts suit most Verilog simulators, such as Cadence's (www.cadence.com) Verilog-XL and synopsys' (www.synopsys.com) VCS. Verilog provides at least two
ways to import file data. The simplest way is to build an input-source module based on a
memory model. Once the module is instantiated into the testbench, the program calls a
$readmemb or $readmemh task to read and load data from the specified results file into the
memory model. Consider Listing 2, which illustrates the
definition of a module decdata used to import data. The code defines a 32-bit memory
block, mem, to hold the imported data. A counter increments the mem address every time the
signal image_din_valid is active-high.
The testbench must then provide a way to access data from the memory block in a manner
and time appropriate to properly use the data. A simple approach to checking results is to
access the memory in a linear manner and present the new data each time the testbench HDL
model generates a new result. In Listing 2, the testbench
initializes the memory from the results file, test.data, using a $readmemh function. A
32-bit output image_din equals the memory-data contents at the current address, and you
can use this data as stimulus to the testbench or to compare simulation results. The
testbench can then do the checking and report any errors. When the testbench finds an
error, it may be appropriate to stop the simulation, or you may continue to gather more
results.
A second approach to importing results files is using Verilog Programming Language
Interface (PLI) routines. PLIs are written in C and allow you to add custom system tasks
to the Verilog code. For verification, you use PLIs primarily to add file I/O functions to
the testbench. There are two reasons to consider using PLIs. First, the $readmemh approach
can use an excessive amount of memory if your reference files are large and abundant.
Second, you may want your reference files to contain data in a format that is incompatible
with $readmemh or $readmemb. There is a learning curve to using PLIs, but the file I/O
functions needed for verification are usually simple. Listing
3 provides an example of a PLI I/O routine.
You can use a C model as a testbench reference at a simple level or as a complex and
powerful tool. In the most elementary situation, the C model can provide reference results
that you can compare with simulation results even outside the testbench using simple
software tools, such as a Unix "diff" command. The next level of complexity uses
the C-model results to do real-time checking as the testbench simulations run. You enhance
the debugging process by finding problems as they occur within the module boundaries.
Perhaps the most rigorous use of a C model is to build a cycle-accurate model that
provides a clock-cycle-by-clock-cycle reference. The scale and complexity of your design
problem determine the value of investing your resources into C-model development. |