Download as pdf or txt
Download as pdf or txt
You are on page 1of 41

Modern SystemC

Dr David Long
Doulos

© Accellera Systems Initiative 1


Motivation

• Much of today's SystemC is based on the C++ versions of 1998 or 2003

• C++ changed considerably starting in 2011

• Newer versions of the SystemC standard itself, including the recent CCI
standard now require C++11 as a minimum

• Older clunky looking SystemC code can now be written more intuitively
and with less effort

• All major compilers now support at least C++14

© Accellera Systems Initiative 2


Inspired by "Modern C++"
• C++11 and C++14 features create
a new C++ language

• Requires a new approach

• SystemC is just a C++ library with


a set of additional rules!

© Accellera Systems Initiative 3


Main idea

• SystemC code can break free from C++98/C++03 limitations.

• "Modern SystemC" has advantages:

– Syntax more is intuitive and fun.

– Can be written more quickly.

– Syntax provides mechanisms safety.

– Provides performance improvements.

• SystemC can be more productive with C++11/14/17

© Accellera Systems Initiative 4


What’s all the fuss about?
Simplicity, Safety & Performance
• Less is more
– Less typing – easier to read, less to mistype, simplicity

• New syntax to specify and measure intent

– Compiler catches more bugs, earlier

• Mechanisms to avoid unnecessary operations

– Faster by reduction of construction/destruction

– Basic containers avoid overhead

© Accellera Systems Initiative 5


How has C++ changed? The not so short list…

• User-defined literals (e.g. 1sec, 2kg, 8mm, 1.3v, 0.2uA)

• Compile-time functions and checks for better safety

• Uniform initialization syntax, constructor delegation

• Automatic type inference, Ranged-for loops and lambdas

• Real null pointer, scoped enumerations,

• Virtual inheritance rules check signatures

• Explicit deletion and default implementation

• Containers designed for speed


© Accellera Systems Initiative 6
C++ Standard Library Additions

• std::array • std::random
• std::chrono • std::ratio
• std::condition_variable • std::recursive
• std::forward_list • std::regex
• std::functional • std::thread
• std::future • std::tuple
• std::initializer_list • std::unordered_map
• std::memory • std::unordered_set
• std::mutex
© Accellera Systems Initiative 7
Awkward C++03 issues

• Initializing module parameters • Unnatural typedef

• Awkward nested templates • Lousy randomization

• Vector setup • Awkward arrays

• Messy iterators • Detecting event triggers

• Unnatural literals • Ugly declarations

• Spawn requires method

© Accellera Systems Initiative 8


Safety issues in C++03

• Unintended conversions • Multiple constructors

• Mismatched signature overrides • Inherited constructors

• Disallow further overrides • Static tests

• Unnatural deletion of defaults – • Weakly scoped enumerations


delete • Awkward null pointer
• Explicit defaults – default

© Accellera Systems Initiative 9


SystemC Refresher - Modules
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;
}
};

© Accellera Systems Initiative 10


SystemC Refresher - Hierarchy
SC_MODULE(Datapath)
{
...
Module instance = data member
Register rega, regb;
sc_signal<int> aa, bb;

SC_CTOR(Datapath) Initializer list


: rega("rega"), regb("regb"), aa("aa")
{
rega.clk.bind(clk);
rega.d.bind(a);
rega.q.bind(aa);

regb.clk.bind(clk);
regb.d.bind(b);
regb.q.bind(bb);

... String names: module mandatory, signal optional


}
};

© Accellera Systems Initiative 11


Nested Template Parameters

C++03 Modern C++


• Template parameter list requires • Space before closing bracket is
space before closing bracket when optional
parameter is class template type

sc_signal<sc_int<32> > sc_signal<sc_int<32>> out;


out;

© Accellera Systems Initiative 12


Uniform Initialization
• Braces can be used to initialize a variable or object

int a[3] = {1,2,3}; //C


int i{10}; //same result as int I = 10;

