Вы находитесь на странице: 1из 110

SystemC and Behavior Coding

Alan P. Su, Ph.D. alansuphd@gmail.com

SystemC 2.3, IEEE 1666-2011

Chapter Chapter Chapter Chapter Chapter

1 2 3 4 5

SystemC Overview Module, Signal, Port & Blockingness Processes, Interface & Channel Data Types Fixed Point Types

Learn how to code in SystemC Understand the SystemC simulation mechanism, thus understand how to verify a SystemC design Familiar with SystemC basic data types Levels of abstraction: system level, transaction level, cycle-accurate, behavior level and RTL Using High Level Synthesis

Accellera is an independent not-for-profit standard organization. It is the home of Verilog, SystemVerilog & SystemC. Among which SystemC is an open source standard for system-level design Where is Accellera

Until March 2013

the latest version is SystemC 2.3 (IEEE 1666-2011)

http://www.accellera.org/downloads/standards/sy stemc

The dream to realize the unison of HW/SW designing languages. A unified design environment. Version 1: it is just another HDL, not much to do with system-level designing Version 2: with the adding of channel, now it is a serious system-level language Version 2.1: adding some programming language features and simulation semantics, e.g. sc_spawn, before_end_of_elaboration, etc. IEEE approved OSCI SystemC 2.1 as the IEEE 1666 Standard on 12/12/2005 SystemC 2.2 is some enhancements and bug fixes of 2.1 IEEE 1666-2011, aka SystemC 2.3, adding TLM 2.0 and some process control syntax

Is a C++ class library and a methodology that one can use to effectively create cycle-accurate models of functions, hardware architecture, and interfaces of the SoC and system-level designs. One can use SystemC and standard C++ development tools to create a systemlevel model, quickly simulate to validate and optimize the design, explore various algorithms, and provide the hardware and software development team with an executable specification of the system.

Modules: component Processes: functions, SC_THREAD & SC_METHOD Ports: I/O ports Signals: wires Rich set of port and signal types Rich set of data types Clocks Cycle-based simulation: ultra light-weight and fast Multiple abstraction levels Communication protocols: channel & interface Debugging support: runtime error checking Waveform tracing: VCD, WIF and ISDB formats

C, C++ System-level Modeling

Done Verilog/VHDL


Simulation & Analysis Simulation

Results Synthesis

To tape out, test and product delivery



Methodology-Specific Libraries
Master/Slave Library, etc.

Layered Libraries
Verification Library, TLM 2.0 Library, etc.

Signal, Mutex, Semaphore, FIFO, etc.

Primitive Channels

Core Language
Modules Ports Interfaces Channels

4-valued Logic Type 4-valued Logic Vectors Bits and Bit Vectors Arbitrary Precision Integers Fixed-Point Types Events, Processes

Data Types

Event-Driven Simulation

C++ Language Standards

SystemC is a set of C++ class definitions and a methodology for using these classes. C++ class definition means systemc.h and the matching library. Methodology means the use of simulation kernel and modeling. You can use all of the C++ syntax, semantics, run time library, STL and such. However you need to follow SystemC methodology closely to make sure the simulation executes correctly.


SystemC is a Hardware Description Language (HDL) from system-level down to gate level. Modules written in traditional HDLs like Verilog and VHDL can be translated into SystemC, but not vise versa. Reason: Verilog and VHDL do not support transaction-level. System-Verilog is Verilog plus assertion, which is an idea borrowed from programming languages. And SystemC supports assertion as well through the C++ syntax and semantics.

SystemVerilog is Verilog plus verification (assertion). Actually the above statement is not fair but it is the truth now. SystemVerilog and SystemC work together to complete the design platform from system-level to gate-level. SystemC deals with whatever above RTL. SystemVerilog deals with RTL and below.




It is a language to unify the design environment, SW and HW. A unified design environment. Well, this is a dream in the academy. In industry, this is a long way to go and as of today, SystemC is not the answer. Notice, SystemC is an HDL, it itself does not support software performance measure mechanism. Will the day that an unified design language be realized? We just dont know. But people are talking about UML, the Unified Modeling Language.

SystemC does not model software. It is an HDL. Period.


SystemC does not run faster, higher abstraction level does.


