Jump to content

Roman Popov

  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by Roman Popov

  1. At least you need to finish elaboration, using sc_start(SC_ZERO_TIME); But usually TLM-2.0 models cannot be used without SystemC scheduler, since TLM models are allowed to use SystemC primitives like processes and events.
  2. When you don't manually set CMAKE_INSTALL_PREFIX, it tries to install into ${SYSTEMC_HOME} or ${SYSTEMC_ROOT_DIR}. One nasty thing about SystemC CMake build system is that it exports targets from build directory, so you can use find_package even without installing SystemC. This may be very confusing. (But some people find it convenient - you don't need to install, just build and use) This is probably what you are encountering - You have old C++98 SystemC build directory somewhere, and find_package founds it, instead of installed modern C++ SystemC build. So I would recommend to print some informational messages in your application CMakeLists.txt: find_package(SystemCLanguage CONFIG REQUIRED) message("Using SystemC from ${SystemCLanguage_DIR}") message("Using C++ Standard: ${SystemC_CXX_STANDARD}") set (CMAKE_CXX_STANDARD ${SystemC_CXX_STANDARD}) When you run cmake it will print something like this: Using SystemC from F:/work/systemc-2.3.3/cmake-build-debug Using C++ Standard: 17
  3. You don't need to run configure if you use CMake. configure is for autotools, another build system. Most likely you have it in CMake Cache. Read how CMake set(...) works here : https://cmake.org/cmake/help/latest/command/set.html Because SystemC standard is written on top of C++03. Unfortunately it looks like we will have to wait another year or two for next IEEE SystemC standard on top of more modern C++. Because SystemC 2.3.1 had no C++11 features. But Accellera releases after 2.3.1 start to include some features made with modern C++. Thus, SystemC 2.3.3 compiled with -std=c++11 and -std=c++98 may be incompatible. More on that here: http://forums.accellera.org/topic/5738-support-for-c1114-in-systemc-232/ Yes, basically the only thing you need to do is to recompile SystemC library with C++ standard you use for your application. I linked SystemC with GTest without any issues. In general, I understand your frustration, because if you use modern Linux with gcc or clang, you get used to good binary compatibility between compiler and libraries versions. However the rest of the world is not like that. On Windows/MSVC for example you usually have to match compiler versions and flags for all libraries you link. If you are unlucky to work in semiconductor industry, you will soon find out that you are forced not only to use specific C++ standard, but also specific Linux (RHEL) and specific outdated GCC version.
  4. Oh nice, seems my workaround was wrong. I did not know that qualified name lookup suppresses dynamic dispatch of virtual functions. https://en.cppreference.com/w/cpp/language/virtual C++ is full of such traps 🙂
  5. This is strange, tlm_nonblocking_peek_if is an abstract class, so compiler should not allow you to construct it. Can you give small complete example reproducing the problem?
  6. Here is comment from Accellera SystemC 2.3.3: void sc_event_list::report_premature_destruction() const { // TDB: reliably detect premature destruction // // If an event list is used as a member of a module, // its lifetime may (correctly) end, although there // are processes currently waiting for it. // // Detecting (and ignoring) this corner-case is quite // difficult for similar reasons to the sc_is_running() // return value during the destruction of the module // hierarchy. // // Ignoring the lifetime checks for now, if no process // is currently running (which is only part of the story): I don't know what "TDB" means here, but it seems like the case when sc_event_list resides on SC_THREAD stack was not considered here. So we have two options how to fix this: Remove report_premature_destruction check. Register sc_event_lists inside process handler, and notify them when thread is terminated so it longer waits for event.
  7. int n_methods = 0; int n_threads = 0; int n_cthreads = 0; void count_procs_recursive(sc_object * parent) { if (std::string ( parent->kind() ) == "sc_method_process" ) n_methods++; if (std::string(parent->kind()) == "sc_thread_process") n_threads++; if (std::string(parent->kind()) == "sc_cthread_process") n_cthreads++; for (sc_object * child : parent->get_child_objects()) { count_procs_recursive(child); } } void before_end_of_elaboration() override { for (sc_object * obj : sc_get_top_level_objects()) { count_procs_recursive(obj); } cout << "n_methods = " << n_methods << "\n"; cout << "n_threads = " << n_threads << "\n"; cout << "n_cthreads = " << n_cthreads << "\n"; } ( Not tested. )
  8. Check section 4.2 "Simulation" of SystemC standard. Values of signals change in "Update Phase", so they don't depend on process evaluation order.
  9. Can you explain what you mean by "ugly"? You don't want to create many events? Or you don't want to connect every module manually? Second problem can be solved with a loop.
  10. Because of dynamic dispatch. https://en.cppreference.com/w/cpp/language/virtual
  11. Ok, thanks a lot for example. I'm not using UVM myself and have no idea what it does. But it looks like UVM does not allow you to call wait() inside run_phase, because it terminates the thread immediately. If you rewrite your code this way: void run_phase(uvm::uvm_phase& phase) { cout<< " run: before fork/join" << endl; // cout << "run: after fork/join" << endl; wait(40,SC_NS); cout<<"Current time is "<< sc_time_stamp() << endl; } You will see that "Current time is.." won't get printed. And because thread is terminated, event_list got destroyed and you see the error you see. From my perspective it is UVM-SystemC bug, it should prevent you from calling wait() from run_phase, by reporting something like "wait() is not allowed inside run_phase". I suggest you to post the issue to UVM SystemC support forum (http://forums.accellera.org/forum/38-systemc-verification-uvm-systemc-scv/) and see if anyone of UVM-SystemC developers can help/comment. If you put your code in a regular SC_THREAD you will see that everything works as expected .
  12. This is interesting, comment in source code says that it reports this error when event_list is destroyed, but some process is still waiting for it. Can you provide a small reproducer (complete code example) showing how you use it?
  13. SystemC has no special concept similar to SystemVerilog interfaces. Instead you can use regular SystemC modules to work as SystemVerilog interfaces. SystemC has it's own notion of interface that is similar to interface notion in object-oriented programming: interface is basically a set of pure virtual functions. So for example you can define a simple memory interface as a set of mem_read and mem_write functions. SystemC requires that interfaces inherit virtually from sc_interface: struct memory_if : virtual sc_interface { virtual uint32_t mem_read( uint32_t address) = 0; // reads data from memory at given address virtual void mem_write( uint32_t address, uint32_t data) = 0; // writes data to memory at given address }; Next you can define a module that implements and exports this interface. For example: struct memory_module : sc_module, memory_if { sc_export<memory_if> mem_export{"mem_export"}; SC_CTOR(memory_module) { mem_export.bind(*this); } private: std::array<uint32_t , 1024> mem; uint32_t mem_read(uint32_t address) override { return mem.at(address >> 2); } void mem_write(uint32_t address, uint32_t data) override { mem.at(address >> 2) = data; } }; You can have other modules implementing memory_if. For example you can write a module that translates mem_read and mem_write function calls into a cycle-accurate signal-level memory protocol. And this is what SystemVerilog interfaces often do. Now the closest analog to SystemVerilog virtual interface is SystemC sc_port. For example you can define a test_module that has a sc_port<memory_if> and sends memory reads and writes using this port: struct test_module : sc_module { sc_port<memory_if> mem_port{"mem_port"}; SC_CTOR(test_module) { SC_THREAD(test_thread); } private: void test_thread() { cout << " test_thread \n"; const int TEST_SIZE = 10; for (int i = 0; i < TEST_SIZE; ++i) { cout << hex << "Write mem[" << i * 4 << "] = " << i << "\n"; mem_port->mem_write(i*4, i); } for (int i = 0; i < TEST_SIZE; ++i) { cout << hex << "Read mem[" << i * 4 << "] == " << mem_port->mem_read( i * 4 ) << "\n"; } } }; To create a simulation you instantiate both memory_module and test_module and bind port to export: int sc_main (int argc, char **argv) { test_module tmod{"tmod"}; memory_module mmod{"mmod"}; tmod.mem_port.bind(mmod.mem_export); sc_start(); return 0; }
  14. It is explained in section 14.13 of SystemC standard. You should read all items from a) to n) to understand all usage details. From my experience, most models that I've seen do not use byte enables. But let's consider a basic example. Suppose we have a a memory target and it receives following transaction: { command = TLM_WRITE_COMMAND, address = 0x0, data[4] = { 0x1, 0x2, 0x3, 0x4 } byte_enable[4] = { 0x0, 0xff, 0x0, 0xff } } byte_enable [ N ] == 0x0 means that N'th byte of data should be ignored by target. Consider that memory had following values before processing the transaction: memory[ 0 ] == 0xA; memory[ 1 ] == 0xB; memory[ 2 ] == 0xC; memory[ 3 ] == 0xD; Since byte_enable[0] and byte_enable[2] are equal to 0x0, it means that corresponding bytes will be ignored during processing of transaction. So after processing the transaction memory will hold following values: memory[ 0 ] == 0xA; memory[ 1 ] == 0x2; memory[ 2 ] == 0xC; memory[ 3 ] == 0x4;
  15. As expected: https://stackoverflow.com/questions/52550/what-does-the-comma-operator-do
  16. Do you want to concatenate a group of sc_signal<bool> into sc_signal<sc_uint> ? You need to create a SC_METHOD to do this, for example: sc_signal<bool> bool_sig0{"bool_sig0"}; sc_signal<bool> bool_sig1{"bool_sig1"}; sc_signal<sc_uint<2>> uint_sig{"uint_sig"}; ... SC_METHOD(concat_method); sensitive << bool_sig0 << bool_sig1; ... void concat_method() { sc_uint<2> new_val; new_val[0] = bool_sig0; new_val[1] = bool_sig1; uint_sig = new_val; }
  17. This is a common confusion, but in fact there is no real parallelism in SystemC. What is called SC_THREAD process in SystemC is commonly known as coroutine in software world (or fiber, if you are familiar with Windows API). So you can assume a SystemC model is a single-threaded application. Here is what SystemC standard says: Evaluation phase So: Co-routine semantics is guaranteed. "thread-safety" issues from parallel programming textbooks are not applicable in SystemC models, because they are single-threaded. Order of process evaluation in evaluation phase is not deterministic. In your case it means any of consumers can read from fifo first. So behavior of your model is non-deterministic. To make your model deterministic "access to shared storage should be explicitly synchronized ". There is no recommended way, it all depends on a concept you want to model. Do you want to broadcast a message to all readers? What do you want to do when a reader is not ready to accept a message? Or do you want only a single reader to receive a message? In that case, which one will have the priority?
  18. Yes, with TLM sockets this won't work well: for example classes implementing TLM interfaces inside tlm_utils::* sockets do not inherit from sc_object. So you will have "not an sc_object" reported if you call print_connections function on some generic TLM model. In general such a problem is a subset of "serialization" problem, which is not possible to solve in standard C++. Because C++ wants to be "zero cost" and does not provide dynamic introspection capabilities. You can solve the problem radically by utilizing C++ compiler internals. By doing so you can implement whatever functionality you want (usually people want the same features they have in Verilog/VHDL tools). I'm not sure, but probably you can have this feature of dumping connectivity from commercial SystemC tools.
  19. I think you have answered your question yourself 🙂 In practice however most channels inherit sc_object, so they have method name(). So you can do something like this (print_connections method): #include <systemc.h> struct void_if : sc_interface { }; struct test : sc_module , void_if { struct submodule : sc_module , void_if { sc_export<void_if> SC_NAMED(void_export); SC_CTOR(submodule) { void_export.bind(*this); } } SC_NAMED(sub); struct not_sc_object : void_if { } non_sc_obj; sc_vector<sc_port<void_if>> SC_NAMED(ports, 3); SC_CTOR(test) { ports[0].bind(*this); ports[1].bind(sub.void_export); ports[2].bind(non_sc_obj); } void print_connections(const sc_object * cur, std::string padding ) const { cout << padding << cur->name(); const sc_interface * binded_iface = nullptr; if (auto exp = dynamic_cast<const sc_export_base*>(cur)) { cout << " (export) "; binded_iface = exp->get_interface(); } else if (auto port = dynamic_cast<const sc_port_base*>(cur)) { cout << " (port) "; binded_iface = port->get_interface(); } if (binded_iface) { cout << " bound to "; if (auto binded_obj = dynamic_cast<const sc_object*>(binded_iface)) { cout << binded_obj->name(); } else { cout << "not an sc_object"; } } cout << "\n"; for (auto *child : cur->get_child_objects()) { print_connections(child, padding + " "); } } void start_of_simulation() override { for (auto * obj : sc_get_top_level_objects()) { print_connections ( obj , ""); } } }; int sc_main(int, char**) { test test_inst{"test_inst"}; sc_start(); return 0; } Will print: test_inst test_inst.sub test_inst.sub.void_export (export) bound to test_inst.sub test_inst.ports test_inst.ports_0 (port) bound to test_inst test_inst.ports_1 (port) bound to test_inst.sub test_inst.ports_2 (port) bound to not an sc_object Sometimes you also want to print hierarchical port-to-port connections (instead of port-to-interface) . This I think is not possible. For our internal uses we had modified Accellera SystemC kernel to preserve information about hierarchical connections.
  20. Nice, I didn't know such a method exists! Looks like it does what you want.
  21. In addition to what David said. Here is what SystemC standard says: So SystemC standard guarantees that signal value is initialized by default constructor. So for example sc_signal<int> is guaranteed to be initialized with 0. Accellera SystemC also supports passing initial value as a second constructor parameter: sc_signal<int> int_signal{"int_signal", 42}; // Initialized to 42 Looks like this feature is not part of IEEE standard yet. As David noted, ports in SystemC have no value, they behave more like a pointers to channels. So "value" of port always equals to value of binded channel.
  22. Hi all, For debugging purposes it may be useful to add all signals in design to trace file. However, sc_signal::trace() which may allow to do it automatically is deprecated. Why is that? In general I think it will be useful to add trace method to sc_interface, so that all channels that support tracing can implement it. And then it will be possible to implement something like: sc_trace_all(sc_trace_file *tf) // add all objects that support tracing to trace file.
  23. Yes, but direction of binds matters. For example if you want to bind ( in0 -> in1 -> signal ) then you will need to write: in0 ( in1 ); in1 ( signal ); OR in1 ( signal ); in0 ( in1 ); In your examples you bind like this: Ex1: port0 <- port1 -> signal // port0 not binded Ex2: signal <- port0 -> signal // port0 binded to 2 signals I agree that error message in first case is misleading.
  24. Do you use SystemC 2.3.3? Check RELEASENOTES file.
  25. This is a very old style. With a modern SystemC you can have the same with sc_vector: //exxe.h class EXXE : public sc_module { public: sc_vector<sc_in<sc_dt::uint64>> SC_NAMED(clock,3); sc_vector<sc_in<bool>> SC_NAMED(input,5); EXXE(sc_module_name); ~EXXE(); } But as David mentioned, before starting learning SystemC you should learn C++ first. Trying to understand SystemC without being proficient with C++ is a waste of time.
  • Create New...