Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by karthickg

  1. Maybe once co-routines are in C++20? https://en.cppreference.com/w/cpp/language/coroutines
  2. That still is a problem with SC_ONE_WRITER policy. Quoting from LRM (my emphasis): See the change if you declare sig as: sc_signal<bool, SC_MANY_WRITERS> sig;
  3. Check the documentation of SC_ONE_WRITER and SC_MANY_WRITERS in the LRM. The default value of the "WRITER_POLICY" in sc_signal is SC_ONE_WRITER.
  4. Are you only looking at disabling process_output from getting triggered when reset is on? In that case, it is as simple as: void fir::process_output() { if (reset.read() == 0) { //reset sequence next_trigger(reset.pos()); } else if (clk.read() == 0) { //Ignore the trigger just after reset is de-asserted and //sync to next clock edge next_trigger(clk.pos()); // Note: will work even if we comment out line above, due to static sensitivity } else { if(enable.read()) { //normal operation } } } But this may not really get you a significant improvement in itself. You may want to check what is going on in "normal operation" - and see if you can optimize that.
  5. I'm guessing you are trying to read the signal values immediately after calling write(). That won't return the values just written due to how update semantics work with primitive channels in SystemC. To make sure this is the cause of confusion, you can try creating a thread in your model where the signal values are written and read back after a delta-cycle wait: SC_MODULE(some_module) { sc_signal<pixel> p1; sc_signal<pixel> p2; void some_thread() { p1.write(pixel(1, 2, 3)); p2.write(pixel(4, 5, 6)); // Read and print p1 and p2 // Values won't be updated here wait(0, SC_NS); // Read and print p1 and p2 // Values will be updated here };
  6. Could be a CR-LF issue as well - in case the package was unzipped on Windows and then copied to the Ubuntu machine. Easiest way out will be to download the tar-ball again and unzip/untar on Ubuntu.
  7. It is possible to support "dynamic" model creation using SystemC, for example by reading command line parameters or a configuration file - if done before simulation starts. Once simulation starts, we can't create new models or add more ports. As an example, the following can be done: a. Have multiple top-levels, select one specific during a simulation run (using a command line argument or an environment variable or a config file, etc) b. Create models and connections "on the fly", based on a connection topology in a configuration file. An example can be, a network simulation where the number of end-stations change in every simulation run. c. Create bridges/switches that should add ports as and when connections are being made I do not know enough to answer to your questions in the later part of the post.
  8. Hi Bas, In trying to explain this a bit more, I looked up the double format. As per http://en.wikipedia.org/wiki/Double-precision_floating-point_format, the 8-byte double format can handle 15-17 significant decimal digits without loss of precision. When assigning, double d2 = (double) ull; the width of ull (4614 2566 5655 2045 848) is 19 significant decimal digits, which causes loss in precision.
  9. Hi Bas, Did you mean to say: double d = 3.1415926535897931; unsigned long long * ullp = (unsigned long long *) &d; out_port.write(*ullp); // sc_out<doubleunsigned long long> out_port; connected via sc_signal<doubleunsigned long long> to in_port unsigned long long ull = in_port.read(); // sc_in<doubleunsigned long long> in_port; connected via sc_signal<doubleunsigned long long> to out_port double * dp = (double *) &ull; Also, what is the output of sizeof(double) and sizeof(unsigned long long) in your case?
  10. [snip] SC_MODULE( sub_mod ) { sc_in<sc_bv<BUS_WIDTH> > in; . . sub_mod(sc_module_name nm,int BUS_WIDTH):sc_module(nm) { } }; The error has got nothing to do with SystemC, the code is not valid C++.. you can't use a constructor parameter ('int BUS_WIDTH') as a parameter to the template instance ('sc_in<sc_bv<BUS_WIDTH> >').
  11. The standard doesn't provide any "hooks" by which the user can control the order in which the processes must trigger. The standard only says (my emphasis):
  12. Hi Philipp, Thanks, I was curious to know why the standard doesn't implement set_sensitive for spawned processes, and your response clarifies that. The issue is not so much about having to use "wait(event_list)" instead of just "wait()" - that is not a disadvantage at all (for me). The main problem is different.. My design requires a spawned method to set a boolean flag (say, m_flag) whenever a certain event (say, m_event) fires. This can be easily done: // Where the method gets spawned sc_spawn_options opt; opt.set_sensitive(m_event); opt.dont_initialize(); // Note! opt.spawn_method(); sc_spawn(sc_bind(&my_module::flag_event, this, some_var), NULL, &opt); // .... // Method body: void my_module::flag_event(int some_var) { m_flag = true; next_trigger(); } However, if the method has to wait for sc_event_or_list, then set_sensitive can't be used, and so dont_initialize can't be used. The flag_event has to now distinguish between the invocation at start of simulation, and the subsequent invocations when an event in the or'd list fires: sc_spawn_options opt; opt.spawn_method(); m_did_initialize = false; // Note! sc_spawn(sc_bind(&my_module::flag_event, this, some_var), NULL, &opt); // .... // Method body: void my_module::flag_event(int some_var) { if(m_did_initialize) { m_flag = true; next_trigger(m_event_or_list); } else { m_did_initialize = true; next_trigger(m_event_or_list); } } This is just plain clumsy to read, and needs an extra if-check every time the method gets invoked. I can think of a way to eliminate the if-check at simulation time: by having one more spawned method, but that is even more clumsy code; not worth the run-time cost (in my case). So I guess I do not have any alternative here. Thanks for your inputs anyway.
  13. Hello, Thanks for your reply - after seeing which, I had to go back to the standard to check to ascertain what is allowed and what is not.. My understanding is as below.. Quoting from the specification: So - what you have written certainly applies to unspawned processes. For spawned processes, The only question that remains is, can set_sensitivity be called on processes spawned after start of simulation? I'm not able to find an explicit yes or no in the specification, however there is an example (page 66) that shows that set_sensitivity can be called after start of simulation.
  14. Try changing, //read what we have written: enable_s.write(0); address_en_s.write(1); wait(SC_ZERO_TIME); cout << "Output value: " << dataout_s.read() << endl; To, //read what we have written: enable_s.write(0); address_en_s.write(1); wait(SC_ZERO_TIME); wait(SC_ZERO_TIME); cout << "Output value: " << dataout_s.read() << endl; This is not the recommended solution! But only a step towards debugging the problem. Also, print the return value of sc_time_stamp() and sc_delta_count() in memory thread after wait(), and in the stimulus thread after each wait(SC_ZERO_TIME).
  15. Hello, To allow setting static sensitivity of dynamically spawned processes to an or'd list of sc_events, I expected to see a method like: void sc_spawn_options::set_sensitivity(const sc_event_or_list *); but do not see any such. next_trigger and wait however, do accept a reference to sc_event_or_list, so it appears to be inconsistent? For, dynamic sensitivity can use sc_event_or_list, but not static sensitivity.. I suppose we can't use sc_event_or_list to set the static sensitivity of statically spawned threads either (created using SC_THREAD/SC_METHOD) - but that is not my need. Further, there doesn't seem to be any way to retrieve the list of sc_event from an sc_event_or_list (as, say, std::vector<sc_event *>). If that was possible, then I could have called the set_sensitivity(const sc_event *) method repeatedly, for all events in the or'd list. Any help appreciated, thanks.
  16. Is it possible to model serial interfaces in TLM2? Yes, certainly - UART, SPI, I2C, etc. Specifically on MIPI - I have no idea, maybe someone else can comment. On how to go about modeling such interfaces - why not use TLM initiator/target socket itself?
  17. There are two modules - one of them is clearly a memory. Let us call it 'memory': class memory : public sc_module { // ... }; ..And the other is reading from the memory, let us call it 'master': class master : public sc_module { // ... }; Now, before we can look at solving your question, we need to define how 'master' and 'memory' communicate. One option is via plain signals, for example an APB-like interface (a poort choice for a memory, but easier to list the signals): class master : public sc_module { public: sc_out<uint32_t> paddr; // Address sc_out<uint32_t> pwdata; // Write data sc_in<uint32_t> prdata; // Read data sc_in<bool> pready; // Ready signal from slave sc_in<bool> ptx_type; // Transfer type, read or write sc_out<bool> psel; // Select slave }; // class memory has the inverse of the above set of ports: sc_in for sc_out and vice versa The master class can start a 'write' transfer by writing the address and data on paddr/pwdata, and asserting the psel signal. Then, until the pready signal is asserted by memory, it has to wait. The memory will wait for required time (to simulate RAM access delay) before asserting pready: void master_thread() { // ... paddr = 0x1000; pwdata = 0x1234; psel = 1; wait(pclk.posedge_event()); // Wait for one clock to pass, to allow the slave to respond // Note: the exact number of clock cycles that the master has to wait depends on the bus protocol spec // Wait for slave: if it is not yet ready if(pready != 1) { wait(pready.posedge_event()); } // ... } If the master-memory communication is with a TLM style higher level interface, then it is easier. Assuming a blocking API, it can be as simple as: class master : public sc_module { apb_tlm_port master_port; // TLM APB master port // ... }; void master_thread(void) { // ... // Function will only return when the slave has completed the write, after delays if applicable master_port.write(0x1000, 0x1234); // ... }
  18. Your question doesn't make sense.. you do not seem to understand how SystemC scheduling works. It is not possible to insert a wait statement "without removing the process from the runnable set and without allowing other thread to resume". The converse of that statement is actually true - it is only when a SystemC wait statement gets executed that an other thread is allowed to resume (and the current thread yields). The good news is, it is certainly possible to model memory delays with how the threads work in SystemC. It requires the modules to synchronize over the connected ports - so the 'memory' model will signal when the data is available, and the 'master' model should wait for that signal from memory.
  19. If, a. In real hardware there is no interrupt signal, and b. The initiator can be replaced by an Instruction Set Simulator in future, then, I would suggest to let initiator poll the register content in the target (with some delay between the polls). Suppose the target blocks the initiator's read until the register gets updated - you'll run in to the following issues (as a sample): a. When the initiator is replaced by ISS, the ISS executes S/W which might expect a completely different behavior from the target. This can lead to dead-locks.. for instance, if the S/W has a "poll_thread" that is polling the register, and a "worker_thread" that does something causing the target's register to get updated, it is very much possible that the changed behavior on the target side causes a dead-lock. b. The target can hold the bus until the register gets updated. This can block other masters from accessing the bus - affecting the measured performance c. The behavior above can again potentially lead to a dead-lock, if a DMA is required before the register can get updated (the DMA will be blocked until the bus is free, and the bus is busy until the register gets updated, the regsiter gets updated after the DMA happens). In general, the SystemC model must be as smart as the RTL- but no further.
  20. What is the "Initiator" here? Is it emulating a processor model (and can, perhaps, execute the embedded software as well)? Or is it a DMA agent of a peripheral? In either case, what is the hardware behavior? Does the initiator poll the value of the register until it detects the register has been updated? Also, what is the behavior on the target/slave side? If the target "blocks the read until the register gets updated", the behavior corresponds to a "slave device introducing bus wait cycles until the register gets updated". Is this the behavior of the real hardware? Note that, depending on how the bus is modeled, if the slave blocks the read, it can potentially block other masters on the bus as well. That side-effect may not be something you want to introduce. In real H/W, if polling is costly, the design would have provisioned for an interrupt from the target/slave when the register is updated. Perhaps you can model it the same way? So, it is not easy to answer your question conclusively - it all depends on what you are trying to model.
  21. A quick read shows that this behavior is defined in the standard document, section
  22. I expect this would be a simulator specific behavior. In the specific simulator where you are executing the code, the scheduler is able to detect that there are no more runable threads (and there are no clocked threads) - and hence quits the simulation.
  23. An other way to understand this is to look at SystemC/TLM as pure C++ code and see what is going on.. Let us assume that you have two classes, "Initiator" and "Target", that need to communicate with each other. Target implements a function called "transport" to be called by Initiator, and Initiator implements a function called "response" - that is called by target. For the sake of the explanation here, the payload itself is not important, which we will assume is plain "int" in both directions. You would normally do this as following: // Interface classes: class initiator_if { public: void response(int i) = 0; }; class target_if { public: void transport(int i) = 0; }; class Initiator : public initiator_if { public: // Implement the interface void response(int i) { cout << "Got: " << i << " from target" << endl; } void bind(target_if &_if) { // Store the pointer locally m_target = &_if; } }; class Target : public target_if { public: // Implement the interface void transport(int i) { cout << "Got: " << i << " from initiator " << endl; } void bind(initiator_if &_if) { // Store the pointer locally m_initiator = &_if; } }; Next we instantiate the objects and "bind" them: Initiator initiator; Target target; initiator.bind(target); target.bind(initiator); I hope you are seeing where we are going with this. What has been done above is a crude equivalent of sc_port binding. There is one problem however with the approach above. What if the class "Target" doesn't implement the target_if directly? Like so: class TargetGrandChild : public target_if { public: void transport(int i) { // Implement the interface.. cout << "Got " << i << " from initiator (in grand child)" << std::endl; } }; class TargetChild { public: TargetGrandChild gch; /* Other stuff.. */ }; class Target { public: TargetChild ch; /* Other stuff.. */ }; One way to deal with this is to change the way bind function is called: initiator.bind(target.ch.gch); This is ugly. Firstly, it makes the client of bind function dependent on an internal design aspect of the "Target" class. For example, if the Target class changes tomorrow (to let the TargetChild implement the "target_if" directly), the bind function call needs to change. Also, to allow the client to call the bind function as above, the elements "ch" and "gch" must be made public, which may not be necessarily a good thing. A way out is to have an additional indirection. Let us call this as simple_export (a very simplified version of sc_export): class simple_export : public target_if { public: void transport(int i) { // One level of indirection p_real_target->transport(i); } void bind(target_if &_if) { p_real_target = &_if; } private: target_if *p_real_target; }; The new version of the Target class now looks like the following: class TargetGrandChild : public target_if { public: void transport(int i) { // Implement the interface.. cout << "Got " << i << " from initiator (in grand child)" << std::endl; } }; class TargetChild { public: TargetGrandChild gch; /* Other stuff.. */ }; class Target { public: simple_export port; private: // private is ok TargetChild ch; /* Other stuff.. */ Target() { // Tell "export" where the real implementation is port.bind(ch.gch); } }; The initiator will be "bound" to the target like so: initiator.bind(target.port); So for the simple_export to work, you need two binds: First is where the initiator is bound to the simple_export instance Second is when the "real" implementation is bound to the simple_export instance The simple_export class acts like a bridge. It forward the calls from the initiator to the "real" implementation. In case the "Target" itself implements the interface, the bind would look like: class Target : public target_if { public: simple_export port; private: // private is ok TargetChild ch; /* Other stuff.. */ Target() { // Tell "export" where the real implementation is port.bind(*this); } }; I hope this explains the line you pointed out in the code. The TLM socket has both a port and an export. Please note that the code snips above does not correspond to how OSCI simulator actually implements sc_port/sc_export!
  24. Are you on the reference simulator? Did you mean to say sc_start instead of sc_run ? There is no such thing as sc_run in the reference OSCI simulator.
  • Create New...