29 | | In a VCI-based architecture, all initiators must be indexed by an index. Targets are indexes by their assigned address space. For simplification purposes, we'll also assign an index to the targets. Index space for targets is different from index space for initiators. |
30 | | |
31 | | The target index is used in the routing table implemented by the interconnect components in order to choose destination port for command packet. |
32 | | The initiator index is used by the interconnect components to route the response packets. |
33 | | |
34 | | For the initiators, the index corresponds to the VCI SRCID value. |
35 | | |
36 | | This index can be a simple scalar index, in case of a ''flat'' interconnect, or it can be a composite index in case a hierarchical two level interconnect where each component is identified by two indexes : (cluster_index, local_index). |
| 40 | In a VCI-based architecture, all initiators must be indexed. Targets are identified by their assigned address space. However, for simplification purposes, we'll also give an index to the targets. Index space for targets is different from index space for initiators. |
| 41 | * The target index is the connection port number on interconnects, this index will be decoded from target address in VCI cmd packet. |
| 42 | * The initiator index is used by the interconnect components to route the response packets. |
| 43 | |
| 44 | For the initiators, the index is the VCI SRCID value. |
| 45 | |
| 46 | Indexes can be |
| 47 | * a simple scalar index, in case of a ''flat'' interconnect |
| 48 | * a composite index in case a hierarchical two level interconnect where each component is identified by two scalars: (cluster_index, local_index). |
| 49 | |
| 50 | [source:trunk/soclib/systemc/include/common/int_tab.h common/int_tab.h] defines an utility class storing list of indexes. All indexes are `IntTab`s. |
137 | | The first constructor argument must be the instance name. Other arguments can be identifiers (such as a target VCI index or initiator VCI index). The constructor must configure some hardware ressources, such as the address decoding ROM that exists in any VCI interconnect. An argument frequently used is a reference on the Soclib segment table, that defines the segmentation of the system address space. |
138 | | |
139 | | Moreover, the constructor must define the sensitivity list of the Transition,(), genMoore() and genMealy() methods, that are described below. The sensitivity list of the Transition () method contains only the clock rising edge. The sensitivity list of the genMoore()n method contains only the clock falling edge. The sensitivity list of the genMealy() method contains the clock falling edge, as well as all input ports thare in the support of the Mealy generation function. |
140 | | |
141 | | Be careful : the constructor should NOT initialize the registers. The register initialisation must be an hardware mechanism explicitely described in the Transition function on reset condition. |
142 | | |
143 | | == Transition() method == |
144 | | |
145 | | For each hardware component, there is only one `Transition()` method. It is called once per cycle, as the sensitivity list contains only the clock rising edge. This method computes the next values of the registers (variables that have the sc_signal type). No output port can be assigned in this method. Each register should be assigned only once. |
| 182 | The first constructor argument must be the instance name. Other arguments can be identifiers |
| 183 | (such as a target VCI index or initiator VCI index). The constructor must configure some hardware |
| 184 | ressources, such as the address decoding ROM that exists in any VCI interconnect. An argument frequently |
| 185 | used is a reference on the [source:trunk/soclib/systemc/include/common/mapping_table.h soclib::common::MappingTable], |
| 186 | that defines the segmentation of the system address space. |
| 187 | |
| 188 | Moreover, the constructor must define the sensitivity list of the Transition(), genMoore() and genMealy() |
| 189 | methods, that are described below. |
| 190 | * sensitivity list of the transition() method contains only the clock rising edge. |
| 191 | * sensitivity list of the genMoore() method contains only the clock falling edge. |
| 192 | * sensitivity list of the genMealy() method contains the clock falling edge, as well as all input ports thare in the support of the Mealy generation function. |
| 193 | |
| 194 | Be careful: the constructor should NOT initialize the registers. |
| 195 | The register initialization must be an hardware mechanism explicitly described in the Transition function on reset condition. |
| 196 | |
| 197 | == transition() method == |
| 198 | |
| 199 | For each hardware component, there is only one `Transition()` method. It is called |
| 200 | once per cycle, as the sensitivity list contains only the clock rising edge. This method |
| 201 | computes the next values of the registers (variables that have the `sc_signal` type). |
| 202 | |
| 203 | No output port can be assigned in this method. Each register should be assigned only once. |
154 | | For each hardware component, there is zero, one or several `genMealy()` methods (one method for each output port). These methods can be called several times per cycle. The sensitivity list can contain several input ports. This method computes the Mealy values of the ouput ports, using only the register values and the input ports values. |
155 | | No register can be assigned in this method. Each output port can be assigned only once. This method can use automatic variables. It can be missing if there is no Mealy output. |
156 | | |
| 216 | For each hardware component, there is zero, one or several `genMealy()` methods (one method |
| 217 | for each output port). These methods can be called several times per cycle. The sensitivity |
| 218 | list can contain several input ports. This method computes the Mealy values of the ouput ports, |
| 219 | using only the register values and the input ports values. |
| 220 | |
| 221 | No register can be assigned in this method. Each output port can be assigned only once. |
| 222 | This method can use automatic variables. It can be missing if there is no Mealy output. |
| 223 | |
| 224 | = Complete example file = |
| 225 | |
| 226 | Let's take the [source:trunk/soclib/systemc/include/caba/target/vci_locks.h soclib::caba::VciLocks] |
| 227 | component definition and comment it. |
| 228 | |
| 229 | {{{ |
| 230 | #include <systemc.h> |
| 231 | #include "caba/util/base_module.h" |
| 232 | #include "caba/interface/vci_target.h" |
| 233 | #include "common/mapping_table.h" |
| 234 | |
| 235 | // Have this component in the soclib::caba namespace |
| 236 | namespace soclib { namespace caba { |
| 237 | |
| 238 | // Here we pass the VCI parameters as a template argument. This is intended because VCI parameters |
| 239 | // change data type widths, therefore change some compile-time intrinsics |
| 240 | template<typename vci_param> |
| 241 | class VciLocks |
| 242 | : public soclib::caba::BaseModule |
| 243 | { |
| 244 | |
| 245 | // We have only one FSM in this component. It handles the |
| 246 | // VCI target port. The states are: |
| 247 | enum vci_target_fsm_state_e { |
| 248 | IDLE, |
| 249 | WRITE_RSP, |
| 250 | READ_RSP, |
| 251 | ERROR_RSP, |
| 252 | }; |
| 253 | |
| 254 | // The register holding the FSM state: |
| 255 | sc_signal<int> r_vci_fsm; |
| 256 | |
| 257 | // While we are handling a command, we save the VCI out-of-order markers in order to |
| 258 | // send them back in response |
| 259 | sc_signal<typename vci_param::srcid_t> r_buf_srcid; |
| 260 | sc_signal<typename vci_param::trdid_t> r_buf_trdid; |
| 261 | sc_signal<typename vci_param::pktid_t> r_buf_pktid; |
| 262 | sc_signal<typename vci_param::eop_t> r_buf_eop; |
| 263 | |
| 264 | // Do we have a command to handle ? |
| 265 | sc_signal<bool> r_buf_value; |
| 266 | |
| 267 | // Our assigned segment |
| 268 | soclib::common::Segment m_segment; |
| 269 | |
| 270 | // The actual data handled by this component: the locks |
| 271 | bool *m_contents; |
| 272 | |
| 273 | protected: |
| 274 | // Mandatory SystemC construct |
| 275 | SC_HAS_PROCESS(VciLocks); |
| 276 | |
| 277 | public: |
| 278 | // The ports |
| 279 | sc_in<bool> p_resetn; |
| 280 | sc_in<bool> p_clk; |
| 281 | soclib::caba::VciTarget<vci_param> p_vci; |
| 282 | |
| 283 | // Constructor & descructor, explained above |
| 284 | VciLocks( |
| 285 | sc_module_name insname, |
| 286 | const soclib::common::IntTab &index, |
| 287 | const soclib::common::MappingTable &mt); |
| 288 | ~VciLocks(); |
| 289 | |
| 290 | private: |
| 291 | // The FSM functions |
| 292 | void transition(); |
| 293 | void genMoore(); |
| 294 | }; |
| 295 | |
| 296 | // Namespace closing |
| 297 | }} |
| 298 | }}} |
| 299 | |
| 300 | And the [source:trunk/soclib/systemc/src/caba/target/vci_locks.cc implementation]: |
| 301 | |
| 302 | {{{ |
| 303 | #include "caba/target/vci_locks.h" |
| 304 | |
| 305 | // Namespace, again |
| 306 | namespace soclib { namespace caba { |
| 307 | |
| 308 | // This macro is an helper function to factor out the template parameters |
| 309 | // This is useful in two ways: |
| 310 | // * It makes the syntax clearer |
| 311 | // * It makes template parameters changes easier (only one line to change them all) |
| 312 | // x is the method's return value |
| 313 | #define tmpl(x) template<typename vci_param> x VciLocks<vci_param> |
| 314 | |
| 315 | // The /**/ is a hack to pass no argument to a macro taking one. (constructor has no |
| 316 | // return value in C++) |
| 317 | tmpl(/**/)::VciLocks( |
| 318 | sc_module_name insname, |
| 319 | const soclib::common::IntTab &index, |
| 320 | const soclib::common::MappingTable &mt) |
| 321 | // This is the C++ construct for parent construction and |
| 322 | // member variables initialization. |
| 323 | // We initialize the BaseModule with the component's name |
| 324 | : soclib::caba::BaseModule(insname), |
| 325 | // and get the segment from the mapping table and our index |
| 326 | m_segment(mt.getSegment(index)) |
| 327 | { |
| 328 | |
| 329 | // There is one lock every 32-bit word in memory. We |
| 330 | // allocate an array for the locks, one every 4 addressable |
| 331 | // byte. |
| 332 | m_contents = new bool[m_segment.size()/4]; |
| 333 | |
| 334 | // Sensitivity list for transition() and genMoore(), no genMealy() |
| 335 | // in this component |
| 336 | SC_METHOD(transition); |
| 337 | dont_initialize(); |
| 338 | sensitive << p_clk.pos(); |
| 339 | |
| 340 | SC_METHOD(genMoore); |
| 341 | dont_initialize(); |
| 342 | sensitive << p_clk.neg(); |
| 343 | } |
| 344 | |
| 345 | tmpl(/**/)::~VciLocks() |
| 346 | { |
| 347 | // Here we must delete every dynamically-allocated data... |
| 348 | delete [] m_contents; |
| 349 | } |
| 350 | |
| 351 | tmpl(void)::transition() |
| 352 | { |
| 353 | // On reset condition, we initialize everything in the component, |
| 354 | // from FSMs to internal data. |
| 355 | if (!p_resetn) { |
| 356 | for (size_t i=0; i<m_segment.size()/4; ++i) |
| 357 | m_contents[i] = false; |
| 358 | r_vci_fsm = IDLE; |
| 359 | return; |
| 360 | } |
| 361 | |
| 362 | // We are not on reset case. |
| 363 | |
| 364 | // Take the address, transform it into an index in our locks table. |
| 365 | typename vci_param::addr_t address = p_vci.address.read(); |
| 366 | uint32_t cell = (address-m_segment.baseAddress())/4; |
| 367 | |
| 368 | // Implement the VCI target FSM |
| 369 | switch (r_vci_fsm) { |
| 370 | case IDLE: |
| 371 | if ( ! p_vci.cmdval.read() ) |
| 372 | break; |
| 373 | /* |
| 374 | * We only accept 1-word request packets |
| 375 | * and we check for segmentation violations |
| 376 | */ |
| 377 | if ( ! p_vci.eop.read() || |
| 378 | ! m_segment.contains(address) ) |
| 379 | r_vci_fsm = ERROR_RSP; |
| 380 | else { |
| 381 | switch (p_vci.cmd.read()) { |
| 382 | case VCI_CMD_READ: |
| 383 | r_buf_value = m_contents[cell]; |
| 384 | m_contents[cell] = true; |
| 385 | r_vci_fsm = READ_RSP; |
| 386 | break; |
| 387 | case VCI_CMD_WRITE: |
| 388 | m_contents[cell] = false; |
| 389 | r_vci_fsm = WRITE_RSP; |
| 390 | break; |
| 391 | default: |
| 392 | r_vci_fsm = ERROR_RSP; |
| 393 | break; |
| 394 | } |
| 395 | } |
| 396 | r_buf_srcid = p_vci.srcid.read(); |
| 397 | r_buf_trdid = p_vci.trdid.read(); |
| 398 | r_buf_pktid = p_vci.pktid.read(); |
| 399 | r_buf_eop = p_vci.eop.read(); |
| 400 | break; |
| 401 | |
| 402 | // In those states, we only wait for response to be accepted. |
| 403 | case WRITE_RSP: |
| 404 | case READ_RSP: |
| 405 | case ERROR_RSP: |
| 406 | if ( p_vci.rspack.read() ) |
| 407 | r_vci_fsm = IDLE; |
| 408 | break; |
| 409 | } |
| 410 | } |
| 411 | |
| 412 | tmpl(void)::genMoore() |
| 413 | { |
| 414 | // This is an helper function defined in the VciTarget port definition |
| 415 | p_vci.rspSetIds( r_buf_srcid.read(), r_buf_trdid.read(), r_buf_pktid.read() ); |
| 416 | |
| 417 | // Depending on the state, we give back the expected response. |
| 418 | switch (r_vci_fsm) { |
| 419 | case IDLE: |
| 420 | p_vci.rspNop(); |
| 421 | break; |
| 422 | case WRITE_RSP: |
| 423 | p_vci.rspWrite( r_buf_eop.read() ); |
| 424 | break; |
| 425 | case READ_RSP: |
| 426 | p_vci.rspRead( r_buf_eop.read(), r_buf_value.read() ); |
| 427 | break; |
| 428 | case ERROR_RSP: |
| 429 | p_vci.rspError( r_buf_eop.read() ); |
| 430 | break; |
| 431 | } |
| 432 | } |
| 433 | |
| 434 | }} |
| 435 | }}} |