MIL-STD-1553
TEMU models MIL-STD-1553 as a bus with one active bus controller and up to 31 remote terminals.
The bus model is available from the BusModels plugin as the MilStd1553Bus class.
The bus controller drives the transaction phases, while remote-terminal models receive command and data phases and post their responses as events.
The phased design is intentional: the bus model tracks whether the current word stream is a command, data, or status phase, so each device model can handle the phase it is given instead of reconstructing the whole transaction locally.
This is especially important for RT-to-RT transfers.
The receiving RT sees the receive command and then the data phase in the same way it would for a BC-to-RT transfer, while the transmitting RT sees the transmit command and replies with status and data.
As a result, RT models that follow the phase interface handle RT-to-RT transfers without special-case code.
The MIL-STD-1553 interface is defined in temu-c/Bus/MilStd1553.h.
A device model that can be connected to the bus must implement temu_Mil1553DevIface:
-
connectedis called when the device is connected to a bus address. -
disconnectedis called when the device is removed from a bus address. -
receiveis called for command, data, and status phases. -
busEnteredIdleis called on the bus-controller device when the bus enters the idle state, for example after a reset.
The remote-terminal example keeps the bus reference and the assigned RT address after connection. It also tracks whether the current command expects a following data phase:
uint8_t expectData;
uint8_t pendingBroadcast;
uint8_t pendingSubAddr;
uint8_t pendingWordCount;
temu_Mil1553BusIfaceRef bus;
uint16_t rtAddr;
struct {
uint8_t infiniteSpeed;
} config;
The connect and disconnect callbacks store and clear the bus interface reference. This is the reference the RT uses later when it sends status or data words back to the bus:
void
connected(void *obj, temu_Mil1553BusIfaceRef bus, int rtAddr)
{
GenericRTU *rtu = (GenericRTU *)obj;
rtu->bus = bus;
rtu->rtAddr = rtAddr;
rtu->expectData = 0;
}
void
disconnected(void *obj, temu_Mil1553BusIfaceRef bus, int rtAddr)
{
(void)bus;
(void)rtAddr;
GenericRTU *rtu = (GenericRTU *)obj;
rtu->bus.Obj = NULL;
rtu->bus.Iface = NULL;
rtu->rtAddr = 0;
rtu->expectData = 0;
}
Responses should be posted through TEMU events instead of being sent directly from the receive callback.
This lets the bus transaction advance at either zero model delay or at a delay based on temu_mil1553TransferTime.
It also prevents recursive bus activation while the current command or data phase is still being processed.
The example sends a status word before the data phase for RT transmit commands:
void
txDataEvent(temu_Event *ev)
{
GenericRTU *rtu = (GenericRTU *)(ev->Obj);
// Send status first
temu_Mil1553Msg msg;
uint16_t statWord;
msg = temu_mil1553CreateStatMsg(&statWord, rtu->rtAddr);
if (!rtu->bus.Iface || !rtu->bus.Iface->send) {
return;
}
rtu->bus.Iface->send(rtu->bus.Obj, rtu, &msg);
// Then send data
uint16_t sa = (rtu->cmd >> 5) & 0x1f;
uint16_t wc = rtu->cmd & 0x1f;
wc = wc == 0 ? 32 : wc;
msg = temu_mil1553CreateDataMsg(rtu->txData[sa], wc);
rtu->bus.Iface->send(rtu->bus.Obj, rtu, &msg);
}
void
statusEvent(temu_Event *ev)
{
GenericRTU *rtu = (GenericRTU *)(ev->Obj);
temu_Mil1553Msg msg;
uint16_t stat;
msg = temu_mil1553CreateStatMsg(&stat, rtu->rtAddr);
if (!rtu->bus.Iface || !rtu->bus.Iface->send) {
return;
}
rtu->bus.Iface->send(rtu->bus.Obj, rtu, &msg);
}
The receive callback handles the incoming bus phases.
For RT receive commands it records the pending sub-address and waits for a data phase before sending status.
For RT transmit commands it schedules the status and data response immediately.
Broadcast receive commands do not produce a status response:
void
receive(void *obj, temu_Mil1553Msg *msg)
{
GenericRTU *rtu = (GenericRTU *)(obj);
if (msg->MsgTyp == teMT_Cmd) {
receiveCommand(rtu, msg);
return;
}
if (msg->MsgTyp == teMT_Data) {
if (!rtu->expectData) {
temu_logError(rtu, "unexpected MIL-STD-1553 data phase");
return;
}
uint16_t words = msg->WordCount;
if (words > rtu->pendingWordCount) {
words = rtu->pendingWordCount;
}
memcpy(rtu->rxData[rtu->pendingSubAddr], msg->Data, words * 2);
rtu->expectData = 0;
if (!rtu->pendingBroadcast) {
postStatus(rtu);
}
}
}
The TEMU 5 bus interface also has an explicit reset path.
Bus-controller models call reset on the bus interface when they need to abort or recover a transaction, such as after a timeout.
When the bus returns to idle, the bus model calls the controller device’s busEnteredIdle callback with a temu_Mil1553BusIdleInfo record.
An RT-only model can still provide the callback as a no-op or use it to clear any pending local state:
void
busEnteredIdle(void *obj, temu_Mil1553BusIdleInfo *idleInfo)
{
(void)idleInfo;
GenericRTU *rtu = (GenericRTU *)obj;
rtu->expectData = 0;
}
Finally, the device interface must include all four callbacks and must be registered by the plugin:
temu_Mil1553DevIface MilBusDevIface = {
.connected = connected,
.disconnected = disconnected,
.receive = receive,
.busEnteredIdle = busEnteredIdle,
};
TEMU_PLUGIN_INIT
{
temu_Class *cls = temu_registerClass("GenericRTU", create, dispose);
temu_addProperty(cls, "config.infiniteSpeed",
offsetof(GenericRTU, config.infiniteSpeed), teTY_U8, 1, NULL,
NULL, "Infinite speed mode");
temu_addProperty(cls, "rtAddr", offsetof(GenericRTU, rtAddr), teTY_U16, 1,
NULL, NULL, "Remote terminal address, set on connect.");
temu_addProperty(cls, "milbus", offsetof(GenericRTU, bus), teTY_IfaceRef, 1,
NULL, NULL, "Milbus interface reference");
temu_requireInterface(cls, "milbus", TEMU_MIL1553_BUS_IFACE_TYPE);
temu_addInterface(cls, "Mil1553DevIface", TEMU_MIL1553_DEV_IFACE_TYPE,
&MilBusDevIface, 0, "Milbus device interface");
}
Bus Setup
Create the MIL-STD-1553 bus from the BusModels plugin, instantiate the tutorial RT, and connect it to an RT address:
$ temu --project=temu-project.yaml
temu> import BusModels
temu> import TutorialMilbusRTU
temu> MilStd1553Bus.new name=milbus0
temu> GenericRTU.new name=rt1
temu> milbus-connect bus=milbus0 rt=rt1 addr=1
If the board model provides a MIL-STD-1553 controller, connect it as the bus controller:
temu> milbus-setbc bus=milbus0 bc=board0-b1553brm
The object methods can also be used when that is more convenient:
temu> milbus0.connect rt=rt1 addr=1
temu> milbus0.setBC bc=board0-b1553brm
Disconnecting an RT is explicit:
temu> milbus-disconnect bus=milbus0 addr=1
The object passed to milbus-setbc must implement Mil1553DevIface.
The bus controller is responsible for initiating command sequences and for resetting the bus when a transaction times out or is aborted.
Remote terminals normally only send status and data responses to command phases that are addressed to them.
Timing
MIL-STD-1553 transfers run at 1 Mbit/s, and TEMU accounts for 20 bits per 16-bit word.
The helper temu_mil1553TransferTime(words) returns the transfer time in nanoseconds.
The tutorial RT has config.infiniteSpeed enabled by default so responses are posted on the current event stack.
Disable it when the RT model should delay responses according to the modeled bus transfer time:
temu> rt1.config.infiniteSpeed = 0