SystemC Tutorial Note
Hardware simultion
- source: Learn SystemC from Forte
Introduction
- systemC?
- library of C++ classes
- hardware aware
- concurrency
- bit accuracy
- simulation time advancement
#include <systemc.h> SC_MODULE( and2 ){ sc_in<DT> a; sc_in<DT> b; sc_out<DT> f; sc_in<bool> clk; //DT -> sc_uint<1> void func(){ f.write( a.read() & b.read() ); } SC_CTOR( and2 ){ //systemC constructor //case1 SC_METHOD(func); sensitivity << a << b; //case2 SC_METHOD(func); sensitive << clk.pos(); // or clk.neg() } }
- Threads
- a function made to act like a hardware process
- run concurrently
- sensitive to signals, clock edges or fixed amount of simulation time
- Not called by the user, always active
- three types
- SC_METHOD()
- Executes once every sensitivity event
- runs continuously
- verilog’s @always block
- synthesizable
- useful for combinational expressions or simple sequential logic
- SC_THREAD()
- runs once at start of simulation, then suspends itself when done
- can contain an infinite loop to execute code at a fixed rate of time
- similar to @initial block
- NOT synthesizable
- uesful for testbenches to describe clocks or initial startup signal sequences
- SC_CTHREAD()
- “clocked thread”
- runs continuously
- references a clock edge
- synthesizable
- can take one or more clock cycles to execute a single iteration
- used in 99% of all high-level behavioral designs
- SC_METHOD()
- a function made to act like a hardware process
- Integer datatypes:
- bit-accurate
- fixed width
- always 32bits
- Unsigned and signed
- sc_uint
where N is the bitwidth - sc_int
- sc_uint
- bit-accurate
Using SC_CTHREADs
- SC_METHODs
- Limited to one cycle
- fine for counters or simple sequential designs
- not much different than hand coded RTL
- can’t handle multi-cycle algorithms
- SC_CTHREADs
- not limted to one cycle
- can contain continous loops
- can contain large blocks of code with operations or control
- great for behavior synthesis
- FIR Filter
fir.h
: module and thread declaration
``` cpp #includeSC_MODULE( fir ){ sc_in< bool > clk; sc_in< bool > rst; sc_in< sc_int<16> > inp; sc_out< sc_int<16> > outp; void fir_main(); //just declaration here
SC_CTOR( fir ){ //systemC constructor
SC_CTHREAD( fir_main, clk.pos() ); reset_signal_is(rst, true ); } }
// Coefficients for each FIR const sc_unint<8> coef[5] = {18,77,107,77,18};
* `fir.cc`: thread definition ```cpp // FIR Main thread #include "fir.h" void fir::fir_main(void) { // Reset code // Reset internal variables // Reset outputs sc_int<16> taps[5]; // set coefficients outp.write( 0 ); // initialize wait(); while( true ){ // Read inputs // Algorithm code // Write outputs for(int i = 4; i > 0; i--){ taps[i] = taps[i-1]; } taps[0]=inp.read(); sc_int<16> val; for(int i=0; i < 5; i++){ val += coef[i]*taps[i]; } outp.write(val); wait(); // Associated with 'clk' class } }
Testbenches
- Verification
- Hierarchical test environment
- top level test structure
- instance of FIR module (DUT)
- instance of testbench module
- connectivity
- tentbench module
- top level test structure
- Hierarchical test environment
- Test environment
- In a host module
System
- Module
fir0
- clk, rst, inp, outp
- Testbench
tb0
- clk, rst, inp, outp
clk
is generated from systemC
- Module
- In a host module
- Basic
SYSTEM
structureSC_MODULE( SYSTEM ){ // Module declarations // Local signal declarations SC_CTOR( SYSTEM ){ // Constructor // Module instance signal connections } ~SYSTEM() // Destructor }
main.cc
#include <systemc.h> #include "fir.h" #include "tb.h" SC_MODULE( SYSTEM ){ tb *tb0; // declare a pointer(*) to its reference name(tb0) fir *fir0; sc_signal<bool> rst_sig; sc_signal<sc_int<16>> inp_sig; sc_signal<sc_int<16>> outp_sig; sc_clock clk_sig; SC_CTOR( SYSTEM ) : clk_sig ("clk_sig",10, SC_NS) { tb0 = new tb("tb0"); tb0->clk( clk_sig ); // remind that tb0 is a pointer tb0->rst( rst_sig ); tb0->inp( inp_sig ); tb0->outp( outp_sig); fir0 = new fir("fir0"); fir0->clk(clk_sig); fir0->rst( rst_sig ); fir0->inp( inp_sig ); fir0->outp( outp_sig); } ~SYSTEM(){ delete tb0; delete fir0; } } // SYSTEM instantiation SYSTEM *top = NULL; int sc_main(int argc, char* argv[]){ // acgc = arg count, argv = arg vector top = new SYSTEM("top"); sc_start(); return 0; }
tb.h
#include <systemc.h> SC_MODULE( tb ){ sc_in< bool > clk; sc_out< bool > rst; //opposit to fir module sc_out< sc_int<16> > inp; //opposit to fir module sc_in< sc_int<16> > outp; //opposit to fir module void source(); void sink(); SC_CTOR(tb){ SC_CTHREAD( source, clk.pos() ); SC_CTHREAD( sink, clk.pos() ); }
tb.cc
#include "tb.h" void tb::source(){ //reset inp.write( 0 ); rst.write( 1 ); wait(); rst.write( 0 ); wait(); sc_int<16> tmp; //send stimulus to FIR for( int i=0; i<64; i++){ if(i>23 && i<29) tmp = 256; else tmp = 0; inp.write( tmp ); wait(); } } void tb::sink(){ sc_int<16> indata; //Read output coming from DUT for(int i = 0; i<64; i++){ indata = outp.read(); wait(); cout << i << " :\t" << indata.to_int() << endl; } // End simulation sc_stop(); // calls destructors }
Handshaking
- In the folder …
Makefile
file and typemake
golden
folder
- If simulation fails… communication between
tb0
andfir0
failed- Handshake: Add single bit (
bool
) valid(vld
) and ready(rdy
) signals for both input/ouput like ..
// Add below to SC_MODULE( SYSTEM ) sc_signal<bool> inp_sig_vld; sc_signal<bool> inp_sig_rdy; sc_signal<bool> outp_sig_vld; sc_signal<bool> outp_sig_rdy; // Add to signal connection in constructor // Add valid/ready pins to 'tb.h' and 'fir.h'
- Handshake: Add single bit (
- Modeling asynchronous communication at different levels of abstraction using systemC LINK