Xing Chen Posted August 24, 2020 Report Share Posted August 24, 2020 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! Quote Link to comment Share on other sites More sharing options...
Eyck Posted August 24, 2020 Report Share Posted August 24, 2020 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; } Quote Link to comment Share on other sites More sharing options...
Xing Chen Posted August 24, 2020 Author Report Share Posted August 24, 2020 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? Quote Link to comment Share on other sites More sharing options...
David Black Posted August 24, 2020 Report Share Posted August 24, 2020 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. maehne 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.