index


index


Ports/DTypes

     

1 Introduction

To learn a new language you need to practise, practise and practise and for this you need a SystemC development environment. Luckily the SystemC class library, SystemC event based simulator and all the surrounding tools like compilers, debuggers, VCD viewers, IDE's etc can all be downloaded for free from the web. For this tutorial I will use a simple command-line based environment running under Cygwin, see this link on how to set it up. If you like a full IDE then I would recommend the free Visual C++ 2005 Express under windows or Kdeveloper/Anjuta under Linux. For commercial applications you have a choice of several very powerful (but not cheap!) mixed language HDL simulators such as Modelsim, VCS, NC-Sim, Active-HDL to name a few. See Appendix A for more links. It is advisable to download the SystemC Language Reference Manual(LRM) from the systemc.org website and to read the SystemC user guide after this tutorial.

I have written some very basic examples some of which are described in the tutorial. You can download the examples here (29Kbyte, zipped).

1.1 Free VCD Viewers

Most engineers prefer to look at their simulation results in the form of waveforms. SystemC supports waveforms natively in the language via the so called VCD standard. If you are lucky to have access to a commercial HDL simulator like Modelsim, ActiveHDL, VCS etc than you don't have to worry about VCD files since you can simply look at any signal/variable during the simulation. If you don't then you need to create a VCD file during simulation. After you have run your simulation you can examine the waveforms using a VCD viewer. Below are a number of free VCD viewers I found on the web.

