Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation since 11/19/2018 in all areas

  1. 3 points
    SystemC 2.3.2 (and later) does support a query whether an event was triggered in the previous delta cycle (or immediately in the current evaluation cycle). This is similar to the sc_signal<>::event() function. With this, you can ask every element in your array, whether it was triggered and may have caused a wakeup of the process. Of course, multiple events can have triggered simultaneously and will only cause a single wakeup of the process. for( const auto ev& : e ) { if ( ev.triggered() ) { // ... } } Hope that helps, Philipp
  2. 2 points
    David Black

    Heartbeat, clock and negedge

    You can use it however you like. We didn't use it everywhere and I'm sure there are more areas where it might be applicable. The main point is that "Performance is a function of simulator CPU activity and how well it used." In some cases such as clocks, there is a lot of activity that goes unused. Many designs really only use the positive edge of the clock. In some designs, the activity can even be reduced significantly. Another instance is timers that often are only touched when they are set up and timeout after N clocks. The RTL approach to modeling a timer decrements the timer value on every clock. A behavioral approach would be: void set_timer( int N ) { assert( N > 0 ); delay = N * clock.period(); setup_time = sc_time_stamp(); projected_time = setup_time + delay; timeout_event.notify( delay ); } The current value of the timer can always be had with: int get_timer_value( void ) { return ( projected_time - sc_time_stamp() ) % clock.period() ); } So you really don't even need the clock in many instances. Instead replace clock.period() with a simple constant. Fast and smart SystemC models don't use sc_clock at all.
  3. 2 points
    The issue is likely caused because you access a port (via -> or for example calling functions like .read()) already inside the module constructor. You should only access ports after binding has completed, this means from within a SystemC process or in end_of_elaboration() / start_of_simulation() callbacks. Hope that helps, Philipp
  4. 2 points
    This means that a port is not bound to an interface. A port is just a kind of a forwarder of an interface. So if none is bound nothing can be forwarded. E.g. if you have a sc_in<bool> it forwards the sc_signal_in_if which allows you to read and wait for events. But there needs to be 'something on the other side' which is usually a signal (implementing the sc_signal_in_if) being bound to the socker. But without further information it is hard to provide more help. Best regards
  5. 2 points
    Thanks! I can reproduce the behavior and verified that removing the dynamic sensitivity in sc_thread_process::kill_process fixes the issue: void sc_thread_process::kill_process(sc_descendant_inclusion_info descendants ) { // ... if ( sc_is_running() && m_has_stack ) { m_throw_status = THROW_KILL; m_wait_cycle_n = 0; remove_dynamic_events(); // <-- add this line to avoid the exception simcontext()->preempt_with(this); } // ... } I'm not sure, if it is necessary to do the same for the static sensitivity. At least I haven't come up with a similar scenario, where the error is actually "incorrect".
  6. 2 points
    The problem is, when you integrate RTL IP into Loosely-Timed VP that way, the whole simulator will have a performance of cycle-accurate model. Because clock generator will be always on, and Verilated model will be triggered even if it is idle. So don't try to boot Linux with such a simulator. If your RTL IP supports power gating or clock gating, it is a good idea to disable clock generation when RTL IP is turned off. In that case you don't pay for what you don't use: you can boot your OS quickly and then enable clock generator when you start to debug IP-specific driver code.
  7. 1 point
    But even this won't fix your error. Because compiler will still complain about assigning sc_logic to double, even if it is in always false branch. What you need is if-constexpr and std::is_same if you have C++17. Or SFINAE, if you don't. Something like this may work: if constexpr (std::is_same_v<T, sc_dt::sc_logic>) { out = SC_LOGIC_Z; } else { out = 0; }
  8. 1 point
    You cannot not check typeid() against a string as this is compiler dependend. What you shoudl do is void write_out{ if(typeid(T) == typeid(sc_dt::sc_logic){ out.write(SC_LOGIC_Z); }else{ out.write(0); } } } But actually this is more a C++ related question, in my experience Stackoverflow is a good source of help. Cheers
  9. 1 point
    Eyck

    TLM extension

    Actually this is done in the desrtuctor of the tlm_generic_payload. This part calls for all extensions the free() function. So if out is handled properly destroyed all extensions are destroyed as well. The other option is to call free_all_extensions() explicitly which also calls free() for all extensions as well as for auto extensions (those might be registered when a memory manager for the generic payload is used, usually in AT style modelling using the non-blocking interfaces). HTH
  10. 1 point
    Eyck

    sc_uint and unsigned int

    unsigned int has always the length defined by the underlying platform while sc_uint<> lets you specify the exact bit with of the type. In your case case I would use 'unsigned int' as it is faster and has less overhead. Best regards
  11. 1 point
    Your calls to the configure script don't include the option --prefix to specify the destination directory for installation. This is strongly recommended as the SystemC installation layout does not match well with the standard Unix directory layout for a conflict-free installation. Personally, I prefer to install SystemC on Unix-like platforms to /opt/systemc-2.3.3 (or similar). After make install, you can make sure that the include and lib-* directory below the prefix contains the necessary files. When building against this SystemC version, you will have to pass the proper include and linker flags. I recommend that you read the INSTALL and RELEASENOTES files, which are part of the SystemC PoC implementation. You may also consider to use the experimental CMake-based build system instead of the autotools.
  12. 1 point
    David Black

    Temporal Decoupling

    I believe it would be fair to say that there are no universally accepted best practices, and the system design will dictate much of the implementation. In the case of shared memory, the target would need to have some idea that the memory of interest is shared. So you would need somewhere in the system to have a mapping. It might be the entire device, or a memory map might exist as a configurable object. When the target receives a read request for shared memory, it would then synchronize in order to be certain that any writes from the past are completed in other initiators. Depending on your design, you might be able to reduce the number of synchronizations if you can know apriori the nature of the sharing. E.g. if a block of memory was shared using a mutex, then synchronization might be limited to the mutex with the assumption that if you own the mutex, then the block is not written to by other initiators. This of course has some risks in the face of software defects.
  13. 1 point
    swami-cst

    TLM_INCOMPLETE_RESPONSE

    You seem to be probing the transaction response, before driving your transaction :) Since the default transaction status is TLM_INCOMPLETE_RESPONSE, you are getting that message. Try moving the call to b_transport before checking the trans.is_response_error() ...
  14. 1 point
    Eyck

    Checking ports for power estimations

    I'm not sure if I get your first question right. Essentially this is a C++ question. But what you could do is a type erase of your (POD) data, use it as a byte array and count the changed bits using XOR (be carefull to you plain data, no classes). Something like: struct my_data { int x; long y; }; my_data old_val, new_val; uint8_t* old_data = reinterpret_cast<uint8_t*>(&old_val); uint8_t* new_data = reinterpret_cast<uint8_t*>(&new_val); unsigned toggles=0; for(size_t i=0; i<sizeof(my_data); ++i){ uint8_t diff = *(old_data+i)^*(new_data+i); uint8_t mask=1; for(size_t j=0; j<8; ++j, mask<<=1) if(mask&diff) ++toggles; } Regarding your second question: you transport the data via a signal which implements the signal_in_if. This interface has a value_changed_event() getter which returns an event firing when the value of the signal changes. Just wait for this event.
  15. 1 point
    I think there are a few more options, but first, you need to acutely aware of the issue of OS thread safety. The SystemC kernel is not generally thread-safe unless you use async_request_update(), and a queue. I've done this several times. That said you have several options: Place software inside a SystemC SC_THREAD and provide convenience methods to initiate TLM transport calls. This is the simplest SystemC approach, but does not allow for modeling the actual CPU planned to be used and hence timing is not very accurate. This has the fastest performance from the SystemC point of view. Place the software on a development board that has the target CPU and some type of communications to the machine where you will run SystemC. I generally use TCP/IP sockets. Replace the driver with a special socket call passing packets to to a remote machine and receiving back the response. On the SystemC side, create an OS thread to receive the socket and inject the message into SystemC via the async_request_update call and an unbounded STL queue or FIFO of some type. A TLM 1.0 FIFO might do. A receiving thread can then pass the data into the SystemC simulation and return the result. This is somewhat more overhead, but allows for a more accurate target CPU. Obtain an ISS (Instruction Set Simulator) for the target processor and interface it to SystemC. This varies in complexity. You might look at existing models or talk with vendors such as Cadence or Mentor. Or perhaps your CPU vendor (e.g. Arm has some very nice models). As @Eyck suggested QEMU, DBT-RISE´╗┐-RISCV´╗┐. Also Gem5 (Google it).
  16. 1 point
    What you can do is build your driver software as a shared library with a main function. In a SC_THREAD you just load the library and execute the main function. Along with this you have to implement a few utility functions (read, write, wait) which interact with the SystemC kernel or your DUT and being used by the main function (and the called functions from there). This is called host based or host compiled simulation (you may check the search engine of your choice for it). With some infrastructure it is even possible to mimic interrupt. Another option is to use some instruction set simulator (e.g. QEMU, DBT-RISE-RISCV, or some commercial alternatives) and do you driver development using a virtual prototype... Best regards
  17. 1 point
    Hi Kevin, you are missing the raising and dropping of an objection in the run_phase method (which would be required in SystemVerilog too) to prevent the early finish of the test sequence: void run_phase(uvm::uvm_phase& phase) { phase.raise_objection(this); [...] phase.drop_objection(this); } By adding this I get the desired result: UVM_INFO @ 0 s: reporter [RNTST] Running test ... run: before fork/join Current time is 0 s =========== fun1======== Current time is 0 s =========== fun2======== Current time is 20 ns run: after fork/join Current time is 60 ns UVM_INFO ../../../../uvm-systemc-1.0-beta2/src/uvmsc/report/uvm_default_report_server.cpp(666) @ 60 ns: reporter [UVM/REPORT/SERVER] --- UVM Report Summary --- ** Report counts by severity UVM_INFO : 1 UVM_WARNING : 0 UVM_ERROR : 0 UVM_FATAL : 0 ** Report counts by id [RNTST] 1 UVM_INFO @ 60 ns: reporter [FINISH] UVM-SystemC phasing completed; simulation finished
  18. 1 point
    dynamic_cast to sc_module* would only give you access to methods of sc_module that were overridden in A. fun() and function() are not part of sc_module.
  19. 1 point
    Read up on sc_spawn and sc_process_handle. Basically, you can do something like: // Example of fork-join any std::vector<sc_process_handle> process_handles; process_handles.push_back( sc_spawn( [&](){ ... } ); //< repeat as needed ... sc_event_or_list terminated_events; for( auto& ph : process_handles ) { terminated_events |= ph.terminated_event(); } wait( terminated_events ); //< wait for any process to exit for( auto& ph : process_handles ) { ph.kill(); } // Example of fork-join none (void)spawn( [&](){ ... } ); //< repeat as needed ...
  20. 1 point
    David Black

    simple socket

    I would suggest that those for-loops need a better bound and should be coded: sc_assert( objA->initiator_socket.size() >= objB->target_socket.size() && objB->target_socket.size() > 0 ); for( int i=0;i<objA->initiator_socket.size(); ++i ) { objA->initiator_socket[i].bind(objB->target_socket[i]); } Coding rule: NEVER use a literal constant when a reasonable alternative is possible. Even when "in a hurry", you will not be disappointed if you use this rule.
  21. 1 point
    Eyck

    simple socket

    Just looked further: there is a typo in case 1. It should read //bind for(int i=0;i<3;i++){ objA->initiator_socket[i]->bind(*(objB->target_socket[i])); } as you use an array of pointers. Again, sc_vector eases your life: //Model A sc_core::sc_vector<tlm_utils::simple_initiator_socket_tagged<ModelA>> initiator_socket; ... // Model B sc_core::sc_vector<tlm_utils::simple_target_socket_tagged<ModelB>> target_socket; ... //bind for(int i=0;i<3;i++){ objA->initiator_socket[i].bind(objB->target_socket[i]); } The same applies to case 2: //bind objA->initiator_socket1->bind(*(objB->target_socket2)); objA->initiator_socket2->bind(*(objB->target_socket3)); objA->initiator_socket3->bind(*(objB->target_socket1)); Best regards
  22. 1 point
    David Black

    Initial value port

    The 'initialize(T)' method is a leftover from SystemC 1.0 circa 1999, when SystemC had not yet properly abstracted the port/channel concept. At that point in time, there was a stronger emphasis on making SystemC look like Verilog or VHDL. The 'initialize(T)' method is only present on the 'sc_out<T>' and 'sc_inout<T>' port classes, as part of their partial template specialization. The 'initialize(T)' method is not generally available to 'sc_port<>'. I usually don't mention it because then the reader gets the wrong impression that 'initialize(T)' should be present everywhere. In fact, it is only useful for RTL aspects. Certainly, this is not part of TLM. Since SystemC is more about abstraction and modeling, I avoid it. It is straightforward to override start_of_simulation. @TRANGIt is important for you to understand this distinction. I realize that the specification may say that "port is initialized to zero" or some such, but the concept of port in the specification is quite different than the concept of port in SystemC. If you don't understand this, you will hobble your understanding of SystemC. So there are three ways in SystemC of modeling what the specification says regarding an output pin on a hardware design. Depend on the underlying datatype's initial value to initialize the signal (not very flexible) If using the specialized ports (sc_out and sc_inout only), call the initialize(T) method. Write to the port during start_of_simulation, which is the most general and powerful approach. Challenge: How would you initialize an sc_fifo< float > connected to an sc_fifo< float > channel with four values? #include <systemc> #include <list> #include <iostream> using namespace sc_core; using namespace std; SC_MODULE( Source ) { sc_fifo_out< float > send_port; SC_CTOR( Source ) { SC_THREAD( source_thread ); } void source_thread( void ) { wait( 10, SC_NS ); send_port->write( 99.7 ); wait( 10, SC_NS ); std::cout << "Finished" << std::endl; sc_stop(); } // How to initialize output to contain following initial values? // { 4.2, -8.3e9, 0.0, 3.14 } // Do not add this to the thread. Instead, ensure that it happens before any thread executes. } }; SC_MODULE( Sink ) { sc_fifo_in< float > receive_port; SC_CTOR( Sink ) { SC_THREAD( sink_thread ); }; void sink_thread( void ) { for(;;) { std::cout << "Received " << setw(12) << receive_port->read() << " at " << sc_time_stamp() << std::endl; } } }; SC_MODULE( Top ) { // Constructor SC_CTOR( Top ) { m_source.send_port .bind( m_fifo ); m_sink.receive_port.bind( m_fifo ); } // Local modules Source m_source { "source" }; Sink m_sink { "sink" }; // Local channels sc_fifo< float > m_fifo; }; int sc_main( int argc, char* argv[] ) { Top top { "top" }; sc_start(); return 0; } Key concepts: SystemC is a modeling language mapped on top of C++. SystemC ports are not signals or pins. sc_in<T>, sc_out<T> and sc_inout<T> are partial template specializations of sc_port<T> on the respective sc_signal<T> interface classes. For historic reasons (SystemC 1.0), there are extra methods added to these specializations including initialize(T), read(), and write(T) that can later confuse novice SystemC programmers.
  23. 1 point
    You would need to write a wrapper class with debug aids builtin. A sketch might be: struct debug_mutex : sc_core::sc_mutex { void lock( void ) override { auto requester = sc_core::sc_get_current_process_handle(); INFO( DEBUG, "Attempting lock of mutex " << uint_ptr_t(this) << " from " << requester.name() " at " << sc_time_stamp() ); this->sc_mutex::lock(); locked = true; locker = requester.name(); time = sc_time_stamp(); changed.notify(); } void unlock( void ) override { auto requester = sc_core::sc_get_current_process_handle(); this->sc_mutex::unlock(); time = sc_time_stamp(); locked = false; locker = ""; changed.notify(); } // Attributes bool locked{ false }; sc_event changed; sc_time time; string locker; }; I have not tested above. NOTE: I have a macro INFO that issues an appropriate SC_REPORT_INFO_VERB with the above indicated syntax. Replace with your own. Never use std::cout or std::cerr if coding SystemC.
  24. 1 point
    David Black

    approximately timed

    IEEE 1666-2011 section 10.2 states: IEEE 1666-2011 section 10.3.4 states:
  25. 1 point
    You can use a custom "creator" to initialize elements of a vector with custom constructor parameters - here the inner vector. Something like this (assuming you have lambda support available): auto element_creator = [](const char* nm, size_t) // optional, depending on the "real" value type { return new sca_module(nm); }; size_t inner_size = 42; // adjust for your needs, could also be a vector of sizes element.init( outer_size, [&](const char* nm, size_t) { return new sc_vector<sca_module>( nm, inner_size, element_creator ); } ); If you don't have lambdas in your environment, you need to put the functionality in a custom function, e.g. static sc_vector<sca_module>* element_vector_creator(size_t size, const char* name, size_t) { return new sc_vector<sca_module(name, size); } // using sc_bind to pass in the size - placeholders needed for actual call element.init( outer_size, sc_bind(element_vector_creator, inner_size, sc_unnamed::_1, sc_unnamed::_2) ); Hope that helps, Philipp
×