The goal of the object dictionary in our project is to make process variables (i.e. the rocket's state) globally available to all running processes, across both hostboards. It is a global structure associating a unique 8-bit ID to a stored value of some important variable.
| Ref | Description | Doc. ID | Issue |
|---|
The most intuitive design for such an object dictionary is to store all process variables in a map, which can be accessed by the threads in a synchronized way. Thus, threads have a direct access to the map values through read() and write() primitives. The object dictionary logic then needs to take care of inserting newly written values into a send queue for the CAN bus. Conversely, any updates to values inside the map must be notified to the relevant threads. This design is illustrated in Figure 1.
However, such a design creates major difficulties for the temporal synchronization of messages. Indeed, a value change will be seen (almost) immediately in the local dictionary, while it will take some time to be sent over the bus to update the remote dictionary. Expressed otherwise, two events which happen at the same time on two different hostboards will never be present simultaneously in either object dictionary – unless the transfer latency is negligible compared to the refresh period of all the process variables. A theoretical analysis shows that if at least one of the CAN busses remains correct during the entire operation, the transfer latency of a given frame can be bounded by 6.4 ms, which is compatible with a 100 Hz update frequency. On the other hand, if we only suppose that at every point in time at least one bus is working, then the latency bound is 172 ms, which would create significant de-synchronization.
An alternative design is to make writes only insert their data into the CAN bus queue, without modifying the dictionary value. The latter will only be updated once the successful transmission of the frame over the bus is confirmed. This architecture, shown in Figure 2, has two main advantages:
This is why, for now, this simpler design has been implemented in the CM4/ert/od folder. A major drawback of this scheme, however, is that it relies on the correctness of CAN transmission even for local updates.
The CAN transmission task and the object dictionary update task are assigned a high priority, so that the message queues are drained immediately. Thus the information propagation latency from a write() operation until the update becomes visible in the object dictionary is dominated by the queueing time inside the CAN cores’ TX queue, and the transfer latency on the CAN bus.
The accesses to the object dictionary entries must be done atomically. For this purpose, read() operations must not be called from Interrupt Service Routines (ISRs). Both the read and update operations are guarded by calls to osKernelLock() / osKernelRestoreLock(). While this control function does not disable interrupts, it locks the scheduler and disables task switches, allowing the operation to complete before another access is started.