Jump to content

Non-blocking assignment in systemC


acc_sysC

Recommended Posts

How do I write the below testbench which has non-blocking assignments in systemC?

The entire model in systemC is written using SC_METHODs

I tried to write the below testbench(stimulus) using SC_THREAD since I could use wait(clk.posedge_event()) in place of @(posedge clk)

I am able to see the stimulus correctly same as RTL in the waveforms from VCD file. But will this work same as non-blocking assignment in verilog. I have this question because I know non-blocking assignment in systemC can be done using SC_METHOD().

 

Please find the systemC code I wrote in the attachment

Please advice.

 


module test;

    reg [8-1 : 0] A;
    reg [8-1 : 0] B;
    reg [256-1 : 0] FUNC;
    reg [8-1 : 0] FUNC_ADDR;
    reg [9-1 : 0] RUN;
    reg [9-1 : 0] LOAD_CORE;
    reg [18-1 : 0] SRC_MODE;
    reg [18-1 : 0] IN_MODE;
    reg [36-1 : 0] AIN_ADDR;
    reg [36-1 : 0] BIN_ADDR;
    reg [4-1 : 0] Y_ADDR;
    reg clk;
    reg reset;
    
    wire [8-1 : 0] Y;

PIM_Cluster top(
    .A(A),
    .B(B),
    .FUNC(FUNC),
    .FUNC_ADDR(FUNC_ADDR),
    .RUN(RUN),
    .LOAD_CORE(LOAD_CORE),
    .SRC_MODE(SRC_MODE),
    .IN_MODE(IN_MODE),
    .AIN_ADDR(AIN_ADDR),
    .BIN_ADDR(BIN_ADDR),
    .Y_ADDR(Y_ADDR),
    .clk(clk),
    .reset(reset),
    .Y(Y)
);
    integer n = 16;
    integer index = 0;
    integer a = 0;
    integer b = 0;
    integer kk = 0;
    integer qq =0;
    integer ii = 0;

    
    integer add = 0;
    integer mult = 0;
    
    reg [2048-1:0] FUNC_MULT;
    reg [2048-1:0] FUNC_ADD;
    reg [8-1:0] ADD_tmp;
    reg [8-1:0] MULT_tmp;
    
    reg [16-1:0] ACC;
    
    genvar G;
    
initial begin
    $timeformat(-9,2,"ns", 16);
`ifdef SDFSCAN
    $sdf_annotate("sdf/PIM_Cluster_tsmc18_scan.sdf", test.top);
`endif
    // Initialize Inputs
    ACC = 16'h0;
    A = 8'h0;
    B = 8'h0;
    FUNC = 256'h0;
    FUNC_ADDR = 9'h0;
    RUN = 9'h1FF;
    LOAD_CORE = 9'h0;
    SRC_MODE = 18'h0;
    IN_MODE = 18'h0;//h3ffff
    AIN_ADDR = 36'h0;
    BIN_ADDR = 36'h0;
    Y_ADDR = 4'h0;
    clk = 0;
    reset = 0;
    


    //Generate Function Words for Multiplication and Addition
    for (a=0;a<16;a=a+1) begin
      -

-

-

-

-
            end
            index = index + 1;
        end
    end
    

    #5;
    reset = 1;
    #5;
    reset = 0;

 
    
    //LOAD_CORE = 9'h0;
    
    for (ii=0;ii<n;ii=ii+1) begin


    // @(posedge clk)
    // begin
    //   A <= $random%256;
    //   B <= $random%256;
    // end
    
    @(posedge clk)
    //#6;
    A <= $random%256;
    B <= $random%256;
    
     IN_MODE <= 18'h3FC00;
      SRC_MODE <= 18'h3FC00;
      AIN_ADDR <= {16'h2233,20'h0};
      BIN_ADDR <= {16'h0101,20'h0};
      
     //repeat(2) 
        @(posedge clk);
    
    //#6;
      //A = 0;
      //B = 0;
      B[3:0] <= ACC[3:0];
     IN_MODE <= 18'h00333;
      SRC_MODE <=18'h00001;
      AIN_ADDR <= {16'h0,20'h0B0C00};
      BIN_ADDR <= {16'h0,20'h0D0F0E};
      RUN <= 9'h1E0;
     
    //repeat (3) 
    @(posedge clk);

    //#6;
      ACC[3:0] <= Y[3:0];
      B[3:0]<=  ACC[7:4];
      IN_MODE <= 18'h003BB;
      SRC_MODE <= 18'h00113;
      AIN_ADDR <= {16'h0,20'h80800};
      BIN_ADDR <= {16'h0,20'h87A38};
      RUN<= 9'h015;
      
     //repeat (4) 
    @(posedge clk);//4


    //#6;
      B[3:0] <=ACC[11:8];
      IN_MODE <=18'h00357;
      SRC_MODE <=18'h00201;
      AIN_ADDR<= {16'h0,20'h17638};
      BIN_ADDR<= {16'h0,20'h00002};
      RUN <=9'h015;
      Y_ADDR <=4'h0000;
     
    //repeat (5)  
    @(posedge clk);
    //#6;
      ACC[7:4] <=Y[3:0];
      B[3:0]<= ACC[15:12];
      IN_MODE <=18'h003E3;
      SRC_MODE<= 18'h00342;
      AIN_ADDR <= {16'h0,20'h08006};
      BIN_ADDR <= {16'h0,20'h89208};
      RUN <= 9'h01B;
      
     //repeat (6) 
    @(posedge clk);
    //#6;
      IN_MODE <= 18'h00333;
      SRC_MODE <= 18'h00121;
      AIN_ADDR <= {16'h0,20'h80408};
      BIN_ADDR <= {16'h0,20'h10802};
      RUN <= 9'h01D;
      
     //repeat (7) 
    @(posedge clk);
    //#6;
      ACC[11:8] <= Y[3:0];
     IN_MODE <= 18'h0000B;
      SRC_MODE <= 18'h00002;
      AIN_ADDR<=  {16'h0,20'h00006};
      BIN_ADDR <={16'h0,20'h00028};
      RUN <=9'h015;
      
      //repeat (8)
    @(posedge clk);
    //#6;
     IN_MODE <= 18'h00004;
      SRC_MODE <= 18'h00000;
      AIN_ADDR <= {16'h0,20'h00000};
      BIN_ADDR <= {16'h0,20'h00000};
      RUN<=  9'h001;
      Y_ADDR <= 4'h0001;
      
      //repeat (9)
    @(posedge clk);
    //#6;
      ACC[15:12] <= Y[3:0];
      IN_MODE <= 18'h00000;
      RUN <= 9'h002;
      
    @(posedge clk);
    //#6;
      Y_ADDR <= 4'h0000;
      IN_MODE <= 18'h00000;
      RUN <= 9'h000;
    end
    $stop;          
