Jump to content

Roman Popov

Members
  • Posts

    353
  • Joined

  • Last visited

  • Days Won

    47

Everything posted by Roman Popov

  1. If you use SC_THREAD that means that you are using sc_event under the hood. Because every SC_THREAD in SystemC has event that activates the process. You can for example spawn a SC_THREAD process to do delayed assignment like this: #include <systemc.h> struct spawned_assignment_demo : sc_module { SC_CTOR(spawned_assignment_demo) { SC_THREAD(test_thread); } sc_signal<int, SC_MANY_WRITERS> out{"out", 0}; void print_out_value() { cout << " out value = " << out.read() << " at " << sc_time_stamp() << "\n"; } void test_thread() { print_out_value(); sc_spawn([&]() { wait(10, SC_NS); out = 42; }); wait(5, SC_NS); print_out_value(); sc_spawn([&]() { wait(10, SC_NS); out = 124; }); wait(6, SC_NS); print_out_value(); wait(6, SC_NS); print_out_value(); } }; int sc_main (int argc, char**argv) { spawned_assignment_demo demo{"demo"}; sc_start(); sc_stop(); } Output will be as expected: out value = 0 at 0 s out value = 0 at 5 ns out value = 42 at 11 ns out value = 124 at 17 ns But when you call wait(10,SC_NS) you create timed event notification. You can use debugger to step into wait() to see what happens there.
  2. No, it will be using event one way or another. Verilog implementation most likely also uses even queue under the hood. It just has a more pretty syntax for it.
  3. https://stackoverflow.com/questions/23407635/append-compile-flags-to-cflags-and-cxxflags-while-configuration-make
  4. If your issue is that you don't like to have two variables: data and event, you can create a convenient class like sc_signal, that will pack data end event together. Essentially it will be sc_fifo with immediate notification and capacity == 1. If consumer reads from empty fifo it will be blocked until producer writes into fifo. With immediate notification it will be resumed in the same delta cycle as producer writes into fifo.
  5. Ok, understood. Yes, the only way to execute this in a single delta cycle is to enforce execution order using events with immediate notification. It is impossible to implement it in other way in C++. What you want is a “SystemC-language-aware” compiler. Such a compiler will need to understand a semantics of processes and create a static schedule: such as a producer processes are always executed before consumer processes. Static scheduling is not always possible, because some processes can have cyclic data dependencies. So as you’ve said, sometimes it will need to resolve <<undeterminacy by iterating until a fix-point is reached>> But since SystemC is just a library, it can't guess what your processes code is doing (because C++ has no code reflection capabilities).
  6. Can you please elaborate what do you mean by instantaneous broadcast for those who are not familiar with named formalisms? Some small example will be the best.
  7. On Windows both Visual Studio and MinGW work well with SystemC. Since your question is not about SystemC, but rather about C++ language, I recommend to extract minimal code sample and ask in C++ community. For example on https://stackoverflow.com/
  8. Indeed, my reasoning was wrong for fifo case. Thanks for correction! Ordering is also important however. If you have more than two communicating processes in a system, non-determinism in execution timing can eventually lead to non-determinism of processing results. Also, reader for example can be implemented like this: while(!fifo.num_available()){ // change model state ... wait(1,SC_SEC); } But at the end, it's up to you to decide what should be semantics of your FIFO, depending on your particular modeling case. In SystemC you can also use immediate notifications, instead of delta-notifications. In that case, when reader is blocked on empty fifo, it will be executed in same delta cycle when writer puts new data.
  9. Both questions are answered in SystemC standard (http://standards.ieee.org/getieee/1666/download/1666-2011.pdf) sc_prim_channel interface is used to avoid race conditions when two or more processes access channel in same delta cycle. Example: Suppose that you have non-empty fifo, and you have two processes scheduled in same delta cycle: one wants to write fifo, second wants to read. Standard does not specify any ordering for processes running in the same delta cycle. So we have two possible scenarios: 1) write first, read second; 2) read first, write second. Depending on order either old or new data will be read. So we have undefined behavior here. In general however you want simulations to be deterministic. SystemC channels have "delayed write" semantics, so that write is not executed immediately, but instead performed in a special scheduling phase called "update phase". So simulations that use SystemC channels for IPC are always deterministic: when two processes are executed in a same delta cycle, it is guaranteed that consumer process always reads "old data". I highly recommend reading chapter "4. Elaboration and simulation semantics" of standard, to understand how SystemC scheduler and sc_prim_channel interface work. It is called when port is bound to channel. See "5.14.4 register_port". Personally I don't generally use it, by default it just does nothing.
  10. Your code compiles w/o any errors on my machine with g++. SC_HAS_PROCESS macro is not required, since you have used SC_CTOR. I recommend to look how SC_MODULE, SC_CTOR, SC_HAS_PROCESS and SC_THREAD are expanded to understand what each one is doing.
  11. Can you please give some simple reproducer code sample? In my case srand(time(NULL)) works as expected: I always get some new number.
  12. One drawback of before_end_of_elaboration solution is that we can't remove pointer to temporary storage of ports to bind. SC_MODULE (foo) { std::vector <port_type *> * deffered_bind_ports; void before_end_of_elaboration() { for (auto port* : *deffered_bind_ports) create_and_bind_port( port ); delete deffered_bind_ports; deffered_bind_ports = nullptr; } }; So it will be forever there as a class member. This can be workarounded by some global variable, but does not looks good to me either.
  13. Templates are compile time feature in C++. So you can't use values that are not compile-time constants as non-type template parameters. Instead use a constructor parameter to pass arguments: SC_HAS_PROCESS(select_unit); select_unit(::sc_core::sc_module_name name, std::vector<unsigned> &sbits) : sel_bits(sbits) { /*...*/ } std::vector<unsigned> &sel_bits;
  14. My understanding is that it does not solves the problem with processes. In the past I was using this technique in RTL verification, to add ports to fabric verification model on request. And very often you need to add a process that will handle bus protocol. Pseudocode: add_slave_port ( master_type *master ) { sc_object::hierarchy_scope scope(this); slave_port_vector.emplace_back bind_master_to_slave spawn_protocol_service_thread } In general you need a separate process for each port, because protocols can be multicycle and each port can be in a different state. But I agree that emplace_back for sc_vector will be more convenient than using regular vector of pointers.
  15. Update: In sysc/kernel/sc_object_int.h there is a scope guard (https://stackoverflow.com/questions/31365013/what-is-scopeguard-in-c) for this particular case. So it is possible to rewrite code in previous answer like this: #include <systemc.h> #include <sysc/kernel/sc_object_int.h> ... struct bottom : sc_module { ... void add_input() { sc_object::hierarchy_scope scope(this); inputs.emplace_back(std::make_unique<sc_in<bool>>(sc_gen_unique_name("input"))); }
  16. It is possible, but : Not standardized Requires knowledge of SystemC kernel internals If it's ok for you, take a look on sc_object::sc_object_init (sc_object.cpp) to understand how it is created. Now you can try something like this: (I'm creating sc_in ports, instead of tlm sockets here): #include <systemc.h> struct bottom : sc_module { std::vector<std::unique_ptr<sc_in<bool>>> inputs; void add_input() { sc_get_curr_simcontext()->hierarchy_push(this); inputs.emplace_back(std::make_unique<sc_in<bool>>(sc_gen_unique_name("input"))); sc_get_curr_simcontext()->hierarchy_pop(); } SC_CTOR(bottom) { } }; struct top: sc_module { bottom b{"b"}; SC_CTOR(top) { b.add_input(); b.inputs[0]->bind(bsig); b.add_input(); } sc_signal<bool> bsig; }; int sc_main(int argc, char** argv) { top t{"t"}; sc_start(); return 0; } If you run it, you will have an error message with correct hierarchical name: Error: (E109) complete binding failed: port not bound: port 't.b.input_1' (sc_in) I think your usage case is legitimate and we need to think of standardizing some solution.
  17. In Accellera SystemC you can use simulation time callbacks. Check systemc/RELEASENOTES : ( Experimental features - Extended Simulation Phase Callbacks ). You will need SC_BEFORE_TIMESTEP callback. But they are not yet part of IEEE standard, so portability is not guaranteed.
  18. If you need something opensource for learning than you can try GreenSocs examples (https://www.greensocs.com/get-started) . For commercial models contact you IP vendor.
  19. Yes, but in most cases copy is not created , but used as rvalue in computations. Most likely end user can do something like: auto val = some_signal.read(); that also creates a copy anyway. Great idea. Unfortunately it is not possible to apply it for hierarchical bindings, that are also quite common case where type conversion happes, i.e. when you bind sc_in<T1> to sc_in<T2>
  20. What exactly does not work for you? Did you tried 2.3.2 draft? http://www.accellera.org/downloads/drafts-review
  21. Hi Ralph, Compiler should optimize a copy when possible. So it should not be a performance bottleneck. Its not my problem, but it is a problem in general. A write-through to temp_val will be a SC_METHOD sensitive to value_change_event(). This will be a performance problem when a lot of traffic goes through such a converter Yes, this is another issue.
  22. Hello, I need to connect Verilated model into SystemC testbench . Testbench was written for bit-accurate types like ( sc_in<sc_int<exact_bus_width> > ). Verilator uses sc_in<uint32_t> and sc_in<uint64_t> for all buses that are under 64 bits in width. How can I efficiently convert between two? I know there is a solution with additional SC_METHOD that converts from T1 to T2, but it is unfortunately affecting simulation performance, since I'm introducing another method and signal to design. Instead I've implemented converter from sc_signal_inout_if<T1> to sc_signal_inout_if<T2> and it worked fine for my simulation. But there is a potential problem with a read() method, since it returns a reference: template <typename Treal, typename Tcasted> class signal_interface_converter : public sc_signal_inout_if<Tcasted> { const Tcasted &read() const override { temp_val = host_ptr->read(); return temp_val; } private: Tcasted temp_val; sc_signal_inout_if<Treal> * host_ptr; } In Verilator integration context it looks safe: Verilator converts whole design into a single SC_METHOD so it should not reuse reference between different delta cycles. However in general I can write a code like that: auto & address_ref = addr_signal.read(); wait(); memory[address] = 42; // address_ref may have changed after wait. So basically returning value by reference in read() disables a potential optimization for sc_signal converters :( And the benefits of returning a reference are not clear to me.
  23. Ok, what I've learned that in SystemC 2.3.2 default can be overridden in runtime. So it should close the issue once released.
  24. I have used two SystemC HLS synthesis tools in practice: First one had SC_MANY_WRITERS as default. So every sc_signal<T> is in fact sc_signal<T,SC_MANY_WRITERS> in this tool Second tool allows synthesis of sc_signal<T,SC_MANY_WRITERS> SC_MANY_WRITERS is safe for synthesis and have a clearly defined representation in multiple SC_CTHREAD drivers case: a register with a multiplexer. Unlike SC_UNCHECKED_WRITERS, when two SC_CTHREADS write to signal in same delta cycle error will be detected and reported. I agree that two SC_METHODs driving same sc_signal has no clear HW representation. Test-benches are main reason why I think it should be SC_MANY_WRITERS by default. In testbenches sc_spawn dynamic processes are commonly used to model complex test scenarios. As soon as dynamic thread touches sc_signal your simulation immediately fails. So you have two options: Manually replace all top-level DUT pins to sc_signal<T, SC_MANY_WRITERS> Compile with SC_MANY_WRITERS by default. Most likely users will chose second path. So why not making it as default? Verilog/SystemVerilog has "SC_MANY_WRITERS" by default and I don't hear any complains about it (multiple drivers are detected by synthesis tools, but is ok for SV testbenches). Verilog authors in theory can make "SC_ONE_WRITER" behavior a default and offer some keyword to override it. But no one asks for it. So in my view common industry knowledge is that SC_MANY_WRITERS by default is ok. Because there are so many practical cases when you need it.
  25. Hello, Why SC_MANY_WRITERS is not default SC_DEFAULT_WRITER_POLICY in SystemC? In my opinion SC_ONE_WRITER does more harm then profit. Sooner or later every user faces the fact that he will need to change this default during the SystemC compilation. So why not to change it out-of-the-box?
×
×
  • Create New...