Skip to content

Commit 7172bca

Browse files
committed
tests: add baseline timer suite
Six tests cover the four-timer subsystem that was previously untested: - init zeros state, sets prescaler to 1 - enable rising edge reloads counter from reload value - basic prescaler=1 ticking advances counter - overflow fires IRQ when bit 6 set - overflow stays silent when bit 6 clear - cascade timer increments only on lower-timer overflow, not from raw cycle ticks (prescaler bypassed in cascade mode) The cascade test exposed a wrong assumption while writing it — reload=0xFFFF with prescaler=1 overflows every tick, not 'rarely', so the test now uses reload=0 and a controlled tick count.
1 parent 2a9db06 commit 7172bca

3 files changed

Lines changed: 123 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ set(TEST_SOURCES
181181
tests/test_cpu.c
182182
tests/test_dma.c
183183
tests/test_interrupt.c
184+
tests/test_timer.c
184185
tests/test_rewind.c
185186
tests/test_screenshot.c
186187
tests/test_trace.c

tests/test_runner.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ void run_bus_tests(void);
1111
void run_cpu_tests(void);
1212
void run_dma_tests(void);
1313
void run_interrupt_tests(void);
14+
void run_timer_tests(void);
1415
void run_rewind_tests(void);
1516
void run_screenshot_tests(void);
1617
void run_trace_tests(void);
@@ -36,6 +37,7 @@ int main(void) {
3637
RUN_SUITE(run_cpu_tests);
3738
RUN_SUITE(run_dma_tests);
3839
RUN_SUITE(run_interrupt_tests);
40+
RUN_SUITE(run_timer_tests);
3941
RUN_SUITE(run_rewind_tests);
4042
RUN_SUITE(run_screenshot_tests);
4143
RUN_SUITE(run_trace_tests);

tests/test_timer.c

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#include "test_harness.h"
2+
#include "timer/timer.h"
3+
#include "interrupt/interrupt.h"
4+
5+
/* Timer tests run without an APU; timer_tick tolerates apu==NULL. */
6+
7+
TEST(timer_init_zeros_state_and_sets_prescaler_to_one) {
8+
Timer ts[4];
9+
/* Pre-poison so init must clear. */
10+
memset(ts, 0xFF, sizeof(ts));
11+
12+
timer_init(ts);
13+
for (int i = 0; i < 4; i++) {
14+
ASSERT_EQ(ts[i].counter, 0);
15+
ASSERT_EQ(ts[i].reload, 0);
16+
ASSERT_EQ(ts[i].control, 0);
17+
ASSERT_EQ(ts[i].prescaler, 1);
18+
ASSERT_EQ(ts[i].cascade, false);
19+
ASSERT_EQ(ts[i].irq_enable, false);
20+
ASSERT_EQ(ts[i].enabled, false);
21+
}
22+
}
23+
24+
TEST(timer_enable_reloads_counter_from_reload_value) {
25+
/* On rising edge of enable bit, the counter latches from reload. */
26+
Timer ts[4];
27+
timer_init(ts);
28+
29+
timer_write_reload(&ts[0], 0xFF00);
30+
/* Counter should still be 0 — reload only loads on enable. */
31+
ASSERT_EQ(ts[0].counter, 0);
32+
33+
/* Enable bit is bit 7. */
34+
timer_write_control(&ts[0], 0x80);
35+
ASSERT_EQ(ts[0].counter, 0xFF00);
36+
ASSERT_EQ(ts[0].enabled, true);
37+
}
38+
39+
TEST(timer_basic_tick_increments_counter) {
40+
Timer ts[4];
41+
InterruptController ic;
42+
timer_init(ts);
43+
interrupt_init(&ic);
44+
45+
timer_write_reload(&ts[0], 0);
46+
timer_write_control(&ts[0], 0x80); /* prescaler=1, enabled */
47+
/* Tick a few cycles. With prescaler=1, the counter advances by
48+
* exactly that many. */
49+
timer_tick(ts, 100, &ic, NULL);
50+
ASSERT_EQ(ts[0].counter, 100);
51+
}
52+
53+
TEST(timer_overflow_fires_irq_when_enabled) {
54+
Timer ts[4];
55+
InterruptController ic;
56+
timer_init(ts);
57+
interrupt_init(&ic);
58+
59+
/* Reload near the top so a small tick triggers overflow. */
60+
timer_write_reload(&ts[1], 0xFFFE);
61+
/* Enabled, IRQ enabled (bit 6), prescaler=1 (bits 0-1=00). */
62+
timer_write_control(&ts[1], 0xC0);
63+
/* Sanity: rising-edge enable latched 0xFFFE into counter. */
64+
ASSERT_EQ(ts[1].counter, 0xFFFE);
65+
66+
/* Tick 3 cycles: 0xFFFE -> 0xFFFF -> overflow → reload to 0xFFFE,
67+
* one more increment to 0xFFFF. */
68+
timer_tick(ts, 3, &ic, NULL);
69+
ASSERT_EQ(ic.irf & IRQ_TIMER1, IRQ_TIMER1);
70+
}
71+
72+
TEST(timer_overflow_does_not_fire_irq_when_disabled) {
73+
Timer ts[4];
74+
InterruptController ic;
75+
timer_init(ts);
76+
interrupt_init(&ic);
77+
78+
timer_write_reload(&ts[2], 0xFFFE);
79+
/* Enabled but IRQ-disabled (bit 6 clear). */
80+
timer_write_control(&ts[2], 0x80);
81+
timer_tick(ts, 3, &ic, NULL);
82+
ASSERT_EQ(ic.irf & IRQ_TIMER2, 0);
83+
}
84+
85+
TEST(timer_cascade_increments_only_on_lower_overflow) {
86+
/* Timer N overflowing increments cascade-mode timer N+1 by 1
87+
* regardless of N+1's prescaler. Verify the basic behavior. */
88+
Timer ts[4];
89+
InterruptController ic;
90+
timer_init(ts);
91+
interrupt_init(&ic);
92+
93+
/* Timer 0: reload=0, prescaler=1. Takes 0x10000 ticks per overflow. */
94+
timer_write_reload(&ts[0], 0);
95+
timer_write_control(&ts[0], 0x80); /* prescaler=1, enabled */
96+
97+
/* Timer 1: cascade mode (bit 2), enabled. Prescaler doesn't apply. */
98+
timer_write_reload(&ts[1], 0);
99+
timer_write_control(&ts[1], 0x84); /* enabled + cascade */
100+
101+
/* Tick 100 cycles. Timer 0 only reaches 100 — well below overflow.
102+
* Cascade timer 1 must NOT advance from raw cycles. */
103+
timer_tick(ts, 100, &ic, NULL);
104+
ASSERT_EQ(ts[0].counter, 100);
105+
ASSERT_EQ(ts[1].counter, 0);
106+
107+
/* Tick enough cycles to overflow timer 0 exactly once. */
108+
timer_tick(ts, 0x10000 - 100, &ic, NULL);
109+
ASSERT_EQ(ts[1].counter, 1);
110+
}
111+
112+
void run_timer_tests(void) {
113+
TEST_SUITE("timer");
114+
RUN_TEST(timer_init_zeros_state_and_sets_prescaler_to_one);
115+
RUN_TEST(timer_enable_reloads_counter_from_reload_value);
116+
RUN_TEST(timer_basic_tick_increments_counter);
117+
RUN_TEST(timer_overflow_fires_irq_when_enabled);
118+
RUN_TEST(timer_overflow_does_not_fire_irq_when_disabled);
119+
RUN_TEST(timer_cascade_increments_only_on_lower_overflow);
120+
}

0 commit comments

Comments
 (0)