Observe shared TWI0 IRQ in nRF radio timing captures
This commit is contained in:
1 parent
1b97c089a7
commit
aa480ae79e
5 files changed
+23
-12
No files matched your search
@@ -2,7 +2,7 @@
|
||||
|
||||
The framework's same-object weak vector aliases and its LTO-visible scheduler
|
||||
call bypass GNU --wrap. Only for MESH_HIL_IRQ_TIMING=1, name the wrappers at
|
||||
those seven call sites explicitly. Keep each real handler, SVC/PendSV vector,
|
||||
those eight call sites explicitly. Keep each real handler, SVC/PendSV vector,
|
||||
and the naked port's register/exception-return sequence unchanged. The linker
|
||||
wrap options still resolve the observer's __real_* calls to the real handlers.
|
||||
No installed framework source is modified.
|
||||
@@ -15,6 +15,7 @@ from pathlib import Path
|
||||
IRQ_HANDLERS = (
|
||||
"POWER_CLOCK_IRQHandler", "UARTE0_UART0_IRQHandler", "GPIOTE_IRQHandler",
|
||||
"RTC1_IRQHandler", "USBD_IRQHandler", "UARTE1_IRQHandler",
|
||||
"SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler",
|
||||
)
|
||||
WRAPPED_SYMBOLS = IRQ_HANDLERS + ("vTaskSwitchContext",)
|
||||
PORT_CALL = ' " bl vTaskSwitchContext \\n"\n'
|
||||
|
||||
@@ -6,7 +6,7 @@ extern "C" {
|
||||
volatile Counter mesh_hil_irq_counters[SourceCount] = {};
|
||||
volatile uint32_t mesh_hil_irq_selected[TaskKindCount + 2] = {};
|
||||
volatile Capture mesh_hil_radio_irqs = {
|
||||
0x4d434952, 1, sizeof(Capture), 0, SourceCount, 0, 0, 0, 0, 0, {}};
|
||||
0x4d434952, 2, sizeof(Capture), 0, SourceCount, 0, 0, 0, 0, 0, {}};
|
||||
}
|
||||
std::atomic<uint32_t> revision{0};
|
||||
Work work;
|
||||
@@ -29,6 +29,7 @@ MESH_OBSERVE_IRQ(UARTE0_UART0_IRQHandler, Uart0)
|
||||
MESH_OBSERVE_IRQ(UARTE1_IRQHandler, Uart1)
|
||||
MESH_OBSERVE_IRQ(POWER_CLOCK_IRQHandler, PowerClock)
|
||||
MESH_OBSERVE_IRQ(USBD_IRQHandler, Usb)
|
||||
MESH_OBSERVE_IRQ(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler, Twim0Spim0)
|
||||
#undef MESH_OBSERVE_IRQ
|
||||
|
||||
extern "C" void __real_vTaskSwitchContext(void);
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mesh { namespace irqobserver {
|
||||
enum Source : uint32_t { Rtc1, Gpiote, Uart0, Uart1, PowerClock, Usb, Scheduler, SourceCount };
|
||||
// Append sources to retain the original per-source indices in captures.
|
||||
enum Source : uint32_t { Rtc1, Gpiote, Uart0, Uart1, PowerClock, Usb, Scheduler, Twim0Spim0, SourceCount };
|
||||
enum TaskKind : uint32_t { LoopTask, TimerTask, UsbTask, OtherTask, TaskKindCount };
|
||||
struct Counter { uint32_t sequence, calls, cycles; };
|
||||
struct Sample {
|
||||
@@ -186,7 +187,7 @@ inline void finish(uint32_t direction_index, uint32_t result, bool worst) {
|
||||
barrier();
|
||||
++capture.sequence;
|
||||
}
|
||||
static_assert(sizeof(Capture) == 392 && sizeof(Counter) == 12,
|
||||
static_assert(sizeof(Capture) == 424 && sizeof(Counter) == 12 && SourceCount == 8,
|
||||
"Unexpected IRQ observer ABI");
|
||||
} } // namespace mesh::irqobserver
|
||||
#endif
|
||||
@@ -103,7 +103,7 @@ class Nrf52HilIrqBindingTest(unittest.TestCase):
|
||||
self.assertNotIn("__file__", scope)
|
||||
self.assertEqual(len(env.middleware), expected)
|
||||
|
||||
def test_startup_changes_only_six_vectors_and_preserves_all_real_aliases(self):
|
||||
def test_startup_changes_only_selected_vectors_and_preserves_all_real_aliases(self):
|
||||
patched = PATCH.patched_startup(STARTUP.replace("\n", "\r\n"))
|
||||
self.assertEqual(PATCH.patched_startup(patched), patched)
|
||||
expected = STARTUP
|
||||
@@ -185,13 +185,13 @@ class Nrf52HilIrqBindingTest(unittest.TestCase):
|
||||
source = '#include <cassert>\n#include <cstdint>\n'
|
||||
source += 'extern "C" const uint32_t test_vectors[];\n'
|
||||
source += 'extern "C" void SVC_Handler() {}\nextern "C" void PendSV_Handler() {}\n'
|
||||
source += "static unsigned real_calls[6], wrapped_calls[6];\n"
|
||||
source += f"static unsigned real_calls[{len(PATCH.IRQ_HANDLERS)}], wrapped_calls[{len(PATCH.IRQ_HANDLERS)}];\n"
|
||||
for index, symbol in enumerate(PATCH.IRQ_HANDLERS):
|
||||
source += f'extern "C" void {symbol}() {{ ++real_calls[{index}]; }}\n'
|
||||
source += f'extern "C" void __real_{symbol}();\n'
|
||||
source += (f'extern "C" void __wrap_{symbol}() {{ ++wrapped_calls[{index}]; '
|
||||
f'__real_{symbol}(); }}\n')
|
||||
source += "int main() { for (unsigned i = 0; i < 6; ++i) { "
|
||||
source += f"int main() {{ for (unsigned i = 0; i < {len(PATCH.IRQ_HANDLERS)}; ++i) {{ "
|
||||
source += "reinterpret_cast<void(*)()>(uintptr_t(test_vectors[i+2]))(); "
|
||||
source += "assert(real_calls[i] == 1); assert(wrapped_calls[i] == EXPECT_WRAPPED); } }\n"
|
||||
(directory / "handlers.cpp").write_text(source)
|
||||
|
||||
@@ -27,7 +27,7 @@ FIXTURE=r'''
|
||||
#include <helpers/radiolib/ProfileSwitchTiming.h>
|
||||
@TIMING_GLOBALS@
|
||||
DwtMock dwt;
|
||||
unsigned real_calls[7]{},durations[7]={640,128,192,256,320,384,448};
|
||||
unsigned real_calls[8]{},durations[8]={640,128,192,256,320,384,448,512};
|
||||
bool nested=false;
|
||||
const char* selected_task_name="loop";
|
||||
const char* pcTaskGetName(void* task) { assert(task==nullptr);return selected_task_name; }
|
||||
@@ -38,6 +38,7 @@ extern "C" void __wrap_UARTE1_IRQHandler();
|
||||
extern "C" void __wrap_POWER_CLOCK_IRQHandler();
|
||||
extern "C" void __wrap_USBD_IRQHandler();
|
||||
extern "C" void __wrap_vTaskSwitchContext();
|
||||
extern "C" void __wrap_SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler();
|
||||
extern "C" void __real_RTC1_IRQHandler() {
|
||||
++real_calls[0];dwt.CYCCNT+=durations[0];
|
||||
if(nested) __wrap_GPIOTE_IRQHandler();
|
||||
@@ -49,15 +50,19 @@ REAL_HANDLER(UARTE1_IRQHandler,3)
|
||||
REAL_HANDLER(POWER_CLOCK_IRQHandler,4)
|
||||
REAL_HANDLER(USBD_IRQHandler,5)
|
||||
REAL_HANDLER(vTaskSwitchContext,6)
|
||||
REAL_HANDLER(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler,7)
|
||||
int main() {
|
||||
namespace obs=mesh::irqobserver;
|
||||
namespace timing=mesh::profiletiming;
|
||||
auto& capture=obs::mesh_hil_radio_irqs;
|
||||
assert(capture.magic==0x4d434952 && capture.version==1 && capture.size_bytes==392 && capture.source_count==7);
|
||||
assert(capture.magic==0x4d434952 && capture.version==2 && capture.size_bytes==424 && capture.source_count==8);
|
||||
static_assert(obs::Scheduler==6 && obs::Twim0Spim0==7,"Original indices must remain stable");
|
||||
static_assert(sizeof(obs::mesh_hil_irq_counters)==96,"IRQ counter bank size changed");
|
||||
// Actual wrappers forward every source exactly once, with no active hop.
|
||||
__wrap_UARTE0_UART0_IRQHandler();__wrap_UARTE1_IRQHandler();
|
||||
__wrap_POWER_CLOCK_IRQHandler();__wrap_USBD_IRQHandler();__wrap_vTaskSwitchContext();
|
||||
for(unsigned i=2;i<7;++i) assert(real_calls[i]==1);
|
||||
__wrap_SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler();
|
||||
for(unsigned i=2;i<8;++i) assert(real_calls[i]==1);
|
||||
// Nested source time is explicitly inclusive in its parent; deltas remain
|
||||
// per source and are attached to the same worst successful physical hop.
|
||||
dwt.CYCCNT=100;durations[0]=640;durations[1]=128;nested=true;
|
||||
@@ -94,9 +99,12 @@ int main() {
|
||||
obs::mesh_hil_irq_counters[obs::Uart1].cycles=UINT32_MAX-10;
|
||||
dwt.CYCCNT=UINT32_MAX-31;durations[3]=64;
|
||||
timing::begin(1,0,timing::readClock());__wrap_UARTE1_IRQHandler();
|
||||
__wrap_SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler();
|
||||
timing::finish(timing::Success,timing::readEndpointClock());
|
||||
assert(capture.direction[1].last_ticks==64 && capture.direction[1].last.calls[obs::Uart1]==1);
|
||||
assert(capture.direction[1].last_ticks==576 && capture.direction[1].last.calls[obs::Uart1]==1);
|
||||
assert(capture.direction[1].last.cycles[obs::Uart1]==64);
|
||||
assert(capture.direction[1].last.calls[obs::Twim0Spim0]==1 && capture.direction[1].last.cycles[obs::Twim0Spim0]==512);
|
||||
assert(capture.direction[1].worst.calls[obs::Twim0Spim0]==1 && capture.direction[1].worst.cycles[obs::Twim0Spim0]==512);
|
||||
// A permanently odd writer sequence cannot hang the application or publish
|
||||
// stale counter data as valid attribution. Timing itself remains available.
|
||||
obs::mesh_hil_irq_counters[obs::Gpiote].sequence=1;
|
||||
@@ -106,7 +114,7 @@ int main() {
|
||||
dwt.CYCCNT+=128;
|
||||
timing::finish(timing::Success,timing::readEndpointClock());
|
||||
assert(capture.snapshot_failures==failures+1 && !capture.direction[1].last_valid);
|
||||
for(unsigned i=0;i<7;++i) {
|
||||
for(unsigned i=0;i<8;++i) {
|
||||
assert(capture.direction[1].last.calls[i]==0 && capture.direction[1].last.cycles[i]==0);
|
||||
}
|
||||
assert(capture.sequence%2==0 && capture.snapshots==10 && capture.retries>=8);
|
||||
|
||||
Reference in new issue
Block a user