Jump to content

Eyck

Members
  • Posts

    356
  • Joined

  • Last visited

  • Days Won

    87

Everything posted by Eyck

  1. The example you are refering to is for SystemC 2.0 and more over has errors. Actually this is a bad example to start with. the sc_start() in line 17 needs to be commented out for 2 reasons: a) it doesn't make any sense and b) it runs elaboration and tracing (VCD) needs to be initialized before that. After elaboration you cannot add signals for tracing anymore sc_start() (lines 32, 34, 40, 42, 47, 49, 56 58) has to be called either without any arguments (so it runs to the end) or with a duration in terms of sc_time like this: sc_start(1, SC_NS); My recommendation would be to either use the Doulus tutorials: https://www.doulos.com/knowhow/systemc/tutorial/ or the stuff from SCLive (https://sclive.wordpress.com/)
  2. Actually using new is not recommended at all. You should do something like: std::array<sc_dt::sc_uint<32>, MEMORY_DEPTH> mem; or (C++11): std::vector<sc_dt::sc_uint<32>> mem{MEMORY_DEPTH}; As far as I can see you try to initialize mem in-class (during declaration) and this is not allowed in C++. You can do this only in the constructor. And if you need to use new, don't forget to delete.
  3. Sure. Just add a method reading all bool sc_in and write to the output. You need to make it sensitive to all inputs Something like: SC_MODULE(conv){ sc_vector<sc_in<bool> > input{"input"}; sc_out<bool> ouput{"ouput"}; SC_CTOR(conv){ SC_HAS_PROCESS(conv); SC_METHOD(method); for(auto& in: input){ sensitive<<in; } } void method(){ unsigned res = input[1]?2:0+input[0]?1:0; output=res; } };
  4. First: I'm not going to solve your problems. I only propose possible solutions. So don't expect read-to-use models Second: doing wait() in an SC_THREAD or SC_CTHREAD creates an implicit FSM. I guess this is not what you want. And as you already noticed, it does not solve you problem. You have a syncronous design and you should model it like that. Third: if your latency is not what you want you need to change your model. Maybe there is a stage to much, maybe there aren't enough. This is left to the modeler....
  5. Just as explanation: pos() returns an event_finder (a proxy object which allows to use an event which is not yet available) while posedge_event() returns the event itself. And the operator|() is ony defined for sc_event.
  6. You are writing to ports in the constructor of server. At this point they are not bound. Since you initialize them in the beginning of your thread this is not needed anyways. Aside of that you also do not need the sensitivity list for the monitor thread as you never use the default sensitivity (by using wait() ). And if you use C++11 or newer you should use In-class member initializers which eases the naming of signals (and you should name them to make debugging easier): sc_in<bool> begin1{"begin1"}, begin2{"begin2"}, begin3{"begin3"}; sc_in<bool> end1{"end1"}, end2{"end2"}, end3{"end3"}; sc_in<bool> incoming1{"incoming1"}, incoming2{"incoming2"}, incoming3{"incoming3"}; sc_out<bool> free{"free"}, outgoing1{"outgoing1"}, outgoing2{"outgoing2"}, outgoing3{"outgoing3"};
  7. Eyck

    Queue fifo

    Actually you created an array of inputs of type int, not a pipe. Without more code it is hard to give any advise
  8. You should seriously read the SystemC standard or related books. Neither SC_METHOD nor SC_THREAD pass time, time advances only in the SystemC kernel and the kernel returns control to them at certain time points. The difference is that SC_THREAD preserves state when returning control to the kernel by calling wait(). SC_METHOD cannot preserve the state (it is always called as a function) and is therefore not allowed to call wait(). next_trigger() tells the SC kernel when to invoke the SC_METHOD next time. Calling next_trigger() several times in the same method as you do it is meaningless as the last call to next_trigger() prevails. Again: next_trigger() does not stop execution rather tells the kernel when to start the method next time.
  9. sc_time_stamp() returns a sc_time objec, not a string. It seems the use in a particular context (e.g. cout<<sc_time_stamp()) triggers an implicit type conversion. Pls. refer to the standard how to convert to particular unit.
  10. As the error message says: you forgot to bind a port. Since the are no names given (which I always suggest to do) it is a generated name. Moreover the sc_vector is not initialized to a certain size. So for your module do (C++11 assumed) it should look like: // Module defintion SC_MODULE (mux) { //inputs sc_in<bool> clk{"clk"}; sc_in<bool> reset{"reset"}; sc_in<bool> enable{"enable"}; sc_in<sc_uint<8> > in{"in"}; //output ports sc_vector<sc_out<sc_uint<8> > > out{"out", 8}; and sc_main should look like int sc_main(int argc, char* argv[]) { // Ports sc_signal <bool> reset{"reset"}; sc_signal <bool> clock{"clock"}; sc_signal <bool> enable{"enable"}; sc_signal<sc_uint<8> > input{"input"}; sc_vector<sc_signal<sc_uint<8>>> output{"output", 8}; //instance and port binding mux bind("mux"); bind.reset(reset); bind.clk(clock); bind.enable(enable); bind.in(input); bind.out(output); // Open VCD file sc_trace_file *wf = sc_create_vcd_trace_file("mux"); ... ...
  11. If you setup SC_THREADs the SystemC kernel invokes them and you do not have any control in which order SC_METHODS and SC_THREADS get invoked/resumed. SystemC gives no guarantee about the order of within a delta cycle. So the 2nd or 3rd stage of your pipeline could be executed before or after the 1st stage. If you use POD (C++ types and classes) they immediately change their value when being written. If you now have a variable V between the 1st and the 2nd stage it depends on the order of invocation if the 2nd stage reads the value in the same delta-cycle (1st stage is invoked before 2nd stage) or at the next clock cycle (2nd is invokde first reading the value before 1st stages updates it). This approach is largely used in C++ performance models and works as long as you don't have loops. This should answer your 1st question: txProcess should read its inputs before intermediateProcess updates them and intermediateProcess reads its inputs before rxProcess updates them. This way you model the syncronous nature of a pipeline. For the sake of simplicity I would model it using the means of SystemC: primitive channels and SC_METHODS: SC_MODULE(Design) { sc_in_clk clock; sc_in <bool> reset; sc_vector<sc_in <Flit>> flit_rx{"flit_rx", DIRECTIONS + 2}; sc_vector<sc_out <Flit>> flit_tx{"flit_tx", DIRECTIONS + 2}; static sc_fifo* fifo_creator(char const* name, size_t idx){ return new sc_fifo(name, BUFFER_SIZE); } sc_vector<sc_fifo<Flit>> buffer{"buffer", (DIRECTIONS + 2) * GlobalParams::n_virtual_channels, &Design::fifo_creator}; sc_vector<sc_fifo<Flit>> intermediate_buffer{"intermediate_buffer", (DIRECTIONS + 2) * GlobalParams::n_virtual_channels, &Design::fifo_creator}; void rxProcess(); void intermediateProcess(); void txProcess(); SC_CTOR(Design) { SC_METHOD(rxProcess); sensitive << reset; sensitive << clock.pos(); SC_METHOD(intermediateProcess); sensitive << reset; sensitive << clock.pos(); SC_METHOD(txProcess); sensitive << reset; sensitive << clock.pos(); } }; And the stage processes would look like: void Design::intermediateProcess() { if (reset.read()){ // put your reset stuff here } else if(clk_i.posedge()){ for (int i = 0; i < DIRECTIONS + 2; i++) { for (int vc = 0;vc < GlobalParams::n_virtual_channels; vc++) { if (buffer[i][vc].num_available()>0 && intermediate_buffer[i].num_free()>0) { Flit f; // get the incoming flit from buffer, check that the read is successfull sc_assert(buffer[i].nb_read(f)); // Store the incoming flit in the circular buffer, check that the read is successfull sc_assert(intermediate_buffer[i].nb_write(f)); } } } } } HTH
  12. You would do it like this: int sc_main(int argc, char* argv[]) { // Ports sc_signal <bool> reset; sc_signal <bool> clock; sc_signal<sc_uint<8> > input; sc_vector<sc_signal<sc_uint<8>>> output; //instance and port binding mux bind("mux"); bind.reset(reset); bind.clk(clock); bind.in(input); bind.out(output); // Open VCD file sc_trace_file *wf = sc_create_vcd_trace_file("mux"); ... ... But you cannot call sc_start before opening the VCD database. sc_start initializes the VCD output so you cannot add things to trace. When you write 'Both the methods dont seem to work' what is the problem you are seeing?
  13. You can access elements using array index operator like this: in2[0][1]=42.0; But you write you want to put values onto the array. You cannot write onto ports as these are only proxys and do not hold values. You need to create a similar sc_signal array and connect it to the ports. sc_signals hold values so you can put values on them using write(). A side node: you should declare your vectors as: sc_vector<sc_vector<sc_in<float>>> in2{"in2", 4, [](char const* name, size_t idx) -> sc_vector<sc_in<float>>*{ return new sc_vector<sc_in<float>>(name, 4); }}; This names your ports (very helpful when tracing values) and you don't need to call init() separately.
  14. When implementing some simulation control I usually have the logic as part of a SC_THREAD. Once the thread is active it is save to access all the other data structures as -like @David Black mentioned- SystemC is cooperative multi tasking. Based on the GUI thread you can then decide to run for a certain time, until a certain event, or stop simulation entirely using sc_stop()
  15. Your problem is the use of POD (plain old datatype) for communication between the processes. Why? As soon as you write onto it all other processes see the update. So the behavior of your design depends on the scheduling of the processes. If Design::process is scheduled before Design::second_stage_process the second_stage_process sees the updates of process. Actually there are 2 ways out: you just have one thread and call the function from the output to the input: void Design::process() { txProcess(); intermediateProcess(); rxProcess(); } Although it will work in your case this approach will not scale. As soon as you cross sc_module boundaries you cannot control the order of calling. you use a primitive channel instead of POD. In your case you might use a sc_core::sc_fifo with a depth of one. And you should use sc_vector instead of the POD array type since they need the proper initialization. Why does it help? New values being written will be visible at the output of the fifo in the next delta cycle. So no matter in which order the threads and methods are invoked they will read the 'old' values despite 'new' values have been written. HTH
  16. Esp. as educational project you should implement it in 2 threads which communicate with each other. Since they run in the same process space you can access data safely once the simulation is in paused state. But you cannot restart the simulation without restarting the process it self. SystemC uses static global variables storing the simulation state and those get only initialized at program start.
  17. Actually there is a default time unit in sc_time.h: // ---------------------------------------------------------------------------- // STRUCT : sc_time_params // // Struct that holds the time resolution and default time unit. // ---------------------------------------------------------------------------- struct SC_API sc_time_params { double time_resolution; // in femto seconds bool time_resolution_specified; bool time_resolution_fixed; sc_time::value_type default_time_unit; // in time resolution bool default_time_unit_specified; sc_time_params(); ~sc_time_params(); }; Instantiating it you get the default values....
  18. Basically there are other ways if you have a process handle. But for this you need to get a emthod handle. To do this you have 2 options: You don't use SC_METHOD rather use sc_spawn directly (my _handle is a the part of you rmodule: sc_core::sc_spawn_options opt; opt.dont_initialize(); opt.spawn_method(); my_handle = sc_core::sc_spawn(func, name, &opt); this->sensitive << my_handle; this->sensitive_pos << my_handle; (if you don't use satic sensitivity you can skipe the last 2 liens). Using the handle you can change the sensitivity: reinterpret_cast<sc_method_process*>(my_handle.get_process_object())->next_trigger( sensitivity_change_event ); The other option is to retrieve the process handle upon the first invocation of your method using 'reinterpret_cast<sc_method_handle>(sc_get_current_process_handle()), store it in your module and then call next_trigger for it. But I'm not sure it this is cleaner...
  19. sc_clock triggers itself based on the period and the (in your case default) constructor settings. The period is the default_time_unit.
  20. There is no guarantee which method or thread is activated first There is no means to give priority. Why would you like to do this? In my experience you have a thought problem if you believe you need to do this. thread activation is more expensive (in terms of computing power) than method as the thread context needs to be restored and saved. But threads keep an internal state so they are good to describe state machines.
  21. Each thread will invoked once since they are sensitive to clock. If you use here plain C++ types you will run into issues as you mentioned since it is not specified in which order the threads are activated. Therefor you need to use a signal to cummunicate between the threads: if you write to a signal you will still read the old value until the update phase is executed. This is followed by the next eavaluate phase in the next delta cycle or timestep. This way it doesn't matter in which order the threads are invoked. You should (re-)read some books or check with some online tutorials about SystemC as this mechanism builds the foundation of the simulation semantics,
  22. You declare your modules as local variables in the constructor. Leaving the constructor they are destroyed. Something like this should work: SC_MODULE(dispenser){ sc_in<bool> input1; sc_in<bool>input2; sc_out<bool>out1; sc_signal<bool> s1; module1 m1; module2 m2; SC_CTOR(dispenser): input1("input1"), input2("input2"), out1("out1"), s1("s1"), m1("module1"), m2("module2") { m1.in1(input1); m1.in2(input2) m1.outA(s1); m2.input1(input1); m2.input2(s1); m2.out(out1); } }; I highly advice to name all signals and ports. If you start debugging this will ease you live. If you are using newer versions of SystemC you can write it like: SC_MODULE(dispenser){ sc_in<bool> input1; sc_in<bool>input2; sc_out<bool>out1; sc_signal<bool> s1; module1 m1; module2 m2; SC_CTOR(dispenser): SC_NAMED(input1), SC_NAMED(input2), SC_NAMED(out1), SC_NAMED(s1), SC_NAMED(m1), SC_NAMED(m2) { m1.in1(input1); m1.in2(input2) m1.outA(s1); m2.input1(input1); m2.input2(s1); m2.out(out1); } }; If you compile using C++11 you can also doing it like this: SC_MODULE(dispenser){ sc_in<bool> input1{"input1"}; sc_in<bool>input2{"input2"}; sc_out<bool>out1{"out1"}; sc_signal<bool> s1{"s1"}; module1 m1{"m1"}; module2 m2{"m2"}; SC_CTOR(dispenser){ m1.in1(input1); m1.in2(input2) m1.outA(s1); m2.input1(input1); m2.input2(s1); m2.out(out1); } };
  23. You create 5 signals carrying a TraceVector. Doing so the default constructor is used which does reserve space for elements but has a size of 0... I can't tell where the assertion comes from. For those cases a debugger is pretty helpfull B-)
  24. As I wrote, I do not know Xcelium. But I would check the error in bpad_10634.err...
×
×
  • Create New...