• Skip to main content
  • Skip to search
  • Skip to footer
Cadence Home
  • This search text may be transcribed, used, stored, or accessed by our third-party service providers per our Cookie Policy and Privacy Policy.

  1. Blogs
  2. Verification
  3. The Best C++ Debugger is Not the Best SystemC Debugger
jasona
jasona

Community Member

Blog Activity
Options
  • Subscribe by email
  • More
  • Cancel
virtual platforms
windows
TLM 2.0
SystemC
Visual Studio
C++
debugging

The Best C++ Debugger is Not the Best SystemC Debugger

15 Sep 2010 • 7 minute read

I mentioned in a previous article that I have two girls who are excellent debaters. In debate, they learn how to use evidence and logic to prove or disprove points about the specific cases they debate. During recent discussions with SystemC Virtual Platform users I have noted a trend that debaters would classify as a logical fallacy -- an error in reasoning.

The logic goes something like this:

  • I need to do SystemC modeling to create a Virtual Platform for my project
  • SystemC is a C++ library
  • Before I start I need to get a good debugger since C++ is complex
  • Microsoft Visual Studio is the best C++ debugger
  • Visual Studio is only available on Windows
  • In conclusion, I should use Windows for SystemC development

There is actually nothing wrong with the choice of Windows as an operating system for code development (well, maybe there is, but that's for another day). The logic error results from the conclusion that the best C++ debugger is the best SystemC debugger. The best C++ debugger is not the best SystemC debugger.

To confirm my thinking I turned to George Frazier, a long time Cadence expert in SystemC debugging and analysis. George also has a lot of Windows experience and is familiar with Microsoft Visual Studio. George explained that much of SystemC debugging is about understanding threads. Having a debugger that is thread aware as well as TLM aware (for Virtual Platforms) is a must.

Here is a summary written by George to help you get the picture:

Keep track of your threads

Much of SystemC programming is about keeping track of concurrency. Since most SystemC designs have more than one sc_process, knowing which thread you are in and having the ability to move between threads during debugging is critical.  On Windows, SystemC threads are implemented with fibers, which are not recognized by the Visual Studio debugger as separate pseudo-processes within their parent threads (you can’t list them).

The Cadence SystemC debugger, however, is thread aware, and treats SystemC threads as first class citizens.  For example, you can list all SystemC threads active in your process:

Cadence SystemC Debugger Thread Info Query:

* 3 SystemC Thread "sctop.sclocal.wait_on_inputs" 124 (running)
scTest::wait_on_inputs (this=0x156cad4) at scTest.h:44

2 LWP 1 scTest::wait_on_inputs (this=0x156cad4) at scTest.h:44

1 SystemC Thread "main" 0 (runnable) 0x00cbe7d8 in sc_cor_pkg_qt::yield
(this=0x1401e98, next_cor=0x15c0fa8) at sc_cor_qt.h:201

You can examine all active SystemC threads, switch between threads, look at each callstack, apply actions to threads, and get notified when threads are created and destroyed.

Breakpoints that are thread aware

With the Cadence SystemC debugger, you can not only list and switch between threads, but you can also set breakpoints that are savvy about SystemC instances, processes, and threads -- something that is not possible with Visual Studio.  Why would you want to do this? Because you are debugging SystemC, not an arbitrary C++ program! 

From the SimVision source browser, for example, you can set a traditional breakpoint on a line, or optionally specify which instance or process the breakpoint should apply to. Then the breakpoint will be triggered only when execution reaches your line in that specific instance or process. For example, if you have a TLM initiator module that represents a simple processor and you instantiate 7 different processors for a design, you can pick which processor -- represented as a separate instance -- you want your line breakpoint to apply to. The following figure shows the breakpoint menu of a SystemC process scope. You can see the "Break at Line in Instance" option to stop only in a specific instance of the code.
 

 
 
In addition to instance-specific breakpoint support, you can also set thread resumption breakpoints on a SystemC thread process. When a thread process resumption breakpoint is set, the debugger stops whenever the selected thread resumes from suspension.

If you want even more information on the state of one of your processes, you can probe it. You can set a process monitor on a selected SystemC process and observe how the process state changes over time, in particular by tracking the number of times the process has been scheduled (process activation count), the time when the process was last scheduled, and the trigger for that last scheduling.

A debugger that speaks SystemC

Beyond threads, the Cadence SystemC debug environment understands all of your SystemC modules, the design hierarchy, and the phases of simulation. For example, there are several predefined functions that correspond to important points in simulation. You can set a breakpoint on scDebugEntry which is the first entry into execution of your SystemC code. This "safe harbor" is only reached after SystemC initialization is complete. When you're debugging, you don't want to have to keep track of a list of source code lines (or even worse, monitor which shared objects have been loaded or unloaded). You want to debug your design, not the way that SystemC is implemented on a particular platform! Cadence SystemC has multiple convenience functions for this purpose. If you are using ncsc_run or irun, these breakpoints are automatically set when you are in debug mode.

Native TLM Support

To debug and analyze your design at the TLM level, Incisive SystemC has native support for TLM built into the tools. For example, suppose you want to find out about all activity in a TLM design -- that is, you want to trace the status of the generic payload objects as they flow through the transport functions of your model. A TLM design communicates between modules using READ and WRITE commands (via b_transport, nb_transport_fw, nb_transport_bw), so analyzing the design at the level of the READS and WRITES is natural. If Visual Studio is your only debugging tool, you would have to add code to your design in most cases to do anything meaningful along these lines. For example, suppose you are modeling an image pipeline system and you notice that the displays aren't getting sufficient data in time to properly render the images. The throughput of the system could be insufficient, and this can be manifest by WRITES through the system failing.

A debug strategy might be to look for WRITES that don't complete, such as nb_transport_bw or nb_transport_fw calls that return values other than TLM_OK_RESPONSE (the exact return value you look for is protocol specific, but you get the idea).

Setting breakpoints in some modules nb_transport_fw routine -- if the source code is available -- and waiting for the wrong response is one possible approach. But what if you want to count how many of these incorrect responses you received?  

Cadence SystemC offers several ways to investigate this problem -- and they are constructed with native TLM knowledge, so you can work the way you think at the TLM level!

One way is to use the sctlmrecord capability to automatically record all TLM activity in the design. By "record" we mean that all fields of the generic payload objects of your design are traced before and after READS and WRITES and written into an SST2 transaction database. Each READ and WRITE is recorded as a sequence of its composite "HOPS" along your design topology, so everything about the TLM activity of the simulation is captured. To use the feature you simply add -sctlmrecord to the ncsim, irun, or ncsc_run command line and the database is created automatically. You don' t have to change anything about your design. You can view the resulting transactions in SimVision:
 
 
 

Here each vertical line represents a READ or WRITE. This view is zoomed out, so you get a rough idea of when the TLM activity of happens during simulation.

SimVision offers the ability to split a transaction stream by attribute values. You can right click on the transaction stream name (or an individual transaction) and choose to "expand stream by" ResponseStatus attribute. This will create a separate stream (or row in SimVision) of all the transactions that "failed."  In particular, you can zero in on the time that they failed, and use that information either to guide in debugging (maybe something is wrong with the implementation of b_transport_bw in a router or the b_transport_bw function is just stubbed in and always returns TLM_INCOMPLETE_RESPONSE) or to see that your design needs a more gross-level refinement.

There are many more topics related to SystemC and Virtual Platform debugging, but hopefully this short intro is enough to understand why the best C++ debugger is not the best SystemC debugger. 

Jason Andrews and George Frazier

© 2025 Cadence Design Systems, Inc. All Rights Reserved.

  • Terms of Use
  • Privacy
  • Cookie Policy
  • US Trademarks
  • Do Not Sell or Share My Personal Information