.. _felix-tohost: felix-tohost =========================== Felix-tohost is the application that reads ToHost DMA buffers and publishes the data over the network. Felix-tohost can read multiple devices and for each device multiple ToHost DMA buffers. Top-level Class ---------------------- The top-level class is `ToHost`, used to build two executables: `felix-tohost` that works with a FELIX card and `felix-file2host` that emulates the hardware. `ToHost` is templated to support both use cases. The template parameters are three: the configuration parameters (emulating hardware requires passing in more parameters), the type of ToHost DMA buffer (that interfaces with the FELIX driver or not) and the type of FELIX device (real or emulated). `ToHost` owns the devices and the DMA buffers. .. doxygenclass:: ToHost :no-link: :members: ToHost Buffer ---------------------- The *ToHost* DMA buffer is described by the `ToHostBuffer` class, specialised to the case in which firmware writes and software reads (as opposed to the *FromHost* case) in a circular buffer. `ToHostBuffer` is an abstract class from which two implementations derive: one to interface with a FELIX card and another one in which the behaviour of the card is emulated. In the latter case, a dedicated thread is spawned to write in the DMA buffer (reading the input data from disk and eventually generating data on-the-fly), advance the write pointer and, if configure, emulate hardware interrupts. `ToHostBuffer` is responsible for advancing the read pointer in a thread-safe manner. In fact `ToHostBuffer` supports multiple buffer readers, each running its own thread. .. note:: `ToHostBuffer` supports both buffered and zero-copy publishers. Buffered publishers copy the data from the DMA buffer into network buffers. Because data has been copied the read pointer can be advanced immediately. Zero-copy publishers pass to the network the address of data *in* the DMA buffer. In this case, to make sure that data does not get overwritten, the read pointer can be advanced only after the network reported the completion of the send operation (on_send_complete callback). The read pointer is thus advanced using the of bytes *sent* by each reader. More precisely, the read pointer offset is set to the minimum number of bytes sent across readers modulo the buffer size. A separate counter keeps track of the bytes *read* to determine if there are data awaiting a reader in the DMA buffer. .. doxygenclass:: ToHostBuffer :no-link: :members: Buffer Reader ---------------------- `ToHostReader` reads and decodes data for a given subset of e-links. Readers own a publisher and share a pointer to the buffer. The read function is registered as callback in the event loop run by the publisher (see :ref:`common`), either as a periodic callback (for polling data) or as an asynchronous callback (to react in response to an hardware interrupt). `ToHostReader` owns one decoder per e-link. Each decoder decodes the chunks [#]_ contained in FELIX blocks corresponding to that e-link. .. [#] A chunk corresponds to a message coming from the front-end. FELIX firmware can split chunks in subchunks to fit them in FELIX blocks. More information in the FELIX user manual. .. doxygenclass:: ToHostReader :no-link: :members: Decoder ---------------------- `Decoder` implements the algorithm to decode the chunks contained in blocks. A chunk can be made up of a single subchunk of type WHOLE, or split in sub-chunks across blocks: one FIRST, possible MIDDLE and one LAST. Each subchunk comes with a flag written by firmware that reports possible error conditions: truncation, malformation or CRC error. An example of malformation is the occurrence of of two FIRST subchunks in a row (the second would be marked with error). These flags are OR'ed with possible error conditions in software such as the truncation of a too-large message. The result is encoded in the `status_byte` passed to the publisher. Another type of sub-chunk is TIMEOUT. Blocks are subject to a timeout in firmware and have fixed size. It is thus possible that a block contains one or a few chunks and a TIMEOUT chunk that completes the block. If the TIMEOUT has no truncation flag set, the chunk can be discarded as it contains only zeroed bytes; if the truncation flag is set it contains also the start of a chunk that was never completed and shall not be discarded. .. doxygenclass:: Decoder :no-link: :members: Monitoring ---------------------- Monitoring information consists of a JSON string containing statistics regarding decoders, readers, DMA buffers and devices. The JSON string can be written to a UNIX fifo such that another application can read it and forward it where needed. The `ToHostMonitor` class provides methods to assemble the JSON message from the inputs. .. doxygenclass:: ToHostMonitor :no-link: :members: .. note:: Monitoring counters are updated by the reader threads while monitoring information is printed out by the main application thread. Therefore thread synchronisation would be needed. However no synchronisation has been implement to avoid performance degradation. Instead the main thread makes a copy of the counters and computes the difference with respect to the previous reading. There is no need of re-setting the counters thanks to the use of 64-bit unsigned counters. The lack of thread synchronisation is minimal: it might cause two counters belonging to the same data structure to have values corresponding to slightly different times.