Project

General

Profile

[logo] 
 
Home
News
Activity
About/Contact
Major Tools
  Dinotrace
  Verilator
  Verilog-mode
  Verilog-Perl
Other Tools
  BugVise
  CovVise
  Force-Gate-Sim
  Gspice
  IPC::Locker
  Rsvn
  SVN::S4
  Voneline
  WFH
General Info
  Papers


SystemC::Manual

NAME

SystemC::Manual - SystemPerl primary documentation

SUMMARY

The publicly licensed SystemPerl package provides several extensions to SystemC. It provides sp_preproc, extending the language for AUTOmatic connection of hierarchy like my Verilog-Mode, trace files and netlist linting. Its netlist and parsing utilities are general enough for writing your own tools. Two additional tools provide for speeding up GCC compiles and dependency correction. You can download SystemC from the link off of http://www.veripool.org/systemperl

DESCRIPTION

This package provides several major sub-packages. The SystemC::Parser understands how to read SystemC files, and extract tokens and such, similar to Verilog::Parser.

SystemC::Netlist builds netlists out of SystemC files. This allows easy scripts to determine things such as the hierarchy of SC_MODULEs. The netlist database may also be extended to support other languages.

sp_preproc provides extensions to the SystemC language, called the SystemPerl language. This allows most of the Tedium to be removed from SystemC coding, just as the author's /*AUTO*/ comments did for the Verilog language. See SystemC::SystemPerl after installation.

sp_include shows a technique for speeding up SystemC compiles using GCC. sp_makecheck allows for cleaning up dependency files when dependencies have been removed or changed.

Finally, the src directory contains useful C++ utilities for simulation, such as changing cout to send to both the screen and a file. You may point to this directory underneath the kit, or set SYSTEMPERL_INCLDUE to point to these sources.

Parsing example

    package Trialparser;
    @ISA = qw(SystemC::Parser);
    sub module {
        my $self = shift;
        my $module = shift;
        print $self->filename.":".$self->lineno().": ";
        print "Contains the module declaration for $module\n";
    }
    package main;
    my $sp = Trialparser->new();
    $sp->read ("test.sp");

Netlist example

  use SystemC::Netlist;
    my $nl = new SystemC::Netlist ();
    foreach my $file ('testnetlist.sp') {
        $nl->read_file (filename=>$file,
                        strip_autos=>1);
    }
    $nl->link();
    $nl->autos();
    $nl->lint();
    $nl->exit_if_error();
    foreach my $mod ($nl->modules_sorted) {
        show_hier ($mod, "  ");
    }
    sub show_hier {
        my $mod = shift;
        my $indent = shift;
        print $indent,"Module ",$mod->name,"\n";
        foreach my $cell ($mod->cells_sorted) {
            show_hier ($cell->submod, $indent."  ".$cell->name."  ");
        }
    }

