Skip to content

yoshinrt/scpp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 

Repository files navigation

scpp.pl - SystemC preprocessor

Overview

SystemC is not a hardware-specific language, but it is implemented with C++ and libraries, so there are various writing problems.

The scpp is a preprocessor aimed at improving writing of SystemC. The scpp has following functions.

  • Module connection automation: Signal connections between modules are automatically connected according to rules. This is the same function as AutoConnect in Emacs Verilog mode. It also automatically generates necessary intermediate signals.

  • Suppression of description separation of SC_METHOD(), SC_THREAD(), SC_CTHREAD(): In SystemC, these sentences must be written in SC_CTOR() and usually separated from the function body. In the scpp, these sentences can be written immediately before the function body.

  • Automatic generation of sc_trace(): When dumping signals in SystemC, you need to specify signals manually with hierarchy name. The scpp automates that.

  • Automatic generation of signal name setting by member initializer: When display exact signal names in a waveform dump, you need to specify signal names with string in C++ member initializer. The scpp automates that.

  • Automatic generation of sensitivity list for SC_METHOD(): In Verilog-HDL, sensitivity list can be omitted with always@( * ) sentence. The scpp can do this.

  • Labor saving of writing test bench: Labor saving of stub description creation that instantiates DUT.

The above is achieved by replacing directives ($ScppInstance, etc.) embedded in C++ source code. No other description is modified.

How to try the scpp easily

git clone https://github.com/yoshinrt/scpp.git
cd sample
make SYSTEMC=path_to_systemc_root

Now you can simulation a sample SystemC module. *.cpp /*.h is generated from *.scpp.cpp /*.scpp.h.

You can see what description was automatically generated by comparing the files before and after the scpp changes (ex: diff sample/SimpleDma.scpp.h sample/SimpleDma.h) or by referring to commit e2de8eb.

Command line

scpp.pl [-I<include path>] [-D<name>[=<definition>]] [-o <output file>]
    [-v] [--clean] <input file>
  • -I<include path>: Add <include path> to the end of the include file search path.
  • -D<name>[=<definition>]: Define a macro <name> as <definition>. If <definition> is omitted, name is defined as 1.
  • -o<output file>: Specify the output file name. When omitted, <input file> is renamed to <input file>.bak and then overwritten with <input file>.
  • -v: Outputs automatically recognized port and signal information to <src_file>.list.
  • --clean: Delete description automatically generated by the scpp.
  • <input file>: Specify the input file name.

Directive description

To write directives in C++ source code, use one of the following formats: $ScppSomeDirective specifically describes $ScppInstance etc. See scpp directive list for available directives.

  • 1 line, without Begin - End
// $ScppSomeDirective(<directive argument> ...)
  • 1 line, with Begin - End
// $ScppSomeDirective(<directive argument> ...) Begin
    <C++ code> ...
// $ScppEnd
  • Multiple lines, without Begin - End
/* $ScppSomeDirective(
    <Directive argument> ...
) */
  • Multiple lines, with Begin - End
/* $ScppSomeDirective(
    <Directive argument> ...
) Begin */
    <C++ code> ...
// $ScppEnd

Directives are written as C++ comments.

If there is no directive argument or it is omitted, you can omit ().

If you want to write directive arguments on multiple lines, you must use /* ... */ style comments. It cannot be split into multiple lines with // style comments.

The scpp outputs C++ code automatically generated by directives between Begin ... $ScppEnd. The code written between Begin ... $ScppEnd before the scpp processing are deleted. If Begin ... $ScppEnd is not specified, it is automatically added by the scpp.

Notes on built-in preprocessor

