I am writing timer0 interrupt code to reprogram the timer0 for accurate timekeeping.
But the 8051 inside a network-switch SOC is running at high frequency of 125 MHz.
I encounter two issues.
Issue 1: Calculation is wrong inside the inline-assembly when using large numbers.
Defines and C functions.
// System frequency which is also CPU frequency
#define CLOCK_HZ 125000000
// Timer0 is divided by 12
#define TIMER0_CLK_DIV 12
// Number of Timer0 Interrupts per second.
// Minimum must be 158 because of the high clock frequency and 16-bit timer resolution.
#define SYS_TICK_HZ 200
// Number of timer0 counts needed to get SYS_TICK_HZ
#define SYSTICK_TIMER0_VALUE (CLOCK_HZ / TIMER0_CLK_DIV / SYS_TICK_HZ)
// Number of Timer0 ticks missed while reprogramming.
#define TIMER0_REPROGRAM_TIMER_TICK_COUNT (12 * 4 / TIMER0_CLK_DIV)
// Value programmed into TIMER0.
#define TIMER0_REPROGRAM_VALUE (0x10000 - SYSTICK_TIMER0_VALUE + TIMER0_REPROGRAM_TIMER_TICK_COUNT)
// C-code
inline void update_timer0(void) __naked {
__asm
; Turn off global interrupts, to ensure code below is not interrupted.
clr _EA
; Store ACC, PSW both are overwritten.
push a
push psw
mov a, #(TIMER0_REPROGRAM_VALUE & 0xFF)
clr _TR0
; Timer0 is disables, every instruction below this line is counted as delay
; what the TIMER0 could advance while reprogramming.
add a, _TL0
mov _TL0, a
mov a, _TH0
addc a, #((TIMER0_REPROGRAM_VALUE >> 8) & 0xFF)
mov _TH0, a
setb _TR0
; Timer 0 is enabled again, every instruction above this line is counted as delay.
; Restore PSW, ACC
pop psw
pop a
; Turn on global interrupts.
setb _EA
__endasm;
}
// Interrupt handler
void isr_timer0(void) __interrupt(1)
{
update_timer0();
ticks++;
if (sleep_ticks > 0)
sleep_ticks--;
sec_counter++;
}
SYSTICK_TIMER0_VALUE should be 52083.
TIMER0_REPROGRAM_VALUE is 13457 or 0x3491
But assembly below shows that 0xF3B7 is programmed into TIMER0.
000019 74 B7 [12] 783 mov a, #(((0x10000 - (125000000 / 12 / 200)) + (12 * 4 / 12)) & 0xFF)
00001B C2 8C [12] 784 clr _TR0
785 ; Timer0 is disables, every instruction below this line is counted as delay
786 ; what the TIMER0 could advance while reprogramming.
00001D 25 8A [12] 787 add a, _TL0
00001F F5 8A [12] 788 mov _TL0, a
000021 E5 8C [12] 789 mov a, _TH0
000023 34 F3 [12] 790 addc a, #((((0x10000 - (125000000 / 12 / 200)) + (12 * 4 / 12)) >> 8) & 0xFF)
When replacing TIMER0_REPROGRAM_VALUE with (0x10000 - 52083 + TIMER0_REPROGRAM_TIMER_TICK_COUNT) the values are fine.
000019 74 91 [12] 783 mov a, #((0x10000 - 52083 + (12 * 4 / 12)) & 0xFF)
00001B C2 8C [12] 784 clr _TR0
785 ; Timer0 is disables, every instruction below this line is counted as delay
786 ; what the TIMER0 could advance while reprogramming.
00001D 25 8A [12] 787 add a, _TL0
00001F F5 8A [12] 788 mov _TL0, a
000021 E5 8C [12] 789 mov a, _TH0
000023 34 34 [12] 790 addc a, #(((0x10000 - 52083 + (12 * 4 / 12)) >> 8) & 0xFF)
Do I something wrong?
Issue 2: When use any inline assembly, SDCC wants sdcc_atomic_exchange_rollback_start.
Why is this needed because I can't find any documentation and can I disable this?
Because I don't want extra code inside my interrupt function.
I added this to crtstart.asm as a workaround.
sdcc_atomic_exchange_rollback_start::
ret
sdcc_atomic_exchange_rollback_end::
ret
I am no problem moving inline-assembly update_timer0() to crtstart.asm.
But I am struggling how to use defines from C into asm-files.
I am using SDCC : mcs51/z80/z180/r2k/r2ka/r3ka/sm83/tlcs90/ez80_z80/z80n/r800/ds390/TININative/ds400/hc08/s08/stm8/pdk13/pdk14/pdk15/mos6502/mos65c02/f8 TD- 4.5.0 #15242 (Linux) from debian 13.