| ~ | | Changeset 9104:6e7b0ddcc5b5 |
| | ~ | Changeset 9126:11e228ae7ed4 |
| | |
|
| | | Port: Separate the port and the interface protocol |
| | |
|
| | | This patch splits the port and its interface into a very basic stack.
|
| | | The port itself dictates how things are connected and governs the
|
| | | physical topology of the system. On top of this, the port interfaces
|
| | | (implemented by handlers) are responsible for the transport functions,
|
| | | e.g. send/recv for functional, atomic and timing, and also for
|
| | | functions like querying of address ranges and block size. |
| | |
|
| | | Why are we doing this? This patch enables us to gradually introduce
|
| | | the 4-phase ports as an alternative interface to the classic send/recv
|
| | | interface. In theory, it would also enable Ruby to rely on the normal
|
| | | ports for structurally assembling the system, and then use a custom
|
| | | Ruby interface. If we want different protocols at different locations
|
| | | in the system it is thus possible. |
| | |
|
| | | So what does this patch actually change? Brace yourself, as it
|
| | | is quite extensive. First and foremost each port now has two template
|
| | | parameters, one for the exposed interface of the port itself (i.e. the
|
| | | sending and receiving functionality) and one for the interface
|
| | | required from the peer port (the receiving end of the port's own
|
| | | sending interface). For example, for a conventional master port, this
|
| | | would now be IfaceMasterPort<MasterInterface, SlaveInterface>, where
|
| | | the parameters can be left out as they are the default values. This
|
| | | corresponds to a port on which we can use the MasterInterface, and
|
| | | bind to a peer port with a SlaveInterface. |
| | |
|
| | | For convenience, the name MasterPort and SlavePort are type defined to
|
| | | be a master and slave with the classic MasterInterface and
|
| | | SlaveInterface, as exemplified above. This is sufficient in most
|
| | | typical cases, where no additional functionality is required from the
|
| | | sending interface. In the case of e.g. a DMA port, additional sending
|
| | | functions like dmaAction require the port to expose a DMA-specific
|
| | | interface, which extends the MasterInterface. This is now implemented
|
| | | in a DmaHandler (the name could be discussed with options including
|
| | | Handler, Interface, Iface, IF), which inherits from the
|
| | | MasterInterface, and besides implementing e.g. recvTimingResp also
|
| | | adds the desired DMA functions. By declaring a
|
| | | IfaceMasterPort<DmaHandler> we get a master port with the desired DMA
|
| | | interface (which still binds to a SlaveInterface). |
| | |
|
| | | The implementation of the interfaces is, as already mentioned, done in
|
| | | a handler. Where there previously used to be e.g. a WalkerPort, there
|
| | | is now a WalkerHandler and a MasterPort that uses the handler. We
|
| | | could consider naming the Handlers Interface or Iface instead as
|
| | | already mentioned. The name handler was chosen as they actually
|
| | | implement the interface functions. |
| | |
|
| | | To enable any arbitrary specialisation of the interfaces, the
|
| | | protcol-agnostic base ports have bind functions that call a virtual
|
| | | bind function on the interface base class. It is then up to the
|
| | | specific implementation to overload this function and verify the
|
| | | compatibility. Thus, the ports pass the binding responsibility to the
|
| | | interface, and the master interface tries to cast the slave interface
|
| | | to the right class. |
| | |
|
| | | The separation of the port and the interface is done in a similar
|
| | | manner to TLM-2.0, with the interface of the port being accessible by
|
| | | overloading the pointer operator. Although this might seem like C++
|
| | | abuse, it makes for a far-less verbose way of accessing the interface
|
| | | compared to e.g. a member function. As a result of this change, where
|
| | | it used to say port.send, it will now say port->send, and similarly a
|
| | | port->send becomes (*port)->send. |
| | |
|
| | | As can be seen e.g. in the CPU, the separation of the port and the
|
| | | interface also enable the BaseCPU to declare the instPort and
|
| | | dataPort, and then leave it up to the specific CPU model to provide
|
| | | the interface handlers. Thus, the atomic, timing, inorder and O3 CPU
|
| | | can all implement their own independent handlers, and merely pass them
|
| | | on to the BaseCPU. A similar approach is used in the cache where a
|
| | | handler is created in the subclass and passed to the base cache
|
| | | parent that holds the ports themselves. |
| | |
|
| | | An additional benefit of this separation is that the module itself can
|
| | | implement the interface. This is done in the SimpleMemory, where the
|
| | | port handler was a trivial class that only added a level of
|
| | | indirection (port.recvAtomic calls memory.recvAtomic). Instead of
|
| | | having this handler, the port is now assigned the SimpleMemory itself
|
| | | as the handler, and the SimpleMemory is both a MemObject and a
|
| | | SlaveInterface. |