| | 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 | |