To be categorized as a system-level language, the simulation SPEED is the key. The simulation speed should take no 1,000 time slower than the real HW. In another word, 1 second of HW execution time equals 16 minutes and 40 seconds simulation time To achieve this kind of performance, the system is best modeled in transaction level, e.g. token based



A module is the basic structural building block in SystemC, the same as Verilog module. It may contain
Ports for communication Data members Channel members Processes Member functions not registered as processes Instances of other modules


Derived from class sc_module: class my_module : public sc_module { }; A more commonly used way: SC_MODULE (module_name) { // ports, sc_export, data members, member functions // processes, etc. SC_CTOR(module_name) { // constructur // body of constructor // process registration, sensitivity lists // module instantiations, port binding etc. } }; // do not forget this final ; SC_MODULE is a macro: #define SC_MODULE(user_module_name) \ struct user_module_name : sc_module


sc_in<data_type>; //input port sc_out<data_type>; //output port sc_inout<data_type>; //in/out port If data_type is a type with size declaration, a space is needed before the closing bracket, e.g.

sc_in<sc_uint<10> >;


Using HW port-to-port binding is not intuitive at system-level and slow. Starting 2.1 SC_EXPORT is supported for light weight binding. Vastly used in the TLM library. People say at transaction level SC_EXPORT must be used. The purpose is to speed up the simulation.


Is a un-directional wire when connects to ports The flow of data is determined by the ports a signal connects to Example:

sc_signal<sc_uint<32> > w1;


Non-blocking descriptor Example:

sc_signal<sc_uint<32> > reg1; Reg1 = 3;


An assignment statement has two sides, Right-Hand-Side (LHS) and Left-Hand-Side (LHS)
A = 5 + 3;

Evaluate: the execution of the RHS expression


Update: update the LHS expression



Evaluate and update immediately and in one step/statement

// a == b = a; c = b; a = c; 3, // // // b b c a == is is is 4, c == 5 now 3 now 3 3, again


Two steps Evaluate first and immediately, and update occurs when all evaluations at current time step are completed
// a == b = a; c = b; a = c; Wait(); 3, // // // b b c a == is is is 4, c == 5 still 4 still 5 still 3

// b is updated as 3 // c is updated as 4 // a is updated as 5


sc_uint<8> ary[8] = {1,2,3,4,5,6,7,8}; for (i=0; i<7; i++) ary[i+1] = ary[i]; // blocking // all elements in ary have // the same value, 1


sc_signal<sc_uint<8> > ary[8]; ary[0] = 1; ary[1] = 2; ary[7] = 8; for (i=0; i<7; i++) ary[i+1] = ary[i]; // non-blocking // ary[] becomes a shift register


When using reference C codes downloaded from the Internet, say H.264 codes, notice that all these codes are in SW mindset. Which means, all assignments are blocking


Named Mapping
#include systemc.h #include mult.h #include coeff.h #include sample.h SC_MODULE(filter) { sample *s1; coeff *c1; mult *m1; sc_signal<sc_uint<32> > q, s, c; SC_CTOR(filter) { s1 = new sample(s1); s1->din ( q ); s1->dout ( s ); c1 = new coeff(c1); c1->out ( c ); m1 = new mult(m1); m1->a ( s ); m1->b ( c ); m1->q ( q ); } };





Positional Mapping is no longer supported in 2.2

#include systemc.h #include mult.h #include coeff.h #include sample.h SC_MODULE(filter) { sample *s1; coeff *c1; mult *m1; sc_signal<sc_uint<32> > q, s, c; SC_CTOR(filter) { s1 = new sample(s1); (*s1) ( q,s ); // 2.2 illegal c1 = new coeff(c1); (*c1) ( c ); // 2.2 illegal m1 = new mult(m1); (*m1) ( s, c, q ); // 2.2 illegal } };





Processes are the basic unit of execution within SystemC. Processes are called to emulate the behavior of the target device or system. The real work of a module is performed in processes. Processes are functions that are identified to the SystemC kernel and called/activated whenever signals these processes are sensitive to. These statements are executed sequentially until the end of the process, or being suspended by a wait() statement. SC_METHOD, SC_THREAD, SC_CTHREAD


#include systemc.h SC_MODULE(timer) { sc_inout<bool> start; sc_out<bool> timeout; sc_in<bool> clock; int count; void runtimer(); SC_CTOR(timer) { SC_THREAD(runtimer); sensitive << clock.pos(); sensitive << start; count = 0; } };

// ports

// data and function members

// constructor // sensitivity list

// do not forget the final semi-column

sensitive_pos and sensitive_neg is deprecated in 2.2


// timer.cpp #include timer.h void timer::runtimer() { while(1) { if (start) { cout << Timer: timer start detected << endl; count = 5; timeout.write(0); start.write(0); } else { if (count == 0) { timeout.write(1); } else { count--; timeout.write(0); } } wait(); } }


class sc_clock : public sc_signal_in_if<bool>, public sc_module { public: sc_clock(); explicit sc_clock( sc_module_name name_ ); sc_clock( sc_module_name name_, const sc_time& period_, double duty_cycle_ = 0.5, const sc_time& start_time_ = SC_ZERO_TIME, bool pos_edge_first = true); }; sc_time clkPrd(20, SC_NS), clkDly(2, SC_NS); sc_clock clock1(clock1, clkPrd, 0.50, clkDly, true);




Data Application/ Data Stimuli




Data Application/ Sink



frame data; void transmit(void) { int framenum; frams s; packet buffer; event_t event;

//global data frame storage for Channel //transmits frames to Channel // sequence number for framces // Local frame // Buffer to hold intermediate data // Event to trigger actions in transmit

framenum = 1; // Initialize sequence numbers get_data_fromApp(&buffer); // Get initial data from Application while (true) { // Runs forever s.info = buffer; // Put data into frame to be sent s.seq = framenum; // Set sequence number of frame send_data_toChannel(&s); // Pass frame to Channel to be sent start_timer(s.seq); // Start timer to wait for acknowledge // If timer times out packet was lost wait_for_event(&event); // Wait for events from channel and timer if (event == new_frame) { // Got a new frame get_data_fromChannel(s) // Read frame if (s.ack == framenum) { // Did we get the correct acknowledge get_data_fromApp(&buffer); // Yes, get more data inc(framenum); // Increase framenum } } }

void receiver(void) { int framenum; frame r,s; event_t event;

framenum = 1; while (true) { wait_for_event(&event); if (event == new_frame) { get_data_fromChannel( r ); if (r.seq == framenum) { send_data_toApp(&r.info); inc(framenum); } s.ack = framenum 1; received send_data_toChannel(&s); } } }

// // // //
// // // // // // // //

Gets frames from channel Scratchpad frame number Temp frames to save information Event to cause actions in receiver
Start framenum at 1 Runs forever Wait for data from Channel A new frame has arrived Get the data from the Channel Is this the frame we expected Yes, then send data to application Get ready for the next frame

// Send back an acknowledge that frame was // Send acknowledge






receiver M



// pakcet.h file #ifndef #define #include "systemc.h" class packet_type { public: long info; int seq; int retry;


packet_type() : info(0), seq(0), retry(0) {} packet_type( long info_, int seq_, int retry_ ) : info(info_), seq(seq_), retry(retry_) {} packet_type( const packet_type& a ) : info( a.info ), seq(a.seq), retry(a.retry) {} ~packet_type() {} packet_type& operator = ( const packet_type& a ) { info = a.info; seq = a.seq; retry = a.retry; return *this; } void print( ostream& os ) const { os << info << ", " << seq << ", " << retry << endl; }

friend bool operator == ( const packet_type& a, const packet_type& b ) { return ( a.info == b.info && a.seq == b.seq && a.retry == b.retry ); } friend ostream& operator << ( ostream& os, const packet_type& a ) { a.print( os ); return os; } }; extern void sc_trace(sc_trace_file *tf, const packet_type& v, const std::string& name); #endif

// packet.cpp file #include packet.h

void sc_trace(sc_trace_file *tf, const packet_type& v, const sc_string& NAME) { sc_trace(tf, v.info, NAME + .info); sc_trace(tf, v.seq, NAME + .seq); sc_trace(tf, v.retry, NAME + .retry); }


// transmit.h #include packet.h

SC_MODULE (transmit) { sc_in<packet_type> sc_in<bool> sc_out<packet_type> sc_inout<bool> sc_in<bool>

tpackin; timeout; tpackout; start_timer; clock;

int buffer; int framenum; packet_type packin, tpackold; packet_type s; int retry; bool start;

void send_data(); int get_data_fromApp();

// Constructor SC_CTOR(transmit) { SC_METHOD(send_data); sensitive << timeout; sensitive << clock.pos(); framenum = 1; retry = 0; start = false; buffer = get_data_fromApp(); } };

// transmit.cpp #include transmit.h int transmit::get_data_fromApp() { int result; result = rand(); cout << Generate: Sending Data Value = << result << \n; return result; } void transmit::send_data() { if (timeout) { s.info = buffer; s.seq = framenum; s.retry = retry; retry++; tpackout = s; start_timer = true; cout << Transmit:Sending packet no. << s.seq << endl; } else { packin = tpackin; if (!(packin == tpackold)) { if (packin.seq == framenum) { buffer = get_data_fromApp(); framenum++; retry = 0; } tpackold = tpackin; s.info = buffer; s.seq = framenum; s.retry = retry; retry++; tpackout = s; start_timer = true; cout << Transmit:Sending packet no. << s.seq << endl; } } }


// noisybus.h #include packet.h SC_MODULE (noisybus) { sc_in<packet_type> tpackin; sc_in<packet_type> rpackin; sc_out<packet_type> tpackout; sc_out<packet_type> rpackout; packet_type packin; packet_type packout; packet_type ackin; packet_type ackout; void receive_data(); void send_ack(); // Constructor

// noisybus.cpp #include noisybus.h void noisybus::receive_data() { int i; packin = tpackin; cout << Noisybus: Received packet seq no. = << packin.seq << endl; i = rand(); packout = packin; cout << Noisybus: Random number = << i << endl; if ((i > 1000) && (i < 5000)) { packout.seq = 0; } rpackout = packout; } void noisybus::send_ack() { int i; ackin = rpackin; cout << Noisybus: Received Ack for packet = << ackin.seq << endl; i = rand(); ackout = ackin; if ((i > 10) && (i < 500)) { ackout.seq = 0; } tpackout = ackout; }

SC_CTOR(noisybus) {
SC_METHOD(receive_data); sensitive << tpackin; SC_METHOD(send_ack); sensitive << rpackin; } };


// receiver.h #include packet.h SC_MODULE(receiver) { sc_in<packet_type> rpackin; sc_out<packet_type> rpackout; sc_out<long> dout; sc_in<bool> rclk; int framenum; packet_type packin, packold; packet_type s; int retry; void receive_data(); //Constructor SC_CTOR(receiver) { SC_METHOD(receive_data); sensitive << rclk.pos(); framenum = 1; retry = 1; } };

// receiver.cpp #include receiver.h void receiver::receive_data() { packin = rpackin; if (packin == packold) return; cout << Receiver: got packet no. = << packin.seq << endl; if (packin.seq == framenum) { dout = packin.info; framenum++; retry++; s.retry = retry; s.seq = framenum 1; rpackout = s; } packold = packin; }


// timer.h #include systemc.h SC_MODULE(timer) { sc_inout<bool> start; sc_out<bool> timeout;

// timer.cpp #include timer.h void timer::runtimer() { while(true) { if (start) { cout << Timer: start detected << endl; count = 5; timeout = false; start = false; } else { if (count > 0) { count--; timeout = false; } else { timeout = true; } } wait(); } }

sc_in<bool> clock;
int count; void runtimer(); // Constructor SC_CTOR(timer) { SC_THREAD(runtimer); sensitive << clock.pos(); sensitive << start; count = 0; } };


// display.h #include packet.h SC_MODULE(display) { sc_in<long> din; void print_data(); // Constructor SC_CTOR(display) { SC_METHOD(print_data); sensitive << din; } };

// display.cpp #include display.h void display::print_data() { cout << Display: Data value received, Data = << din << endl; }


// main.cpp #include packet.h #include timer.h #include transmit.h #include noisybus.h #include receiver.h #include display.h int sc_main(int argc, char* argv[]) { sc_signal<packet_type> PACKET1, PACKET2, PACKET3, PACKET4; sc_signal<long> DOUT; sc_signal<bool> TIMEOUT, START; sc_clock CLOCK(clock, 20); // transmit clock sc_clock RCLK(rclk, 15); // receiver clock transmit t1(transimit); // transmit instantiation t1.tpackin(PACKET2); // port to signal binding t1.timeout(TIMEOUT); t1.tpackout(PACKET1); t1.start_timer(START); t1.clock(CLOCK); // clocking noisybus n1(noisybus); instantiation n1.tpackin(PACKET1); // n1.rpackin(PACKET3); // n1.tpackout(PACKET2); // n1.rpackout(PACKET4); // // noisybus

receiver r1(receiver); r1.rpackin(PACKET4); // r1.rpackout(PACKET3); // r1.dout(DOUT); // r1.rclk(RCLK); //

// receiver instantiation connect to noisybus connect to noisybus port to signal binding clocking

display d1(display);// display instantiation d1 << DOUT; // signal to port connection timer tm1(timer); // timer instantiation tm1 << START << TIMEOUT << CLOCK.signal(); // signal to port connections // tracing: // trace file creation, with VCD type output sc_trace_file *tf = sc_create_vcd_trace_file(simplex); // External signals sc_trace(tf, CLOCK.signal(), clock); sc_trace(tf, TIMEOUT, timeout); sc_trace(tf, START, start); sc_trace(tf, PACKET1, packet1); sc_trace(tf, PACKET2, packet2); sc_trace(tf, PACKET3, packet3); sc_trace(tf, PACKET4, packet4); sc_trace(tf, DOUT, dout); sc_start(10000); // simulate for 10000 time steps // default is ps

connect port to connect port to

to transmit signal binding to transmit signal binding

return(0); }



