Jump to content

All Activity

This stream auto-updates     

  1. Yesterday
  2. tudor.timi

    Transaction Design Patterns

    Regarding point 4: If you want reusable abstractions, one of them is "register/memory accesses". Most hardware blocks use bus transactions to update/query special function registers or memory locations. This is also an abstraction that software/firmware engineers understand. You should look into that. There is so much variation in bus protocols that it's difficult to talk about a universal abstraction. It's also mostly pointless, as when you're talking about verifying bus level aspects, you're interested in the details of that bus protocol.
  3. tudor.timi

    Transaction Design Patterns

    The blog post you quoted w.r.t. working with types is correct regarding " The correct thing to do from an OOP perspective is to create a virtual function ", but not regarding the further points. In that case, where a protocol uses heterogeneous transaction types (i.e. different kinds have different properties), you're better off using the visitor pattern. The transactions would have a virtual function accept function.
  4. tudor.timi

    Transaction Design Patterns

    Regarding point number 3, I don't see why the coupling between transaction, driver and monitor is a bad thing. If you treat transactions as mere data classes, the behavior based on this data will have to be implemented in a different class. Should a transaction know how to drive itself and how to monitor itself? Should it also know how to cover itself? What if you have to add another operation, like tracing itself in a waveform viewer? Do you add that to the transaction class too? This violates the single responsibility principle.
  5. tudor.timi

    Transaction Design Patterns

    Regarding point number 1: Transactions aren't supposed to model traditional classes (not sure what the correct term for such classes is), which contain behavior (i.e. methods) and make use of polymorphism. Transactions are data classes, where you bundle information together to pass around, similar to plain old structs. Contrast the following examples: // Bad design // Using tag classes, where a "tag" field controls the behavior of methods is a code smell class service; direction_e dir; function void do_stuff(); if (dir == READ) do_read(); else do_write(); endfunction endclass // Better design, have two classes interface class service; pure virtual function void do_stuff(); endclass class read_service; virtual function void do_stuff(); // do read stuff endfunction endclass class write_service; // ... endclass In the case above, it makes sense to create different classes for handling reads and writes, because you have a common abstraction (doing stuff), which comes in two different flavors. How would you handle processing different kinds of transactions in a driver (for example) if you had different classes for read and for write? You'd need to cast, which is very frowned upon (at least in the software world). My point about transactions being data classes isn't strictly true w.r.t how they are currently used in the industry. Transactions are also used for randomization, which is a polymorphic operation. Even here though, assuming you want to generate a list of transactions, where some of them are reads, some of them are writes, it will be impossible to do this in a single step if you build up your class hierarchy in such a way that you have a 'read_transaction' class and a 'write_transaction' class. This is because you can't choose an object's type (and I mean from the point of view of the compiler) via randomization. Finally, why is 'direction' the field you choose to specialize on? Assuming you would also have another field in your transaction class called 'sec_mode', which could be either 'SECURE' or 'NONSECURE', would you be inclined to say that you need to create a 'secure_transaction' and a 'non_secure_transaction' because they are different things? Because you also chose to specialize based on direction, would you have 'secure_read_transaction', 'secure_write_transaction', 'nonsecure_read_transaction' and 'nonsecure_write_transaction'? What would happen if you would add another field called 'priviledge_mode', which could be 'PRIVILEGED' or 'UNPRIVILEGED'?
  6. tudor.timi

    Transaction Design Patterns

    Regarding point number 2: Having both a write_data and a read_data field in the transaction is bad design. A field called data would be sufficient and it would contain that data being transmitted in that transaction, whether it is a read or a write (i.e. regardless of what direction that data flows). The direction field tells you whether you're dealing with read_data or with write_data. Having both fields makes for a pretty difficult to use API if you want to do things irrespective of the direction: if (trans.direction == READ) do_stuff(trans.read_data); else do_stuff(trans.write_data); You'll find your code repeating these conditional statements all over. Contrast this to the case where you only have data: do_stuff(trans.data);
  7. Last week
  8. https://matthieu-moy.fr/sc-during/doc/html/index.html I have found some library, which for the first look sound interesting and useful Is there any collective experience with it?
  9. Grisha

    Build error SystemC 2.3.3 with C++17

    +1. I tried to understand why I can't build under c++14 very long. Until I found this post. Thank you.
  10. Earlier
  11. From the standard: "Tagged? Incoming interface method calls are tagged with an id to indicate the socket through which they arrived."
  12. I have a multipass through target with nb_btransport_fw registered. init1 (target) init2 (target) Something like this target.register_nb_transport_fw( this, &top::nb_transport_fw); tlm::tlm_sync_enum nb_transport_fw(int id, tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_time& delay){ } does the id parameter identify the incoming initiators or is it the id of the target socket which means this is stuck at 0?
  13. Use the appropriate target convenience socket.
  14. I have two multipass through initiators bound to same target. Is there a way to know the id of calling initiator?
  15. The list of open source UVM repositories can also provide an answer to question 4. I couldn't find any well supported (project with many stars) library of sequence items. There are many verification components for various bus protocols but they all have a single sequence items tailored specifically for that bus. This leads me back to question 3...
  16. The blog post also touches my third question although not providing an answer
  17. Looking at open source UVM repositories I think it safe to say that keeping all transaction types within the same sequence item class is the most common design pattern (the only pattern I found when looking at many of these repos). Luckily, I also found an interesting blog post about type handles that address what I'm looking for. There is a way of doing what I'm looking for although standard practice seems to be something else. I guess that keeping everything in one packet is more convenient. I think this answers my first question.
  18. Well, without lnowing the implementation it is hard to tell. I suppose your initiator sockets are connected to target sockets at he bus and the initiator sockets of the bus are connected to the target sockets of your target components. All these sockets need to be bound to each other (by calling bind() or the operator() function). There is no concept of bind-by-name in SystemC. Maybe you should have a look here: https://www.doulos.com/knowhow/systemc/tlm2/tutorial__3/ Sockets need to be bound except they are declared with the appropriate binding policy (the 4th template of the sockets). If this is not the case you need to create dummy targets.
  19. I'm guessing you are trying to read the signal values immediately after calling write(). That won't return the values just written due to how update semantics work with primitive channels in SystemC. To make sure this is the cause of confusion, you can try creating a thread in your model where the signal values are written and read back after a delta-cycle wait: SC_MODULE(some_module) { sc_signal<pixel> p1; sc_signal<pixel> p2; void some_thread() { p1.write(pixel(1, 2, 3)); p2.write(pixel(4, 5, 6)); // Read and print p1 and p2 // Values won't be updated here wait(0, SC_NS); // Read and print p1 and p2 // Values will be updated here };
  20. Andy Goodrich

    The problem of reading the custom structure is reasonable.

    I addition to the requirements mentioned by David, you need to define a constructor with 3 arguments for pixel, something like: struct pixel { sc_dt::sc_uint<8> r; sc_dt::sc_uint<8> g; sc_dt::sc_uint<8> b; pixel() :r(0), g(0), b(0) {} pixel( const sc_dt::sc_uint<8>& r_init, const sc_dt::sc_uint<8>& g_init, const sc_dt::sc_uint<8>& b_init ) : r(r_init), g(g_init), b(b_init) {} };
  21. I found an example from UVM Primer that has dedicated transactions as well as monitors for input and output so I guess that answers my second question.
  22. David Black

    The problem of reading the custom structure is reasonable.

    sc_core::sc_signal<T> requires that you define the following: T::operator=(const T&) T::operator==(const T&) ostringstream&. operator<<( ostringstream&,const T& )
  23. Hello friends. I have questions to ask for your help. I have a struct defined as struct pixel { sc_uint<8> r; sc_uint<8> g; sc_uint<8> b; }; Then I try to pass actual pixel values to the ports, defined as pixel data type in main.cpp sc_signal<pixel> p1; sc_signal<pixel> p2; p1.write(pixel(1, 2, 3)); p2.write(pixel(4, 5, 6)); But it seems p1.write() and p2.write() didn't initialize p1 and p2 correctly as I still see they don't have the expected pixel values (1,2,3) and (4,5,6). Do I need to use sc_interface to pass this customized data type around? Thank's a lot!
  24. I'm not looking for complete answers. Any clues that you can provide are appreciated.
  25. I have multiple registers in my design blk0 for which i have created a RAL model? How to read all the register in RAL model using reg_map? Please explain with example...
  26. Where do you set send_ptr? What with : cout << "send_ptr: " << hex << send_ptr << endl;
  27. Bind it... or change the binding policy to SC_ZERO_OR_MORE_BOUND but risk a NULL dereference crash if you forget to check for binding at run time.
  1. Load more activity
×