The use of SystemC/C++ system models significantly increases the productivity of hardware design flows. SystemC/C++ source code is more compact than RTL, simulates faster, and can target a wide range of microarchitectures, depending on performance, area and timing requirements. On the functional verification front, due to a lack of tools and methodologies benefits are less evident. Top-level test vectors used to validate behavioral model provide limited coverage. Failures are hard to debug. Verification of the generated RTL code comes late in the development process and is not efficient. This paper shows how automated formal verification solutions well established in RTL development, once adapted and extended to analyze and verify SystemC/C++ code prior to high-level synthesis, provide a much needed boost to verification quality and productivity. Experiences in industrial applications are reported.
Automated Formal Verification of SystemC/C++ High-Level Synthesis Models
1. 1
Automated Formal Verification of SystemC/C++
High-Level Synthesis Models
Sergio Marchese, Sven Beyer, Vladislav Palfy
OneSpin Solutions, Munich, Germany
{firstname.lastname}@onespin.com
Abstract—The use of SystemC/C++ system models significantly increases the productivity of hardware design flows.
SystemC/C++ source code is more compact than RTL, simulates faster, and can target a wide range of
microarchitectures, depending on performance, area and timing requirements. On the functional verification front,
due to a lack of tools and methodologies benefits are less evident. Top-level test vectors used to validate behavioral
model provide limited coverage. Failures are hard to debug. Verification of the generated RTL code comes late in the
development process and is not efficient. This paper shows how automated formal verification solutions well established
in RTL development, once adapted and extended to analyze and verify SystemC/C++ code prior to high-level synthesis,
provide a much needed boost to verification quality and productivity. Experiences in industrial applications are
reported.
Keywords—C, C++, SystemC, formal verification, bug hunting, code inspection, datapath optimization, ESL, HLS.
I. INTRODUCTION
System level models (SLMs) written in SystemC/C++ provide a number of significant benefits, as
demonstrated by their increased adoption, particularly in applications relying on complex data processing
algorithms. Signal and image processing applications, including image scaling, image interpolation and video
codecs, are often expressed using SLMs. The corresponding hardware designs typically have large latencies, and
their register-transfer level (RTL) representations are hard to code and slow to simulate. Untimed or loosely timed
SystemC/C++ models, on the other hand, simulate significantly faster. Moreover, high-level synthesis (HLS) tools
may read SLMs and automatically generate a variety of RTL representations, enabling engineers to explore
different RTL micro architectural choices targeting specific area, performance and timing goals. Additionally,
SLMs can be used in virtual prototyping and software development environments. Finally, SystemC/C++ code is
often an order of magnitude more compact than the corresponding RTL.
However, functional verification still represents a substantial portion of the development effort. Due to a lack
of dedicated tools and methodologies, on this front the benefits of using SLMs are less evident [1, 5]. Two crucial
aspects of any efficient verification flow are discussed below.
A. Verification Shift-Left
Verification needs to start as soon as possible in the development flow in order to detect functional bugs as
close as possible to their entry point. Detecting a functional bug during gate level simulation is far more costly than
doing so during RTL simulation. Cost increases exponentially for bugs detected during silicon testing. Similarly, it
is more efficient to detect functional bugs on SystemC/C++ code than on the RTL generated by HLS tools.
However, while engineers can rely on advanced, mature tools and methodologies for RTL verification, at
SystemC/C++ level the industry is just starting to catch up.
B. Module-Level Verification
Verification needs to have the appropriate level of granularity. System level tests have the advantage of being
reusable, stimulate large portion of hardware, and ensure specific use cases work as expected. However, due to
controllability and observability limitations, system tests may miss deep corner case issues, and, in case of failures,
are hard to debug. Hardware verification at module level should ideally catch all the functional issues, prior to
2. 2
integration and system level testing. Comprehensive module level verification requires additional effort to develop
dedicated test benches. For modules that are not meant to be widely reused and featuring custom interfaces, this
extra effort might be hard to justify. Engineers must find an optimal trade-off between the two aims of minimizing
both system and module verification efforts, while ensuring no bugs reach the final product.
In this paper we present formal verification solutions for SystemC/C++ hardware models that enable automatic
detection of bugs early in the development flow. This methodology does not require test bench. Therefore, it can
be applied at module level without additional effort. Section II includes a brief overview of hardware development
methodologies making use of SystemC/C++ code. Section III introduces relevant aspects of formal verification
while Section IV focuses on automatic formal verification of SystemC/C++ hardware models. Section V presents
industrial experiences and results. Section VI concludes the paper.
II. SYSTEMC/C++ IN HARDWARE DEVELOPMENT FLOWS
Different organizations and projects use SystemC/C++ in different ways within their hardware development
flows (see Figure 1). MATLAB®/Simulink® models can be used to generate C++ code. Engineers can then use
this code as reference to implement corresponding synthesizable RTL. A different approach is to use algorithmic
SystemC/C++, for example developed for virtual prototyping platforms, as reference for coding synthesizable
SystemC. In some cases, for example when fast models of existing RTL modules are required, manual or automated
processes can be used to create a SystemC model from the RTL. Finally, untimed, loosely timed, or cycle accurate
SystemC models can be used to generate RTL through HLS tools.
In a common approach, engineers start from behavioral, algorithmic SystemC/C++ models, and through a
number of architectural choices and refinement iterations, a hardware-friendly SystemC/C++ model is derived,
with its functionality partitioned into several modules (see Figure 2). The derived model might be untimed, cycle
accurate, or, more likely, loosely timed, and include clock and reset notions. Data types and structures used in the
code may leverage proprietary libraries if the aim is to use a specific HLS tool to generate RTL. For example,
instead of using SystemC sc_(u)int and sc_(u)fixed types, engineers may apply the corresponding synthesizable
variants from the chosen vendor, which are prefixed cynw_ for Cadence Stratus™ and ap_ for NEC Cyber-
Workbench™. It is also common to have teams that derive the RTL manually, instead of using HLS tools.
The development of synthesis friendly SystemC/C++ model is a time consuming and error-prone task. Several
engineers may work for a number of months to code tens of modules. The algorithmic SystemC/C++ models may
have been validated using top-level test bench and extensive I/O test data. The environment can be reused to verify
the synthesis friendly SystemC/C++ model prior to HLS or manual RTL coding. However, this is not an efficient
verification flow. Implementation dependent bugs and optimization issues may be missed. Many issues that would
be much easier to detect and debug at module level, may be hard to root cause at system level.
Figure 1. Overview of use models of SystemC/C++.
3. 3
Ideally, engineers would have a module-level testbench that can be run regularly as coding progresses, and
before integrating their module into the system. On the other hand, developing simulation test benches for each and
every module may require too much effort. The optimal trade-off between these conflicting requirements is not
easy to find.
III. FORMAL VERIFICATION OF HARDWARE DESIGNS
Formal methods are mathematically rigorous techniques and tools for the specification, design and verification
of software and hardware systems [6]. Formal property-checking tools have been used in hardware development
for decades. Over the last ten years, thanks to the advances in both ease of use and capacity, formal tools and related
methodologies have achieved widespread adoption in the semiconductor industry. Formal verification is recognized
as a powerful technique to complement, and in some cases replace, simulation in the functional verification of RTL
designs.
A key characteristic of formal tools is the ability to examine design behavior exhaustively, without the need for
the user to construct a testbench or define input stimuli. Even for simple designs, simulation cannot achieve this
level of quality and efficiency. Formal verification checks, or assertions, can be classified in three categories:
Automatically generated after design compilation. No formal expertise is required to generate and run
these checks.
Prepackaged in verification intellectual properties (VIP), or generated by the tool according to user
input. Low to medium level of formal expertise is required to create and run these checks.
User defined. Medium to high level of formal expertise is required to code assertions.
SystemVerilog is a popular language to code assertions (SVA). It is worth noting that, thanks to the exhaustive
verification of formal tools, even simple assertions can uncover bugs that become apparent only in remote
corner case scenarios, hard to foresee or hit during random testing. Simulators can also compile and check
assertions, though only for the set of tests executed, not exhaustively.
Some formal tools may also support SystemC/C++ as design entry language, in addition to VHDL, Verilog and
SystemVerilog. This effectively extends the benefits of formal verification to SLMs, including loosely timed and
untimed designs. OneSpin’s advanced formal verification platform, including 360 DV-Verify and 360 DV-Inspect,
supports SystemC/C++ [1].
A. User-Defined and Prepackaged Assertions
SystemVerilog assertions can concisely capture expected temporal behavior of SystemC/C++ models. In
addition to SVA, C assert and SystemC sc_assert statements are also supported and can be verified exhaustively.
Figure 2. Partitioning algorithms in synthesis friendly SystemC/C++ modules.
4. 4
The capability to apply temporal SVAs to SystemC/C++ code enables a wide range of applications, particularly
as assertions can be reused on the corresponding RTL model. As the focus of this paper is on automated formal
checks, this topic is not discussed further. For more information, please refer to [3].
B. Automated Formal Checks
Formal tools automatically extract checks from the compiled design. This is sometimes referred to as super
linting. Regular linting checks are useful to detect poor coding style or violation of project-specific coding
guidelines. Violations often do not reveal actual functional bugs. Automatic formal checks on the other hand are
proven exhaustively on the design. The formal tool will either mark the check as a pass, or provide a
counterexample, equivalent to a simulation trace, that shows a scenario in which the design violates the check.
Waveform viewer, source code tracing and other advance capabilities are available to debug the counterexample.
The type of checks that are automatically generated by formal tools depend on the design entry language. Details
on the checks generated for SystemC/C++ models are reported in the next section.
IV. AUTOMATED FORMAL CHECKS FOR SYSTEMC/C++
Automated formal checks can detect a number of different issues in SystemC/C++ code. The checks are
automatically generated by the formal tool during design compilation. Certain checks are relevant only for design
with arithmetic datapath. These are presented separately.
A. Generic Checks
Automated generic checks detect the following code issues and ambiguities:
Initialization. These checks test that states are set to a uniquely determined value after the design is
stimulated with the reset sequence. Unintended uninitialized states may cause circuit malfunction even
many cycles after the reset sequence has completed.
Array index out of bounds. These checks test the index used to access the array does not exceeds its
bounds. An out of bound array access causes an undefined result.
Division by zero. This operation creates an undefined result and must be corrected.
Race conditions. SystemC ambiguities may results in unintended and harmful races between parallel
threads [2]. These checks detect race scenarios, e.g. write-write races where two threads may be
simultaneously writing the same variable with different values.
Dead code. These checks detect unreachable code, or code that will never be executed during any
simulation. Although dead code may not directly point to functional bugs, designers should justify
unreachable code and avoid it whenever possible.
Toggle. These checks detect signals that never toggle. Although stuck-at signals are often not
associated to functional bugs, they may point to areas worth further analysis.
FSM. These checks tests that, for a given finite state machine (FSM), all states are reachable after
reset, all syntactically defined transitions can occur, and finally, that no deadlock states are present.
5. 5
B. Arithmetic Datapath Checks
To simplify the hardware and allow for HLS, engineers prefer to use integral and fixed-point data types over
floating-point to model data processing algorithms. Specific checks can be generated on registers storing fixed-
point and integral operands, including intermediate results during algorithm execution [3]. The formal tool will
exhaustively test the numbering system. Checking all valid input data scenarios is not feasible in simulation.
Moreover, complex algorithms and specific implementation choices may results in functional bugs being apparent
only under rare input scenarios.
Overflow. Large operands could result in intermediate results too large to be stored in datapath
registers. If this check fails, the counterexample will point to a scenario in which a specific signal has
not enough bits to store the results of an intermediate arithmetic operation necessary to implement the
algorithm. This is a functional bug.
Redundancy. Designers may unintentionally oversize datapath registers. Although unused state bits
do not corrupt functionality, they do result in wasted power and area. This check will detect registers
for which precision can be reduced without affecting the correct execution of the algorithm.
For all the checks above, an intuitive debug environment is crucial to speed up the analysis of failures. Figure
3 shows a datapath analysis session on a SystemC design implementing a finite impulse response (FIR) algorithm.
The formal tool has generated a trace that can be analyzed using the waveform viewer and the source code debugger.
The input stimuli to the design have been generated automatically, with no effort spent to develop a test bench.
Note that the source code navigator annotates signals with the value they have at the selected cycle in the waveform
viewer. Moreover, values are displayed according to the fixed-point type used in signal declaration. The picture
also shows a SystemVerilog cover property applied on the SystemC design.
Any engineer coding SytemC/C++ targeting HLS tools, or before manual coding of RTL, can automatically run
all the above checks regularly during development and before final module sign-off. This flow ensures that a much
higher quality design reaches the next stage of verification, where corner cases could be missed and debugging is
far more time consuming. As no module-level testbench is required, the investment to setup and run automated
formal checks is minimal.
The supported SystemC/C++ is a superset of what HLS tools like Stratus accept. It is important that
SystemC/C++ code that will be read into HLS tools to generate RTL can also be read, without modifications, by
verification tools like OneSpin.
6. 6
In the next section we present results of the application of both generic and arithmetic datapath formal checks
on industrial applications.
V. RESULTS
The results presented in this section refer to SystemC/C++ design modules for image processing applications.
Design1 and Design2 are two small modules modeling portions of an algorithm. They both use signals of type
cynw_int and cynw_fixed. Generation of autochecks is only enabled for user-defined code and disabled for
standard libraries. Results are shown in Table I and Table II respectively.
Table I. Automated formal checks data on Design1.
Number of autochecks Type of autochecks Runtime Issues
58
Initialization
Dead code
Toggle
FSM
Arithmetic Datapath
3 minutes
Output port not initialized
3 FSM states not reachable
Figure 3. Datapath analysis of SystemC design in formal verification tool.
7. 7
Table II. Automated formal checks data on Design2.
Another set of valuable data is reported in Table III. The flow to run automated formal autochecks was deployed
on a large organization. Over the course of two years checks were run and results analyzed on a large number of
both legacy and newly developed modules. Design, rather than verification engineers were the prevalent users. The
effort to deploy the tool and methodology is reported to be very low, without accounting for effort to isolate and
report tool issues in initial phase of deployment. Moreover, in order to enable widespread adoption, initial users
developed a quick start document and a collection of tips specific for the type of modules and coding style common
within the organizations.
Table III. Deployment of automated formal checks in large organization.
Although detailed information on the issues found is not available, engineers reported that:
“Several bugs missed by conventional simulation-based verification were quickly detected by
automated formal checks”.
“Debug is easy because the tool shows the designer the counterexample”.
Finally, it is worth noting that runtime includes design compilation, generation and run of autochecks. Short
runtime enables designers to run formal autochecks regularly during development of a module. Moreover, overnight
runs are possible even on large number of modules without the need for sophisticated parallelization.
VI. CONCLUSION
The use of SytemC/C++ in the design flow has many benefits, particularly when implementing data processing
algorithms. Increased productivity and higher quality implementation thanks to the ability to explore different micro
architectural choices are key differentiators over RTL code. Functional verification, however, is still mainly
performed on the generated - either manually or through HLS tools - RTL designs.
The role of SystemC/C++ in the development flow may vary. In this paper, we have focused on the practice of
partitioning behavioral, algorithmic SystemC/C++ models into several SystemC/C++ modules suitable for HLS
(see Figure 2). As designers develop these modules, having an easy to run verification environment is of great help
Number of autochecks Type of autochecks Runtime Issues
772
Initialization
Dead code
Toggle
FSM
Arithmetic Datapath
4 minutes
19 redundant bits
400 signals bits do not toggle
3 FSM states not reachable
Number of modules Type of autochecks Runtime per module Issues
70
Initialization
Array index out of bound
Dead code
Toggle
Arithmetic Datapath
From 1 minute to 60 minutes.
Several issues found,
particularly on newly developed
modules.
(No detailed information
available)
8. 8
to catch as many issues as possible at each code revision. The effort to create a fully-fledged test bench is excessive.
Formal verification offers an alternative whereby a number of checks are generated automatically at design
compilation. These checks do not perform a complete verification of the module, but do detect many issues,
including bugs that may appear only in remote corner case scenarios.
Thanks to the short runtime, automated formal checks can be run regularly by designers during module
development, ideally before a new version of the code is committed. Organizations using continuous integration
systems like Jenkins or Atlassian Bamboo, can use this infrastructure to automate the process.
REFERENCES
[1] OneSpin Solutions, “OneSpin Formal Verification Solutions for C/C++/SystemC”, White Paper.
[2] S. Beyer, D. Strasser, “Detecting Harmful Race Conditions in SystemC Models Using Formal Techniques”, DVCon USA 2015.
[3] S. Beyer, D. Strasser, D. Kelf,: “The Application of Formal Technology on Fixed-Point Arithmetic SystemC Designs”, DVCon Europe
2015.
[4] Technical Tutorial, "SystemC Design and Verification – Solidifying the Abstraction Above RTL", www.accellera.org.
[5] D. Kelf, “Linking high-level synthesis with formal verification”, www.techdesignforums.com.
[6] https://shemesh.larc.nasa.gov/fm/fm-what.html