Jump to content

David Black

Members
  • Posts

    690
  • Joined

  • Last visited

  • Days Won

    154

Everything posted by David Black

  1. Basic question: Why are you not just using a port that carries doubles? When modeling, you can assign timing independent of what the data format is. I make the presumption that you are doing high-level modeling rather than RTL. If your answer is to store it in a memory model that uses unsigned long long int or as its base type, then you should consider: // C++ program to test using unsigned long long int to carry data across a connection #include <iostream> #include <cassert> using namespace std; int main() { bool success = true; // innocent until proven otherwise typedef unsigned long long int data_t; //< generic definition of the data passed over port assert(sizeof(double)<=sizeof(data_t)); //< ensure it is big enough for (size_t i=0; i!=1000; ++i) { // Create a random number double xmit_double = double(random())/double(1<<(random()%32)); data_t& xmit_data = reinterpret_cast<data_t&>(xmit_double); // Send the data to the "other" end data_t recv_data = xmit_data; double& recv_double = reinterpret_cast<double&>(recv_data); success &= (recv_double == xmit_double); } cout << (success?"PASSED":"FAILED") << endl; return success?0:1; }
  2. That last solution doesn't work if you want to use different bus widths at different times. The templated solution is exactly what the original needed.
  3. Replace start and end address range with another associative array. Populate it with the addresses of interest: class i2c_addr_map; ... typedef int unsigned addr_t; typedef addr_t list_t[$]; local list_t slave2addrs[string]; local string addr2slave[addr_t]; // Constructor -- uses default // function to add a slave function void add_slave(string name, int unsigned addr_list[$]); assert(name != ""); //< minimal criteria if (slave2addrs.exists(name)) `uvm_warning("i2c_addr_map","i2c device name already exists! - adding") foreach (addr_list[i]) begin if (addr2slave.exists(addr_list[i]) && addr2slave[addr_list[i] != name) begin `uvm_error("i2c_addr_map","Overlapping i2c address - ignoring") end else begin addr2slave[addr_list[i]] = name; slave2addrs[name].push_back(addr_list[i]); end end endfunction // a utility function to return the slave at a certain address function string get_slave_by_address(addr_t address); if (addr2slave.exists(address)) return addr2slave[address]; `uvm_error("i2c_addr_map","no such i2c device name registered") return ""; endfunction endclass Now you have to add all the addresses: addr_map.add_slave("slave1", {'h10,'h11,'h13}); Obviously, you could add methods for adding ranges and make it more complex as you like.
  4. Your request is certainly non-standard, but if you have a custom protocol you could add a payload extension with a rule that allows it to kill a transaction. A more standard approach would be to allow the transaction to complete, but have the target or interconnect tag the incoming transaction with an error status or perhaps just leave the TLM_INCOMPLETE_RESPONSE in place. Question: Where/what is cancelling the transaction? The initiator or the target? Does this have to do with response timeout (target too slow) or some other situation? Observation: The standard protocol requires that you use memory management when using nb_transport (Approximately Timed coding style). I don't see evidence of acquire() that in the code snippet above.
  5. UVM takes a while to put in place. Personally, I design from the top-down and then implement/test from the bottom up with a small test case for each level. Start with transaction classes. Test all the methods in a trivial test case. Next design trivial sequences (send a single item). Then create an agent. You may need to create dummy DUT to source or sink accross the interface. It takes time. The first time around always takes much longer.
  6. Suggest you see http://videos.accellera.org/ieee16662011/index.html
  7. First, why are you calling the .value() method? To output time, you should use just sc_time_stamp() or possibly (but not necessary) .to_string() method. cout << sc_time_stamp() << endl; Next, you have a race condition. Process run() and run2() are not guaranteed to start in any particular order with respect to each other. Yes, for a given simulator implementation, one will run before the other, but that is purely based on the implementation. The standard is very specific about this aspect. Next, the error in your disable/enable situation occurs because run2() is attempting to control run(), but run() has already terminated. See http://videos.accellera.org/ieee16662011/index.html for more information.
  8. Could you illustrate a specific example where $write was required? Unless you are writing catchers, I have a hard time seeing this. 99.9999% of the time, the built-in UVM reporting mechanisms should work.
  9. Imagine you have a device such as a UART (serial port) with a transmit data register. Every time you write a byte to this register, it sends the data. Now imagine you want to send 64 bytes through that stream with a single transaction. So you stream a payload with 64 bytes to the register address (SEND_ADDR) with a streaming width of 1. All 64 bytes are written to the single address. If the streaming width were set to 64, then the bytes would go to SEND_ADDR+0, SEND_ADDR+1, SEND_ADDR+2, ... , SEND_ADDR+63. In other words, one address per data item.
  10. For each type (extension class), there can only be one attached object on the extensions. So I suspect you are confused about how extensions work in general. If you want to "count" attached extension objects, then you might be needed a base extension to contain your counted objects. If you are adding extensions based on the instance, then you may consider using the utility class for instance-specific extensions. There is a good presentation for this on the Doulos website under http://www.doulos.com/knowhow/systemc/tlm2/ (examples using bus locking/snooping).
  11. To reinforce Tudor's reply, you will need to "override" twice - once in the OO sense, and once in the factory sense: 1. derive a new driver class (assuming you are overriding an existing driver) 2. implement your new build_phase -- IMPORTANT: you will probably want to call super.build_phase from within your new implementation 3. set_type_override or set_instance_override in the test class (or possibly the environment) during the build_phase of that class
  12. sc_event's are the way to guarantee ordering in SystemC. Most channels use events to establish ordering. One or more processes wait on an sc_event, while another generates the sc_event. If you want to guarantee ordering, use events. If you look under the hood of a sc_mutex, sc_semaphore, sc_signal, sc_fifo, or any other properly designed channel, you will find events and flags. class Top_module { sc_event evt1, evt2; void thread1(void); void thread2(void); SC_CTOR(Top_module); }; Top_module::Top_module(sc_module_name nm) { SC_HAS_PROCESS(Top_module); SC_THREAD(thread1); SC_THREAD(thread2); } void Top_module::thread1(void) { for(; { evt1.notify(SC_ZERO_TIME); wait(evt2); cout << "Got evt2" << endl; } } void Top_module::thread2(void) { for (int i=0; i!=10; ++i) { wait(evt1); cout << "Got evt1" << endl; evt2.notify(SC_ZERO_TIME); } sc_stop(); }
  13. Careful... if you always wait(SC_ZERO_TIME), then your simulation will not be able to progress time. If your simulation has no need of time, then this is fine. Usually I find that waiting for events or non-zero time serves best. wait(SC_ZERO_TIME) is for special situations when you know the information needed is in the current time, but be careful you don't lock out other processes that may need time to progress. One or two is fine, but if that is all you have then there are problems...
  14. SystemC processes operate in a manner known as cooperative multi-tasking. This is common to event driven simulators such as VHDL, Verilog, SystemVerilog, SystemC, etc. It means that until a process decides to voluntarily yield, the process may execute for as long as it likes. Yielding in SystemC means calling the SystemC wait() function. So if you have some code actively executing in an SC_THREAD or SC_METHOD process, it can issue multiple event notifications and do all sorts of b_transport calls without allowing other processes to execute. Only when you call wait() will the other processes be allowed to execute. In their turn, they get the same privilege. This is quite different from processes and threads in a modern OS, which operate pre-emptively. The operating system gives each process or thread a certain amount of time, and interrupts the execution of the process or thread when that time is up. The reason cooperative multi-tasking was chosen for event driven simulators is to simply issues in coding. If pre-emptive scheduling were adopted, then a lot of extra work would need to be done to ensure safe communications between processes (which multi-tasking software programmers have to deal with all the time).
  15. I suggest you take a class in C++ before proceeding further.
  16. There are several things wrong with the code you provided and perhaps indicate a serious lack of appropriate C++ background: #include <iostream> #include <fstream> using namespace std; SC_MODULE(stimulus) { sc_out<bool> reset; sc_out<bool> input_valid; sc_out<int> sample; sc_in<bool> CLK; sc_int<8> send_value1; unsigned cycle; ofstream myfile; SC_CTOR(stimulus) { myfile.open ("example.txt"); myfile << "Writing this to a file.\n"; SC_METHOD(entry(ofstream)); dont_initialize(); sensitive << CLK.pos(); send_value1 = 0; cycle = 0; myfile.close(); } void entry(ofstream); }; You should check for success after opening a file If your process method requires an argument, you cannot use SC_METHOD to register it. It is unclear why you need ofstream as an argument to the entry() method. It is unclear what you are using the file for, and why you would write it inside the constructor.
  17. When you refer to callback's in UVM, there are two possible definitions: 1. So-called "callback" virtual methods implementing build_phase(), connect_phase(), start_of_simulation_phase(), etc. 2. Real callbacks implemented in a callback class with complete registration, etc. If you are refering to #1, then you are limited to the class inheritance definitions. If #2, then anything is possible as long as you have a handle to the callback object.
  18. Actually, you don't need a sensitive statement. You can simply insert: wait(1,SC_NS); into each of the loops to observe the behavior. In fact, I rarely use static sensitivity in my SystemC code. I prefer dynamic because it results in easier to understand code. Mind you, I don't write RTL when writing SystemC. RTL more or less requires static sensitivity.
  19. sc_fifo is of fixed size and defaults to 16. sc_fifo::read() and sc_fifo::write() are blocking. In other words, read() will wait if the fifo is empty, and write() will wait if the fifo is full. I don't see any consumers for your fifos. Add a process to read the fifo. Alternately, you could use sc_fifo::nb_write(). Also, a suggestion (pedantic): change dataToSend.read() to dataToSend->read(). Get into a habit of using -> when accessing channel methods. It will save you grief in the long run. Fortunately, since you are using sc_in, there exists sc_in::read() that calls port->read().
  20. SystemC is a standard that defines a C++ library containing constructs useful for modeling complex electronic designs above the RTL level of abstraction.
  21. `define won't do the job because that is a compile-time feature. You should define your constants with sized enum's or const in the package: Never use `define if you can avoid it, because macros are hard to debug, don't have an underlying data type and have to be recompiled at every turn. package My_pkg; typedef bit [31:0] address_t; typedef enum address_t { timer_config_reg = 'h2800; timer_start_reg = 'h2804, timer_current_reg = 'h2808, ... } registers_e; const address_t timer_base_addr = 'h2800; endpackage Notice that I also abstract the base type because it will likely be used in the verification and design environments. This also makes the code more portable.
  22. Your description does not provide enough information. Please provide code illustrating the problem.
  23. Another idea might be to observe that if the calls come from different initiators, then they likely come from different processes. Processes have unique process ids.
  24. Because SystemC is based on C++ and because the design intentionally chose to ensure that only correct port types are connected to like types at compilation, what you ask is probably not something that would be possible as an executable unless you are willing to give up most of the safety, which I would not recommend. An alternative solution would be create a script that assembles your source code prior to compilation. This could easily be done using something like PERL or Python.
×
×
  • Create New...