One of the main requirements for the VCD viewer is to quickly invoke or being able to re-read the VCD file without restarting the program, ideally it should checks the file timestamp and refresh the display when changed. Although SynaptiCAD is no doubt the most feature rich one, it is slow to startup and doesn't have a refresh option or at least I couldn't find one. For this tutorial I will use the Interconnect Systems Solution one which is quick to startup and contains a refresh GUI button (note the F5 refresh button doesn't always seem to work reliably on my 1.12 version)


SynaptiCAD's free Waveform viewer

1.2 SystemC "Hello World"

As is customary with any new language tutorial you start with a simple "hello world" program. However, since we are engineers I will start with a simple Flip-Flop example instead. Don't worry about some of the SystemC keywords and constructs as they will be explained later in the tutorial.

// SystemC FlipFlop, dff.h

#include "systemc.h"

SC_MODULE(dff) {
    sc_in<bool > clk;
    sc_in<sc_logic > din;
    sc_out<sc_logic > dout;
 
    void p1() {
        if (clk.posedge()) {
           dout=din;
        }
    }
 
    SC_CTOR(dff) {
        SC_METHOD(p1);
          sensitive(clk);
    }

};

-- VHDL FlipFlop, dff.vhd
library ieee;
use ieee.std_logic_1164.ALL;

ENTITY dff is
    port(clk  : in  std_logic;
         din  : in  std_logic;
         dout : out std_logic);
end dff;

ARCHITECTURE rtl of dff is
begin
    p1:process(clk)
        begin
            if rising_edge(clk) then
                dout <= din;
            end if;
    end process;
end rtl;

The above code example shows a simple Flip-Flop (FF) in both VHDL and SystemC. As one can immediately see there is a lot of similarity between the two languages. However, before I explain the above SystemC constructs I should point out that the SystemC coding style was deliberately done in VHDL style and you might not want to write a SystemC FF in this way. Section 1.4 shows the recommended coding style.

We start each module with the systemc.h header file which contains functions, macros, defines etc which we will use in our code. This is similar to for example the VHDL std_logic_1164 package which defines amongst others the std_logic types.

An SC_MODULE serves the same purpose as a VHDL Entity-Architecture pair or a Verilog module.  It contains I/O Ports, functions, component instantiations processes etc. The module name dff is specified between the round brackets.  SC_MODULE's are normally written to a so called header file with an extension .h.

Following SC_MODULE we define a number of I/O ports. In this particular case we have 2 input "sc_in" and 1 output port "sc_out". The data in "din" and data out "dout" ports are of the SystemC type sc_logic which is the similar to std_logic in VHDL. The clock "clk" signal however is of the native C++ type bool (boolean).  The reason for using a boolean is that SystemC contains a clock generator (discussed later) which output type is a boolean.  Port types sc_in and sc_out are actually called templated classes and you pass on the required port type (sc_logic/bool in the above case) as an argument between the angled brackets <std_logic>. Templates can be consider as a superset of VHDL generics.

The function p1 contains the process code which in our case is a simple assignment. This assignment is executed on the rising edge of the clk signal. Similar to a VHDL process there is no return value hence the void keyword. In the above example the function code is included in the header (SC_MODULE) file but as we will see later on this is normally written to a separate cpp file.

The only bit in the above example which stands out is the constructor part SC_CTOR(dff) for which there is no VHDL equivalent. Without going into C++ terminology, the constructor is called for each instance of this dff module (component). The task of the constructor is to create and initialise the module in memory and to pass on some info to the SystemC kernel before the simulation starts.

Inside the constructor SC_CTOR(dff) we to tell the SystemC kernel what kind of process we want for function p1. In VHDL we have just one single process type but in SystemC we have 3 (SC_METHOD, SC_THREAD, SC_CTHREAD). In the above example we tell (or more accurate register with) the SystemC kernel that function p1 requires a process of type SC_METHOD. Chapter 2 describes the different process types.  

Note that SC_MODULE, SC_CTOR and SC_METHOD are not SystemC keywords but C++ macros. The reason for using these macros is that they hide some complex looking C++ code constructs, unfortunately there are occasions were you have to use the full C++ constructs as we will see later on.

Similar to VHDL we specify an optional sensitivity list. In SystemC you use the sensitive keyword for this. In the above example sensitive(clk) is similar to VHDL process(clk) statement.

Finally note the last closing bracket contains a trailing semicolon "};". If you forget it then you get an obscure error message from the compiler.

$ make
g++  -g -Wno-deprecated -Wall -I. -I.. -I/usr/local/systemc-2.2/include -c sc_main.cpp
sc_main.cpp:12: error: new types may not be defined in a return type
sc_main.cpp:12: error: extraneous `int' ignored
sc_main.cpp: In function `dff sc_main(int, char**)':
sc_main.cpp:12: error: new declaration `dff sc_main(int, char**)'
/usr/local/systemc-2.2/include/sysc/kernel/sc_externs.h:49: error: ambiguates old declaration `int sc_main(int, char**)'
sc_main.cpp: In function `dff sc_main(int, char**)':
sc_main.cpp:38: error: conversion from `int' to non-scalar type `dff' requested
make: *** [sc_main.o] Error 1

1.3 Simulate

Like with VHDL you need to create a testbench to stimulate the dff module and to look at the output signals. The testbench for the above dff module is located in the file sc_main.cpp. Contrary to an commercial HDL simulator like e.g. Modelsim, in SystemC you not only instantiate the device and specify the stimuli, you also code up which signals you want to look at (VCD file) and how long you want your simulation to run. The sc_main.cpp file will be discussed later on.
Running the simulation is quite straightforward assuming you have compiled and setup your SystemC environment correctly. To compile the design and link in the SystemC event based simulator navigate to the examples helloworld directory and issue the following command (note the g++ arguments might be different for your environment)

g++ -I/usr/local/systemc-2.2/include sc_main.cpp -l/usr/local/systemc-2.2/lib-cygwin/libsystemc.a 

The resulting output file is called a.exe and when you run it you hopefully get something like:

$ ./a.exe
 
             SystemC 2.2.0 --- Jul  5 2007 12:37:47
        Copyright (c) 1996-2006 by all Contributors
                    ALL RIGHTS RESERVED
 
Info: (I804) /IEEE_Std_1666/deprecated: use of () to specify sensitivity is deprecated, use << instead
Note: VCD trace timescale unit is set by user to 1.000000e-10 sec.
 
Info: (I804) /IEEE_Std_1666/deprecated: You can turn off warnings about
             IEEE 1666 deprecated features by placing this method call as the
             first statement in your sc_main() function:
 
  sc_report_handler::set_actions("/IEEE_Std_1666/deprecated", SC_DO_NOTHING);

After running the simulation a new file wave.vcd will be created. Open this file in a VCD viewer and observe the simulation results.


ISS's free Waveform viewer showing the dff flip-flop simulation results

1.4 HelloWorld 2

As mentioned in the beginning of this chapter the given Flip-Flop coding style is not really recommended. To start off with, when you run the simulation you get some warnings about deprecated functions. In our particular case we use a sensitivity list construct which will not be supported in future versions of SystemC. Instead of using the sensitive(clk) construct the recommended method is to use the overloaded "<<" operator. The advantage is not only easier to read code, since the << operator is used for reading data in C++, but it is also less verbose,

Old Style

New Style

sensitive(clk);
sensitive(reset);

sensitive << clk << reset;

The second recommendation is to use the .write() and .read() methods for signals and port operations, this to distinguish from variable assignments (the same simulation update rules apply to variables and signal as in VHDL). This will be discussed in later chapters.

Old Style

Recommended Style

dout=din;

dout.write( din.read() );

The third recommendation is to use the C++ native types as much as possible since they improve simulation speed. For example, if we are only interested in '0'/'1' and not in 'X'/'Z' signal states then we can replace our SystemC sc_logic type with the much faster native C++ bool type.

Using sensitive << clk will result in function p1 being called on both the rising and falling edge. Inside function p1 we then filter out only the rising/positive edge ( clk.posedge() ). Obviously for a event based simulator like SystemC this is not particular efficient since you create 2 events per clock cycle and we are only interested in one of them (the rising edge). The way to solve this is to specify the rising edge on the sensitive list as in sensitive << clk.pos().

Old Style

Recommended Style

    void p1() {
        if (clk.posedge()) {
           dout=din;
        }
    }
 
    SC_CTOR(dff) {
        SC_METHOD(p1);
          sensitive(clk);
    }
};

    void p1() {
        dout=din;
    }
 
    SC_CTOR(dff) {
        SC_METHOD(p1);
          sensitive << clk.pos();
    }
};

