Cosimulating with SystemC Models

You can simulate Verilog and SystemC models together using CvSDL and SystemC libraries in a C++ application program. You need to use versions SystemC 2.1 and CvSDL 0.4.275 or later. CvSDL and SystemC models simulate on a same OS-managed thread by default for high performance.

 Basic Steps

The basic steps you need to follow are:

  1. Write SystemC modules as you normally do.

  2. Write Verilog modules as you normally do except for SystemC inteface ports; they need to be declared as module-local regs not as ports. Translate them to equivalent CvSDL modules using hdl2cvsdl. Bidirectional signals need to be split into unidirectional signals (maybe using a wrapper).

  3. Write a SystemC module as the interface module between SystemC and CvSDL domains.

  4. Instantiate SystemC and CvSDL modules, connect them up, compile and simulate.

 Interface Module

The interface module presented here is one of more than a few possible ways to implement cosimulation functionality between SystemC and CvSDL.

The module supports a primary-secondary simulator configuration. SystemC is prirmary and CvSDL is secondary. The primary simulator provides interface clocks and time-advancement function. The secondary simulator can still correctly handle any time delay events, unrelated to interface clocks, within its domain. For example it can have its own locally-generated clocks.

The module supports a clock-synchronous interface, which means that

  1. interface signals are associated with a clock (interface clock) that is passed from SystemC to CvSDL,

  2. all signals from SystemC to CvSDL (defined as inputs) are sampled right before the interface clock edges, and

  3. all signals to SystemC from CvSDL models (defined as outputs) are from flipflops triggered by the interface clock in CvSDL.

One caveat is that because inputs are sampled right before the clock edges, associated CvSDL signals will not follow the original input transitions in precise timing, which might confuse you at first, though there is no functional problem. Bidirectional signals need to be split into unidirectional signals beforehand.

The interface module may be structured as follows:

SC_MODULE(cvsc_if)
{
  // Define ports that connect to the rest of 
  // SystemC design.
  ....

  // Define local variables and pointers to CvSDL port
  // signals to be imported (see ImportCvSDL() below).
  ...

  // Provide a function to import CvSDL port signals.
  // This function should be called during elaboration.
  void ImportCvSDL(TCvSdl*)
  {
    // Find signals in the CvSDL design hierarchy 
    // and save the C++ pointers to them.
  }

  // Define as many process-functions as are unique 
  // interface clocks.
  void f0() {...}
  ...

  // Interface module constructor.
  SC_CTOR(cvsc_if)
  {
    // Define process-functions as SC_THREADs.
    // Each thread should be sensitive to both the 
    // positive and negative edges of the associated 
    // clock.
    SC_THREAD(f0);
    sensitive << clk0;
    ...
  }
}; 

 Importing CvSDL Ports

There are two tasks that the import function (named ImportCvSDL in the example above) needs to perform. One is to obtain the pointers (TCvVar*) to the SystemC port variables in CvSDL (declared as just regs) and save them as interface module variables. The other task is to describe each interface clock to the CvSDL simulator.

To get a pointer to a SystemC port, use the hierarchical name to the port in the GetVar() call. For example,

port_a=cvsdl->GetVar(“top1.port_a”);

If there is no such port, 0 is returned.

Note that CvSDL

Each interface clock is assumed to be a 2-state (0/1) signal that toggles between the states forever. The first state is referred to as the phase 0 (ph0) and the second the phase 1 (ph1). ph0 can be 0 or 1 and ph1 can be 1 or 0, respectively. There can be a delay (called the offset) from time 0 to the very first phase 0 state. All these values are measured in the time unit you specify. The possible values for the time unit follows the Verilog specification, namely 0 is 1 s, -1 is 100 ms, .., -6 is 1 us, ..., -9 is 1 ns, ..., -12 is 1 ps, etc.

Use the TCvVar member function, SetupClock, to specify these values.

TCvVar::SetupClock(int ph0, int ph1, int offset, int timeunit);

For example, if a clock is defined in SystemC as

sc_clock clk("clk",60,SC_PS,0.75,10,SC_PS,true);

and if the pointer to the associated CvSDL clock is cv_clk, you specify it as

cv_clk->SetupClock(45,15,10,-12); 

