15 | | These rules enforce the PDES (Parallel Discrete Event Simulation) principles. Each PDES process involved in the simulation has its own local time, and PDES processes synchronize through messages piggybacked with time information. |
16 | | Models complying to these rules can be used with the "standard" OSCI simulation engine (SystemC 2.x), but can also be used also with others simulation engines, especially distributed, parallelized simulation engines. |
| 15 | These rules enforce the PDES (Parallel Discrete Event Simulation) principles. Each PDES process involved in |
| 16 | the simulation has its own local time, and PDES processes synchronize through messages piggybacked with time information. |
| 17 | Models complying to these rules can be used with the "standard" OSCI simulation engine (SystemC 2.x), but can |
| 18 | also be used also with others simulation engines, especially distributed, parallelized simulation engines. |
22 | | Figure 1 presents a minimal system containing one single initiator, '''!VciSimpleInitiator''' , and one single target, '''!VciSimpleTarget''' . The '''!VciSimpleInitiator''' module behavior is modeled by the SC_THREAD '''execLoop()''', that contains an infinite loop. The call-back function '''rspReceived()''' is executed when a VCI response packet is received by the initiator module. |
| 24 | Figure 1 presents a minimal system containing one single initiator, '''!VciSimpleInitiator''' , and one single |
| 25 | target, '''!VciSimpleTarget''' . The '''!VciSimpleInitiator''' module behavior is modeled by |
| 26 | the SC_THREAD '''execLoop()''', that contains an infinite loop. |
| 27 | The call-back function '''rspReceived()''' is executed when a VCI response packet is received by the initiator module. |
34 | | In the proposed example, the initiator module is modeled by the '''!VciSimpleInitiator''' class. This class inherits from the '''soclib::tlmt::!BaseModule''' class, that acts as the root class for all TLM-T modules. The process time, as well as other useful process attributes, is contained in a C++ structure called a '''tlmt_core::tlmt_thread_context'''.As there is only one thread in this module, there is only one member variable '''c0''' of type '''tlmt_core::tlmt_thread_context'''. '''c0''' mostly corresponds to the PDES process local time ('''H''' on the figure). In all the TLM-T models, time is handled by the '''tlmt_core::tlmt_time''' class. In order to get the time associated to the initiator process, one should use the getter function '''time()''' on the '''c0''' object, and the setter functions '''set_time()''' and '''update_time()''' to assign a new time to the process. |
35 | | |
36 | | The '''execLoop()''' method, describing the initiator behaviour must be declared as a member function of the '''!VciSimpleInitiator''' class. |
37 | | |
38 | | Finally, the class '''!VciSimpleInitiator''' must contain a member variable '''p_vci''', of type '''!VciInitiator'''. This object has a template parameter <'''vci_param'''> defining the widths of the VCI ADDRESS and DATA fields. |
| 40 | In the proposed example, the initiator module is modeled by the '''!VciSimpleInitiator''' class. |
| 41 | This class inherits from the '''soclib::tlmt::!BaseModule''' class, that acts as the root class for all TLM-T modules. |
| 42 | The process time, as well as other useful process attributes, is contained in a C++ structure called |
| 43 | a '''tlmt_core::tlmt_thread_context'''. As there is only one thread in this module, there is only one member |
| 44 | variable '''c0''' of type '''tlmt_core::tlmt_thread_context'''. '''c0''' mostly contains the PDES process local |
| 45 | time ('''H''' on the figure). In all the TLM-T models, time is handled by the '''tlmt_core::tlmt_time''' class. |
| 46 | In order to get the time associated to the initiator process, one should use the getter |
| 47 | function '''time()''' on the '''c0''' object, and should use the setter functions |
| 48 | '''set_time()''' and '''update_time()''' to assign a new time to the PDES process. |
| 49 | |
| 50 | The '''execLoop()''' method, describing the initiator behaviour must be declared as a member function of |
| 51 | the '''!VciSimpleInitiator''' class. |
| 52 | |
| 53 | Finally, the class '''!VciSimpleInitiator''' must contain a member variable '''p_vci''', of type '''!VciInitiator'''. |
| 54 | This object has a template parameter <'''vci_param'''> defining the widths of the VCI ADDRESS and DATA fields. |
42 | | To send a VCI command packet, the '''execLoop()''' method must use the '''send()''' method, that is a member function of the '''p_vci''' port. The prototype is the following: |
43 | | {{{ |
44 | | void send(vci_cmd_packet<vci_param> *cmd, // VCI command packet |
45 | | tlmt_core::tlmt_time time); // initiator local time |
46 | | }}} |
47 | | |
48 | | The informations transported by a VCI command packet are defined below: |
| 58 | To send a VCI command packet, the '''execLoop()''' method must use the '''send()''' method, that is a member function of |
| 59 | the '''p_vci''' port. The prototype is the following: |
| 60 | {{{ |
| 61 | void send(soclib::tlmt::vci_cmd_packet<vci_param> *cmd, // pointer to a VCI command packet |
| 62 | tlmt_core::tlmt_time time); // initiator local time |
| 63 | }}} |
| 64 | |
| 65 | The contents of a VCI command packet is defined below: |
68 | | The '''contig''' field can be used for optimisation. |
69 | | |
70 | | The '''send()''' function is non-blocking. To implement a blocking transaction (such as a cache line read, where the processor is ''frozen'' during the VCI transaction), the model designer must use the SystemC '''sc_core::wait(x)''' primitive ('''x''' being of type '''sc_core::sc_event''') : the '''execLoop()''' thread is then suspended, and will be reactivated when the response packet is actually received. |
| 85 | The '''contig''' field can be used for optimization. |
| 86 | |
| 87 | The '''send()''' function is non-blocking. |
| 88 | To implement a blocking transaction (such as a cache line read, where the processor is ''frozen'' during the VCI transaction), |
| 89 | the model designer must use the SystemC '''sc_core::wait(x)''' primitive ('''x''' being of type '''sc_core::sc_event'''): |
| 90 | the '''execLoop()''' thread is then suspended, and will be reactivated when the response packet is actually received. |
74 | | To receive a VCI response packet, a call-back function must be defined as a member function of the class '''!VciSimpleInitiator'''. This call-back function (named '''rspReceived()''' in the example), is executed each time a VCI response packet is received on the '''p_vci''' port. The function name is not constrained, but the arguments must respect the following prototype: |
75 | | {{{ |
76 | | tmpl(tlmt_core::tlmt_return&)::callback(soclib::tlmt::vci_rsp_packet<vci_param> *pkt, |
77 | | const tlmt_core::tlmt_time &time, |
78 | | void *private_data) |
79 | | }}} |
80 | | |
81 | | The informations transported by a VCI response packet are defined below: |
| 94 | To receive a VCI response packet, a call-back function must be defined as a member function of the |
| 95 | class '''!VciSimpleInitiator'''. This call-back function (named '''rspReceived()''' in the example), must be |
| 96 | declared in the '''!VciSimpleInitiator''' class and |
| 97 | is executed each time a VCI response packet is received on the '''p_vci''' port. The function name is not |
| 98 | constrained, but the arguments must respect the following prototype: |
| 99 | {{{ |
| 100 | tlmt_core::tlmt_return &rspReceived(soclib::tlmt::vci_rsp_packet<vci_param> *pkt, |
| 101 | const tlmt_core::tlmt_time &time, |
| 102 | void *private_data); |
| 103 | }}} |
| 104 | |
| 105 | The contents of a VCI response packet is defined below: |
96 | | The actions executed by the call-back function depend on the response transaction type ('''cmd''' field), as well as the '''pktid''' and '''trdid''' fields. |
| 120 | The second parameter, '''time''', is of type '''tlmt_core::tlmt_time''' and corresponds to the time of the response. The third parameter, |
| 121 | '''private_data''' has a default value of '''NULL''' and is not used when transmitting or receiving VCI packets. |
| 122 | |
| 123 | |
| 124 | The actions executed by the call-back function depend on the response transaction type ('''cmd''' field), as well as |
| 125 | the '''pktid''' and '''trdid''' fields. |
104 | | The constructor of the class '''!VciSimpleInitiator''' must initialize all the member variables, including the '''p_vci''' port. The '''rspReceived()''' call-back function being executed in the context of the thread sending the response packet, a link between the '''p_vci''' port and the call-back function must be established. Moreover, the '''p_vci''' port must contain a pointer to the initiator thread context '''c0'''. |
| 133 | The constructor of the class '''!VciSimpleInitiator''' must initialize all the member variables, including |
| 134 | the '''p_vci''' port. The '''rspReceived()''' call-back function being executed in the context of the thread sending |
| 135 | the response packet, a link between the '''p_vci''' port and the call-back function must be established. |
| 136 | Moreover, the '''p_vci''' port must contain a pointer to the initiator thread context '''c0'''. This allows the target to |
| 137 | get information on the initiator that actually sends VCI packets (the initiator local time, the initiator activity, etc). |
114 | | The SystemC simulation engine behaves as a cooperative, non-preemptive multi-tasks system. Any thread in the system must stop execution after at some point, in order to allow the other threads to execute. With the proposed approach, a TLM-T initiator will never stop if it does not execute blocking communication (such as a processor that has all code and data in the L1 caches). |
115 | | |
116 | | To solve this issue, it is necessary to define - for each initiator module- a '''lookahead''' parameter. This parameter defines the maximum number of cycles that can be executed by the thread before it is automatically stopped. The '''lookahead''' parameter allows the system designer to bound the de-synchronization time interval between threads. |
117 | | |
118 | | A small value for this parameter results in a better timing accuracy for the simulation, but implies a larger number of context switches, and a slower simulation speed. |
| 147 | The SystemC simulation engine behaves as a cooperative, non-preemptive multi-tasks system. Any thread in the system must stop execution |
| 148 | after at some point, in order to allow the other threads to execute. With the proposed approach, a TLM-T initiator will never stop if |
| 149 | it does not execute blocking communication (such as a processor that has all code and data in the L1 caches). |
| 150 | |
| 151 | To solve this issue, it is necessary to define -for each initiator module- a '''lookahead''' parameter. This parameter defines the maximum |
| 152 | number of cycles that can be executed by the thread before it is automatically stopped. The '''lookahead''' parameter allows the system designer |
| 153 | to bound the de-synchronization time interval between threads. |
| 154 | |
| 155 | A small value for this parameter results in a better timing accuracy for the simulation, but implies a larger number of context switches, |
| 156 | and a slower simulation speed. |
123 | | template <typename vci_param> |
124 | | class my_initiator : tlmt::BaseModule { |
| 161 | |
| 162 | ////////////////////////// vci_simple_initiator.h //////////////////////////////// |
| 163 | |
| 164 | #ifndef VCI_SIMPLE_INITIATOR_H |
| 165 | #define VCI_TLMT_SIMPLE_INITIATOR_H |
| 166 | |
| 167 | #include <tlmt> |
| 168 | #include "tlmt_base_module.h" |
| 169 | #include "vci_ports.h" |
| 170 | |
| 171 | namespace soclib { namespace tlmt { |
| 172 | |
| 173 | template<typename vci_param> |
| 174 | class VciSimpleInitiator |
| 175 | : public soclib::tlmt::BaseModule |
| 176 | { |
| 177 | tlmt_core::tlmt_thread_context c0; |
| 178 | sc_core::sc_event m_rsp_received; |
| 179 | tlmt_core::tlmt_return m_return; |
| 180 | soclib::tlmt::vci_cmd_packet<vci_param> cmd; |
| 181 | |
| 182 | uint32_t addresses[8]; // address table, |
| 183 | uint32_t localbuf[8]; // local buffer |
| 184 | |
| 185 | uint32_t m_counter; // iteration counter |
| 186 | uint32_t m_lookahead; // lookahead value |
| 187 | uint32_t m_index; // initiator index |
| 188 | |
| 189 | protected: |
| 190 | SC_HAS_PROCESS(VciSimpleInitiator); |
| 191 | |
126 | | VciInitiatorPort <vci_param> p_vci; |
| 193 | soclib::tlmt::VciInitiator<vci_param> p_vci; |
| 194 | |
| 195 | VciSimpleInitiator( sc_core::sc_module_name name, |
| 196 | uint32_t initiator index, |
| 197 | uint32_t lookahead ); |
| 198 | |
| 199 | tlmt_core::tlmt_return &rspReceived(soclib::tlmt::vci_rsp_packet<vci_param> *pkt, |
| 200 | const tlmt_core::tlmt_time &time, |
| 201 | void *private_data); |
| 202 | void behavior(); |
| 203 | }; |
| 204 | |
| 205 | }} |
| 206 | |
| 207 | #endif |
| 208 | |
| 209 | ////////////////////////// vci_simple_initiator.cpp //////////////////////////////// |
| 210 | |
| 211 | #include "../include/vci_simple_initiator.h" |
| 212 | |
| 213 | namespace soclib { namespace tlmt { |
| 214 | |
| 215 | #define tmpl(x) template<typename vci_param> x VciSimpleInitiator<vci_param> |
| 216 | |
| 217 | tmpl(tlmt_core::tlmt_return&)::rspReceived(soclib::tlmt::vci_rsp_packet<vci_param> *pkt, |
| 218 | const tlmt_core::tlmt_time &time, |
| 219 | void *private_data) |
| 220 | { |
| 221 | if(pkt->cmd == vci_param::VCI_CMD_READ) { |
| 222 | c0.set_time(time + tlmt_core::tlmt_time(pkt->length)); |
| 223 | m_rsp_received.notify(sc_core::SC_ZERO_TIME) ; |
| 224 | } |
| 225 | return m_return; |
| 226 | } |
| 227 | |
| 228 | tmpl(void)::behavior() |
| 229 | { |
| 230 | |
| 231 | for(;;) { |
| 232 | |
| 233 | // sending a read VCI packet |
128 | | //////// constructor |
129 | | my_initiator (sc_module_name name, |
130 | | uint32_t initiatorIndex |
131 | | uint32_t lookahead) : |
132 | | p_vci(“vci”, this, &my_initiator::rspReceived, &m_time), |
133 | | tlmt::BaseModule(name), |
134 | | m_time(0) |
135 | | { |
136 | | m_index = InitiatorIndex; |
137 | | m_lookahed = lookahead; |
138 | | m_counter = 0; |
139 | | SC_THREAD(execLoop); |
140 | | } // end constructor |
| 235 | cmd.cmd = vci_param::CMD_READ; // a VCI read packet |
| 236 | addresses[0] = 0xBFC00000; // the start address |
| 237 | cmd.address = addresses; // assigned |
| 238 | cmd.be = 0xF; // reading full words |
| 239 | cmd.contig = true; // at successive addresses |
| 240 | cmd.buf = localbuf; // storing the read results in localbuf |
| 241 | cmd.length = 8; // packet of 8 words |
| 242 | cmd.srcid = 0; // srcid=0 |
| 243 | cmd.trdid = 0; // trdid=0 |
| 244 | cmd.pktid = 0; // pktid=0 |
| 245 | |
| 246 | tlmt_core::tlmt_return ret; // in case a test on the send function is needed |
| 247 | ret = p_vci.send(&cmd, c0.time()); // sending the packet |
| 248 | sc_core::wait(m_rsp_received); // and waiting for the response packet |
| 249 | |
| 250 | // sending a write VCI packet |
142 | | private: |
143 | | tlmt_time m_time; // local clock |
144 | | uint32_t m_index; // initiator index |
145 | | uint32_t m_counter; // iteration counter |
146 | | uint32_t m_lookahed; // lookahead value |
147 | | vci_param::data_t m_data[8]; // local buffer |
148 | | vci_cmd_t m_cmd; // paquet VCI commande |
149 | | sc_event m_rsp_received; // synchronisation signal |
150 | | |
151 | | //////// thread |
152 | | void execLoop() |
153 | | { |
154 | | while(1) { |
155 | | … |
156 | | m_cmd.cmd = VCI_CMD_READ; |
157 | | p_vci.send(&m_cmd, m_time.getTime()); // lecture bloquante |
158 | | wait(m_rsp_resceived); |
159 | | … |
160 | | m_cmd.cmd = VCI_CMD_WRITE; |
161 | | p_vci.send(&m_cmd, m_time.getTime()); // écriture non bloquante |
162 | | ... |
163 | | // lookahead management |
164 | | m_counter++ ; |
165 | | if (m_counter >= m_lookahead) { |
166 | | m_counter = 0 ; |
167 | | wait(SC_ZERO_TIME) ; |
168 | | } // end if |
169 | | m_time.addtime(1) ; |
170 | | } // end while |
171 | | } // end execLoop() |
172 | | |
173 | | //////////////// call-back function |
174 | | void rspReceived(vci_cmd_t *cmd, uint32_t rsp_time) |
175 | | { |
176 | | if(cmd == VCI_CMD_READ) { |
177 | | m_time.set_time(rsp_time + length); |
178 | | m_rsp_received.notify(SC_ZERO_TIME) ; |
179 | | } |
180 | | } // end rspReceived() |
181 | | } // end class my_initiator |
| 252 | localbuf[0]=0x00112233; // first, fill the write local buffer with write data |
| 253 | |
| 254 | cmd.cmd = vci_param::CMD_WRITE; // then issue the VCI write packet |
| 255 | addresses[0] = 0x10000000; // starting with this address |
| 256 | cmd.address = addresses; |
| 257 | cmd.be = 0xF; |
| 258 | cmd.contig = 0; |
| 259 | cmd.buf = localbuf; |
| 260 | cmd.length = 1; |
| 261 | cmd.srcid = 0; |
| 262 | cmd.trdid = 0; |
| 263 | cmd.pktid = 0; |
| 264 | |
| 265 | ret = p_vci.send(&cmd, c0.time()); // Don't wait for the answer |
| 266 | |
| 267 | // lookahead management |
| 268 | m_counter++ ; |
| 269 | if (m_counter >= m_lookahead) { |
| 270 | m_counter = 0 ; |
| 271 | sc_core::wait(sc_core::SC_ZERO_TIME) ; |
| 272 | } // end if |
| 273 | |
| 274 | // process time= process time+1 |
| 275 | c0.set_time(c0.time()+tlmt_core::tlmt_time(1)) ; |
| 276 | |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | tmpl(/**/)::VciSimpleInitiator( sc_core::sc_module_name name, |
| 281 | uint32_t initiator index, |
| 282 | uint32_t lookahead) |
| 283 | : soclib::tlmt::BaseModule(name), |
| 284 | m_index(initiator_index), |
| 285 | m_lookahead(lookahead), |
| 286 | m_counter(0), |
| 287 | p_vci("vci", new tlmt_core::tlmt_callback<VciSimpleInitiator,soclib::tlmt::vci_rsp_packet<vci_param> *>( |
| 288 | this, &VciSimpleInitiator<vci_param>::rspReceived), &c0) |
| 289 | { |
| 290 | SC_THREAD(behavior); |
| 291 | } |
| 292 | |
| 293 | }} |
| 294 | |
186 | | In the proposed example, the target handle two types of command : a read burst of 8 words, and a write burst of 8 words. To simplify the model, there is no error management. |
187 | | |
188 | | The class '''my_target''' inherit the class '''!BaseModule''', that is the basis for all TLM-T modules. The class '''my_target''' contains a member variable '''p_vci''' of type '''!VciTargetPort'''. This object has a template parameter '''<vci_param>''' defining the widths of the VCI ADRESS & DATA fields. |
| 299 | In the proposed example, the target handles two types of command: a read burst of 8 words, and a write burst of 8 words. |
| 300 | To simplify the model, there is no error management. |
| 301 | |
| 302 | The class '''!VciSimpleTarget''' inherits from the class '''!BaseModule'''. The class '''!VciSimpleTarget''' contains a member |
| 303 | variable '''p_vci''' of type '''!VciTarget'''. This object has a template parameter '''<vci_param>''' defining the widths of |
| 304 | the VCI ADDRESS and DATA fields. |
192 | | To receive a VCI command packet, a call-back function must be defined as a member function of the class '''my_target'''. This call-back function (named '''cmdReceived()''' in the example), will be executed each time a VCI command packet is received on the '''p_vci''' port. The function name is not constrained, but the arguments must respect the following prototype: |
193 | | {{{ |
194 | | uint32_t cmdReceived(vci_cmd_t *cmd, |
195 | | uint32_t time) |
| 308 | To receive a VCI command packet, a call-back function must be defined as a member function of the class '''!VciSimpleTarget'''. |
| 309 | This call-back function (named '''cmdReceived()''' in the example), will be executed each time a VCI command packet is received on |
| 310 | the '''p_vci''' port. The function name is not constrained, but the arguments must respect the following prototype: |
| 311 | {{{ |
| 312 | tlmt_core::tlmt_return &cmdReceived(soclib::tlmt::vci_cmd_packet<vci_param> *pkt, |
| 313 | const tlmt_core::tlmt_time &time, |
| 314 | void *private_data); |
202 | | To send a VCI response packet the '''cmdReceived()''' function must use the '''send()''' method, that is a member function of the class '''!VciTargetPort''', and has the following prototype: |
203 | | {{{ |
204 | | void send( vci_rsp_t *cmd, |
205 | | uint32_t time) |
206 | | }}} |
207 | | For a reactive target, the response packet date is computed as the command packet date plus the target intrinsic latency. |
| 322 | To send a VCI response packet the '''cmdReceived()''' function must use the '''send()''' method, that is a member function of |
| 323 | the class '''!VciTarget''', and has the following prototype: |
| 324 | {{{ |
| 325 | void send(soclib::tlmt::vci_rsp_packet<vci_param> *rsp, // pointer to a VCI response packet |
| 326 | tlmt_core::tlmt_time time); // initiator local time |
| 327 | }}} |
| 328 | For a reactive target, the response packet time is computed as the command packet time plus the target intrinsic latency. |
211 | | The constructor of the class '''my_target''' must initialize all the member variables, including the '''p_vci''' port. The '''cmdReceived()''' call-back function being executed in the context of the thread sending the command packet, a link between the '''p_vci''' port and the call-back function must be established. |
212 | | The '''!VciTargetPort''' constructor must be called with the following arguments: |
213 | | {{{ |
214 | | p_vci(“vci”, this, &my_initiator::cmdReceived) |
| 332 | The constructor of the class '''!VciSimpleTarget''' must initialize all the member variables, including |
| 333 | the '''p_vci''' port. The '''cmdReceived()''' call-back function being executed in the context of the thread sending the command packet, |
| 334 | a link between the '''p_vci''' port and the call-back function must be established. |
| 335 | The '''!VciTarget''' constructor must be called with the following arguments: |
| 336 | {{{ |
| 337 | p_vci("vci", new tlmt_core::tlmt_callback<VciSimpleTarget,soclib::tlmt::vci_cmd_packet<vci_param> *>( |
| 338 | this, &VciSimpleTarget<vci_param>::cmdReceived)) |
219 | | template <typename vci_param> |
220 | | class my_target : tlmt::BaseModule { |
| 343 | |
| 344 | ////////////////////////// vci_simple_target.h //////////////////////////////// |
| 345 | |
| 346 | #ifndef VCI_SIMPLE_TARGET_H |
| 347 | #define VCI_SIMPLE_TARGET_H |
| 348 | |
| 349 | #include <tlmt> |
| 350 | #include "tlmt_base_module.h" |
| 351 | #include "vci_ports.h" |
| 352 | |
| 353 | namespace soclib { namespace tlmt { |
| 354 | |
| 355 | template<typename vci_param> |
| 356 | class VciSimpleTarget |
| 357 | : public soclib::tlmt::BaseModule |
| 358 | { |
| 359 | private: |
| 360 | tlmt_core::tlmt_return m_return; |
| 361 | uint32_t m_index; |
| 362 | uint32_t m_latency; |
| 363 | soclib::tlmt::vci_rsp_packet<vci_param> rsp; |
| 364 | |
227 | | uint32_t latency): |
228 | | p_vci(“vci”,this, &my_target::cmdReceived), |
229 | | tlmt::BaseModule(name) |
230 | | { |
231 | | m_latency = latency; |
232 | | m_index = targetIndex; |
233 | | } // end constructor |
| 370 | uint32_t latency); |
| 371 | |
| 372 | tlmt_core::tlmt_return &cmdReceived(soclib::tlmt::vci_cmd_packet<vci_param> *pkt, |
| 373 | const tlmt_core::tlmt_time &time, |
| 374 | void *private_data); |
| 375 | }; |
| 376 | |
| 377 | }} |
| 378 | |
| 379 | #endif |
| 380 | |
| 381 | ////////////////////////// vci_simple_target.cpp //////////////////////////////// |
| 382 | |
| 383 | #include "../include/vci_simple_target.h" |
| 384 | |
| 385 | namespace soclib { namespace tlmt { |
| 386 | |
| 387 | #define tmpl(x) template<typename vci_param> x VciSimpleTarget<vci_param> |
| 388 | |
| 389 | tmpl(tlmt_core::tlmt_return&)::cmdReceived(soclib::tlmt::vci_cmd_packet<vci_param> *pkt, |
| 390 | const tlmt_core::tlmt_time &time, |
| 391 | void *private_data) |
| 392 | { |
| 393 | uint32_t m_data[128]; |
235 | | private: |
236 | | vci_param::data_t m_data[8]; // local buffer |
237 | | uint32_t m_latency; // target latency |
238 | | uint32_t m_index; // target index |
239 | | vci_rsp_t m_rsp; // paquet VCI réponse |
240 | | |
241 | | /////////////// call-back function |
242 | | uint32_t cmdReceived(vci_cmd_t *cmd, |
243 | | uint32_t cmd_time) |
244 | | { |
245 | | if(cmd->cmd == VCI_CMD_WRITE) { |
246 | | for(int i = 0 ; i < length ; i++) m_data[i] = cmd->buf[i]; |
| 395 | if(pkt->cmd == vci_param::VCI_CMD_WRITE) { |
| 396 | for(int i = 0 ; i < pkt->length ; i++) |
| 397 | m_data[i] = cmd->buf[i]; |
251 | | m_rsp.srcid = cmd->srcid; |
252 | | m_rsp.trdid = cmd->trdid; |
253 | | m_rsp.pktid = cmd>pktid; |
254 | | m_rsp.length = cmd->length; |
255 | | m_rsp.error = 0; |
256 | | rsp_time = cmd_time + latency; |
257 | | p_vci.send(&m_rsp, rsp_time) ; |
258 | | return (rsp_time + cmd->length); |
259 | | } // end cmdReceived() |
260 | | } // end class my_target |
| 403 | rsp.srcid = pkt->srcid; |
| 404 | rsp.trdid = pkt->trdid; |
| 405 | rsp.pktid = pkt->pktid; |
| 406 | rsp.length = pkt->length; |
| 407 | rsp.error = 0; |
| 408 | p_vci.send(&m_rsp, time+tlmt_core::tlmt_time(latency+pkt->length)) ; |
| 409 | m_return.time=time+tlmt_core::tlmt_time(latency+pkt->length; |
| 410 | return (m_return); |
| 411 | } |
| 412 | |
| 413 | tmpl(/**/)::VciSimpleTarget(sc_core::sc_module_name name, |
| 414 | uint32_t targetIndex, |
| 415 | uint32_t latency) |
| 416 | : soclib::tlmt::BaseModule(name), |
| 417 | m_index(targetIndex), |
| 418 | m_latency(latency), |
| 419 | p_vci("vci", new tlmt_core::tlmt_callback<VciSimpleTarget,soclib::tlmt::vci_cmd_packet<vci_param> *>( |
| 420 | this, &VciSimpleTarget<vci_param>::cmdReceived)) |
| 421 | { |
| 422 | } |
| 423 | |
| 424 | }} |
| 425 | |
267 | | we don't want to reproduce the cycle-accurate behavior of a particular interconnect. We only want to simulate the contention in the network, when several VCI intitiators try to reach the same VCI target. |
268 | | Therefore, the network is actually modeled as a complete cross-bar : In a physical network such as the multi-stage network described in Figure 2.a, conflicts can appear at any intermediate switch. In the '''!VciVgmn''' network described in Figure 2.b, conflicts can only happen at the output ports. It is possible to specify a specific latency for each input/output couple. As in most physical interconnects, the general arbitration policy is round-robin. |
| 432 | we don't want to reproduce the cycle-accurate behavior of a particular interconnect. We only want to simulate the contention in |
| 433 | the network, when several VCI intitiators try to reach the same VCI target. |
| 434 | Therefore, the network is actually modeled as a complete cross-bar : In a physical network such as the multi-stage network described |
| 435 | in Figure 2.a, conflicts can appear at any intermediate switch. In the '''!VciVgmn''' network described in Figure 2.b, conflicts can |
| 436 | only happen at the output ports. It is possible to specify a specific latency for each input/output couple. As in most physical |
| 437 | interconnects, the general arbitration policy is round-robin. |
274 | | There is actually two fully independent networks for VCI command packets and VCI response packets. There is a routing function for each input port, and an arbitration function for each output port, but the two networks are not symmetrical : |
275 | | * For the command network, the arbitration policy is distributed: there is one arbitration thread for each output port (i.e. one arbitration thread for each VCI target). Each arbitration thread is modeled by a SC_THREAD, and contain a local clock. |
276 | | * For the response network, there is no conflicts, and there is no need for arbitration. Therefore, there is no thread (and no local time) and the response network is implemented by simple function calls. |
| 443 | There is actually two fully independent networks for VCI command packets and VCI response packets. There is a routing function for each input |
| 444 | port, and an arbitration function for each output port, but the two networks are not symmetrical : |
| 445 | * For the command network, the arbitration policy is distributed: there is one arbitration thread for each output port |
| 446 | (i.e. one arbitration thread for each VCI target). Each arbitration thread is modeled by a SC_THREAD, and contains a local clock. |
| 447 | * For the response network, there are no conflicts, and there is no need for arbitration. Therefore, there is no thread |
| 448 | (and no local time) and the response network is implemented by simple function calls. |
282 | | == E.2) VCI initiators and targets synchronisations == |
283 | | |
284 | | As described in sections B & C, each VCI initiator TLM-T module contains a thread and a local clock. But, in order to increase the TLM-T simulation speed, the VCI targets are generally described by reactive call-back functions. Therefore, there is no thread, and no local clock in the TLM-T module describing a VCI target. For a VCI target, the local clock is actually the clock associated to the corresponding arbitration thread contained in the '''!VciVgmn''' module. |
285 | | |
286 | | As described in Figure 4, when a VCI command packet - sent by the corresponding arbitration thread - is received by a VCI target, two synchronization mechanisms are activated : |
287 | | * The '''cmdReceived()''' function sends a VCI response packet with a date to the source initiator, through the '''!VciVgmn''' response network. The corresponding date can be used to update the initiator local clock. |
288 | | * The '''cmdReceived()''' function returns a date to the arbitration thread. This date is used to update the arbitration thread local clock. |
| 454 | == E.2) VCI initiators and targets synchronizations == |
| 455 | |
| 456 | As described in sections B & C, each VCI initiator TLM-T module contains a thread and a local clock. But, in order to increase the TLM-T simulation |
| 457 | speed, the VCI targets are generally described as reactive call-back functions. Therefore, there is no thread, and no local clock in the TLM-T |
| 458 | module describing a VCI target. For a VCI target, the local clock is actually the clock associated to the corresponding arbitration |
| 459 | thread contained in the '''!VciVgmn''' module. |
| 460 | |
| 461 | As described in Figure 4, when a VCI command packet - sent by the corresponding arbitration thread - is received by a VCI target, |
| 462 | two synchronization mechanisms are activated : |
| 463 | * The '''cmdReceived()''' function sends a VCI response packet with an associated time to the source initiator, through |
| 464 | the '''!VciVgmn''' response network. The corresponding time can be used to update the initiator local clock. |
| 465 | * The '''cmdReceived()''' function returns a time to the arbitration thread. This time is used to update the arbitration thread local |
| 466 | clock. |
294 | | Interrupts are asynchronous events that are not transported by the VCI network. |
295 | | |
296 | | As illustrated in Figure 5, each interrupt line is modeled by a specific point to point, uni-directional channel. It use two ports of type '''!SynchroOutPort''' and '''!SynchroInPort''' that must be declared as member variables of the source and destination modules respectively. |
| 472 | Interrupts are asynchronous events that are not carried by the VCI network. |
| 473 | |
| 474 | As illustrated in Figure 5, each interrupt line is modeled by a specific point to point, uni-directional channel. This channel uses two ports of |
| 475 | type '''tlmt_core::tlmt_out<bool>''' and '''tlmt_core::tlmt_in<bool>''' that must be declared as member variables of the source and |
| 476 | destination modules respectively. |
302 | | The source module (named '''my_source''' in this example) must contain a member variable '''p_irq''' of type '''!SynchroOutPort'''. To activate, or desactivate an interruption, the source module must use the '''send()''' method, that is a member function of the '''!SynchroOutPort''' class. Those interrupt packets transport both a Boolean, and a date. The '''send()''' prototype is defined as follows : |
303 | | {{{ |
304 | | void send( bool val, uint32_t time) |
| 482 | The source module (named '''!VciSimpleSourceInterrupt''' in this example) must contain a member variable '''p_irq''' of |
| 483 | type '''tlmt_core::tlmt_out<bool>'''. To activate, or desactivate an interruption, the source module must use the '''send()''' method, |
| 484 | that is a member function of the '''tlmt_core::tlmt_out<bool>''' class. These interrupt packets transport both a Boolean, |
| 485 | and a time. The '''send()''' prototype is defined as follows : |
| 486 | {{{ |
| 487 | void send( bool val, const tlmt_core::tlmt_time &time) |
309 | | The destination module (named here '''my_processor''') must contain a member variable '''p_irq''' of type |
310 | | '''!SynchroInPortt''', and a call-back function (named here '''irqReceived()''' that is executed when an interrupt packet is received on the '''p_irq''' port. |
311 | | |
312 | | A link between the '''p_irq''' port and the call-back function mus be established by the port constructor in the constructor of the class '''my_processor''' : |
313 | | {{{ |
314 | | p_irq(“irq”, this, &my_processor::irqReceived) |
315 | | }}} |
316 | | |
317 | | In the Parallel Discrete Event Simulation, the pessimistic approach suppose that any PDES process is not allowed to update his local time until he has received messages on all input ports with dates larger than his local time. |
318 | | |
319 | | Therefore, a SC_THREAD modeling the behavior of a processor containing an '''!SynchroInPort''' should in principle wait a dated packet on this interrupt port before executing instructions. Such behavior would be very inefficient, and could create dead-lock situations. |
| 492 | The destination module (named here '''!VciProcessor''') must contain a member variable '''p_irq''' of type |
| 493 | '''tlmt_core::tlmt_in<bool>''', and a call-back function (named here '''irqReceived()''' that is executed when an interrupt |
| 494 | packet is received on the '''p_irq''' port. |
| 495 | |
| 496 | A link between the '''p_irq''' port and the call-back function must be established by the port constructor in the constructor of |
| 497 | the class '''VciProcessor''' : |
| 498 | {{{ |
| 499 | tlmt_core::tlmt_callback < VciProcessor,bool >(this, &VciProcessor < iss_t, |
| 500 | vci_param >::irqReceived); |
| 501 | }}} |
| 502 | |
| 503 | In the Parallel Discrete Event Simulation, the pessimistic approach relies on the fact that any PDES process is not allowed to update |
| 504 | his local time until it has received messages on all its input ports with times greater than its local time. |
| 505 | |
| 506 | Therefore, a SC_THREAD modeling the behavior of a processor containing an '''tlmt_core::tlmt_in<bool>''' should in principle |
| 507 | wait a timestamped packet on its interrupt port before executing instructions. However, such a behavior would be very inefficient, |
| 508 | and is prone to dead-lock situations. |
322 | | * The call-back function '''irqReceived()''' sets the member variables '''m_irqpending''' and '''m_irqtime''', when a interrupt packet is received on the '''p_irq''' port. |
323 | | * The '''execLoop()''' thread must test the '''m_irqpending''' variable at each cycle (i.e. in each iteration of the infinite loop). |
324 | | * If there is no interrupt pending, the thread continues execution. If an interrupt is pending, and the interrupt date is larger than the local time, the thread continues execution. If the interrupt date is equal or smaller than the local time, the interrupt is handled. |
325 | | |
326 | | Such violation of the the pessimistic parallel simulation create a loss of accuracy on the interrupt handling date. This inaccuracy in the TLM-T simulation is acceptable, as interrupts are asynchronous events, and the timing error is bounded by the '''m_lookahead''' parameter. |
| 511 | * The call-back function '''irqReceived()''' sets the member variables '''m_irqpending''' and '''m_irqtime''', when a interrupt packet |
| 512 | is received on the '''p_irq''' port. |
| 513 | * The '''execLoop()''' thread must test the '''m_irqpending''' variable at each cycle (i.e. in each iteration of |
| 514 | the infinite loop). |
| 515 | * If there is no interrupt pending, the thread continues its execution. If an interrupt is pending, and the interrupt time is greater |
| 516 | than the local time, the thread continues execution. If the interrupt time is equal or smaller than the local time, the interrupt |
| 517 | is handled. |
| 518 | |
| 519 | Such a violation of the the pessimistic parallel simulation principles creates a loss of accuracy on the interrupt handling |
| 520 | timestamp. This loss of accuracy in the TLM-T simulation is acceptable, as interrupts are asynchronous events, and the timing error |
| 521 | is bounded by the '''m_lookahead''' parameter. |
331 | | class my_processor : tlmt::BaseModule { |
| 526 | |
| 527 | ////////////////////////// vci_processor.h //////////////////////////////// |
| 528 | |
| 529 | namespace soclib { namespace tlmt { |
| 530 | |
| 531 | template<typename iss_t,typename vci_param> |
| 532 | class VciProcessor |
| 533 | : public soclib::tlmt::BaseModule |
| 534 | { |
| 535 | tlmt_core::tlmt_thread_context c0; |
| 536 | sc_core::sc_event m_rsp_received; |
| 537 | tlmt_core::tlmt_return m_return; |
| 538 | bool m_irqpendig; // pending interrupt request |
| 539 | tlmt_core::tlmt_time m_irqtime; // irq date |
| 540 | uint32_t m_counter; // iteration counter |
| 541 | uint32_t m_lookahead; // lookahead value |
| 542 | |
| 543 | protected: |
| 544 | SC_HAS_PROCESS(VciProcessor); |
| 545 | |
333 | | SynchroInPort p_irq; |
334 | | |
335 | | ////// constructor |
336 | | my_processor (sc_module_name name, |
337 | | uint32_t lookahead) : |
338 | | p_irq(“irq”, this, &my_initiator::irqReceived), |
339 | | m_time(0), |
340 | | tlmt::BaseModule(name) |
| 547 | soclib::tlmt::VciInitiator<vci_param> p_vci; |
| 548 | tlmt_core::tlmt_in<bool> p_irq; |
| 549 | |
| 550 | VciProcessor( sc_core::sc_module_name name, int id ); |
| 551 | |
| 552 | tlmt_core::tlmt_return &rspReceived(soclib::tlmt::vci_rsp_packet<vci_param> *pkt, |
| 553 | const tlmt_core::tlmt_time &time, |
| 554 | void *private_data); |
| 555 | tlmt_core::tlmt_return &irqReceived(bool, |
| 556 | const tlmt_core::tlmt_time &time, |
| 557 | void *private_data); |
| 558 | void execLoop(); |
| 559 | }; |
| 560 | |
| 561 | }} |
| 562 | |
| 563 | ////////////////////////// vci_processor.cpp //////////////////////////////// |
| 564 | |
| 565 | #include "../include/vci_processor.h" |
| 566 | |
| 567 | namespace soclib |
| 568 | { |
| 569 | namespace tlmt |
| 570 | { |
| 571 | |
| 572 | #define tmpl(x) template<typename iss_t, typename vci_param> x VciProcessor<vci_param> |
| 573 | |
| 574 | tmpl (tlmt_core::tlmt_return &)::rspReceived (soclib::tlmt:: vci_rsp_packet < vci_param > *pkt, |
| 575 | const tlmt_core:: tlmt_time & time, void *private_data) |
342 | | m_lookahed = lookahead; |
343 | | m_counter = 0; |
344 | | m_irqset = false; |
345 | | SC_THREAD(execLoop); |
346 | | } // end constructor |
347 | | |
348 | | private: |
349 | | tlmt_time m_time; // local clock |
350 | | bool m_irqpendig; // pending interrupt request |
351 | | uint32_t m_irqtime; // irq date |
352 | | uint32_t m_counter; // iteration counter |
353 | | uint32_t m_lookahed; // lookahead value |
354 | | |
355 | | /////////////// thread |
356 | | void execLoop() |
| 577 | if (pkt->cmd == vci_param::CMD_WRITE) |
| 578 | m_write_error = (pkt->error != 0); |
| 579 | else |
| 580 | m_write_error = false; |
| 581 | if (pkt->cmd == vci_param::CMD_READ) |
| 582 | m_read_error = (pkt->error != 0); |
| 583 | else |
| 584 | m_read_error = false; |
| 585 | m_rsptime = time + tlmt_core::tlmt_time (pkt->length); |
| 586 | m_vci_pending = false; |
| 587 | m_rsp_received.notify (sc_core::SC_ZERO_TIME); |
| 588 | |
| 589 | return m_return; |
| 590 | } |
| 591 | |
| 592 | tmpl (tlmt_core::tlmt_return &)::irqReceived (bool v, const tlmt_core:: |
| 593 | tlmt_time & time, void *private_data) |
365 | | // lookahead management |
366 | | m_counter++ ; |
367 | | if (m_counter >= m_lookahead) { |
368 | | m_counter = 0 ; |
369 | | wait(SC_ZERO_TIME) ; |
370 | | } // end if |
371 | | m_time.addtime(1) ; |
372 | | } // end while |
373 | | } // end execLoop() |
374 | | |
375 | | /////////////////////////////////////// |
376 | | void irqReceived(bool val, sc_time time) |
| 612 | // lookahead management |
| 613 | m_counter++ ; |
| 614 | if (m_counter >= m_lookahead) { |
| 615 | m_counter = 0 ; |
| 616 | sc_core::wait(sc_core::SC_ZERO_TIME) ; |
| 617 | } // end if |
| 618 | c0.set_time(c0.time()+tlmt_core::tlmt_time(1)) ; |
| 619 | } // end while |
| 620 | } |
| 621 | |
| 622 | tmpl ( /**/)::VciProcessor( |
| 623 | sc_core::sc_module_name name, |
| 624 | int id ) |
| 625 | : soclib::tlmt::BaseModule (name), |
| 626 | m_counter (0), |
| 627 | m_lookahead (10), |
| 628 | p_vci("vci",new tlmt_core::tlmt_callback<VciProcessor,vci_rsp_packet<vci_param>*>( |
| 629 | this,&VciProcessor<vci_param>::rspReceived),&c0), |
| 630 | p_irq("irq",new tlmt_core::tlmt_callback<VciProcesspr,bool>( |
| 631 | this,&VciProcessor<vci_param>::irqReceived)) |