Storing data in registers

When a computer executes instructions, it often needs intermidiate storage places. Reading instructions from memory, writing results back to memory. For example adding numbers, and writing back only when all numbers have been added. Then registers can be used, to hold the intermediate sum, while the calculation is ongoing. We can refer to such a row using the term register. Another use of registers is for addressing. In this scenario, the value stored in the register is an address, addressing a part of the memory. One such register is holding an address pointing to the next instruction to be executed. This register is referred to as the program counter.

A register

A D flip-flop can store one bit. We can imagine a register as a row of D flip-flops, each storing one bit, with the possibility to load new values into all D flip-flops simultaneously.

A register implementation in SystemC is shown in Figure 6.

#include "n_bit_register.h"

SC_HAS_PROCESS(n_bit_register);

n_bit_register::n_bit_register(sc_core::sc_module_name name, int N):
    sc_module(name)
{
    reg_value_max = (1 << N) - 1;
    cout << "reg max is " << reg_value_max << "\n"; 
    SC_METHOD(update);
    sensitive << clk.pos(); 
}

void n_bit_register::update()
{
    reg_value = data_in.read();
    data_out.write(reg_value);
    // std::cout << "data_in " << data_in.read() << "\n"; 
    // std::cout << "reg_value " << reg_value << "\n"; 
    // std::cout << data_out << "\n"; 
}

Figure 6. A register in SystemC.

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

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

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

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 will contain the actual value stored in the register.

Read more

A testbench

An external module, referred to as a testbench, can be used for the purpose of generating input signals to, and observing output signals from, the register in Figure 6.

In the testbench module, we use a parameter, to specify the width of the register. The parameter is defined using a C++ define directive, as

#define N 4

The clock signal is generated using a variable of the class sc_clock, defined as

    sc_clock clk; 

The actual clock generation is done using parameters specified in the instantiation of the clk variable. This is done in by instantiating the clk variable inside the constructor. The constructor is implemented as

n_bit_register_tb::n_bit_register_tb(sc_core::sc_module_name name):
    sc_module(name),
    n_bit_register_0("n_bit_register_0", N),
    clk("n_bit_register_clk", 4, SC_NS, 1.0), 
    data_in_value(1)
{
    n_bit_register_0.clk(clk); 
    n_bit_register_0.data_in(n_bit_register_data_in); 
    n_bit_register_0.data_out(n_bit_register_data_out);
    SC_METHOD(stim_gen);
    sensitive << n_bit_register_0.clk.pos();
    SC_METHOD(reporter);
    sensitive << n_bit_register_0.clk.pos();
}

and the instantiation of the clk variable is done in the constructor initialization block, as

    clk("n_bit_register_clk", 4, SC_NS, 1.0), 

The generation of input signals to the register in Figure 6 is done using a SystemC process, defined as a function as

void n_bit_register_tb::stim_gen()
{
    n_bit_register_data_in.write(data_in_value++);
}

and made into a process by the SC_METHOD directive. The SC_METHOD directive is used in the constructor, as shown above.

Read more

Build and Run

The register in Figure 6 and a testbench, with code as shown in in Section A testbench, can be built and run.

A makefile can be created. The makefile can contain commands for building and running the register and the testbench. A makefile is shown in Figure 7.

UNITS := n_bit_register n_bit_register_tb
HEADER_ONLY_UNITS := 
MAIN_UNIT := n_bit_register_tb_main

OBJS := $(addsuffix .o, $(UNITS) $(MAIN_UNIT))
HEADERS := $(addsuffix .h, $(UNITS) $(HEADER_ONLY_UNITS))

SYSTEMC := /usr/local/systemc-2.3.1a
INCLUDE_DIR := $(SYSTEMC)/include
UNAME := $(shell uname)
ifeq ($(UNAME),Darwin)
  LIB_DIR_NAME := lib-macosx64
else
  LIB_DIR_NAME := lib-linux64
endif
LIB_DIR := $(SYSTEMC)/$(LIB_DIR_NAME)/
LIB_NAME := systemc

$(MAIN_UNIT): $(OBJS)
	g++ -o $@ $^ -L $(LIB_DIR) -l $(LIB_NAME)

%.o: %.cpp $(HEADERS)
	g++ -c -Wall -g -I $(INCLUDE_DIR) $<

.PHONY: clean

clean: 
	rm $(MAIN_UNIT) $(OBJS)

Figure 7. A makefile for building and running the register in Figure 6.

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

It can be seen, in the makefile in Figure 7, that the g++ command is used, in the same way as described in Section Build and run in Chapter Storing one bit.

Read more