my_class* cptr = new {a,b,c}; //a,b,c constructor args

my_class cobj{a,b,c}; //a,b,c constructor args

• Catches accidental narrowing of types

float a,b,c;
int i{a+b+c}; //Error! Inits int with float expression

© Accellera Systems Initiative 13


Initializing module parameters

C++03 Modern C++


• Constructor initializer list feels • Uniform initialization more intuitive
awkward

SC_MODULE(Top_module) { SC_MODULE(Top_module) {
Cpu cpu; Cpu cpu {"cpu", 1.4e9 };
Bus bus; Bus bus {"bus", "c.map" };
Mem ram; Mem ram {"ram", RW, 1e6 };
Top_module( sc_module_name nm ) : Top_module( sc_module_name nm ){}
cpu("cpu",1.4e9), };
bus("bus","c.map"),
ram("ram",RW,1e6) {}
}; • Old syntax still necessary for dynamic
initialization

© Accellera Systems Initiative 14


Initializing STL Containers

C++03 Modern C++


• Separate assignment required for each • Uniform initialization possible (similar
element to C arrays)

std::map<string,Addr_t> std::map<string,Addr_t>
start; start = {
start["rom"] = 0; {"rom",0},
start["ram"] = 0x1000; {"ram",0x1000}
};

© Accellera Systems Initiative 15


Range-based for
• Gets loop index from expression

for (const int& i : v) // access by const reference


std::cout << i << ' ';

for (auto i : v) // type of i is int


std::cout << i << ' ';

© Accellera Systems Initiative 16


Accessing STL Container Elements

C++03 Modern C++


• Declaration and use of iterators is • Range-based for loops can use
messy! reference instead

std::vector<int> vect(10); std::vector<int> vect(10);


int i = 0; int i = 0;
for (std::vector<int>::iterator for (auto &elt:vect) {
it=vect.begin(); elt = i++; //sets values 0..9
it!=vect.end(); };
++it) {
*it = i++; //sets values 0..9
};

© Accellera Systems Initiative 17


User-defined Literals
• Enables declaration of literal values with units

long double operator "" _w(long double);


long double iw = 10.2_w; // iw = 10.2L

• Can also declare conversion functions


long double operator"" _deg ( long double deg ) {
return deg * 3.14159265358979323846264L / 180;
}

• std::chrono library defines literals and functions for time

© Accellera Systems Initiative 18


Representing Time

C++03 Modern C++


• Time represented by sc_time objects • User-defined time literals (in header
with ugly enum constructor argument file) make the notation more natural

sc_time prev, diff; inline sc_time operator "" _ms