The ph0 and ph1 values of a clock are deduced from the value used in the very first clock tick sent to the CvSDL simulator.

 Interface Process

The interface-clock-sensitive functions defined as SC_THREADs in the interface module are referred to as interface processes. Each interface process should handle certain events in the order shown below:

At the start,

  1. Wait one delta to make sure initial input values become stable

wait(0, SC_PS);

In an infinite loop, process the steps below.

  1. Sample all inputs and update the CvSDL signals associated with them.

*cv_cnt0_in=cnt.to_int();
  1. Send a clock tick to CvSDL. Use TCvVar::UpdateClock(int)

cv_clk0->UpdateClock(clk0.read());
  1. Wait for 1 simtime (the smallest time increment) to avoid potenial race conditions in outputs.

wait(1, SC_PS);
  1. Sample all outputs and update the SystemC signals associated with them.

cnt0_out=cv_cnt0_out->GetInt();
  1. Wait for the next clock edge.

wait();

 SystemC Top-Level Routine

In the SystemC top-level routine, declare all SystemC top-level signals, instantiate all top-level SystemC modules, the SystemC-CvSDL interface module and connect them as you normally do.

Then follow the steps below to include a CvSDL simulator and translated Verilog modules:

  1. Instantiate a CvSDL simulator by calling CreateCvSim(). Save the returned TCvSdl pointer.

TCvSdl *cvsdl=CreateCvSim();
  1. Setup the CvSDL simulator timescale to the smallest of the interface clocks (or even smaller).

cvsdl->TimeScale(“1 ps /1 ps”);
  1. Register all CvSDL top-level components.

cvsdl->CV_COMPONENT(my_top(“vtop”));
  1. Call the ImportCvSDL function of the interface module.

cvsc_if.ImportCvSDL(cvsdl);

The rest of the top-level routine may include calling trace functions, starting a simulation and cleaning up after the simulation, and so forth.

 Example

VERILOG FILE

cvsdl_test.v

 

// cvsdl_test is the only Verilog module
//   - two up and down counters with 2 clocks
//   - inputs come from SystemC 
//   - outputs go to SystemC
//   - clocks and resets also come from SystemC
//   - if interface malfunctions, counter
//     values would be wrong.

module cvsdl_test;
// systemC ports
  reg               clk0, clk1;
  reg               rst0_n, rst1_n;
  reg signed [31:0] cnt0_in, cnt1_in;
  reg signed [31:0] cnt0_out, cnt1_out;


// local
  reg        clk2;
  reg [31:0] cnt2;
  initial
  begin // test local clock
    #10 clk2=0;
    cnt2=0;
    forever #110 clk2=~clk2;
  end
  always @(posedge clk2)
    cnt2<=#90 cnt2+1;
  always @(posedge clk0)
  begin
    if (!rst0_n)
      cnt0_out <= 0;
    else
      cnt0_out <= cnt0_in+1;
  end

  always @(posedge clk1)
  begin
    if (~rst1_n)
      cnt1_out <= 0;
    else
      cnt1_out <= cnt1_in-1;
  end

  initial
  begin
    $dumpfile("cv_dump.vcd");
    $dumpvars();
    $monitor($stime, ": clk0=%b, clk1=%b, \
rst0_n=%b, rst1_n=%b, cnt0_out=%0d, cnt1_out=%0d",
clk0, clk1, rst0_n, rst1_n, cnt0_out, cnt1_out);
  end
endmodule

SYSTEMC FILES

RESET_N GENERATION

sysc_util.h

sysc_util.cpp

#ifndef sysc_utilH
#define sysc_utilH
#include "systemc.h"

SC_MODULE(sysc_util)
{
  sc_out<bool> rst_n;

  void gen_reset();

  SC_CTOR(sysc_util)
  {
    SC_THREAD(gen_reset);
  }
};
#endif
#include "sysc_util.h"

void sysc_util::gen_reset()
{
  rst_n=true;

  wait(125, SC_PS);

  rst_n = false;

  wait(200, SC_PS);

  rst_n = true;
};
COUNTERS

sysc_cnt.h

sysc_cnt.cpp

#ifndef sysc_cntH
#define sysc_cntH
#include "systemc.h"

