[nexus] implement microsecond alarm platform APIs (#12580)

This commit implements the otPlatAlarmMicro* platform APIs in the
Nexus simulation environment. It also transitions the simulation's
internal time base from millisecond to microsecond granularity to
ensure that both millisecond and microsecond alarms operate on a
consistent and shared time reference.

Changes include:
- Enabled OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE in Nexus config.
- Updated Nexus::Core to use a 64-bit microsecond time base (mNow).
- Implemented otPlatAlarmMicroGetNow, otPlatAlarmMicroStartAt, and
  otPlatAlarmMicroStop.
- Refactored Nexus::Node to support separate millisecond and
  microsecond alarm instances (mAlarmMilli and mAlarmMicro).
- Updated radio timestamps and PCAP logging to use the new high-
  resolution microsecond time base.
- Adjusted simulation time advancement and alarm triggering logic to
  handle both alarm types correctly.
This commit is contained in:
Jonathan Hui
2026-03-02 11:31:27 -06:00
committed by GitHub
parent 709217aa8a
commit 4866887933
7 changed files with 80 additions and 24 deletions
+1 -1
View File
@@ -120,7 +120,7 @@
#define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 0
#define OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE 0
#define OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE 0
#define OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE 0
#define OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE 1
#define OPENTHREAD_CONFIG_RADIO_STATS_ENABLE 0
#define OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE 1
#define OPENTHREAD_CONFIG_SEEKER_ENABLE 1
+19 -4
View File
@@ -26,6 +26,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <openthread/platform/alarm-micro.h>
#include <openthread/platform/alarm-milli.h>
#include "nexus_alarm.hpp"
@@ -36,7 +37,7 @@ namespace ot {
namespace Nexus {
//---------------------------------------------------------------------------------------------------------------------
// otPlatAlarmMilli APIs
// otPlatAlarm Milli and Micro APIs
extern "C" {
@@ -44,15 +45,29 @@ uint32_t otPlatAlarmMilliGetNow(void) { return Core::Get().GetNow().GetValue();
void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
{
Alarm &alarm = AsNode(aInstance).mAlarm;
Alarm &alarm = AsNode(aInstance).mAlarmMilli;
alarm.mScheduled = true;
alarm.mAlarmTime.SetValue(aT0 + aDt);
Core::Get().UpdateNextAlarmTime(alarm);
Core::Get().UpdateNextAlarmMilli(alarm);
}
void otPlatAlarmMilliStop(otInstance *aInstance) { AsNode(aInstance).mAlarm.mScheduled = false; }
void otPlatAlarmMilliStop(otInstance *aInstance) { AsNode(aInstance).mAlarmMilli.mScheduled = false; }
uint32_t otPlatAlarmMicroGetNow(void) { return Core::Get().GetNowMicro().GetValue(); }
void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
{
Alarm &alarm = AsNode(aInstance).mAlarmMicro;
alarm.mScheduled = true;
alarm.mAlarmTime.SetValue(aT0 + aDt);
Core::Get().UpdateNextAlarmMicro(alarm);
}
void otPlatAlarmMicroStop(otInstance *aInstance) { AsNode(aInstance).mAlarmMicro.mScheduled = false; }
} // extern "C"
-1
View File
@@ -38,7 +38,6 @@ struct Alarm
{
Alarm(void) { Reset(); }
void Reset(void) { mScheduled = false; }
bool ShouldTrigger(Time aNow) const { return mScheduled && (aNow >= mAlarmTime); }
bool mScheduled;
Time mAlarmTime;
+48 -11
View File
@@ -51,7 +51,7 @@ Core::Core(void)
sCore = this;
sInUse = true;
mNextAlarmTime = mNow.GetDistantFuture();
mNextAlarmTime = NumericLimits<uint64_t>::kMax;
pcapFile = getenv("OT_NEXUS_PCAP_FILE");
@@ -256,27 +256,58 @@ Node &Core::CreateNode(void)
return *node;
}
void Core::UpdateNextAlarmTime(const Alarm &aAlarm)
void Core::UpdateNextAlarmMilli(const Alarm &aAlarm)
{
if (aAlarm.mScheduled)
{
mNextAlarmTime = Min(mNextAlarmTime, Max(mNow, aAlarm.mAlarmTime));
uint64_t alarmTime;
if (GetNow() >= aAlarm.mAlarmTime)
{
alarmTime = mNow;
}
else
{
alarmTime = mNow - (mNow % 1000u) + (static_cast<uint64_t>(aAlarm.mAlarmTime - GetNow()) * 1000u);
}
mNextAlarmTime = Min(mNextAlarmTime, alarmTime);
}
}
void Core::UpdateNextAlarmMicro(const Alarm &aAlarm)
{
if (aAlarm.mScheduled)
{
uint64_t alarmTime;
if (GetNowMicro() >= aAlarm.mAlarmTime)
{
alarmTime = mNow;
}
else
{
alarmTime = mNow + static_cast<uint64_t>(aAlarm.mAlarmTime - GetNowMicro());
}
mNextAlarmTime = Min(mNextAlarmTime, alarmTime);
}
}
void Core::AdvanceTime(uint32_t aDuration)
{
TimeMilli targetTime = mNow + aDuration;
uint64_t targetTime = mNow + (static_cast<uint64_t>(aDuration) * 1000u);
while (mPendingAction || (mNextAlarmTime <= targetTime))
{
mNextAlarmTime = mNow.GetDistantFuture();
mNextAlarmTime = NumericLimits<uint64_t>::kMax;
mPendingAction = false;
for (Node &node : mNodes)
{
Process(node);
UpdateNextAlarmTime(node.mAlarm);
UpdateNextAlarmMilli(node.mAlarmMilli);
UpdateNextAlarmMicro(node.mAlarmMicro);
}
if (!mPendingAction)
@@ -298,11 +329,17 @@ void Core::Process(Node &aNode)
ProcessTrel(aNode);
#endif
if (aNode.mAlarm.ShouldTrigger(mNow))
if (aNode.mAlarmMilli.mScheduled && (GetNow() >= aNode.mAlarmMilli.mAlarmTime))
{
aNode.mAlarm.mScheduled = false;
aNode.mAlarmMilli.mScheduled = false;
otPlatAlarmMilliFired(&aNode.GetInstance());
}
if (aNode.mAlarmMicro.mScheduled && (GetNowMicro() >= aNode.mAlarmMicro.mAlarmTime))
{
aNode.mAlarmMicro.mScheduled = false;
otPlatAlarmMicroFired(&aNode.GetInstance());
}
}
void Core::ProcessRadio(Node &aNode)
@@ -326,7 +363,7 @@ void Core::ProcessRadio(Node &aNode)
ackRequested = aNode.mRadio.mTxFrame.GetAckRequest();
mPcap.WriteFrame(aNode.mRadio.mTxFrame, mNow.GetValue() * 1000ull);
mPcap.WriteFrame(aNode.mRadio.mTxFrame, mNow);
otPlatRadioTxStarted(&aNode.GetInstance(), &aNode.mRadio.mTxFrame);
@@ -347,7 +384,7 @@ void Core::ProcessRadio(Node &aNode)
Radio::Frame rxFrame(aNode.mRadio.mTxFrame);
rxFrame.mInfo.mRxInfo.mTimestamp = (mNow.GetValue() * 1000u);
rxFrame.mInfo.mRxInfo.mTimestamp = mNow;
rxFrame.mInfo.mRxInfo.mRssi = kDefaultRxRssi;
rxFrame.mInfo.mRxInfo.mLqi = 0;
@@ -388,7 +425,7 @@ void Core::ProcessRadio(Node &aNode)
(ackMode == kSendAckFramePending));
ackFrame.UpdateFcs();
mPcap.WriteFrame(ackFrame, mNow.GetValue() * 1000ull);
mPcap.WriteFrame(ackFrame, mNow);
otPlatRadioTxDone(&aNode.GetInstance(), &aNode.mRadio.mTxFrame, &ackFrame, kErrorNone);
}
+6 -4
View File
@@ -55,7 +55,8 @@ public:
Node &CreateNode(void);
LinkedList<Node> &GetNodes(void) { return mNodes; }
TimeMilli GetNow(void) { return mNow; }
TimeMilli GetNow(void) { return TimeMilli(static_cast<uint32_t>(mNow / 1000u)); }
TimeMicro GetNowMicro(void) { return TimeMicro(static_cast<uint32_t>(mNow)); }
void AdvanceTime(uint32_t aDuration);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -75,7 +76,8 @@ public:
void SetActiveNode(Node *aNode) { mActiveNode = aNode; }
Node *GetActiveNode(void) { return mActiveNode; }
void UpdateNextAlarmTime(const Alarm &aAlarm);
void UpdateNextAlarmMilli(const Alarm &aAlarm);
void UpdateNextAlarmMicro(const Alarm &aAlarm);
void MarkPendingAction(void) { mPendingAction = true; }
private:
@@ -117,8 +119,8 @@ private:
Array<NetworkKey, 16> mNetworkKeys;
uint16_t mCurNodeId;
bool mPendingAction;
TimeMilli mNow;
TimeMilli mNextAlarmTime;
uint64_t mNow;
uint64_t mNextAlarmTime;
Node *mActiveNode;
};
+2 -1
View File
@@ -38,7 +38,8 @@ void Node::Reset(void)
uint32_t id = GetId();
mRadio.Reset();
mAlarm.Reset();
mAlarmMilli.Reset();
mAlarmMicro.Reset();
mMdns.Reset();
mPendingTasklet = false;
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
+4 -2
View File
@@ -46,7 +46,8 @@ class Platform
{
public:
Radio mRadio;
Alarm mAlarm;
Alarm mAlarmMilli;
Alarm mAlarmMicro;
Mdns mMdns;
Settings mSettings;
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
@@ -121,7 +122,8 @@ public:
static Node &From(otInstance *aInstance) { return static_cast<Node &>(*aInstance); }
using Platform::mAlarm;
using Platform::mAlarmMicro;
using Platform::mAlarmMilli;
using Platform::mMdns;
using Platform::mPendingTasklet;
using Platform::mRadio;