Jump to content

Clock Gating in SystemC?


Recommended Posts

Hello,

I'm trying to simulate a sync clock gater in SystemC: input: clk, clk_enable, output: enabled_clk. However, I noticed that having a SC_METHOD over clk breaks the sync design:

#include "systemc.h"
SC_MODULE(ClockPropagater) {
  sc_in<bool> clk{"clk"};
  sc_out<bool> p_clk{"p_clk"};  
                                               
  // Clock gating can be potentially added.
  void Propagate() { p_clk = clk; }

  SC_CTOR(ClockPropagater)  {
    SC_METHOD(Propagate);                                                                      
    sensitive << clk;
  }                  
};
                                               
SC_MODULE(outputer) {              
  sc_in<bool> clk;
  sc_out<int> c_out;   
                                               
  void writer() {
    for (int i = 0; i < 5; ++i) {
      cout << "Write " << this->name() << " " << i << endl;
      c_out.write(i);
      wait();
    }
    exit(0);                  
  }               
                                               
  SC_CTOR(outputer) {
    SC_CTHREAD(writer, clk.pos());
  }
};         

SC_MODULE(reader) {
  sc_in<bool> clk;
  sc_in<int> c_in;

  void read() {
    while (true) {
      cout << "Run " << this->name() << " " << this->c_in.read() << endl;
      wait();
    }
  }

  SC_CTOR(reader) {
    SC_CTHREAD(read, clk.pos());
  }
};


int sc_main(int argc, char* argv[])
{
  sc_clock TestClk("TestClock", 10, SC_NS,0.5);
  sc_signal<bool> ck;
  sc_signal<int> sig;

  ClockPropagater cp{"clock_propagater"};
  cp.clk(TestClk);
  cp.p_clk(ck);

  outputer o{"output"};
  o.clk(TestClk);
  o.c_out(sig);

  reader r{"reader_propagated_clk"};
  r.clk(ck);
  r.c_in(sig);

  reader r1{"reader_raw_clk"};
  r1.clk(TestClk);
  r1.c_in(sig);

  sc_start();  // run forever

  return 0;
}
$ g++ a.cc -lsystemc && ./a.out

        SystemC 2.3.3-Accellera --- Feb 13 2020 22:17:20
        Copyright (c) 1996-2018 by all Contributors,
        ALL RIGHTS RESERVED
Write output 0
Run reader_raw_clk 0
Run reader_propagated_clk 0
Write output 1
Run reader_raw_clk 0
Run reader_propagated_clk 1
Write output 2
Run reader_raw_clk 1
Run reader_propagated_clk 2
Write output 3
Run reader_raw_clk 2
Run reader_propagated_clk 3
Write output 4
Run reader_raw_clk 3
Run reader_propagated_clk 4

In the output above, the reader using propagated clk is getting the input after the clock edge. I'd like reader_propagated_clk behaves the same as reader_raw_clk.

What would be the proper way to propagate or gate a clk in SystemC? Thanks!

Link to post
Share on other sites

Due to the METHOD your derived clock switches in the second delta cycle. One way I can think of is 'gating' the primary clock as well:

#include "systemc.h"
SC_MODULE(ClockPropagater) {
  sc_in<bool> clk{"clk"};
  sc_out<bool> p_clk1{"p_clk1"}, p_clk2{"p_clk2"};  
                                               
  // Clock gating can be potentially added.
  void Propagate() { p_clk1 = clk; p_clk2 = clk;}

  SC_CTOR(ClockPropagater)  {
    SC_METHOD(Propagate);                                                                      
    sensitive << clk;
  }                  
};
                                               
SC_MODULE(outputer) {              
  sc_in<bool> clk;
  sc_out<int> c_out;   
                                               
  void writer() {
    for (int i = 0; i < 5; ++i) {
      cout << "Write " << this->name() << " " << i << endl;
      c_out.write(i);
      wait();
    }
    exit(0);                  
  }               
                                               
  SC_CTOR(outputer) {
    SC_CTHREAD(writer, clk.pos());
  }
};         

SC_MODULE(reader) {
  sc_in<bool> clk;
  sc_in<int> c_in;

  void read() {
    while (true) {
      cout << "Run " << this->name() << " " << this->c_in.read() << endl;
      wait();
    }
  }

  SC_CTOR(reader) {
    SC_CTHREAD(read, clk.pos());
  }
};


int sc_main(int argc, char* argv[])
{
  sc_clock TestClk("TestClock", 10, SC_NS,0.5);
  sc_signal<bool> ck1, ck2;
  sc_signal<int> sig;

  ClockPropagater cp{"clock_propagater"};
  cp.clk(TestClk);
  cp.p_clk1(ck1);
  cp.p_clk2(ck2);

  outputer o{"output"};
  o.clk(ck1);
  o.c_out(sig);

  reader r{"reader_propagated_clk"};
  r.clk(ck2);
  r.c_in(sig);

  reader r1{"reader_raw_clk"};
  r1.clk(ck1);
  r1.c_in(sig);

  sc_start();  // run forever

  return 0;
}

 

Link to post
Share on other sites
1 hour ago, Eyck said:

Due to the METHOD your derived clock switches in the second delta cycle. One way I can think of is 'gating' the primary clock as well:

Eh, if this is inside a larger heirachy/clk tree, I think it's hard to get it work alongside the other branches. Thanks for the idea though!

Is it possible avoid the clock switch to become a separate delta cycle?

Link to post
Share on other sites

Three thoughts might be helpful for you to consider:

You could of course use next_trigger() in SC_METHOD:

SC_MODULE(HARD_WAY) {
  sc_in<bool> clock, clock_enabled;
  SC_CTOR(HARD_WAY) {
    SC_METHOD(gated_method);
    sensitive << clock;
    ...
  }
  ...
  void gated_method(void) {
    if ( clock_enabled and clock.posedge() ) {
      // Normal operations
    } else {
      // Wait until clock is re-enabled
      next_trigger( not clock_enabled );
    }
  }

The better way is to use sc_process_handle::disable() and enable()

#define SC_INCLUDE_DYNAMIC_PROCESSES
#include <systemc>
#include <vector>
using namespace sc_core;
std::vector<sc_process_handle> gated; //< collect processes needing gating
SC_MODULE(EASY_WAY) {
  sc_in<bool> clock;
  SC_CTOR(EASY_WAY) {
    SC_METHOD(gated_method);
    sensitive << clock;
    gated.push_back(sc_get_current_process_handle());
    ...
  }
  void gated_method(void) {
    // Normal operations
  }
  ...
};
  
// Process controlling gating
void control thread(void) {
  ...
  if ( turn_off_clock ) {
    for( auto& process : gated ) process.disable();
  }
  if ( turn_on_clock ) {
    for( auto& process : gated ) process.enable();
  }
  ...
 }

Above approach works on all static SystemC process types. Dynamic processes might escape.

A more comprehensive and easier approach could be done if your gated processes are all submodules of a containing module. In that case, you skip the registration issue, because you can simply do a hierarchical search to collect the list of processes that need to be disabled or enabled at the time of power down/up. This approach will also capture dynamic processes.

 

 

Link to post
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...