Jump to content

David Black

Members
  • Posts

    678
  • Joined

  • Last visited

  • Days Won

    151

Everything posted by David Black

  1. See https://www.edaplayground.com/x/S5J3 for an example of my suggestion. W.r.t. the question, no we don't have macros such as UVM's uvm_analysis_imp_decl. Most C++ programmers consider macros to be evil and dirty. The C++ standards are evolving C++ to remove the need for macros altogether. Macros create bugs that are messy to debug and avoid proper type checking etc. For example, consider: #include <iostream> #define MAX(x,y) (((x)>(y))?(x):(y)) int main() { int i=1; double j=1.1; std::cout << MAX(++i,j) << '\n'; //< what gets printed? return 0; } Those macros were required in uvm because SystemVerilog has a number of syntactic issues due to the time at which it was invented. They lacked proper multiple inheritance was the primary issue. To be sure, there are still some good uses for macros, but we need to be thinking of eliminating them when possible.
  2. First, there are no "register" methods for basic SystemC nor is this needed. This is really just a basic C++ question; however, you can use sc_export as part of the solution. You could redirect the call to a submodule that implements the method and bind the appropriate sc_export there. That is what the convenience sockets of TLM-2 do to implement their registration mechanism.
  3. Use a TLM tagged socket from the utilities. There are several to choose from. They allow you to register the method of choice or you can use the tag to differentiate.
  4. There are no sticky methods as you suggest; however, it would not be too hard to construct. Here is an outline: struct Some_module : sc_module { sc_buffer<Data_t> my_buff; bool changed{false}; SC_CTOR( Some_module ) { SC_METHOD( method_process ); sensitive << my_buff; SC_THREAD( main_thread ); } void method_process() { changed = true; } bool was_changed() { auto result = changed; changed = false; return changed; } void main_thread() { ... do stuff ... if( was_changed ) { ...stuff with changed info } ... } }; Of course, you could replace my_buff with port (sc_in<Data_t>) that was tied to the (sc_buffer<Data_t>) externally.
  5. The definition of a channel in systemc is a class that implements one or more interfaces and a base class. In the case of sc_fifo, the base class is sc_prim_channel, and there are two interfaces, which you appear to have discovered. However, you are trying to derive from the interfaces, which will not work. Interfaces are merely the API and should/do not contain implementation. You should derive from the sc_fifo<T> class directly to override the read method. Then you could call sc_fifo<T>::read(). This is all basic C++ stuff once you see the architecture. // The following is for conceptualization and does not represent the full details of sc_fifo, // which can be found in the IEEE-1666-2011 standard document. The depth might be an int. template<typename T> struct sc_fifo_in_if : virtual sc_interface { virtual T read() = 0; }; template<typename T> struct sc_fifo_out_if : virtual sc_interface { virtual void write(const T&) = 0; }; template<typename T> struct sc_fifo : sc_prim_channel, sc_fifo_in_if<T>, sc_fifo_out_if<T> { sc_fifo(char* nm, size_t depth = 16) : sc_prim_channel{ nm }, m_depth{ depth } { sc_assert( depth > 0 ); ... } T read() override { ... } void write(const T& data) override { ... } // other methods... }; // Outline of what you do template<typename T> struct my_fifo<T> : sc_fifo<T> { // IMPORTANT: You must pass an instance name and depth back to the base class static constexpr const size_t default = 0; // or whatever value you want my_fifo(char* nm, size_t depth = default) : sc_fifo<T>{ nm, depth } { sc_assert( depth > 0 )... } T read() override { auto data = sc_fifo<T>::read(); // log data from here return data; } };
  6. Why use SC_METHOD instead of SC_THREAD? If your answer is performance, you are very likely committing the sin of premature optimization. The overhead of SC_THREAD is very minimal (a few percentage points), and the benefit of easier coding is very high. SystemC is as much about faster coding as it is about high-performance models. Anything you can do in SC_THREAD can be accomplished in SC_METHOD, but with a complexity cost (i.e., increased development time and the likelihood of bugs). You could use next_trigger( time); return, and add some state to your code in order to resume at the proper point.
  7. If you are using Modern C++ (C++14 or C++17 would be best), then you could accomplish this with constexpr functions. You should also consider using sc_vector. Vanilla C/C++ arrays are simply evil anyhow. For non-SystemC components (i.e., modules, ports, channels) use std::vector<T> or std::array<T>). Also, you would save some code space if you used simple constructor arguments for your ShiftRegister and implement with std::vector.
  8. Read up on fork-join, fork-join_any, and fork-join_none. You can disable any labeled block or task. You can also kill processes if you know the process id. Your "After fork" won't execute until ALL three processes within the join complete. You likely want fork-join_any or fork-join_none. Also, you only have three threads/processes (the three labeled blocks). Your $displays inside those threads will execute sequentially.
  9. The message means "You have not connected the port core_pim.decoder_addr.IN." The error happens at the end of elaboration and so you never get to sc_start.
  10. I won't show you a library, but I have one comment: SystemC with TLM, in general, is generally used to model cycle "approximate", which is the basis for the Approximately-Timed modeling style described in the standards document. Virtual platforms and behavioral models tend to favor the less detailed Loosely-Timed modeling style for reasons of performance. If you model at the cycle "accurate" level, then I don't see why you don't just go for RTL modeling since the overhead of cycle accuracy will tend to slow down the model to the same level. Cycle-Approximate simply means that transactions take place with a roughly equivalent timing for transactions, but there are no events for each clock edge. In fact, efficient and fast models do not have any sc_clocks at all. They can still be very close in timing results but eliminate the incessant context switches imposed by real clocks (not to mention that half of the context switches are not even useful.
  11. I concur with @Eyck on the use of explicit read/write. Using the operator= override can lead to unexpected interpretation, and the explicit presence of method calls alerts the reader to the presence of a channel behavior. If you care about simulation performance, I would avoid sc_biguint and sc_uint if possible. Native C++ types work great and are much faster. Many models are written without using the SystemC datatypes at all. Some groups even forbid them except at the interface points to SystemVerilog or VHDL for this reason. Bit twiddling is a simple art. For really large vectors, sc_bv is fine, but you might also consider std::vector<bool> and std::bitset<N>. [See https://stackoverflow.com/questions/4156538/how-can-stdbitset-be-faster-than-stdvectorbool for a comparative comment.]
  12. I suggest that you invoke your compiler with -DSC_INCLUDE_DYNAMIC_PROCESSES to avoid the issue of making sure all #include's of systemc are preceded by it. You might also consider using https://www.edaplayground.com to put your code when sharing. That way others can examine/execute the code with less effort. It's entirely free and quite popular.
  13. To use sc_spawn, yes you need SC_INCLUDE_DYNAMIC_PROCESS. It's a compile-time option to simply allow the code into compilation and has nothing to do with the definition of dynamic vs static in the strictest sense. Also, SC_THREAD/SC_METHOD do not allow for using the same class member more than once if I recall correctly. So you will need to use sc_spawn.
  14. @acc_sysC, to help clarify a bit more: sc_signal<T> is a channel, not data. Conceptually, channels provide transport for data for communication. Use the write() method deposits a copy of the data (as a whole) into the sc_signal channel's write buffer (next_value). At the end of the delta cycle, the write buffer is copied into the current_value in it's entirety. The read() method simply returns a copy of the current_value. sc_bv is a data type, not a channel, and supports bit-specific access (reading and writing) via operator[]. Finally, if you use ports (e.g., sc_in<T> or sc_out<T>, which are really just specializations of sc_port<sc_signal_in_if<T>> and sc_port<sc_signal_inout_if<T>> respectively), they are effectively pointers to channels that support the channel's API's. I mention this because newbies to SystemC frequently confuse the ideas of data, channels, and ports. Finally, before you ask: No, there is unlikely to be any attempt to "fix" SystemC to provide extensions to all of this to make modeling at this (low-level) more comfortable. SystemC attempts to raise the abstraction level and tends to avoid bit/pin twiddling. If you need to code RTL, please use SystemVerilog or VHDL.
  15. I got it to work after realizing that you were missing connect phase and had things in scrambled ordering within build phase. During build phase, which happens top-down uvm_config_db#(TYPE)::get things set previously in the object hierarchy if needed for the next step uvm_config_db#(TYPE)::set set_type overrides create child components During connect phase uvm_config_db#(TYPE)::get connections for virtual interfaces in monitor and driver (usually done in the agent) connect ports During the run-time phase (usually in env) Create, randomize, and start a top-level sequence Also, you had some confusion about the config_db names "des_if" vs "des_vif" in the string name. Also observed that you had some indented code intended for `if` statements; however, no surrounding begin/end. I ALWAYS use begin/end for EVERY if, case and loop. Avoids so many issues.
  16. SystemC components (sc_module, sc_channel, sc_port, sc_export, and sc_prim_channel) are considered to be like blocks of silicon. They are created before the simulator starts time during a phase called elaboration. Elaboration starts when you create (instantiate) the first component. After elaboration closes there are some intermediate phases (end_of_elaboration and start_of_simulation) at which point it is no longer legal to create new components. This differs from processes, data objects (e.g., transactions) created, modified and, passed during run-time. Processes can be created, suspended, resumed, disabled, enabled, reset and killed during runtime. Processes are where all the action occurs during simulation. If you created your own custom channels with internal routing and switches, you can dynamically route information between all connected components using your custom channels. You could create a pool of N modules maximally interconnected to these routing channels and then manage suspend/resume and routing at run-time. It's straight-forward SystemC for any competent SystemC coder.
  17. If you understand SystemVerilog class-based syntax properly, you will see that you simply are describing a UVM subscriber. You could alternatively use the general purpose UVM callback mechanism.
  18. There are several ways to do this: Use the register front door sequence Create a layered agent with a high-level driver/transaction that directs the low level driver to perform two transactions to accomplish the desired goal
  19. Did you run 'autoreconf -i' before configuring? You may also need to run 'autoupdate'. Easier: use the cmake flow. Cmake recognizes more architectures and requires less fiddling.
  20. How is N declared? Also, you need to keep the distinctions of compile-time, elaboration-time and run-time distinct in your thinking. Template parameters and array size declarators all require compile-time constants. In turn these can be dealt with computationally but only if using constexpr functions. Depending on the version of C++ and your library implementation, you might be having issues. Yor problems are all C++ syntax and very little to do with SystenC itself.
  21. If you do use the Accellera version, simply downloading it won't suffice. SystemC library needs to be compiled and installed into a directory separate from the source code. Then you need to add the path to the include directory of the installed location. Read the README and installation instructions. However, @AmeyaVSis correct. SystemC should be part of the Vitis installation, which means a compiled installation of SystemC already exists inside your Vitis installation files. Also, although, your example is not synthesizable, it should simulate. It would be better if you learned to use #include <systemc> instead of #include "systemc.h".
  22. This looks suspiciously like assigned homework at a university. The description of the task is quite clear. I don't believe it would be fair to you or your classmates for anybody here to provide you with a solution. I will provide you with a few function clues: sc_core::wait( sc_time ) sc_core::sc_time_stamp() You should be able to solve this problem. Read the textbook if all else fails.
  23. What distribution of SystemC are you using? Please note that the DEBUG_MSG is not part of SystemC API and should not be used except by internal developers. It is intentionally disabled. You are obviously not passing the correct parameters and should study the source code to understand it. If your company is a member of Accellera, you may ask in the private language working group forum there (assuming your employer allows).
  24. For threads, context changes when you call sc_core::wait(). How that is accomplished depends on the operating system and is the responsibility of the SystemC library implementation.
×
×
  • Create New...