SystemPerl example

    SC_MODULE(mod) {
        /*AUTOSIGNAL*/
        SC_CTOR(mod) {
            SP_CELL (sub, submod);
            /*AUTOINST*/

This expands into:

    SC_MODULE(mod) {
        /*AUTOSIGNAL*/
        // Beginning of SystemPerl automatic signals
        sc_signal<bool>             a;       // For submod
        // End of SystemPerl automatic signals
        SC_CTOR(mod) {
            SP_CELL (sub, submod);
            /*AUTOINST*/
            // Beginning of SystemPerl automatic pins
            SP_PIN (sub, a,       a);
            // End of SystemPerl automatic pins

SUPPORTED SYSTEMS

This version of SystemPerl has been built and tested on:

  * i386-linux

It should run on any system with Perl, a C compiler, bison, and flex.

SystemC must be installed to get the complete function. Currently 1.2.1beta, 2.0.1, 2.1 and 2.2 are the versions supported for tracing, other versions should work without tracing or with minor editing.

INSTALLATION

Download the latest package from http://www.veripool.org/systemperl or CPAN and decompress.

  gunzip SystemPerl_version.tar.gz ; tar xvf SystemPerl_version.tar}

cd to the directory containing this README notice. Some files will permanently live in this directory, so make sure it's in a site wide area.

  cd SystemPerl*

Make sure the SYSTEMC environment variable points to your SystemC installed directory; or that SYSTEMC_INCLUDE points to the include directory with systemc.h in it, and that SYSTEMC_LIBDIR points to the directory with libsystemc.a in it. If using bash, this would consist of the line

    export SYSTEMC=/path/to/systemc
    export SYSTEMC_INCLUDE=/path/to/systemc/include   # with systemc.h
    export SYSTEMC_LIBDIR=/path/to/systemc/lib    # with libsystemc.a

in your ~/.bashrc file.

If different than the above, make sure the SYSTEMC_KIT environment variable points to your original SystemC source code kit.

Type perl Makefile.PL to configure SystemPerl for your system.

You may get a warning message about needing Verilog::Netlist, if so you need to install or upgrade the verilog-perl package from CPAN.

Type make to compile SystemPerl.

If you get a error message ``y.tab.c: error: parse error before goto,'' there is a problem between Bison and GCC. The simplest fix is to edit y.tab.c to comment out ``__attribute__ ((unused))''.

Type make test to check the compilation.

You can see examples under the test_dir directory. The *.sp files are "inline" converted, while the *.h and *.cpp files are expanded from the .sp files.

Type make install to install the programs and any data files and documentation.

Add a SYSTEMPERL environment variable that points to the directory of this kit (the directory you typed ``make install'' inside, not your site-perl directory.) If using bash, this would consist of the line

  export SYSTEMPERL=/path/to/systemperl

in your ~/.bashrc file.

Optionally add a SYSTEMPERL_INCLUDE environment variable if for some reason you wish to relocate the include files (the files under the kit's src directory). For example distributions often want to relocate the SystemPerl includes into the standard system include directory. Note that these files are not installed with make install (as Perl doesn't provide a standard way to install includes), which is why SYSTEMPERL or SYSTEMPERL_INCLUDE must point to them.

DISTRIBUTION

SystemPerl is part of the http://www.veripool.org/ free SystemC software tool suite. The latest version is available from CPAN and from http://www.veripool.org/systemperl.

Copyright 2001-2013 by Wilson Snyder. This package is free software; you can redistribute it and/or modify it under the terms of either the GNU Lesser General Public License Version 3 or the Perl Artistic License Version 2.0.

This code is provided with no warranty of any kind, and is used entirely at your own risk.

AUTHORS

Wilson Snyder <wsnyder@wsnyder.org>.

SEE ALSO

Primary Documentation:

SystemC::Manual (This document)

Language Documentation:

SystemC::SystemPerl

Programs:

sp_includer, sp_makecheck, sp_preproc

Major modules:

SystemC::Netlist, SystemC::Parser

Submodules:

SystemC::Coverage SystemC::Coverage::Item SystemC::Coverage::ItemKey

SystemC::Netlist SystemC::Netlist::AutoCover SystemC::Netlist::AutoTrace SystemC::Netlist::Cell SystemC::Netlist::Class SystemC::Netlist::CoverGroup SystemC::Netlist::CoverPoint SystemC::Netlist::File SystemC::Netlist::Method SystemC::Netlist::Module SystemC::Netlist::Net SystemC::Netlist::Pin SystemC::Netlist::Port

Verilog::Netlist


SystemC::SystemPerl

NAME

SystemC::SystemPerl - SystemPerl Language Extension to SystemC

DESCRIPTION

SystemPerl is a version of the SystemC language. It is designed to expand text so that needless repetition in the language is minimized. By using sp_preproc, SystemPerl files can be expanded into C++ files at compile time, or expanded in place to make them valid stand-alone SystemC files.

The concept of SystemPerl is based upon the AUTOS in the verilog-mode package for Emacs, by the same author.

LANGUAGE

#sp

#sp directives are recognized by SystemPerl to split up files and control preprocessing. Use of any #sp's forces use of SystemPerl preprocessing, and removes full SystemC compatibility.

/*AUTOS*/

AUTOmatics provide a way of expanding interconnections, while potentially retaining fully compatible SystemC code. The preprocessor can edit the source code file directly, resulting in the source code having the expanded automatics.

Code with expanded AUTOs are fully valid SystemC code, and can be sent to anyone who does not even have system perl. Anyone with SystemPerl has the benefit of being able to automatically regenerate them, and saves coding time.

LANGUAGE REQUIREMENTS

SystemPerl requires the following coding conventions. These tokens are not changed in any way, but are simply required for SystemPerl to be able to derive required information from the source code.

SP_AUTO_COVER()

Create a coverage point at the current file and line number. If the statement is executed, the coverage bucket will increment.

SP_AUTO_COVER_CMT (comment)

Create a coverage point at the current file and line number, and note the specified comment. If the statement is executed, the coverage bucket will increment.

SP_AUTO_COVER_CMT_IF (comment,condition)

Create a coverage point at the current file and line number, and note the specified comment. If the statement is executed and the specified condition is true, the coverage bucket will increment.

SP_COVER_INSERT (valuePtr,key,value[,key2,value2...])

Create a coverage point at the current file and line number. Store the key and value pairs as attributes of the coverage bucket; generally these are arbitrary text; use "comment" to set the general comment field.

The coverage variable is passed as a pointer, which the user must increment manually. The pointer will be read by dereference at the end of time, and so must still point to a valid structure at that time.

For example:

    // In the class declaration:
    SpZeroed<uint32_t> m_fcCases[FsmState::MAX][FsmState::MAX]; // [old][new]
    ...
    // In the constructor:
    for (a = 0; a<FsmState::MAX; a++) {
      for (b = 0; b<FsmState::MAX; b++) {
        SP_COVER_INSERT(&m_fcCases[*a][*b],
                    "comment","FsmState Transitions",
                    "OldState",*a,
                    "NewState",*b,
                    "TestplanSectionNumber","1.4.2.1");
    // Wherever you need to increment:
    ++m_fcCases[oldstate][newstate];
SP_COVERGROUP groupname (coverpoints...])

For example:

SC_MODULE(__MODULE__) {

  EnumType var1;
  EnumType var2;
  uint32_t var3;
  uint32_t var4;
  bool e1;
  bool e2;
  ...
  SP_COVERGROUP name (
    option per_instance = 1;                     // this group is covered separately per instance
    description = "a bunch of text that will appear next to the HTML table; what's this covergroup all about?";
    coverpoint      var3[16] = [0:0x1000];       // 16 evenly space bins dividing [0:0x1000]
    coverpoint      var3_alt(var3) {             // alternate name
      bins sizes[] = {0, 1, [2:5], [8:100]};     // 4 bins as specified
      bins few = [3:5];
      bins three = EnumType::THREE;              // can use enums on the RHS
      bins dist_ranges[4] = [200:299];           // 4 bins spread over a range
      bins other = default;                      // named default
      limit_func = var3_limit();
    }
    coverpoint var1 {                            // automatic enum bins
      auto_enum_bins = EnumType;
    };
    coverpoint var4[16] = [0:0x1000] {           // illegal and ignored bins
      option radix = 16;                         // name the bins in hex
      illegal_bins_func = var4_illegal();
      ignore_bins_func = var4_ignore();
      illegal_bins banish_bad_luck = 13;
      ignore_bins  ignore_zero = 0;
    };
    cross myCross {                              // 3-dimensional cross; up to 8 are permitted
      rows = {var1,var4};
      cols = {var3_alt};
      page = "mypage";                           // put this table on a separate page
      description = "this text goes above the table";
    };
    window myWin(e1,e2,4);                       // 9 bins +/- e1 occuring 4 samples before/after e2
  );
  ...
  bool var4_ignore(uint64_t var4) { ... }  // return true if value should be ignored
  bool var4_illegal(uint64_t var4) { ... } // return true if test should assert
  uint32_t var3_limit(uint64_t var3_alt) { ... } // return CovVise limit for "high"; default is 10
  ...
  /*AUTOMETHODS*/
}

SP_CTOR_IMP(__MODULE__) /*AUTOINIT*/ { SP_AUTO_CTOR; ... }

void __MODULE__::foo() { ... SP_COVER_SAMPLE(name); ... }

SP_CLASS (class)

SP_CLASS declares a SC_MODULE like structure, most likely to accomplish special inheritance. If a new base class is derived from sc_module, you must use SP_CLASS when the derived class is used, so that SystemPerl knows the final class is a child of a sc_module. For example:

    class FooBase : public sc_module {...};
  Then to use FooBase, instead of
    class Foo : public FooBase {...};
  use
    SP_CLASS(Foo) : public FooBase {...};
SP_CELL_DECL (refname, instname[s])

SP_CELL_DECL declares the cell structures. It is only needed to declare those cells that AUTOCELLS will not create itself; currently this is any cells which are arrayed.

SP_CELL (instname, refname)

SP_CELL instantiates the given module named refname as a instantiation called instname. The instname is also passed as a parameter to refname as a string. Note if you are doing an array, you probably want SP_CELL_FORM.

SP_CELL_FORM (instname, refname, form, ...)

SP_CELL_FORM instantiates the given module named refname as a instantiation. The instantiation is named by using a sprintf of the given format and arguments. Generally this is used for arrays: i.e.:

    SP_CELL_FORM(slice[i], smm_pi_slice, "slice[%d]", i);
SC_MODULE (modulename)

Though a standard optional SystemC construct, SystemC requires use of the SC_MODULE macro when defining a module class. For example "struct mod1 : public sc_module" must instead be coded as "SC_MODULE(mod1)".

SP_MODULE_CONTINUED (modulename)

SP_MODULE_CONTINUED allows a new source file to continue declaration of functions that were declared in a different .sp file. This will not put any code into the output, it is only required for proper parsing of Verilated files.

SP_PIN (instname, portname, netname)

SP_PIN declares a connection of a instantiation's port to the specified net.

SP_TEMPLATE ("<instregexp>", "<portregexp>", "<netregexp>", ["<typeregexp>"]);

SP_TEMPLATE defines a rule for connecting pins with AUTOINST that can apply to a regular expression of instance names and port names. This is useful for connecting signals to arrayed instances, or other mass-renaming tasks.

The first parameter is a unquoted instance name, or a double-quoted Perl regular expression which must match against the instance name for the template to apply. Regular expressions must conform to those described in pcrepattern, and are anchored to the instance name - in that SystemPerl implies a leading "^" and trailing "$" in the regular expression, and the ^/$ must not be included by the user. Be careful with brackets, as they specify regular expression character classes; if a literal bracket is wanted such as for matching array references, quote it with a backslash.

The second parameter is a regular expression which must match against the port name for the template to apply. As with the instance regexp, it is anchored for you.

The third parameter is the net to connect when the template matches. The regular expression may contain $1, $2 etc, which correspond to the parenthesis in both the instance and port regular expressions concatenated.

The optional fourth parameter is a regular expression which must match the type of the cell's pin. For example "sc_out" would make a template that matches only outputs.

For example,

    SP_TEMPLATE("sub(\d+)", "arrayed_(.*)", "$2_array$1");
    Where there is a sub1 instance with a pin "arrayed_foo".

When a instance name matches /^sub\d+$/ which "sub1" does, and a port name matches /^arrayed_.*$/, which "arrayed_foo" does, then the pin will be named based on the port name and sub number, in this case $2 has "foo" and $1 has "1", so the resulting connection will be to "foo_array1".

SP_TRACED

SP_TRACED is used as an attribute like static or const. It indicates the simple variable inside a SC_MODULE or another class should be added to any waves files that AUTOTRACE creates.

SP_TRACED may also be used on the member variables inside standard classes. This allows the class to be traced if it is used as the type of a sc_in/sc_out/sc_signal. For a sample, see MySigStruct in the examples.

EXPANSIONS

SystemPerl expands the following special tokens.

__MODULE__

__MODULE__ is predefined to the name of the module, from the basename of the filename. This allows files to be more easily replicated, and to avoid obscure errors when the filename does not match the module name.

For example:

    SC_MODULE (__MODULE__) {
      ...
/*AUTOATTR("attribute")*/

Sets a internal attribute. There are no attributes currently specified for general usage.

/*AUTOCTOR*/

AUTOCTOR creates the pin name initializers required in the SC_MODULE's constructor. For example:

    sc_in<bool> in_signal;
    ...
    SP_CTOR_IMP(__MODULE__) /*AUTOCTOR*/ {

Becomes:

    SP_CTOR_IMP(__MODULE__) : in_signal("in_signal") {
/*AUTOINIT*/

AUTOINIT creates signal and port name initializers for SC_CTOR's. /*AUTOCTOR*/ is a backward compatible alias, but was depreciated due to the similarity in name with SP_AUTO_CTOR.

For example:

    SC_MODULE(submod) {
        sc_in_clk   clk;
        ...
    SC_MODULE(mod) {
        SC_CTOR(mod) /*AUTOINIT*/ {

Becomes:

    SC_MODULE(mod) {
        SC_CTOR(mod)
              // Beginning of SystemPerl automatic initializer
              : clk("clk")
              // End of SystemPerl automatic initializer
/*AUTOENUM_CLASS|GLOBAL(enum)*/

AUTOENUM is used to take an existing enumeration and make it into a class with functions to return the ASCII name of the enumeration, and to create an iterator to loop over all enum values. This makes it easy to print the value of the enumeration in text form.

For example:

    class MyENumClass {
    public:
        enum en {
        IDLE = 0,
        ONE, TWO
        };
        /*AUTOENUM_CLASS(MyENumClass.en)*/
    };
    /*AUTOENUM_GLOBAL(MyENumClass.en)*/

Becomes:

    class MyENumClass {
    public:
        enum en {
            IDLE = 0,
            ONE, TWO
        };
        /*AUTOENUM_CLASS(MyENumClass.en)*/
        // Beginning of SystemPerl automatic enumeration
        enum en e_en;
        inline MyENumClass () {};
        inline MyENumClass (en _e) : e_en(_e) {};
        explicit inline MyENumClass (int _e) : e_en(static_cast<en>(_e)) {};
        operator const char * (void) const { return ascii(); };
        operator en (void) const { return e_en; };
        const char *ascii (void) const {
           switch (e_en) {
           case IDLE: return "IDLE";
           case ONE: return "ONE";
           case TWO: return "TWO";
           default: return "%E:BadVal:MyENumClass";
           };
        };
        class iterator; ...
        // End of SystemPerl automatic enumeration
    };
    /*AUTOENUM_GLOBAL(MyENumClass.en)*/
    // Beginning of SystemPerl automatic enumeration
    inline bool operator== (MyENumClass lhs, MyENumClass rhs) { return (lhs.e_en == rhs.e_en); }
    //... other accessors
    // End of SystemPerl automatic enumeration
/*AUTOINOUT_MODULE(mod[,signal[,direction-or-type]])*/

AUTOINOUT_MODULE indicates the input/output list should be copied from the specified other module. Optionally only signal names matching the specified regular expression, and with direction and data type matching the third parameter are included.

This is useful for creating null modules which need identical pinouts to the module which they are nulling out. AUTOSIGNAL must be used along with this auto, as the ports are inserted at the point where the AUTOSIGNAL is.

/*AUTOIMPLEMENTATION*/

AUTOIMPLEMENTATION includes function definitions required inside the .cpp file, such as functions implementing ENUM ascii functions. AUTOIMPLEMENTATION will be included automatically at the end of a expanded .cpp file if it is not otherwise found in the file.

/*AUTOINST*/

AUTOINST connects any unreferenced ports for the current SP_CELL to signals named the same as the port name.

For example:

    SC_MODULE(submod) {
        sc_in_clk   clk;
        ...
    SC_MODULE(mod) {
        SC_CTOR(mod) {
            SP_CELL (sub, submod);
              /*AUTOINST*/

Becomes:

    SC_MODULE(mod) {
        SC_CTOR(mod) {
            SP_CELL (sub, submod);
              // Beginning of SystemPerl automatic instantiation pins
              SP_PIN (sub, clk,     clk);
              // End of SystemPerl automatic instantiation pins
/*AUTOINTERFACE*/

AUTOINTERFACE includes function definitions required inside the .h file. Nothing needs to be inserted there yet, so this AUTO does not need to be used.

/*AUTOMETHODS*/

AUTOMETHODS indicates where interface declarations should be inserted. It also declares a SC_CTOR(__MODULE__) method if there is not already one in the class header. Additional methods that are inserted are is described under AUTOTRACE.

/*AUTOSUBCELL_CLASS*/

AUTOSUBCELL_CLASS creates forward class declarations for the submodules instantiated in SP_CELL declarations.

For example:

    /*AUTOSUBCELL_CLASS*/
    SC_MODULE(mod) {
        SC_CTOR(mod) {
            SP_CELL (sub, submod);

Becomes:

    /*AUTOSUBCELL_CLASS*/
    // Beginning of SystemPerl automatic subcell includes
    class submod;
    // End of SystemPerl automatic subcell includes
    ...
/*AUTOSUBCELL_DECL*/

AUTOSUBCELL_DECL declares the submodules instantiated in SP_CELL declarations.

For example:

    SC_MODULE(mod) {
        /*AUTOSUBCELL_DECL*/
        SC_CTOR(mod) {
            SP_CELL (sub, submod);
              SP_PIN (sub, a,       a);

Becomes:

    SC_MODULE(mod) {
        /*AUTOSUBCELL_DECL*/
        // Beginning of SystemPerl automatic subcells
        submod            *sub;
        // End of SystemPerl automatic subcells
        SC_CTOR(mod) {
            SP_CELL (sub, submod);
              SP_PIN (sub, a,       a);
/*AUTOSUBCELL_INCLUDE*/

AUTOSUBCELL_INCLUDE creates includes for the submodules instantiated in SP_CELL declarations.

For example:

    /*AUTOSUBCELL_INCLUDE*/
    SC_MODULE(mod) {
        SC_CTOR(mod) {
            SP_CELL (sub, submod);

Becomes:

    /*AUTOSUBCELL_INCLUDE*/
    // Beginning of SystemPerl automatic subcell includes
    #include "submod.h"
    // End of SystemPerl automatic subcell includes
    ...
/*AUTOSIGNAL*/

AUTOSIGNAL declares any signals used in SP_PIN connections that are not declared elsewhere.

For example:

    SC_MODULE(mod) {
        /*AUTOSIGNAL*/
        SC_CTOR(mod) {
            SP_CELL (sub, submod);
              SP_PIN (sub, a,       a);

Becomes:

    SC_MODULE(mod) {
        /*AUTOSIGNAL*/
        // Beginning of SystemPerl automatic signals
        sc_signal<bool>             a;       // For submod
        // End of SystemPerl automatic signals
        SC_CTOR(mod) {
            SP_CELL (sub, submod);
              SP_PIN (sub, a,       a);
/*AUTOTIEOFF*/

AUTOTIEOFF creates zeroing assignments for all outputs of the module. Normally this is used in a method called at reset time.

For example:

    /*AUTOTIEOFF*/

Becomes:

    /*AUTOTIEOFF*/
    // Beginning of SystemPerl automatic tieoffs
    a.write(0);
    // End of SystemPerl automatic tieoffs
/*AUTOTRACE(module,recurse)*/

AUTOTRACE creates a routine to trace the ports and signals in the current module, and then call the tracing routine on all submodules. AUTOMETHODS is also required in the declaration of the module. A optional second argument of "recurse" indicates the trace code should include all submodules, leading to smaller trace files, but longer compile and link times.

AUTOTRACE will not trace signals beginning with a underscore. It also replaces __DOT__ in signal names to "." to support fake hierarchy, such as is created with Verilator.

Note because of a SystemC limitation, output ports cannot be traced. Also the hierarchy is not properly placed into the trace file; the hierarchy path will be added to the signal name itself. Also, arrayed nets/cells aren't quite right yet.

Example:

    SC_MODULE (ExMod) {
        ...
        sc_in<bool>         in;
        SP_CELL_DECL(ExModSub,          suba);
        /*AUTOMETHODS*/
        // Beginning of SystemPerl automatic declarations
        void trace (sc_trace_file *tf, const sc_string& prefix, int levels, int options=0);
        // End of SystemPerl automatic declarations
    }
    ...
    /*AUTOTRACE(ExMod)*/
    // Beginning of SystemPerl automatic trace file routine
    void ExMod::trace (sc_trace_file *tf, const sc_string& prefix, int levels, int options=0) {
        sc_trace(tf, this->in.read(),           prefix+"in", 1);
        if (levels > 0) {
            this->suba->trace (tf, prefix+"suba.", levels-1, options);
        }
    }
    // End of SystemPerl automatic trace file routine
SP_AUTO_CTOR

SP_AUTO_CTOR should be called first in every constructor. It will call internal functions needed for coverage analysis, and other possible future enhancements.

SP_AUTO_METHOD

SP_AUTO_METHOD is placed in a SC_METHOD function. It takes a method name and a sensitivity argument, and declares the specified method under the AUTOMETHODS auto, and declares the SC_METHOD and sensitivity under the SP_AUTO_CTOR area.

For example the below

    void __MODULE__::clockMethod() {
        SP_AUTO_METHOD(clockMethod, clk.pos());

This will automatically insert the following: in the class declaration under AUTOMETHODS: private: void clockMethod();

   in the constructor under SP_AUTO_CTOR:
        SC_METHOD(clockMethod);
        sensitive << clk.pos();
SP_CTOR_IMP

SP_CTOR_IMP provides the implementation function corresponding to SC_CTOR. This allows the header to use "SC_CTOR;" and the body of the constructor to be moved into the implementation file. This greatly decreases the number of includes needed in the header file, and thus speeds compilation.

sp_ui

Sp_ui acts a magic SystemC signal type. Sp_ui takes two template-like arguments, representing the MSB and LSB of the data. SystemPerl will replace the sp_ui with a bool, uint32_t, uint64_t or sc_bv depending on the width of the required type, using MSB alone. This matches the rules that Verilator requires for created shells. Thus a Verilog definition

     input [35:3]  signal;

is equivalent to the SystemPerl declaration:

     sc_in<sp_ui<35,3> >   signal;

Sp_ui's can interconnect with the standard uint types. Only the bits up to the MSB will be traced in waves files, while the LSB is only for Verilog interconnecting; it's presumed the lower bits are still stored and zero inside SystemC. Note it is the programmers job to insure that bits above the MSB and below the LSB are zero, this is not done for you.

#sp else

Inverts the last #sp ifdef/ifndef.

#sp endif

Ends a #sp ifdef/ifndef.

#sp ifdef define

Turns off processing until the next #sp endif, if the specified define is not defined. Allows SystemPerl preprocessing similar to C++ #ifdef.

#sp ifndef define

Turns off processing until the next #sp endif, if the specified define is defined. Allows SystemPerl preprocessing similar to C++ #ifdef.

#sp include

Includes the file for the preprocessor, it will be expanded into the output .h/.cpp files. Note that regular "#include" files are not read or expanded to save disk space and time.

#sp interface

Interface specifies the following code should be moved into a the header file. This allows a common .sp file to contain both .h and .cpp information. SystemPerl automatically adds include guards in the header, to protect against multiple inclusion.

#sp implementation

Specifies the following code should be part of the cpp file. This allows a common .sp file to contain both .h and .cpp information.

#sp slow

Specifies the following code should be part of a slow cpp file, compiled with minimal optimization because the functions contained within are only executed once. This allows a common .sp file to contain both .h, fast .cpp, and slow .cpp information.

#sp use

This specifies a file that must be #included at compile time. In addition the file is a .sp file, which must be preprocessed by sp_preproc, and added to the link list. Thus using #sp use automatically finds the objects required for linking against the includer. (Just like Perl's package and use statements.)

In addition to specifying an exact string like with #include, you may also specify a define symbol or a cell search path. The value of a definition will be substituted to find the file to be included. If a name of the form .cell.subcell is used, it will include both the header for the module for "cell", and for "subcell" underneath "cell".

SEE ALSO

SystemC::Manual


sp_includer

NAME

sp_includer - Form include statements

SYNOPSIS

  sp_includer <file1.cpp>...

DESCRIPTION

sp_includer simply takes all of the arguments on the command line and prints #include statements for each argument.

This allows multiple files to be compiled in one pass; rather than using

    gcc file1.cpp
    gcc file2.cpp
    gcc file3.cpp

or the equivalently slow

    gcc file1.cpp file2.cpp file3.cpp

this program allows

    sp_includer file1.cpp file2.cpp file3.cpp > file_CONCAT.cpp
    gcc file_CONCAT.cpp

where any headers all files require will be read only once. With the SystemC headers, this saves ~5 seconds per file, many minutes across an entire large project.

ARGUMENTS

--help

Displays this message and program version and exits.

--version

Displays program version and exits.

SEE ALSO

SystemC::Manual


sp_makecheck

NAME

sp_makecheck - Read dependency files and check for missing dependencies

SYNOPSIS

  sp_makecheck *.d

DESCRIPTION

A common technique with make is to use GCC to generate .d dependency files using the -MMD switch. This creates a files similar to foo.d:

    foo.o foo.d: foo.cc foo.h

The problem is if a header file is removed, then make will complain that there is no rule to build foo.h. Adding a fake target is one way around this, but that requires additional .d's, and leaves old objects around.

sp_makecheck reads the specified dependency files, and checks for the existence of all dependencies in the file. If a file does not exist, it simply removes all of the targets.

ARGUMENTS

--help

Displays this message and program version and exits.

--mtime

Consider the modification time, removing any out of date files.

--show

Show each target and the tree of required dependencies.

--version

Displays program version and exits.

SEE ALSO

SystemC::Manual

make


sp_preproc

NAME

sp_preproc - SystemPerl Preprocessor

SYNOPSIS

  sp_preproc <file.sp>

DESCRIPTION

sp_preproc takes a .sp (systemperl) file and creates the SystemC header and C files.

It is generally only executed from the standard build scripts.

ARGUMENTS

--help

Displays this message and program version and exits.

--hier-only

Read only hierarchy information, ignore all signal information. Useful for faster generation of sp_lib files.

--inline

Edit the existing source code "inline". Similar to the Verilog-mode AUTOs. Use --inline --noautos to remove the expanded automatics.

--libfile filename

Filename to write a list of sp_cells into, for later use as a --libcell to another sp_preproc run.

--libfile-libcells

Include library cells in the --libfile report.

--libcell

Files listed before --libcell will be preprocessed or inlined as appropriate. Files after noexpand will only be used for resolving references, they will not be linked, linted, or otherwise checked. --nolibcell can be used to re-enable checking of subsequent files.

--M filename
--MMD filename

Write dependencies in Makefile format to the specified filename.

--Mdir dirname

Write preprocessor outputs to the specified directory instead of the current directory.

--ncsc

Create output files compatible with Cadence NC-SystemC.

--nolint

Disable lint style error checks, such as required to run doxygen on the SystemPerl output.

--preproc

Preprocess the code, writing to separate header and cpp files.

--trace-duplicates

Include code to trace submodule signals connected directly to a parent signal, generally for debugging interconnect. Without this switch such signals will be presumed to have the value of their parent module's signal, speeding and compressing traces.

--tree filename

Write a report showing the design hierarchy tree to the specified filename. This format may change, it should not be parsed by tools.

--noautos

With --inline, remove any expanded automatics.

--verbose

Shows which files are being written, or are the same.

--version

Displays program version and exits.

--write-verilog filename

Write the SystemC interconnections in Verilog format to the specified filename. Note this does not include logic, it only contains module ports and cells.

-M filename

Writes the dependency listing (similar to cpp -M) to the specified filename.

-Dvar=value

Sets a define to the given value (similar to cpp -D).

-f file

Parse parameters from the given file.

LANGUAGE

See SystemC::SystemPerl for the language specification.

SEE ALSO

SystemC::Manual SystemC::SystemPerl


vcoverage

NAME

vcoverage - Verilog/SystemC coverage analyzer

SYNOPSIS

 Create report:
    vcoverage -f input.vc <datafile>
 Merge reports
    vcoverage --noreport -write <merged.dat>  <datafiles>

DESCRIPTION

Vcoverage reads the specified data file and generates annotated source code with coverage metrics annotated. By default logs/coverage.pl is read. If multiple coverage points exist on the same line, additional lines will be inserted to report the additional points.

Additional Verilog-standard arguments specify the search paths necessary to find the source code that the coverage analysis was performed on.

To get correct coverage percentages, you may wish to read logs/coverage.pl into Emacs and do a M-x keep-lines to include only those statistics of interest.

For Verilog conditions that should never occur, you should add a $stop statement. This will remove the coverage during the next build.

ARGUMENTS

--all-files

Specifies all files should be shown. By default, only those source files which have low coverage are written to the output directory.

--help

Displays this message and program version and exits.

--min count

Specifies the minimum occurrence count that should be flagged if the coverage point does not include a specified threshold. Defaults to 10.

--noreport

Don't produce output files. Used with --write to merge files.

--o output_directory

Sprcifies the directory name that source files with annotated coverage data should be written to.

--unlink

When using --write to combine coverage data, unlink all input files after the output has been created.

--version

Displays program version and exits.

--write filename

Specifies the aggregate coverage results, summed across all the files, should be written to the given filename. This is useful in scripts to combine many sequential runs into one master coverage file.

VERILOG ARGUMENTS

The following arguments are compatible with GCC, VCS and most Verilog programs.

+libext+ext+ext...

Defines the extensions for Verilog files.

+define+var+value =item -Dvar=value

Defines the given variable.

+incdir+dir =item -Idir

Specifies a directory for finding include files.

-f file

Specifies a file containing additional command line arguments.

-y dir

Specifies a module search directory.

SEE ALSO

SystemC::Manual, Verilog::Getopt, SystemC::Coverage


SystemC::Coverage

NAME

SystemC::Coverage - Coverage analysis utilities

SYNOPSIS

  use SystemC::Coverage;
  $Coverage = new SystemC::Coverage;
  $Coverage->read (filename=>'cov1');
  $Coverage->read (filename=>'cov2');
  $Coverage->write (filename=>'cov_together');

DESCRIPTION

SystemC::Coverage provides utilities for reading and writing coverage data, usually produced by the SP_COVER_INSERT or SP_AUTO_COVER function of the SystemPerl package.

The coverage data is stored in a global hash called %Coverage, thus subsequent reads will increment the same global structure.

METHODS

clear

Clear the coverage variables

delete_item

Delete specified coverage item.

inc (args..., count=>value)

Increment the coverage statistics, entering keys for every value. The last value is the increment amount. See SystemC::Coverage::Item for the list of standard named parameters.

items

Return all coverage items, as a list of SystemC::Coverage::Item objects.

items_sorted

Return all coverage items in sorted order, as a list of SystemC::Coverage::Item objects.

new ([filename=>filename])

Make a new empty coverage container.

read ([filename=>filename])

Read the coverage data from the file, with error checking.

write ([filename=>filename])

Write the coverage variables to the file in a form where they can be read back by simply evaluating the file.

SEE ALSO

SystemC::Manual

vcoverage, SystemC::Coverage::Item


SystemC::Coverage::Item

NAME

SystemC::Coverage::Item - Coverage analysis item

SYNOPSIS

  use SystemC::Coverage;
  $Coverage = new SystemC::Coverage;
  foreach my $item ($Coverage->items()) {
      print $item->count;
  }

DESCRIPTION

SystemC::Coverage::Item provides data on a single coverage point.

METHODS

count_inc (inc)

Increment the item's count by the specified value.

hash

Return a reference to a hash of key/value pairs.

key

Return a key suitable for sorting.

ACCESSORS

col[0-9]

The (enumeration) value name for this column in a table cross.

col[0-9]_name

The column title for the header line of this column.

column

Column number for the item. Used to disambiguate multiple coverage points on the same line number.

comment

Textual description for the item.

count

The numerical count for this point.

filename

Filename of the item.

groupdesc

Description of the covergroup this item belongs to.

groupname

Group name of the covergroup this item belongs to.

hier

Hierarchy path name for the item.

lineno

Line number for the item.

per_instance

True if every hierarchy is independently counted; otherwise all hierarchies will be combined into a single count.

row[0-9]

The (enumeration) value name for this row in a table cross.

row[0-9]_name

The row title for the header line of this row.

table

The name of the table for automatically generated tables.

type

Type of coverage (block, line, fsm, etc.)

SEE ALSO

SystemC::Manual

SystemC::Coverage


SystemC::Coverage::ItemKey

NAME

SystemC::Coverage::ItemKey - Coverage analysis item key values

SYNOPSIS

  use SystemC::Coverage::ItemKey;
  # $SystemC::Coverage::Item::CompressKey{...}
  # $SystemC::Coverage::Item::DecompressKey{...}

DESCRIPTION

SystemC::Coverage::ItemKey provides details on each datum key that is attached to each coverage item. This is a low level class used by SystemC::Coverage::Item; direct usage is unlikely to be desirable.

METHODS

SEE ALSO

SystemC::Manual

SystemC::Coverage::Item


SystemC::Netlist

NAME

SystemC::Netlist - SystemC Netlist

SYNOPSIS

  use SystemC::Netlist;
  # See Verilog::Netlist for base functions
    $nl->autos();
    $nl->exit_if_error();

DESCRIPTION

SystemC::Netlist contains interconnect information about a whole design database. The classes of SystemC::Netlist parallel those of Verilog::Netlist, which should be seen for all documentation.

The database is composed of files, which contain the text read from each file.

A file may contain modules, which are individual blocks that can be instantiated (designs, in Synopsys terminology.)

Modules have ports, which are the interconnection between nets in that module and the outside world. Modules also have nets, (aka signals), which interconnect the logic inside that module.

Modules can also instantiate other modules. The instantiation of a module is a Cell. Cells have pins that interconnect the referenced module's pin to a net in the module doing the instantiation.

Each of these types, files, modules, ports, nets, cells and pins have a class. For example SystemC::Netlist::Cell has the list of SystemC::Netlist::Pin (s) that interconnect that cell.

FUNCTIONS

See Verilog::Netlist for all common functions.

$netlist->autos

Updates /*AUTO*/ comments in the internal database. Normally called before lint.

$netlist->sc_version

Return the version number of SystemC.

SEE ALSO

SystemC::Manual

SystemC::Netlist::Cell, SystemC::Netlist::Class, SystemC::Netlist::CoverGroup, SystemC::Netlist::File, SystemC::Netlist::Module, SystemC::Netlist::Net, SystemC::Netlist::Pin, SystemC::Netlist::Port, Verilog::Netlist::Subclass


SystemC::Netlist::AutoCover

NAME

SystemC::Netlist::AutoCover - Coverage analysis routines

DESCRIPTION

SystemC::Netlist::AutoCover creates the SP_AUTO_COVERAGE features. It is called from SystemC::Netlist::Module.

SEE ALSO

SystemC::Netlist::Module


SystemC::Netlist::AutoTrace

NAME

SystemC::Netlist::AutoTrace - Tracing routines

DESCRIPTION

SystemC::Netlist::AutoTrace creates the /*AUTOTRACE*/ features. It is called from SystemC::Netlist::Module.

SEE ALSO

SystemC::Netlist::Module


SystemC::Netlist::Cell

NAME

SystemC::Netlist::Cell - Cell for a SystemC Module

DESCRIPTION

This is a superclass of Verilog::Netlist::Cell, derived for a SystemC netlist pin.

SEE ALSO

Verilog::Netlist::Cell SystemC::Netlist Verilog::Netlist


SystemC::Netlist::Class

NAME

SystemC::Netlist::Class - Class (type) information

DESCRIPTION

SystemC::Netlist::Class contains type information. It is called from SystemC::Netlist.

SEE ALSO

SystemC::Netlist


SystemC::Netlist::CoverGroup

NAME

SystemC::Netlist::CoverGroup - Coverage group routines

DESCRIPTION

SystemC::Netlist::CoverGroup creates the SP_COVERGROUP features. It is called from SystemC::Netlist::Module.

SEE ALSO

SystemC::Netlist::Module # SystemC - SystemC Perl Interface # See copyright, etc in below POD section. ######################################################################

package SystemC::Netlist::CoverPoint; use Class::Struct; use Config; use Carp;

use Verilog::Netlist; use Verilog::Netlist::Subclass; @ISA = qw(SystemC::Netlist::CoverPoint::Struct Verilog::Netlist::Subclass); $VERSION = '1.341'; use strict;

# allow 64-bit values without bonking no warnings 'portable';

# The largest value for which we will use the faster lookup table # to compute bin number (as opposed to if statements) use constant MAX_BIN_LOOKUP_SIZE => 256;

# longest allowed user-defined string #use constant MAX_USER_STRING_LEN => 256; use constant MAX_USER_STRING_LEN => 5000;

# CovVise limit default use constant DEFAULT_LIMIT => 10;

struct('Bin'
=>[name => '$', #'# name of bin
ranges => '@', #'# ranges
values => '@', #'# individual values
isIllegal => '$', #'# is it an illegal bin (assert)
isIgnore => '$', #'# is it an ignore bin (no need to cover)
]);

structs('new',
'SystemC::Netlist::CoverPoint::Struct'
=>[name => '$', #'# coverpoint name
connection => '$', #'# class member to which we connect
description => '$', #' # description of the point
page => '$', #' # HTML page name; default group's page
defaultName => '$', #'# Name of default bin
defaultIsIllegal => '$', #'# Is the default bin illegal?
defaultIsIgnore => '$', #'# Is the default bin ignore?
type => '$', #'# type of coverpoint
num_bins => '$', #'# number of (non-default) bins
max_bins => '$', #'# maximum number of bins (don't blow up memory by mistake)
weight => '$', #'# statistical weight for CovVise
bins => '@', #'# list of bin data structures
maxValue => '$', #'# max specified value
minValue => '$', #'# min specified value
enum => '$', #'# if an enum, what's the enum name?
limitFunc => '$', #'# if present, the function name to compute limits (0 means "waive this")
ignoreFunc => '$', #'# if present, the function name to compute ignores
illegalFunc => '$', #'# if present, the function name to compute illegals
isCross => '$', #'# is this point a cross?
isWindow => '$', #'# is this point a timing window?
event1 => '$', #'# if a timing window, the first event
event2 => '$', #'# if a timing window, the second event
windowDepth => '$', #'# if a timing window, the depth (+/-)
crossMember => '$', #'# is this point a member of another cross?
radix => '$', #'# for standard bins, with what radix to number them
rows => '@', #'# (cross) list of rows
cols => '@', #'# (cross) list of columns
tables => '@', #'# (cross) list of tables
#
attributes=> '%', #'# Misc attributes for systemperl
#
module=> '$', #'# Module containing statement
filename => '$', #'# Filename this came from
lineno=> '$', #'# Linenumber this came from
]);

###################################################################### #### Accessors

sub logger { return $_[0]->module->logger; }

###################################################################### #### Module additions

package SystemC::Netlist::Module;

sub close_new_coverpoint { my $self = shift;

    # add to group
    my $currentCovergroup = $self->current_covergroup();
    $currentCovergroup->add_point($self->attributes("_openCoverpoint"));
    # allow next call to make a new one
    $self->attributes("_openCoverpoint",undef);
}

sub current_coverpoint { my $self = shift;

    if (!defined $self->attributes("_openCoverpoint")) {
        # Create a new coverage point
        my $coverpointref = new SystemC::Netlist::CoverPoint
            (module   => $self,
             lineno   => $self->lineno,
             filename => $self->filename,
             num_bins => 0,
             max_bins => 1024,
             name     => "",
             description => "",
             defaultName => "",
             defaultIsIllegal=> 0,
             defaultIsIgnore=> 1, # by default, we ignore 'default' and don't insert a bin
             maxValue => 0,
             minValue => 0,
             crossMember => 0,
             isWindow => 0,
             radix => 10,
             weight => 1.0,
             );
        $self->attributes("_openCoverpoint",$coverpointref);
    }
    return $self->attributes("_openCoverpoint");
}

###################################################################### #### Automatics (Preprocessing) package SystemC::Netlist::CoverPoint;

sub current_bin { my $self = shift;

    if (!defined $self->attributes("_openBin")) {
        $self->attributes("_openBin",
                          Bin->new(isIllegal => 0,
                                   isIgnore => 0,
                                   )
                          );
    }
    return $self->attributes("_openBin");
}

sub coverpoint_sample_text { my $self = shift; my $groupname = shift;

    my $pointname = $self->name;
    my $out;
    if ($self->isWindow) {
        # update history arrays; increment bins if there's a match
        $out .= "{ /* point name = $pointname - a timing window */\n";
        $out .= "  /* step 1 - shift event history down the pipe */\n";
        $out .= "  for(int i=0;i<=".$self->windowDepth.";i++) {\n";
        $out .= "    _sp_cg_".$groupname."_".$pointname."_ev1_history[i] = _sp_cg_".$groupname."_".$pointname."_ev1_history[i+1];\n";
        $out .= "    _sp_cg_".$groupname."_".$pointname."_ev2_history[i] = _sp_cg_".$groupname."_".$pointname."_ev2_history[i+1];\n";
        $out .= "  }\n";
        $out .= "  _sp_cg_".$groupname."_".$pointname."_ev1_history[".$self->windowDepth."+1] = ".$self->event1.";\n";
        $out .= "  _sp_cg_".$groupname."_".$pointname."_ev2_history[".$self->windowDepth."+1] = ".$self->event2.";\n";
        $out .= "  /* step 2 - increment bins now if events warrant */\n";
        $out .= "  if(".$self->event1.") {\n;";
        $out .= "    for(int i=0;i<=".$self->windowDepth.";i++) {\n";
        $out .= "      if(_sp_cg_".$groupname."_".$pointname."_ev2_history[".$self->windowDepth."+1-i]) {\n";
        $out .= "        ++_sp_cg_".$groupname."_".$pointname."_bin[".$self->windowDepth."-i]; /* bin corresponding to ".$self->event2." i cycles ago */\n";
        $out .= "      }\n";
        $out .= "    }\n";
        $out .= "  }\n";
        $out .= "  if(".$self->event2.") {\n;";
        $out .= "    for(int i=1;i<=".$self->windowDepth.";i++) { // zero is already handled!\n";
        $out .= "      if(_sp_cg_".$groupname."_".$pointname."_ev1_history[".$self->windowDepth."+1-i]) {\n";
        $out .= "        ++_sp_cg_".$groupname."_".$pointname."_bin[".$self->windowDepth."+i]; /* bin corresponding to ".$self->event1." i cycles ago */\n";
        $out .= "      }\n";
        $out .= "    }\n";
        $out .= "  }\n";
        $out .= "}\n";
    } elsif ($self->crossMember) {
        $out .= "/* point name = $pointname is a crossMember - no separate sample needed */\n";
    } elsif ($self->isCross) {
        $out .= "/* cross name = $pointname */\n";
        $out .= "{ ++_sp_cg_".$groupname."_".$pointname;
        my @dimensions;
        push @dimensions, @{$self->rows};
        push @dimensions, @{$self->cols};
        push @dimensions, @{$self->tables};
        my @args;
        foreach my $dimension (@dimensions) {
            $out .= "[_sp_cg_".$groupname."_".$dimension->name;
            $out .= "_computeBin(".$dimension->connection.")]";
            push @args, $dimension->connection;
        }
        $out .= "; }\n";
        if ($self->illegalFunc) {
            my $argsWithCommas = join(', ',@args);
            my $argsWithStreamAndCommas = join(' << ", " << ',@args);
            #$out .= "if (".$self->illegalFunc."($argsWithCommas)) { SP_ERROR_LN(\"".$self->filename."\",".$self->module->lineno.",\"SP_COVERGROUP illegal sample of ".$self->name.", asserted by: ".$self->illegalFunc."(".$argsWithCommas.")\\n\"); }\n";
            $out .= "if (".$self->illegalFunc."($argsWithCommas)) { ostringstream ostr; ostr << \"SP_COVERGROUP illegal sample of ".$self->name.", asserted by: ".$self->illegalFunc."(".$argsWithCommas."), values: \" << ".$argsWithStreamAndCommas." << endl; SP_ERROR_LN(\"".$self->filename."\",".$self->module->lineno.",ostr.str().c_str()); }\n";
        }
    } else {
        $out .= "/* point name = $pointname */\n";
        #$out .= "{ printf(\"val %d -> bin %d\\n\",(int)".$coverpointref->connection.".read(),(int)_sp_cg_".$groupname."_".$pointname."_computeBin(".$coverpointref->connection.")); fflush(stdout); }\n";
        $out .= "{ ++_sp_cg_".$groupname."_".$pointname."[_sp_cg_".$groupname."_".$pointname."_computeBin(".$self->connection.")]; }\n";
    }
    return $out;
}

sub cross_build { my $self = shift; my $fileref = shift; my $type = shift;

    if ($type eq "start_rows") {
        print "start rows\n" if $SystemC::Netlist::Debug;
        $self->attributes("dimension","rows");
    } elsif ($type eq "start_cols") {
        print "start cols\n" if $SystemC::Netlist::Debug;
        $self->attributes("dimension","cols");
    } elsif ($type eq "start_table") {
        print "start table\n" if $SystemC::Netlist::Debug;
        $self->attributes("dimension","tables");
    } elsif ($type eq "item") {
        my $item = shift;
        print "item $item\n" if $SystemC::Netlist::Debug;
        # check that $item is a coverpoint we already know about
        my $currentCovergroup = $self->module->current_covergroup();
        foreach my $point (@{$currentCovergroup->coverpoints}) {
            if ($point->name eq $item) {
                if($point->isWindow) {
                    $self->error("Crossing windows is not (yet) supported.\n");
                }
                $point->crossMember(1);
                my $dimension = $self->attributes("dimension");
                if ($dimension eq "rows") {
                    push @{$self->rows}, $point;
                } elsif ($dimension eq "cols") {
                    push @{$self->cols}, $point;
                } elsif ($dimension eq "tables") {
                    push @{$self->tables}, $point;
                } else {
                    $self->error("CoverPoint internal error: dimension == $dimension\n");
                }
                return; # if we never get a match, fall through to the error below
            }
        }
        $self->error("Netlist::File: cross parsed an unrecognized coverpoint: $item\n");
    } else {
        $self->error("Netlist::File: cross parsed an unexpected type: $type\n");
    }
}

sub coverpoint_build { my $self = shift; my $fileref = shift; my $type = shift;

    if ($type eq "binval") {
        my $val_str = shift;
        print "Netlist::File: coverpoint parsed binval: $val_str\n" if $SystemC::Netlist::Debug;
        my $bin = $self->current_bin();
        $bin->name($self->attributes("binname"));
        push @{$bin->values}, $val_str;
        if ($self->attributes("in_multi_bin")) {
            $bin->name($self->attributes("multi_bin_basename")
                       ."_"
                       .$self->attributes("multi_bin_count"));
            $self->attributes("multi_bin_count",
                              1 + $self->attributes("multi_bin_count"));
        }
        $bin->isIllegal(1) if ($self->attributes("in_illegal"));
        $bin->isIgnore(1)  if ($self->attributes("in_ignore"));
        # add this bin to the point
        push @{$self->bins}, $bin;
        # undef it so that the next bin will be fresh
        $self->attributes("_openBin",undef);
    } elsif ($type eq "binrange") {
        my $lo_str = shift;
        my $hi_str = shift;
        print "Netlist::File: coverpoint parsed binrange: $lo_str:$hi_str\n" if $SystemC::Netlist::Debug;
        my $bin = $self->current_bin();
        $bin->name($self->attributes("binname"));
        push @{$bin->ranges}, "$hi_str,$lo_str";
        if ($self->attributes("in_multi_bin")) {
            $bin->name($self->attributes("multi_bin_basename")
                       ."_"
                       .$self->attributes("multi_bin_count"));
            $self->attributes("multi_bin_count",
                              1 + $self->attributes("multi_bin_count"));
        }
        $bin->isIllegal(1) if ($self->attributes("in_illegal"));
        $bin->isIgnore(1)  if ($self->attributes("in_ignore"));
        # add this bin to the point
        push @{$self->bins}, $bin;
        # undef it so that the next bin will be fresh
        $self->attributes("_openBin",undef);
    } elsif ($type eq "illegal") {
        my $binname = shift;
        print "Netlist::File: coverpoint parsed illegal bin, name = $binname\n" if $SystemC::Netlist::Debug;
        $self->attributes("binname",$binname);
        $self->attributes("in_illegal",1);
        $self->attributes("in_ignore",0);
    } elsif ($type eq "ignore") {
        my $binname = shift;
        print "Netlist::File: coverpoint parsed ignore bin, name = $binname\n" if $SystemC::Netlist::Debug;
        $self->attributes("binname",$binname);
        $self->attributes("in_illegal",0);
        $self->attributes("in_ignore",1);
    } elsif ($type eq "ignore_func") {
        my $func = shift;
        $self->ignoreFunc($func);
    } elsif ($type eq "limit_func") {
        my $func = shift;
        $self->limitFunc($func);
    } elsif ($type eq "illegal_func") {
        my $func = shift;
        $self->illegalFunc($func);
    } elsif ($type eq "normal") {
        my $binname = shift;
        print "Netlist::File: coverpoint parsed normal bin, name = $binname\n" if $SystemC::Netlist::Debug;
        if (length($binname) > MAX_USER_STRING_LEN) {
            my $max = MAX_USER_STRING_LEN;
            $self->error ("SP_COVERGROUP \"$binname\" string too long (max $max chars)\n");
        }
        $self->attributes("binname",$binname);
        $self->attributes("in_illegal",0);
        $self->attributes("in_ignore",0);
    } elsif ($type eq "default") {
        print "Netlist::File: coverpoint parsed default\n" if $SystemC::Netlist::Debug;
        $self->defaultName($self->attributes("binname"));
        $self->defaultIsIgnore($self->attributes("in_ignore"));
        $self->defaultIsIllegal($self->attributes("in_illegal"));
    } elsif ($type eq "single") {
        print "Netlist::File: coverpoint parsed single\n" if $SystemC::Netlist::Debug;
    } elsif ($type eq "multi_begin") {
        print "Netlist::File: coverpoint parsed multi_begin\n" if $SystemC::Netlist::Debug;
        my $bin = $self->current_bin();
        $bin->name($self->attributes("binname"));
        $self->attributes("in_multi_bin",1);
        $self->attributes("multi_bin_count",0);
        $self->attributes("multi_bin_basename",$bin->name);
    } elsif ($type eq "multi_begin_num") {
        my $num_ranges = shift;
        print "Netlist::File: coverpoint parsed multi_begin_num\n" if $SystemC::Netlist::Debug;
        my $bin = $self->current_bin();
        $bin->name($self->attributes("binname"));
        $self->attributes("in_multi_bin",1);
        $self->attributes("multi_bin_num_ranges",$num_ranges); # we use this in multi_bin_end
        $self->attributes("multi_bin_count",0);
        $self->attributes("multi_bin_basename",$bin->name);
    } elsif ($type eq "multi_end") {
        print "Netlist::File: coverpoint parsed multi_end\n" if $SystemC::Netlist::Debug;
        $self->attributes("in_multi_bin",0);
    } elsif ($type eq "multi_auto_end") {
        print "Netlist::File: coverpoint parsed multi_auto_end\n" if $SystemC::Netlist::Debug;
        $self->attributes("in_multi_bin",0);
        # there's a single bin with a range; we want to
        # convert it into a bunch of individual bins of size 1
        my $bin = pop @{$self->bins};
        my $range = pop @{$bin->ranges};
        $range =~ /(\S+),(\S+)/;
        my $hi_str = $1;
        my $lo_str = $2;
        my $lo = $self->validate_value($lo_str,$fileref);
        my $hi = $self->validate_value($hi_str,$fileref);
        if ($self->attributes("multi_bin_num_ranges")) {
            $self->make_standard_bins($self->attributes("multi_bin_num_ranges")
                                      ,$lo,$hi,$self->attributes("multi_bin_basename"));
        } else {
            $self->make_standard_bins(($hi - $lo + 1),$lo,$hi,$self->attributes("multi_bin_basename"));
        }
        $self->attributes("multi_bin_num_ranges",0);
    } elsif ($type eq "standard") {
        print "Netlist::File: coverpoint parsed standard\n" if $SystemC::Netlist::Debug;
        # only the default bin
    } elsif ($type eq "standard_bins_range") {
        my $binsize_str = shift;
        my $lo_str = shift;
        my $hi_str = shift;
        print "Netlist::File: coverpoint parsed standard_bins_range, size = $binsize_str, lo = $lo_str, hi = $hi_str\n" if $SystemC::Netlist::Debug;
        my $binsize = $self->validate_value($binsize_str,$fileref);
        my $lo = $self->validate_value($lo_str,$fileref);
        my $hi = $self->validate_value($hi_str,$fileref);
        $self->make_standard_bins($binsize,$lo,$hi,$self->name);
    } elsif ($type eq "standard_bins") {
        my $binsize_str = shift;
        print "Netlist::File: coverpoint parsed standard_bins, size = $binsize_str\n" if $SystemC::Netlist::Debug;
        my $binsize = $self->validate_value($binsize_str,$fileref);
        # FIXME default 1024 is a hack
        # we should look up the size from the sp_ui etc.
        $self->make_standard_bins($binsize,0,1023,$self->name);
    } elsif ($type eq "bins") {
        print "Netlist::File: coverpoint parsed explicit bins\n" if $SystemC::Netlist::Debug;
        $self->num_bins(scalar(@{$self->bins}));
    } elsif ($type eq "enum") {
        my $enum = shift;
        print "Netlist::File: coverpoint parsed enum bins, enum = $enum\n" if $SystemC::Netlist::Debug;
        $self->enum($enum);
        # we don't actually make the bins for enums until output time.
    } elsif ($type eq "page") {
        my $page = shift;
        print "Netlist::File: coverpoint parsed page = $page\n" if $SystemC::Netlist::Debug;
        if (length($page) > MAX_USER_STRING_LEN) {
            my $max = MAX_USER_STRING_LEN;
            $self->error ("SP_COVERGROUP \"$page\" string too long (max $max chars)\n");
        }
        $self->page($page);
    } elsif ($type eq "description") {
        my $desc = shift;
        print "Netlist::File: coverpoint parsed description = $desc\n" if $SystemC::Netlist::Debug;
        if (length($desc) > MAX_USER_STRING_LEN) {
            my $max = MAX_USER_STRING_LEN;
            $self->error ("SP_COVERGROUP \"$desc\" string too long (max $max chars)\n");
        }
        $self->description($desc);
    } elsif ($type eq "option") {
        my $var = shift;
        my $val = shift;
        if (length($var) > MAX_USER_STRING_LEN) {
            my $max = MAX_USER_STRING_LEN;
            $self->error ("SP_COVERGROUP \"$var\" string too long (max $max chars)\n");
        }
        if ($var eq "radix") {
            if (($val == 16) ||
                ($val == 10) ||
                ($val == 2)) {
                $self->radix($val);
            } else {
                $self->error("Unrecognized radix option \"$val\"; I know about 2/10/16\n");
            }
        } elsif ($var eq "max_bins") {
            # check it's a number and >0
            if ($val =~ /^0x[0-9a-fA-F]+$/) { # hex number
                #print "recognized hex $val as ". (hex $val)."\n";
                $self->max_bins(hex $val);
            } elsif ($val =~ /^\d+$/) { # decimal number
                #print "recognized dec $val\n";
                $self->max_bins($val);
            } else {
                $self->error("max_bins option \"$val\" is not a natural number!\n");
            }
        } else {
            $self->error("Unrecognized coverpoint option \"$var = $val\"\n");
        }
    } else {
        $self->error("Netlist::File: coverpoint parsed an unexpected type: $type\n");
    }
}

sub validate_value { my $self = shift; my $str = shift; my $fileref = shift;

    if ($str =~ /^0x[0-9a-fA-F]+$/) { # hex number
        #print "recognized hex $str as ". (hex $str)."\n";
        if (length $str > 2+16) {
            $self->error("Hex value of over 64 bits: $str\n");
        } elsif (length $str > 2+8 && !_perl64()) {
            $self->error("Hex value of 64 bits; need a 64-bit Perl interpreter: $str\n");
        }
        return hex $str;
    } elsif ($str =~ /^\d+$/) { # decimal number
        #print "recognized dec $str\n";
        return $str;
    } elsif ($str =~ /^(\w+)::(\w+)$/) { # enum
        my $enumclass = $1;
        my $enumname = $2;
        # do we recognize the enum name?
        my $netlist = $fileref->netlist();
        my $vals = $netlist->{_enums}{$enumclass};
        if (!defined $vals) {
            $self->error("parsed what looks like an enum but is an unrecognized enum class: ${enumclass}\n");
            return 0;
        }
        my $val = $vals->{"en"}{$enumname};
        if (!defined $val) {
            $self->error("parsed a recognized enum type but an unrecognized value: ${enumclass}::${enumname}\n");
            return 0;
        }
        return $val;
    } else {
        $self->error("parsed coverpoint bin value not a decimal or hex number: $str");
        return 0;
    }
}

sub _perl64 { return 0 if $ENV{SYSTEMPERL_WARN_PERL64}; # So we don't break 'make test' on 32 bits return 1 if $Config{ivsize}>=8; return 0; }

sub make_standard_bins { my $self = shift; my $num_bins = shift; my $lo_range = shift; my $hi_range = shift; my $name = shift;

    my $span = $hi_range - $lo_range + 1;
    if ($span < $num_bins) {
        $self->error("more bins specified ($num_bins) than values in range ($lo_range:$hi_range)\n");
    }
    my $radix_char = "d";
    $radix_char = "x" if ($self->radix == 16);
    $radix_char = "d" if ($self->radix == 10);
    $radix_char = "b" if ($self->radix == 2);
    # add just the right number of leading zeros
    my $s = sprintf("%".$radix_char,$num_bins-1+$lo_range);
    my $digits = length $s;
    my $s2 = sprintf("%".$radix_char,$num_bins-1);
    my $name_digits = length $s2;
    # make bins
    for(my $i=0;$i<$num_bins;$i++) {
        my $bin = $self->current_bin();
        $bin->name($self->attributes("binname"));
        my $lo = int(($span / $num_bins) * $i) + $lo_range;
        my $hi = (int(($span / $num_bins) * ($i+1)) - 1) + $lo_range;
        push @{$bin->ranges}, sprintf("%u,%u",$hi,$lo); # force unsigned
        if ($span == $num_bins) {
            # no names required!
            $bin->name(sprintf("%0".$digits.$radix_char,$i+$lo_range));
        } else {
            $bin->name(sprintf("%s_%0".$name_digits.$radix_char,$name,$i));
        }
        push @{$self->bins}, $bin;
        # undef it so that the next bin will be fresh
        $self->attributes("_openBin",undef);
    }
    $self->num_bins(scalar(@{$self->bins}));
}

sub make_auto_enum_bins { my $self = shift; my $fileref = shift; my $enum = $self->enum;

    my $netlist = $fileref->netlist();
    if (!defined $netlist) {
        $self->error("Internal error: no netlist!\n");
    }
    # do we recognize the enum name?
    my $vals = $netlist->{_enums}{$enum};
    if (!defined $vals) {
        $self->error("Netlist::File: coverpoint parsed 'auto_enum_bins' with an unrecognized enum class: $enum\n");
        return;
    }
    my $enumtype = "en";
    if (!defined $vals->{$enumtype}) {
        $self->error("Netlist::File: coverpoint parsed 'auto_enum_bins' and couldn't find either an auto-enum or a ${enum}::en\n");
        return;
    }
    foreach my $valsym (sort {$vals->{$enumtype}{$a} <=> $vals->{$enumtype}{$b}}
                        (keys %{$vals->{$enumtype}})) {
        next if $valsym eq "MAX"; # auto-enums contain a value named MAX which isn't real
        my $val = $vals->{$enumtype}{$valsym};
        my $bin = $self->current_bin();
        $bin->name($self->attributes("binname"));
        push @{$bin->values}, sprintf("%u",$val); # force unsigned
        if ($val < $self->minValue) { $self->minValue($val);}
        if ($val > $self->maxValue) { $self->maxValue($val);}
        $bin->name($valsym);
        # add this bin to the point
        push @{$self->bins}, $bin;
        # undef it so that the next bin will be fresh
        $self->attributes("_openBin",undef);
    }
    $self->num_bins(scalar(@{$self->bins}));
}

################################# # Write SystemC

sub _write_coverpoint_decl { my $self = shift; my $fileref = shift; my $prefix = shift; my $modref = shift; my $covergroupref = shift;

    # only now (when all the autoenums have been parsed) do we make the enum bins
    if (defined $self->enum) {
        $self->make_auto_enum_bins($fileref);
    }
    if ($self->isWindow) {
        # declare event history to track old samples
        $fileref->printf ("%sbool\t_sp_cg_%s_%s_ev1_history[%d];\t// SP_COVERGROUP window event history\n",
                          $prefix,
                          $covergroupref->name,
                          $self->name,
                          $self->windowDepth+2);
        $fileref->printf ("%sbool\t_sp_cg_%s_%s_ev2_history[%d];\t// SP_COVERGROUP window event history\n",
                          $prefix,
                          $covergroupref->name,
                          $self->name,
                          $self->windowDepth+2);
        # declare coverage bins
        $fileref->printf ("%sSpZeroed<uint32_t>\t_sp_cg_%s_%s_bin[%d];\t// SP_COVERGROUP window declaration\n",
                          $prefix,
                          $covergroupref->name,
                          $self->name,
                          2*$self->windowDepth+1);
        ###########################################################################
        # write the function returning the bin name
        ###########################################################################
        $fileref->printf ("%sstatic const char* _sp_cg_%s_%s_binName(uint64_t point) { \t// SP_COVERGROUP declaration\n",
                          $prefix,
                          $covergroupref->name,
                          $self->name);
        $fileref->printf ("%s  static const char* _s_bin_to_name[] = {",$prefix);
        for (my $i=$self->windowDepth;$i>0;$i--) {
            # just the number itself
            $fileref->printf ("\"-$i\",",$i);
        }
        for (my $i=0;$i<=$self->windowDepth;$i++) {
            # just the number itself
            $fileref->printf ("\"$i\",",$i);
        }
        $fileref->printf ("};\n");
        $fileref->printf ("%s  return (_s_bin_to_name[point]);\n%s}\n",$prefix,$prefix);
        ###########################################################################
        # write the function returning an arbitrary value per bin
        ###########################################################################
        $fileref->printf ("%suint64_t _sp_cg_%s_%s_getArbitraryValue(uint64_t bin) { \t// SP_COVERGROUP declaration\n",
                          $prefix,
                          $covergroupref->name,
                          $self->name);
        my $binnum=0;
        for (my $i=$self->windowDepth;$i>0;$i--) {
            # FIXME should be return -%d
            # but negative windows are going away anyway
            $fileref->printf ("%s  if (bin == %d) return %d; // FIXME negative window \n",
                              $prefix,$binnum,$i);
            $binnum++;
        }
        for (my $i=0;$i<=$self->windowDepth;$i++) {
            $fileref->printf ("%s  if (bin == %d) return %d; // positive window \n",
                              $prefix,$binnum,$i);
            $binnum++;
        }
        $fileref->printf ("%s  SP_ERROR_LN(\"%s\",%d,\"Internal error: Illegal bin value for point %s\\n\");\n",
                          $prefix,$fileref->name,$covergroupref->module->lineno,$self->name);
        $fileref->printf ("%s  return 0;\n", $prefix);
        $fileref->printf ("%s}\n", $prefix);
        ###########################################################################
        # write the function returning the ignoredness
        ###########################################################################
        $fileref->printf ("%sbool _sp_cg_%s_%s_ignored(uint64_t bin) { \t// SP_COVERGROUP declaration\n",
                          $prefix,
                          $covergroupref->name,
                          $self->name);
        if ($self->ignoreFunc) {
            $fileref->printf ("%s  if (%s(_sp_cg_%s_%s_getArbitraryValue(bin))) { return true; }\n",
                              $prefix,$self->ignoreFunc,
                              $covergroupref->name,
                              $self->name);
        }
        $fileref->printf ("%s  return false;\n%s}\n",
                          $prefix,$prefix);
        ###########################################################################
        # write the function returning the illegality
        ###########################################################################
        $fileref->printf ("%sbool _sp_cg_%s_%s_illegal(uint64_t bin) { \t// SP_COVERGROUP declaration\n",
                          $prefix,
                          $covergroupref->name,
                          $self->name);
        if ($self->illegalFunc) {
            $fileref->printf ("%s  if (%s(_sp_cg_%s_%s_getArbitraryValue(bin))) { return true; }\n",
                              $prefix,$self->illegalFunc,
                              $covergroupref->name,
                              $self->name);
        }
        $fileref->printf ("%s  return false;\n%s}\n",
                          $prefix,$prefix);
    } elsif ($self->isCross) {
        # write the cross stuff
        my @dimensions;
        push @dimensions, @{$self->rows};
        push @dimensions, @{$self->cols};
        push @dimensions, @{$self->tables};
        # declare the coverpoint
        $fileref->printf ("%sSpZeroed<uint32_t>\t_sp_cg_%s_%s",
                          $prefix,
                          $covergroupref->name,
                          $self->name);
        foreach my $dimension (@dimensions) {
            $fileref->printf ("[%d]",$dimension->num_bins+1); # +1 for default
        }
        $fileref->printf (";\t// SP_COVERGROUP declaration\n");
        ###########################################################################
        # write the function returning the ignoredness
        ###########################################################################
        $fileref->printf ("%sbool _sp_cg_%s_%s_ignored(",
                          $prefix,
                          $covergroupref->name,
                          $self->name);
        my @args;
        foreach my $dimension (@dimensions) {
            my $dimname = $dimension->name;
            push @args, "uint64_t $dimname";
        }
        my $argsWithCommas = join(', ',@args);
        $fileref->printf ("%s) { \t// SP_COVERGROUP declaration\n",$argsWithCommas);
        $fileref->printf ("%s  return (0 // if any dimension is ignored\n",$prefix);
        foreach my $dimension (@dimensions) {
            $fileref->printf ("%s         || _sp_cg_%s_%s_ignored(%s)\n",
                              $prefix,
                              $covergroupref->name,
                              $dimension->name,
                              $dimension->name);
        }
        if ($self->ignoreFunc) {
            my @args2;
            foreach my $dimension (@dimensions) {
                my $dimname = $dimension->name;
                my $str = "_sp_cg_".$covergroupref->name."_".$dimension->name."_getArbitraryValue(${dimname})";
                push @args2, $str;
            }
            my $args2WithCommas = join(', ',@args2);
            $fileref->printf ("%s         || %s(%s) // or if my func says it should be ignored\n",
                              $prefix,$self->ignoreFunc,$args2WithCommas);
        }
        $fileref->printf ("%s         );\n",$prefix);
        $fileref->printf ("%s}\n",$prefix);
        ###########################################################################
        # write the function returning the illegality
        ###########################################################################
        $fileref->printf ("%sbool _sp_cg_%s_%s_illegal(",
                          $prefix,
                          $covergroupref->name,
                          $self->name);
        my @args3;
        foreach my $dimension (@dimensions) {
            my $dimname = $dimension->name;
            push @args3, "uint64_t $dimname";
        }
        $argsWithCommas = join(', ',@args3);
        $fileref->printf ("%s) { \t// SP_COVERGROUP declaration\n",$argsWithCommas);
        $fileref->printf ("%s  return (0 // if any dimension is illegal\n",$prefix);
        foreach my $dimension (@dimensions) {
            $fileref->printf ("%s         || _sp_cg_%s_%s_illegal(%s)\n",
                              $prefix,
                              $covergroupref->name,
                              $dimension->name,
                              $dimension->name);
        }
        if ($self->illegalFunc) {
            my @args2;
            foreach my $dimension (@dimensions) {
                my $dimname = $dimension->name;
                my $str = "_sp_cg_".$covergroupref->name."_".$dimension->name."_getArbitraryValue(${dimname})";
                push @args2, $str;
            }
            my $args2WithCommas = join(', ',@args2);
            $fileref->printf ("%s         || %s(%s) // or if my func says it should be illegal\n",
                              $prefix,$self->illegalFunc,$args2WithCommas);
        }
        $fileref->printf ("%s          );\n",$prefix);
        $fileref->printf ("%s}\n",$prefix);
    } else { # not a cross
        if (!$self->crossMember) {
            # declare the coverpoint (only if not a crossMember)
            $fileref->printf ("%sSpZeroed<uint32_t>\t_sp_cg_%s_%s[%d];\t// SP_COVERGROUP declaration\n",
                              $prefix,
                              $covergroupref->name,
                              $self->name,
                              $self->num_bins+1); # +1 for default
        }
        ###########################################################################
        # write the function returning the ignoredness
        ###########################################################################
        $fileref->printf ("%sbool _sp_cg_%s_%s_ignored(uint64_t bin) { \t// SP_COVERGROUP declaration\n",
                          $prefix,
                          $covergroupref->name,
                          $self->name);
        $fileref->printf ("%s  static int _s_bin_to_ignore[] = {",$prefix);
        my @lookupTable = (0) x ($self->num_bins);
        my $bin_num = 0;
        foreach my $bin (@{$self->bins}) {
            $lookupTable[$bin_num] = $bin->isIgnore;
            $bin_num+=1;
        }
        # now printf the table
        for (my $i = 0; $i < $self->num_bins; $i++) {
            $fileref->printf ("%d,",$lookupTable[$i]);
        }
        # and add the default bin
        $fileref->printf ("%d,",$self->defaultIsIgnore);
        $fileref->printf ("};\n");
        if ($self->ignoreFunc) {
            $fileref->printf ("%s  if (%s(_sp_cg_%s_%s_getArbitraryValue(bin))) { return true; }\n",
                              $prefix,$self->ignoreFunc,
                              $covergroupref->name,
                              $self->name);
        }
        $fileref->printf ("%s  if (bin >= %d) { SP_ERROR_LN(\"%s\",%d,\"Internal error: Illegal bin value in %s_ignore\\n\"); return true; }\n",
                          $prefix,$self->num_bins+1, # +1 for default
                          $fileref->name,$covergroupref->module->lineno,$self->name);
        $fileref->printf ("%s  return (_s_bin_to_ignore[bin]);\n%s}\n",
                          $prefix,$prefix);
        ###########################################################################
        # write the function returning the illegality
        ###########################################################################
        $fileref->printf ("%sbool _sp_cg_%s_%s_illegal(uint64_t bin) { \t// SP_COVERGROUP declaration\n",
                          $prefix,
                          $covergroupref->name,
                          $self->name);
        $fileref->printf ("%s  static int _s_bin_to_illegal[] = {",$prefix);
        @lookupTable = (0) x ($self->num_bins);
        $bin_num = 0;
        foreach my $bin (@{$self->bins}) {
            $lookupTable[$bin_num] = $bin->isIllegal;
            $bin_num+=1;
        }
        # now printf the table
        for (my $i = 0; $i < $self->num_bins; $i++) {
            $fileref->printf ("%d,",$lookupTable[$i]);
        }
        # and add the default bin
        $fileref->printf ("%d,",$self->defaultIsIllegal);
        $fileref->printf ("};\n");
        if ($self->illegalFunc) {
            $fileref->printf ("%s  if (%s(_sp_cg_%s_%s_getArbitraryValue(bin))) { return true; }\n",
                              $prefix,$self->illegalFunc,
                              $covergroupref->name,
                              $self->name);
        }
        $fileref->printf ("%s  if (bin >= %d) { SP_ERROR_LN(\"%s\",%d,\"Internal error: Illegal bin value in %s_illegal\\n\"); return true; }\n",
                          $prefix,$self->num_bins+1, # +1 for default
                          $fileref->name,$covergroupref->module->lineno,$self->name);
        $fileref->printf ("%s  return (_s_bin_to_illegal[bin]);\n%s}\n",
                          $prefix,$prefix);
        ###########################################################################
        # write the function returning the bin name
        ###########################################################################
        $fileref->printf ("%sstatic const char* _sp_cg_%s_%s_binName(uint64_t point) { \t// SP_COVERGROUP declaration\n",
                          $prefix,
                          $covergroupref->name,
                          $self->name);
        $fileref->printf ("%s  static const char* _s_bin_to_name[] = {",$prefix);
        foreach my $bin (@{$self->bins}) {
            $fileref->printf ("\"%s\",",$bin->name);
        }
        $fileref->printf ("\"%s\"",$self->defaultName);
        $fileref->printf ("};\n");
        $fileref->printf ("%s  return (_s_bin_to_name[point]);\n%s}\n",$prefix,$prefix);
        foreach my $bin (@{$self->bins}) {
            my @values = @{$bin->values};
            foreach my $value_str (@values) {
                my $val = $self->validate_value($value_str,$fileref);
                if ($val < $self->minValue) { $self->minValue($val);}
                if ($val > $self->maxValue) { $self->maxValue($val);}
            }
            my @ranges = @{$bin->ranges};
            foreach my $range (@ranges) {
                $range =~ /(\S+),(\S+)/;
                my $hi_str = $1;
                my $lo_str = $2;
                my $lo = $self->validate_value($lo_str,$fileref);
                my $hi = $self->validate_value($hi_str,$fileref);
                if ($lo < $self->minValue) { $self->minValue($lo);}
                if ($hi < $self->minValue) { $self->minValue($hi);}
                if ($lo > $self->maxValue) { $self->maxValue($lo);}
                if ($hi > $self->maxValue) { $self->maxValue($hi);}
            }
        }
        ###########################################################################
        # write the function returning an arbitrary value per bin
        ###########################################################################
        $fileref->printf ("%suint64_t _sp_cg_%s_%s_getArbitraryValue(uint64_t bin) { \t// SP_COVERGROUP declaration\n",
                          $prefix,
                          $covergroupref->name,
                          $self->name);
        $bin_num = 0;
        foreach my $bin (@{$self->bins}) {
            if (scalar @{$bin->values}) {
                my @vals = @{$bin->values};
                my $arbitrary_val = $vals[0];
                # if it's not an enum, then add ULL to allow 64-bit numbers
                $arbitrary_val .= "ULL" unless ($arbitrary_val =~ /^(\w+)::(\w+)$/);
                $fileref->printf ("%s  if (bin == %s) return %s; // an arbitrary value in bin %s\n",
                                  $prefix,$bin_num,$arbitrary_val,$bin->name);
            } elsif (scalar @{$bin->ranges}) {
                my @ranges = @{$bin->ranges};
                $ranges[0] =~ /(\S+),(\S+)/;
                my $hi_str = $1;
                # if it's not an enum, then add ULL to allow 64-bit numbers
                $hi_str .= "ULL" unless ($hi_str =~ /^(\w+)::(\w+)$/);
                $fileref->printf ("%s  if (bin == %s) return %s; // an arbitrary value in bin %s\n",
                                  $prefix,$bin_num,$hi_str,$bin->name);
            } else {
                my $binname = $bin->name;
                $self->error("CoverPoint internal error: bin $binname has no values or ranges!\n");
            }
            $bin_num++;
        }
        if ($self->defaultName eq "") {
            $fileref->printf ("%s  if (bin == %s) return %dULL; // the unnamed default bin - return a value not in any other bin\n",
                              $prefix,$bin_num,($self->maxValue+1));
        } else {
            $fileref->printf ("%s  if (bin == %s) return %dULL; // the default bin (%s) - return a value not in any other bin\n",
                              $prefix,$bin_num,($self->maxValue+1),$self->defaultName);
        }
        $fileref->printf ("%s  SP_ERROR_LN(\"%s\",%d,\"Internal error: Illegal bin value for point %s\\n\");\n",
                          $prefix,$fileref->name,$covergroupref->module->lineno,$self->name);
        $fileref->printf ("%s  return 0;\n", $prefix);
        $fileref->printf ("%s}\n", $prefix);
        ###########################################################################
        # write the function computing which bin to increment
        ###########################################################################
        if (($self->minValue < 0) || ($self->maxValue > MAX_BIN_LOOKUP_SIZE)) {
            $fileref->printf ("%sint _sp_cg_%s_%s_computeBin(uint64_t point) { \t// SP_COVERGROUP declaration\n",
                              $prefix,
                              $covergroupref->name,
                              $self->name);
            if ($self->illegalFunc) {
                #$fileref->printf ("%s  if (%s(point)) { SP_ERROR_LN(\"%s\",%d,\"SP_COVERGROUP illegal sample of %s, asserted by: %s\\n\"); }\n",
                $fileref->printf ("%s  if (%s(point)) { ostringstream ostr; ostr << \"SP_COVERGROUP illegal sample of %s, asserted by %s, value: \" << point << endl; SP_ERROR_LN(\"%s\",%d,ostr.str().c_str()); }\n",
                                  $prefix,$self->illegalFunc,
                                  $self->name,$self->illegalFunc,
                                  $fileref->name,$covergroupref->module->lineno);
            }
            $bin_num = 0;
            foreach my $bin (@{$self->bins}) {
                $fileref->printf ("%s  if (0\n",$prefix);
                my @values = @{$bin->values};
                foreach my $value_str (@values) {
                    # if it's not an enum, then add ULL to allow 64-bit numbers
                    $value_str .= "ULL" unless ($value_str =~ /^(\w+)::(\w+)$/);
                    $fileref->printf ("%s     || (point == %s)\n",$prefix,$value_str);
                }
                my @ranges = @{$bin->ranges};
                foreach my $range (@ranges) {
                    $range =~ /(\S+),(\S+)/;
                    my $hi_str = $1;
                    my $lo_str = $2;
                    my $lo = $self->validate_value($lo_str,$fileref);
                    my $hi = $self->validate_value($hi_str,$fileref);
                    # if it's not an enum, then add ULL to allow 64-bit numbers
                    $lo .= "ULL" unless ($lo =~ /^(\w+)::(\w+)$/);
                    $hi .= "ULL" unless ($hi =~ /^(\w+)::(\w+)$/);
                    $fileref->printf ("%s     || ((point >= %s) && (point <= %s))\n", $prefix,$lo, $hi);
                }
                if ($bin->isIllegal) {
                    #$fileref->printf ("%s     ) { SP_ERROR_LN(\"%s\",%d,\"Sampled %s and hit illegal bin: %s\\n\"); return 0; } // %s\n",
                    $fileref->printf ("%s     ) { ostringstream ostr; ostr << \"SP_COVERGROUP Sampled %s and hit illegal bin: %s, value: \" << point << endl; SP_ERROR_LN(\"%s\",%d,ostr.str().c_str()); return 0; } // %s\n",
                                      $prefix,$self->name,$bin->name,
                                      $fileref->name,$covergroupref->module->lineno,
                                      $bin->name);
                } else {
                    $fileref->printf ("%s     ) return %d; // %s\n", $prefix,$bin_num,$bin->name);
                }
                $bin_num+=1;
            }
            # else the default bin
            if ($self->defaultIsIllegal) {
                #$fileref->printf ("%s  SP_ERROR_LN(\"%s\",%d,\"Sampled %s and hit illegal default bin: %s\\n\"); return 0;\n%s}\n",
                $fileref->printf ("%s  ostringstream ostr; ostr << \"SP_COVERGROUP Sampled %s and hit illegal default bin: %s, value: \" << point << endl; SP_ERROR_LN(\"%s\",%d,ostr.str().c_str()); return 0;\n%s}\n",
                                  $prefix,$self->name,$self->defaultName,
                                  $fileref->name,$covergroupref->module->lineno,$prefix);
            } else {
                $fileref->printf ("%s  return %d; // default\n%s}\n",$prefix,$bin_num,$prefix);
            }
        } else { # all values in range, use a lookup table
            $fileref->printf ("%sint _sp_cg_%s_%s_computeBin(uint64_t point) { \t// SP_COVERGROUP declaration\n",
                              $prefix,
                              $covergroupref->name,
                              $self->name);
            $fileref->printf ("%s  static int _s_value_to_bin[] = {",$prefix);
            # start with all default, which is bin number $self->num_bins
            # 0 thru $self->maxValue inclusive
            my @lookupTable = ($self->num_bins) x ($self->maxValue+1);
            # now populate the lookup table
            my $bin_num = 0;
            foreach my $bin (@{$self->bins}) {
                my @values = @{$bin->values};
                foreach my $value_str (@values) {
                    my $value = $self->validate_value($value_str,$fileref);
                    $lookupTable[$value] = $bin_num;
                }
                my @ranges = @{$bin->ranges};
                foreach my $range (@ranges) {
                    $range =~ /(\S+),(\S+)/;
                    my $hi_str = $1;
                    my $lo_str = $2;
                    my $lo = $self->validate_value($lo_str,$fileref);
                    my $hi = $self->validate_value($hi_str,$fileref);
                    for (my $i = $lo; $i <= $hi; $i++) {
                        $lookupTable[$i] = $bin_num;
                    }
                }
                $bin_num+=1;
            }
            # now printf the table
            for (my $i = 0; $i <= $self->maxValue; $i++) {
                $fileref->printf ("%d,",$lookupTable[$i]);
            }
            $fileref->printf ("};\n");
            if ($self->illegalFunc) {
                #$fileref->printf ("%s  if (%s(point)) { SP_ERROR_LN(\"%s\",%d,\"SP_COVERGROUP illegal sample of %s, asserted by: %s\\n\"); }\n",
                $fileref->printf ("%s  if (%s(point)) { ostringstream ostr; ostr << \"SP_COVERGROUP illegal sample of %s, asserted by: %s, value: \" << point << endl; SP_ERROR_LN(\"%s\",%d,ostr.str().c_str()); }\n",
                                  $prefix,$self->illegalFunc,
                                  $self->name, $self->illegalFunc,
                                  $fileref->name,$covergroupref->module->lineno);
            }
            $fileref->printf ("%s  if ((point > %d) | (point < %d)) return %d; // default\n",
                              $prefix,
                              $self->maxValue,
                              $self->minValue,
                              $self->num_bins);
            $fileref->printf ("%s  return (_s_value_to_bin[point]);\n%s}\n",
                              $prefix,$prefix);
        }
    }
}

sub _write_coverpoint_ctor { my $self = shift; my $fileref = shift; my $prefix = shift; my $modref = shift; my $covergroupref = shift;

    # if self->page is undefined, use group page
    my $page = $self->page || $covergroupref->page;
    $page =~ s/"//g;
    $page = "{no-page}" if $page eq '';
    $page = "sp_group/${page}/".$self->name;
    # if neither exists, use empty quotes
    my $description = $self->description || "\"\"";
    if ($self->isWindow) {
        $modref->netlist->add_coverpoint_page_name($page,$self);
        # initialize the event history
        $fileref->printf("for(int i=0;i<%d;i++) {\n",$self->windowDepth+2);
        $fileref->printf("  _sp_cg_%s_%s_ev1_history[i] = false;\n",
                         $covergroupref->name,
                         $self->name);
        $fileref->printf("  _sp_cg_%s_%s_ev2_history[i] = false;\n",
                         $covergroupref->name,
                         $self->name);
        $fileref->printf("}\n");
        # SP_COVER_INSERT the bins
        $fileref->printf("{ for(int i=0;i<%d;i++) {\n",2*$self->windowDepth+1);
        $fileref->printf("    if (!_sp_cg_%s_%s_ignored(i) && !_sp_cg_%s_%s_illegal(i)) {\n",
                         $covergroupref->name, $self->name,
                         $covergroupref->name, $self->name);
        $fileref->printf('      SP_COVER_INSERT(&_sp_cg_%s_%s_bin[i]',
                         $covergroupref->name,
                         $self->name);
        $fileref->printf(',"filename","%s"', $self->filename);
        $fileref->printf(',"lineno","%s"', $self->lineno);
        $fileref->printf(',"groupname","%s"', $covergroupref->name);
        $fileref->printf(',"per_instance","%s"', $covergroupref->per_instance);
        $fileref->printf(',"groupcmt",%s', $description); # quotes already present
        $fileref->printf(',"pointname","%s"', $self->name);
        $fileref->printf(',"hier",name()');
        # fields so the auto-table-generation code will recognize it
        $fileref->printf(',"page","%s"', $page);
        if ($self->limitFunc) {
            # windows have values == bin numbers # FIXME no they don't; make a getArbitraryValue()
            $fileref->printf (',"limit",SpCvtToCStr(%s(i))',
                              $self->limitFunc,
                              $covergroupref->name,
                              $self->name);
        } else {
            $fileref->printf (',"limit","%d"',DEFAULT_LIMIT);
        }
        $fileref->printf(',"weight","%f"', ($self->weight / (2*$self->windowDepth+1)));
        $fileref->printf(',"table", "%s"',$self->name);
        $fileref->printf(',"col0",_sp_cg_%s_%s_binName(i)',
                         $covergroupref->name,
                         $self->name);
        $fileref->printf(',"col0_name","%s observed N samples before(-) or after(+) %s"',$self->event1,$self->event2);
        $fileref->printf(");");
        $fileref->printf("\n");
        $fileref->printf("} } }\n");
    } elsif ($self->isCross) {
        $modref->netlist->add_coverpoint_page_name($page,$self);
        # write the cross stuff
        my @dimensions;
        push @dimensions, @{$self->rows};
        push @dimensions, @{$self->cols};
        push @dimensions, @{$self->tables};
        my $indent = "";
        my $total_bins = 1;
        foreach my $dimension (@dimensions) {
            $indent .= "  "; # indent two more spaces
            $fileref->printf("%sfor(int _sp_cg_%s=0;_sp_cg_%s<%d;_sp_cg_%s++) {\n",
                             $indent,$dimension->name,$dimension->name,
                             $dimension->num_bins + 1,$dimension->name); # include default
            # $total_bins uses the "+ 1" because if default is not ignored, then it's
            # still a bin which might be SP_COVER_INSERTed.
            #
            # This total does not take into account bins which CovVise will never
            # see because they are ignored or illegal; default is ignored unless
            # specified otherwise
            #
            $total_bins = $total_bins * ($dimension->num_bins + 1);
        }
        if ($total_bins > $self->max_bins) {
            $self->error("cross ".$self->name." has $total_bins bins (max ".$self->max_bins.", change with \"option max_bins = <num>\")!\n");
        }
        $indent .= "  ";
        # don't insert illegals and ignores
        my @ignoreVars;
        foreach my $dimension (@dimensions) {
            my $dimname = $dimension->name;
            push @ignoreVars, "_sp_cg_${dimname}";
        }
        $fileref->printf("%sif (!_sp_cg_%s_%s_ignored(%s) && !_sp_cg_%s_%s_illegal(%s)) {\n",
                         $indent,
                         $covergroupref->name,
                         $self->name,
                         join(', ',@ignoreVars),
                         $covergroupref->name,
                         $self->name,
                         join(', ',@ignoreVars));
        $fileref->printf('%s  SP_COVER_INSERT(&_sp_cg_%s_%s',
                         $indent,
                         $covergroupref->name,
                         $self->name);
        foreach my $dimension (@dimensions) {
            $fileref->printf ("[_sp_cg_%s]",$dimension->name);
        }
        $fileref->printf(',"filename","%s"', $self->filename);
        $fileref->printf(',"lineno","%s"', $self->lineno);
        $fileref->printf(',"groupname","%s"', $covergroupref->name);
        $fileref->printf(',"per_instance","%s"', $covergroupref->per_instance);
        $fileref->printf(',"groupcmt",%s', $description); # quotes already present
        $fileref->printf(',"pointname","%s"', $self->name);
        $fileref->printf(',"hier",name()');
        # fields so the auto-table-generation code will recognize it
        $fileref->printf(',"page","%s"', $page);
        if ($self->limitFunc) {
            my @limitArgs;
            foreach my $dimension (@dimensions) {
                my $dimname = $dimension->name;
                my $cgname = $covergroupref->name;
                push @limitArgs, "_sp_cg_${cgname}_${dimname}_getArbitraryValue(_sp_cg_${dimname})";
            }
            $fileref->printf(',"limit",SpCvtToCStr(%s(%s))',
                             $self->limitFunc,
                             join(', ',@limitArgs));
        } else {
            $fileref->printf (',"limit","%d"',DEFAULT_LIMIT);
        }
        $fileref->printf(',"weight","%f"', ($self->weight / $total_bins));
        # FIXME old-style
        $fileref->printf(',"table", "%s"', $self->name);
        my $rownum = 0;
        foreach my $row (@{$self->rows}) {
            $fileref->printf(',"row%d_name","%s"',
                             $rownum,
                             $row->name);
            $fileref->printf(',"row%d",_sp_cg_%s_%s_binName(_sp_cg_%s)',
                             $rownum,
                             $covergroupref->name,
                             $row->name,
                             $row->name);
            $rownum++;
        }
        my $colnum = 0;
        foreach my $col (@{$self->cols}) {
            $fileref->printf(',"col%d_name","%s"',
                             $colnum,
                             $col->name);
            $fileref->printf(',"col%d",_sp_cg_%s_%s_binName(_sp_cg_%s)',
                             $colnum,
                             $covergroupref->name,
                             $col->name,
                             $col->name);
            $colnum++;
        }
        # unused so far
        my $tablenum = 0;
        foreach my $table (@{$self->tables}) {
            $fileref->printf(',"table%d_name","%s"',
                             $tablenum,
                             $table->name);
            $fileref->printf(',"table%d",_sp_cg_%s_%s_binName(_sp_cg_%s)',
                             $tablenum,
                             $covergroupref->name,
                             $table->name,
                             $table->name);
            $tablenum++;
        }
        $fileref->printf(");");
        $fileref->printf("\n");
        $fileref->printf("%s}\n",$indent);
        foreach my $dimension (@dimensions) {
            $fileref->printf("}\n");
        }
    } elsif (!$self->crossMember) {
        my $total_bins = $self->num_bins + 1;
        if ($total_bins > $self->max_bins) {
            $self->error("coverpoint ".$self->name." has $total_bins bins (max ".$self->max_bins.", change with \"option max_bins = <num>\")!\n");
        }
        $modref->netlist->add_coverpoint_page_name($page,$self);
        $fileref->printf("{ for(int i=0;i<%d;i++) {\n",$self->num_bins + 1); # include default
        $fileref->printf("    if (!_sp_cg_%s_%s_ignored(i) && !_sp_cg_%s_%s_illegal(i)) {\n",
                         $covergroupref->name, $self->name,
                         $covergroupref->name, $self->name);
        $fileref->printf('      ');
        $fileref->printf('SP_COVER_INSERT(&_sp_cg_%s_%s[i]',
                         $covergroupref->name,
                         $self->name);
        $fileref->printf(',"filename","%s"', $self->filename);
        $fileref->printf(',"lineno","%s"', $self->lineno);
        $fileref->printf(',"groupname","%s"', $covergroupref->name);
        $fileref->printf(',"per_instance","%s"', $covergroupref->per_instance);
        $fileref->printf(',"groupcmt",%s', $description); # quotes already present
        $fileref->printf(',"pointname","%s"', $self->name);
        $fileref->printf(',"hier",name()');
        # fields so the auto-table-generation code will recognize it
        $fileref->printf(',"page","%s"', $page);
        if ($self->limitFunc) {
            $fileref->printf (',"limit",SpCvtToCStr(%s(_sp_cg_%s_%s_getArbitraryValue(i)))',
                              $self->limitFunc,
                              $covergroupref->name,
                              $self->name);
        } else {
            $fileref->printf (',"limit","%d"',DEFAULT_LIMIT);
        }
        $fileref->printf(',"weight","%f"', ($self->weight / ($self->num_bins+1)));
        $fileref->printf(',"table", "%s"', $self->name);
        $fileref->printf(',"row0",_sp_cg_%s_%s_binName(i)',
                         $covergroupref->name,
                         $self->name);
        $fileref->printf(',"row0_name","%s"',
                         $self->name);
        $fileref->printf(");");
        $fileref->printf("\n");
        $fileref->printf("} } }\n");
    } else {
        # else this is a 1-d which is a member of a cross; don't insert any points
    }
}

###################################################################### #### Package return 1; __END__


SystemC::Netlist::CoverPoint

NAME

SystemC::Netlist::CoverPoint - Coverage point routines

DESCRIPTION

SystemC::Netlist::CoverPoint implements coverpoints associated with the SP_COVERGROUP features. It is called from SystemC::Netlist::Module.

SEE ALSO

SystemC::Netlist::Module SystemC::Netlist::CoverGroup # SystemC - SystemC Perl Interface # See copyright, etc in below POD section. ######################################################################

package SystemC::Netlist::File; use Class::Struct; use Carp;

use SystemC::Netlist; use SystemC::Template; use Verilog::Netlist::Subclass; @ISA = qw(SystemC::Netlist::File::Struct Verilog::Netlist::Subclass); $VERSION = '1.341'; use strict;

structs('new',
'SystemC::Netlist::File::Struct'
=>[name=> '$', #'# Filename this came from
basename=> '$', #'# Basename of the file
netlist=> '$', #'# Netlist is a member of
userdata=> '%',# User information
module_exp=> '$', #'# What to expand __module__ into
#
text=> '$',#'# ARRAYREF: Lines of text
is_libcell=> '$',#'# True if is a library cell
has_slow=> '$',#'# True if has #sp slow
# For special procedures
_write_var=> '%',# For write() function info passing
_enums=> '$', #'# For autoenums, hash{class}{en}{def} = value
_autoenums=> '%', # For autoenums, hash{class} = en
_modules=> '%',# For autosubcell_include
_intf_done=> '$', #'# For autointf, already inserted it
_impl_done=> '$', #'# For autoimpl, already inserted it
_uses=> '%',# For #sp use
]);

###################################################################### ###################################################################### #### Read class

package SystemC::Netlist::File::Parser; use SystemC::Parser; use Carp; use strict; use vars qw (@ISA); use vars qw (@Text); # Local for speed while inside parser. @ISA = qw (SystemC::Parser);

# longest allowed user-defined string #use constant MAX_USER_STRING_LEN => 256; use constant MAX_USER_STRING_LEN => 5000;

sub new { my $class = shift; my %params = (@_); # filename=>

    # A new file; make new information
    $params{fileref} or die "%Error: No fileref parameter?";
    $params{netlist} = $params{fileref}->netlist;
    my $parser = $class->SUPER::new (%params,
                                     modref=>undef,     # Module being parsed now
                                     cellref=>undef,    # Cell being parsed now
                                     _ifdef_stack => [], # For parsing, List of outstanding ifdefs
                                     _ifdef_off => 0,   # For parsing, non-zero if not-processing
                                     );
    $parser->{filename} = $parser->{netlist}->resolve_filename($params{filename});
    if (!$parser->{filename}) {
        $params{error_self} and $params{error_self}->error("Cannot open $params{filename}\n");
        die "%Error: Cannot open $params{filename}\n";
    }
    $parser->read (filename=>$parser->{filename});
    return $parser;
}

sub logger { return $_[0]->netlist->logger; }

sub netlist { return $_[0]->{netlist}; }

sub push_text { push @Text, $_[1] if $_[0]->{need_text}; }

sub text { my $self = shift; my $line = shift; return if $self->{_ifdef_off};

    # comment lines while inside a covergroup
    if (defined $self->{parsing_covergroup} &&
       ($self->{parsing_covergroup} == 1)) {
        print "\"$line\"\n" if (defined $line && $SystemC::Netlist::Debug);
        $line =~ s#\n#\n//#g if defined $line;
    }
    push_text($self, [ 0, $self->filename, $self->lineno,
                       $line ]);
    if ($self->{netref}) {
        # Snarf comment following signal declaration
        # Note comments must begin on the same line as the signal
        if ($line =~ /^[ \t]*\/\/[ \t]*([^\n]+)/
            || $line =~ /^[ \t]*\/\*[ \t]*(.*)/) {
            my $cmt = $1;
            $cmt =~ s/\*\/.*$//;  # Strip */ ... comment endings
            $cmt =~ s/\s+/ /g;
            $self->{netref}->comment($cmt);
        }
        $self->{netref} = undef;
    }
}

sub module { my $self = shift; my $module = shift; return if $self->{_ifdef_off};

    my $fileref = $self->{fileref};
    my $netlist = $self->{netlist};
    $module = $self->{fileref}->module_exp if $module eq "__MODULE__";
    print "Module $module\n" if $SystemC::Netlist::Debug;
    $self->endmodule();   # May be previous module in file
    $self->{modref} = $netlist->new_module
        (name=>$module,
         is_libcell=>$fileref->is_libcell(),
         filename=>$self->filename, lineno=>$self->lineno);
    $fileref->_modules($module, $self->{modref});
}

sub module_continued { my $self = shift; my $module = shift; return if $self->{_ifdef_off};

    my $fileref = $self->{fileref};
    my $netlist = $self->{netlist};
    $module = $self->{fileref}->module_exp if $module eq "__MODULE__";
    print "Module_Continued $module\n" if $SystemC::Netlist::Debug;
    $self->endmodule();   # May be previous module in file
    $self->{modref} = $netlist->find_module($module);
    if (!$self->{modref}) {
        $self->error ("SP_MODULE_CONTINUED of module that doesn't exist: $module\n");
        $self->module($module);
    }
}

sub endmodule { my $self = shift; if ($#{$self->{_ifdef_stack}}>-1) { $self->error("'#sp ifdef' never terminated with '#sp endif"); } $self->_add_code_symbols($self->symbols()); $self->{modref} = undef; }

sub _add_code_symbols { my $self = shift; my $hashref = shift; return if !$self->{modref}; my $modref = $self->{modref}; if (!$modref->_code_symbols) { $modref->_code_symbols($hashref); } else { # Add to existing hash my $csref = $modref->_code_symbols; while (my ($key, $val) = each %{$hashref}) { $csref->{$key} = $val; } } }

sub auto { my $self = shift; my $line = shift;

    return if (!$self->{strip_autos});
    return if $self->{_ifdef_off};
    my $modref = $self->{modref};
    my $cellref = $self->{cellref};
    if ($line =~ /^(\s*)\/\*AUTOCTOR\*\//       # Depreciated
        || $line =~ /^(\s*)\/\*AUTOINIT\*\//) {
        if (!$modref) {
            return $self->error ("AUTOINIT outside of module definition", $line);
        }
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::Module::_write_autoinit,
                           $modref, $self->{fileref}, $1]);
    }
    elsif ($line =~ /^(\s*)\/\*AUTOSIGNAL\*\//) {
        if (!$modref) {
            return $self->error ("AUTOSIGNAL outside of module definition", $line);
        }
        $modref->_autosignal($modref->_decl_max + 10);
        $modref->_decl_max(100000000+$modref->_decl_max);  # Leave space for autos
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::Module::_write_autosignal,
                           $modref, $self->{fileref}, $1]);
    }
    elsif ($line =~ /^(\s*)\/\*AUTOSUBCELL(S|_DECL)\*\//) {
        if (!$modref) {
            return $self->error ("AUTOSUBCELL_DECL outside of module definition", $line);
        }
        $modref->_autosubcells(1);
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::Module::_write_autosubcell_decl,
                           $modref, $self->{fileref}, $1]);
    }
    elsif ($line =~ /^(\s*)\/\*AUTOSUBCELL_CLASS\*\//) {
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::File::_write_autosubcell_class,
                           $self->{fileref}, $self->{fileref}, $1]);
    }
    elsif ($line =~ /^(\s*)\/\*AUTOSUBCELL_INCLUDE\*\//) {
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::File::_write_autosubcell_include,
                           $self->{fileref}, $self->{fileref}, $1]);
    }
    elsif ($line =~ /^(\s*)\/\*AUTOINST\*\//) {
        if (!$cellref) {
            return $self->error ("AUTOINST outside of cell definition", $line);
        }
        elsif ($cellref->_autoinst()) {
            return $self->error ("AUTOINST already declared earlier for same cell", $line);
        }
        $cellref->_autoinst(1);
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::Cell::_write_autoinst,
                           $cellref, $self->{fileref}, $1]);
    }
    elsif ($line =~ /^(\s*)\/\*AUTOENUM_CLASS\(([a-zA-Z0-9_]+)(\.|::)([a-zA-Z0-9_]+)\)\*\//) {
        my $prefix = $1; my $class = $2;  my $enumtype = $4;
        $self->{fileref}->_autoenums($class, $enumtype);
        $self->{netlist}->{_enum_classes}{$class} = 1;
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::File::_write_autoenum_class,
                           $self->{fileref}, $class, $enumtype, $prefix,]);
    }
    elsif ($line =~ /^(\s*)\/\*AUTOENUM_GLOBAL\(([a-zA-Z0-9_]+)(\.|::)([a-zA-Z0-9_]+)\)\*\//) {
        my $prefix = $1; my $class = $2;  my $enumtype = $4;
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::File::_write_autoenum_global,
                           $self->{fileref}, $class, $enumtype, $prefix,]);
    }
    elsif ($line =~ /^(\s*)\/\*AUTOMETHODS\*\//) {
        my $prefix = $1;
        if (!$modref) {
            return $self->error ("AUTOMETHODS outside of module definition", $line);
        }
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::Module::_write_autodecls,
                           $modref, $self->{fileref}, $prefix]);
    }
    elsif ($line =~ /^(\s*)\/\*AUTOTRACE\(([a-zA-Z0-9_]+)((,manual)?(,recurse)?(,activity)?(,exists)?(,standalone)?(,c)?)\)\*\//) {
        my $prefix = $1; my $modname = $2; my $manual = $3;
        $modname = $self->{fileref}->module_exp if $modname eq "__MODULE__";
        my $mod = $self->{netlist}->find_module ($modname);
        $mod or $self->error ("Declaration for module not found: $modname\n");
        $mod->_autotrace('on',1);
        $mod->_autotrace('manual',1) if $manual =~ /\bmanual\b/;
        $mod->_autotrace('recurse',1) if $manual =~ /\brecurse\b/;
        $mod->_autotrace('activity',1) if $manual =~ /\bactivity\b/;
        $mod->_autotrace('exists',1) if $manual =~ /\bexists\b/;
        $mod->_autotrace('standalone',1) if $manual =~ /\bstandalone\b/;
        $mod->_autotrace('c',1) if $manual =~ /\bc\b/;
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::AutoTrace::_write_autotrace,
                           $mod, $self->{fileref}, $prefix,]);
    }
    elsif ($line =~ /^(\s*)\/\*AUTOATTR\(([a-zA-Z0-9_,]+)\)\*\//) {
        my $attrs = $2 . ",";
        $modref or $self->error ("Attribute outside of module declaration\n");
        foreach my $attr (split (",", $attrs)) {
            if ($attr eq "verilated") {
            } elsif ($attr eq "no_undriven_warning") {
                $modref->lesswarn(1);
            } elsif ($attr eq "check_outputs_used"
                     || $attr eq "check_inputs_used") {
                $modref->attributes($attr,1);
            } else {
                $self->error ("Unknown attribute $attr\n");
            }
        }
    }
    elsif ($line =~ /^(\s*)\/\*AUTOIMPLEMENTATION\*\//) {
        my $prefix = $1;
        $self->{fileref}->_impl_done(1);
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::File::_write_autoimpl,
                           $self->{fileref}, $prefix]);
    }
    elsif ($line =~ /^(\s*)\/\*AUTOINTERFACE\*\//) {
        my $prefix = $1;
        $self->{fileref}->_intf_done(1);
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::File::_write_autointf,
                           $self->{fileref}, $prefix]);
    }
    elsif ($line =~ /^(\s*)SP_AUTO_CTOR\s*;/) {
        my $prefix = $1;
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::File::_write_autoctor,
                           $self->{fileref}, $prefix, $modref]);
    }
    elsif ($line =~ /^(\s*)SP_AUTO_METHOD\(([a-zA-Z0-9_]+)\s*,\s*([a-zA-Z0-9_().]*)\)\s*;/) {
        my $prefix = $1; my $name=$2; my $sense=$3;
        if (!$modref) {
            return $self->error ("SP_AUTO_METHOD outside of module definition", $line);
        }
        $modref->new_method(name=>$name,
                            filename=>$self->filename, lineno=>$self->lineno,
                            module=>$modref,
                            sensitive=>$sense);
        foreach my $symb (split /[^a-zA-Z0-9_]+/, $sense) {
            $self->_add_code_symbols({$symb=>1});  # Track that we consume the clock, etc
        }
    }
    elsif ($line =~ /^(\s*)\/\*AUTOINOUT_MODULE\(([a-zA-Z0-9_]+)(?: *, *"([^"]*)" *, *"([^"]*)"|) *\)\*\//) {
        if (!$modref) {
            return $self->error ("AUTOINOUT_MODULE outside of module definition", $line);
        }
        !$modref->_autoinoutmod() or return $self->error("Only one AUTOINOUT_MODULE allowed per module");
        $modref->_autoinoutmod([$2,$3,$4]);
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::Module::_write_autoinout,
                           $modref, $self->{fileref}, $1]);
    }
    elsif ($line =~ /^(\s*)\/\*AUTOTIEOFF\*\//) {
        if (!$modref) {
            return $self->error ("AUTOTIEOFF outside of module definition", $line);
        }
        $modref->attributes("autotieoff",1);
        push_text($self, [ 1, $self->filename, $self->lineno,
                           \&SystemC::Netlist::Module::_write_autotieoff,
                           $modref, $self->{fileref}, $1]);
    }
    elsif ($line    =~ /^(\s*)SP_AUTO_COVER       # $1 prefix
                         (?:  inc \s* \( \s* \d+, # SP_AUTO_COVERinc(id,
                          |   \d* \s* \( )        # SP_AUTO_COVER1(
                         (?:     \s* \"([^\"]+)\" |) # What
                         (?: \s*,\s* \"([^\"]+)\" |) # File
                         (?: \s*,\s*   (\d+)      |) # Line
                         (?: \s*,\s* \"([^\"]+)\" |) # Comment
                         ()                       # Enable
                         \s* \) \s* ;/x
           || $line    =~ /^(\s*)SP_AUTO_COVER_CMT # $1 prefix
                         (?:  \d* \s* \( )        # #(
                         ()()()                   # What, File, Line
                         (?: \s* \"([^\"]+)\"  )  # Comment
                         ()                       # Enable
                         \s* \) \s* ;/x
           || $line    =~ /^(\s*)SP_AUTO_COVER_CMT_IF # $1 prefix
                         (?:  \d* \s* \( )        # #(
                         ()()()                   # What, File, Line
                         (?: \s* \"([^\"]+)\"  )  # Comment
                         \s* , \s* ([^;]+)        # Enable (should check for matching parens...)
                         \s* \) \s* ;/x
           ) {
        my ($prefix,$what,$file,$line,$cmt,$enable) = ($1,$2,$3,$4,$5,$6);
        $what = 'line' if !defined $what;
        $enable = 1 if (!defined $enable || $enable eq "");
        if (!$file) {
            $file = $self->filename; $line = $self->lineno;
        }
        $cmt ||= '';
        $modref or return $self->error ("SP_AUTO_COVER outside of module definition", $line);
        my $coverref = $modref->new_cover (filename=>$file, lineno=>$line,
                                           what=>$what, comment=>$cmt,
                                           enable=>$enable,);
        # We simply replace the existing SP_AUTO instead of adding the comments.
        if ($self->{need_text}) {
            my $last = pop @Text;
            ($last->[3] =~ /SP_AUTO/) or die "Internal %Error,"; # should have poped SP_AUTO we're replacing
            push_text($self, [ 0, $self->filename, $self->lineno, $coverref->call_text($prefix) ]);
        }
    }
    else {
        return $self->error ("Unknown AUTO command", $line);
    }
}

sub ctor { my $self = shift; my $modref = $self->{modref}; return if $self->{_ifdef_off}; $modref or return $self->error ("SC_CTOR outside of module definition\n"); $modref->_ctor(1); }

sub cell_decl { my $self = shift; my $submodname=shift; my $instname=shift; return if $self->{_ifdef_off};

    print "Cell_decl $instname\n" if $SystemC::Netlist::Debug;
    my $modref = $self->{modref};
    if (!$modref) {
        return $self->error ("SP_CELL_DECL outside of module definition", $instname);
    }
    my $instnamebase = $instname;
    if ($instnamebase =~ s/\[(.*)\]//) {        # Strip any arrays
        $modref->_cellarray($instnamebase,$1);
    }
    $modref->_celldecls($instnamebase,$submodname);
}

sub cell { my $self = shift; my $instname=shift; my $submodname=shift; return if $self->{_ifdef_off};

    print "Cell $instname\n" if $SystemC::Netlist::Debug;
    my $modref = $self->{modref};
    if (!$modref) {
        return $self->error ("SP_CELL outside of module definition", $instname);
    }
    $self->{cellref} = $modref->new_cell
        (name=>$instname,
         filename=>$self->filename, lineno=>$self->lineno,
         submodname=>$submodname);
}

sub pin { my $self = shift; my $cellname = shift; my $pin = shift; my $pinvec = shift; my $net = shift; my $netvec = shift;

    return if !$self->{need_signals};
    return if $self->{_ifdef_off};
    my $modref = $self->{modref};
    if (!$modref) {
        return $self->error ("SP_PIN outside of module definition", $pin);
    }
    # Lookup cell based on the name
    my $cellref = $modref->find_cell($cellname);
    if (!$cellref) {
        return $self->error ("Cell name not found for SP_PIN:", $cellname);
    }
    my $pinref;
    my $pinname = $pin;
    if ($pinref = $cellref->find_pin($pin)) {
        if (!defined $pinvec) {
            return $self->error ("SP_PIN previously declared, at line ".$pinref->lineno
                                 .": ".$pinref->name, $pinref->name);
        } else {
            # Multiple pins are ok if a vector, so make name unique
            $pinname .= ";".$self->lineno;
        }
    }
    $cellref->new_pin (name=>$pinname,
                       filename=>$self->filename, lineno=>$self->lineno,
                       portname=>$pin,
                       netname=>$net, );
}

sub _pin_template_clean_regexp { my $self = shift; my $regexp = shift; # Take regexp and clean it

    $regexp =~ s/^\"//;
    $regexp =~ s/\"$//;
    if ($regexp =~ /^\^/ || $regexp =~ /\$$/) {
        $self->error ("SP_TEMPLATE does not need ^/\$ anchoring",$regexp);
    }
    return $regexp;
}

sub _pin_template_check_regexp { my $self = shift; my $regexp = shift; # Take regexp and test for correctness # Return new regexp string, and compiled regexp $regexp = $self->_pin_template_clean_regexp($regexp);

    my $compiled;
    eval {
        $compiled = qr/^$regexp$/;
    };
    if (my $err = $@) {
        $err =~ s/ at .*$//;
        $self->error ("SP_TEMPLATE compile error: ",$err);
    }
    return ($regexp,$compiled);
}

sub pin_template { my $self = shift; my $cellregexp = shift; my $pinregexp = shift; my $netregexp = shift; my $typeregexp = shift || ".*"; return if $self->{_ifdef_off};

    my $modref = $self->{modref};
    if (!$modref) {
        return $self->error ("SP_TEMPLATE outside of module definition");
    }
    my ($cellre, $pinre, $netre, $typere);
    ($cellregexp,$cellre) = $self->_pin_template_check_regexp($cellregexp);
    ($pinregexp,$pinre) = $self->_pin_template_check_regexp($pinregexp);
    ($typeregexp,$typere) = $self->_pin_template_check_regexp($typeregexp);
    # Special rules for replacement
    $netregexp = $self->_pin_template_clean_regexp($netregexp);
    $modref->new_pin_template (filename=>$self->filename, lineno=>$self->lineno,
                               cellregexp => $cellregexp, cellre => $cellre,
                               pinregexp => $pinregexp, pinre => $pinre,
                               typeregexp => $typeregexp, typere => $typere,
                               netregexp => $netregexp,
                               );
}

sub _find_or_new_class { my $self = shift; if (!$self->{class}) { $self->error("Not inside a class declaration"); $self->{class} = '_undeclared'; } my $class = $self->{netlist}->find_class($self->{class}); if (!$class) { $class = $self->{netlist}->new_class (name=>$self->{class}, filename=>$self->filename, lineno=>$self->lineno); } return $class; }

sub signal { my $self = shift; my $inout = shift; my $type = shift; my $netname = shift; my $array = shift; my $msb = shift; my $lsb = shift;

    return if !$self->{need_signals};
    return if $self->{_ifdef_off};
    if ($type eq "sc_clock" && (($self->{netlist}->sc_version||0) > 20020000
                                || $self->{netlist}{ncsc})) {
        # 2.0.1 changed the basic type of sc_in_clk to a bool
        $type = "bool";
    }
    if ($array) {
        $array =~ s/^\[//;
        $array =~ s/\]$//;
    }
    my $modref = $self->{modref};
    if (!$modref && $inout eq "sp_traced") {
        $modref = $self->_find_or_new_class();
    }
    if (!$modref) {
        return $self->error ("Signal declaration outside of module definition", $netname);
    }
    if ($inout eq "sc_signal"
        || $inout eq "sc_clock"
        || $inout eq "sp_traced"
        || $inout eq "sp_traced_vl"
        ) {
        my $net = $modref->find_net ($netname);
        $net or $net = $modref->new_net
            (name=>$netname,
             filename=>$self->filename, lineno=>$self->lineno,
             sp_traced=>($inout eq "sp_traced"),
             simple_type=>($inout eq "sp_traced" || $inout eq "sp_traced_vl"),
             data_type=>$type, array=>$array,
             comment=>undef, msb=>$msb, lsb=>$lsb,
             );
        $net->_decl_order($modref->_decl_max(1+$modref->_decl_max));
        $self->{netref} = $net;
    }
    elsif ($inout =~ /vl_(inout|in|out)/) {
        my $dir = $1;
        my $net = $modref->find_net ($netname);
        $net or $net = $modref->new_net
            (name=>$netname,
             filename=>$self->filename, lineno=>$self->lineno,
             simple_type=>1, data_type=>$type, array=>$array,
             comment=>undef, msb=>$msb, lsb=>$lsb,
             );
        $self->{netref} = $net;
        my $port = $modref->new_port
            (name=>$netname,
             filename=>$self->filename, lineno=>$self->lineno,
             direction=>$dir, data_type=>$type,
             array=>$array, comment=>undef,);
    }
    elsif ($inout =~ /sc_(inout|in|out)$/) {
        my $dir = $1;
        my $net = $modref->new_port
            (name=>$netname,
             filename=>$self->filename, lineno=>$self->lineno,
             direction=>$dir, type=>$type,
             array=>$array, comment=>undef,);
        $net->_decl_order($modref->_decl_max(1+$modref->_decl_max));
        $self->{netref} = $net;
    }
    else {
        return $self->error ("Strange signal type: $inout", $inout);
    }
    # Replace our special types
    if ($type =~ /^sp_ui\b/) {
        $self->_var_decl_guts($type);
    }
}

sub covergroup_begin { my $self = shift; my $name = shift;

    return if $self->{_ifdef_off};
    return if !$self->{need_covergroup};
    print "Netlist::File: covergroup_begin parsed with name: $name\n" if $SystemC::Netlist::Debug;
    my $modref = $self->{modref};
    if (!$modref) {
        return $self->error ("SP_COVERGROUP \"$name\" outside of module definition\n");
    }
    if (length($name) > MAX_USER_STRING_LEN) {
        my $max = MAX_USER_STRING_LEN;
        return $self->error ("SP_COVERGROUP \"$name\" string too long (max $max chars)\n");
    }
    # make a new covergroup
    my $covergroupref = $modref->current_covergroup();
    # name it
    $covergroupref->name($name);
    $covergroupref->page("\"$name\""); # default page = name with quotes
    $covergroupref->lineno($self->lineno);
    my $modname = $modref->name;
    if (defined $modref->_covergroups($name)) {
        $self->error("SP_COVERGROUP name \"$name\" appears more than once in module $modname\n");
    }
    # add it to this module's list
    $modref->_covergroups($name,$covergroupref);
    $self->{parsing_covergroup} = 1;
    my $last = pop @Text;
    # comment the initial SP_COVERGROUP line
    $last->[3] =~ s/(SP_COVERGROUP)/\/\/SP_COVERGROUP/ if defined $last->[3];
    push_text($self, [ 0, $self->filename, $self->lineno, "\n//SP_COVERGROUP Begin of SystemPerl coverage group\n"]);
    push_text($self, $last);

}

sub covergroup_end { my $self = shift;

    return if $self->{_ifdef_off};
    return if !$self->{need_covergroup};
    print "Netlist::File: covergroup_end parsed\n" if $SystemC::Netlist::Debug;
    $self->{parsing_covergroup} = 0;
    my $modref = $self->{modref};
    $modref->close_new_covergroup ();
    push_text($self, [ 0, $self->filename, $self->lineno, "\n//SP_COVERGROUP End of SystemPerl coverage group\n"]);
}

sub covergroup_option { my $self = shift; my $var = shift; my $val = shift;

    return if $self->{_ifdef_off};
    return if !$self->{need_covergroup};
    print "Netlist::File: covergroup_option parsed with var = value: $var = $val\n" if $SystemC::Netlist::Debug;
    if (length($var) > MAX_USER_STRING_LEN) {
        my $max = MAX_USER_STRING_LEN;
        return $self->error ("SP_COVERGROUP \"$var\" string too long (max $max chars)\n");
    }
    my $modref = $self->{modref};
    my $currentCovergroup = $modref->current_covergroup();
    $currentCovergroup->set_option($var, $val);
}

sub covergroup_description { my $self = shift; my $desc = shift;

    return if $self->{_ifdef_off};
    return if !$self->{need_covergroup};
    print "Netlist::File: covergroup_description parsed with name: $desc\n" if $SystemC::Netlist::Debug;
    if (length($desc) > MAX_USER_STRING_LEN) {
        my $max = MAX_USER_STRING_LEN;
        return $self->error ("SP_COVERGROUP \"$desc\" string too long (max $max chars)\n");
    }
    my $modref = $self->{modref};
    if (!$modref) {
        return $self->error ("SP_COVERGROUP outside of module definition\n");
    }
    my $currentCovergroup = $modref->current_covergroup();
    $currentCovergroup->add_desc($desc);
}

sub covergroup_page { my $self = shift; my $page = shift;

    return if $self->{_ifdef_off};
    return if !$self->{need_covergroup};
    print "Netlist::File: covergroup_page parsed with name: $page\n" if $SystemC::Netlist::Debug;
    if (length($page) > MAX_USER_STRING_LEN) {
        my $max = MAX_USER_STRING_LEN;
        return $self->error ("SP_COVERGROUP \"$page\" string too long (max $max chars)\n");
    }
    my $modref = $self->{modref};
    if (!$modref) {
        return $self->error ("SP_COVERGROUP outside of module definition\n");
    }
    my $currentCovergroup = $modref->current_covergroup();
    $currentCovergroup->add_page($page);
}

sub coversample { my $self = shift; my $name = shift;

    return if $self->{_ifdef_off};
    return if !$self->{need_covergroup};
    print "Netlist::File: coversample parsed with name: $name\n" if $SystemC::Netlist::Debug;
    if (length($name) > MAX_USER_STRING_LEN) {
        my $max = MAX_USER_STRING_LEN;
        return $self->error ("SP_COVERGROUP \"$name\" string too long (max $max chars)\n");
    }
    my $modref = $self->{modref};
    if (!$modref) {
        return $self->error ("SP_COVER_SAMPLE($name) outside of module definition\n");
    }
    my %cgh = %{$modref->_covergroups};
    my $covergroupref = $cgh{$name}; # look up by name
    $covergroupref || $self->error("SP_COVER_SAMPLE($name) but no SP_COVERGROUP found with that name!\n");
    push_text($self, [ 0, $self->filename, $self->lineno, "\n//SP_COVER_SAMPLE Begin of SystemPerl coverage sample\n"]);
    push_text($self, [ 0, $self->filename, $self->lineno,
                       SystemC::Netlist::CoverGroup::covergroup_sample_text($covergroupref,"\n" )]);
    push_text($self, [ 0, $self->filename, $self->lineno, "//SP_COVER_SAMPLE End of SystemPerl coverage sample\n"]);
}

sub cross_begin { my $self = shift; my $connection = shift; my $name = shift;

    return if $self->{_ifdef_off};
    return if !$self->{need_covergroup};
    print "Netlist::File: parsed cross name: $name, connecting to $connection\n" if $SystemC::Netlist::Debug;
    if (length($name) > MAX_USER_STRING_LEN) {
        my $max = MAX_USER_STRING_LEN;
        return $self->error ("SP_COVERGROUP \"$name\" string too long (max $max chars)\n");
    }
    my $modref = $self->{modref};
    my $point = $modref->current_coverpoint();
    $point->lineno($self->lineno);
    $point->isCross(1);
    $point->name($name);
    $point->connection($connection);
}

sub cross { my $self = shift;

    return if $self->{_ifdef_off};
    return if !$self->{need_covergroup};
    my $modref = $self->{modref};
    my $point = $modref->current_coverpoint();
    # pass the rest of the args on down, with fileref added at the front
    unshift @_, $self->{fileref};
    $point->cross_build(@_);
}

sub coverpoint_begin { my $self = shift; my $connection = shift; my $name = shift;

    return if $self->{_ifdef_off};
    return if !$self->{need_covergroup};
    print "Netlist::File: coverpoint parsed point name: $name, connecting to $connection\n" if $SystemC::Netlist::Debug;
    if (length($name) > MAX_USER_STRING_LEN) {
        my $max = MAX_USER_STRING_LEN;
        return $self->error ("SP_COVERGROUP \"$name\" string too long (max $max chars)\n");
    }
    my $modref = $self->{modref};
    my $point = $modref->current_coverpoint();
    $point->lineno($self->lineno);
    $point->name($name);
    $point->connection($connection);
    $point->isCross(0);
}

sub coverpoint_window { my $self = shift; my $name = shift; my $ev1 = shift; my $ev2 = shift; my $depth = shift;

    return if $self->{_ifdef_off};
    return if !$self->{need_covergroup};
    print "Netlist::File: coverpoint parsed window name: $name\n" if $SystemC::Netlist::Debug;
    if (length($name) > MAX_USER_STRING_LEN) {
        my $max = MAX_USER_STRING_LEN;
        return $self->error ("SP_COVERGROUP \"$name\" string too long (max $max chars)\n");
    }
    my $modref = $self->{modref};
    my $point = $modref->current_coverpoint();
    $point->isWindow(1);
    $point->name($name);
    $point->event1($ev1);
    $point->event2($ev2);
    $point->windowDepth($depth);
}

sub coverpoint { my $self = shift;

    return if $self->{_ifdef_off};
    return if !$self->{need_covergroup};
    my $modref = $self->{modref};
    my $point = $modref->current_coverpoint();
    # pass the rest of the args on down, with fileref added at the front
    unshift @_, $self->{fileref};
    $point->coverpoint_build(@_);
}

sub coverpoint_end { my $self = shift;

    return if $self->{_ifdef_off};
    return if !$self->{need_covergroup};
    my $modref = $self->{modref};
    # close this one out
    $modref->close_new_coverpoint();
}

sub preproc_sp { my $self = shift; my $line = shift; if ($line=~ /^\s*\#\s*sp\s+(.*)$/) { my $cmd = $1; $cmd =~ s/\s+$//; $cmd =~ s!\s+//.*$!!; while ($cmd =~ s!\s*/\*.*?\*/!!) {} if ($cmd =~ m!(/\*|\*/)!) { $self->error("/* without terminating */ on same line not supported here"); } # Ifdef/else/etc if ($cmd =~ /^ifdef\s+(\S+)$/) { my $def = $self->{netlist}->defvalue_nowarn($1); my $enable = defined $def; push @{$self->{_ifdef_stack}}, $enable; $self->{_ifdef_off}++ if !$enable; } elsif ($cmd =~ /^ifndef\s+(\S+)$/) { my $def = $self->{netlist}->defvalue_nowarn($1); my $enable = ! defined $def; push @{$self->{_ifdef_stack}}, $enable; $self->{_ifdef_off}++ if !$enable; } elsif ($cmd =~ /^else$/) { if ($#{$self->{_ifdef_stack}}<0) { $self->error("'#sp else' outside of any '#sp ifdef"); } else { my $lastEnable = pop @{$self->{_ifdef_stack}}; $self->{_ifdef_off}-- if !$lastEnable; # my $enable = !$lastEnable; push @{$self->{_ifdef_stack}}, $enable; $self->{_ifdef_off}++ if !$enable; } } elsif ($cmd =~ /^endif$/) { if ($#{$self->{_ifdef_stack}}<0) { $self->error("'#sp endif' outside of any '#sp ifdef"); } else { my $enable = pop @{$self->{_ifdef_stack}}; $self->{_ifdef_off}-- if !$enable; } } # Those that only apply when processing elsif ($cmd =~ /^implementation$/) { return if $self->{_ifdef_off}; push_text($self, [ 0, $self->filename, $self->lineno, \&SystemC::Netlist::File::_start_implementation, $self->{fileref}, $line]); } elsif ($cmd =~ /^interface$/) { return if $self->{_ifdef_off}; push_text($self, [ 0, $self->filename, $self->lineno, \&SystemC::Netlist::File::_start_interface, $self->{fileref}, $line]); } elsif ($cmd =~ /^slow$/) { return if $self->{_ifdef_off}; $self->{fileref}->has_slow(1); push_text($self, [ 0, $self->filename, $self->lineno, \&SystemC::Netlist::File::_start_slow, $self->{fileref}, $line]); } elsif ($cmd =~ /^use/) { return if $self->{_ifdef_off}; my $origtext = ""; my $incname; my $dotted; if ($cmd =~ m/^use\s+(\S+)$/ && $cmd !~ /\"/) { $origtext = $1; $incname = $origtext; $incname = $self->{netlist}->remove_defines($incname); $dotted = 1 if $incname =~ /^\./; } elsif ($cmd =~ m/^use\s+\"([^\" \n]+)\"$/) { $origtext = $1; $incname = $origtext; } else { return $self->error("Badly formed sp use line", $line); } if (!$dotted) { $incname =~ s/\.(h|sp)$//; ($incname !~ s/(\.[a-z]+)$//) or $self->error("No $1 extensions on sp use filenames", $line); } push_text($self, [ 0, $self->filename, $self->lineno, \&SystemC::Netlist::File::_write_use, $self->{fileref}, $line, $incname, $origtext, $self->filename, $self->lineno, ]); $self->{fileref}->_uses($incname,{name=>$incname, found=>0}) if !$dotted; } elsif ($cmd =~ /^include/) { ($cmd =~ m/^include\s+\"([^\" \n]+)\"$/) or return $self->error("Badly formed sp include line", $line); return if $self->{_ifdef_off}; my $filename = $1; print "#include $filename\n" if $SystemC::Netlist::Debug; $filename = $self->{netlist}->resolve_filename($filename) or $self->error("Cannot find include $filename\n"); $self->read_include (filename=>$filename); } else { return $self->error ("Invalid sp_preproc directive",$line); } } }

sub class { my $self = shift; my $class = shift; my $inhs = shift; # Track class x { enum y ...} $class = $self->{fileref}->module_exp if $class eq "__MODULE__"; $self->{class} = $class; #print "CLASS $class INH $inhs $self->{netlist}\n" if $Debug; if ($inhs) { foreach my $inh (split /[:,]/,$inhs) { #print "INHSPLIT $class $inh\n" if $Debug; $self->{netlist}{_class_inherits}{$class}{$inh} = $self; } # See if it's really a module via inheritance _class_recurse_inherits($self, $self->{netlist}{_class_inherits}{$class}); } }

sub _class_recurse_inherits { my $self = shift; my $inhsref = shift; # Recurse inheritance tree looking for sc_modules foreach my $inh (keys %$inhsref) { #print "Class rec $self->{class} $inh\n"; if ($inh eq 'sc_module') { if (!$self->{modref} || $self->{modref}->name ne $self->{class}) { module($self,$self->{class}); } } else { _class_recurse_inherits($self,$self->{netlist}{_class_inherits}{$inh}); # Inh->inh # Clone cells/pinouts from lower modules } } }

sub enum_value { my $self = shift; my $enum = shift; my $def = shift; my $value = shift;

    if ($value =~ /^0x[0-9a-fA-F]+$/) { # convert hex number
        #print "recognized hex $str as ". (hex $str)."\n";
        $value = hex $value;
    }
    # We haven't defined a class for enums... Presume others won't use them(?)
    return if $self->{_ifdef_off};
    my $fileref = $self->{fileref};
    my $class = $self->{class} || "TOP";
    my $href = $fileref->_enums() || {};
    if (!defined $href->{$class}{$enum}) {
        $self->{_last_enum_value} = -1;  # So first enum gets '0'
    }
    # If user didn't specify a value, C++ simply increments from the last value
    if (($value||"") eq "") {
        $value = $self->{_last_enum_value}+1;
    }
    $self->{_last_enum_value} = $value;
    $href->{$class}{$enum}{$def} = $value;
    $fileref->_enums($href);
    # write this to the netlist too
    my $netlist = $fileref->netlist();
    $netlist->{_enums}{$class} = $href->{$class};
}

sub var_decl { my $self = shift; my $type = shift; # Callback from parser return if $self->{_ifdef_off}; $self->_var_decl_guts($type); }

sub _var_decl_guts { my $self = shift; my $type = shift; # Callback or expansion of type used in another call # Replace our special types if ($type =~ /^sp_ui\b/) { my $typeref = $self->netlist->find_class($type); if ($typeref && $typeref->convert_type && $self->{need_text}) { my $last = pop @Text; my $out = $typeref->sc_type; ($last->[3] =~ s!(sp_ui\s*<[^>]+>)!$out/*$1*/!g) or $self->error("%Error, can't find type $type on text line\n"); push_text($self, $last); } } else { $self->error("%Error, unexpected declaration callback on '$type'"); } }

sub error { my $self = shift; my $text = shift; my $token = shift;

    my $fileref = $self->{fileref};
    # Call Verilog::Netlist::Subclass's error reporting, it will track # errors
    my $fileline = $self->filename.":".$self->lineno;
    $fileref->error ($self, "$text\n"
                     ."%Error: ".(" "x length($fileline))
                     .": At token '".($token||"")."'\n");
}

package SystemC::Netlist::File;

###################################################################### #### Accessors

sub filename { return $_[0]->name(); } sub lineno { return 0; } sub logger { return $_[0]->netlist->logger; }

###################################################################### ###################################################################### #### Functions

sub read { my %params = (#filename => undef, append_filenames=>[], # Extra files to read and add on to current parse @_); # If error_self==0, then it's non fatal if we can't open the file.

    my $filename = $params{filename} or croak "%Error: ".__PACKAGE__."::read_file (filename=>) parameter required, stopped";
    my $netlist = $params{netlist} or croak ("Call SystemC::Netlist::read_file instead,");
    $params{strip_autos} = $netlist->{strip_autos} if !exists $params{strip_autos};
    my $filepath = $netlist->resolve_filename($filename);
    if (!$filepath) {
        return if (!$params{error_self});  # Non-fatal
        $params{error_self} and $params{error_self}->error("Cannot open $params{filename}\n");
        die "%Error: Cannot open $params{filename}\n";
    }
    print __PACKAGE__."::read_file $filepath\n" if $SystemC::Netlist::Debug;
    my $fileref = $netlist->new_file (name=>$filepath,
                                      module_exp=>(Verilog::Netlist::Module::modulename_from_filename($filepath)),
                                      is_libcell=>$params{is_libcell}||0,
                                      );
    # For speed, we use @Text instead of the accessor function
    local @SystemC::Netlist::File::Parser::Text = ();
    $params{need_text} = $netlist->{need_text} if !defined $params{need_text};
    $params{need_signals} = $netlist->{need_signals} if !defined $params{need_signals};
    $params{need_covergroup} = $netlist->{need_covergroup} if !defined $params{need_covergroup};
    $params{strip_autos} = $netlist->{strip_autos} if !defined $params{strip_autos};
    my $parser = SystemC::Netlist::File::Parser->new
        ( fileref=>$fileref,
          filename=>$filepath,  # for ->read
          strip_autos=>$params{strip_autos}||0,         # for ->read
          need_text=>$params{need_text},                # for ->read
          need_signals=>$params{need_signals},          # for ->read
          need_covergroup=>$params{need_covergroup},    # for ->read
          );
    foreach my $addfile (@{$params{append_filenames}}) {
        $parser->read(filename=>$addfile);
    }
    $fileref->text(\@SystemC::Netlist::File::Parser::Text);
    $parser->endmodule();
    return $fileref;
}

###################################################################### ###################################################################### # Linking/Dumping

sub _link { my $self = shift; foreach my $incref (values %{$self->_uses()}) { if (!$incref->{fileref}) { print "FILE LINK $incref->{name}\n" if $SystemC::Netlist::Debug; my $filename = $self->netlist->resolve_filename($incref->{name}); if (!$filename) { if (!$self->netlist->{link_read_nonfatal}) { $self->error("Cannot find module $incref->{name}\n"); } next; } $incref->{fileref} = $self->netlist->find_file($filename); if (!$incref->{fileref} && $self->netlist->{link_read}) { print " use_Link_Read ",$filename,"\n" if $Verilog::Netlist::Debug; my $filepath = $self->netlist->resolve_filename($filename) or $self->error("Cannot find module $filename\n"); (my $filename_h = $filename) =~ s/\.sp$/.h/; if (!$filepath && $self->netlist->resolve_filename($filename_h)) { # There's a .h. Just consider it as a regular #include } else { $incref->{fileref} = $self->netlist->read_file(filename=>$filepath); $incref->{fileref} or die; $self->netlist->{_relink} = 1; } } } } }

sub dump { my $self = shift; my $indent = shift||0; print " "x$indent,"File:",$self->name()," Lines:",$#{@{$self->text}},"\n"; }

sub uses_sorted { my $self = shift; # Return all uses return (sort {$a->{name} cmp $b->{name}} (values %{$self->_uses()})); }

###################################################################### ###################################################################### # WRITING

# _write locals use vars qw($_Write_Type $outputting);

sub print { shift if ref $_[0]; SystemC::Template::print (@_); } sub printf { shift if ref $_[0]; SystemC::Template::printf (@_); }

sub write { my $self = shift; ref $self or croak "%Error: Call as \$ref->".__PACKAGE__."::write, stopped"; my %params = (@_);

    $SystemC::Netlist::Verbose = 1 if $SystemC::Netlist::Debug;
    my $filename = $params{filename} or croak "%Error: ".__PACKAGE__."::write (filename=>) parameter required, stopped";
    local $_Write_Type = $params{type} || "";
    my $autos  = $params{expand_autos};
    my $program = $params{program} || __PACKAGE__;      # Allow user to override it
    foreach my $var (keys %params) {
        # Copy variables so subprocesses can see them
        $self->_write_var($var, $params{$var});
    }
    my $tpl = new SystemC::Template (ppline=>($_Write_Type),
                                     keep_timestamp=>$params{keep_timestamp},
                                     # Eval is to support pre-Verilog-Perl 3.041 w/o logger
                                     logger=>(eval { $self->logger } || undef),
                                     );
    foreach my $lref (@{$tpl->src_text()}) {
        #print "GOT LINE $lref->[1], $lref->[2], $lref->[3]";
        $tpl->print_ln ($lref->[1], $lref->[2], $lref->[3]);
    }
    local $outputting = 1;
    if ($_Write_Type) {
        my $hc = (($_Write_Type eq 'interface') && "_H"
                  || ($_Write_Type eq 'slow') && "SLOW_CPP"
                  || "_CPP");
        $tpl->printf("#ifndef _%s${hc}_\n#define _%s${hc}_ 1\n", uc $self->basename, uc $self->basename);
        $tpl->print("// This file generated automatically by $program\n");
        $tpl->printf("#include \"%s.h\"\n", $self->basename) if ($_Write_Type ne 'interface');
    }
    my $module_exp = $self->module_exp;
    foreach my $line (@{$self->text}) {
        # [autos, filename, lineno, text]
        # [autos, filename, lineno, function, args, ...]
        my $needautos = $line->[0];
        my $src_filename   = $line->[1];
        my $src_lineno = $line->[2];
        if ($autos || !$needautos) {
            my $func = $line->[3];
            if (ref $func) {
                # it contains a function and arguments to that func
                #print "$func ($line->[1], $fh, $line->[2], );\n";
                &{$func} ($line->[4],$line->[5],$line->[6],$line->[7],$line->[8],
                          $line->[9],$line->[10],$line->[11],$line->[12],);
            } else {
                my $text = $line->[3];
                if (defined $text && $outputting) {
                    # This will also substitute in strings.  This was deemed a feature.
                    $text =~ s/\b__MODULE__\b/$module_exp/g;
                    $tpl->print_ln ($src_filename, $src_lineno, $text);
                }
            }
        }
    }
    # Automatic AUTOIMPLEMENTATION/AUTOINTERFACE at end of each file
    $outputting = 1;
    if (0&&$autos && $_Write_Type eq 'interface' && !$self->_intf_done) {
        $self->_write_autointf("");
    }
    if ($autos && $_Write_Type eq 'implementation' && !$self->_impl_done) {
        $self->_write_autoimpl("");
    }
    if ($_Write_Type) {
        $tpl->print ("// This file generated automatically by $program\n");
        $tpl->printf ("#endif /*guard*/\n");
    }
    # Write the file
    $self->netlist->dependency_out ($filename);
    $tpl->write( filename=>$filename,
                 # Bug in NCSC 05.40-p004
                 absolute_filenames => $self->netlist->{ncsc},
                 );
}

sub _start_implementation { my $self = shift; my $line = shift; if ($_Write_Type) { $self->print ("//$line"); $outputting = ($_Write_Type eq 'implementation'); } else { $self->print ($line); } } sub _start_interface { my $self = shift; my $line = shift; if ($_Write_Type) { $self->print ("//$line"); $outputting = ($_Write_Type eq 'interface'); } else { $self->print ($line); } } sub _start_slow { my $self = shift; my $line = shift; if ($_Write_Type) { $self->print ("//$line"); $outputting = ($_Write_Type eq 'slow'); } else { $self->print ($line); } }

sub _write_in_slow { my $self = shift; return ($_Write_Type eq 'slow' || (!$self->has_slow && $_Write_Type eq 'implementation')); } sub _write_in_fast { my $self = shift; return ($_Write_Type eq 'implementation'); }

our $_Write_Use_Last_Filename = ""; our $_Write_Use_Last_Lineno = 0; our %_Write_Use_Did_Includes; #{include_filename}

sub _write_use { my $self = shift; my $line = shift; my $incname = shift; my $origtext = shift; my $src_filename = shift; my $src_lineno = shift; return if !$SystemC::Netlist::File::outputting;

    # Flush the duplicate #include cache any time include lines aren't adjacent
    # This way it works if there are #ifdef's around the uses
    if ($_Write_Use_Last_Filename ne $src_filename
        || ($_Write_Use_Last_Lineno != $src_lineno
            && ($_Write_Use_Last_Lineno+1) != $src_lineno)
        ) {
        %_Write_Use_Did_Includes = ();
    }
    $_Write_Use_Last_Filename = $src_filename;
    $_Write_Use_Last_Lineno = $src_lineno;
    # Output it
    if ($_Write_Type) {
        if ($incname =~ /^\./) {
            my $line = $incname;
            my $curmodref = (values %{$self->_modules})[0];
            my $path = "";
            my $top = 1;
            while ($line =~ s/^\.([^.]+)//) {
                my $subname = $1;
                $path .= ".".$subname;
                my $subcell = $curmodref && $curmodref->find_cell($subname);
                $top = 0 if $subcell;
                if ($top) {
                    # Look for a top level module with same name
                    $curmodref = $self->netlist->find_module($subname);
                } else {
                    $curmodref = $subcell->submod if $subcell;  # else error printed below
                }
                if (!$curmodref || (!$top && !$subcell)) {
                    # We put out a #error for C++ to complain about instead
                    # of us erroring, so user #ifdefs can wrap around the error
                    $self->printf("#error sp_preproc didnt find subcell of name '$subname' in sp use: $incname\n");
                    return;
                }
                $self->printf ("#include \"%-22s  // For sp use %s\n",
                               $curmodref->name.'.h"', $path)
                    if (!$_Write_Use_Did_Includes{$curmodref->name});
                $_Write_Use_Did_Includes{$curmodref->name} = 1;
                $top = 0;
            }
            $line eq "" or $self->error("Strange sp use line, leftover text '$line': $incname\n");
        } else {
            $self->printf ("#include \"%-22s  // For sp use %s\n", $incname.'.h"', $origtext)
                if (!$_Write_Use_Did_Includes{$incname});
            $_Write_Use_Did_Includes{$incname} = 1;
        }
    } else {
        $self->print ($line);
    }
}

sub _write_autointf { my $self = shift; my $prefix = shift; return if !$SystemC::Netlist::File::outputting; $self->print ("${prefix}// Beginning of SystemPerl automatic interface\n"); $self->print ("${prefix}// End of SystemPerl automatic interface\n"); }

sub _write_autoctor { my $self = shift; my $prefix = shift; my $modref = shift; return if !$SystemC::Netlist::File::outputting; $self->print ("${prefix}// Beginning of SystemPerl automatic constructors\n"); SystemC::Netlist::AutoCover::_write_autocover_ctor($self,$prefix,$modref); SystemC::Netlist::CoverGroup::_write_covergroup_ctor($self,$prefix,$modref);

    my $last_meth = "";
    foreach my $meth ($modref->methods_sorted) {
        if ($last_meth ne $meth) {
            $last_meth = $meth;
            $self->print($prefix."SC_METHOD(".$meth->name.");  // SP_AUTO_METHOD at ".$meth->fileline."\n");
        }
        if ($meth->sensitive) {
            $self->print($prefix."sensitive << ".$meth->sensitive.";  // SP_AUTO_METHOD at ".$meth->fileline."\n");
        }
    }
    $self->print ("${prefix}// End of SystemPerl automatic constructors\n");
}

sub _write_autoimpl { my $self = shift; my $prefix = shift; return if !$SystemC::Netlist::File::outputting; $self->print ("${prefix}// Beginning of SystemPerl automatic implementation\n"); foreach my $class (sort (keys %{$self->_autoenums()})) { my $enumtype = $self->_autoenums($class); $self->_write_autoenum_impl($prefix,$class,$enumtype); } foreach my $modref (values %{$self->_modules}) { SystemC::Netlist::AutoCover::_write_autocover_impl($self,$prefix,$modref); SystemC::Netlist::CoverGroup::_write_covergroup_impl($self,$prefix,$modref); } $self->print ("${prefix}// End of SystemPerl automatic implementation\n"); }

sub _write_autoenum_class { my $self = shift; my $class = shift; my $enumtype = shift; my $prefix = shift;

    return if !$SystemC::Netlist::File::outputting;
    $self->print
        ("${prefix}// Beginning of SystemPerl automatic enumeration\n"
         ."${prefix}enum ${enumtype} e_${enumtype};\n"
         ."${prefix}// Avoid the default constructor; it may become private.\n"
         ."${prefix}inline ${class} () : e_${enumtype}(static_cast<${enumtype}>(0x0 /* 0xdeadbeef */)) {};\n"
         .("${prefix}inline ${class} (${enumtype} _e)"
           ." : e_${enumtype}(_e) {};\n")
         .("${prefix}explicit inline ${class} (int _e)"
           ." : e_${enumtype}(static_cast<${enumtype}>(_e)) {};\n")
         ."${prefix}operator const char* () const { return ascii(); };\n"
         ."${prefix}operator ${enumtype} () const { return e_${enumtype}; };\n"
         ."${prefix}const char* ascii () const;\n"
         ."${prefix}${enumtype} next () const;\n"
         );
    my ($min,$max) = $self->_enum_min_max_value($class,$enumtype);
    if (defined $min && defined $max) {
        $self->print
            ("${prefix}class iterator {\n"
             ."${prefix}    ${enumtype} m_e; public:\n"
             ."${prefix}    inline iterator(${enumtype} item) : m_e(item) {};\n"
             ."${prefix}    iterator operator++();\n"
             ."${prefix}    inline operator ${class}() const { return ${class}(m_e); }\n"
             ."${prefix}    inline ${class} operator*() const { return ${class}(m_e); }\n"
             ."${prefix}};\n"
             ."${prefix}static iterator begin() { return iterator($class($min)); }\n"
             ."${prefix}static iterator end()   { return iterator($class($max+1)); }\n"
             );
    } else {
        $self->print("${prefix}// No ${class}::iterator, as some enum values are assigned from non-numerics\n");
    }
    #Can do this, but then also need setting functions...
    #foreach my $valsym (sort (keys %{$href->{$enumtype}})) {
    #    $self->print ("${prefix}bool is${valsym}() const {return e_${enumtype}==${valsym};};\n");
    #}
    $self->print ("${prefix}// End of SystemPerl automatic enumeration\n");
}

sub _write_autoenum_global { my $self = shift; my $class = shift; my $enumtype = shift; my $prefix = shift; return if !$SystemC::Netlist::File::outputting; $self->print ("${prefix}// Beginning of SystemPerl automatic enumeration\n" ."${prefix}inline bool operator== (const ${class}& lhs, const ${class}& rhs)" ." { return (lhs.e_${enumtype} == rhs.e_${enumtype}); }\n" ."${prefix}inline bool operator== (const ${class}& lhs, const ${class}::${enumtype} rhs)" ." { return (lhs.e_${enumtype} == rhs); }\n" ."${prefix}inline bool operator== (const ${class}::${enumtype} lhs, const ${class}& rhs)" ." { return (lhs == rhs.e_${enumtype}); }\n" ."${prefix}inline bool operator!= (const ${class}& lhs, const ${class}& rhs)" ." { return (lhs.e_${enumtype} != rhs.e_${enumtype}); }\n" ."${prefix}inline bool operator!= (const ${class}& lhs, const ${class}::${enumtype} rhs)" ." { return (lhs.e_${enumtype} != rhs); }\n" ."${prefix}inline bool operator!= (const ${class}::${enumtype} lhs, const ${class}& rhs)" ." { return (lhs != rhs.e_${enumtype}); }\n" ."${prefix}inline std::ostream& operator<< (std::ostream& lhs, const ${class}& rhs)" ." { return lhs << rhs.ascii(); }\n" ."${prefix}// End of SystemPerl automatic enumeration\n" ); }

sub _write_autoenum_impl { my $self = shift; my $prefix = shift; my $class = shift; my $enumtype = shift;

    $self->print
        ("${prefix}// AUTOIMPLEMENTATION: AUTOENUM($class,$enumtype)\n"
         ."${prefix}const char* ${class}::ascii () const {\n"
         ."${prefix}   switch (e_${enumtype}) {\n"
         );
    my $href = $self->_enums() || {{}};
    my $vals = $href->{$class};
    $vals = $href->{TOP} if !defined $vals;
    foreach my $valsym (sort (keys %{$vals->{$enumtype}})) {
        my $name = $valsym;
        $self->print ("${prefix}   case ${valsym}: return \"${name}\";\n");
    }
    $self->print
        ("${prefix}   default: return \"%E:BadVal:${class}\";\n"
         ."${prefix}   };\n"
         ."${prefix}}\n"
         );
    # Now the iterator
    my ($min,$max) = $self->_enum_min_max_value($class,$enumtype);
    if (defined $min && defined $max) {
        $self->print
            ("${prefix}${class}::iterator ${class}::iterator::operator++() {\n"
             ."${prefix}   switch (m_e) {\n"
             );
        my @valsyms = (sort {$vals->{$enumtype}{$a} <=> $vals->{$enumtype}{$b}}
                       (keys %{$vals->{$enumtype}}));
        my %next_values;
        my $last;
        foreach my $valname (@valsyms) {
            my $valval = $vals->{$enumtype}{$valname};
            if (!defined $last || $valval ne $vals->{$enumtype}{$last}) {
                if ($last) {
                    if ($valval == $vals->{$enumtype}{$last}+1) {
                        $next_values{inc}{$last} = "${class}(m_e + 1)";
                    } else {
                        $next_values{expr}{$last} = $valname;
                    }
                }
                $last = $valname;
            }
        }
        # Note final value isn't in next_values; the default will catch it.
        foreach my $inc ("inc", "expr") {
            my @fields = (sort keys %{$next_values{$inc}});
            for (my $i=0; $i<=$#fields; ++$i) {
                my $field = $fields[$i];
                my $next_field = $fields[$i+1];
                $self->printf ("${prefix}   case %s:",$field);
                if ($next_field && $next_values{$inc}{$field} eq $next_values{$inc}{$next_field}) {
                    $self->printf (" /*FALLTHRU*/\n");
                } else {
                    $self->printf (" m_e=%s; return *this;\n"
                                   ,$next_values{$inc}{$field});
                }
            }
        }
        $self->print
            ("${prefix}   default: m_e=$class($max+1); return *this;\n"
             ."${prefix}   }\n"
             ."${prefix}}\n"
             );
    }
}

sub _enum_min_max_value { my $self = shift; my $class = shift; my $enumtype = shift; # Return (minvalue, maxvalue) for enumeration if it only is # assigned to numbers, else return undef. # Also, convert any hex values to decimal.

    my $href = $self->_enums() || {{}};
    my $vals = $href->{$class};
    $vals = $href->{TOP} if !defined $vals;
    my $min;
    my $max;
    foreach my $valsym (sort (keys %{$vals->{$enumtype}})) {
        my $val = $vals->{$enumtype}{$valsym};
        if ($val =~ /^\d+$/) {
        } elsif ($val =~ /^0x([a-f0-9]+)$/i) {
            $val = hex $1;
        } else {
            return undef;
        }
        $vals->{$enumtype}{$valsym} = $val;
        $min = $val if !defined $min || $val<$min;
        $max = $val if !defined $max || $val>$max;
    }
    return ($min,$max);
}

sub _cells_in_file { my $fileref = shift; my %cells; foreach my $modref (values %{$fileref->_modules}) { foreach my $cellref ($modref->cells_sorted) { $cells{$cellref->submodname} = $cellref; } } return (sort {$a->submodname cmp $b->submodname} (values %cells)); }

sub _write_autosubcell_class { my $self = shift; my $fileref = shift; my $prefix = shift; return if !$SystemC::Netlist::File::outputting; $fileref->print ("${prefix}// Beginning of SystemPerl automatic subcell classes\n"); foreach my $cellref ($fileref->_cells_in_file) { $fileref->printf ("%sclass %-21s // For %s.%s\n" ,$prefix,$cellref->submodname.";" ,$cellref->module->name, $cellref->name); } $fileref->print ("${prefix}// End of SystemPerl automatic subcell classes\n"); }

sub _write_autosubcell_include { my $self = shift; my $fileref = shift; my $prefix = shift; return if !$SystemC::Netlist::File::outputting; $fileref->print ("${prefix}// Beginning of SystemPerl automatic implementation includes\n"); foreach my $modref (values %{$fileref->_modules}) { SystemC::Netlist::AutoCover::_write_autocover_incl($self,$prefix,$modref); } foreach my $cellref ($fileref->_cells_in_file) { $fileref->printf ("#include \"%-22s // For %s.%s\n" ,$self->netlist->remove_defines($cellref->submodname).".h\"" ,$cellref->module->name, $cellref->name); } $fileref->print ("${prefix}// End of SystemPerl automatic implementation includes\n"); }

###################################################################### #### Package return 1; __END__


SystemC::Netlist::File

NAME

SystemC::Netlist::File - File containing SystemC code

SYNOPSIS

  use SystemC::Netlist;
  my $nl = new SystemC::Netlist;
  my $fileref = $nl->read_file (filename=>'filename');
  $fileref->write (filename=>'new_filename',
                   expand_autos=>1,);

DESCRIPTION

SystemC::Netlist::File allows SystemC files to be read and written.


ACCESSORS

$self->basename

The filename of the file with any path and . suffix stripped off.

$self->name

The filename of the file.

MEMBER FUNCTIONS

$self->dump

Prints debugging information for this file.

$self->read

Generally called as $netlist->read_file. Pass a hash of parameters. Reads the filename=> parameter, parsing all instantiations, ports, and signals, and creating SystemC::Netlist::Module structures. The optional preserve_autos=> parameter prevents default ripping of /*AUTOS*/ out for later recomputation.

$self->write

Pass a hash of parameters. Writes the filename=> parameter with the contents of the previously read file. If the expand_autos=> parameter is set, /*AUTO*/ comments will be expanded in the output. If the type=> parameter is set to 'implementation', 'interface' or 'slow', only that type of code will be written.

SEE ALSO

SystemC::Netlist


SystemC::Netlist::Method

NAME

SystemC::Netlist::Method - Methods in a file

SYNOPSIS

  use SystemC::Netlist;

DESCRIPTION

SystemC::Netlist::Method contains information on a method added with SP_AUTO_METHOD.

ACCESSORS

$self->name

The method name.

$self->sensitive

The sensitivity list of the method.

MEMBER FUNCTIONS

$self->dump

Prints debugging information for this file.

SEE ALSO

SystemC::Netlist


SystemC::Netlist::Module

NAME

SystemC::Netlist::Module - Module on a SystemC Cell

DESCRIPTION

This is a superclass of Verilog::Netlist::Module, derived for a SystemC netlist pin.

SEE ALSO

Verilog::Netlist::Module Verilog::Netlist SystemC::Netlist


SystemC::Netlist::Net

NAME

SystemC::Netlist::Net - Net for a SystemC Module

DESCRIPTION

This is a superclass of Verilog::Netlist::Net, derived for a SystemC netlist pin.

SEE ALSO

Verilog::Netlist::Net SystemC::Netlist Verilog::Netlist


SystemC::Netlist::Pin

NAME

SystemC::Netlist::Pin - Pin on a SystemC Cell

DESCRIPTION

This is a superclass of Verilog::Netlist::Pin, derived for a SystemC netlist pin.

SEE ALSO

Verilog::Netlist::Pin Verilog::Netlist SystemC::Netlist


SystemC::Netlist::Port

NAME

SystemC::Netlist::Port - Port for a SystemC Module

DESCRIPTION

This is a superclass of Verilog::Netlist::Port, derived for a SystemC netlist port.

SEE ALSO

Verilog::Netlist::Port Verilog::Netlist SystemC::Netlist


SystemC::Parser

NAME

SystemC::Parser - Parse SystemC Files

SYNOPSIS

    package Trialparser;
    @ISA = qw(SystemC::Parser);
    sub module {
        my $self = shift;
        my $module = shift;
        print $self->filename.":".$self->lineno().": ";
        print "Contains the module declaration for $module\n";
    }
    package main;
    my $sp = Trialparser->new();
    $sp->read ("test.sp");

DESCRIPTION

SystemC::Parser reads SystemC files, and parses out the netlist interconnectivity and other information. As the tokens are recognized, callbacks are invoked. Note that the parser is designed to work on UNPREPROCESSED files.

MEMBER FUNCTIONS

$self->new()

Creates a new parser element.

$self->read(filename)

Reads the filename and invokes necessary callbacks.

$self->read_include(filename)

When called from inside a read() callback function, switches to the specified include file. The EOF of the include file will automatically switch back to the original file.

ACCESSOR FUNCTIONS

$self->filename()

Returns the filename of the most recently returned object. May not match the filename passed on the command line, as #line directives are honored.

$self->lineno()

Returns the line number at the beginning of the most recently returned object.

$self->symbols()

Returns hash reference to list of all symbols with line number the symbol first was encountered on. (The hash is created instead of invoking a callback on each symbol for speed reasons.) Keywords may also be placed into the symbol table, this behavior may change.

CALLBACKS

$self->auto (text)

Auto is called with the text matching /*AUTOINST*/, etc.

$self->cell (instance, type)

Cell is called when SP_CELL is recognized. Parameters are the instance and type of the cell.

$self->cell_decl (type, instances)

Cell_decl is called when SP_CELL_DECL is recognized. Parameters are the type and instances of the cell. (Note the parameter order is opposite that of cell().)

$self->ctor (modulename)

Ctor is called when CP_CTOR is recognized. Parameter is the modulename.

$self->enum_value (enum_type, enum_name, enum_value)

Enum value is called with the enum type and name for a enumeration value. If in the file, the enumeration value (=n) is given.

$self->error (error_text, token)

Error is called when the parser hits a error. Token is the last unparsed token, which often gives a good indication of the error position.

$self->module (modulename)

Module is called when SC_MODULE is recognized.

$self->pin (cell, pin, pin_bus, signal, signal_bus)

Pin is called when SP_PIN is recognized.

$self->preproc_sp (text)

Preproc is called when a #sp line is recognized.

$self->signal (type, type_bus,name,name_bus)

Signal is called on port declarations or sc_signal declarations. The busses are any [] subscripts after the type names.

$self->text (text)

Text is called for all text not otherwise recognized. Dumping all text to a file will produce the original file, minus any #sp and stripped // Auto inserted comments.

$self->var_decl (type)

Var_decl is called on a variable declaration of sp_ui and any future SystemPerl types outside of other callbacks.

SEE ALSO

SystemC::Manual


SystemC::Template

NAME

SystemC::Template - Replace text in a file with new text

SYNOPSIS

  use SystemC::Template;
  my $tpl = new SystemC::Template;
  $tpl->read (filename=>'filename',
              ppline=>1,
              );
  $tpl->print_ln ("newfilename", 100, "inserted: This is line 100 of newfile\n");
  foreach my $lref (@{$tpl->src_text()}) {
    $tpl->print_ln ($lref->[1], $lref->[2], $lref->[3]);
  }
  $tpl->write (filename=>'new_filename',);

DESCRIPTION

SystemC::Template is the class that SystemC uses to read files and write the file .sp files and expand the contents of them.

It is similar to Text::Template, but uses arrays for speed, understands how to create #line comments for the C preprocessor, and not to write the file if nothing has changed.

First $read is called, which loads the $self->src_text() as a array of [$self, filename, lineno, text] structures. The external code then manipulates this array and loads $self->out_text() probably using $self->printf(). $self->write() is then called to write the results.

For convenience, most methods can be called as non-method calls, this will use the template that was most recently called with write. (This enables functions to simply call SystemC::Template::print and not need to pass the class around.)

ACCESSORS

$self->name

The filename read.

$self->ppline

Insert #line comments for GCC. If set to 'basename' strip the directory off the filename.

MEMBER FUNCTIONS

$self->read

Pass a hash of parameters. Reads the filename=> filename parameter and loads the internal structures.

$self->write

Pass a hash of parameters. Writes the filename=> parameter with the contents of the out_text() array.

$self->print_ln (filename, lineno, text...)

Adds to the out_text the filename, line and given text.

$self->print (text...)

Adds to the out_text the given text.

$self->printf (format, params...)

Adds to the out_text the given formatted text.

SEE ALSO

SystemC::Netlist, SystemC::Netlist::File, Text::Template