Might I suggest CAN? The FlexCAN controller has one particular advantage in that the TX and RX message events are timestamped at the same bit-point 'on the wire', and you can use these 'correction factors' for the message contents sent by the 'master' to calibrate the time differentials.
From the manual, for this field in the MailBox:
TIME STAMP — Free-Running Counter Time Stamp
This 16-bit field is a copy of the Free-Running Timer, captured for Tx and Rx frames at
the time when the beginning of the Identifier field appears on the CAN bus.
This free-running counter runs at the bus bit-rate, so something like 500K/250K/125K diff-pair, or 83.3/33.3Kbps 'one-wire' is 'common'. That gives a 'fundamental' resolution of 'some microseconds'.
Our implementation used J1939-style messages, but that is not important. The detail is that the 'master' sends a 'current time' message, but the slaves just 'store' that for the moment. Then the Master makes 'note' of the time delay between the free-running-counter value when it packed-up the time to send, and the 'actual' send time per the CAN timestamp read at the TX-complete interrupt. This info goes in a second message, and when received the original-message RX-timestamp is compared to the current free-running-counter value at the second-RX processing instant to create a fully corrected time value for update:
1.1.1. DateTime (PGN 65290)
Sent by the time master periodically in order to synchronize date & time across all nodes on the bus. This is the first of a 2-part time synchronization sequence. Upon receipt of this message, the time slave shall simply remember the time contained within. The receiver shall not store the time into the local real-time clock.
A time slave may also send this message in order to set the system time. In response, the time master shall immediately send the DateTime message followed by the DateTime Sync message.
Byte 0: UTC Seconds (units=seconds, range=0 to 59, offset=0, length=1 byte)
Byte 1: UTC Minutes (units=minutes, range=0 to 59, offset=0, length=1 byte)
Byte 2: UTC Hours (units=hours, range=0 to 23, offset=0, length=1 byte)
Byte 3: UTC Month (units=months, range=1 to 12, offset=0, length=1 byte)
Byte 4: UTC Day (units=days, range=1 to 31, offset=0, length=1 byte)
Byte 5: UTC Year (units=years, range=0 to 250, offset=1985 years, length=1 byte)
Byte 6: Local Offset Minutes (units=minutes, range=-59 to 59, offset=-125, length=1 byte)
Byte 7: Local Offset Hours (units=hours, range=-12 to 14, offset=-125, length=1 byte)
Of course it would be 'much easier' to just work with the raw 32-bit 'seconds' register from the RTC module.
1.1.2. DateTime Sync (PGN 65291)
Sent by the time master following the DateTime message. This message contains the fractional seconds (high resolution) portion of the timestamp, as well as a calibration constant that incorporates the propagation delay of the DateTime message. Upon receipt of this message, the time slave shall compute the “adjusted current time”, which is the sum of 1) the time stored in the most recently received DateTime message, 2) the calibration offset contained in the DateTime Sync message, and 3) the difference in time between receiving the DateTime and DateTime Sync messages. This “adjusted current time” shall then be written into the local real-time clock.
Bytes 0-3: UTC Microseconds (units=us, range=0 to 999999, offset=0, length=4 bytes)
Bytes 4-7: Calibration Offset (units=us, range=0 to 10000000, offset=0, length=4 bytes)
With this, one can 'easily' approach the fundamental bit-rate-resolution time synchronization. With our 'simple' 33.3K one-wire interconnect we had no trouble hitting our 100us time-differential guarantee.