Jump to content

Philipp A Hartmann

Members
  • Content Count

    525
  • Joined

  • Last visited

  • Days Won

    126

Everything posted by Philipp A Hartmann

  1. The creator struct and its use looks fine to me. What's suspicious is indeed the second call to ~mod() from within ~mod() itself. What types of members do you have in mod? How do you initialize them? Do you (ab)use smart-pointers? What happens, if you add the following line to sc_main? delete new mod("tmp", args1, args2); Can you assemble a minimal but compilable example demonstrating the problem? From your description above, it's more or less just the (relevant parts of the) definition of mod that's missing. @dakupoto: The sc_vector itself will cleanup its allocated elements. A creator function/class for sc_vector is one of the rare examples, where users must not write their own delete to the new. /Philipp
  2. Of course, sc_thread_process::prepare_for_simulation is called for dynamic processes as well, see sc_simcontext::create_[c]thread_process. But you should be able to observe the start of the simulation before the assertion fails, in case of a later resource exhaustion being the problem. You don't provide sufficient information about what kind of module you instantiate 6000-8000 times. Based on your comments, you seem to have at least 12000 threads in your simulation, which indeed seems to be quite many. You could try to add a simple (static) counter to the stack_protect call and print out a message for the first failing mprotect call to shed some more light on this issue. My guess would still be that some OS resources are running low. /Philipp
  3. It depends on your particular scenario, whether there is a significant benefit in maintaining a complex set of DMI allowances. If you think it is necessary, I would recommend to implement a dedicated data structure to store the DMI access information, which you can reuse and test separately keep the ranges non-overlapping and sorted by start-address (and probably have two sets for reads and writes in the initiator) use std::lower_bound to efficiently search the sorted vector internally merge adjacent/overlapping entries to keep the number of entries small forward the invalidate_direct_mem_ptr call to update the ranges in the (internal) vector /Philipp
  4. First of all, it is recommended to keep the stack protection activated (where available) to avoid arbitrary memory corruption upon stack overflow. Stack-related memory corruption is very hard to debug... Assuming, you're running on Linux, then secondly, there are indeed (kernel) resources allocated within the mprotect call. Quoting the mprotect(2) manpage: ERRORS ... ENOMEM Internal kernel structures could not be allocated. ENOMEM Addresses in the range [addr, addr+len-1] are invalid for the address space of the process, or specify one or more pages that are not mapped. (Before kernel 2.4.19, the error EFAULT was incorrectly produced for these cases.) Activating the memory protection on some page(s) requires to setup an appropriate memory mapping, of which a limited number can be created by the kernel. You can try to increase this limit by $ sudo bash -c 'echo 131060 > /proc/sys/vm/max_map_count' (which doubles the default number on my system). Last but not least, some additional questions: Do you encounter the problem during elaboration or during simulation? If it occurs during simulation, you may have "leaking" dynamic processes. If possible, try to reuse dynamic process instances you've created.Which version of SystemC are you using? There has been an internal "dynamic process/object leak" in some cases, which has been fixed in SystemC 2.3.0.Can you change some thread processes to method processes? SC_METHODs have a significantly lower simulation overhead. Especially, when you're using thousands of processes, this may be a worthy optimization.Hope that helps, Philipp
  5. The problem in those two lines is probably that you never clean up your p_dmi vector? Does it help to empty it when invalidating the DMI access in invalidate_direct_mem_ptr? p_dmi_enabled = false; p_dmi.clear(); Otherwise, you may need to implement a more efficient way to handle multiple active DMI accesses with a custom DMI table wrapped around p_dmi, allowing more efficient address range handling and lookup, e.g. by combining and sorting address ranges and avoiding duplicates. NB: Obviously, modelling a memory latency in the initiator does not improve the simulation speed of the model. I probably misunderstood your original question here.
  6. The code is still quite wrong: an array of pointers is not a two-dimensional array and won't work at all. You need to pass a contiguous memory block as data pointer in the generic payload. As said in my previous answer, you need to provide a buffer of the target type (i.e. the two dimensional array), not a raw pointer of unsigned char. Otherwise alignment, offsets and arithmetic will go completely wrong. I don't really understand, what you intend to do in print_memory, as there are still quite strange constants in use (128, 32, vs. MSIZE1, MSIZE2). Nevertheless, I tried to address your "casting issue" in the following (fully untested) snippet: template <typename DATA_TYPE, unsigned int BUS_WIDTH> void traffic_injector<DATA_TYPE,BUS_WIDTH>::print_memory(){ tlm::tlm_generic_payload trans; // no need to use a pointer, avoid memory leaks! trans.set_address(0); trans.set_read(); trans.set_data_length(32); // you only read 32 bytes?! // don't use a char pointer. use the "real" type instead // again, no need for dynamic allocation with leaking memory uint32_t data[MSIZE1][MSIZE2]; // sizes are just a guess // cast for generic payload data pointer trans.set_data_ptr(reinterpret_cast<unsigned char*>(data)); // don't you want to read the whole memory? trans.set_data_length( sizeof(data) ); uint32_t count = initiator_socket->transport_dbg( trans ); for(uint32_t j=0;j<MSIZE2;j++){ for(uint32_t i=0;i<MSIZE1;i++){ // no cast here, correct pointer arithmetic printf("MEM[%d][%d]=%d \n",j,i,data[i][j]))); } } } Side note: Please educate yourself about the C++ memory model and management. You leak memory in nearly every function. Never use new without a matching delete, C++ is not Java or C#. hth, Philipp
  7. I didn't review the full source code. To add a delay to the DMI access, you can add a wait after the memcpy call: if( i->get_write_latency() != sc_core::SC_ZERO_TIME ) wait( length * sizeof(*data) * i->get_write_latency() ); The write latency needs to be filled by the target and/or interconnect modules that fill the tlm_dmi information. As said before, see IEEE 1666-2011, Section 11.2 for details, especially 11.2.5 (ab)-(ad). Generally speaking, you should respect the time annotation received from the target/interconnect in the non-DMI case as well. The corresponding wait(t) call is currently disabled in your code. hth, Philipp
  8. When using DMI, the initiator is responsible for modelling read/write latencies, since no explicit transactions are generated for each access. Unless your processor model adds additional delays for the direct memory accesses internally, it is expected that DMI does not consume simulation time. To approximate the access delays, the target (and the interconnect in-between) can fill the latency fields in the tlm_dmi structure during the get_direct_mem_ptr call, when granting the DMI access to the initiator: class tlm_dmi { public: // ... sc_core::sc_time get_read_latency() const; sc_core::sc_time get_write_latency() const; // ... void set_read_latency(sc_core::sc_time t); void set_write_latency(sc_core::sc_time t); }; For more information, see IEEE 1666-2011, Section 11.2. Greetings from Oldenburg, Philipp
  9. In the print_memory function, you allocate (and never delete, but this is a separate problem) a plain unsigned char array: unsigned char* data = new unsigned char[128]; trans->set_data_ptr(data); Later in that function, you try to use it as a two-dimensional array: printf("MEM[%d][%d]=%d \n",j,i,*(reinterpret_cast<uint32_t*>(&(data[i][j])))); Obviously, this can't work. Apart from the invalid dimensions, the offsets and alignments will also be quite wrong for casting the values to uint32_t, I suppose. Instead, you should use a properly typed array within print_memory (maybe even related to the DATA_TYPE template argument?) and cast it to unsigned char when passing it to the set_data_ptr of the transaction. Some other observations in that function: Why do you allocate the generic payload object with new? (memory leak) Why do you allocate the array with new? (memory leak) Using hard-coded constants (128, 32, ...) seems to be inconsistent with MSIZE1, MSIZE2… I didn't look at the other parts of the code. /Philipp
  10. Side note: If you don't use dynamic processes in your code, just using the aforementioned convenience sockets should no longer require to manually define SC_INCLUDE_DYNAMIC_PROCESS since the release of (Accellera proof-of-concept) SystemC 2.3.1 and its bundled TLM version. /Philipp
  11. You need to keep out "sc_main_main.cpp" (and "sc_main.cpp", providing the main() function) from your DLL building project. Windows DLLs do not support unresolved symbols, unlike ELF shared objects do. You then need to add (a library containing) sc_main(_main).cpp/obj to your application, to provide a main and to properly initialize the kernel via "sc_elab_and_sim". Of course you can adjust the implementation of this function to not call (the unresolved) sc_main function but add another mechanism to enter the user model at this point. hth, Philipp
  12. No, you can also explicitly init() the sub-vectors in a loop, as you did before. A creator function is only strictly needed if you are forced to call a custom constructor for the vector elements, e.g. with additional parameters. In the vector of vector case, this can be the size of the vector. You can pass any C++ entity that can be called with a signature (const char*, size_t) and returns a pointer of the correct vector member type. The sc_bind example is a convenient way to pass additional information to the creator. By binding a member function, you can for example access the members of the module within the creator. In both examples you have quoted after your question, you see the options to pass additional information to the creator. In case 1, it is an explicit constructor argument passed to the my_module_creator function object, in case 2, it's the this pointer, passed to sc_bind. With a C++ lambda, you can use the various "capturing" mechanisms. /Philipp
  13. Yes, if you use an sc_vector in your sub-module //possible alternative: but how do i bind this using sc_assemble_vector sc_vector<sc_in<sc_uint <16> > > in; SC_CTOR(sub_module) :in("in",3) // note the required name prefix, naming objects is good style anyway {} and an sc_vector of sc_vectors for the top-level ports (or a flattened sc_vector and then use the iterator-based binding API), you can use sc_assemble_vector to bind the whole hierarchy in one call (outside of the loops): // vector of ports sc_vector<sc_vector < sc_in<sc_uint<16> > > > in_vec; // ... and in the constructor sc_assemble_vector( m_sub_vec, &sub_module::in).bind( in_vec );If you want it even more neatly, use a "custom creator" to initialize the two-dimensional port vector: // creator function (beware of the hard-coded size 3) static sc_vector<sc_in<sc_uint<16> > >* create_in_vector(const char* nm, size_t ) { return new sc_vector<sc_in<sc_uint<16> > >(nm,3); } // constructor - look, ma', no loops! module( sc_core::sc_module_name, unsigned n_sub ) : m_sub_vec( "sub_modules", n_sub ) // set name prefix, and create sub-modules , in_vec( "in_vec" ) // set name prefix, no init yet { in_vec.init(n_sub, create_in_vector); // use custom creator // push it even further: use a C++11 lambda as a creator // in_vec.init(n_sub // , [](const char* nm, size_t) // lambda function // { return new sc_vector<sc_in<sc_uint<16> > >(nm,3); } ); // do the binding sc_assemble_vector( m_sub_vec, &sub_module::in).bind( in_vec ); } Hope that helps, Philipp
  14. Before trying to answer your question, can you provide some more information? Why don't you use an sc_vector of boolean input ports instead of an array in your submodule? How do you want to wire up the top-level module signals with the submodule signals (indexwise)? Can you provide a stripped-down but compilable code example? (add comments for the missing parts) In general, it is possible to have both an array of sc_vectors as well as an sc_vector of sc_vectors. If you don't want to use a custom "creator function", you just need to take care of the proper initialisation yourself. sc_assemble_vector might not be of much help (depending on the intended topology). You can still do the binding manually in a nested loop over both dimensions, of course. /Philipp
  15. And one should add that generating a DOT file along the way instead of merely printing the hierarchy is not significantly more complex.
  16. Yes, static members are shared among all instances of a class. But, a class template is more like a recipe to build classes. The instances of a class template are all different classes. The static members of "t1" and "t2" can differ, because t1 and t2 are instances of different classes (my_top<17> and my_top<42>). These classes just happen to both have static constants named depth, etc. as they are cooked (instantiated) from the same recipe (template). /Philipp
  17. I don't understand, why using the top module with different values of TOP_DEPTH is a showstopper for declaring the DEPTH_MODULE_xxx as static members of my_top. Integer template arguments have to be known at compile-time in order to instantiate the correct type. Would something like the following be sufficient for your situation? template <int DEPTH=10> SC_MODULE(sub_module_xxx); template <int TOP_DEPTH=10> SC_MODULE(my_top) { static const int depth = TOP_DEPTH; // I usually forward template constants to proper member constants static const int depth_a = depth + 1; // or other math on depth static const int depth_b = depth / 2; // ... sub_module_a<depth_a> sub_a; // no need for pointers? sub_module_b<depth_b> sub_b; SC_CTOR(my_top) : sub_a("sub_a") // initialize sub-modules , sub_b("sub_b") {} }; int sc_main(int, char*[]) { my_top<17> t1("t1"); my_top<42> t2("t2"); sc_assert( t1.depth == 17 && t1.depth_a == 18 ); sc_assert( t2.depth == 42 && t2.depth_b == 21 ); return 0; } Greetings from Oldenburg, Philipp
  18. See the IEEE 1666-2011 standard, chapter 4 for details about the SystemC elaboration and simulation semantics. If you don't "need" to run the initialization phase separately, you don't need to worry about this detail. The first invocation of sc_start will take care of completing the elaboration and initialization automatically. There is an example in the SystemC standard (in section 4.5.7), which shows a simulation running in individual steps: In this example, you need the explicit initialization call. /Philipp
  19. You should not use sc_initialize(). It's deprecated and not part of the IEEE 1666 SystemC standard. If you want to prepare the simulation without running it yet, your can complete the elaboration and run the initialization via sc_start( SC_ZERO_TIME ); from within your sc_main() function. Afterwards, you can call sc_start multiple times again, e.g. with non-zero duration arguments, to perform the simulation itself. /Philipp
  20. The answer is slightly more complex than you might think. 1. Arithmetic operations on sc_(u)int<> are performed on 64-bit accuracy internally This is by design, see IEEE 1666-2011, Sections 7.5.2.6, 7.5.3.6. The limited-precision integer types have a conversion operator to their C++ representation (sc_dt::(u)int64) and the arithmetics are performed according to rules of the C++ standard. 2. Signed modulo expressions in C++ are implementation-defined Quote from ISO/IEC 14882:2003(E), 5.6.(4) (formatting and highlighting by me): 3. C++ integer promotion rules will give you an unsigned operation anyway Integer arithmetics in C++ are subject to operand promotion rules to determine the appropriate common type for the computation (called usual arithmetic conversions). In case of mixed signed and unsigned 64-bit types, this will usually be a 64-bit unsigned type. Details can be found in the C++ standard as well, see clause 5(9). Note: in C++11, the wording has been slightly extended to explicitly cover 64-bit integer types. Hope that helps, Philipp
  21. In case you've found a solution to your question all by yourself, please consider adding an answer with your findings instead of emptying out your previous post. This way, all users can benefit in the long run. Thanks, Philipp
  22. Assuming that you mean "Visual Studio" by mentioning "VS", please see http://forums.accellera.org/topic/71-/ for a solution. BTW, this is documented in the INSTALL instructions of SystemC. /Philipp
  23. I would expect your example to fail (statically) linking already, because you #include <systemc> already. If you want to have SystemC as a dynamic dependency only, you need to hide this behind a properly defined "plugin interface", as you have sketched with "create_object" and "destroy_object" already. Due to the C++ name mangling, this plugin interface should be declared as extern "C". That said, two final remarks: All of this is rather off-topic here, as it is not specific to SystemC itself, but to C++ shared libraries and "plugins" in general. You should always include the error messages you're seeing. Greetings from Oldenburg, Philipp
  24. Conversion from char to sc_logic is not possible in all cases. But fortunately, you can find the solution to your question is Section 7.9.2.8 of the IEEE Std. 1666-2011for SystemC (emphasis added): You can the use ctrlout.write( sc_dt::SC_LOGIC_Z ); to write to the port/signal directly. Greetings from Oldenburg, Philipp
  25. Yes, there is an sc_pause in SystemC (since IEEE 1666-2011 / 2.3.0). By calling this function from within your simulation, you can return to sc_main (see IEEE 1666-2011, Section 4.5.2). Please note that in the context of the SystemC AMS extension, pausing the simulation may be somewhat dangerous, as noted in Section 5.1.1 of the AMS extensions 2.0 LRM: That said, the use cases you have described seem to be blocking in nature. Why not simply call the code to ask for user input directly from the "specified point in the code"? In this case, you don't need to interfere with the simulator directly. NB: sc_halt is not part of IEEE 1666 and is not meant to be used in new code. Greetings from Oldenburg, Philipp
×
×
  • Create New...