end

always
  #3 clk = ~clk;

endmodule
 

PIMC_stimulus (1).h

Link to comment
Share on other sites

sc_signal<T> is exclusively non-blocking writes. It does NOT depend on SC_THREAD or SC_METHOD. There are two syntaxes (one preferred):

//File: example.hpp
#include <systemc>

SC_MODULE( NbaExample ) {
  sc_core::sc_trace_file* tracefile{nullptr}; // for waveforms
  const sc_core::sc_time delay{ 10, sc_core::SC_NS };
  sc_core::sc_signal<int> mySignal{"mySignal"}; // ALL sc_signals are non-blocking
  int var{ 5 }; // always blocking assign
  explicit NbaExample( const sc_core::sc_module_name& instance )
  : sc_module{ instance }
  {
    SC_HAS_PROCESS( NbaExample );
    SC_THREAD( main_thread );
    SC_METHOD( observe_method );
      sensitive << mySignal;
  }
  void start_of_simulation() override;
  void end_of_simulation() override;
  void main_thread();
  void observe_method();
};

//-------------------------------------------------------------------------------------- 
//File: example.cpp
#include "example.hpp"
#include <string>
using namespace std::string_literals;
using namespace sc_core;

void NbaExample::start_of_simulation() {
  tracefile = sc_create_vcd_trace_file( "dump" );
  sc_trace( tracefile, mySignal, "mySignal" );
  mySignal.write( 8 ); //< initial value for t=0;
}

void NbaExample::end_of_simulation() {
  sc_close_vcd_trace_file( tracefile );
}

void NbaExample::main_thread() {
  wait( delay );
  mySignal.write( 20 ); // Preferred syntax for assignment
  var = 8; // Blocking assignment -- do not use between processes
  wait( delay );
  mySignal = 30; // Convenience, but looks too much like blocking assignment
  var = 4;
  wait( delay );
  sc_core::sc_stop();
}

void NbaExample::observe_method() {
  // Using SC_REPORT_* macros allows better control of message reports
  SC_REPORT_INFO( "/Doulos/NbaExample",
                  ( " At "s +  sc_time_stamp().to_string()
                  + " mySignal="s + std::to_string(mySignal.read())
                  + " var="s + std::to_string(var)
                  ).c_str()
  );
}

//-------------------------------------------------------------------------------------- 
//File: main.cpp
#include "example.hpp"
[[maybe_unused]] //< avoid warnings
int sc_main( [[maybe_unused]]int argc, [[maybe_unused]]char* argv[] ) {
  NbaExample top{ "top" };
  sc_core::sc_start();
  return 0;
}

 

Link to comment
Share on other sites

Thanks David! That helped me understand where I'm going wrong.

But I'm a little confused as to how to implement this in the structure of code I'm using. 

I will used an example which is similar to my situation here:  https://edaplayground.com/x/JaQu

If I declare everything in my stimulus.h file as sc_signal, it gives error like this when I connect it in my main file(register_main.cpp):

register_main.cpp:37:23: error: no match for call to '(sc_core::sc_signal<sc_dt::sc_bv<256> >) (sc_core::sc_signal<sc_dt::sc_bv<256> >&)'
stim1.Data_in(DATA_IN);

which makes sense because of data type conflicts.

So does this mean I should be implementing the syntax you suggested directly in my main file(register_main.cpp)?

How do I do this if I want to keep the stimulus in a separate headerfile and then just connect the signals in the main file?

Please advice.

 

Link to comment
Share on other sites

  • 1 year later...

A related question for a model that enables multiple voltage rails. I have an internal signal that is a sc_buffer to enable the rails. For example

// signal to write from state machine

sc_buffer<bool> raila_en

 

// input to the module

raila.rail_en_in(raila_en_wire);

 

if (mystate == RAMPDOWN) {

    raila_en.write(0);

    railb_en.write(0);

    railc_en.write(0);

...

When using this in a CTHREAD, I see in the debugger that the first rail gets disabled, skews to zero, but the other two never get disabled. Of course I can connect all the rails to the same input and configure them to stagger the rails correctly but I want to be able to control each enable independently. Should I be using sc_signal<bool> rather than sc_buffer<bool>?

Link to comment
Share on other sites

  • 2 weeks later...

The difference between sc_buffer and sc_signal is that sc_buffer<T>::write issues an event on every write; whereas, sc_signal<T>::write only issues an event if the value changes (e.g., from 0 to 1 or 1 to 0).

I would have to see all your code (preferably on EDA playground) to understand the issue.

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...