Upload
others
View
7
Download
0
Embed Size (px)
Citation preview
High level hardware design
and verification with SystemC
Mikhail Moiseev
2015
2
Introduction
Curriculum
SystemC language basic
SystemC modeling semantic
Synthesizable subset of SystemC
Real-world design architecture in SystemC
SystemC design verification
TLM 2.0
High level synthesis with Cadence CtoS tool
Knowledge required
C++, Verilog/VHDL, Hardware design
3
Hardware design techniques
Year Hardware complexity Design technique
1970 Tiny CPU, 10^4 Gate-level design
1980 One core CPU, 10^6 Gate-level design and Verilog/VHDL
1990 2-4 core CPU, 10^8 Verilog/VHDL
2000 8-32 core CPU and SoC, 10^10 Verilog/VHDL/System Verilog and
ESL design with C/C++
2010 Manycore CPU and large SoC Verilog/VHDL/System Verilog and
ESL design with
SystemC/OpenCL/C/C++
4
SystemC language
SystemC is a C++ based language and a methodology to create a models
of hardware and hardware/software designs
SystemC intended for
System level modelling System-on-Chip (SoC)
Algorithm verification and parameter estimation
Hardware design, verification and synthesis
Virtual prototyping for software/hardware co-design and co-verification
…
SystemC implemented as a C++ template library
SystemC is defined and promoted by the Accellera Systems Initiative
www.accelera.org
5
History
1999 Open SystemC Initiative announced
2000 SystemC v1.0 released
2001 SystemC v2.0 specification
2005 SystemC 2.1 and TLM 1.0 standard
2005 IEEE approves the IEEE 1666–2005 standard for SystemC
2007 SystemC v2.2 released
2009 TLM-2.0 released
2011 IEEE approves the IEEE 1666–2011 standard for SystemC
2014 SystemC v2.3.1 released
2016 SystemC synthesizable subset standard 1.4.7
6
What is System-on-Chip?
System-on-Chip is a hardware or hardware/software implementation of a
system
CPU coreCPU core
HW
accelerator 1
HW
accelerator N
Inter chip comminucation
On-chip
memory
External bus
controller
...
7
Classic SoC design methodology
Reference
code C, C++,
...
Specification
with some
algorithms
Analysis
C, C++
System Level
Model
Verilog/VHDL
Results
Simulation
Synthesis
C, C++ for
CPU cores
Compilation
Manual
transformation
8
SystemC based methodology
Reference
code C, C++,
...
Specification
with some
algorithms
SystemC
Simulation
Refinement
High level
synthesis
Verilog/VHDL
Automatic
transformation
C, C++ for
CPU cores
9
Why SystemC is based on C++?
Reference algorithms usually presented in C, C++
SystemC based on C++ to provide
Seamless transition from reference code to target implementation
Reuse reference testbench
C++ quite popular language, specially in HW world
What C++ standard is SystemC based?
There are other languages for hardware synthesis based on
Scala (chisel)
Phython (pyverilog)
10
SystemC model levels
Cycle-accurate models
Model behaviour strictly corresponded to target system
Slow simulation, high analysis precision
Approximately-timed (TLM 2.0 supported)
Time information annotated into process interaction or timed events, processes
executed in common simulation time
Fast simulation
Loosely-timed (TLM 2.0 supported)
Time information annotated into process interaction, processes executed in its
own time up to explicit synchronization points
Very fast simulation
Untimed
No time annotations, processes synchronization in pre-determined points
11
Use case and coding styles
Use case Coding style
Virtual prototyping and software
performance analysis
Loosely-timed, untimed
Hardware architectural analysis and
functional verification
Loosely-timed, approximately-timed
Hardware performance verification Approximately-timed, cycle-accurate
Synthesis of SoC top level Approximately-timed, cycle-accurate
Synthesis of critical hardware modules
and SoC`s
Cycle-accurate
12
Hello world!!!
#include "systemc.h"
int sc_main(int argc, char* argv[]) {
cout << "Hello world!!!" << endl;
}
13
Hello world in SC process
#include "systemc.h"
SC_MODULE(MyModule) {
SC_HAS_PROCESS(MyModule);
SC_CTOR(MyModule) {
SC_THREAD(myProc); // Run process with myProc function
}
void myProc() {
cout << "Hello world!!!" << endl;
}
};
int sc_main(int argc, char* argv[]) {
MyModule m("MyModule");
sc_start();
}
14
SystemC installation
Requirement
Linux, gcc/clang
Windows, MinGW/Cygwin/VS
Download systemc-2.3.1 from Accellera web-site
http://www.accellera.org/downloads/standards/systemc
Linux installation, unpack and follow INSTALL instructions
mkdir objdir
cd objdir
../configure –enable-debug –disable-optimize
make
make install
15
Make with SystemC
$SYSTEMC_HOME = /opt/systemc-2.3.1/
include/systemc.h
include/tlm.h
lib-linux64/systemc.a (/lib-mingw)
lib-linux64/systemc.so
g++
-I$(SYSTEMC_HOME)/include
-L$(SYSTEMC_HOME)/lib-linux64 -lsystemc
16
SystemC language basics
17
SystemC project structure
SystemC project includes
Design under test (DUT)
Testbench (TB)
sc_main function where DUT and TB are constructed and simulation
run with sc_start call
DUTTB
sc_main
18
Design under test
DUT contains one or more modules (sc_module class inheritor
instances) which are connected to each other and to TB by signals
(sc_signal class instances)
Module is the main structural unit, like module in Verilog
Processes, which implement logic
Internal and external signals and ports
Data fields
Module instances
19
sc_module
sc_module implemented as a C++ class which is used as base class
for the design modules
SC_MODULE(MyModule){...}
class MyModule : public sc_module{...}
SC_HAS_PROCESS(MyModule)
Module instantiation
MyModule m(“MyModule”)
MyModule* m = new MyModule(“MyModule”)
m(“MyModule”), …
Module in SystemC design is C++ class
There are some limitation for synthesizable DUT modules
20
sc_module example
class A : public sc_module {
public:
A(const sc_module_name& name) : sc_module(name) {}
};
SC_MODULE(B) {
A a;
SC_CTOR(B) :
a("a")
{}
};
int sc_main(int argc, char* argv[]) {
B* b1 = new B("b1");
B b2("b2");
sc_start();
}
21
sc_module example
template<class T, int N>
class A : public sc_module {
public:
A(const sc_module_name& name) : sc_module(name) {}
private:
T fifo_array[N];
};
SC_MODULE(B) {
A<char, 5> a;
SC_CTOR(B) :
a("a")
{}
};
22
Processes in SystemC
There are three kinds of processes macro defined in sc_module
SC_METHOD(fname)
SC_THREAD(fname)
SC_CTHREAD(fname, clk)
Called from module constructor
Process function has signature void(void)
It is normal C++ function may have local variables, other function calls, ...
DUT logic is usually implemented in processes, not in module constructors
23
sc_time
sc_time represents simulation time and time intervals, including delays and
time-outs
sc_time is implemented as a C++ class
sc_time()
sc_time(double val, sc_time_unit time_unit)
enum sc_time_unit {SC_FS, SC_PS, SC_NS, SC_US, SC_MS, SC_SEC}
24
sc_time example
int sc_main(int argc, char* argv[]) {
sc_time t1(1.5, SC_PS);
sc_time t2(10, SC_NS);
sc_time t3(50.5, SC_MS);
}
25
sc_event
sc_event is an object for process synchronization
Process may be triggered or resumed by event notification
Event notification works for all processes, waiting for the event
Event notification has no delayed effect
sc_event is implemented as a C++ class which is instantiated as a
global variable or a sc_module field
sc_event()
sc_event(char* name)
notify()
notify(sc_time&)
cancel()
26
sc_event example
sc_event e1;
class A : public sc_module {
public:
sc_event e2;
sc_event* e4;
A(const sc_module_name& name, sc_event* e) :
sc_module(name),
e2("e2"),
e4(e)
{}
};
int sc_main(int argc, char* argv[]) {
sc_event* e3 = new sc_event("e3");
A a("a", e3);
}
27
sc_clock
sc_clock is intended to model the behavior of a digital clock signal
sc_clock is implemented as a C++ class which is usually
instantiated in sc_main()
sc_clock(char* name)
sc_clock(char* name, sc_time& period,
double duty_cycle = 0.5,
sc_time& start_time = SC_SERO_TIME,
bool posedge_first = true)
sc_event& posedge_event()
sc_event& negedge_event()
28
sc_in_clk
sc_in_clk is input clock port in sc_module
inheritor of sc_in<bool>, which is input port of 1-bit value
sc_in_clk
void bind (sc_signal_in_if<bool>& )
void operator() (sc_signal_in_if<bool>& )
sc_event_finder& pos()
sc_event_finder& neg()
29
sc_clock and sc_in_clk example
class A : public sc_module {
public:
sc_in_clk clk;
A(const sc_module_name& name) :
sc_module(name),
clk("clk")
{}
};
int sc_main(int argc, char* argv[]) {
sc_clock clock1("clock1", sc_time(10, SC_NS));
A a("a");
a.clk(clock1); // a.clk.bind(clock1);
}
30
sc_sensitive
sc_sensitive provides the operators to build the static sensitivity of
processes
sc_sensitive
sc_sensitive& operator<< (sc_event& )
sc_sensitive& operator<< (sc_interface& )
sc_sensitive& operator<< (sc_port_base& )
sc_sensitive& operator<< (sc_event_finder& )
31
SC_METHOD
Method process implements logic which is activated by an event the process
is sensitive
Method function is called each time the event occurred
Method function is usually stateless
Method body is executed in one delta cycle
Method sensitivity is specified with context variable sensitive which is instance
of sc_sensitive class
HL synthesis of SC_METHOD produces combinational logic
32
SC_METHOD exampleSC_MODULE(A) {
public:
sc_in_clk clk;
sc_in<bool> enbl;
SC_CTOR(A) : clk("clk") {
SC_METHOD(proc);
sensitive << clk.pos() << enbl;
}
void proc() {
if (enbl) cout << sc_time_stamp() << endl;
}
};
int sc_main(int argc, char* argv[]) {
sc_clock clk("clk", sc_time(10, SC_NS));
A a("a");
a.clk(clk);
sc_start();
}
33
next_trigger method
next_trigger call from SC_METHOD process body, sets dynamic
sensitivity of the current process, which overrides static sensitivity
Dynamic sensitivity is set up to very next process occasion and only for that
In multiple calls of next_trigger only last call is applied
next_trigger is a method of sc_module and sc_prim_channel
next_trigger() – static sensitive is activated
next_trigger(sc_event& e) – process triggered by event e
next_trigger(sc_time& t) – process triggered after the time given
next_trigger(sc_time& t, sc_event& e)
34
next_trigger exampleSC_MODULE(A) {
public:
sc_in<bool> rst;
sc_in<bool> enbl;
SC_CTOR(A) : clk("clk") {
SC_METHOD(proc);
sensitive << rst;
}
void proc() {
if (rst) {
next_trigger(enbl);
} else
if (enbl) {
cout << “do something” << endl;
next_trigger();
}
}};
35
Method another exampletemplate<class T> A : public sc_module {
public:
sc_in<T> a, b;
sc_out<T> sum;
sc_signal<bool> sign; // sc_signal may be used in module interface
SC_HAS_PROCESS(A);
A(const sc_module_name& name) : sc_module(name),a(“a"),b(“b”),sum(“sum”) {
SC_METHOD(proc);
sensitive << a << b;
}
void proc() {
sign = a > b;
sum = (a > b) ? a - b : a + b;
}
};
36
wait method
wait suspend process execution to the corresponding event or specified time
wait is a method of sc_module
May be called from module processes only
wait() – suspend process until next event
wait(int n) – suspend process until n next events
wait(sc_event& e) – suspend process until event e
wait(sc_time& t) – suspend process period t
wait(SC_ZERO_TIME) – suspend process to next delta cycle
37
SC_THREAD
Thread process implements logic which is resumed by an event the process is
sensitive
Thread function is called once if no reset happens
Thread function usually has state based on local or global variable
Thread function usually has reset section and main loop, which executes infinitely
Thread sensitivity is specified with context variable sensitive
Thread function may call all wait methods
HL synthesis of SC_THREAD produces sequential and combinational logic
38
SC_THREAD example 9SC_MODULE(A) {
sc_in_clk clk;
sc_in<bool> rst;
sc_in<T> A, B; sc_out<T> sum;
SC_CTOR(A) {
SC_THREAD(proc);
sensitive << clk.pos();
async_reset_signal_is(rst, true);
}
void proc() {
sum = 0; // Reset actions
wait();
while (true) {
sum = A + B;
cout << "Main loop " << sc_time_stamp() << endl;
wait();
}}
};
39
SC_CTHREAD
Clocked thread process is sensitive to single clock, given as SC_CTHREAD second
argument
Clocked thread function is called once if no reset happens
Clocked thread function has state based on local or global variable
Clocked thread function usually has reset section and infinite main loop
sensitive is not used for clocked thread
Clocked thread function may call wait() and wait(int) methods
HL synthesis of SC_CTHREAD produces sequential and combinational logic
40
SC_CTHREADSC_MODULE(A) {
sc_in_clk clk;
sc_in<bool> rst;
SC_CTOR(A) {
SC_CTHREAD(proc, clk.pos());
async_reset_signal_is(rst, true);
}
void proc() {
cout << "Reset actions" << endl;
wait();
while (true) {
cout << "Main loop " << sc_time_stamp() << endl;
wait();
}
}
};
41
Cthread another exampleSC_MODULE(A) {
sc_in_clk clk;
sc_in<bool> nrst;
sc_in<bool> satur;
sc_out<T> counter;
SC_CTOR(A) {
SC_CTHREAD(proc, clk.pos());
async_reset_signal_is(nrst, false);
}
void proc() {
counter = 0;
wait();
while (true) {
counter = (counter == MAX_T-1) ?
(satur ? MAX_T-1 : 0) : counter+1;
wait();
}
}};
42
Process and module connection
Process interaction, hierarchical structure and module connection are
provided with channels, ports and exports
sc_in, sc_out, sc_inout
sc_signal, sc_fifo
All channels inherited sc_prim_channel
sc_in sc_out
sc_signal
sc_signal
sc_in
sc_out
sc_signal
sc_signal
43
sc_signal
sc_signal is a predefined primitive channel intended to model the behavior
of a single piece of wire(s)
Corresponded to net or register type in Verilog
sc_signal<class T> is template class
T specifies signal type
T& read() – get current value of signal
write(T&) – set new value of signal in the next delta cycle
operator = () – to skip write()
operator T() – to skip read()
sc_event& value_changed_event
Normally has one writer and one or more readers
44
sc_signal with a bug
SC_MODULE(A) {
sc_signal<bool> s;
// proc1 and proc2 are SC_CTHREAD`s
void proc1() {
while (true) {
s.write(!s.read());
wait();
}}
void proc2() {
while (true) {
if (s.read()) cout << "s is on " << endl;
wait();
}}
45
sc_signal fixed
sc_signal<unsigned> b;
void proc1() {
s.write(0);
unsigned a = …;
wait();
while (true) {
s.write(!s.read());
wait();
b = a+1; // b.write(a);
}
}
46
sc_signal example w/o read/write
void proc1() {
s = 0;
wait();
while (true) {
s = !s;
wait();
}}
void proc2() {
while (true) {
if (s) cout << "s is on " << endl;
wait();
}}
47
sc_in
sc_in is a specialized port class to use with signals
Intended to bind input signal
Corresponded to input in Verilog
sc_in<class T> is template class
T& read() – get current value of the signal bound
sc_event& value_changed_event
void bind (sc_signal_in_if<bool>& )
void operator() (sc_signal_in_if<bool>& )
sc_in instances are fields of sc_module only
48
sc_out, sc_inout
sc_out is a specialized port class to use with signals
Intended to bind output signal
Corresponded to output in Verilog
sc_out<class T> is template class
write(T&) – set new value of signal in the next delta cycle
T& read() – get current value of the signal bound
sc_event& value_changed_event
void bind (sc_signal_in_if<bool>& )
void operator() (sc_signal_in_if<bool>& )
sc_inout is a bidirectional port
sc_out,sc_inout instances are fields of sc_module only
49
sc_in and sc_out
bool c;
SC_MODULE(A) {
sc_out<bool> so;
void proc() {
c = 1;
}
}
SC_MODULE(B) {
sc_in<bool> si;
void proc() {
… = c;
}
}
int sc_main(int argc, char* argv[]) {
…
sc_signal<bool> s("s");
a.so(s);
b.si(s);
50
Reset signal
Reset signal usually is bound to sc_in<bool> port
SC_METHOD reset is specified in sensitivity list
sensitive << reset
SC_CTHREAD and SC_THREAD use special function to specify reset signals
reset_signal_is(sc_in<bool>&, bool)
async_reset_signal_is(sc_in<bool>&, bool)
51
Reset signal
sc_in<bool> nrst;
sc_in<bool> rst_sync;
SC_CTOR(A) : rst("rst")
{
SC_CTHREAD(proc, clk.pos());
async_reset_signal_is(nrst, false);
sync_reset_signal_is(rst_sync, true);
}
52
SystemC simulation semantic
53
Elaboration and simulation phases
Elaboration
Construction of the module hierarchy
Callbacks before_end_of_elaboration, end_of_elaboration execution – virtual
methods of sc_module
Simulation
Callbacks start_of_simulation execution
Initialization phase
Evaluation, update, delta notification, timed notification phases
Callbacks end_of_simulation execution
Destruction of module hierarchy
54
Elaboration phase
Instances created on elaboration phase
sc_module – may be created in sc_main and in other modules
sc_prim_channel - may be created in sc_main and in other modules
sc_port/sc_export - may be created in modules only
All unspawned processes are created in elaboration phase with
SC_METHOD, SC_THREAD and SC_CTHREAD macros
Port and export binding is done is elaboration phase
In the end there is complete design structure
55
Simulation phase
Simulation of the design and testbench
There is a set of processes created in elaboration phase
Processes are executed under control of the scheduler
All processes are started in initialization phase
Non-preemptive multitasking, each SC_THREAD/SC_CTHREAD process works up
to suspend, each SC_METHOD process works up to return process at function end
Scheduler
Determines runnable processes
Runs them one by one
Gathers update requests, and notifications
Increase simulation time
56
Initialization phase
Initialization phase starts once after simulation start
Run update phase
Mark all processes as runnable except marked as dont_initialize
Run delta notification phase
Initialization phase required to set up initial values of channels
57
Simulation time
Simulation time is measured in ns, ps, fs
sc_set_time_resolution()
In synchronous designs there is one or a few clocks, which may have different
periods
Time resolution determines as minimal clock period or minimal period in timed
notification
SC_CTHREAD and SC_THREAD processes which have clock event only in
sensitivity list are executed once pre clock tick by rising or falling clock edge
58
Delta cycle
SC_METHOD process may be triggered by event notified in clocked thread
process and may notify an event, which triggers another method process
Do these activities run at the same simulation time?
To represent non-timed event order delta cycles are used
SC_CTHREAD SC_METHOD1 SC_METHOD2sc_signal
sc_time_stamp = t
sc_signal
59
Delta cycle
Delta cycle is zero long period of time, in which all values of SC channels are
updated
If a channel written at delta cycle i, then new value may be read at delta cycle i+1
Each moment of time contains a number of delta cycles enough to present all
process interactions
t325 ns 326 ns
Time resolution = 1 ns
1 2 30 ...
60
Notification types
Event notification provided with notify method of sc_event
Immediate notification – in the current delta cycle
notify()
Delta notification – in the next delta cycle
notify(SC_ZERO_TIME), #define SC_ZERO_TIME 0
Timed (delayed) notification – after the specified time period
notify(sc_time) – method of sc_event
61
Evaluation phase
Scheduler resumes or triggers each runnable process
Process runs without interruption up point where it suspends or returns
SC_THREAD/SC_CTHREAD process suspend on wait/suspend call
SC_METHOD returns at the function end
Process execution order is not specified
Active process may do immediate notification, in which case all processes
sensitive to the event becomes runnable and will be run in this evaluation
phase
Active process may operate with a channel that leads to call
request_update of the channel, results to pending update calls
Active process may do delta/delayed notification
62
Evaluation phase
SC_CTHREAD process may resume with call
wait() – for the next clock tick time
wait(int n)– for the time after n clock ticks
SC_THREAD process may resume with call
wait() and wait(int) related to events in sensitivity list
wait(sc_event) – for the delta cycle where the event is notified
wait(SC_ZERO_TIME)– for the next delta cycle
wait(sc_time)
wait(sc_time, sc_event)
63
Update phase
Update phase starts after end of evaluation phase, when no more runnable
process
Execute pending calls of update functions
For channels which request_update was called in the very last evaluation phase
Function update applies operations done with the channel
sc_signal – assign last written value to current value
sc_fifo – perform pop and push operations done and update FIFO internal state
64
Delta and timed notification phases
Delta notification phase starts after update phase
Determine runnable processes by delta notification and wait(SC_ZERO_TIME)
calls
Start evaluation and update phases
Timed notification phase starts after all delta notification phases
finished, no runnable processes
Simulation time is advanced to the moment of earlier timed notification
Determine runnable processes and start first evaluation phase
65
Initialization phase
Timed notification
phase
Delta notification
phase
Evaluation phase
Execute/Run/Notify processes
No runnable process
no yes
No runnable process yes
No runnable process
no
no
Update phase
66
SystemC data types
67
SystemC data types
All C++ core and standard library data types
uint8_t, uint16_t, uint32_t, uint64_t from stdint.h
Special data types – implemented as C++ templates
Integers with specified length
sc_int, sc_uint
sc_bigint, sc_gibuint
Fixed-point types
sc_fixed, sc_ufixed
Logic types
sc_logic –four value 1 bit logic: 0,1,X,Z
Vectors
sc_bv – bit vector
sc_lv – logic vector
68
Integer data types
Commonly used types
sc_int<int>, sc_uint<int> - limited length (1..64 bit), stored in C++ 64-bit
native type
sc_bigint<int>, sc_biguint<int> - finite length, no limitations
sc_int and sc_bigint are represented in two complement form
Take into account different ranges of signed and unsigned variables with the same
number of bits
Signed and unsigned comparison issue
Two common bugs
sc_int<3> a; sc_uint<3> b;
a = b;
for(sc_uint<3> i = 0; i < 8; ++i) {…}
69
Integer data types
sc_int, sc_uint, sc_bigint, sc_biguint methods
range(int max, int min) – get [max..min] bits of the integer, may be used in
left and right parts as well
bit(int) or operator[](int) – get the bit of integer
concat(arg0, arg1, …) or operator, - concatenation of several integers,
the result length is sum of all argument lengths
and_reduce(), or_reduce(), xor_reduce(), …
to_int, to_uint, to_long, …
May be mixed with C++ data types with unary and binary operators
Integer literal with “0x” “0o” “0b” may be used
70
Integer example
sc_uint<5> a = 0;
sc_uint<5> b = 3;
sc_uint<10> c = (a, b); // concatenation
bool d = c.bit(4);
sc_uint<1> e = !d; // implicit type conversion
sc_uint<4> f = b.range(3,e);
a.range(4,3) = (c.range(3,3), (sc_uint<3>)f);
d = a.and_reduce();
int cpp_a = 5;
b = a + cpp_a; // mix with C++ data types
d = (b == cpp_a); // signed and unsigned equality
f = cpp_a; // implicit type conversion
c = 0xA3F; // Hex literal
b = 0b011011; // Binary literal
71
Logic data type
Four value logic data type - sc_logic
Operates with sc_logic_value_t or predefined constants
Log_0=0, Log_1=1, Log_Z, Log_X
SC_LOGIC_0, SC_LOGIC_1, …
to_bool() and is_01() methods
Logic operation semantic
AND operation
OR operation
72
Vector data types
sc_bv and sc_lv represent finite length vector of bits or logic values
operator[](int)– get the specified bit
range(int max, int min) or operator()(int, int) – get [max..min] bits
and_reduce(), or_reduce(), xor_reduce(), …
lrotate(int n ), rrotate(int n) – rotate on n elements
reverse() – reverse element order
to_int, to_uint, to_long, …
is_01() – all elements are 0 or 1
length() – element number
73
Vector examples
sc_bv<4> a = 11;
sc_lv<6> b = a << 2;
b(3,2) = b(4,3);
sc_uint<4> c = a;
sc_logic e[4] = (SC_LOGIC_Z, SC_LOGIC_0, SC_LOGIC_1, SC_LOGIC_Z);
sc_lv<4> d = e;
d[3] = SC_LOGIC_X;
bool f = d.is_01();
f = (d[1] == SC_LOGIC_X);
f = (d.length() == b.length());
74
Fixed-point data types
Fixed-point types represent sequence of bits with a specified position for the
point separates integer and fractional parts
sc_fixed, sc_ufixed
sc_(u)fixed<WL, IWL, Q_MODE, O_MODE, N_BITS>
WL – word length
IWL – integer word length
Q_MODE – quantization mode
O_MODE – overflow mode
N_BITS - number of saturated bits
75
Fixed-point data types
Q_MODE – quantization mode
SC_RND, // Rounding to plus infinity
SC_RND_ZERO, // Rounding to zero
SC_RND_MIN_INF, // Rounding to minus infinity
SC_RND_INF, // Rounding to infinity
SC_RND_CONV, // Convergent rounding
SC_TRN, // Truncation
SC_TRN_ZERO // Truncation to zero
O_MODE – overflow mode
SC_SAT, // Saturation
SC_SAT_ZERO, // Saturation to zero
SC_SAT_SYM, // Symmetrical saturation
SC_WRAP, // Wrap-around
SC_WRAP_SM // Sign magnitude wrap-around
76
Fixed-point data types
Default template values
sc_(u)fixed<WL=32, IWL=32, Q_MODE=SC_TRN, O_MODE=SC_WRAP,
N_BITS=0>
sc_fixed, sc_ufixed methods
wl(), iwl(), n_bits(), o_mode(), q_mode()
overflow_flag() – last write caused overflow
quantization_flag() – last write caused quantization
77
Fixed-point examples
#include “$SC_HOME/include/sysc/datatypes/fx/sc_fixed.h”
#include “$SC_HOME/include/sysc/datatypes/fx/sc_ufixed.h”
using namespace sc_dt;
sc_fixed<5,3> a = 3.25;
sc_ufixed<4,2> b = 3.25;
bool c = (a == b);
float f = 7.45;
sc_ufixed<6,3, SC_TRN> g1 = f; // 7.375
sc_ufixed<6,3, SC_RND> g2 = f; // 7.5
sc_ufixed<4, 4, SC_TRN, SC_SAT, 4> d = 15; // d = 15
d += 1; // d = 15
78
Predefined channels and ports
79
sc_prim_channel
sc_prim_channel is the base class for all primitive channels
request_update() causes scheduler to enroll the update request of this channel
and following call of the update method at update phase, no more than one call of
the channel update method is guaranteed
virtual update() must be defined in channels, it can update only the current
object state, this method cannot get other channel states, call any
sc_prim_channel methods, does immediate notification and process control
(suspend)
wait()/next_trigger() methods are identical to sc_module onces
80
Predefined channels
Predefined channels inherited of sc_prim_channel
sc_signal<T>
sc_signal_resolved, sc_signal_rv
sc_buffer<T>
sc_clock
sc_fifo<T>
Other channels
sc_mutex
sc_semaphore
sc_event_queue
81
Data types for channels
Template parameter T of a channel must be a C++ type with predefined
assignment and equality semantic which are adequate, or type which
bool T::operator==( const T& ) adequate to provide event generation
std::ostream& operator<<( std::ostream&, const T& ) gets channel state to
print/dump functions
const T& operator=( const T& ) to assign or copy values, should correspond to
operator==
Default constructor
sc_trace() method
All SC data types comply these requirements
sc_uint, sc_int, sc_fixed, …
T cannot be sc_module, sc_signal, …, or a class contains any of them
82
sc_signal interface
template <class T, sc_writer_policy WRITER_POLICY = SC_ONE_WRITER>
class sc_signal: public sc_signal_inout_if<T>, public sc_prim_channel {...}
template <class T> class sc_signal_inout_if
: public sc_signal_in_if<T> , public sc_signal_write_if<T> {...}
template <class T>class sc_signal_write_if : virtual public sc_interface {
virtual void write( const T& ) = 0;
}
template <class T> class sc_signal_in_if : virtual public sc_interface {
virtual const T& read() const = 0;
virtual const sc_event& value_changed_event() const = 0;
virtual bool event() const = 0;
}
83
sc_signal<bool> interface
template <> class sc_signal_in_if<bool> : virtual public sc_interface {
public:
virtual const T& read() const = 0;
virtual const sc_event& value_changed_event() const = 0;
virtual const sc_event& posedge_event() const = 0;
virtual const sc_event& negedge_event() const = 0;
virtual bool event() const = 0;
virtual bool posedge() const = 0; // return true if the event occurs
virtual bool negedge() const = 0; // in the very last delta cycle
}
84
sc_signal implementation
T m_cur_val, m_new_val;
T read() { return m_cur_val;}
void write(const T& value_) {
bool value_changed = !(m_cur_val == value_);
m_new_val = value_;
if (value_changed) {
request_update();
}
}
void update() {
if (m_new_val != m_cur_val) {
m_cur_val = m_new_val;
if (m_change_event_p) m_change_event_p->notify_next_delta();
}
}
85
sc_clock
class sc_clock : public sc_signal<bool> {
sc_clock( const char* name_,
const sc_time& period_,
double duty_cycle_ = 0.5,
const sc_time& start_time_ = SC_ZERO_TIME,
bool posedge_first_ = true );
virtual const sc_event& posedge_event() const = 0;
virtual const sc_event& negedge_event() const = 0;
double duty_cycle() const;
const sc_time& start_time() const;
bool posedge_first() const;
}
86
sc_buffer
sc_buffer – primitive channel derived from sc_signal
Value-changed event is notified whenever the buffer is written, value is not
considered
87
Signals with many writers
sc_signal_resolved – primitive channel inheritor of sc_signal
Template argument T is sc_logic
If more than one process write the channel, it resolves in according with general
rules
1 and 0 -> X, 1 and X -> X, X and Z -> X, 0 and Z ->0, …
sc_signal_rv – uses sc_lv as T
88
sc_fifo interface
template <class T> class sc_fifo: public sc_fifo_in_if<T>,
public sc_fifo_out_if<T>, public sc_prim_channel {...}
template <class T> class sc_fifo_in_if :
public sc_fifo_nonblocking_in_if<T>, public sc_fifo_blocking_in_if<T> {
virtual int num_available() const = 0;
}
template <class T> class sc_fifo_nonblocking_in_if: public sc_interface {
virtual bool nb_read( T& ) = 0; // return false if FIFO is empty
virtual const sc_event& data_written_event() const = 0;
};
template <class T> class sc_fifo_blocking_in_if : public sc_interface {
virtual T read() = 0; // block until FIFO is empty
};
89
sc_fifo interface
template <class T> class sc_fifo_out_if :
public sc_fifo_nonblocking_out_if<T>, public sc_fifo_blocking_out_if<T> {
virtual int num_free() const = 0;
};
template <class T> class sc_fifo_nonblocking_out_if : public sc_interface {
virtual bool nb_write( const T& ) = 0; // return false if FIFO is full
virtual const sc_event& data_read_event() const = 0;
};
template <class T> class sc_fifo_blocking_out_if : public sc_interface {
virtual void write( const T& ) = 0; // block until FIFO is full
};
90
sc_fifo implementation
int m_size, m_free, m_ri, m_wi;
int m_num_readable;
int m_num_read, m_num_written; // in the current delta cycle
T* m_buf;
int num_available() {return (m_num_readable – m_num_read);}
void update() {
if (m_num_read > 0) m_data_read_event.notify(SC_ZERO_TIME);
if (m_num_written > 0) m_data_written_event.notify(SC_ZERO_TIME);
m_num_readable = m_size – m_free;
m_num_read = 0;
m_num_written = 0;
}
91
sc_fifo implementation
void read(T& val) {
while (num_available() == 0) wait(m_data_written_event);
m_num_read++;
buf_read(val);
request_update();
}
bool nb_read(T& val) {
if (num_available() == 0) return false;
m_num_read++;
buf_read(val);
request_update();
return true;
}
92
sc_mutex
sc_mutex – predefined channel to model SW style mutex
Not inheritor of sc_prim_channel
class sc_mutex : public sc_mutex_if, public sc_object
{
virtual int lock();
virtual int trylock();
virtual int unlock();
};
93
User defined channel
It is possible to implement own channels as C++ class
Must derived from one of primitive channels or directly from sc_prim_channel
User channel may be also implemented using one of primitive channel
Lets go to http://collabedit.com/g95cv
94
Process interaction
95
Process interaction classification
Synchronization primitive
sc_signal, sc_fifo, …
sc_event
Shared variable
C++ or external synchronization object
C++ 11 mutex, conditional variable, …
Pthread mutex, semaphore, …
Process number
1-1
1-to-N
Process types
Will discuss SC_CTHREAD and SC_METHOD
Synthesizability
96
sc_signal for SC_CTHREADs
sc_signal<bool> req;
void consumer(){
wait();
while (1) {
while (!req) wait();
...
}}
void produces(){
req = 0; wait();
while (1) {
...
req = 1;
wait();
req = 0;
}}
clk
req
1 2 3
DC: 1 2 ... DC: 1 2 ...
Producer Consumerreq
ack
97
// Request-Acknowledge synchronization
sc_signal<bool> req, ack; sc_signal<T> val;
void consumer(){
ack = 0; wait();
while (1) {
while (!req) wait();
consumeValue(val); // takes some time Tcons
ack = 1;
wait();
ack = 0;
}}
void producer(){
req = 0; wait();
while (1) {
val = produceValue(); // takes some time Tprod
req = 1;
wait();
req = 0;
while (!ack) wait();
}}
98
Tcons = 0, Tprod = 0
Tcons = 1, Tprod = 0
99
// Ready-Request synchronization
sc_signal<bool> req, rdy; sc_signal<T> val;
void consumer(){
rdy = 1; wait();
while (1) {
while (!req) wait();
rdy = 0;
val_local = val.read();
consumeValue(val_local); // takes some time Tcons, copy val if Tcons
> 1
rdy = 1; // May be asserted inside of consumeValue 1 clock before
wait(); // My be removed if Tcons > 0
}}
void producer(){
req = 0; wait();
while (1) {
while (!rdy) wait();
val = produceValue(); // takes some time Tprod
req = 1;
wait();
req = 0;
100
Tcons = 0, Tprod = 0
Tcons = 1 (wait() is removed from consumer), Tprod = 0
101
// Always ready to get request
sc_signal<bool> req; sc_signal<T> val;
void consumer(){
wait();
while (1) {
while (!req) wait();
consumeValue(val); // takes no time, i.e. Tcons = 0
wait();
}}
void producer(){
req = 0; wait();
while (1) {
val = produceValue(); // takes some time Tprod, must copy val!!!
req = 1;
wait();
req = 0;
}}
102
sc_signal for SC_METHODs
sc_signal<bool> req, ack, cond;
sc_signal<T> val, res;
// Must be Tcons = 0, Tprod = 0!!!
void consumer(){
ack = 0; res = 0;
if (req && cond) {
res = consumeValue(val);
ack = 1;
}
}}
// External signal @do_req
void producer() {
if (!rst && do_req) { // rst is not required
req = produceValue(val);
}}
103
sc_signal for SC_METHODs
SC_METHODs triggered by events notified in SC_CTHREAD
Signal modified in SC_METHOD may result
in other SC_METHOD activated
using by SC_CTHREAD at the next posedge/negedge
No backward relations of SC_METHODs are possible
Backward relation leads to combinational loop
SC_CTHREAD SC_METHOD SC_METHOD ...
104
Combinational loop
sc_signal<bool> req, rdy;
sc_signal<T> val, res;
void consumer(){
rdy = 0; res_val = 0;
if (req) {
res = consumeValue(val);
rdy = 1;
}
}}
void producer(){
req = 0;
if (do_req || rdy) {
req = produceValue(val);
}
}
105
Async/sync process usage
Complex design usually contains SC_METHODs as well as SC_CTHREADs
SC_CTHREADs are used to
Implement state control in FSM
Perform synchronous bus transactions
Store value in FF
Control operations with memory
SC_METHODs are used to
Reduce latency in process interaction, do something in the current clock tick
Provide immediate acknowledge
Provide speculative processing of bus transaction before it happens
Prepare address and data to memory request
106
// Request-Acknowledge synchronization with extra latency
sc_signal<bool> req, ack; sc_signal<T> val;
void consumer(){
ack = 0; wait();
while (1) {
while (!req) wait();
consumeValue(val); // takes some time Tcons
ack = 1;
wait();
ack = 0;
}}
void producer(){
req = 0; wait();
while (1) {
val = produceValue(); // takes some time Tprod
req = 1;
while (!req || !ack) wait(); // Why checking req here?
req = 0;
}}
107
// Request-Acknowledge synchronization without extra latency
void consumer_async(){
ack = req && reg_free;
}
void consumer_sync(){
reg_free = 1;
wait();
while (1) {
while (!req || !ack) wait();
reg_free = 0;
consumeValue(val); // Tcons > 0
reg_free = 1;
}}
108
Synchronization. Keep It Simple.
In practice, almost all time both side are ready to interact
The most difficult is provide correct pause and resume interaction
Use minimal synchronization which provides correct process interaction
Remove rdy/ack/req is the process can always produce or consume
Do not use async processes if extra latency is not a problem
Less logic generated, less chance to make mistake
90% of mistakes are done in synchronization!!!
109
Shared variable synchronization
Shared variable are synthesizable, represented with a register or memory
Normal FF is 1RW, so it accepts simultaneous access from one process only
in the delta cycle
In different delta cycle register may be accessed by various processes,
no concurrency allowed
Looks like sc_signal based synchronization
Memory (register file, SRAM block, …) may have arbitrary number of ports:
1RW, 1R1W, 2RW, …
Different cells may be accessed at the same time by several processes
Simultaneous access to one cell usually leads to undefined behavior
Shared variable does not give new possibilities compared with sc_signal
This synchronization is implicit
We have to provide no simultaneous access manually
110
sc_event synchronization
sc_event allows to synchronize all process types
In SC_CTHREADs sc_event is the macro second parameter, may do event
notification
In SC_METHODs event notification may be done, instead wait methods
next_trigger may be used
In SC_THREADs all notify and wait call types may be used
sc_event is not synthesizable
May be used for testbenches, specially if it needs represent time events not aligned
with clock
111
// Synchronization with sc_event in two SC_THREADs
sc_event e1, e2;
void consumer(){
while (1) {
wait(e1);
consumeValue(val);
e2.notify(SC_ZERO_TIME);
}}
void producer(){
while (1) {
val = produceValue(); // No time information
e1.notify(sc_time(5, SC_NS)); // Time information
wait(e2);
}}
112
// Synchronization with sc_event in SC_CTHREAD and SC_METHOD
sc_event e1, e2, e3;
{...
SC_CTHREAD(consumer, e2);
SC_METHOD(consume);
sensitive << e1;
...}
void consumer(){
while (1) {
wait(); // waiting for e2
e1.notify(SC_ZERO_TIME); // consume value in method
}}
void consume(){
consumeValue(val);
if (val > N) next_trigger(e3) else next_trigger();
}}
113
sc_fifo synchronization
sc_fifo based synchronization is very similar with sc_signal one
Lets go to
Asynchronous FIFO http://collabedit.com/s7cum
114
Spawned processes
Process types
Static process – created at elaboration phase
Dynamic process – created in end_of_elaboration callback or in simulation
Processes created with SC_METHOD, SC_THREAD, SC_CTHREAD macros
called unspawned processes, typically static processes
Processes created with sc_spawn function called spawned processes
Typically create dynamic processes
sc_process_handle template <class T> sc_spawn (T fobject,
chqr* pname = 0, sc_spawn_options* opt = 0)
sc_process_handle template <class T> sc_spawn (
typename T::result_type* res, T fobject,
chqr* pname = 0, sc_spawn_options* opt = 0)
sc_spawned_options – contains process options including
spawn_method(), set_sensitivity(), reset_signal_is(),
async_reset_signal_is(), dont_initialize()
115
...
SC_CTHREAD(proc);
...
int ret;
int f(){...};
void proc() {
sc_spawn_options opt;
opt.spawn_method();
opt.set_sensitivity(&sig);
// create process with options
sc_spawn(f, "f1", &opt);
// create process with result value
sc_spawn(&ret, fr, "f2", &opt);
// create several processes with waiting for termination
SC_FORK
sc_spawn(f,…), sc_spawn(g,…)
SC_JOIN
}
116
Process control
sc_process_handle – process descriptor that allows to control process
execution
class sc_process_handle {
...
bool dynamic();
bool terminated();
void suspend(…);
void resume (…);
void disable (…);
void enable (…);
void kill (…);
void reset (…);
sc_event& reset_event();
}
117
sc_process_handle a, b; // Two empty handles
SC_CTHREAD(g, clk.pos());
SC_CTHREAD(f, clk.pos());
a = sc_get_current_process_handle();
...
b = sc_spawn(...);
a.suspend();
wait(10, SC_NS);
a.resume();
b.reset();
wait(10, SC_NS);
b.kill();
118
Design architecture
119
Design architecture
Complex design contains tens to hundreds of modules which should be
organized in modules hierarchy
Module class (inheritor of sc_module) consists
Module
ProcessNProcess1
Logic implementationData fields
Child modules
Por
ts/e
xpo
rts
120
sc_port sc_port<T> – port belongs to the module, provides methods to call from the
module processes
sc_in, sc_out, sc_inout – are particular cases of sc_port template
Port may be bound to
Channel with the same value type T (sc_signal<T>)
Another port in the parent/child module
Another export in a child module
sc_in
sc_outChild
module
Parent module
sc_in
sc_out
sc_in
sc_out
sc_in sc_out
sc_signalChild module A
Child module B
121
Port bound to a channel translates method calls to the channel instance
class A : public sc_module {
sc_in<T> port_in;
void proc() {
T var = port_in.read(); // Call sig.read()
}}
class B : public sc_module {
sc_out<T> port_out;
void proc() {
port_out.write(val); // Call sig.write()
}}
class parent : public sc_module {
A a; B b;
sc_signal<T> sig;
parent() : a(“a”), b(“b”) {
a.port_in(sig);
b.port_out(sig);
}}
122
Port bound to a port of parent module instance in parent constructor
class A : public sc_module {
sc_in<T> port_in;
void proc() {
T var = port_in.read(); // Call parent port_in.read()
}}
class parent : public sc_module {
A a;
sc_in<T> port_in;
parent() : a(“a”) {
a.port_in(port_in);
}}
What method is called from parent port_in.read()?
Is it possible to bind sc_in to sc_out directly, without channel?
123
sc_port with user defied interface sc_port is template class with interface parameter
template <class IF> class sc_port_b : public sc_port_base {
IF* operator->();
sc_interface* get_interface();
...}
class myIF : public sc_interface {int func(int);} is user defined and may
contain arbitrary methods
sc_port<myIF> myPort;
myPort->func(val);
sc_in, sc_out, sc_inout have predefined interface
template <class T> class sc_in : public sc_port<sc_signal_in_if<T>,1>
{...}
Module with sc_port or its inheritors may calls methods of the interface from
the processes
124
sc_export sc_export – export belongs to a module provides methods to call from
outside of this module
template<class IF> sc_export {...}
Providing interface via export is an alternative to implements interface
by the module
class myModule : public sc_module, public myIF {...}
class myModule : public sc_module {
sc_export<myIF> export; ...}
125
class myIF : public sc_interface{
int func(int val);
}
class A : public sc_module, private myIF {
sc_export<myIF> exp1;
A(const sc_module_name& name) : sc_module(name), exp1("exp1") {
exp1(*this);
}
int func(int val) {...}
}
class B : public sc_module {
sc_port<myIF> port1;
void proc () {
val = port1->func(1);
}}
A a(“a”); B b(“b”);
b.port1(a.exp1);
126
Hierarchical connections
Port of child modules connected to ports of parents
Must be the same direction
Process inside of most internal module provides sc_out` value
sc_in
sc_out Module
sc_in
sc_out
sc_in
sc_out
Module
Module
127
Internal connections
Processes connected with channels (sc_signals), no port/export required
Child modules connected with channels (sc_signals)
sc_in sc_out
sc_signalChild module A Child module B
sc_in
sc_outsc_signal
sc_signal
128
Bad connection examples
C++ allows to create some tricky things which are correct but hard to
understand and may lead to synthesis issues
Child module A Child module B
sc_signalprocess process
sc_signal
129
Interface method call
Call a method from a process of another module
Usually called method is declared in module or export interface, that looses
modules implementation
Allow accessing the module fields, including signals
In synthesis such modules are joined together
Child module A Child module B
process
sc_signal
process
IF
Call of IF method
130
Connection delay
Channel (sc_signal) update time is one delta cycle
Chain of channels connected with SC_METHOD logic may have arbitrary delay in
delta cycles
How this delay should be considered? Is different delay on signals correct?
A
sc_signal
Process 1
Method process
sc_signal
Process 2
sc_signalB
A+B
131
Design partitioning
Module may contain
processes and child modules
child modules only
Balanced module size
Linked logic placed in one module or in several modules instantiated in
common top module
Module reuse and design easy reconfiguration strategies
Large module may be connected with an intra-chip buses
Taking into account synthesis tool recommendations
132
SystemC under hood
133
sc_object
sc_object – base class for most SC classes including sc_module,
sc_process, sc_port, sc_prim_channel
sc_event, sc_processs_handle, sc_time are not child of sc_object
class sc_object {
sc_object();
sc_object(const char*);
const char* kind(); // Get object type string, for example “sc_module”
void print( std::ostream& = std::cout ); // Print name
void dump( std::ostream& = std::cout ); // Print name and kind
const char* name(); // dot-separated hierarchical name
const char* basename(); // object name
std::vector<sc_object*>& get_child_objects();
std::vector<sc_event*>& get_child_events();
sc_object* get_parent_object();
trace(sc_trace_file*) // Puts object values into VCD-file
}
134
name and basename
SC_MODULE(Module) {
sc_signal s;
Module() : s(“signal”)
}
SC_MODULE(TopModule) {
Module m;
sc_in_clk clk;
TopModule() : m(“mod”), clk(“clock”)
}
TopModule t(“top”);
...
cout << s.name() << s.basename(); // Puts “top.mod.signal” and “signal”
135
Trace functions
Value change dump (VCD) file, contains time-ordered sequence of values
class sc_trace_file {
void set_time_unit( double , sc_time_unit );
}
sc_trace_file* sc_create_vcd_trace_file( const char* name );
void sc_close_vcd_trace_file( sc_trace_file* tf );
void sc_trace(...); // Trace value passed in second argument to file passed
// in first argument with identifier in third argument
// There are several overloaded version for various parameter types
void sc_trace( sc_trace_file* , sc_dt::sc_uint_base& , const std::string& );
void sc_trace( sc_trace_file* , const bool& , const std::string& );
...
sc_object method trace() uses sc_trace to output values
136
Trace functions
Trace method is implemented for channels (sc_signal/sc_fifo)
Implemented in update() method
Is deprecated
Need to define DEBUG_SYSTEMC before #include “systemc.h”
VCD signal diagram viewers
GTKWave (GNU license)
Commercial tools like VCS, Insicive, …
137
Trace example
SC_MODULE(Module) {
sc_signal<bool> B;
sc_signal<sc_uint<4> > C;
sc_trace_file* tf;
SC_CTOR(Module) : clk("clk"), B("B"), C("C")
{
tf = sc_create_vcd_trace_file("trace1");
B.trace(tf);
C.trace(tf);
}
~ModA() {sc_close_vcd_trace_file(tf);};
Makefile:
$(CXX) ... -DDEBUG_SYSTEMC ...
138
Object hierarchy exploration
SC_MODULE(Module) {
sc_event e;
Module() : e(“event”);
...
events = get_child_events(); // e
parent = get_parent_object(); // t
}
SC_MODULE(TopModule) {
Module m;
sc_in_clk clk;
TopModule() : m(“mod”), clk(“clock”)
...
objs = get_child_objects(); // m, clk
}
TopModule t(“top”);
139
System functions
Simulation control functions
sc_start(), sc_start(int)
sc_stop()
sc_pause()
sc_time_stamp() – simulation time
sc_delta_count() – delta cycle in current simulation time
sc_get_status () – returns current simulation phase and simulatior state
(SC_ELABORATION, SC_RUNNING, SC_PAUSED, SC_STOPPED, …)
void set_stack_size( size_t ) – sets the stack size of an unspawned
process instance during initialization
SC_DEFAULT_STACK_SIZE = 0x20000 (0x50000)
140
Assertions
C++ assert (assert.h)
sc_report and sc_report_handler allow flexibility reporting an issue and
processing report with predefined actions
SC_REPORT_INFO( msg_type , msg )
Info: msg_type : msg
SC_REPORT_WARNING( msg_type , msg )
Warning: msg_type : msg
In file: main.cpp:23
In process: mod_a:proc @ 40ns
SC_REPORT_ERROR( msg_type , msg )
Like warning but stops simulation
SC_REPORT_FATAL( msg_type , msg )
Like warning but terminates application
sc_assert, sc_interrupt_here, sc_stop_here
141
Design verification
142
Verification techniques
Dynamic methods – simulation design with testbench and control results in
some design points
Static methods – design code analysis to detect design structure defects,
verify constraints and assertions, find out synchronization errors
143
Static verification methods
Basic approaches
Static analysis
Model checking
Theorem proving
Existing static checkers
Lint – a tool does code analysis to check templates
Synthesis tool built-in checkers
External analysis tools
144
Dynamic verification methods
All dynamic methods requires design simulation, differences are in
Input signal setup
Control points used
Matching simulation result with reference
SystemC simulators
OSCI distribution
Cadence Incisive (ncsim)
Synopsys VCS
ModelSim
…
145
Dynamic verification
Input signal setup
Reference model
Input vectors
Verification IPs
Testbench that models the environment
Control points used
Design interface
Internal points, may be specified with debug outputs or assertions
Matching simulation result with reference
Reference model output matching, provided by some simulators
Output vectors
Verification IPs
Testbench control results
146
Testbench development
Testbench imitates input signals of the design-under-test (DUT) and match the
results with control values
sc_in
sc_out DUT
Testbench
Processes
sc_in
sc_out DUTTestbench
147
Debug tools and methodology
SystemC design usually developed in a C++ IDE, so using C++ debugger is
one of the common ways
Debug with print and debugger features
Challenge is high parallel design architecture
Efficient to debug an issue localized in processes and long in time
Coarse grained bug localization
Algorithmic issue debug
Signal diagram viewer to debug
Normal way for hardware designs
Required to give object name in constructor, normally the same as a variable name
Efficient to debug an issue localized in time, with a number of parallel processes
Final bug localization
The analyzed diagrams fit one or several screens
Interface issue debug, synchronization issue debug
148
Assertion based verification
Any real design should have some assertion
Assertion used for
Testing internal logic of the design, improve observability
Bug detection with constraining the occurrence of a bug
Assertions of module interface prevent incorrect using the module
Assertion used in simulation and formal verification
There is no SVA analog in SystemC yet
149
Mixed language simulation
Most simulators support Verilog, VHDL, SystemC in mixed design/testbench
simulation
Legacy code and standard components may be developed in some HDL
No difference in simulator reports/diagrams
Typically SystemC design has SystemC testbench, which may be reused for
generated RTL verification
Generated RTL need to be verified as SystemC simulation differ from RTL
Using binary logic instead of 4-value logic, ‘0’ in SystemC, ‘X’ in RTL for non-reset
variables
No UPF support and no power features verification in SystemC
150
SystemC UVM
Universal Verification Methodology (UVM) 1.2, 2014
Analog of Verilog UVM in SystemC
UVM provides library and a methodology for testbench architecture and
implementation
151
High level synthesis
152
High level synthesis
HLS is a procedure of translation SystemC program to another language
Verilog
VHDL
Program transformation is done by most compilers
GCC has several SSA-based representations
LLVM frontends translates input language to IR
HLS specific is the target design in Verilog/VHDL should be
synthesizable into hardware =>
All design elements must be statically determined
153
Statically determined elements
Design elements
Structural elements: modules, ports, channels, fields and variables
Process functions
Structural elements are created at elaboration phase
Element instances and their properties must be statically determined, i.e. be
specified in source code in a form which the HLS tool supported
Process functions
Spawned processes cannot be statically determined in general case, so only
unspawned processes are synthesizable
Function behavior must be statically determined to HLS tool is able to create
equivalent combinational logic and FSM
154
Synthesizable subset standard
SystemC like Verilog/System Verilog includes language statements not
intended for synthesis
HLS tools support different subsets of SystemC
SystemC synthesizable standard intended to hardware designer and HLS tool
developers
Portability SC designs between HLS tools
Define minimal subset, which may be extended HLS tool vendors
SystemC synthesizable standard 1.4.7 is a part of IEEE1666, provided by
Accellera, March 2016
155
Design structure
Multiple translation units supported
Libraries are not supported
Pre-processing directive supported, usage specified by HLS tool
Top level module is not specified, should be specified by HLS tool parameter/script
156
Synthesizable modules Module type should be declared with
SC_MODULE macro
Class or template class inherited from sc_module
Explicit and implicit template instantiation is supported
Full and partial template specialization is supported
Class member fields are supported
Access to non-const variable allowed from one process only
Inter-process communication should use sc_signal`s
Class constructors are supported
Pointer initialization, object allocation with new, port/signal creation and binding
Constant integer field initialization, initialization of other data members is prohibited
Processes creation and sensitivity specification
Copy constructors and assignment are disabled, destructors are ignored
157
Module parametrization
All structural elements including modules are created at elaboration phase
Module parametrization
Field and function types may be specified via template parameters
Array length may be specified as literal, static constant or template parameter
Other module properties may be specified via template parameters and via
constructor
158
Module parametrization exampletemplate <class element_type>
void func(element_type& elem) {
elem.clk(clk); // Bind port to signal
enable(elem.enable); // Bind port to signal
elem.bind_to(this); // Call method
}
template <class elem_type, unsigned N>
class module : public sc_module {
static const unsigned M = N >> 2;
elem_type A[5];
elem_type B[N];
elem_type C[M];
elemt_type* D;
explicit module(sc_module_name& name,
const unsigned K) : sc_module(name) {
D = new D(“D”);
}}
159
Synthesizable processes
Process creation with SC_CTHREAD, SC_METHOD and SC_THREAD
Dynamically created processes (sc_spawn) are not supported
Process control via sc_process_handle is not supported
Process creation may be parametrized with template and constructor
parameters
160
Synthesizable method process
SC_METHOD allowed to describe combinational logic without latches
Sensitive to all signal must be set
All outputs must be signal or ports
Each output must be a function of inputs and nothing more
Each output must be assigned on every execution path
No synchronization like wait/notify
161
Synthesizable thread process
SC_CTHREAD and SC_THREAD are synonyms
Must be sensitive to exactly one clock edge and have at least one reset (at most
one synchronous and at most one asynchronous reset)
Processes in module may have different clocks/resets
Thread body contains reset section and one infinite main loop
Optional reset section finished with wait()
Multiple wait() are possible between reset section and main loop
Main loop may contain wait() and wait(int) only
No event synchronization
Thread process translated to combinational logic and FSM
Each wait() statement add new state in FSM
No execution paths without wait(), normally each main loop iteration should have
one or more wait()
162
Synthesizable processes example
void methodFunc(){
// Some logic without any wait statements
}
void procFunc(){
// Optional reset section, initialization of signals and variables
wait();
// Optional operational behavior
wait();
wait();
// Main loop
while (true) {
// Some logic with wait() and wait(int) calls
wait();
}
}
163
Loop transformation
All loops except thread process main loop must
Be unrolled if loop body has no wait() statement
Have one or more wait() statements
Number of loop iteration must be statically determined
Loop unrolling – create a number of copies of loop body equal with loop
iteration number
Number of logic increased
Partial loop unrolling
Loop breaking – add one or more wait statement into loop body
Loop with wait statements may be pipelined
Loop pipelining – for loop with several states, which separates processing stages,
provide parallel execution of processing stages
164
Loop pipelining example
sc_in<T> a, b;
T c, d;
for (int i = 0; i < N; i++) {
c = a + b;
wait();
d = c << M;
wait();
}
sc_in<T> a, b;
T c, d;
c = a + b;
wait();
for (int i = 0; i < N; i++) {
d = c << M;
c = a + b;
wait();
}
• Manual loop pipelining
• HLS tools usually support loop pipelining without code modification
• Initiation interval and latency interval
165
Loop pipelining example
c = a + b d = c << M
c = a + b d = c << M
Clock ticks / Loop iteration
0
10 2 3
1
c = a + b d = c << M
c = a + b d = c << M
Clock ticks / Loop iteration
0
10 2 3
1
c = a + b d = c << M2
166
Synthesizable channels and ports
sc_signal<T> with one writer (SC_ONE_WRITER) is only supported
Other channels and sc_event are not supported
sc_in<T>, sc_out<T> and sc_inout<T> ports are supported
T should not be sc_logic
167
Synthesizable data types
Native C++ and SystemC types
Fundamental C++ types
Integer types and literals supported
Floating point literals supported only
Compound types have restricted support
Statically determined pointers are supported
References are supported
SystemC types
sc_int, sc_uint, sc_bigint, sc_biguint, sc_fixed, sc_ufixed, sc_bv
sc_logic and sc_lv are supported without X and with Z restricted support
bit() and range() operators are supporteds
concat() and reduction are supported
168
Synthesizable expressions and others
Function calls are supported except recursive functions
Type conversion is mostly supported
sizeof() is not supported
new and delete
new is supported for all C++ types, SC data types and sc_module/
sc_port/sc_signal
delete is not supported
Most functions of standard C/C++ library are not supported
printf, fprintf , cout << are ignored
169
HLS tool demo
mult_0
mult_1
mult_4
dispatchProc
respProc
readyProc
...
soc
170
Transaction Level Modelling
171
TLM Intro
Transaction level modelling provides higher abstraction level, above SystemC
Less code, more functionality
Reuse components for common operations
Speed up simulation process
The most low level details are required for process interaction description
TLM suggests to describe process interaction with interface function calls
172
TLM Intro
TLM = Methodology + coding guidelines + library classes
Transaction level modelling (TLM)
Is a part of SystemC standard
TLM 1.0
TLM 2.0
TLM is synthesizable with some limitation
173
TLM function call
sc_port<IF> port;
void process1() {
...
// Bad code style
module2.send_data(...);
// TLM style
this->port.send_data(...);
}
sc_export<IF> port;
sc_signal<int> data;
void send_data(int val) {
data.write(val);
}
void process2() {
...
int a = data.read();
}
Process 1
FunctionCall
Process 2
FunctionCall
Module1 Module2
exp
ort
por
t
174
TLM interface
class IF = public virtual sc_interface {
void func1(int);
int func2(bool, unsigned);
}
class module2 : public sc_module, public IF {
// There must be implementation of func1() and func2()
}
class module2 : public sc_module {
sc_export<IF> export;
module() {
// There must be binding export to an object implemented
// func1() and func2()
export.bind(...); // to this module or to child module
}
}
IF is a parent of sc_interface - contains function declarations
175
Reset in TLM
class IF = public virtual sc_interface {
void reset();
...
}
class module2 : public sc_module, protected IF {
sc_export<IF> export;
sc_signal<int> data;
module() {
export.bind(this);
}
void reset() {
data = 0;
}
}
To initialize variables and signals by reset special function should be used
Several reset function
176
TLM function call
sc_export<IF_A> export;
sc_port<IF_B> port;
// IF_A function in channel
void send_data(int val){
data.write(val);
};
// Process in channel
void channel_process() {
B_send_data(val);
}
Process 1
FunctionCall
Process 2
FunctionCallChannel
Module1 Module2
exp
ort
por
t
exp
ort
por
t
IF_A IF_B
177
TLM channel
class IF_A = public virtual sc_interface {
void send_data(int);
}
class IF_B = public virtual sc_interface {
void send_data(int);
}
// sc_channel is typedef of sc_module
class channel : public sc_channel, public IF, protected IF_A {
sc_export<IF_A> export;
sc_port<IF_B> port;
channel() {
export.bind(*this);
SC_METHOD(channel_proc); sensitive << data;
}
// IF_A function in channel
void send_data(int val){data.write(val);}
// Process in channel
void channel_proc(){
port.send_data(data);
}}
178
TLM 1.0
TLM 1.0 provides several common way interfaces
Blocking – operation is blocked until required data/slot is ready
Non-blocking – operation return false if required data/slot is not ready
TLM 1.0 operates with
sc_interface
sc_port, sc_export
sc_channel
179
TLM 1.0 blocking interfaces
template < typename T > class tlm_blocking_put_if: public virtual sc_interface {
virtual void put( const T &t ) = 0;
}
template < typename T > class tlm_blocking_get_if: public virtual sc_interface {
virtual T get( tlm_tag<T> *t = 0 ) = 0;
virtual void get( T &t ) { t = get(); }
};
template < typename T > class tlm_blocking_peek_if: public virtual sc_interface {
virtual T peek( tlm_tag<T> *t = 0 ) const = 0;
virtual void peek( T &t ) const { t = peek(); }
}
180
TLM 1.0 non-blocking interfaces
template < typename T > class tlm_nonblocking_put_if: public virtual sc_interface {
virtual bool nb_put( const T &t ) = 0;
virtual bool nb_can_put( tlm_tag<T> *t = 0 ) const = 0;
virtual const sc_core::sc_event &ok_to_put( tlm_tag<T> *t = 0 ) const = 0;
};
template < typename T > class tlm_nonblocking_get_if: public virtual sc_interface {
virtual bool nb_get( T &t ) = 0;
vitual bool nb_can_get( tlm_tag<T> *t = 0 ) const = 0;
virtual const sc_core::sc_event &ok_to_get( tlm_tag<T> *t = 0 ) const = 0;
};
template < typename T > class tlm_nonblocking_peek_if: public virtual sc_interface{
virtual bool nb_peek( T &t ) const = 0;
virtual bool nb_can_peek( tlm_tag<T> *t = 0 ) const = 0;
virtual const sc_core::sc_event &ok_to_peek( tlm_tag<T> *t = 0 ) const = 0;
};
181
TLM 1.0 synthesis TLM 1.0 is synthesizable with the following restrictions
A channel must derive from class sc_module (sc_channel)
Synthesis does not support asynchronous communication; therefore, methods that
return sc_event references are not supported.
Custom ports (classes derived from sc_port/sc_export) are not supported
Number of processes access to sc_port/sc_export is restricted by one at delta
cycle
182
TLM 2.0 Transport interfaces
TLM 2.0 includes TLM 1.0
http://www.cl.cam.ac.uk/research/srg/han/ACS-
P35/documents/TLM_2_0_presentation.pdf
183
SystemC vs SystemVerilog
SystemC and System Verilog synthesizable subsets are discussed
SystemC advantages
Power of C++
Reference algorithms usually are in C/C++
Libraries may be used for TB
More flexibility in design configuration and component reuse
Higher level of abstraction, that leads to more compact and clear* code
SystemC most important features
Multiple wait processes
Module inheritance
Template class
Template function – passing arbitrary object as function argument
Class as template parameter – parametrizing with module type
Functor as template parameter – parametrizing with functionality
…
184
Resume
SystemC and HLS is quite new and promising methodology, which evolution
is slow down because of conservative hardware designer community
SystemC has lots of lacks
No explicit memory description
No power specification support
No synthesizable subset commonly adopted standard
…
Anyway we need more efficient HDL than there are, SystemC is one
of such attempts