Global training solutions for engineers creating the world's electronics

Debugging

This tutorial shows you some basic debugging techniques, including the use of waveform tracing. First, it shows some basic text based methods, then extends the example from the Modules and Processes tutorial to use waveform tracing.

Text-Based Debugging

Even without waveforms, some simple techniques can be used to find out what is going on inside your design. These include the use of the name() method and careful placement of text output statements.

SystemC provides overloaded stream insertion operators for the built-in data types, so you can just use statements such as

cout << mydata << endl;

 

and it will work, even for SystemC data types.

Here is the code of the mon.h from the previous chapter

#include "systemc.h"
#include 
SC_MODULE(mon)
{
    sc_in<bool> A,B,F;
    sc_in<bool> Clk;

  void monitor()
  {
    cout << setw(10) << "Time";
    cout << setw(2) << "A" ;
    cout << setw(2) << "B";
    cout << setw(2) << "F" << endl;
    while (true)
    {
      cout << setw(10) << sc_time_stamp();
      cout << setw(2) << A.read();
      cout << setw(2) << B.read();
      cout << setw(2) << F.read() << endl;
      wait();    // wait for 1 clock cycle
    }
  }

  SC_CTOR(mon)
  {
    SC_THREAD(monitor);
    sensitive << Clk.pos();
  }
};
  • Note that cout works fine here, as the read() method returns values of type bool, which is a built in C++ data type; but it would have worked fine for SystemC data types such as sc_logic, sc_int, and so on.
  • Secondly, note the use of sc_time_stamp() to print out the current simulation time.

 

Another use for cout is to find out how your design is built-up before simulation starts. Will the NAND gates of the previous chapter be constructed before the EXOR gate? They should be, but a simple way to prove it is to add a line inside each constructor printing out a message. E.g.

SC_CTOR(nand2)
{
  cout << "Constructing nand2" << endl;

 

Here is the corresponding output after adding statements like that

Constructing stim
Constructing nand2
Constructing nand2
Constructing nand2
Constructing nand2
Constructing exor2
Constructing mon

 

The disadvantage of this is that it does not show which instance is referred to. This can be overcome by using the name() method. The name() method is defined for most things in SystemC. We can simply say

cout << "Constructing nand2 " << name() << endl;

 

instead, and now the output is

Constructing stim
Constructing nand2 exor2.N1
Constructing nand2 exor2.N2
Constructing nand2 exor2.N3
Constructing nand2 exor2.N4
Constructing exor2
Constructing mon

 

Note that name() returns what the user specifies in the labels of the instances. Other methods you might want to look at are dump() and print() (have a look at sc_object in the Doulos SystemC Golden Reference Guide to see examples).

Waveform Tracing

The SystemC library supports waveform tracing. This requires adding statements to the top level of your system (inside sc_main). Here is the top level from the Modules and Processes tutorial with waveform tracing (to a vcd, Value Change Dump, file).

#include "systemc.h"
#include "stim.h"
#include "exor2.h"
#include "mon.h"

int sc_main(int argc, char* argv[])
{
  sc_signal<bool> ASig, BSig, FSig;
  sc_clock TestClk("TestClock", 10, SC_NS,0.5, 1, SC_NS);

  ... instance of stim

  exor2 DUT("exor2");
  DUT.A(ASig);
  DUT.B(BSig);
  DUT.F(FSig);

  ... instance of mon

  sc_trace_file* Tf;
  Tf = sc_create_vcd_trace_file("traces");
  ((vcd_trace_file*)Tf)->sc_set_vcd_time_unit(-9);
  sc_trace(Tf, ASig  , "A" );
  sc_trace(Tf, BSig  , "B" );
  sc_trace(Tf, FSig  , "F" );
  sc_trace(Tf, DUT.S1, "S1");
  sc_trace(Tf, DUT.S2, "S2");
  sc_trace(Tf, DUT.S3, "S3");

  sc_start();  // run forever
  sc_close_vcd_trace_file(Tf);
  return 0;
}

 

Note the sequence of operations:

 

  • Declare the trace file
  • Create the trace file
  • (Optionally specify the trace time unit)
  • Register signals or variables for tracing
  • Run the simulation
  • Close the trace file

 

Note also that it is possible to trace hierarchically (e.g. DUT.S1).

Here are the resulting waveforms:

Conclusion

Some debugging techniques have been demonstrated, based on outputting text and on waveform tracing.

For more difficult problems you might need to use a C++ debugger (for instance gdb/ddd on UNIX), or a dedicated SystemC debugger.

Prev Next