Jump to content

Philipp A Hartmann

Members
  • Posts

    537
  • Joined

  • Last visited

  • Days Won

    130

Posts posted by Philipp A Hartmann

  1. Quoting IEEE 1666-2011, 8.1.2 (color highlighting mine):

    Quote

    Member function set_time_unit shall be overridden in the derived class to set the time unit for the trace file. The value of the double argument shall be positive and shall be a power of 10. In the absence of any call function set_time_unit, the default trace file time unit shall be 1 picosecond.

    So I think, the implementation in SystemC 2.3.2 is in line with the SystemC standard.

    Instead of failing with an assertion, a more explanatory error message would be helpful, of course.

    Greetings from Duisburg,
      Philipp

  2. Hi Jarodw,

    Thanks for your report. I can confirm and reproduce the issue in SystemC 2.3.2.

    It looks indeed like a regression compared to SystemC 2.3.0/1 that has been introduced by the fix for optionally unbound sockets, see:

    It seems, the SystemC regression tests didn't cover the hierarchical binding for the multi sockets, so it wasn't caught before the release.

    Your example can be fixed by changing line 228 in src/tlm_utils/multi_passthrough_target_socket.h:

      if (unbound && !m_hierarch_bind) return;
      //          ^-- add check for hierarchical binding here

    Hope that helps,
      Philipp

  3. In addition to Thorsten's comments, your main problem is that you call the processes directly instead of invoking sc_start.  You should never call process functions manually.  They are  scheduled by the kernel during the simulation instead.

    Here's a cleaned up version of your sc_main:

    int sc_main(int /*argc*/, char* /*argv*/[])
    {
      sc_signal<double> force_main("force main");
      sc_signal<double> area_main("area_main");
    
      stimuli_pressure_mech S("stimuli_pressure_mech");
        S.force_out(force_main);
        S.area_out(area_main);
    
      pass_on_impulse PI("pass_on_impulse");
        PI.force_in(force_main);
        PI.area_in(area_main);
    
      sc_start();
      return 0;
    } 

    I did the following changes:

    • Drop warning suppression - why do you want to allow deprecated features? They are deprecated for a reason.
    • Add names to your signals
    • Drop dynamic allocation of modules in favor of properly cleaned up local variables
    • Drop manual calls to processes
    • Call sc_start to start the simulation (without specific end time, but Thorsten's suggestions is also possible)

    Hope that helps,
      Philipp

  4. You can check out the example Makefiles in the installation (examples/build-unix/Makefile.{config,rules} and e.g. examples/sysc/simple_bus/Makefile) as a starting point. The files in examples/build-unix are reasonably generic, and you may "just" need to adjust the settings in Makefile.config. In the project's Makefile itself, you then set the PROJECT variable and your SRCS (to point to your source files).  Admittedly, documentation could be better (as usual), but you can ask here, if you have further questions.

    The CMake support included in SystemC 2.3.2 is still experimental and is mostly targeted for early adopters with CMake knowledge.

    Greetings from Duisburg,
      Philipp

  5. Hi Matthias,

    IIRC, KPNs require that inputs are available on all incoming connections for an actor to fire.  In your model, you already consume tokens from some inputs while being then blocked at others.  This can be the reason that you don't see the deadlock in your model.  But of course, you can also have deadlocks under this approach.  Maybe you can trigger it by just switching the order of some of the read statements in your multi-input actors.

    As a general solution, you need to use a similar pattern as recently discussed in the following thread:

     

    Here's an example for one of your processes:

    void kpn::p7() // merge
    {
      static const int f7_n = 1; // number of tokens needed from each input
      static const int f8_n = 1;
      
      while( true )
      {
        while( f7.num_available() < f7_n || f8.num_available() < f8_n ) {
          // at least one side is blocking, wait for activity on either side
          wait( f7.data_written_event() | f8.data_read_event() );
        }
      
        // forwarding possible, process tokens - will not block on inputs
        unsigned int u = f7.read();
        unsigned int v = f8.read();
     
        // ...
        // may still block out outputs
      }
    }

     

    Greetings from Duisburg,
      Philipp

  6. Hi Patrick,

    You can use every Copyable/CopyAssignable type with sc_fifo or tlm_fifo, as entries are always copied in and out of the fifo slots. There is no special support for e.g. move-only types, yet.  It might be a nice to explore options to add such support in order to reduce the cost for passing complex types through fifos.

    So yes, you can pass std::shared_ptr and std::vector, but e.g. not std::unique_ptr.

    Greetings from Duisburg,
      Philipp

  7. The model you describe is not "just" a passthrough model though, as you assume that tokens can be both intercepted (other processes reading from incoming_fifo) and injected (other processes than B writing to outgoing_fifo). From my experience, it is very difficult to design robust models with multiple readers and writers for FIFOs, at least without higher-level synchronization orchestrating the accesses somehow.  That said, let's assume these arbitrary FIFO accesses are needed and OK for your use case.

    I would suggest to indeed use the non-blocking APIs to do the forwarding:

    while( true )
    {
      if( incoming_fifo.num_available() > 0 && outgoing_fifo.num_free() > 0 ) {
        // forwarding possible, process token - will never block
        outgoing_fifo.write( incoming_fifo.read() );
      }
      // at least one side is blocking, wait for activity on either side
      wait( incoming_fifo.data_written_event() | outgoing_fifo.data_read_event() );
    }

    This will not lead to an additional implicit slot in the passthrough block, but the forwarding is "non-greedy" now, i.e. happens only if a slot is available on both sides.  Together with arbitrary other readers/writers on the FIFOs, your passthrough model might never succeed to forward a single token.  See above.

    Minor comment: Your thread_A pseudo code above looks like Java/SystemVerilog and would have a memory leak in C++.  You should not put dynamically allocated objects into a FIFO.

    Hope that helps,
      Philipp

  8. When you use MSVC 2015/2017, did you make sure to use SystemC 2.3.2? Earlier SystemC versions did not support such recent versions of MSVC.

    When you say "regression tests", do you mean the SystemC regression test suite?  If so, please check the accompanying README_windows.txt for any steps you might have missed.

    If this still doesn't help, please post some more details on what exact errors you see.

    Greetings from Duisburg,
      Philipp

  9. Ok, now I understand your concern: It's also about stack-allocated variables in your threads that are not cleaned up at the end of the simulation, as their stacks are not unwound and dynamic process stacks may not even be explicitly deallocated.  I'm not sure, if these cases qualify as real memory leaks, though.

    Small comment: instead of throwing an (non-standard) sc_halt object, you can simply call :

     h.kill(SC_INCLUDE_DESCENDANTS); // also kills all child processes

    There is currently no way to obtain (or kill) all running processes in the simulation, but you can roll your own function to

    • recurse over the object hierarchy via get_child_objects()
    • convert objects to process handles, if valid() and !terminated() call kill(SC_INCLUDE_DESCENDANTS) 

    /Philipp

  10. If you invert the logic and add a function add_initiator to the interconnect, temporarily storing the pointer to the to-be-connected initiator socket (and maybe a name, although this can be derived from the given socket or its parent), you can complete the binding and target socket creation in the interconnect's before_end_of_elaboration hook.  Sockets instantiated in this function will be placed correctly in the hierarchy and this approach is fully standards-compliant.

  11. Starting with SystemC 2.3.2, the MSVC project files shipped with the package use the DLL-based runtime library.  Therefore it is not necessary (instead rather harmful) to select the statically linked runtime library in your application.

    Secondly, you should not set the SC_SIGNAL_WRITE_CHECK environment variable to DISABLE (unrelated to your current issue, but suggested in the video above).

    More instructions on using MSVC with SystemC can be found in the INSTALL file shipped with the proof-of-concept simulator.

    Hope that helps,
      Philipp

  12. As said above, for the TLM-2.0 interconnect case, you can just use multi sockets on don't add additional sockets to the interconnect model at all.

    My question hasn't been on the "surrounding side", but on the usage of the sockets inside the interconnect and the cpu/mem/uart models.  Thes modules actually have to make use of the "injected sockets", which likely requires some additional hacks.  How does the memory handle a second "tgt()" socket during simulation?  How does the CPU suddenly leverage a second initiator?

  13. To me, this approach still looks backwards.

    If you need to finalize things in the model after the configuration is complete (e.g. spawning threads, etc), you can use the before_end_of_elaboration hook.  You can even do the instantiation itself there, if needed, by just storing the pointer to the master and doing both instantiation and process spawning during before_end_of_elaboration.

    Adding emplace_back to sc_vector would just reduce the boilerplate of the delayed instantiation of structural members.  But there are already ways to achieve all of this without any changes to the language.

  14. Quote

    The second version is probably less portable as it includes a file not intended to be included, according to the comments in the file.

    Both versions are non-portable, as the SystemC simcontext is not part of the standard either.  I am even surprised that the hierarchy_push/pop functions are not restricted (as they should be).  So the first solution might in fact break in future versions of SystemC as you shall not mess with the object hierarchy stack yourself.

    Whether or not the "hierarchy scope guard" can be extended to become a standardized solution will likely require some discussions first, especially how to avoid breaking the object hierarchy.  Certainly, this internal hierarchy stack is not  something to arbitrarily use from the model side, other than from the sc_module_name usage today.

    For the particular use case, you can now look into the new "optionally bound convenience sockets" in SystemC 2.3.2, which you can safely leave unbound when not used.  An arbitrary number of sockets can be bound to the multi_passthrough_*_socket, without having to do any such magic above.

    For a generic pattern to request additional structural things to "appear later", you can use an sc_vector with delayed initialization (since SystemC 2.3.2). This is (currently) limited to a single call, but I could envision to extend this to support distributed emplace_back() calls during elaboration.  Such an approach would at least restrict the external modification of the hierarchy to much more controllable contexts.

    Regarding your current use case: How does the destination module actually use these newly added ports/sockets?

    /Philipp

  15. Any eventual copy returned from read() by value will never be optimized away, as the source location continues to exist in the signal.

    The simplest solution is to change your signal converter as follows (untested):

    template <typename Treal, typename Tcasted>
    class signal_interface_converter
       : public sc_core::sc_signal<Treal>
       , public sc_core::sc_signal_in_if<Tcasted> // only read allowed
    {
      typedef sc_core::sc_signal<Treal> real_type;
    public:
      explicit signal_interface_converter(const char* nm)
        : real_type(nm), casted_val() {}
    
      const Tcasted &read() const override {
        return casted_val;
      }
    
    private:
      void update() override {
        real_type::update();
        casted_val = static_cast<Tcasted>(real_type::read());
      ]
    
      Tcasted casted_val;
    };

    So the idea is basically to leverage the update phase to update the casted value. If the above doesn't work due to read() overloads based on the return type only, you may need to wrap it differently to separate the two conflicting interfaces (e.g. by using an internal signal with an overridden update() function, triggering the update of the casted value in the converter).

    Hope that helps,
      Philipp

  16. Two things are to be noted here:

    • IEEE 1666-2011 added support for writer policies, IEEE 1666-2005 required to have conflict detection. So keeping the default behavior was a natural choice back then
    • SC_DEFAULT_WRITER_POLICY is not part of the IEEE Std. 1666-2011, but an extension in the proof-of-concept implementation

    Last but not least, you can always use your own signal template alias:

    template<typename T>
    using sc_signal_mw = sc_core::sc_signal<T, sc_core::SC_MANY_WRITERS>

    Hope that helps,
     Philipp

  17. 11 hours ago, yair said:

    in my case it isnt an Error if we try to write to that register, the initiator can try to do write but the target doesnt have to accept it.
    i think that the right solution would be to add a condition to the b-transport() method

    You would need to describe your actual question in a bit more detail.

    • Is it about the logic inside the target (i.e. not performing the write)?  Sure, you can check the state of the control bit in b_transport before processing the command.
    • Is it about informing the initiator that this happened? Then you need to either use the response status or an extension.

    /Philipp

  18. Hi Avihai,

    I can confirm this behavior with the latest SystemC 2.3.2 pre-release and would classify this as a bug.

    As a short-term workaround, you can mark affected threads with dont_initialize(), which happens not to trigger this misbehavior:

        SC_THREAD(thread1);
        sensitive << thread1_event;
        async_reset_signal_is(reset_in,true);
        dont_initialize(); // avoid crash when starting in reset state

    I'll forward this issue to the Language Working Group for further analysis.

    Greetings from Duisburg,
      Phiipp

×
×
  • Create New...