Storing one bit

A bit can have the values 0 or 1. In a computer, these values are represented by a low and a high value of an electrical signal.

The value of a bit can be stored. This means that the value is remembered, as long as it is stored. While the value is stored, the value can be read, and used, for the purpose of performing different operations. Examples of operations could be to store the value somewhere, for example in memory, or using the value in an addition operation.

A D flip-flop

The value of a bit can be stored in a building block called D flip-flop.

A D flip-flop stores one bit of data. A new value can be stored when a clock signal changes value. A component which can change its stored value only when a clock signal changes is called a synchronous component.

A D flip-flop implementation in SystemC is shown in Figure 2.

#include "d_ff.h"

SC_HAS_PROCESS(d_ff);

d_ff::d_ff(sc_core::sc_module_name name):
    sc_module(name) 
{
    SC_METHOD(update);
    sensitive << clk.pos(); 
}

void d_ff::update()
{
    reg_value = data_in.read();
    data_out.write(reg_value);
}

Figure 2. A D flip-flop in SystemC.

This the SystemC-TLM view - other views are VHDL - Verilog

The code in Figure 2 starts with an include directive. The include directive refers to a header file called d_ff.h. The header file defines a class called d_ff, as

class d_ff : sc_core::sc_module
{
  public: 
    sc_in<bool> clk;
    sc_in<bool> data_in;
    sc_out<bool> data_out;
    d_ff(sc_core::sc_module_name name); 
  private:
    void update();
    bool reg_value;
}; 

The class defines two inputs, called clk and data_in, and one output, called data_out.

The class also defines a function called update, and a variable called reg_value. The variable reg_value is defined using the keyword bool.

The variable reg_value will contain the actual value stored in the D flip-flop.

Read more

A testbench

The D flip-flop implementation in Figure 2 has inputs and outputs. An external module, referred to as a testbench, can be used for the purpose of generating input signals to the D flip-flop, and observing output signals from the D-flip-flop.

A SystemC testbench is shown in Figure 3.

#include "d_ff_tb.h"

SC_HAS_PROCESS(d_ff_tb);

d_ff_tb::d_ff_tb(sc_core::sc_module_name name):
    sc_module(name),
    d_ff_0("d_ff_0"),
    clk("d_ff_clk", 4, SC_NS, 1.0)
{
    SC_THREAD(stim_gen);
    d_ff_0.clk(clk); 
    d_ff_0.data_in(d_ff_data_in); 
    d_ff_0.data_out(d_ff_data_out);
    SC_METHOD(reporter);
    sensitive << d_ff_0.clk.pos();
    sensitive << d_ff_data_in; 
}

void d_ff_tb::stim_gen()
{
    d_ff_data_in.write(true);
    wait(1, SC_NS);
    d_ff_data_in.write(false);
    wait(5, SC_NS);
    d_ff_data_in.write(true);
    wait(3, SC_NS);
    d_ff_data_in.write(false);
    wait(); 
}

void d_ff_tb::reporter()
{
    std::cout << "Time: " << sc_time_stamp(); 
    std::cout << ", data_in=" << d_ff_data_in.read(); 
    std::cout << ", data_out=" << d_ff_data_out.read()
	      << std::endl;
}

Figure 3. A D flip-flop testbench in SystemC.

This the SystemC-TLM view - other views are VHDL - Verilog

The code in Figure 3 starts with an include directive. The include directive refers to a header file called d_ff_tb.h. The header file defines a class called d_ff_tb, as

class d_ff_tb : sc_core::sc_module
{
  public: 
    d_ff_tb(sc_core::sc_module_name name); 
    d_ff d_ff_0; 
  private:
    sc_clock clk; 
    sc_signal<bool> d_ff_data_in; 
    sc_signal<bool> d_ff_data_out;
    void stim_gen();
    void reporter(); 
}; 

The code in Figure 3 defines the functions stim_gen and reporter to be SystemC processes. This is done using the keywords SC_THREAD and SC_METHOD.

Read more

Build and run

The D flip-flop in Figure 2 and the testbench in Figure 3 can be compiled using

g++ -c -I /usr/local/systemc-2.3.1a/include d_ff_tb.cpp
g++ -c -I /usr/local/systemc-2.3.1a/include d_ff.cpp
g++ -c -I /usr/local/systemc-2.3.1a/include d_ff_tb_main.cpp

The combined system, containing the D flip-flip and the testbench, can be created by linking the object files from the compilation into an executable program. The linking is simplified by setting an environment variable L_SYSTEMC, as described in Section Make it run.

An executable program can be generated by giving the command

g++ -o d_ff_tb_main d_ff_tb_main.o d_ff_tb.o d_ff.o \
-L $L_SYSTEMC -lsystemc

The program can now be run, in Linux by giving the command

LD_LIBRARY_PATH=$L_SYSTEMC ./d_ff_tb_main

and on Mac by giving the command

./d_ff_tb_main 

The resulting printout is shown in Figure 4.

        SystemC 2.3.1-Accellera --- Jan  3 2017 19:23:14
        Copyright (c) 1996-2014 by all Contributors,
        ALL RIGHTS RESERVED
Time: 0 s, data_in=0, data_out=0
Time: 0 s, data_in=1, data_out=0

Info: (I702) default timescale unit used for tracing: 1 ps \
(d_ff_tb_wave.vcd)
Time: 1 ns, data_in=0, data_out=1
Time: 4 ns, data_in=0, data_out=1
Time: 6 ns, data_in=1, data_out=0
Time: 8 ns, data_in=1, data_out=0
Time: 9 ns, data_in=0, data_out=1
Time: 12 ns, data_in=0, data_out=1
Time: 16 ns, data_in=0, data_out=0

Figure 4. Printout from running the testbench in Figure 3.

This the SystemC-TLM view - other views are VHDL - Verilog

The printout in Figure 4 shows the values of data_in and data_out for a sequence of time instants. The time instants are defined by the sensitivity statements for the SystemC method named reporter in Figure 3, as

    sensitive << d_ff_0.clk.pos();
    sensitive << d_ff_data_in; 

with the effect that the SystemC method reporter is executed whenever the clock signal has a rising edge, or the variable d_ff_data_in changes value. The changes for the variable d_ff_data_in are defined in the function d_ff_tb::stim_gen in Figure 3.

Read more

Making waves

The testbench in Figure 3 generates printouts as shown in Figure 4. The printouts show values of digital signals, each having the value one or zero. We can represent these signals as waveforms, with the level of the waveform being one or zero. Thinking of the value one as a high voltage level, and the value zero as a low voltage level, we can think of the waveforms as representing actual voltages, in an actual digital system.

A waveform can be visualized using the GTKWave program. We can download a GTKWave version for Mac, in the form of a zip-file that contains an executable GTKWave program. The GTKWave program can be started from a Mac Terminal, by giving the command open followed by the app file name of the program. As an example, I could start the program by doing

open /Users/oladahl/prog/gtkwave/gtkwave.app

A GTKWave version for Ubuntu can be installed in Ubuntu, by giving the command

sudo apt-get install gtkwave

The program can then be started by giving the command gtkwave.

A waveform can be generated from SystemC by calling the function sc_create_vcd_trace_file, and then calling a function sc_trace, for determining which data that shall be recorded.

Read more