Track 3: ESL and SystemC
The Definitive Guide to SystemC:
The SystemC Language
David C Black, Doulos
Track 3: The Definitive Guide to SystemC
The SystemC Language
Introduction to SystemC
Overview and background
Central concepts
The SystemC World
Use cases and benefits
Core Concepts and Syntax
Bus Modeling
Odds and Ends
2
What is SystemC?
System-level modeling language
Network of communicating processes (c.f. HDL)
Supports heterogeneous models-of-computation
Models hardware and software
C++ class library
Open source proof-of-concept simulator
Owned by Accellera Systems Initiative
Communicating
processes
3
Features of SystemC
Modules (structure)
Ports (structure)
Processes (computation, concurrency)
Channels (communication)
Interfaces (communication refinement)
Events (time, scheduling, synchronization)
Data types (hardware, fixed point)
Port Interface
Module Module
Process
Channel Process
4
Modules and Channels
Module Module
Process Channel Process
Functionality / Separated! Communication /
Computation Protocol
Architecture of SystemC
User Applications
SystemC
Other Model
Verification TLM Library
Libraries
Library SCV
Primitive Channels
(signal, buffer, fifo, mutex, semaphore)
Core Language
(module, port, process, Data Types
channel, interface, event)
C++ Language
6
Typical Use Case: Virtual Platform
Multiple software stacks
Software Software
CPU ROM RAM DMA DSP ROM RAM
Bridge
Interrupt Timer I/O Bridge Interrupt Timer A/D
Multiple buses and bridges
TLM-2.0
Memory Custom
I/O RAM DMA D/A
interface peripheral
Digital and analog hardware IP blocks
10
Track 3: The Definitive Guide to SystemC
The SystemC Language
Introduction to SystemC
Core Concepts and Syntax
Data
Modules and connectivity
Processes & Events
Channels and Interfaces
Ports
Bus Modeling
Odds and Ends
14
SystemC Data Types
In namespace sc_dt::
Template Base class Description
sc_int<W> sc_int_base Signed integer, W < 65
sc_uint<W> sc_uint_base Unsigned integer, W < 65
sc_bigint<W> sc_signed Arbitrary precision signed integer
sc_biguint<W> sc_unsigned Arbitrary precision unsigned integer
(intermediate results unbounded)
sc_logic 4-valued logic: '0' '1' 'X' 'Z'
sc_bv<W> sc_bv_base Bool vector
sc_lv<W> sc_lv_base Logic vector
sc_fixed<> sc_fix Signed fixed point number
sc_ufixed<> sc_ufix Unsigned fixed point number
15
Limited Precision Integer sc_int
int i;
sc_int<8> j;
i = 0x123;
sc_assert( i == 0x123 );
j = 0x123; Truncated to 8 bits
sc_assert( j == 0x23 );
sc_assert( j[0] == 1 ); Bit select
sc_assert( j.range(7,4) == 0x2 ); Part select
sc_assert( concat(j,j) == 0x2323 ); Concatenation
Other useful operators: arithmetic, relational, bitwise, reduction, assignment
length() to_int() to_string() implicit-conversion-to-64-bit-int
16
Logic and Vector Types
sc_logic and sc_lv<W>
Values SC_LOGIC_0, SC_LOGIC_1, SC_LOGIC_X, SC_LOGIC_Z
Initial value is SC_LOGIC_X
No arithmetic operators
sc_logic R, S; sc_int<4> n = "0b1010";
R = '1'; bool boo = n[3];
S = 'Z'; sc_lv<4> lv = "01XZ";
S = S & R; sc_assert( lv[0] == 'Z' );
n += lv.to_int();
cout << n.to_string(SC_HEX);
17
Fixed Point Types
sc_fixed <wl, iwl, q_mode, o_mode, n_bits> a;
sc_ufixed<wl, iwl, q_mode, o_mode, n_bits> b;
sc_fix c(wl, iwl, q_mode, o_mode, n_bits);
sc_ufix d(wl, iwl, q_mode, o_mode, n_bits);
Word length - number of stored bits - no limit
Integer word length - number of bits before binary point
Quantization mode - behavior when insufficient precision
Overflow mode - behavior when result too big
Number of saturated bits - used with wrap overflow modes
Compiler flag -DSC_INCLUDE_FX
18
Data Summary
C++ SystemC SystemC SystemC
Unsigned unsigned int sc_bv, sc_lv sc_uint sc_biguint
Signed int sc_int sc_bigint
Precision Host-dependent Limited Limited Unlimited
precision precision precision
Operators C++ operators No arithmetic Full set of Full set of
operators operators operators
Speed Fastest Faster Slower Slowest
19
Modules
Module
Module Module
Channel
Process Channel Process
Ports
Instances
- connection
Channels
Processes
of other
- communication
-to
modules
computation
external
- hierarchy
channels
20
SC_MODULE
#include "systemc.h"
Class SC_MODULE(Mult)
{
sc_in<int> a;
Ports sc_in<int> b;
sc_out<int> f;
void action() { f = a * b; }
Constructor SC_CTOR(Mult)
{
SC_METHOD(action); Process
sensitive << a << b;
}
};
21
SC_MODULE or sc_module?
Equivalent SC_MODULE(Name)
: {
...
};
struct Name: sc_module
{
...
};
class Name: public sc_module
{
public:
...
};
22
Separate Header File
// mult.h
#include "systemc.h"
// mult.cpp
SC_MODULE(Mult)
#include "mult.h"
{
sc_in<int> a; void Mult::action()
sc_in<int> b; {
f = a * b;
sc_out<int> f; }
void action();
SC_CTOR(Mult)
{
SC_METHOD(action);
Define constructor in .cpp?
sensitive << a << b;
Yes - explained later
}
};
23
The Test Bench
Top
testclk
asig
fsig
Mult
Stim Mon
bsig f=a*b
Module Port Channel
25
Top Level Module
#include "systemc.h"
#include "stim.h"
Header files #include "mult.h"
#include "mon.h"
SC_MODULE(Top)
{
sc_signal<int> asig, bsig, fsig;
Channels
sc_clock testclk;
Stim stim1;
Modules Mult uut;
Mon mon1;
...
}
26
Module Instantiation
SC_MODULE(Top)
{
sc_signal<int> asig, bsig, fsig;
sc_clock testclk;
Stim stim1;
Mult uut;
Mon mon1;
Name of data member
SC_CTOR(Top)
: testclk("testclk", 10, SC_NS),
stim1("stim1"),
uut ("uut"),
mon1 ("mon1")
{
...
} String name of instance (constructor argument)
}
27
Port Binding
SC_CTOR(Top)
: testclk("testclk", 10, SC_NS),
stim1("stim1"),
uut("uut"),
mon1("mon1")
{
stim1.a(asig);
stim1.b(bsig);
stim1.clk(testclk);
uut.a(asig);
uut.b(bsig);
uut.f(fsig); Alternative function
mon1.a.bind(asig);
mon1.b.bind(bsig);
mon1.f.bind(fsig);
mon1.clk.bind(testclk);
}
Port name Channel name
28
sc_main
sc_main is the entry point to a SystemC application
#include "systemc.h"
#include "top.h"
Called from main()
int sc_main(int argc, char* argv[])
{
Top top("top"); Instantiate one top-level module
sc_start(); End elaboration, run simulation
return 0;
}
29
Namespaces
#include "systemc.h" Old header - global namespace
SC_MODULE(Mod)
{
sc_in<bool> clk;
sc_out<int> out;
... cout << endl;
#include "systemc" New header #include "systemc"
using namespace sc_core;
SC_MODULE(Mod) using namespace sc_dt;
{ using std::cout;
sc_core::sc_in<bool> clk; using std::endl;
sc_core::sc_out<int> out;
SC_MODULE(Mod) {
... std::cout << std::endl; sc_in<bool> clk;
sc_out<int> out;
... cout << endl; 31
31
Summary of Files
systemc.h
stim.h mult.h mon.h
stim.cpp mult.cpp mon.cpp
#include
top.h
main.cpp
32
Compilation and Simulation
C++ Development
.cpp
SystemC Pre-compiled Environment
.h User's
class libraries headers? Text Editor source
.cpp files
.h
Compiler
Makefile
Linker make
Pre-compiled
library
Executable
prompt> make
prompt> run.x
Debugger
prompt> ddd run.x
33
Kinds of Process
Processes
Must be within a module (not in a function)
A module may contain many processes
Three different kinds of process
Methods SC_METHOD
Threads SC_THREAD
Clocked threads SC_CTHREAD (for synthesis)
Processes can be static or dynamic
34
SC_METHOD Example
#include <systemc.h> template<class T>
void Register<T>::entry()
template<class T> {
SC_MODULE(Register) if (reset)
{ q = 0; // promotion
sc_in<bool> clk, reset; else if (clk.posedge())
sc_in<T> d; q = d;
sc_out<T> q; }
void entry(); SC_METHODs execute in zero
time
SC_CTOR(Register)
SC_METHODs cannot be
{ suspended
SC_METHOD(entry);
sensitive << reset; SC_METHODs should not
sensitive << clk.pos();
contain infinite loops
}
};
35
SC_THREAD Example
#include "systemc.h" #include "stim.h"
SC_MODULE(Stim) void Stim::stimulus()
{ {
sc_in<bool> Clk; wait();
sc_out<int> A; A = 100;
sc_out<int> B; B = 200;
wait(); for Clk edge
void stimulus(); A = -10;
B = 23;
SC_CTOR(Stim) wait();
{ A = 25;
SC_THREAD(stimulus); B = -3;
sensitive << Clk.pos(); wait();
} sc_stop(); Stop simulation
}; }
More general and powerful than an SC_METHOD
Simulation may be slightly slower than an SC_METHOD
Called once only: hence often contains an infinite loop
36
SC_HAS_PROCESS
#include "systemc.h"
class Counter: public sc_module
{
public:
sc_in<bool> clock, reset;
sc_out<int> q;
Constructor arguments
Counter(sc_module_name _nm, int _mod)
: sc_module(_nm), count(0) , modulus(_mod)
{
SC_HAS_PROCESS(Counter);
Needed if there's a process
SC_METHOD(do_count);
and not using SC_CTOR
sensitive << clock.pos();
}
private:
void do_count();
int count;
int const modulus;
};
37
Dynamic Sensitivity
SC_CTOR(Module)
{
SC_THREAD(thread);
sensitive << a << b; Static sensitivity list
}
void thread()
{
for (;;)
{
wait(); Wait for event on a or b
...
wait(10, SC_NS); Wait for 10ns
... ignore a or b
wait(e); Wait for event e
...
}
}
38
sc_event and Synchronization
SC_MODULE(Test)
{ Shared variable
int data;
sc_event e;
SC_CTOR(Test)
{ Primitive synchronization object
SC_THREAD(producer);
SC_THREAD(consumer);
}
void producer()
{
wait(1, SC_NS);
for (data = 0; data < 10; data++) {
e.notify();
wait(1, SC_NS); Schedule event immediately
}
}
void consumer()
{ Resume when event occurs
for (;;) {
wait(e);
cout << "Received " << data << endl;
}
}
};
39
sc_time
Simulation time is a 64-bit unsigned integer
Time resolution is programmable - must be power of 10 x fs
Resolution can be set once only, before use and before simulation
Default time resolution is 1 ps
enum sc_time_unit {SC_FS, SC_PS, SC_NS, SC_US, SC_MS, SC_SEC};
sc_time(double, sc_time_unit); Constructor
void sc_set_time_resolution(double, sc_time_unit);
sc_time sc_get_time_resolution();
const sc_time& sc_time_stamp(); Get current simulation time
40
sc_clock
sc_time sc_time
sc_clock clk("clk", period, 0.4, firstedge, true);
sc_clock clk("clk", 10, SC_NS, 0.4, 12, SC_NS, true);
Duty cycle 1st edge rising
0 12 16 22 26 32
("clock_N", 1, SC_NS, 0.5, 0, SC_NS, true);
Defaults
Avoid clocks altogether for fast models!
41
Elaboration / Simulation Callbacks
Instantiation
Elaboration sc_start()
before_end_of_elaboration()
end_of_elaboration()
start_of_simulation()
Every process without
Initialization Phase
dont_intialize() runs once
Simulation
Scheduler
sc_stop()
Done
end_of_simulation()
44
Overriding the Callbacks
SC_MODULE(Test)
Called for each {
SC_CTOR(Test) {}
Module
void before_end_of_elaboration(){...}
Primitive channel void end_of_elaboration() {...}
void start_of_simulation() {...}
Port void end_of_simulation() {...}
};
Export
Do nothing by default
before_end_of_elaboration() may perform instantiation and port binding
In PoC simulator, end_of_simulation() only called after sc_stop()
45
The Scheduler in Detail
sc_start()
Initialization
Runnable processes {R} Evaluation request_update()
notify() {R} empty
Update requests {U}
sensitive
notify(0) Update
{U} empty
Delta notifications {D} Empty {D} into {R}
{R} empty
{T} empty
notify(>0) sc_stop()
Advance time
Timed notifications {T} All at current
Done
time from {T} into
{R} 46
Kinds of Channel
Primitive channels
Implement one or more interfaces
Derived from sc_prim_channel
Have access to the update phase of the scheduler
Examples - sc_signal, sc_signal_resolved, sc_fifo
Hierarchical channels
Implement one or more interfaces
Derived from sc_module
Can instantiate ports, processes and modules
Minimal channels - implement one or more interfaces
47
Built-in Primitive Channels
Channel Interfaces Events
sc_signal<T> sc_signal_in_if<T> value_changed_event()
sc_signal_inout_if<T>
sc_buffer<T> Same as sc_signal On every write()
sc_signal_resolved Same as Same a sc_signal
sc_signal_rv<W> sc_signal<sc_logic>
sc_clock Same as sc_signal<bool> posedge & negedge
sc_fifo<T> sc_fifo_in_if<T> data_written_event()
sc_fifo_out_if<T> data_read_event()
sc_mutex sc_mutex_if n/a
sc_semaphore sc_semaphore_if n/a
sc_event_queue n/a Every notify() invocation
48
Interface Method Call
Port Interface Port-less access
Module
Channel Process
Process
Required interface Implements the interface
An interface declares of a set of methods (pure virtual functions)
An interface is an abstract base class of the channel
A channel implements one or more interfaces (c.f. Java)
A module calls interface methods via a port
49
Declare the Interface
Important
#include "systemc"
class queue_if : virtual public sc_core::sc_interface
{
public:
virtual void write(char c) = 0;
virtual char read() = 0;
};
50
Queue Channel Implementation
#include "queue_if.h"
class Queue : public queue_if, public sc_core::sc_object
{
public:
Queue(char* nm, int _sz)
: sc_core::sc_object(nm), sz(_sz)
{ data = new char[sz]; w = r = n = 0; }
void write(char c);
char read(); Implements interface methods
private:
char* data;
int sz, w, r, n;
};
51
Understanding Ports
Port Required interface Provided interface
Module
Channel
Process
sc_port<i_f> p; struct Chan: i_f,
sc_module
{
p->method(); void method() {...}
};
struct i_f: virtual sc_interface
{
virtual void method() = 0;
};
52
Queue Ports
class Producer : public sc_core::sc_module
{
public:
sc_core::sc_port<queue_write_if> out;
void do_writes();
SC_CTOR(Producer)
{
SC_THREAD(do_writes);
}
};
queue_write_if queue_read_if
Producer Consumer
Channel
53
Calling Methods via Ports
#include <systemc>
#include "producer.h"
using namespace sc_core;
void Producer::do_writes()
{
std::string txt = "Hallo World.";
for (int i = 0; i < txt.size(); i++)
{
wait(SC_ZERO_TIME);
out->write(txt[i]);
}
}
Note: -> overloaded Interface Method Call
54
Why Ports?
Ports allow modules to be independent of their environment
Ports support elaboration-time checks (register_port,
end_of_elaboration)
Ports can have data members and member functions
Producer Consumer
Channel
55
Exports
Port Channel Port
Producer Consumer
write() read()
Queue
Module
Port Export Export Port
Producer Consumer
write() read()
Queue
Channel
Producer Consumer
write() read()
Queue
56