Introduction to
SystemC
Reference:
• SystemC: From the Ground Up 2nd edition by David C.
Black
• SystemC Language Reference Manual
Standard Methodology for ICs
• System-level designers write a C or C++ model
• Written in a stylized, hardware-like form
• Sometimes refined to be more hardware-like
• C/C++ model simulated to verify functionality
• Model given to Verilog/VHDL coders
• Verilog or VHDL specification written
• Models simulated together to test equivalence
• Verilog/VHDL model synthesized
Idea of SystemC
• C and C++ are being used as ad-hoc modeling
languages
• Why not formalize their use?
• Why not interpret them as hardware specification
languages just as Verilog and VHDL were?
• SystemC developed at Synopsys to do just this
What Is SystemC?
• A subset of C++ that models/specifies
synchronous digital hardware
• A collection of simulation libraries that can be
used to run a SystemC program
• A compiler that translates the “synthesis subset”
of SystemC into a netlist
Why we need SystemC
• SystemC is a system design and modeling language.
• Evolved to meet a system designer’s requirements for designing and integrating
today’s complex electronic systems very quickly while assuring that the final system
will meet performance expectations.
• Thorough functional (and architectural) verification is required to avoid expensive
and sometimes catastrophic failures in the device.
• Concurrent and multidisciplinary approach to the design of complex systems is
electronic system-level design or ESL.
• ESL happens by modeling systems at higher levels of abstraction
• Portions of the system model are subsequently iterated and refined, as needed.
• A set of techniques has evolved called Transaction-Level Modeling or TLM to
aide with this task.
• ESL and TLM impose a set of requirements on a language that is different than
the requirements for hardware description languages (HDLs) or the
requirements for traditional software languages like C, C++, or Java
• The increasingly shortened time to market requirements
• Verify the design in early time
• The growing complexity
• Integration of developed devices
SystemC
• Strictly speaking, SystemC is not a language. SystemC is a
class library within a well established language, C++.
• SystemC is not a panacea that will solve every design
productivity issue.
• In addition, SystemC provides a common language for
software and hardware, C++.
Needed tools
• SystemC latest library package v2.3.1
• Linux platform
• GCC compiler
• GTKWave – Waveform tool
• some text editor
Quick Overview
• A SystemC program consists of module definitions plus a top-
level function that starts the simulation
• Modules contain processes (C++ methods) and instances of other
modules
• Ports on modules define their interface
• Rich set of port data types (hardware modeling, etc.)
• Signals in modules convey information between instances
• Clocks are special signals that run periodically and can trigger
clocked processes
• Rich set of numeric types (fixed and arbitrary precision numbers)
Modules
• Hierarchical entity
• Similar to Verilog’s module
• Actually a C++ class definition
• Simulation involves
• Creating objects of this class
• They connect themselves together
• Processes in these objects (methods) are called by the
scheduler to perform the simulation
SystemC program structure:
SC_MODULE(<module name>)
{..........
............//this part is described next
...........
};
• Every module should have a constructor 'SC_CTOR' with the same name as that of
the module. The above module with the constructor looks like :
SC_MODULE(<module name>)
{..........
............//these steps are described next
SC_CTOR(<module name>)
{...........
............//these steps are described next
}
};
SC_MODULE(<module name>)
{
sc_in<datatype> port1; Note that the constructor itself
calls the process which means that
sc_out<datatype> port2; • all the processes are executed
when ever an object of the
module is instantiated or
• the value of any of the signal or
SC_CTOR(<module name>) port in the sensitivity list
{ changes.
SC_METHOD(<process name>);
sensitive << <sensitivity list>;
};
Modules
SC_MODULE(mymod) {
/* port definitions */
/* signal definitions */
/* clock definitions */
/* storage and state variables */
/* process definitions */
SC_CTOR(mymod) {
/* Instances of processes and modules */
}
};
Ports
• Define the interface to each module
• Channels through which data is communicated
• Port consists of a direction
• input sc_in
• output sc_out
• bidirectional sc_inout
• and any C++ or SystemC type
Ports
SC_MODULE(mymod) {
sc_in<bool> load, read;
sc_inout<int> data;
sc_out<bool> full;
/* rest of the module */
};
Signals
• Convey information between modules within a
module
• Directionless: module ports define direction of
data transfer
• Type may be any C++ or built-in type
Signals
SC_MODULE(mymod) {
/* port definitions */
sc_signal<sc_uint<32> > s1, s2;
sc_signal<bool> reset;
/* … */
SC_CTOR(mymod) {
/* Instances of modules that connect to the signals */
}
};
Processes
• Only thing in SystemC that actually does
anything
• Procedural code with the ability to suspend and
resume
• Like Verilog’s initial blocks
Three Types of Processes
• Method processes (SC_METHOD) – similar to Verilog
always block
• Thread processes (SC_THREAD) – similar to Verilog
initial block
• CThread processes (SC_CTHREAD) – similar to
SC_THREAD but sensitive to a particular clock edge
METHOD Processes
• Triggered in response to changes on inputs
• Cannot store control state between invocations
• Designed to model blocks of combinational
logic
METHOD Processes
Process is simply a
method of this class
SC_MODULE(onemethod) {
sc_in<bool> in;
sc_out<bool> out;
void inverter();
SC_CTOR(onemethod) { Instance of this process
created
SC_METHOD(inverter);
sensitive <<in; and made sensitive to an
input
}
};
METHOD Processes
• Invoked once every time input “in” changes
• Should not save state between invocations
• Runs to completion: should not contain infinite
loops
void inverter() {
bool internal; Read a value from the port
internal = in;
out = ~internal; Write a value to an output
port
}
THREAD Processes
• Triggered in response to changes on inputs
• Can suspend itself and be reactivated
• Method calls wait to relinquish control
• Scheduler runs it again later
• Designed to model just about anything
THREAD Processes
Process is simply a
method of this class
SC_MODULE(onemethod) {
sc_in<bool> in;
sc_out<bool> out;
void toggler();
SC_CTOR(onemethod) { Instance of this process
created
SC_THREAD(toggler);
sensitive << in; alternate sensitivity list
} notation
};
THREAD Processes
• Reawakened whenever an input changes
• State saved between invocations
• Infinite loops should contain a wait()
Relinquish control until
void toggler() { the next change of a
bool last = false; signal on the
for (;;) { sensitivity list for this
last = in; out = last; wait(); process
last = ~in; out = last; wait();
}
}
CTHREAD Processes
• Triggered in response to a single clock edge
• Can suspend itself and be reactivated
• Method calls wait to relinquish control
• Scheduler runs it again later
• Designed to model clocked digital hardware
CTHREAD Processes
Instance of this process
SC_MODULE(onemethod) { created and relevant
sc_in_clk clock; clock edge assigned
sc_in<bool> trigger, in;
sc_out<bool> out;
void toggler();
SC_CTOR(onemethod) {
SC_CTHREAD(toggler, clock.pos());
}
};
CTHREAD Processes
• Reawakened at the edge of the clock
• State saved between invocations Relinquish control until
• Infinite loops should contain a wait()
the next clock cycle in
which the trigger input
void toggler() { is 1
bool last = false;
for (;;) {
wait_until(trigger.delayed() == true);
last = in; out = last; wait();
last = ~in; out = last; wait(); Relinquish control until
} the next clock cycle
}
Ports and Signals
• Types of ports and signals:
• All natives C/C++ types
• All SystemC types
• User defined types
• How to declare
• IN : sc_in<port_type>
• OUT : sc_out<port_type>
• Bi-Directional : sc_inout<port_type>
Ports and Signals
• How to read and write a port ?
• Methods read( ); and write( );
• Examples:
• in_tmp = in.read( ); //reads the port in to in_tmp
• out.write(out_temp); //writes out_temp in the out port
Ports and Signals
• Ports of a module are the external interfaces
that pass information to and from a module
• In SystemC one port can be IN, OUT or INOUT
• Signals are used to connect module ports
allowing modules to communicate
• Very similar to ports and signals in VHDL
//andgate.h //driver.h
#include "systemc.h" #include "systemc.h"
SC_MODULE(andgate) SC_MODULE(driver)
{
{ sc_out<bool> a1,b1,c1;
sc_in<bool> a1;
void inputs(){
sc_in<bool> b1;
a1.write(false);
sc_in<bool> c1;
sc_out<bool> d1;
b1.write(false);
void compute_and() c1.write(false); wait(5,SC_NS);
{ a1.write(true);
d1.write(a1.read() && b1.read) && (c1. b1.write(true);
read());
c1.write(true); wait(5,SC_NS); }
}
SC_CTOR(driver){
SC_CTOR(andgate)
SC_THREAD(inputs);
{
SC_METHOD(compute_and); sensitive <<a1 <<b1 <<c1;
sensitive <<a1 <<b1 <<c1; }
}}; };
//monitor.h //main program – main.
#include "systemc.h" #include "systemc.h"
SC_MODULE(monitor){ #include "andgate.h"
sc_in<bool> a1,b1,c1,d1; #include "driver.h"
void mon(){ #include "monitor.h"
cout << "Inputs: "<<a1 <<b1 <<c1 << " Output: " int sc_main( int argc, char* argv[] ){
<<d1 <<endl; }
sc_signal <bool> a1,b1,c1,d1;
SC_CTOR(monitor)
{ andgate agate("andgate");
SC_METHOD(mon);
driver drive("driver");
sensitive <<a1 <<b1 <<c1;
monitor mon("monitor");
}};
//Waveform agate.a1(a1); agate.b1(b1);
sc_trace_file *Tf; agate.c1(c1); agate.d1(d1);
Tf = sc_create_vcd_trace_file("traces");
sc_trace(Tf, a1, "a1"); sc_trace(Tf, b1, "b1"); drive.a1(a1); drive.b1(b1);
sc_trace(Tf, c1, "c1"); sc_trace(Tf, d1, "d1"); drive.c1(c1); mon.a1(a1);
sc_start(30,SC_NS);
sc_close_vcd_trace_file(Tf); mon.b1(b1); mon.c1(c1);
return 0; } mon.d1(d1);
Compile and Run using
Cadence
csh
source /cad/cshrc
irun –sysc main.cpp –lrt –sctop
sc_main +gui
Clocks
• Special object
• How to create ?
sc_clock clock_name (
“clock_label”, period, duty_ratio, offset, initial_value );
• Clock connection
f1.clk( clk_signal ); //where f1 is a module
•
Clock example:
Data Types
• SystemC supports:
• C/C++ native types
• SystemC types
• SystemC types
• Types for systems modelling
• 2 values (‘0’,’1’)
• 4 values (‘0’,’1’,’Z’,’X’)
• Arbitrary size integer (Signed/Unsigned)
• Fixed point types
Native C++ Data Types
SystemC types
Type Description
sc_logic Simple bit with 4 values(0/1/X/Z)
sc_int Signed Integer from 1-64 bits
sc_uint Unsigned Integer from 1-64 bits
sc_bigint Arbitrary size signed integer
sc_biguint Arbitrary size unsigned integer
sc_bv Arbitrary size 2-values vector
sc_lv Arbitrary size 4-values vector
sc_fixed templated signed fixed point
sc_ufixed templated unsigned fixed point
sc_fix untemplated signed fixed point
sc_ufix untemplated unsigned fixed point
SC_LOGIC type
• More general than bool, 4 values :
• (‘0’ (false), ‘1’ (true), ‘X’ (undefined) , ‘Z’(high-impedance) )
• Declaration
• sc_logic my_logic;
• Assignment like bool
• my_logic = ‘0’;
• my_logic = ‘Z’;
• Simulation time bigger than bool
Fixed precision integers
• Used when arithmetic operations need fixed size arithmetic
operands
• INT can be converted in UINT and vice-versa
• “int” in C++
• The size depends on the machine
• Faster in the simulation
• 1-64 bits integer in SystemC
• sc_int<n> -- signed integer with n-bits
• sc_uint<n> -- unsigned integer with n-bits
Arbitrary precision integers
• Integer bigger than 64 bits
• sc_bigint<n>
• sc_biguint<n>
• More precision, slow simulation
• Can be used together with:
• Integer C++
• sc_int, sc_uint
Other SystemC types
• Bit vector
• sc_bv<n>
• 2-value vector (0/1)
• Not used in arithmetics operations
• Faster simulation than sc_lv
• Logic Vector
• sc_lv<n>
• Vector to the sc_logic type
• Assignment operator (“=“)
• my_vector = “XZ01”
String Input and Output
• SystemC data types may be converted to a standard C++ string using the
data type’s to_string method
Operators for SystemC Data Types
Example
• sc_bv<5> positions = "01101";
• sc_bv<6> mask = "100111";
• sc_bv<5> active = positions & mask;// 00101
• sc_bv<1> all = active.and_reduce(); //
SC_LOGIC_0
• positions.range(3,2) = "00";// 00001
• positions[2] = active[0] ^ flag;
Exercises
1. Write a program to read data from a file using the unified string
representation and store in an array of sc_uint<W>. Output the
values as SC_DEC and SC_HEX_SM.
2. Write a program to generate 10 random values and compute the
squares of these values. Do the math using each of the following
data types: short, int, unsigned, long, sc_int<8>, sc_uint<19>,
sc_bigint<8>, sc_bigint<100>. Be certain to generate numbers
distributed over the entire range of possibilities.
3. Write a module from scratch using what you know. The output
should count down from 3 to 1 and display the corresponding
words “Ready”, “Set”, “Go” with each count. Compile and run.
A Notion of Time
• Wall-clock time, processor time, and simulated time
• The simulation’s wall-clock time is the time from the
start of execution to completion, including time
waiting on other system activities and applications
• The simulation’s processor time is the actual time
spent executing the simulation, which will always be
less than the simulation’s wall-clock time
• The simulated time is the time being modeled by
the simulation
• sc_time - represented by a minimum of a 64-bit
unsigned integer
• used by the simulation kernel to track simulated
time and to specify delays and timeouts.
A Notion of Time
• SC_FS, SC_PS, SC_NS, SC_US, SC_MS,SC_SEC
• All objects of sc_time use a single (global) time
resolution that has a default of 1 picosecond
• sc_time_stamp ()
• cout << " The time is now “ << sc_time_stamp()
• The time is now 0 ns
• sc_start() - used to start simulation
• sc_start(60.0,SC_SEC); // Limit sim to one minute
• wait(sc_time) – delays in SC_THREAD processes
//fsm.h detect 101 overlapping sequence case 1:
#include "systemc.h" if (in.read()){
SC_MODULE(fsm){ next_state = 1;out=0;}
sc_in<bool> rst,in,clk; else{
sc_out<bool> out; next_state = 2;out=0;}
sc_uint<2> state,next_state; break;
void update_state(){ case 2:
if (rst.read() == true){ if (in.read()){
state = 0; next_state = 1;out=1;}
} else { else{
state = next_state; next_state = 0;out=0;}
}} break;
void ns_logic(){ default:
// Determine next state and output {
switch(state) { next_state = 0;out=0;}
case 0: break;
if (in.read()) { }
next_state = 1;out=0;} }
else {
next_state = 0; out=0;} break;
SC_CTOR(fsm){
SC_METHOD(update_state); rst.write(false);
sensitive << clk.pos()<<rst; in.write(true);
SC_METHOD(ns_logic); wait();
sensitive <<clk.pos()<<in<<rst; in.write(false);
wait();
}}; in.write(true);
wait();
#include "systemc.h" in.write(false);
SC_MODULE(driver) wait();
{ }
sc_out<bool>rst,in;
sc_in<bool>clk; SC_CTOR(driver)
void inputs() {
{ SC_THREAD(inputs);
rst.write(false); sensitive << clk.pos();
in.write(true); }
wait(); };
rst.write(true);
in.write(true);
wait();
#include "systemc.h"
SC_MODULE(monitor)
{
sc_in<bool> rst, in,out;
sc_in<bool> clk;
void mon()
{
while (true){
cout << sc_time_stamp();
cout << " rst "<<rst.read();
cout << " clk "<<clk.read();
cout << " in "<<in.read();
cout << " out "<<out.read() <<endl;
wait();
}}
SC_CTOR(monitor)
{
SC_THREAD(mon);
sensitive << clk.pos();
}};
#include "systemc.h" mon.rst(rst);
#include "driver.h" mon.in(in);
#include "monitor.h" mon.out(out);
#include "fsm.h" mon.clk(clk);
int sc_main(int argc, char* argv[]) //Waveform
{ sc_trace_file *Tf;
sc_signal <bool> rst,in,out; Tf = sc_create_vcd_trace_file("traces");
sc_clock clk("TestClock",10,SC_NS,0.5);
sc_trace(Tf, rst, "rst");
fsm f_sm("state_machine"); sc_trace(Tf, in, "in1");
driver drive("driver"); sc_trace(Tf, clk, "clk");
monitor mon("monitor"); sc_trace(Tf, out, "out");
sc_trace(Tf, f_sm.state, "state");
f_sm.rst(rst);
f_sm.in(in); sc_start(100,SC_NS);
f_sm.clk(clk);
f_sm.out(out); sc_close_vcd_trace_file(Tf);
drive.rst(rst); return 0;
drive.in(in); }
drive.clk(clk);
Basic Channels
• Primitive Channels
• sc_mutex, sc_semaphore, and sc_fifo<T>
• sc_mutex - Mutex is short for mutually exclusive text
• A program object that lets multiple program threads share a common
resource, such as file access, without colliding
• A mutex will be in one of two exclusive states: unlocked or locked.
• Only one process can lock a given mutex at one time.
• A mutex can only be unlocked by the particular process that
locked the mutex, but may be locked subsequently by a different
process.
• sc_mutex class comes with pre-defined methods as below.
• int lock() : Lock the mutex if it is free, else wait till mutex gets free.
• int unlock() : Unlock the mutex
• int trylock() : Check if mutex is free, if free then lock it else return -1.
Example of sc_mutex used in bus class Used with an SC_METHOD process, access
might look like this
sc_module bus{
sc_mutex bus_access; void grab_bus_method() {
… if (bus_access.trylock() == 0) {
void write(int addr, int data) { // access bus
bus_access.lock(); …
// perform write bus_access.unlock();
bus_access.unlock(); }
} }
…
};
• sc_semaphore
• For some resources, you may want to model
more than one copy or owner.
• Ex: parking spaces in a parking lot
• A multiport memory model
class multiport_RAM {
sc_semaphore read_ports(3);
sc_semaphore write_ports(2);
…
void read(int addr, int& data) {
read_ports.wait();
// perform read
read_ports.post();
}
void write(int addr, const int& data) {
write_ports.wait();
// perform write
write_ports.post();
}
…
};//endclass
• sc_fifo
• Predefined primitive channel intended to model the behavior of a fifo, that is,
a first-in first-out buffer. Each fifo has a number of slots for storing values.
• The number of slots is fixed when the object is constructed. Default slots
size is 16.
• sc_fifo has following predefined methods.
• write() : This method write the values passed as an argument into the fifo. If
fifo if full then write() function waits till fifo slot is available
• nb_write() : This method is same as write(), only difference is, when fifo is
full nb_write() does not wait till fifo slot is avaible. Rather it returns false.
• read() : This method returns the least recent written data in fifo. If fifo is
empty, then read() function waits till data is available in fifo.
• nb_read() :This method is same as read(), only difference is, when fifo is
empty, nb_read() does not wait till fifo has some data. Rather it returns false.
• num_available() : This method returns the numbers of data values available
in fifo in current delta time.
• num_free() : This method returns the number of free slots available in fifo in
current delta time.
Example void fifowrite(void) {
int val = 10;
#include "systemc.h"
SC_MODULE(example_fifo) { for (;;) {
// Declare the FIFO wait(2, SC_NS);
sc_fifo<int> packet_fifo; val++;
packet_fifo.write(val);
void fiforead(void) { cout << sc_time_stamp() << ": wrote " << val << " No.
int val; of Free Slots in FIFO " <packet_fifo.num_free()<<endl;
for (;;) { }
wait(3, SC_NS); }
packet_fifo.read(val);
cout << sc_time_stamp() << ": Read " << val <<endl;
//cout << sc_time_stamp()<<"No.of Data in FIFO " SC_CTOR(example_fifo) :
<<packet_fifo.num_available()<<endl; packet_fifo(25)
{
}
SC_THREAD(fifowrite);
} SC_THREAD(fiforead);
}
};
#include "systemc.h"
#include "fifo.h"
int sc_main(int argc, char* argv[]) {
example_fifo ex_fifo ("ex_fifo0");
sc_start(10, SC_NS);
return 0;
}
SystemC Highlights (1)
• Support Hardware-Software Co-Design
• Interface in a C++ environment
• Modules
• Container class includes hierarchical Entity and Processes
• Processes
• Describe functionality, Event sensitivity
• Ports
• Single-directional(in, out), Bi-directional(inout) mode
• Signals
• Resolved, Unresolved signals
• Rich set of port and signal types
• Rich set of data types
• All C/C++ types, 32/64-bit signed/unsigned, fixed-points, MVL, user
defined
SystemC Highlights (2)
• Interface in a C++ environment (continued)
• Clocks
• Special signal, Timekeeper of simulation and Multiple clocks,
with arbitrary phase relationship
• Cycle-based simulation
• High-Speed Cycle-Based simulation kernel
• Multiple abstraction levels
• Untimed from high-level functional model to detailed clock cycle
accuracy RTL model
• Communication Protocols
• Debugging Supports
• Run-Time error check
• Waveform Tracing