The scpp has a built-in C preprocessor (cpp). When the scpp analyzes C++ source code (for example, module port analysis), the scpp processes source code through the built-in cpp. Therefore, there are the following restrictions.

  • When including systemc.h, use <...> and write #include <systemc.h>. The scpp suppresses expansion of macros such as SC_MODULE by ignoring #include <...>.
  • Similarly, system include headers (stdio.h etc.) should be written with <...>.
  • If the source code is switched by #ifdef etc., it will be processed in the define status when the scpp is executed. If the define state is changed after the scpp processing, it may not match the code automatically generated by the scpp.
  • Due to the limitations described above, it is recommended that parameterization design be performed using a method that does not expand cpp macros, such as C++ templates, instead of defining with #define.

Known issues

Please refer to here for known issues.

A decent C++ parser is not implemented, so the scpp may not work for just a little elaborate C++ source code.

scpp directive list


$ScppAutoMember

Overview

Output necessary signals, pointer variables to modules, and prototype declarations.

Format

$ScppAutoMember

There are no arguments.

Explanation

This directive outputs follwing:

Please refer to each explanation for details.

Example description

// $ScppAutoMember

Output result

// $ScppAutoMember Begin
sc_signal<sc_uint<32> > SrcAddr;
sc_signal<sc_uint<32> > DstAddr;
sc_signal<sc_uint<32> > XferCnt;
sc_signal<bool> Run;
sc_signal<bool> Done;
sc_signal<bool> NceCh[CH_NUM];
sc_signal<sc_uint<32> > RegRDataCh[CH_NUM];
sc_signal<sc_uint<32> > SrcAddrCh[CH_NUM];
sc_signal<sc_uint<32> > DstAddrCh[CH_NUM];
sc_signal<sc_uint<32> > XferCntCh[CH_NUM];
sc_signal<bool> RunCh[CH_NUM];
sc_signal<bool> DoneCh[CH_NUM];
SimpleDmaCore *u_SimpleDmaCore;
SimpleDmaReg *u_SimpleDmaReg[CH_NUM];
void AddrDecorder( void );
void ArbiterSelector( void );
void RDataSelector( void );
// $ScppEnd

$ScppInitializer

Overview

Output a member initializer description that sets a name of a signal.

Format

$ScppInitializer[( <delimiter> )]

  • delimiter: Specify output delimiters as a string.
    • If the string contains :, output : at the beginning of the initialization list. However, nothing is output when there is no initialization list.
    • If the string contains ,, output , at the end of the initialization list. However, nothing is output when there is no initialization list.
    • Both can be specified like ":,".

Explanation

Generate a member initializer description for signal name initialization specified in the constructor.

To dump a signal (sc_trace()) in SystemC and display the signal name correctly in a dump file, you need to specify the signal name in the member initializer. $ScppInitializer automates that.

$ScppInitializer generates member initializer descriptions of all recognized sc_in_clk / sc_in / sc_out / sc_inout / sc_signal.

However, member initializer descriptions for array signals are not generated (this is restriction of C++ language specification).

Example description