In a typical programming language, processes are executed sequentially as control is transferred from one process to another to perform the desired function. Processes are not hierarchical, so no process can call another process directly. Processes can call methods and functions that are not processes. Processes have sensitivity lists. The process is called, or activated, whenever any values in the sensitivity lists are changed.


However, hardware components and devices (including wires) are executed in parallel. To mimic the hardware behavior, at each time period, or on the trigger of an event, ready processes are called to simulate the behavior at that moment, till before next time period, or the next wait().


SC_METHOD Method is just SystemC ways of saying function, or subroutine. Therefore it inherits the behavior of a function. A process is called to execute and returns the execution back to the calling mechanism when completed. A locally declared variable is not permanent. Meaning, the value of a local variable is no longer valid after the end of a method.


SC_THREAD Thread is a process that always alive. Unlike method, its local variables are alive throughout the simulation. Thread can be suspended and reactivated. A thread can contain wait() statements that suspend the process until an event occurs on one of the signals the process is sensitive to. Thread is not supported in the OSCI Synthesizable Subset


Clocked thread is a special case of a thread process. A clocked thread is only sensitive to one edge of a clock cycle (positive or negative). Clocked thread is used to clearly describe reset behavior by using reset_signal_is() async_reset_signal_is() The use of wait() is the same as in SC_THREAD.


