| 1 | {{{ |
| 2 | #!html |
| 3 | <h1>Writing efficient TLM-T SystemC simulation models for SoCLib</h1> |
| 4 | }}} |
| 5 | |
| 6 | Authors : Alain Greiner, François Pêcheux, Emmanuel Viaud, Nicolas Pouillon |
| 7 | |
| 8 | [[PageOutline]] |
| 9 | |
| 10 | = A) Introduction = |
| 11 | |
| 12 | '''This document is under development'''. |
| 13 | |
| 14 | This new document describes the modeling rules for writing TLM-T SystemC simulation models for SoCLib. |
| 15 | This is the second release, as the modeling rules have been modified to respect the TLM2.0 standard. |
| 16 | |
| 17 | In TLM2.0, both the '''payload''' (defining the actual content of the exchanged packets), and the '''phase''' |
| 18 | (defining the actual communication protocol steps) are template parameters. |
| 19 | Therefore, specific '''payload''' and '''phase''' types have been defined for SoCLib. |
| 20 | Those types are well suited for the VCI/OCP protocol, but they more general and can |
| 21 | used to describe any shared-memory architecture using routed network on chip. |
| 22 | |
| 23 | The TLM-T rules used in SoCLib enforce the PDES (Parallel Discrete Event Simulation) principles. Each PDES process involved in |
| 24 | the simulation has its own local time, and PDES processes synchronize through messages piggybacked with time information. |
| 25 | Models complying to these rules can be used with the "standard" OSCI simulation engine (SystemC 2.x), but can |
| 26 | also be used also with others simulation engines, especially distributed, parallelized simulation engines. |
| 27 | |
| 28 | Before writing a new model, it is useful to have a look at the [WritingRules/General general SoCLib rules]. |
| 29 | |
| 30 | = B) Single VCI initiator and single VCI target = |
| 31 | |
| 32 | Figure 1 presents a minimal system containing one single initiator, '''!VciSimpleInitiator''' , and one single |
| 33 | target, '''!VciSimpleTarget''' . The '''!VciSimpleInitiator''' module behavior is modeled by |
| 34 | the SC_THREAD '''execLoop()''', that contains an infinite loop. |
| 35 | |
| 36 | [[Image(tlmt_figure_1.png, nolink)]] |
| 37 | |
| 38 | Unlike the initiator, the target module has a purely reactive behaviour and there is no need to use a SC_THREAD : |
| 39 | The target behaviour is entirely described by the call-back function, |
| 40 | that is executed in the context of the '''execLoop() thread when a VCI command packet is received by the target module. |
| 41 | |
| 42 | The VCI communication channel is a point-to-point bi-directionnal channel, encapsulating two separated uni-directionnal |
| 43 | channels: one to transmit the VCI command packet, one to transmit the VCI response packet. |
| 44 | |
| 45 | = C) VCI initiator Modeling = |
| 46 | |
| 47 | In the proposed example, the initiator module is modeled by the '''!VciSimpleInitiator''' class. |
| 48 | This class inherits from the '''soclib::tlmt::!BaseModule''' class, that acts as the root class for all TLM-T modules. |
| 49 | The process time, as well as other useful process attributes, is contained in a C++ structure called |
| 50 | a '''tlmt_core::tlmt_thread_context'''. As there is only one thread in this module, there is only one member |
| 51 | variable '''c0''' of type '''tlmt_core::tlmt_thread_context'''. '''c0''' mostly contains the PDES process local |
| 52 | time ('''H''' on the figure). In all the TLM-T models, time is handled by the '''tlmt_core::tlmt_time''' class. |
| 53 | In order to get the time associated to the initiator process, one should use the getter |
| 54 | function '''time()''' on the '''c0''' object, and should use the setter functions |
| 55 | '''set_time()''' and '''update_time()''' to assign a new time to the PDES process. |
| 56 | |
| 57 | The '''execLoop()''' method, describing the initiator behaviour must be declared as a member function of |
| 58 | the '''!VciSimpleInitiator''' class. |
| 59 | |
| 60 | Finally, the class '''!VciSimpleInitiator''' must contain a member variable '''p_vci''', of type '''!VciInitiator'''. |
| 61 | This object has a template parameter <'''vci_param'''> defining the widths of the VCI ADDRESS and DATA fields. |
| 62 | |
| 63 | == C.1) Sending a VCI command packet == |
| 64 | |
| 65 | To send a VCI command packet, the '''execLoop()''' method must use the '''send()''' method, that is a member function of |
| 66 | the '''p_vci''' port. The prototype is the following: |
| 67 | {{{ |
| 68 | void send(soclib::tlmt::vci_cmd_packet<vci_param> *cmd, // pointer to a VCI command packet |
| 69 | tlmt_core::tlmt_time time); // initiator local time |
| 70 | }}} |
| 71 | |
| 72 | The contents of a VCI command packet is defined below: |
| 73 | {{{ |
| 74 | template<typename vci_param> |
| 75 | class vci_cmd_packet |
| 76 | { |
| 77 | public: |
| 78 | typename vci_param::cmd_t cmd; // VCI transaction type |
| 79 | typename vci_param::addr_t address; // address on the target side |
| 80 | uint32_t int be; // byte_enable value (same value for all packet words) |
| 81 | bool contig; // contiguous addresses (when true) |
| 82 | typename vci_param::data_t *buf; // pointer to the local buffer on the initiator |
| 83 | size_t nwords; // number of words in the packet |
| 84 | uint32_t srcid; // VCI Source ID |
| 85 | uint32_t trdid; // VCI Thread ID |
| 86 | uint32_t pktid; // VCI Packet ID |
| 87 | }; |
| 88 | }}} |
| 89 | |
| 90 | The possible values for the '''cmd''' filed are `vci_param::CMD_READ`, `vci_param::CMD_WRITE`, `vci_param::CMD_READ_LOCKED`, |
| 91 | and `vci_param::CMD_STORE_COND`. |
| 92 | The '''contig''' field can be used for optimization. |
| 93 | |
| 94 | The '''send()''' function is non-blocking. |
| 95 | To implement a blocking transaction (such as a cache line read, where the processor is ''frozen'' during the VCI transaction), |
| 96 | the model designer must use the SystemC '''sc_core::wait(x)''' primitive ('''x''' being of type '''sc_core::sc_event'''): |
| 97 | the '''execLoop()''' thread is then suspended, and will be reactivated when the response packet is actually received. |
| 98 | |
| 99 | == C.2) Receiving a VCI response packet == |
| 100 | |
| 101 | To receive a VCI response packet, a call-back function must be defined as a member function of the |
| 102 | class '''!VciSimpleInitiator'''. This call-back function (named '''rspReceived()''' in the example), must be |
| 103 | declared in the '''!VciSimpleInitiator''' class and |
| 104 | is executed each time a VCI response packet is received on the '''p_vci''' port. The function name is not |
| 105 | constrained, but the arguments must respect the following prototype: |
| 106 | {{{ |
| 107 | tlmt_core::tlmt_return &rspReceived(soclib::tlmt::vci_rsp_packet<vci_param> *pkt, |
| 108 | const tlmt_core::tlmt_time &time, |
| 109 | void *private_data); |
| 110 | }}} |
| 111 | |
| 112 | The contents of a VCI response packet is defined below: |
| 113 | {{{ |
| 114 | template<typename vci_param> |
| 115 | class vci_rsp_packet |
| 116 | { |
| 117 | public: |
| 118 | typename vci_param::cmd_t cmd; // VCI transaction type |
| 119 | size_t nwords; // number of words in the packet |
| 120 | uint32_t error; // error code (0 if no error) |
| 121 | uint32_t srcid; // VCI Source ID |
| 122 | uint32_t trdid; // VCI Thread ID |
| 123 | uint32_t pktid; // VCI Packet ID |
| 124 | }; |
| 125 | }}} |
| 126 | |
| 127 | The second parameter, '''time''', is of type '''tlmt_core::tlmt_time''' and corresponds to the time of the response. The third parameter, |
| 128 | '''private_data''' has a default value of '''NULL''' and is not used when transmitting or receiving VCI packets. |
| 129 | |
| 130 | |
| 131 | The actions executed by the call-back function depend on the response transaction type ('''cmd''' field), as well as |
| 132 | the '''pktid''' and '''trdid''' fields. |
| 133 | |
| 134 | In the proposed example : |
| 135 | * In case of of a blocking read , the call-back function updates the local time, and reactivates the suspended thread. |
| 136 | * In case of a non-blocking write, the call-back function does nothing. |
| 137 | |
| 138 | == C.3) Initiator Constructor == |
| 139 | |
| 140 | The constructor of the class '''!VciSimpleInitiator''' must initialize all the member variables, including |
| 141 | the '''p_vci''' port. The '''rspReceived()''' call-back function being executed in the context of the thread sending |
| 142 | the response packet, a link between the '''p_vci''' port and the call-back function must be established. |
| 143 | Moreover, the '''p_vci''' port must contain a pointer to the initiator thread context '''c0'''. This allows the target to |
| 144 | get information on the initiator that actually sends VCI packets (the initiator local time, the initiator activity, etc). |
| 145 | |
| 146 | The '''!VciInitiator''' constructor for the '''p_vci_''' object must be called with the following arguments: |
| 147 | {{{ |
| 148 | p_vci("vci", new tlmt_core::tlmt_callback<VciSimpleInitiator,soclib::tlmt::vci_rsp_packet<vci_param> *>( |
| 149 | this, &VciSimpleInitiator<vci_param>::rspReceived), &c0) |
| 150 | }}} |
| 151 | |
| 152 | == C.4) Lookahead parameter == |
| 153 | |
| 154 | The SystemC simulation engine behaves as a cooperative, non-preemptive multi-tasks system. Any thread in the system must stop execution |
| 155 | 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 |
| 156 | it does not execute blocking communication (such as a processor that has all code and data in the L1 caches). |
| 157 | |
| 158 | To solve this issue, it is necessary to define -for each initiator module- a '''lookahead''' parameter. This parameter defines the maximum |
| 159 | number of cycles that can be executed by the thread before it is automatically stopped. The '''lookahead''' parameter allows the system designer |
| 160 | to bound the de-synchronization time interval between threads. |
| 161 | |
| 162 | A small value for this parameter results in a better timing accuracy for the simulation, but implies a larger number of context switches, |
| 163 | and a slower simulation speed. |
| 164 | |
| 165 | == C.4) VCI initiator example == |
| 166 | |
| 167 | {{{ |
| 168 | |
| 169 | ////////////////////////// vci_simple_initiator.h //////////////////////////////// |
| 170 | |
| 171 | #ifndef VCI_SIMPLE_INITIATOR_H |
| 172 | #define VCI_SIMPLE_INITIATOR_H |
| 173 | |
| 174 | #include <tlmt> |
| 175 | #include "tlmt_base_module.h" |
| 176 | #include "vci_ports.h" |
| 177 | |
| 178 | namespace soclib { namespace tlmt { |
| 179 | |
| 180 | template<typename vci_param> |
| 181 | class VciSimpleInitiator |
| 182 | : public soclib::tlmt::BaseModule |
| 183 | { |
| 184 | tlmt_core::tlmt_thread_context c0; |
| 185 | sc_core::sc_event m_rsp_received; |
| 186 | tlmt_core::tlmt_return m_return; |
| 187 | soclib::tlmt::vci_cmd_packet<vci_param> cmd; |
| 188 | |
| 189 | uint32_t addresses[8]; // address table, |
| 190 | uint32_t localbuf[8]; // local buffer |
| 191 | |
| 192 | uint32_t m_counter; // iteration counter |
| 193 | uint32_t m_lookahead; // lookahead value |
| 194 | uint32_t m_index; // initiator index |
| 195 | |
| 196 | protected: |
| 197 | SC_HAS_PROCESS(VciSimpleInitiator); |
| 198 | |
| 199 | public: |
| 200 | soclib::tlmt::VciInitiator<vci_param> p_vci; |
| 201 | |
| 202 | VciSimpleInitiator( sc_core::sc_module_name name, |
| 203 | uint32_t initiator index, |
| 204 | uint32_t lookahead ); |
| 205 | |
| 206 | tlmt_core::tlmt_return &rspReceived(soclib::tlmt::vci_rsp_packet<vci_param> *pkt, |
| 207 | const tlmt_core::tlmt_time &time, |
| 208 | void *private_data); |
| 209 | void behavior(); |
| 210 | }; |
| 211 | |
| 212 | }} |
| 213 | |
| 214 | #endif |
| 215 | |
| 216 | ////////////////////////// vci_simple_initiator.cpp //////////////////////////////// |
| 217 | |
| 218 | #include "../include/vci_simple_initiator.h" |
| 219 | |
| 220 | namespace soclib { namespace tlmt { |
| 221 | |
| 222 | #define tmpl(x) template<typename vci_param> x VciSimpleInitiator<vci_param> |
| 223 | |
| 224 | tmpl(tlmt_core::tlmt_return&)::rspReceived(soclib::tlmt::vci_rsp_packet<vci_param> *pkt, |
| 225 | const tlmt_core::tlmt_time &time, |
| 226 | void *private_data) |
| 227 | { |
| 228 | if(pkt->cmd == vci_param::VCI_CMD_READ) { |
| 229 | c0.set_time(time + tlmt_core::tlmt_time(pkt->length)); |
| 230 | m_rsp_received.notify(sc_core::SC_ZERO_TIME) ; |
| 231 | } |
| 232 | return m_return; |
| 233 | } |
| 234 | |
| 235 | tmpl(void)::behavior() |
| 236 | { |
| 237 | |
| 238 | for(;;) { |
| 239 | |
| 240 | // sending a read VCI packet |
| 241 | |
| 242 | cmd.cmd = vci_param::CMD_READ; // a VCI read packet |
| 243 | addresses[0] = 0xBFC00000; // the start address |
| 244 | cmd.address = addresses; // assigned |
| 245 | cmd.be = 0xF; // reading full words |
| 246 | cmd.contig = true; // at successive addresses |
| 247 | cmd.buf = localbuf; // storing the read results in localbuf |
| 248 | cmd.length = 8; // packet of 8 words |
| 249 | cmd.srcid = 0; // srcid=0 |
| 250 | cmd.trdid = 0; // trdid=0 |
| 251 | cmd.pktid = 0; // pktid=0 |
| 252 | |
| 253 | tlmt_core::tlmt_return ret; // in case a test on the send function is needed |
| 254 | ret = p_vci.send(&cmd, c0.time()); // sending the packet |
| 255 | sc_core::wait(m_rsp_received); // and waiting for the response packet |
| 256 | |
| 257 | // sending a write VCI packet |
| 258 | |
| 259 | localbuf[0]=0x00112233; // first, fill the write local buffer with write data |
| 260 | |
| 261 | cmd.cmd = vci_param::CMD_WRITE; // then issue the VCI write packet |
| 262 | addresses[0] = 0x10000000; // starting with this address |
| 263 | cmd.address = addresses; |
| 264 | cmd.be = 0xF; |
| 265 | cmd.contig = 0; |
| 266 | cmd.buf = localbuf; |
| 267 | cmd.length = 1; |
| 268 | cmd.srcid = 0; |
| 269 | cmd.trdid = 0; |
| 270 | cmd.pktid = 0; |
| 271 | |
| 272 | ret = p_vci.send(&cmd, c0.time()); // Don't wait for the answer |
| 273 | |
| 274 | // lookahead management |
| 275 | m_counter++ ; |
| 276 | if (m_counter >= m_lookahead) { |
| 277 | m_counter = 0 ; |
| 278 | sc_core::wait(sc_core::SC_ZERO_TIME) ; |
| 279 | } // end if |
| 280 | |
| 281 | // process time= process time+1 |
| 282 | c0.set_time(c0.time()+tlmt_core::tlmt_time(1)) ; |
| 283 | |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | tmpl(/**/)::VciSimpleInitiator( sc_core::sc_module_name name, |
| 288 | uint32_t initiator index, |
| 289 | uint32_t lookahead) |
| 290 | : soclib::tlmt::BaseModule(name), |
| 291 | m_index(initiator_index), |
| 292 | m_lookahead(lookahead), |
| 293 | m_counter(0), |
| 294 | p_vci("vci", new tlmt_core::tlmt_callback<VciSimpleInitiator,soclib::tlmt::vci_rsp_packet<vci_param> *>( |
| 295 | this, &VciSimpleInitiator<vci_param>::rspReceived), &c0) |
| 296 | { |
| 297 | SC_THREAD(behavior); |
| 298 | } |
| 299 | |
| 300 | }} |
| 301 | |
| 302 | }}} |
| 303 | |
| 304 | = D) VCI target modeling = |
| 305 | |
| 306 | In the proposed example, the target handles two types of command: a read burst of 8 words, and a write burst of 8 words. |
| 307 | To simplify the model, there is no error management. |
| 308 | |
| 309 | The class '''!VciSimpleTarget''' inherits from the class '''!BaseModule'''. The class '''!VciSimpleTarget''' contains a member |
| 310 | variable '''p_vci''' of type '''!VciTarget'''. This object has a template parameter '''<vci_param>''' defining the widths of |
| 311 | the VCI ADDRESS and DATA fields. |
| 312 | |
| 313 | == D.1) Receiving a VCI command packet == |
| 314 | |
| 315 | To receive a VCI command packet, a call-back function must be defined as a member function of the class '''!VciSimpleTarget'''. |
| 316 | This call-back function (named '''cmdReceived()''' in the example), will be executed each time a VCI command packet is received on |
| 317 | the '''p_vci''' port. The function name is not constrained, but the arguments must respect the following prototype: |
| 318 | {{{ |
| 319 | tlmt_core::tlmt_return &cmdReceived(soclib::tlmt::vci_cmd_packet<vci_param> *pkt, |
| 320 | const tlmt_core::tlmt_time &time, |
| 321 | void *private_data); |
| 322 | }}} |
| 323 | For the read and write transactions, the actual data transfer is performed by this '''cmdReceived()''' function. |
| 324 | To avoid multiple data copies, only the pointer on the initiator data buffer is transported in the VCI command packet |
| 325 | (source buffer for a write transaction, or destination buffer for a read transaction). |
| 326 | |
| 327 | == D.2) Sending a VCI response packet == |
| 328 | |
| 329 | To send a VCI response packet the '''cmdReceived()''' function must use the '''send()''' method, that is a member function of |
| 330 | the class '''!VciTarget''', and has the following prototype: |
| 331 | {{{ |
| 332 | void send(soclib::tlmt::vci_rsp_packet<vci_param> *rsp, // pointer to a VCI response packet |
| 333 | tlmt_core::tlmt_time time); // initiator local time |
| 334 | }}} |
| 335 | For a reactive target, the response packet time is computed as the command packet time plus the target intrinsic latency. |
| 336 | |
| 337 | == D.3) Target Constructor == |
| 338 | |
| 339 | The constructor of the class '''!VciSimpleTarget''' must initialize all the member variables, including |
| 340 | the '''p_vci''' port. The '''cmdReceived()''' call-back function being executed in the context of the thread sending the command packet, |
| 341 | a link between the '''p_vci''' port and the call-back function must be established. |
| 342 | The '''!VciTarget''' constructor must be called with the following arguments: |
| 343 | {{{ |
| 344 | p_vci("vci", new tlmt_core::tlmt_callback<VciSimpleTarget,soclib::tlmt::vci_cmd_packet<vci_param> *>( |
| 345 | this, &VciSimpleTarget<vci_param>::cmdReceived)) |
| 346 | }}} |
| 347 | |
| 348 | == D.4) VCI target example == |
| 349 | {{{ |
| 350 | |
| 351 | ////////////////////////// vci_simple_target.h //////////////////////////////// |
| 352 | |
| 353 | #ifndef VCI_SIMPLE_TARGET_H |
| 354 | #define VCI_SIMPLE_TARGET_H |
| 355 | |
| 356 | #include <tlmt> |
| 357 | #include "tlmt_base_module.h" |
| 358 | #include "vci_ports.h" |
| 359 | |
| 360 | namespace soclib { namespace tlmt { |
| 361 | |
| 362 | template<typename vci_param> |
| 363 | class VciSimpleTarget |
| 364 | : public soclib::tlmt::BaseModule |
| 365 | { |
| 366 | private: |
| 367 | tlmt_core::tlmt_return m_return; |
| 368 | uint32_t m_index; |
| 369 | uint32_t m_latency; |
| 370 | soclib::tlmt::vci_rsp_packet<vci_param> rsp; |
| 371 | |
| 372 | public: |
| 373 | soclib::tlmt::VciTarget<vci_param> p_vci; |
| 374 | |
| 375 | VciSimpleTarget(sc_core::sc_module_name name, |
| 376 | uint32_t targetIndex, |
| 377 | uint32_t latency); |
| 378 | |
| 379 | tlmt_core::tlmt_return &cmdReceived(soclib::tlmt::vci_cmd_packet<vci_param> *pkt, |
| 380 | const tlmt_core::tlmt_time &time, |
| 381 | void *private_data); |
| 382 | }; |
| 383 | |
| 384 | }} |
| 385 | |
| 386 | #endif |
| 387 | |
| 388 | ////////////////////////// vci_simple_target.cpp //////////////////////////////// |
| 389 | |
| 390 | #include "../include/vci_simple_target.h" |
| 391 | |
| 392 | namespace soclib { namespace tlmt { |
| 393 | |
| 394 | #define tmpl(x) template<typename vci_param> x VciSimpleTarget<vci_param> |
| 395 | |
| 396 | tmpl(tlmt_core::tlmt_return&)::cmdReceived(soclib::tlmt::vci_cmd_packet<vci_param> *pkt, |
| 397 | const tlmt_core::tlmt_time &time, |
| 398 | void *private_data) |
| 399 | { |
| 400 | uint32_t m_data[128]; |
| 401 | |
| 402 | if(pkt->cmd == vci_param::VCI_CMD_WRITE) { |
| 403 | for(int i = 0 ; i < pkt->length ; i++) |
| 404 | m_data[i] = cmd->buf[i]; |
| 405 | } |
| 406 | if(pkt->cmd == vci_param::VCI_CMD_READ) { |
| 407 | for(int i = 0 ; i < pkt->length ; i++) |
| 408 | cmd->buf[i] = m_data[i]; |
| 409 | } |
| 410 | rsp.srcid = pkt->srcid; |
| 411 | rsp.trdid = pkt->trdid; |
| 412 | rsp.pktid = pkt->pktid; |
| 413 | rsp.length = pkt->length; |
| 414 | rsp.error = 0; |
| 415 | p_vci.send(&m_rsp, time+tlmt_core::tlmt_time(latency+pkt->length)) ; |
| 416 | m_return.time=time+tlmt_core::tlmt_time(latency+pkt->length; |
| 417 | return (m_return); |
| 418 | } |
| 419 | |
| 420 | tmpl(/**/)::VciSimpleTarget(sc_core::sc_module_name name, |
| 421 | uint32_t targetIndex, |
| 422 | uint32_t latency) |
| 423 | : soclib::tlmt::BaseModule(name), |
| 424 | m_index(targetIndex), |
| 425 | m_latency(latency), |
| 426 | p_vci("vci", new tlmt_core::tlmt_callback<VciSimpleTarget,soclib::tlmt::vci_cmd_packet<vci_param> *>( |
| 427 | this, &VciSimpleTarget<vci_param>::cmdReceived)) |
| 428 | { |
| 429 | } |
| 430 | |
| 431 | }} |
| 432 | |
| 433 | }}} |
| 434 | |
| 435 | = E) VCI Interconnect = |
| 436 | |
| 437 | The VCI interconnect used for the TLM-T simulation is a generic simulation model, named '''!VciVgmn'''. |
| 438 | The two main parameters are the number of initiators, and the number of targets. In TLM-T simulation, |
| 439 | we don't want to reproduce the cycle-accurate behavior of a particular interconnect. We only want to simulate the contention in |
| 440 | the network, when several VCI intitiators try to reach the same VCI target. |
| 441 | Therefore, the network is actually modeled as a complete cross-bar : In a physical network such as the multi-stage network described |
| 442 | in Figure 2.a, conflicts can appear at any intermediate switch. In the '''!VciVgmn''' network described in Figure 2.b, conflicts can |
| 443 | only happen at the output ports. It is possible to specify a specific latency for each input/output couple. As in most physical |
| 444 | interconnects, the general arbitration policy is round-robin. |
| 445 | |
| 446 | [[Image(tlmt_figure_2.png, nolink)]] |
| 447 | |
| 448 | == E.1) Generic network modeling == |
| 449 | |
| 450 | There is actually two fully independent networks for VCI command packets and VCI response packets. There is a routing function for each input |
| 451 | port, and an arbitration function for each output port, but the two networks are not symmetrical : |
| 452 | * For the command network, the arbitration policy is distributed: there is one arbitration thread for each output port |
| 453 | (i.e. one arbitration thread for each VCI target). Each arbitration thread is modeled by a SC_THREAD, and contains a local clock. |
| 454 | * For the response network, there are no conflicts, and there is no need for arbitration. Therefore, there is no thread |
| 455 | (and no local time) and the response network is implemented by simple function calls. |
| 456 | |
| 457 | This is illustrated in Figure 3 for a network with 2 initiators and three targets : |
| 458 | |
| 459 | [[Image(tlmt_figure_3.png, nolink)]] |
| 460 | |
| 461 | == E.2) VCI initiators and targets synchronizations == |
| 462 | |
| 463 | 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 |
| 464 | 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 |
| 465 | module describing a VCI target. For a VCI target, the local clock is actually the clock associated to the corresponding arbitration |
| 466 | thread contained in the '''!VciVgmn''' module. |
| 467 | |
| 468 | As described in Figure 4, when a VCI command packet - sent by the corresponding arbitration thread - is received by a VCI target, |
| 469 | two synchronization mechanisms are activated : |
| 470 | * The '''cmdReceived()''' function sends a VCI response packet with an associated time to the source initiator, through |
| 471 | the '''!VciVgmn''' response network. The corresponding time can be used to update the initiator local clock. |
| 472 | * The '''cmdReceived()''' function returns a time to the arbitration thread. This time is used to update the arbitration thread local |
| 473 | clock. |
| 474 | |
| 475 | [[Image(tlmt_figure_4.png, nolink)]] |
| 476 | |
| 477 | = F) Interrupt modeling = |
| 478 | |
| 479 | Interrupts are asynchronous events that are not carried by the VCI network. |
| 480 | |
| 481 | 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 |
| 482 | type '''tlmt_core::tlmt_out<bool>''' and '''tlmt_core::tlmt_in<bool>''' that must be declared as member variables of the source and |
| 483 | destination modules respectively. |
| 484 | |
| 485 | [[Image(tlmt_figure_5.png, nolink)]] |
| 486 | |
| 487 | == F.1) Source modeling == |
| 488 | |
| 489 | The source module (named '''!VciSimpleSourceInterrupt''' in this example) must contain a member variable '''p_irq''' of |
| 490 | type '''tlmt_core::tlmt_out<bool>'''. To activate, or desactivate an interruption, the source module must use the '''send()''' method, |
| 491 | that is a member function of the '''tlmt_core::tlmt_out<bool>''' class. These interrupt packets transport both a Boolean, |
| 492 | and a time. The '''send()''' prototype is defined as follows : |
| 493 | {{{ |
| 494 | void send( bool val, const tlmt_core::tlmt_time &time) |
| 495 | }}} |
| 496 | |
| 497 | == F.2) Destination modeling == |
| 498 | |
| 499 | The destination module (named here '''!VciProcessor''') must contain a member variable '''p_irq''' of type |
| 500 | '''tlmt_core::tlmt_in<bool>''', and a call-back function (named here '''irqReceived()''' that is executed when an interrupt |
| 501 | packet is received on the '''p_irq''' port. |
| 502 | |
| 503 | A link between the '''p_irq''' port and the call-back function must be established by the port constructor in the constructor of |
| 504 | the class '''VciProcessor''' : |
| 505 | {{{ |
| 506 | tlmt_core::tlmt_callback < VciProcessor,bool >(this, &VciProcessor < iss_t, |
| 507 | vci_param >::irqReceived); |
| 508 | }}} |
| 509 | |
| 510 | In the Parallel Discrete Event Simulation, the pessimistic approach relies on the fact that any PDES process is not allowed to update |
| 511 | his local time until it has received messages on all its input ports with times greater than its local time. |
| 512 | |
| 513 | Therefore, a SC_THREAD modeling the behavior of a processor containing an '''tlmt_core::tlmt_in<bool>''' should in principle |
| 514 | wait a timestamped packet on its interrupt port before executing instructions. However, such a behavior would be very inefficient, |
| 515 | and is prone to dead-lock situations. |
| 516 | |
| 517 | The recommended policy for handling interrupts is the following: |
| 518 | * The call-back function '''irqReceived()''' sets the member variables '''m_irqpending''' and '''m_irqtime''', when a interrupt packet |
| 519 | is received on the '''p_irq''' port. |
| 520 | * The '''execLoop()''' thread must test the '''m_irqpending''' variable at each cycle (i.e. in each iteration of |
| 521 | the infinite loop). |
| 522 | * If there is no interrupt pending, the thread continues its execution. If an interrupt is pending, and the interrupt time is greater |
| 523 | than the local time, the thread continues execution. If the interrupt time is equal or smaller than the local time, the interrupt |
| 524 | is handled. |
| 525 | |
| 526 | Such a violation of the the pessimistic parallel simulation principles creates a loss of accuracy on the interrupt handling |
| 527 | timestamp. This loss of accuracy in the TLM-T simulation is acceptable, as interrupts are asynchronous events, and the timing error |
| 528 | is bounded by the '''m_lookahead''' parameter. |
| 529 | |
| 530 | == F.3) Processor with interrupt example == |
| 531 | |
| 532 | {{{ |
| 533 | |
| 534 | ////////////////////////// vci_processor.h //////////////////////////////// |
| 535 | |
| 536 | namespace soclib { namespace tlmt { |
| 537 | |
| 538 | template<typename iss_t,typename vci_param> |
| 539 | class VciProcessor |
| 540 | : public soclib::tlmt::BaseModule |
| 541 | { |
| 542 | tlmt_core::tlmt_thread_context c0; |
| 543 | sc_core::sc_event m_rsp_received; |
| 544 | tlmt_core::tlmt_return m_return; |
| 545 | bool m_irqpendig; // pending interrupt request |
| 546 | tlmt_core::tlmt_time m_irqtime; // irq date |
| 547 | uint32_t m_counter; // iteration counter |
| 548 | uint32_t m_lookahead; // lookahead value |
| 549 | |
| 550 | protected: |
| 551 | SC_HAS_PROCESS(VciProcessor); |
| 552 | |
| 553 | public: |
| 554 | soclib::tlmt::VciInitiator<vci_param> p_vci; |
| 555 | tlmt_core::tlmt_in<bool> p_irq; |
| 556 | |
| 557 | VciProcessor( sc_core::sc_module_name name, int id ); |
| 558 | |
| 559 | tlmt_core::tlmt_return &rspReceived(soclib::tlmt::vci_rsp_packet<vci_param> *pkt, |
| 560 | const tlmt_core::tlmt_time &time, |
| 561 | void *private_data); |
| 562 | tlmt_core::tlmt_return &irqReceived(bool, |
| 563 | const tlmt_core::tlmt_time &time, |
| 564 | void *private_data); |
| 565 | void execLoop(); |
| 566 | }; |
| 567 | |
| 568 | }} |
| 569 | |
| 570 | ////////////////////////// vci_processor.cpp //////////////////////////////// |
| 571 | |
| 572 | #include "../include/vci_processor.h" |
| 573 | |
| 574 | namespace soclib |
| 575 | { |
| 576 | namespace tlmt |
| 577 | { |
| 578 | |
| 579 | #define tmpl(x) template<typename iss_t, typename vci_param> x VciProcessor<vci_param> |
| 580 | |
| 581 | tmpl (tlmt_core::tlmt_return &)::rspReceived (soclib::tlmt:: vci_rsp_packet < vci_param > *pkt, |
| 582 | const tlmt_core:: tlmt_time & time, void *private_data) |
| 583 | { |
| 584 | if (pkt->cmd == vci_param::CMD_WRITE) |
| 585 | m_write_error = (pkt->error != 0); |
| 586 | else |
| 587 | m_write_error = false; |
| 588 | if (pkt->cmd == vci_param::CMD_READ) |
| 589 | m_read_error = (pkt->error != 0); |
| 590 | else |
| 591 | m_read_error = false; |
| 592 | m_rsptime = time + tlmt_core::tlmt_time (pkt->length); |
| 593 | m_vci_pending = false; |
| 594 | m_rsp_received.notify (sc_core::SC_ZERO_TIME); |
| 595 | |
| 596 | return m_return; |
| 597 | } |
| 598 | |
| 599 | tmpl (tlmt_core::tlmt_return &)::irqReceived (bool v, const tlmt_core:: |
| 600 | tlmt_time & time, void *private_data) |
| 601 | { |
| 602 | m_irqpending = val; |
| 603 | m_irqtime = time; |
| 604 | |
| 605 | return m_return; |
| 606 | } |
| 607 | |
| 608 | |
| 609 | tmpl (void)::execLoop () |
| 610 | { |
| 611 | while(1) { |
| 612 | ... |
| 613 | // test interrupts |
| 614 | if (m_irqpending && (m_irqtime <= c0.time())) |
| 615 | { |
| 616 | // interrupt handling |
| 617 | } |
| 618 | ... |
| 619 | // lookahead management |
| 620 | m_counter++ ; |
| 621 | if (m_counter >= m_lookahead) { |
| 622 | m_counter = 0 ; |
| 623 | sc_core::wait(sc_core::SC_ZERO_TIME) ; |
| 624 | } // end if |
| 625 | c0.set_time(c0.time()+tlmt_core::tlmt_time(1)) ; |
| 626 | } // end while |
| 627 | } |
| 628 | |
| 629 | tmpl ( /**/)::VciProcessor( |
| 630 | sc_core::sc_module_name name, |
| 631 | int id ) |
| 632 | : soclib::tlmt::BaseModule (name), |
| 633 | m_counter (0), |
| 634 | m_lookahead (10), |
| 635 | p_vci("vci",new tlmt_core::tlmt_callback<VciProcessor,vci_rsp_packet<vci_param>*>( |
| 636 | this,&VciProcessor<vci_param>::rspReceived),&c0), |
| 637 | p_irq("irq",new tlmt_core::tlmt_callback<VciProcesspr,bool>( |
| 638 | this,&VciProcessor<vci_param>::irqReceived)) |
| 639 | { |
| 640 | SC_THREAD (execLoop); |
| 641 | } |
| 642 | |
| 643 | }} |
| 644 | |
| 645 | }}} |
| 646 | |