SC_CTOR( SimpleDma )
    // $ScppInitializer( ":" )
{

Output result

SC_CTOR( SimpleDma )
    // $ScppInitializer Begin
    : clk( "clk" ),
    nrst( "nrst" ),
    RegAddr( "RegAddr" ),
    RegWData( "RegWData" ),
    RegNce( "RegNce" ),
    RegWrite( "RegWrite" ),
    RegRData( "RegRData" ),
    SramAddr( "SramAddr" ),
    SramWData( "SramWData" ),
    SramNce( "SramNce" ),
    SramWrite( "SramWrite" ),
    SramRData( "SramRData" ),
    SrcAddr( "SrcAddr" ),
    DstAddr( "DstAddr" ),
    XferCnt( "XferCnt" ),
    Run( "Run" ),
    Done( "Done" )
    // $ScppEnd
{

$ScppCthread

Overview

SC_CTHREAD() description can be written just before the function body.

Format

$ScppCthread( <clock>[, <reset>, <attr>])

  • clock: Specify a clock event
  • reset: Specify a reset signal
  • attr: Specify reset attributes as a character string in the following combinations:
    • "a": generates async_reset_signal_is()
    • "s": generates reset_signal_is()
    • "p": Set reset polarity to true
    • "n": Set reset polarity to false

If reset and attr are omitted, async_reset_signal_is() and reset_signal_is() descriptions are not generated.

Explanation

SC_CTHREAD() description can be written just before the function body.

SC_CTHREAD() must be described in SC_CTOR() and is usually separated from the function body describing the behavior, but this has a problem in readability.

By writing $ScppCthread immediately before the function body, SC_CTHREAD() description is generated at the position of $ScppSensitive.

Also, a prototype declaration description of the function is generated at the position of $ScppAutoMember.

Example description

// $ScppCthread( clk.pos(), nrst, "an" )
void SimpleDmaReg::RegWriteThread( void ){

Output result ($ScppSensitive)

// $ScppSensitive( "SimpleDmaReg.cpp" ) Begin
SC_CTHREAD( RegWriteThread, clk.pos() );
async_reset_signal_is( nrst, false );

// $ScppEnd

Output result ($ScppAutoMember)

// $ScppAutoMember Begin
void RegWriteThread( void );
// $ScppEnd

$ScppFunction

Overview

Generate prototype declaration of member function in header file.

Format

$ScppFunction

There are no arguments.

Explanation

By describing $ScppFunction immediately before the function body, a prototype declaration description of the function is generated at the position of $ScppAutoMember.

This directive is not directly related to SystemC, but you can generate a prototype declaration description in the header file in the same way as $ScppMethod or $ScppCthread.

Example description

// $ScppFunction
sc_uint<32> Decoder::GaloaLog( sc_uint<32> idx ){
    ...
}

// $ScppFunction
void Decoder::Compute(
    sc_uint<32>     *DataBuf,
    sc_uint<32>&    DataSize
){
    ...
}

Output result ($ScppAutoMember)

// $ScppAutoMember Begin
sc_uint<32> GaloaLog( sc_uint<32> idx );
void Compute(
    sc_uint<32>     *DataBuf,
    sc_uint<32>&    DataSize
);
// $ScppEnd

$ScppInstance

Overview

Instantiate SystemC module and connect automatically. Necessary intermediate signals are automatically generated.

Format

$ScppInstance( <submodule>, <instance>, <file> [, <regexp> ...])

  • submodule: Specify the submodule name to instantiate.
  • instance: Specify the instance name of the submodule to instantiate.
  • file: Specify the file where the submodule is described. If "." is specified, the file itself in which $ScppInstance is written is specified.
  • regexp: Specify the port ⇔ signal connection rule.

Explanation

Instantiate SystemC module and connect automatically. Necessary intermediate signals are automatically generated.

Currently, the only ports that can be automatically connected are sc_(in|out|inout) sc_(in|out|inout)_clk sc_fifo_(in|out|inout).

Conversion from submodule port to signal name

Submodule ports are converted to module signal names according to the rules specified in regexp. The format of regexp is as follows.

"/port/signal/option"

You can use perl regular expressions for port. If there are multiple regexps, the rule is checked to see if it matches port in order from the first, and the first matching rule is adopted. For ease of description, port is automatically prepended with '^' and ending with '$'.

  • Example: To match both clka and clkb, you need to specify clk.*.

signal specifies the name of the signal connected to the port. If you use grouping (...) on port, you can use backreferences such as $1.

  • Example: "/clk(.*)/CLK$1/": clka port is connected to CLKa signal.

If none of the rules match, the signal with the same name as the port name is connected.

Specify some of the following strings in option. option is optional.

  • I: Force connected signal to sc_in
  • O: Force connected signal to sc_out
  • IO: Force connected signal to sc_inout
  • W: Force connected signal to sc_signal
  • NC: Declares that this output port is a floating port. In fact, a dummy sc_signal is created and connected to it.
  • d: Ports matching this rule are excluded from automatic connection.

port is optional. If omitted, ^(.*)$ is assumed.

signal is optional. If omitted, the signal with the same name as port is connected.

The regular expression delimiter / can be written with other character, and the first character of regexp is recognized as a delimiter. For example, if you write "/foo.*/bar/", it will be recognized as the end of a multi-line comment at the part of */, so it cannot be parsed correctly. In this case, you need to use another character, such as " @foo.*@bar@".

Signal generation

After processing all $ScppInstance in the module where $ScppInstance is described, signals are generated according to the following rules and output to the place of $ScppAutoMember.

  1. If a signal declaration such as sc_in already exists in the module: no signal is generated
  2. If I, O, IO or W opiton is specified: The signal is generated according to that option
  3. When connected to both sc_in and sc_out (for example, connected to sc_in of submodule_a and sc_out of submodule_b): sc_signal is generated
  4. If only connected to sc_in: sc_in is generated
  5. If only connected to sc_out: sc_out is generated

If the submodule port is a multidimensional array, it is connected to a signal of the same dimension and size. In other words, it is connected like u_Inst.port[ n ] -> signal[ n ].

Generate instance array

instance can contain an array index. In this case, an instance array is generated. In the example below, 8 modules of Module are instantiated.

Example: $ScppInstance( Module, u_Inst[8], ... )

In this case, [] can be specified at the end of signal in the connection rule. In this case, an array of signals is generated and connected to u_Inst[ n ].port -> signal[ n ]. Furthermore, if port is a multidimensional array, it is connected as u_Inst[ n ].port[ m ] -> signal[ n ][ m ].

Example description

/* $ScppInstance(
    SimpleDmaReg, u_SimpleDmaReg[ CH_NUM ], "SimpleDmaReg.h",
    "/clk|nrst//",
    "/(Addr|WData|Write)/Reg$1/",
    "/(RData)/Reg$1Ch[]/W",
    "//$1Ch[]/W",
) */

Output result ($ScppInstance)

/* $ScppInstance(
    SimpleDmaReg, u_SimpleDmaReg[ CH_NUM ], "SimpleDmaReg.h",
    "/clk|nrst//",
    "/(Addr|WData|Write)/Reg$1/",
    "/(RData)/Reg$1Ch[]/W",
    "//$1Ch[]/W",
) Begin */
for( int _i_0 = 0; _i_0 < CH_NUM; ++_i_0 ){
    u_SimpleDmaReg[_i_0] = new SimpleDmaReg(( std::string( "u_SimpleDmaReg(" ) + std::to_string(_i_0) + ")" ).c_str());
    u_SimpleDmaReg[_i_0]->clk( clk );
    u_SimpleDmaReg[_i_0]->nrst( nrst );
    u_SimpleDmaReg[_i_0]->Addr( RegAddr );
    u_SimpleDmaReg[_i_0]->WData( RegWData );
    u_SimpleDmaReg[_i_0]->Nce( NceCh[_i_0] );
    u_SimpleDmaReg[_i_0]->Write( RegWrite );
    u_SimpleDmaReg[_i_0]->RData( RegRDataCh[_i_0] );
    u_SimpleDmaReg[_i_0]->SrcAddr( SrcAddrCh[_i_0] );
    u_SimpleDmaReg[_i_0]->DstAddr( DstAddrCh[_i_0] );
    u_SimpleDmaReg[_i_0]->XferCnt( XferCntCh[_i_0] );
    u_SimpleDmaReg[_i_0]->Run( RunCh[_i_0] );
    u_SimpleDmaReg[_i_0]->Done( DoneCh[_i_0] );
}
// $ScppEnd

Output result ($ScppAutoMember)

// $ScppAutoMember Begin
SimpleDmaReg *u_SimpleDmaReg[CH_NUM];
// $ScppEnd

$ScppMethod

Overview

SC_METHOD() description can be written just before the function body.

Format

$ScppMethod[( code )]

  • code: Write C++ code that represents the sensitivity list

Explanation

SC_METHOD() description can be written just before the function body.

SC_METHOD() must be described in SC_CTOR(), and is usually separated from the function body describing the behavior, but this has a problem in readability.

By writing $ScppMethod immediately before the function body, SC_METHOD() description is generated at the position of $ScppSensitive.

Also, a prototype declaration description of the function is generated at the position of $ScppAutoMember.

When the argument is omitted, a sensitivity list is automatically generated using all signals that are .read () in the function.

Example description

// $ScppMethod
void SimpleDma::AddrDecorder( void ){
    // (...omitted...)

/* $ScppMethod(
for( int i = 0; i < CH_NUM; ++i ){
    sensitive << DstAddrCh[i] << RunCh[i] << SrcAddrCh[i] << XferCntCh[i];
}
sensitive << Done;
)*/
void SimpleDma::ArbiterSelector( void ){

Output result ($ScppSensitive)

// $ScppSensitive( "." ) Begin
SC_METHOD( AddrDecorder );
sensitive << RegAddr << RegNce;

SC_METHOD( ArbiterSelector );
for( int i = 0; i < CH_NUM; ++i ){
    sensitive << DstAddrCh[i] << RunCh[i] << SrcAddrCh[i] << XferCntCh[i];
}
sensitive << Done;

// $ScppEnd

Output result ($ScppAutoMember)

// $ScppAutoMember Begin
void AddrDecorder( void );
void ArbiterSelector( void );
// $ScppEnd

$ScppSensitive

Overview

Output the sensitivity list described by $ScppMethod, $ScppThread, and $ScppCthread.

Format

$ScppSensitive( <file> [, <file> ...])

  • file: Specify a file containing $ScppMethod, $ScppThread, $ScppCthread. If "." Is specified for the file name, the file itself with $ScppSensitive is specified.

Explanation

Output the sensitivity list described by $ScppMethod, $ScppThread, and $ScppCthread.

SC_METHOD() SC_TREAD() SC_CTHREAD() must be described in SC_CTOR() and is usually separated from the function body describing the operation, but this has a problem in readability.

By describing $ScppSensitive in SC_CTOR(), Search for $ScppMethod, $ScppThread, $ScppCthread from the specified file, then the sensitivity list description is generated at the position of $ScppSensitive.

Example description

// $ScppSensitive( "." )

Output result

// $ScppSensitive( "." ) Begin
SC_METHOD( AddrDecorder );
sensitive << RegAddr << RegNce;

SC_METHOD( ArbiterSelector );
for( int i = 0; i < CH_NUM; ++i ){
    sensitive << DstAddrCh[i] << RunCh[i] << SrcAddrCh[i] << XferCntCh[i];
}
sensitive << Done;

SC_METHOD( RDataSelector );
for( int i = 0; i < CH_NUM; ++i ) sensitive << RegRDataCh[i];

// $ScppEnd

$ScppThread

Overview

SC_THREAD() description can be written just before the function body.

Format

$ScppThread( code )

  • code: Write C++ code that represents the sensitivity list

Explanation

SC_THREAD() description can be written just before the function body.

SC_THREAD() must be described in SC_CTOR() and is usually separated from the function body describing the behavior, but this has a problem in readability.

By writing $ScppThread immediately before the function body, SC_THREAD() description is generated at the position of $ScppSensitive.

Also, a prototype declaration description of the function is generated at the position of $ScppAutoMember.

Example description

// $ScppThread( sensitive << clk.pos())
void SimpleDma::ArbiterSelector( void ){

Output result ($ScppSensitive)

// $ScppSensitive( "." ) Begin
SC_THREAD( ArbiterSelector );
sensitive << clk.pos();

// $ScppEnd

Output result ($ScppAutoMember)

// $ScppAutoMember Begin
void ArbiterSelector( void );
// $ScppEnd

$ScppSigTrace

Overview

Generate sc_trace() descriptions of the signals in the module.

Format

$ScppSigTrace[( <regexp> ... )]

  • regexp: Specify the rule (regular expression) of signals to be traced or excluded. If the argument is omitted, all signals in the module are traced.

Explanation

Generate sc_trace() descriptions of the signals in the module. However, sc_fifo cannot be traced due to SystemC limitation.

It is assumed that sc_trace_file *ScppTraceFile is declared as a global variable. Also, ScppTraceFile must be opened using sc_create_vcd_trace_file() before this module is instantiated.

Since the generated code is delimited with #ifdef VCD_WAVE, VCD_WAVE macro must be defined in order to output VCD.

Writing rules

The signals to be traced or excluded are determined according to the rules specified in regexp. The format of regexp is as follows.

"/signal/option"

You can use perl regular expressions for signal. If more than one regexp is described, the rule is checked to see if it matches signal, starting with the first rule. For ease of description, signal is automatically prepended with '^' and ending with '$'.

  • Example: To match both clka and clkb, you need to write clk.*.

Signals that do not match all the rules are judged to be traced.

Specify some of the following strings in option. option is optional.

  • S: This rule is applied only to scalar signals.
  • A: This rule is applied only to array type signals.
  • N: Signals that match this rule are excluded from tracing. Signals that match a rule for which this option is not specified will be traced.

signal is optional. If omitted, .* Is assumed.

The regular expression delimiter / can be described with other character, and the first character of regexp is recognized as a delimiter. For example, if you write "/foo.*/", it will be recognized as the end of a multi-line comment in the */ part, so it cannot be parsed correctly. In this case, you need to use another character, such as "@foo.*@".

Example description

sc_main() description

#ifdef VCD_WAVE
sc_trace_file *ScppTraceFile;
#endif

int sc_main( int argc, char **argv ){
    
#ifdef VCD_WAVE
    ScppTraceFile = sc_create_vcd_trace_file( "simple_dma" );
    ScppTraceFile->set_time_unit( 1.0, SC_NS );
#endif

    SimpleDma *u_SimpleDma = new SimpleDma( "u_SimpleDma" );

Each module description

#ifdef VCD_WAVE
extern sc_trace_file *ScppTraceFile;
#endif

SC_MODULE( SimpleDma ){
    // ...(omitted)...
    /* $ScppSigTrace(
        "/DataBuf/N"
    ) */

Output result

// $ScppSigTrace Begin
#ifdef VCD_WAVE
sc_trace( ScppTraceFile, clk, std::string( this->name()) + ".clk" );
sc_trace( ScppTraceFile, nrst, std::string( this->name()) + ".nrst" );
sc_trace( ScppTraceFile, RegAddr, std::string( this->name()) + ".RegAddr" );
sc_trace( ScppTraceFile, RegWData, std::string( this->name()) + ".RegWData" );
sc_trace( ScppTraceFile, RegNce, std::string( this->name()) + ".RegNce" );
sc_trace( ScppTraceFile, RegWrite, std::string( this->name()) + ".RegWrite" );
sc_trace( ScppTraceFile, RegRData, std::string( this->name()) + ".RegRData" );
sc_trace( ScppTraceFile, SramAddr, std::string( this->name()) + ".SramAddr" );
sc_trace( ScppTraceFile, SramWData, std::string( this->name()) + ".SramWData" );
sc_trace( ScppTraceFile, SramNce, std::string( this->name()) + ".SramNce" );
sc_trace( ScppTraceFile, SramWrite, std::string( this->name()) + ".SramWrite" );
sc_trace( ScppTraceFile, SramRData, std::string( this->name()) + ".SramRData" );
sc_trace( ScppTraceFile, SrcAddr, std::string( this->name()) + ".SrcAddr" );
sc_trace( ScppTraceFile, DstAddr, std::string( this->name()) + ".DstAddr" );
sc_trace( ScppTraceFile, XferCnt, std::string( this->name()) + ".XferCnt" );
sc_trace( ScppTraceFile, Run, std::string( this->name()) + ".Run" );
sc_trace( ScppTraceFile, Done, std::string( this->name()) + ".Done" );
#endif // VCD_WAVE
// $ScppEnd

About

SystemC preprocessor

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages