81 lines
3.7 KiB
C++
81 lines
3.7 KiB
C++
// cxxChronoProbe.cpp - exercise the etl::chrono surface (Phase 5.3).
|
|
//
|
|
// Takes two steady_clock::now() readings around a busy loop, verifies
|
|
// that the second is >= the first (clock is monotonic), then prints
|
|
// the duration count via printf. Also validates the chrono rep is
|
|
// `int32_t` (the etl_profile.h override) so that i64 libcalls don't
|
|
// creep into chrono::now() comparison paths.
|
|
//
|
|
// Marker layout (16-bit little-endian at $025xxx unless noted):
|
|
// $025010 = 0xBEEF main entered
|
|
// $025012 = 1 if sizeof(steady_clock::duration::rep) == 4 (i32 rep)
|
|
// 0 otherwise
|
|
// $025014 = 1 if t1 >= t0 (monotonic), 0 otherwise
|
|
// $025016 = (uint16_t)(t1.time_since_epoch().count() & 0xFFFF)
|
|
// low 16 bits of the second reading; used by smoke to
|
|
// confirm a non-zero value (clock is actually ticking)
|
|
// $025018 = (uint16_t)(elapsed_ms & 0xFFFF)
|
|
// low 16 bits of (t1 - t0).count() in milliseconds; will
|
|
// be small but ETL chrono::duration_cast<milliseconds>
|
|
// returning the raw rep means even tiny elapsed values
|
|
// write *something* here (proves the subtract path works)
|
|
// $025000 = 0xC0DE reached end-of-main (sentinel for runInGno --check)
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include "etl/chrono.h"
|
|
|
|
int main(void) {
|
|
*(volatile uint16_t *)0x025010UL = 0xBEEF;
|
|
|
|
// Compile-time contract: clock-rep is i32 (etl_profile.h override).
|
|
// Any chrono lib that snuck i64 back in would FAIL HERE — caught
|
|
// on the demo build, not silently bloating the .omf.
|
|
static_assert(sizeof(etl::chrono::steady_clock::duration::rep) == 4,
|
|
"etl::chrono::steady_clock::rep must be i32 — check "
|
|
"ETL_CHRONO_STEADY_CLOCK_DURATION in etl_profile.h");
|
|
static_assert(sizeof(etl::chrono::system_clock::duration::rep) == 4,
|
|
"etl::chrono::system_clock::rep must be i32");
|
|
static_assert(sizeof(etl::chrono::high_resolution_clock::duration::rep) == 4,
|
|
"etl::chrono::high_resolution_clock::rep must be i32");
|
|
*(volatile uint16_t *)0x025012UL = 1;
|
|
|
|
// Two readings around a busy loop. Loop is sized to take long
|
|
// enough that one VBL tick (16.67 ms) reliably elapses, but short
|
|
// enough that the demo completes well within the runInGno timeout.
|
|
auto t0 = etl::chrono::steady_clock::now();
|
|
|
|
// Busy-spin. volatile keeps the optimizer from collapsing it.
|
|
volatile uint16_t spin = 0;
|
|
for (uint16_t i = 0; i < 20000; i++) {
|
|
spin = (uint16_t)(spin + 1);
|
|
}
|
|
|
|
auto t1 = etl::chrono::steady_clock::now();
|
|
|
|
// Monotonic check. Use the raw count() so the comparison is on
|
|
// i32 reps, not the time_point operator< (which works too — just
|
|
// belt-and-braces).
|
|
long c0 = t0.time_since_epoch().count();
|
|
long c1 = t1.time_since_epoch().count();
|
|
*(volatile uint16_t *)0x025014UL = (uint16_t)((c1 >= c0) ? 1 : 0);
|
|
|
|
// Low 16 bits of the absolute reading. If the VBL counter
|
|
// wasn't ticking at all both readings would be 0 — make that
|
|
// visible to the host.
|
|
*(volatile uint16_t *)0x025016UL = (uint16_t)(c1 & 0xFFFFL);
|
|
|
|
// Elapsed milliseconds. steady_clock's rep is already
|
|
// milliseconds (period = etl::milli), so the count delta IS
|
|
// the elapsed ms; no duration_cast required.
|
|
long elapsed_ms = c1 - c0;
|
|
*(volatile uint16_t *)0x025018UL = (uint16_t)(elapsed_ms & 0xFFFFL);
|
|
|
|
// Human-readable. GNO redirects stdout to fd 1; printf %ld is
|
|
// fully supported (Phase 1 audit closed the printf gaps).
|
|
printf("steady_clock t0 = %ld ms\n", c0);
|
|
printf("steady_clock t1 = %ld ms\n", c1);
|
|
printf("elapsed = %ld ms\n", elapsed_ms);
|
|
|
|
*(volatile uint16_t *)0x025000UL = 0xC0DE;
|
|
return 0;
|
|
}
|