johnmac Posted October 17, 2018 Report Share Posted October 17, 2018 I am using sc_inout<struct T> to emulate a handshake flow of 'ready' and 'valid'. 'ready' flows in Responder --> Initiator and 'valid' flows in Initiator --> Responder. The setup doesnt work and causes the simulation to hang at 'Waiting'. I don't get a waveform because the simulation never completes. I suspect Can someone tell me if there are things I am overlooking: Can struct contain signals that flow in different directions? Can sc_inout<struct T> also map directly to another module with the same sc_inout<struct T>? Am I also setting the struct contents correctly? I eventually want to expand this handshake to several more signals (20+ more) which will connect directly to another module. Any alternate suggestions will also help a lot. #include <systemc.h> #include <iostream> struct hs { bool ready; bool valid; inline friend void SetValid(hs & pt, bool value) { pt.valid = value; } inline friend void SetReady(hs & pt, bool value) { pt.ready = value; } // inline friend bool GetReady(const hs& pt) { inline friend bool GetReady(const hs & pt) { return pt.ready; } inline friend bool GetValid(const hs & pt) { return pt.valid; } inline bool operator == (const hs & rhs) const { return false; } inline hs& operator = (const hs& rhs) { ready = rhs.ready; valid = rhs.valid; return *this; } inline friend void sc_trace(sc_trace_file *tf, const hs & v, const std::string& NAME ) { sc_trace(tf,v.ready, NAME + ".ready"); sc_trace(tf,v.valid, NAME + ".valid"); } inline friend ostream& operator << ( ostream& os, hs const & v ) { os << v.ready << " " << v.valid << endl; return os; } }; SC_MODULE(initiator) { sc_in <bool> clk; sc_inout<hs> handshake; void func1(); SC_CTOR(initiator) { SC_THREAD(func1); sensitive << clk; dont_initialize(); }; }; SC_MODULE(responder) { sc_in <bool> clk; sc_inout<hs> handshake; void func2(); SC_CTOR(responder) { SC_THREAD(func2) sensitive << clk; dont_initialize(); }; }; void initiator::func1() { cout << "Initator started\n"; SetValid((hs&)handshake, false); while(GetReady(handshake) == false) { cout << "Waiting" << endl; wait(clk->posedge_event()); } SetValid((hs&)handshake, true); cout << "Initator valid set to HIGH\n"; } void responder::func2() { int sleep_i; SetReady((hs&)handshake, false); cout << "Responder started\n"; for(sleep_i = 0; sleep_i < 40; sleep_i++) wait(clk->posedge_event()); SetReady((hs&)handshake, true); } The instantiation looks like this: int sc_main(int argc, char* argv[]) { sc_clock clock("clock", 10, SC_NS); sc_signal<hs, SC_MANY_WRITERS> intf; initiator init("init"); responder resp("resp"); init.handshake(intf); resp.handshake(intf); init.clk (clock); resp.clk (clock); // Open a trace file sc_trace_file *fp; fp = sc_create_vcd_trace_file("wave"); sc_trace(fp, intf, "intf"); sc_start(); sc_start(600, SC_NS); sc_close_vcd_trace_file(fp); cout << "Simulation finished @ " << sc_time_stamp() << endl; sc_stop(); return 0; }; Quote Link to comment Share on other sites More sharing options...
Eyck Posted October 17, 2018 Report Share Posted October 17, 2018 The problem is that you do not write to the signal carrying the struct (and therefore you do not trigger any event). The SetValid() call implicitly triggers a read (thru the overloaded cast) but you never write back to the signal. Either you change the use of SetValid (and SetReady): { hs data = handshake.read(); SetValid(hs, false); handshake.write(data); } Here the call to wriite() trigges the respective events. The other option is to change SetValid() to take a signal reference and move the code into the function. Best regards Quote Link to comment Share on other sites More sharing options...
Eyck Posted October 17, 2018 Report Share Posted October 17, 2018 Actually, I forgot to answer your quesions, so here we go. Can struct contain signals that flow in different directions? Yes, of course. struct is just a a wrapper which handles datatype as one Can sc_inout<struct T> also map directly to another module with the same sc_inout<struct T>? Well, ports need to connect to signals, there is no way to connect 2 ports directly except you do a hierarchical binding Am I also setting the struct contents correctly? This I answer in the last post. Quote Link to comment Share on other sites More sharing options...
johnmac Posted October 17, 2018 Author Report Share Posted October 17, 2018 Thanks for the response @Eyck I followed your suggestions, and did something like this. Pls bear with me if I am being thick ? I am running into an error that looks like this: SystemC 2.3.1-Accellera --- Sep 7 2015 10:55:18 Copyright (c) 1996-2014 by all Contributors, ALL RIGHTS RESERVED Initator started Waiting Responder started Error: (E115) sc_signal<T> cannot have more than one driver: signal `signal_0' (sc_signal) first driver `init.func1' (sc_thread_process) second driver `resp.func2' (sc_thread_process) first conflicting write in delta cycle 1 In file: ../../../../src/sysc/communication/sc_signal.cpp:73 In process: resp.func2 @ 0 s I have the interfacing signal declared with SC_MANY_WRITERS. sc_signal<hs, SC_MANY_WRITERS> intf; #include <systemc.h> #include <iostream> struct hs { bool ready; bool valid; inline friend void SetReady(hs & pt, bool value) { pt.ready = value; } inline friend void SetValid(hs & pt, bool value) { pt.valid = value; } inline bool operator == (const hs & rhs) const { return false; } inline hs& operator = (const hs& rhs) { ready = rhs.ready; valid = rhs.valid; return *this; } inline friend void sc_trace(sc_trace_file *tf, const hs & v, const std::string& NAME ) { sc_trace(tf,v.ready, NAME + ".ready"); sc_trace(tf,v.valid, NAME + ".valid"); } inline friend ostream& operator << ( ostream& os, hs const & v ) { os << v.ready << " " << v.valid << endl; return os; } }; SC_MODULE(initiator) { sc_in <bool> clk; sc_inout<hs> handshake; void func1(); SC_CTOR(initiator) { SC_THREAD(func1); sensitive << clk; dont_initialize(); }; }; SC_MODULE(responder) { sc_in <bool> clk; sc_inout<hs> handshake; void func2(); SC_CTOR(responder) { SC_THREAD(func2) sensitive << clk; dont_initialize(); }; }; void initiator::func1() { cout << "Initator started\n"; hs tmp_hs; tmp_hs = handshake; SetValid(tmp_hs, false); handshake = tmp_hs; while(true) { tmp_hs = handshake.read(); if (tmp_hs.ready == false) { cout << "Waiting" << endl; wait(clk->posedge_event()); } else { break; } } tmp_hs = handshake; SetValid(tmp_hs, true); handshake = tmp_hs; wait(clk->posedge_event()); cout << "Initator valid set to HIGH\n"; } void responder::func2() { int sleep_i; hs tmp_hs; cout << "Responder started\n"; tmp_hs = handshake; SetReady(tmp_hs, false); handshake = tmp_hs; for(sleep_i = 0; sleep_i < 40; sleep_i++) wait(clk->posedge_event()); tmp_hs = handshake; SetReady(tmp_hs, true); handshake = tmp_hs; } Quote Link to comment Share on other sites More sharing options...
Roman Popov Posted October 17, 2018 Report Share Posted October 17, 2018 Hi johnmac, What you try to do is conceptually wrong: Ready and Valid should be two separate signals. Initiator drives the Valid signal, and responder drives the Ready. There is an example of ready-valid channel that comes together with SystemC, check in systemc/examples/sysc/2.3/sc_rvd If your final goal is synthesizable code, then both Mentor and Cadence synthesis tools already have it implemented in their libraries. Check vendor documentation. If you still want to simulate your design, you can try to use SC_UNCHECKED_WRITERS instead of SC_MANY_WRITERS. This will disable a check for multiple drivers in a single delta cycle. Quote Link to comment Share on other sites More sharing options...
David Black Posted October 18, 2018 Report Share Posted October 18, 2018 SC_UNCHECKED_WRITERS also means allowing nasty race conditions. Quote Link to comment Share on other sites More sharing options...
johnmac Posted October 22, 2018 Author Report Share Posted October 22, 2018 Thanks for all the comments. Will using a std::mutex lock() and unlock() around the critical section help when accessing the struct help ? Quote Link to comment Share on other sites More sharing options...
Roman Popov Posted October 24, 2018 Report Share Posted October 24, 2018 On 10/22/2018 at 10:42 AM, johnmac said: Thanks for all the comments. Will using a std::mutex lock() and unlock() around the critical section help when accessing the struct help ? SystemC is single threaded, you don't need std::mutex. However SystemC does not guarantee any order for processes executed in the same delta cycle. SystemC is a discrete-event simulator, and I suggest you to learn the concepts of simulation semantics from Paragraph 4 "Elaboration and simulation semantics" of IEEE-1666 SystemC standard. The purpose of primitive channels, like sc_signal, is to remove non-determinism by separating channel update request from actual update of value into two different simulation phases. But it only works if there is a single writing process during a delta cycle. In your case if initiator and responder threads are executed on same cycle, they both will read the same "current value" of signal. Initiator will request to set signal value to (valid = 1, ready = 0) and responder will request to set it to (valid = 0, ready = 1). Since there is no guarantee on order between processes, you will either get (1,0) or (0,1) on the next cycle. kartikkg 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.