SC_MODULE(sysc_cnt)
{ // two counters clocked by clk0 and clk1
  sc_in<bool> clk0, clk1, rst_n;
  sc_in<sc_int<32> > cnt0_in, cnt1_in;
  sc_out<sc_int<32> > cnt0_out, cnt1_out;

  void count_up();
  void count_down();

  sc_int<32> cnt0, cnt1;

  SC_CTOR(sysc_cnt)
  {
    SC_THREAD(count_up);
    sensitive_pos << clk0;
    SC_CTHREAD(count_down, clk1);
  }
};
#endif
#include "sysc_cnt.h"

void sysc_cnt::count_up()
{
  while (true)
  {
    cnt0=cnt0_in.read();
    if (rst_n==false)
      cnt0_out=0;
    else
      cnt0_out=cnt0+1;
    wait();
  }
}
void sysc_cnt::count_down()
{
  while (true)
  {
    cnt1=cnt1_in.read();
    if (rst_n==false)
      cnt1_out=0;
    else
      cnt1_out=cnt1-1;
    wait();
  }
}
SYSTEMC-CVSDL INTEFACE MODULE

cvsc_if.h

 

#ifndef cvsc_ifH
#define cvsc_ifH
#include "cv_types.h"
#include "systemc.h"
SC_MODULE(cvsc_if)
{
  sc_in<bool> clk0, clk1, rst_n;
  sc_in<sc_int<32> > cnt0_in, cnt1_in;
  sc_out<sc_int<32> > cnt0_out, cnt1_out;
  sc_int<32> cnt;

  TCvVar *cv_rst0_n, *cv_rst1_n;
  TCvVar *cv_clk0, *cv_clk1;
  TCvVar *cv_cnt0_in, *cv_cnt0_out;
  TCvVar *cv_cnt1_in, *cv_cnt1_out;
  bool ImportCvSDL(TCvSdl*);
  void if_proc0();
  void if_proc1();

  SC_CTOR(cvsc_if)
  {
    SC_THREAD(if_proc0);
    sensitive << clk0;
    SC_THREAD(if_proc1);
    sensitive << clk1;
  }
};
#endif



cvsc_if.cpp

 

#include "cvsc_if.h"

bool cvsc_if::ImportCvSDL(TCvSdl* cvsdl)
{ // if connections fail, return true.
  cv_rst0_n=cvsdl->GetVar("cv_test.rst0_n");
  cv_rst1_n=cvsdl->GetVar("cv_test.rst1_n");
  cv_clk0=cvsdl->GetVar("cv_test.clk0");
  cv_clk1=cvsdl->GetVar("cv_test.clk1");
  cv_cnt0_in =cvsdl->GetVar("cv_test.cnt0_in");
  cv_cnt0_out=cvsdl->GetVar("cv_test.cnt0_out");
  cv_cnt1_in =cvsdl->GetVar("cv_test.cnt1_in");
  cv_cnt1_out=cvsdl->GetVar("cv_test.cnt1_out");
  if (cv_rst0_n==0 || cv_rst1_n==0
    || cv_clk0==0 || cv_clk1==0
    || cv_cnt0_in==0 || cv_cnt0_out==0
    || cv_cnt1_in==0 || cv_cnt1_out==0)
  {
    cout << "cvsdl interface failed; can't find CvSDL\
 variables\n";
    return true;
  }
  // setup clocks
  // clk0("clk0", 100, SC_PS);
  cv_clk0->SetupClock(50,50,0,-12);
  // clk1("clk1", 60,SC_PS, 0.75,10,SC_PS,true);
  cv_clk1->SetupClock(45,15,10,-12);
  return false;
}
void cvsc_if::if_proc0()
{
  wait(0, SC_PS);
  while (1)
  {
    *cv_rst0_n=rst_n.read();
    cnt=cnt0_in.read();
    *cv_cnt0_in=cnt.to_int();
    cv_clk0->UpdateClock(clk0.read());
    wait(1, SC_PS);
    cnt0_out=cv_cnt0_out->GetInt();
    wait();
  }
}
void cvsc_if::if_proc1()
{
  wait(0, SC_PS);
  while (1)
  {
    *cv_rst1_n=rst_n.read();
    cnt=cnt1_in.read();
    *cv_cnt1_in=cnt.to_int();
    cv_clk1->UpdateClock(clk1.read());
    wait(1, SC_PS);
    cnt1_out=cv_cnt1_out->GetInt();
    wait();
  }
}
MAIN FILE

