Scheduler RX ================================= Overview -------- **Scheduler_rx** is a HotRIO receive-side scheduler and packet decoder. It accepts decoded incoming packets, checks whether each packet is addressed to one of the configured slave addresses or to a broadcast address, and stores the received payload into per-slave history buffers. The module provides the latest payload per slave on ``rx_payload_arr_o`` and generates one-cycle valid pulses on ``rx_payload_valid_arr_o`` whenever a payload is stored. Invalid packet addresses are reported with a one-cycle pulse on ``invalid_addr_pulse_o``. The design supports: * Configurable number of slave addresses. * Configurable payload history depth per slave. * Sequential address table generation from a base address. * Unicast packet decoding and storage. * Broadcast packet decoding and storage to all slaves. * Address table reload using update or reset pulses. * Invalid-address detection for debug and diagnostics. ``scheduler_rx`` is interfaced to the HotRIO master Rx which receives packets from a group of addressed HotRIO slave endpoints. The module converts a stream of decoded packet fields into structured per-slave payload outputs. It typically does the following functions : * Receiving slave-specific status or measurement payloads. * Maintaining the latest received payload for each slave. * Keeping a short circular history of recently received payloads per slave. * Applying a broadcast payload update to all slave entries. * Detecting packets whose address does not belong to the configured address map. The module is designed to sit after the HotRIO master Rx. The HotRIO master Rx asserts ``rx_valid_i`` with ``rx_addr_i`` and ``rx_payload_i``. The scheduler latches the packet, decodes the address, stores the payload, and then returns to waiting for the next packet. Source Dependencies ------------------- The design uses the following packages: .. list-table:: :header-rows: 1 :widths: 25 75 * - Package - Purpose * - ``work.globals`` - Provides project-specific constants and types such as ``HOTRIO_ADDR_WIDTH_C``, ``HOTRIO_PAYLOAD_WIDTH_C``, ``payload_array_t``, ``array_slv8`` etc. Detailed portlist ----------------- .. vhdlentity:: :file: ../Firmware/Source/modules/scheduler_rx/hdl/scheduler_rx.vhd :entity: scheduler_rx Generics -------- .. list-table:: :header-rows: 1 :widths: 24 18 16 42 * - Generic - Type / Range - Default - Description * - ``NUM_SLAVES_G`` - ``positive range 1 to 256`` - ``3`` - Defines the number of slave addresses handled by the scheduler. This generic sizes the address table, output payload array, output valid vector, match index range, write-pointer array, and internal history storage. * - ``HISTORY_DEPTH_G`` - ``positive range 1 to 256`` - ``3`` - Defines how many received payload entries are retained internally for each slave. Each slave has an independent circular history buffer and an independent write pointer. Generic Use Cases ~~~~~~~~~~~~~~~~~ ``NUM_SLAVES_G`` Use this generic to match the number of slave endpoints in the receive address map. For example, if the system has eight slaves addressed sequentially from a base address, set ``NUM_SLAVES_G => 8``. The internal address table is generated as: .. code-block:: text slave_addrs_s(i) = first_slave_addr_i + i Address generation uses unsigned arithmetic at ``HOTRIO_ADDR_WIDTH_C`` width. The system should ensure that ``first_slave_addr_i`` and ``NUM_SLAVES_G`` do not create unintended address wraparound. ``HISTORY_DEPTH_G`` Use this generic to configure how many recent payloads are stored per slave. A value of ``1`` stores only the most recent payload in the internal history. Larger values create a per-slave circular buffer. The current entity exposes only the latest payload per slave through ``rx_payload_arr_o``. The history buffers are internal to this implementation. They are useful for future extensions, debug visibility, or additional interfaces that may expose historical payload entries. Internal Architecture --------------------- Reset Behavior ~~~~~~~~~~~~~~ When ``rstn_i = '0'`` on a rising clock edge: * ``fsm_s`` is set to ``IDLE``. * The generated slave address table is cleared. * Payload history, valid history, and fresh history are cleared. * All per-slave write pointers are reset to ``0``. * Latest payload outputs are cleared. * Output valid pulses and invalid-address pulse are deasserted. * Packet latches are cleared. * Match state is cleared. * Address update and address reset edge-detection registers are cleared. Enable Behavior ~~~~~~~~~~~~~~~ At the beginning of every enabled clock cycle outside reset: * ``rx_payload_valid_arr_s`` is cleared to all ``0``. * ``invalid_addr_pulse_s`` is cleared to ``0``. This makes the receive valid outputs and invalid-address output behave as single-cycle pulses. When ``en_i`` is not asserted: * The FSM is forced to ``IDLE``. * Payload history, valid history, fresh history, and write pointers are cleared. * Latest payload outputs are cleared. * Match state is cleared. Address Table Generation ~~~~~~~~~~~~~~~~~~~~~~~~ The address table is generated in the ``LOAD_ADDR`` state: .. code-block:: vhdl for i in 0 to NUM_SLAVES_G-1 loop slave_addrs_s(i) <= std_logic_vector( unsigned(first_slave_addr_i) + to_unsigned(i, HOTRIO_ADDR_WIDTH_C) ); end loop; After the table is generated, the module clears receive history and returns to packet-waiting operation through the ``WAIT_PACKET`` state. Payload History Model ~~~~~~~~~~~~~~~~~~~~~ Each slave has an independent circular history buffer: .. code-block:: text payload_hist_s(slave_index)(history_index) Each slave also has: * A ``valid_hist_s`` vector to mark which history entries contain valid data. * A ``fresh_hist_s`` vector to mark the newest entry for that slave. * A ``wr_ptr_s`` value to select the next history entry to overwrite. On each unicast store, only the matched slave history is updated. On each broadcast store, every slave history is updated with the broadcast payload. Circular Write Pointer ~~~~~~~~~~~~~~~~~~~~~~ For each slave, the write pointer advances after a payload is stored: .. code-block:: text 0 -> 1 -> 2 -> ... -> HISTORY_DEPTH_G - 1 -> 0 The pointer wraps to ``0`` after the final history entry. Each slave has an independent pointer, so one active slave does not disturb the history position of other slaves. Finite State Machine -------------------- ``IDLE`` State ~~~~~~~~~~~~~~ The ``IDLE`` state is the inactive starting point of the receive scheduler. Actions performed: * No packet is decoded. * No payload is stored. * The FSM advances to ``LOAD_ADDR`` when the module is enabled. This state prevents packet processing before the address table has been initialized. ``LOAD_ADDR`` State ~~~~~~~~~~~~~~~~~~~ The ``LOAD_ADDR`` state builds the internal slave address table using ``first_slave_addr_i`` as the base address. It also clears all receive history and output payload storage. Actions performed: * Generates ``slave_addrs_s(0)`` through ``slave_addrs_s(NUM_SLAVES_G - 1)``. * Clears ``payload_hist_s``. * Clears ``valid_hist_s``. * Clears ``fresh_hist_s``. * Resets all entries of ``wr_ptr_s`` to ``0``. * Clears ``rx_payload_arr_s``. * Clears match state. * Advances to ``WAIT_PACKET``. This state is entered after ``IDLE`` and when either ``addr_update_i`` or ``addr_reset_i`` is detected in ``WAIT_PACKET``. ``WAIT_PACKET`` State ~~~~~~~~~~~~~~~~~~~~~ The ``WAIT_PACKET`` state is the normal idle-running state. The scheduler waits for either an address-table reload request or a valid received packet. Priority order in ``WAIT_PACKET``: 1. Rising edge of ``addr_reset_i``. 2. Rising edge of ``addr_update_i``. 3. ``rx_valid_i = '1'``. If ``addr_reset_i`` or ``addr_update_i`` has a rising edge, the FSM moves to ``LOAD_ADDR``. If ``rx_valid_i`` is asserted, the module latches the incoming address and payload: .. code-block:: vhdl rx_addr_latch_s <= rx_addr_i; rx_payload_latch_s <= rx_payload_i; fsm_s <= DECODE_ADDR; The latched values are used in later FSM states. This gives a stable packet view during address decoding and storage. ``DECODE_ADDR`` State ~~~~~~~~~~~~~~~~~~~~~ The ``DECODE_ADDR`` state classifies the latched address. The decode order is: 1. Check whether ``rx_addr_latch_s`` equals ``broadcast_addr_i``. 2. If not broadcast, compare ``rx_addr_latch_s`` against every entry in ``slave_addrs_s``. 3. If a slave match is found, store the matched index and proceed to ``STORE_UNICAST``. 4. If no match is found, proceed to ``ERROR_ADDR``. Broadcast has priority over unicast matching. If the broadcast address is equal to one of the generated slave addresses, the packet is treated as broadcast. The unicast search loop is: .. code-block:: vhdl match_found_v := '0'; match_idx_v := 0; for i in 0 to NUM_SLAVES_G-1 loop if rx_addr_latch_s = slave_addrs_s(i) then match_found_v := '1'; match_idx_v := i; end if; end loop; If duplicate addresses exist in ``slave_addrs_s``, the final matching index in the loop is selected. A correctly configured system should avoid duplicate slave addresses. ``STORE_UNICAST`` State ~~~~~~~~~~~~~~~~~~~~~~~ The ``STORE_UNICAST`` state stores the latched payload for the matched slave. Actions performed for ``match_idx_s``: * Clears all ``fresh_hist_s(match_idx_s)`` entries. * Reads the current write pointer for the matched slave. * Writes ``rx_payload_latch_s`` into the selected history entry. * Sets the corresponding ``valid_hist_s`` bit. * Sets the corresponding ``fresh_hist_s`` bit. * Updates ``rx_payload_arr_s(match_idx_s)`` with the latest payload. * Pulses ``rx_payload_valid_arr_s(match_idx_s)``. * Advances the matched slave write pointer with wraparound. * Returns to ``WAIT_PACKET``. Only the matched slave is updated. Other slave histories and latest payloads are left unchanged. ``STORE_BROADCAST`` State ~~~~~~~~~~~~~~~~~~~~~~~~~ The ``STORE_BROADCAST`` state stores the latched payload into every slave entry. For each slave ``i``: * Clears all ``fresh_hist_s(i)`` entries. * Reads ``wr_ptr_s(i)``. * Writes ``rx_payload_latch_s`` into ``payload_hist_s(i)(wr_ptr_s(i))``. * Sets the matching ``valid_hist_s`` bit. * Sets the matching ``fresh_hist_s`` bit. * Updates ``rx_payload_arr_s(i)`` with the broadcast payload. * Pulses ``rx_payload_valid_arr_s(i)``. * Advances ``wr_ptr_s(i)`` with wraparound. After all slaves are updated, the FSM returns to ``WAIT_PACKET``. ``ERROR_ADDR`` State ~~~~~~~~~~~~~~~~~~~~ The ``ERROR_ADDR`` state handles packets whose address is neither the broadcast address nor a configured slave address. Actions performed: * Asserts ``invalid_addr_pulse_s`` for one clock cycle. * Returns to ``WAIT_PACKET``. No payload history or latest payload output is modified in this state. Output Behavior --------------- Latest Payload Outputs ~~~~~~~~~~~~~~~~~~~~~~ ``rx_payload_arr_o`` reflects ``rx_payload_arr_s``, which stores the latest payload received for each slave. For a unicast packet: * Only the matched slave payload output is updated. * Only the matched valid bit pulses. For a broadcast packet: * Every slave payload output is updated with the broadcast payload. * Every valid bit pulses. Payload Valid Pulses ~~~~~~~~~~~~~~~~~~~~ ``rx_payload_valid_arr_o`` is cleared to all zeros at the beginning of each non-reset clock cycle. It is then asserted only in store states: * ``STORE_UNICAST`` asserts one bit. * ``STORE_BROADCAST`` asserts all bits. This output should be treated as a pulse notification that a latest payload entry was updated. Invalid Address Pulse ~~~~~~~~~~~~~~~~~~~~~ ``invalid_addr_pulse_o`` is also cleared at the beginning of each non-reset clock cycle. It is asserted in ``ERROR_ADDR`` for one cycle when an unmatched packet address is detected. Address Reload Behavior ----------------------- Address reload requests are detected using sampled previous values: .. code-block:: vhdl if (addr_update_old_s = '0' and addr_update_i = '1') then fsm_s <= LOAD_ADDR; The edge-history registers are updated at the end of each non-reset clock cycle: .. code-block:: vhdl addr_update_old_s <= addr_update_i; The current implementation checks these edges only in ``WAIT_PACKET``. For reliable address reload behavior, assert ``addr_update_i`` while the scheduler is enabled and waiting for packets.