... (unsigned long long val) {
prev = sc_time_stamp(); return
wait(some_event); sc_time(double(val),SC_MS);
diff = prev – sc_time_stamp(); }
...
if( diff > sc_time(100,SC_MS) { if( diff > 100_ms) {
... ...

© Accellera Systems Initiative 19


Lambda Expressions
• Create function objects

auto fn = []() { cout << "hello" << endl; };


(*fn)(); //prints "hello"

• Most useful for functions that are only ever called once – code can be
written in-place

int gt = 5;
std::remove_copy_if(v1.begin(),v1.end(),std::back_inserter(v2),
[&gt](int x) -> bool { return x > gt;} );

Capture list Parameter list Return type Function body

© Accellera Systems Initiative 20


Dynamic Processes 1

C++03 Modern C++


• sc_spawn requires separate definition • Lambdas enable work to defined
of the function to do the work when sc_spawn called (less typing,
more intuitive)

void work_func(void) { sc_spawn([]()


/* do some work */ { /* do some work */ }
} );
...
sc_spawn(&work_func); • sc_bind and sc_ref are now regular
functions rather than macros

© Accellera Systems Initiative 21


Dynamic Processes 2

C++03 Modern C++


• Member functions required with • Lambda function can be defined in
SC_THREAD macro place
SC_MODULE(Proc) { SC_MODULE(Proc) {
sc_fifo_in <data_t> ip; sc_fifo_in <data_t> ip;
void do_proc(void) { Proc(sc_module_name nm) {
data_t v = ip.read(); auto m = this;
... sc_spawn([m]() {
} data_t v = m->ip.read();
SC_HAS_PROCESS(Proc); ...
Proc(sc_module_name nm) { }, "my_thread");
SC_THREAD(do_proc); }
} );
};

© Accellera Systems Initiative 22


Creating Type Alias

C++03 Modern C++


• Typedefs improve function pointer • Type alias declaration easier to read
declaration but still seem odd

int f1(int,int); int f1(int,int);


int f2(int,int); int f2(int,int);
typedef int (*fptr_t)(int,int); using fptr_t = int(*)(int,int);

fptr_t fp = &f1; fptr_t fp = &f1;


int i = (*fp)(10,20); int i = (*fp)(10,20);

© Accellera Systems Initiative 23


Creating Random Values

C++03 Modern C++


• Standard rand() has poor distribution • New random library has better
and only one basic type generators and distribution options

#include <cstdlib> #include <random>


for(size_t n=100; n>0; --n) { std::default_random_engine gen;
std::cout<< std::rand()%1000 std::uniform_int_distribution<>
<<"\n"; dist(1,1000);
} for(size_t n=100; n>0; --n) {
std::cout << dist(gen)
<< "\n";
}

© Accellera Systems Initiative 24


Arrays

C++03 Modern C++


• C arrays often used if overhead of • std::array<T> provides safe and
std::vector<T> is unacceptable efficient alternative

int arr[100]; #include <array>


memset(arr,0,sizeof(arr)); using Arr_t=std::array<int,100>;
Arr_t arr2;
void f1(int* arr, size_t n) { void f2(Arr_t& arr) {
for(int i=0; i<n; ++i) int i;
arr[i] += i; for(auto& elt:arr)
} elt += i++;
f1(arr,100); }
f2(arr2);

© Accellera Systems Initiative 25


Detecting event triggers

SystemC 2.3.2 Modern C++


• Events have no observable state • triggered() method proposed as part
of next standard

sc_event e1, e2; sc_event e1, e2;


... ...

wait( 10_ns, e1 | e2 ); wait( 10_ns, e1 | e2 );


// No way to tell which event if( e1.triggered() ) ...
if( e2.triggered() ) ...
if( not e1.triggered()
and not e2.triggered() ) ...

© Accellera Systems Initiative 26


Ugly declarations

C++03 Modern C++


• Complex variable types or functions • Repurposing auto keyword makes
returning complicated types code less verbose

std::vector<sc_object> auto kids{sc_get_child_objects()};


kids( sc_get_child_objects() );
auto ph = sc_spawn( func );
sc_core::sc_process_handle
ph = sc_spawn( func );

© Accellera Systems Initiative 27


Unintended conversions

C++03 Modern C++


• Implicit conversions can be annoying • Keyword explicit avoids this

typedef size_t Id; class Transaction {


class Transaction { ...
... explicit operator Id() {
operator Id() return m_id; return m_id;
}; }
void dump(int t) { ... } };
Transaction t1;
dump( t1 ); // silently converts

© Accellera Systems Initiative 28


Mismatched signature overrides

C++03 Modern C++


• Typing mistakes in overridden method • Keyword override specifies intent –
name produce unreported bug checked by compiler

SC_MODULE(M) { SC_MODULE(M) {
... ...
// Silently adds new method // Fails to compile
// instead of overriding void
// inherited virtual function start_of_stimulation() override;
void start_of_stimulation();

© Accellera Systems Initiative 29


Preventing overrides in derived classes

C++03 Modern C++


• Not possible • Keyword final specifies intent –
checked by compiler

struct Transaction { struct Transaction {


... ...
virtual size_t Id() const; virtual size_t Id() const final;
}; };

struct Derived : Transaction { struct Derived : Transaction {


size_t Id() const; // Fails to compile
}; size_t Id() const;
};

© Accellera Systems Initiative 30


Prevention of Default Class Members

C++03 Modern C++


• Dummy private members prevent • Keyword delete specifies intent -
compiler-generated defaults observed by compiler

struct BusMap { struct BusMap {


BusMap(string filename); BusMap(string filename);

private: BusMap(const BusMap&) = delete;


//uncallable copy constructor };
BusMap(const BusMap& m) {}
};

© Accellera Systems Initiative 31


Enabling Default Class Members

C++03 Modern C++


• All required overloaded methods must • Keyword default generates overloaded
be explicitly defined method with default behavior

struct Transaction { struct Transaction {


Transaction() {} Transaction() = default;
Transaction( int ); Transaction( int );
... ...
}; };

© Accellera Systems Initiative 32


Restricting Object Creation
• Make default constructor and copy constructor private
• Can be used to create "singleton" object

class Transaction {
...
private:
Transaction() = default;
Transaction(const Transaction& ) = default;
friend Transaction& get_tx();
...
};

Transaction& get_tx() {
static Transaction tx;
return tx;
};

© Accellera Systems Initiative 33


Overloaded Constructors

C++03 Modern C++


• Each overloaded constructor must be • A constructor may delegate member
fully defined separately initializations to another constructor

struct Cfg { struct Cfg {


Cfg( int a, float b) Cfg( int a, float b)
: m_a(a), m_b(b) {} : m_a(a), m_b(b) {}

explicit Cfg( float a) explicit Cfg( float a)


: m_a(a), m_b(5) {} : Cfg( a, 5 ) {}
... ...
}; };

© Accellera Systems Initiative 34


Static Tests

C++03 Modern C++


• Compiler may not detect bad • Compiler runs additional checks
parameter values or produce unclear specified by static_assert and prints
error messages specified message on failure

template<size_t depth> template<size_t depth>


SC_MODULE( Memory ) { SC_MODULE( Memory ) {
vector<int,depth> mem; vector<int,depth> mem;
... static_assert( depth > 0,
}; "Memory depth must be > 0" );
...
};

© Accellera Systems Initiative 35


Enumerated Types

C++03 Modern C++


• Enumerations are named integer • Enumerations with enum class have
constants - can lead to scope and class scope to avoid collisions
range errors

enum RegType { RO, RW }; enum class RegType { RO, RW };


RegType rt = RW; RegType rt;
rt++; // Uncaught error rt++; // compiler error
rt = 2; // Uncaught error rt = RegType::RO;
switch( rt ) { switch( r ) {
case RW: ... case RegType::RW: ...
case 2: // Uncaught error case 2: // compiler error

© Accellera Systems Initiative 36


Null Pointers

C++03 Modern C++


• Null pointers have value 0 that might • Keyword nullptr is easier to see and
be misinterpreted prevents errors

void f(int x) { std::cout << x << "\n"; }


void f(int* p) {
p == 0 ? std::cout << "null" << "\n" : std::cout << *p << "\n";
}

f( NULL ); // prints "0" f( nullptr ); // prints "null"

© Accellera Systems Initiative 37


Other Interesting Features
• Variadic Templates
– Allows class templates with variable number of template parameters

• Move semantics
– Dynamically allocated contents can be moved between objects
– Efficient + Safe
– Implemented by overriding class move constructor and assignment operator

• decltype construct to assist template metaprogramming

© Accellera Systems Initiative 38


Where to learn more about
C++11/C++14/C++17
• What compiler version support do you need?
https://en.cppreference.com/w/cpp/compiler_support

• More examples and syntax


https://www.cppreference.com

© Accellera Systems Initiative 39


Where to download more examples
• https://github.com/dcblack/ModernSystemC.git

© Accellera Systems Initiative 40


Questions?

© Accellera Systems Initiative 41

You might also like