// IIR.h
SC_MODULE(IIR) { sc_in<sc_uint<32> > sc_out<sc_uint<32> > sc_in<bool> sc_in<sc_clock> void iir(); sc_uint<32> x, y; mid, mid1, mid2; sc_signal<sc_uint<32> > sc_uint<32> SC_CTOR(IIR) { SC_CTHREAD(iir, clk.pos()); reset_signal_is(enable, false); a1 = 354; a2 = 1799; b1 = 573; b2 = 1254; } input; output; enable; clk;

a1, a2, b1, b2;

#include IIR.h void IIR::iir() { // reset mid = 0; mid1 = 0; mid2 = 0; // computation while (1) { wait(); x = input.read(); y = mid + a2 * mid1 + b2 * mid2; mid = x + a1 * mid1 + b1 * mid2; mid2 = mid1; mid1 = mid; output.write(y); } }



Starting 2.1, SC_SPAWN is supported Dynamic process means a component is materialized only when used. This is a programming skill to speed up the simulation performance. The concept of dynamic process is not HW oriented.


int sc_main(int argc, char* argv[]) { sc_fifo<int> s1(10); // fifo of depth 10 sc_fifo<int> s2(10); sc_fifo<int> s3(10); stimgen stime(stime); // stimgen module stim(s1, s2); // binding adder add(add); // adder module add(s1, s2, s3); // binding monitor mon(mom); // monitor module mon.re(s3); // binding sc_start(); // run indefinitely return 0; }



250 ps a mult a

250 ps

250 ps


c b



1.Input to b is changed 2.Triggered signal b to compute the change 3.Input to a is changed 4.Triggered signal a to compute the change 5.Inputs to mult and signal c are not changed 6.No execution to mult and c

1.Inputs to mult are changed 2.Triggered mult to compute the change 3.Inputs to signals a, b and c are not changed 4.No execution to a, b and c

1.Input to signal c is changed 2.Triggered signal c to compute the change 3.Inputs to signals a, b and mult are not changed 4.No execution to a, b and mult



event based simulator. Events occur at a given simulation time. main() is part of the SystemC library. It calls sc_main() Elaboration: The execution of the sc_main() from the start to the first invocation of sc_start().

The schedule controls the timing and order of process execution, handles event notifications and manages updates to channels. The scheduler supports the notion of delta-cycle, which consists of an evaluate phase and update phase. Processes are non-preemptive, meaning for a thread process, codes between two wait() will execute without any other process interruption and a method process completes its execution without interrupted by another process.


the first step in the simulator scheduler. Each method process is executed once during initialization and each thread process is executed until a wait statement is encountered. To turn off initialization for a particular process, call dont_initialize() after the SC_METHOD, SC_THREAD or SC_CTHREAD process declarations inside a module constructor. The order of processes execution is unspecified. However the execution order between processes is determined. This only means every two simulation runs to the same code always have the same execution ordering to yield identical results.

Synchronization Bus Arbitration

evaluate update

A Arequests checks bus grant

B B B requests checks reads from grant bus memory

evaluate update

request_update grant-ID evaluate

read checkGrant


Cycle: 0 1
read memory evaluate write update


Starting the simulation: sc_start() is called in sc_main() to start the scheduler Stopping the simulation: sc_stop() is called to stop the scheduler and return control back to the sc_main(). In this case the simulation cannot be continued anymore. Obtaining current simulation time: sc_time_stamp() & sc_simulation_time()


Data type sc_time

Time unit:
enum sc_time_unit {
SC_FS = 0, // femtosecond SC_PS, // picosecond SC_NS, // nanosecond SC_US // microsecond SC_MS, // millisecond SC_SEC // sec };

sc_time t(123, SC_NS); // t = 123 nanoseconds


The time resolution is the smallest amount of time that can be represented by all sc_time objects in a SystemC simulation. The default value for the time resolution is 1 picosecond. A user may set the time resolution to some other value by calling the sc_set_time_resolution(). This function, if called, must be called before any sc_time objects are constructed. A user may ascertain the current time resolution by calling the sc_get_time_resolution(). Any time smaller than the time resolution will be rounded off, using round-to-nearest.

Time unit is to specify the period of one simulation step. It is larger or equal to time resolution. The default time unit is 1 nanosecond. Call sc_set_default_time_unit() to set default time unit: Time values may sometimes be specified with a numeric value without time unit:
sc_start(1000); // run for 1000 time units
sc_set_default_time_unit(10, SC_MS);


An event is an object and its synopsis is:

class sc_event { public: sc_event(); ~sc_event(); void cancel(); void notify(); void notify( const sc_time& ); void notify( double, sc_time_unit ); sc_event_or_list& operator | (const sc_event& ) const; sc_event_and_list& operator & (const sc_event& ) const; private: sc_event (const sc_event&); sc_event& operator = ( const sc_event& ); }


An sc_event instance determines when and whether a process execution is triggered or resumed. The sc_event provides basic synchronization for processes. Event notification causes the kernel to call a method process, or to resume a thread process that is sensitive to the event. The event keeps a list of processes that are sensitive to occurrences of the event. The owner of an event, a process or a channel, is responsible to report change to the event. However, it is the events to keep the list of what processes the scheduler should trigger or resume on this event.


A, B & C: Processes D: Channel


Notify B

Notify C


3 ways to notify an event:

Immediate: the event is triggered in the current evaluation phase of the current deltacycle; notify() Delta-cycle delayed: the event will be triggered during the evaluate phase of the next delta-cycle; notify(0, SC_NS) or notify(SC_ZERO_TIME) Timed: the event will be triggered at the specified time in the future; notify(10, SC_NS)
sc_event my_event; sc_time t(10, SC_NS); my_event.notify(); my_event.notify(0); my_event.notify(t);
// // // // a 10ns time interval immediate delta-cycle delayed notification in 10ns

Earlier notification will always override one scheduled to occur later, and an immediate notification is always earlier than any deltacycle delayed or timed notification. Notice a potential non-deterministic situation:
Process A{ Process B { Process C { my_event.notify(); my_event.notify(0); wait(my_event); } } }

If A first then B, C will be executed at both current and next delta-cycle. However if B first then A, C will execute once only at the current delta-cycle


A pending delayed event notification may be canceled using cancel(). However immediate event cannot be canceled.
sc_event a, b, c; sc_time t(10, SC_MS); a.notify(); notify(0, b); notify(t, c);

a.cancel(); b.cancel(); c.cancel();

// Error! // Canceled! // Canceled!


While sc_event() can only allow a single pending event, sc_event_queue() can handle multiple pending events.



The basic modeling elements of inter-module communication are interface, port and channel

An interface defines the set of access functions (methods) of a channel. It specifies the name, parameters and return type of functions but does not have the implementation details.
sc_fifo_in_if sc_fifo_out_if sc_mutex_if sc_semaphore_if sc_signal_in_if sc_signal_inout_if


A channel implements the interface functions. Channel provides the communication between methods or within a module provides the communication between processes. Channel may implement one or more interfaces Different channels may implement the same interface in different ways sc_buffer sc_fifo sc_mutex sc_semaphore sc_signal sc_signal_resolved sc_signal_rv


2 value single bit type 0 (false) and 1 (true)

& (and) | (or) ^ (xor) ~ (not)

sc_bit operators Bitwise






a = a & b; a = a | b;

a &= b; a |= b;


4 value single bit type 0 (false), 1 (true), X (unknown), Z (high-impedance)

sc_logic operators Bitwise Assignment & (and) = | (or) &= ^ (xor) |= ~ (not) ^=




x = 1; x ^= y;

x = Z; x != y;



1 to 64 bit fixed precision signed/unsigned integer type

Fixed precision integer operators
Bitwise Arithmetic Assignment Equality Relational Autoincrement Autodecrement ~ + = == < ++ -& += != <= > >= | * -= *= ^ >> / /= << % %= &= |= ^=

Bit Select
Part Select Concatenation

range() (,)

mybit = myint[7]; intc = (inta, intb);

myrange = myint.range(7,4);

Arbitrary size signed/unsigned integer type

Fixed precision integer operators Bitwise
Arithmetic Assignment Equality

+ = ==

+= !=

* -=


% %= &= |= ^=








Autodecrement -Bit Select Part Select Concatenation [x] range() (,)

sc_biguint<128> b1; sc_biguint<64> b2; sc_biguint<150> b3; b3 = b1*b2; // 42 bits will be truncated before assign into b3

Arbitrary sized 2 value vector type

Fixed precision integer operators
Bitwise Assignment Equality Bit Select Part Select ~ = == [x] range() & &= != | |= ^ ^= >> <<

Concatenation (,) Reduction and_reduce() or_reduce() xor_reduce()

sc_bv<64> databus; sc_logic result; result = databus.or_reduce(); // or_reduce returns the result of ORing all bits in databus // and_reduce(), or_reduce() and xor_reduce() return bool value

Arbitrary sized 4 value vector type

Fixed precision integer operators Bitwise Assignment Equality Bit Select Part Select ~ = == [x] range() & &= != | |= ^ ^= >> <<

Concatenation (,) Reduction and_reduce() or_reduce() xor_reduce()

sc_lv<16> bus1; Bus1 = ZZZZZZZZZZZZZZZZ; // this string like assignment also works for sc_bv



Total word length (wl): the total number of bits. Word length must be greater than 0. Integer word length (iwl): the integer fraction bit length. iwl can be positive or negative, and can larger than wl.
xxx.xx .ssxxxxx xxxxx00 : wl = 5, iwl = 3 : wl = 5, iwl = -2 : wl = 5, iwl = 7

For above examples, notice carefully the position of the decimal point


How to handle the situation when the result of an operation generates more precision in the LSB than is available as specified by wl and iwl. For example, a division, 1/3.
Quantization Mode Round to plus infinity Name SC_RND

Round to zero
Round to minus infinity Round to infinity Convergent round Truncate


Truncate to zero


Round the value to the closest representable number by adding the MSB of the removed bits to the remaining bits. () 01001.010[10100] 01001.011


sc_fixed<4, 2> x; sc_fixed<3, 2, SC_RND> y; x = 1.25; // 1.25 = 01.01 y = x; // 01.0[1] 01.1 = 1.5

x = -1.25 // -1.25 = 10.11 (01.01 2s complement) y = x; // 10.1[1] 11.0 = -1


Performs SC_RND if the two nearest representable numbers are not an equal distance apart, otherwise round to zero is performed. x.x is not of equal distance. .xx or xx. Are of equal distance When round to zero, for a positive number redundant bits are deleted, and for a negative number SC_RND is performed.


Performs SC_RND if the two nearest representable numbers are not an equal distance apart, otherwise round to minus infinity is performed. Minus infinity: omitting the redundant bits on the LSB side.


Performs SC_RND if the two nearest representable numbers are not an equal distance apart, otherwise round to plus infinity is performed. Plus infinity: for positive numbers perform SC_RND. For negative numbers eliminate the redundant bits.


Performs SC_RND if the two nearest representable numbers are not an equal distance apart, otherwise checks the LSB of the remaining bits. If the LSB is 1 then round towards plus infinity. If the LSB is 0 then round towards minus infinity. sc_fixed<4, 2> x; sc_fixed<3, 2, SC_RND> y; x = .75; // .75 = 00.11 y = x; // 01.0 = 1


Default mode. The redundant bits are always deleted. Other ways of saying is it always rounds towards minus infinity.


Performs truncate to positive numbers. Performs SC_RND to negative numbers.


Overflow: when the result of an operation generated more bits on the MSB side than are available for representation. Overflow mode is specified by the o_mode and n_bits parameters to a fixed point data type, it has two major modes, saturate and wrap-around. MIN is the smallest negative number that can be represented. MAX is the largest positive number that can be represented. Overflow Mode
Saturate Saturate to zero Symmetrical saturate Wrap-around


Sign magnitude wrap-around


Converts the value to MAX for an overflow and MIN for an underflow.


sc_fixed<4,4> x; sc_fixed<3,3,SC_TRN,SC_SAT> y; x = 6; // 0110, with a sign bit y = x; // 011 = 3, remember the sign bit x= -5; // 1011 y = x; // 100 = -4


Sets the result to 0 for any value that is outside the representable range of the fixed point data type.


In 2s complement notation there is one more negative representation than positive. Sometimes it is desirable to have the same positive and negative numbers of representations. SC_SAT_SYM generates MAX and negative overflow generates MAX for signed numbers.


When overflow occurs, this operation wraps around the overflowed value from MAX to MIN. The unsigned case is similar to the way a counter would work in hardware. There are two different cases within the wrap-around operation: n_bits parameter set to 0 or is greater than 0. The n_bits parameter sets the number of bits to be wrapped around.


The default overflow mode. Any MSB bits outside the range of the target type are deleted.


n_bits MSB bits are to be saturated. The sign bit is retained. The bits that are not saturated are maintained.


Deletes any MSB bits that are outside the result work length. Sign bit is set to the value of the LSB of deleted bits.


A sign magnitude wrap is performed and n-bits MSB bits will be saturated. The first n_bits MSB bits are saturated to MAX for positive numbers and to MIN for negative numbers.


Signed and unsigned fixed point data type Use static way to set parameters Declaration Syntax:
sc_fixed <wl, iwl [,q_mode [, o_mode [, n_bits]]]> var_name([init_val][,cast_switch] [,observer]);

sc_ufixed<16, 1, SC_RND_CONV, SC_SAT_SYM, 1> a(1.5);


Declaration syntax:

sc_ufix a(1.5, 16, 1, SC_RND_CONV,SC_SAT_SYM, 1);


sc_fix var_name([init_val] [,wl,iwl] [,q_mode,o_mode[,n_bits]] [,cast_switch] [,observer]); sc_fix var_name([init_val] ,type_params [,cast_switch] [,observer]);


Thanks to you all!

11 0