sysc_main.cpp

 

#include "sysc_util.h"
#include "sysc_cnt.h"
#include "cvsc_if.h"
#include "cvsdl_test.h"
int main(int argc, char** argv)
{
  cout << "\n"<< sc_copyright() << "\n";
  cout << sc_version() <<  "\n\n";

  sc_clock clk0("clk0", 100, SC_PS);
  sc_clock clk1("clk1", 60,SC_PS, 0.75,10,SC_PS,true);
  sc_signal<bool> rst_n;

  sysc_util util("util");
  util.rst_n(rst_n);


  // two counters, sc_cnt0 & sc_cnt1 on systemc side;
  sc_signal<sc_int<32> > sc_cnt0, sc_cnt1;
  // two counters, cv_cnt0 & cv_cnt1 on cvsdl side;
  sc_signal<sc_int<32> > cv_cnt0, cv_cnt1;

  sysc_cnt counters("counter");
  counters.clk0(clk0);
  counters.clk1(clk1);
  counters.rst_n(rst_n);
  counters.cnt0_out(sc_cnt0);
  counters.cnt0_in(cv_cnt0);
  counters.cnt1_out(sc_cnt1);
  counters.cnt1_in(cv_cnt1);

  // CvSDL models
  TCvSdl* cvsdl=CreateCvSim();
  cvsdl->TimeScale("1ps/1ps");
  cvsdl->CV_COMPONENT(new cvm_cvsdl_test("cv_test"));
  // SystemC-CvSDL interface
  cvsc_if cvsc_if("cvsc_if");
  cvsc_if.clk0(clk0);
  cvsc_if.clk1(clk1);
  cvsc_if.rst_n(rst_n);
  cvsc_if.cnt0_out(cv_cnt0);
  cvsc_if.cnt0_in(sc_cnt0);
  cvsc_if.cnt1_out(cv_cnt1);
  cvsc_if.cnt1_in(sc_cnt1);

  cvsc_if.ImportCvSDL(cvsdl);

  // VCD dump
  sc_trace_file *tf = 
  sc_create_vcd_trace_file("sc_dump");

  sc_trace(tf, clk0.signal(), "clk0");
  sc_trace(tf, clk1.signal(), "clk1");
  sc_trace(tf, rst_n, "rst_n");
  sc_trace(tf, sc_cnt0, "sc_cnt0");
  sc_trace(tf, sc_cnt1, "sc_cnt1");
  sc_trace(tf, cv_cnt0, "cv_cnt0");
  sc_trace(tf, cv_cnt1, "cv_cnt1");


  // Start simulation
  sc_start(1, SC_NS);

  sc_close_vcd_trace_file(tf);

  DeleteCvSim(cvsdl);

  return 0;
}

COMPILE and RUN

  1. Translate Verilog files into CvSDL files.

hdl2cvsdl cvsdl_test.v
  1. Compile c++ files

// VC++7 or VC++8
cl /EHsc /I\cvsdl\lib /I\systemc\src \
sysc_main.cpp sysc_util.cpp sysc_cnt.cpp cvsc_if.cpp \
cvsdl_test.cpp systemc.lib cvsdl_vc7.lib 
// g++ on GNU/Linux x86
g++ -osysc_main -I~/cvsdl/lib -I~/systemc/src \
sysc_main.cpp sysc_util.cpp sysc_cnt.cpp cvsc_if.cpp \
cvsdl_test.cpp libsystemc.a libcvsdl_g323.so
  1. Simulate

> sysc_main

OUTPUT

Both the sysc_main.cpp and cvsdl_test.v generate a VCD dump file, sc_dump.vcd and cv_dump.vcd. You can verify the cosimulation by looking at the waveforms. You should also see, on the console, the following SystemC and CvSDL messages and dump from the $monitor statement in cvsdl_test.v:

        Copyright (c) 1996-2004 by all Contributors
                    ALL RIGHTS RESERVED
             SystemC 2.1_oct_12_04.beta --- Aug 12 2005 18:19:14