Note the different methods used, in the old style function p1 we use clk.posedge() and on the recommended style sensitivity list we use clk.pos(). The pos() function is an event finder and only used on a sensitivity list whereas posedge() is a function returning a bool.

A common programming technique in C++ header files is to add a header guard (also known as sentinels). This is just a define statement that avoid any compile errors if the header is included more than once.

The updated FF module now looks like:

// SystemC FlipFlop, dff.h

#ifndef DFF_H
#define DFF_H


#include "systemc.h"

SC_MODULE
(dff) {
    sc_in<bool > clk;
    sc_in<bool > din;
    sc_out<bool > dout;
 
    void p1() {
        dout.write(din.read());
    }
 
    SC_CTOR(dff) {
        SC_METHOD(p1);
          sensitive << clk.pos();
    }

};
#endif

-- VHDL FlipFlop, dff.vhd
library ieee;
use ieee.std_logic_1164.ALL;

ENTITY dff is
    port(clk  : in  std_logic;
         din  : in  std_logic;
         dout : out std_logic);
end dff;

ARCHITECTURE rtl of dff is
begin
    p1:process(clk)
        begin
            if rising_edge(clk) then
                dout <= din;
            end if;
    end process;
end rtl;

The final recommendation is to use Makefiles if you are not using an IDE. Makefiles are simple and the web is full of good examples and tutorials. See the helloworld2 directory for the updated source files and a simple Makefile.

$ make
g++  -g -Wno-deprecated -Wall -I. -I.. -I/usr/local/systemc-2.2/include -c sc_main.cpp
g++ -g -Wno-deprecated -Wall -I. -I.. -I/usr/local/systemc-2.2/include -L. -L.. -L/usr/local/systemc-2.2/lib-cygwin -o run.exe sc_main.o -lsystemc -lm 2>&1 | c++filt

 

 


home

 


index


index


Ports/DTypes