Copyright(C) 2003-2005, Tenko Technologies Inc. All rights reserved.
CvSDL Simulator (version 0.4.275)

simulator timescale 1 ps
simulation started
August 12, 2005 23:11:20
         0: clk0=1, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=1, cnt1_out=x
WARNING: Default time step is used for VCD tracing.
        10: clk0=1, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=1, cnt1_out=-1
        50: clk0=0, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=1, cnt1_out=-1
        55: clk0=0, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=1, cnt1_out=-1
        70: clk0=0, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=1, cnt1_out=-2
       100: clk0=1, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=2, cnt1_out=-2
       115: clk0=1, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=2, cnt1_out=-2
       130: clk0=1, clk1=1, rst0_n=1, rst1_n=0, cnt0_out=2, cnt1_out=0
       150: clk0=0, clk1=1, rst0_n=0, rst1_n=0, cnt0_out=2, cnt1_out=0
       175: clk0=0, clk1=0, rst0_n=0, rst1_n=0, cnt0_out=2, cnt1_out=0
       190: clk0=0, clk1=1, rst0_n=0, rst1_n=0, cnt0_out=2, cnt1_out=0
       200: clk0=1, clk1=1, rst0_n=0, rst1_n=0, cnt0_out=0, cnt1_out=0
       235: clk0=1, clk1=0, rst0_n=0, rst1_n=0, cnt0_out=0, cnt1_out=0
       250: clk0=0, clk1=1, rst0_n=0, rst1_n=0, cnt0_out=0, cnt1_out=0
       295: clk0=0, clk1=0, rst0_n=0, rst1_n=0, cnt0_out=0, cnt1_out=0
       300: clk0=1, clk1=0, rst0_n=0, rst1_n=0, cnt0_out=0, cnt1_out=0
       310: clk0=1, clk1=1, rst0_n=0, rst1_n=0, cnt0_out=0, cnt1_out=0
       350: clk0=0, clk1=1, rst0_n=1, rst1_n=0, cnt0_out=0, cnt1_out=0
       355: clk0=0, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=0, cnt1_out=0
       370: clk0=0, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=0, cnt1_out=-1
       400: clk0=1, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=1, cnt1_out=-1
       415: clk0=1, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=1, cnt1_out=-1
       430: clk0=1, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=1, cnt1_out=-2
       450: clk0=0, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=1, cnt1_out=-2
       475: clk0=0, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=1, cnt1_out=-2
       490: clk0=0, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=1, cnt1_out=-3
       500: clk0=1, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=2, cnt1_out=-3
       535: clk0=1, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=2, cnt1_out=-3
       550: clk0=0, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=2, cnt1_out=-4
       595: clk0=0, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=2, cnt1_out=-4
       600: clk0=1, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=3, cnt1_out=-4
       610: clk0=1, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=3, cnt1_out=-5
       650: clk0=0, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=3, cnt1_out=-5
       655: clk0=0, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=3, cnt1_out=-5
       670: clk0=0, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=3, cnt1_out=-6
       700: clk0=1, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=4, cnt1_out=-6
       715: clk0=1, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=4, cnt1_out=-6
       730: clk0=1, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=4, cnt1_out=-7
       750: clk0=0, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=4, cnt1_out=-7
       775: clk0=0, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=4, cnt1_out=-7
       790: clk0=0, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=4, cnt1_out=-8
       800: clk0=1, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=5, cnt1_out=-8
       835: clk0=1, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=5, cnt1_out=-8
       850: clk0=0, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=5, cnt1_out=-9
       895: clk0=0, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=5, cnt1_out=-9
       900: clk0=1, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=6, cnt1_out=-9
       910: clk0=1, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=6, cnt1_out=-10
       950: clk0=0, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=6, cnt1_out=-10
       955: clk0=0, clk1=0, rst0_n=1, rst1_n=1, cnt0_out=6, cnt1_out=-10
       970: clk0=0, clk1=1, rst0_n=1, rst1_n=1, cnt0_out=6, cnt1_out=-11




Trademarks and registered trademarks are property of the respective owners.
CvSDL is a trademark of Tenko Technologies Inc.
Copyright © 2005 Tenko Technologies Inc. All rights reserved. Last modified on 08/17/05.