mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-29 13:03:36 +01:00
core: finalize TLB management in timer callbacks (STABLE)
This change enables interrupts within timer callbacks, making it possible to load pages to MMU while handling a timer underflow. The call to TLB_LoadPTEH() has been moved directly into the VBR handler to avoid jumping to ILRAM for a short call on SH4. The TMU and ETMU handlers have been changed to callback through a new function gint_inth_callback() that saves the user bank and a few registers, then invokes the callback with interrupts enabled and in user bank; until now, callbacks were invoked with interrupts disabled and in kernel bank. Note that IMASK is still set so a callback can only be interrupted by a high-priority interrupt. A timer_wait() function has also been added to simplify tests that involve timers. Finally, the priority level of the TMU0 underflow interrupt has been set to 13 (as per the comments) instead of 7. This version is the first stable version that handles TLB misses transparently for large add-ins. It is suitable for every gint application.
This commit is contained in:
parent
8148d89c88
commit
2fd4238d31
8 changed files with 167 additions and 78 deletions
2
TODO
2
TODO
|
@ -1,5 +1,4 @@
|
||||||
For the 2.1.0 release:
|
For the 2.1.0 release:
|
||||||
* core: use gint_switch() to handle TLB misses
|
|
||||||
* core: the four basic memory functions (with automated tests)
|
* core: the four basic memory functions (with automated tests)
|
||||||
* bopti: remove the deprecated image_t definition
|
* bopti: remove the deprecated image_t definition
|
||||||
* project: remove the compat branch
|
* project: remove the compat branch
|
||||||
|
@ -12,6 +11,7 @@ Issues:
|
||||||
* #10 support fx-CG 20
|
* #10 support fx-CG 20
|
||||||
|
|
||||||
Extensions on existing code:
|
Extensions on existing code:
|
||||||
|
* bopti: try to display fullscreen images with TLB access + DMA on fxcg50
|
||||||
* gray: add gprint()
|
* gray: add gprint()
|
||||||
* gray: double-buffer gray settings and unify d* with g*
|
* gray: double-buffer gray settings and unify d* with g*
|
||||||
* topti: support unicode fonts
|
* topti: support unicode fonts
|
||||||
|
|
|
@ -141,6 +141,12 @@ void timer_pause(int timer);
|
||||||
@timer Timer id, as returned by timer_setup() */
|
@timer Timer id, as returned by timer_setup() */
|
||||||
void timer_stop(int timer);
|
void timer_stop(int timer);
|
||||||
|
|
||||||
|
/* timer_wait() - wait for a timer to stop
|
||||||
|
Waits until the specified timer stops running. If the timer is not running,
|
||||||
|
returns immediately. The timer might not be free if it has just been paused
|
||||||
|
instead of stopped entirely. */
|
||||||
|
void timer_wait(int timer);
|
||||||
|
|
||||||
//---
|
//---
|
||||||
// Predefined timer callbacks
|
// Predefined timer callbacks
|
||||||
//---
|
//---
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
.global _gint_inth_7705
|
.global _gint_inth_7705
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
.global _gint_inth_callback
|
||||||
|
|
||||||
.section .gint.blocks, "ax"
|
.section .gint.blocks, "ax"
|
||||||
.align 4
|
.align 4
|
||||||
|
|
||||||
|
@ -152,3 +154,70 @@ _inth_remap:
|
||||||
.byte 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
.byte 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* CALLBACK HELPER
|
||||||
|
This function implements the callback with context saving. It is a general
|
||||||
|
function and does not need to reside in VBR space which is block-structured.
|
||||||
|
This function saves registers r0_bank...r7_bank, enables interrupts,
|
||||||
|
switches back to user bank and executes the callback. It does not save other
|
||||||
|
registers (pr/mach/macl/gbr) which are managed by the handler entry. */
|
||||||
|
|
||||||
|
.section .gint.mapped
|
||||||
|
|
||||||
|
/* gint_inth_callback()
|
||||||
|
Calls the specified function with the given argument after saving the user
|
||||||
|
context, enabling interrupts and going to user bank.
|
||||||
|
|
||||||
|
@r4 Callback function (volatile void * -> int)
|
||||||
|
@r5 Argument (volatile void *)
|
||||||
|
Returns the return value of the callback (int). */
|
||||||
|
_gint_inth_callback:
|
||||||
|
stc.l r0_bank, @-r15
|
||||||
|
stc.l r1_bank, @-r15
|
||||||
|
stc.l r2_bank, @-r15
|
||||||
|
stc.l r3_bank, @-r15
|
||||||
|
stc.l r4_bank, @-r15
|
||||||
|
stc.l r5_bank, @-r15
|
||||||
|
stc.l r6_bank, @-r15
|
||||||
|
stc.l r7_bank, @-r15
|
||||||
|
stc.l spc, @-r15
|
||||||
|
stc.l ssr, @-r15
|
||||||
|
stc.l sr, @-r15
|
||||||
|
|
||||||
|
/* Enable interrupts and go back to user bank. SR.IMASK is already set
|
||||||
|
to the level of the current interrupt, which makes sure we can only
|
||||||
|
be re-interrupted by something with a higher priority. */
|
||||||
|
mov.l .SR_clear_RB_BL, r1
|
||||||
|
stc sr, r0
|
||||||
|
and r1, r0
|
||||||
|
ldc r0, sr
|
||||||
|
|
||||||
|
/* We are now in the user bank, but we can still use r0..r7 as the
|
||||||
|
important values have been saved on the stack. Call back. */
|
||||||
|
stc r4_bank, r0
|
||||||
|
sts.l pr, @-r15
|
||||||
|
jsr @r0
|
||||||
|
stc r5_bank, r4
|
||||||
|
lds.l @r15+, pr
|
||||||
|
|
||||||
|
/* We want to forward the return value to the system bank */
|
||||||
|
ldc r0, r0_bank
|
||||||
|
|
||||||
|
/* Restore the previous status register and the registers of the
|
||||||
|
interrupted procedure. */
|
||||||
|
ldc.l @r15+, sr
|
||||||
|
ldc.l @r15+, ssr
|
||||||
|
ldc.l @r15+, spc
|
||||||
|
ldc.l @r15+, r7_bank
|
||||||
|
ldc.l @r15+, r6_bank
|
||||||
|
ldc.l @r15+, r5_bank
|
||||||
|
ldc.l @r15+, r4_bank
|
||||||
|
ldc.l @r15+, r3_bank
|
||||||
|
ldc.l @r15+, r2_bank
|
||||||
|
ldc.l @r15+, r1_bank
|
||||||
|
rts
|
||||||
|
ldc.l @r15+, r0_bank
|
||||||
|
|
||||||
|
.align 4
|
||||||
|
.SR_clear_RB_BL:
|
||||||
|
.long ~((1 << 29) | (1 << 28))
|
||||||
|
|
|
@ -9,8 +9,7 @@
|
||||||
** * File system, because it's a mess and we might ruin the ROM.
|
** * File system, because it's a mess and we might ruin the ROM.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* TLB management */
|
.section .pretext
|
||||||
.global ___TLB_LoadPTEH
|
|
||||||
|
|
||||||
/* Dynamic allocation */
|
/* Dynamic allocation */
|
||||||
.global _malloc
|
.global _malloc
|
||||||
|
@ -51,26 +50,6 @@
|
||||||
|
|
||||||
#define syscall(id) syscall_(id, syscall_table)
|
#define syscall(id) syscall_(id, syscall_table)
|
||||||
|
|
||||||
/*** Special syscalls that must remain mapped ***/
|
|
||||||
|
|
||||||
.section .gint.mapped
|
|
||||||
|
|
||||||
#ifdef FX9860G
|
|
||||||
___TLB_LoadPTEH:
|
|
||||||
syscall_(0x0003, 2f)
|
|
||||||
2: .long 0x80010070
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef FXCG50
|
|
||||||
___TLB_LoadPTEH:
|
|
||||||
syscall_(0x000c, 2f)
|
|
||||||
2: .long 0x80020070
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*** Normal syscalls in ROM ***/
|
|
||||||
|
|
||||||
.section .pretext
|
|
||||||
|
|
||||||
#ifdef FX9860G
|
#ifdef FX9860G
|
||||||
|
|
||||||
/* Dynamic allocation */
|
/* Dynamic allocation */
|
||||||
|
|
|
@ -28,8 +28,15 @@ test_tea:
|
||||||
|
|
||||||
map:
|
map:
|
||||||
/* If TEA is mappable, map a page and return */
|
/* If TEA is mappable, map a page and return */
|
||||||
mov.l .TLB_LoadPTEH, r0
|
#ifdef FX9860G
|
||||||
jsr @r0
|
mov #3, r0
|
||||||
|
#endif
|
||||||
|
#ifdef FXCG50
|
||||||
|
mov #12, r0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mov.l .syscall, r2
|
||||||
|
jsr @r2
|
||||||
nop
|
nop
|
||||||
|
|
||||||
lds.l @r15+, macl
|
lds.l @r15+, macl
|
||||||
|
@ -66,5 +73,12 @@ panic:
|
||||||
.long 0x00300000
|
.long 0x00300000
|
||||||
.max_mapped_rom:
|
.max_mapped_rom:
|
||||||
.long 0x00300000 + _srom
|
.long 0x00300000 + _srom
|
||||||
.TLB_LoadPTEH:
|
|
||||||
.long ___TLB_LoadPTEH
|
#ifdef FX9860G
|
||||||
|
.syscall:
|
||||||
|
.long 0x80010070
|
||||||
|
#endif
|
||||||
|
#ifdef FXCG50
|
||||||
|
.syscall:
|
||||||
|
.long 0x80020070
|
||||||
|
#endif
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
priority registers or the interrupt masks, and make sure that all the
|
priority registers or the interrupt masks, and make sure that all the
|
||||||
interrupts that it leaves enabled are handled by the new VBR handlers.
|
interrupts that it leaves enabled are handled by the new VBR handlers.
|
||||||
|
|
||||||
@arg vbr New VBR address (uint32_t)
|
@r4 vbr New VBR address (uint32_t)
|
||||||
@arg configure Configuration function (void -> void)
|
@r5 configure Configuration function (void -> void)
|
||||||
Returns the previous VBR address. */
|
Returns the previous VBR address. */
|
||||||
_gint_setvbr:
|
_gint_setvbr:
|
||||||
mov.l r9, @-r15
|
mov.l r9, @-r15
|
||||||
|
|
|
@ -40,9 +40,8 @@ _inth_tmu_0:
|
||||||
mova .storage0, r0
|
mova .storage0, r0
|
||||||
mov #0, r1
|
mov #0, r1
|
||||||
|
|
||||||
/*** This is the first shared section ***/
|
/*** This is the first shared section ***/
|
||||||
|
.shared1:
|
||||||
.clearflag:
|
|
||||||
sts.l pr, @-r15
|
sts.l pr, @-r15
|
||||||
mov.l r1, @-r15
|
mov.l r1, @-r15
|
||||||
|
|
||||||
|
@ -54,32 +53,31 @@ _inth_tmu_0:
|
||||||
mov.w r2, @r1
|
mov.w r2, @r1
|
||||||
|
|
||||||
/* Invoke the callback function and pass the argument */
|
/* Invoke the callback function and pass the argument */
|
||||||
mov.l @r0, r1
|
mov.l .gint_inth_callback_1, r1
|
||||||
|
mov.l @r0, r4
|
||||||
jsr @r1
|
jsr @r1
|
||||||
mov.l @(4, r0), r4
|
mov.l @(4, r0), r5
|
||||||
|
|
||||||
/* Prepare stopping the timer and jump to second section */
|
/* Jump to second section */
|
||||||
|
mov.l .timer_stop_1, r1
|
||||||
|
bra .shared2
|
||||||
mov.l @r15+, r4
|
mov.l @r15+, r4
|
||||||
mov.l .timer_stop, r1
|
|
||||||
bra .stoptimer
|
|
||||||
nop
|
|
||||||
|
|
||||||
/* SECOND GATE - TMU1 entry and stop timer */
|
/* SECOND GATE - TMU1 entry and stop timer */
|
||||||
_inth_tmu_1:
|
_inth_tmu_1:
|
||||||
mova .storage1, r0
|
mova .storage1, r0
|
||||||
bra .clearflag
|
bra .shared1
|
||||||
mov #1, r1
|
mov #1, r1
|
||||||
|
|
||||||
/*** This is the second shared section ***/
|
/*** This is the second shared section ***/
|
||||||
|
.shared2:
|
||||||
.stoptimer:
|
|
||||||
/* Stop the timer if the return value is not zero */
|
/* Stop the timer if the return value is not zero */
|
||||||
tst r0, r0
|
tst r0, r0
|
||||||
bt .end
|
bt .shared3
|
||||||
jsr @r1
|
jsr @r1
|
||||||
nop
|
nop
|
||||||
|
|
||||||
.end:
|
.shared3:
|
||||||
lds.l @r15+, pr
|
lds.l @r15+, pr
|
||||||
rts
|
rts
|
||||||
nop
|
nop
|
||||||
|
@ -89,10 +87,13 @@ _inth_tmu_1:
|
||||||
/* THIRD GATE - TMU2 entry and storage for TMU0 */
|
/* THIRD GATE - TMU2 entry and storage for TMU0 */
|
||||||
_inth_tmu_2:
|
_inth_tmu_2:
|
||||||
mova .storage2, r0
|
mova .storage2, r0
|
||||||
bra .clearflag
|
bra .shared1
|
||||||
mov #2, r1
|
mov #2, r1
|
||||||
|
|
||||||
.zero 14
|
.zero 10
|
||||||
|
|
||||||
|
.gint_inth_callback_1:
|
||||||
|
.long _gint_inth_callback
|
||||||
|
|
||||||
.storage0:
|
.storage0:
|
||||||
.long 0 /* Callback: Configured dynamically */
|
.long 0 /* Callback: Configured dynamically */
|
||||||
|
@ -104,8 +105,8 @@ _inth_tmu_storage:
|
||||||
|
|
||||||
.mask:
|
.mask:
|
||||||
.long 0x0000feff
|
.long 0x0000feff
|
||||||
.timer_stop:
|
.timer_stop_1:
|
||||||
.long _timer_stop /* gint's function from <gint/timer.h> */
|
.long _timer_stop
|
||||||
|
|
||||||
.storage1:
|
.storage1:
|
||||||
.long 0 /* Callback: Configured dynamically */
|
.long 0 /* Callback: Configured dynamically */
|
||||||
|
@ -142,27 +143,24 @@ _inth_tmu_storage:
|
||||||
|
|
||||||
/* FIRST GATE - ETMU2 entry, invoke callback and prepare clear flag */
|
/* FIRST GATE - ETMU2 entry, invoke callback and prepare clear flag */
|
||||||
_inth_etmu2:
|
_inth_etmu2:
|
||||||
/* Warning: the size of the following section (4 bytes) is hardcoded in
|
|
||||||
the jump in _inth_etmux */
|
|
||||||
mova .storage_etmu2, r0
|
mova .storage_etmu2, r0
|
||||||
mov #5, r1
|
mov #5, r1
|
||||||
|
|
||||||
.extra_callback:
|
.shared4:
|
||||||
sts.l pr, @-r15
|
sts.l pr, @-r15
|
||||||
mov.l r1, @-r15
|
mov.l r0, @-r15
|
||||||
|
|
||||||
/* Invoke the callback function */
|
/* Clear interrupt flag */
|
||||||
mov.l @r0, r1
|
mov.l .timer_clear, r2
|
||||||
jsr @r1
|
jsr @r2
|
||||||
mov.l @(4, r0), r4
|
mov r1, r4
|
||||||
|
|
||||||
/* Load timer ID and forward the callback's return value */
|
/* Prepare invoking the callback function */
|
||||||
mov.l .timer_clear, r1
|
mov.l @r15+, r0
|
||||||
mov.l @r15+, r4
|
mov.l .gint_inth_callback_2, r1
|
||||||
|
mov.l @r0, r4
|
||||||
bra _inth_etmu_help
|
bra _inth_etmu_help
|
||||||
mov r0, r5
|
mov.l @(4, r0), r5
|
||||||
|
|
||||||
nop
|
|
||||||
|
|
||||||
.storage_etmu2:
|
.storage_etmu2:
|
||||||
.long 0 /* Callback: Configured dynamically */
|
.long 0 /* Callback: Configured dynamically */
|
||||||
|
@ -171,18 +169,28 @@ _inth_etmu2:
|
||||||
/* SECOND GATE - Helper entry, invoke callback and stop timer if requested */
|
/* SECOND GATE - Helper entry, invoke callback and stop timer if requested */
|
||||||
_inth_etmu_help:
|
_inth_etmu_help:
|
||||||
|
|
||||||
/* Clear the flag and possibly stop the timer */
|
/* Invoke callback; if return value is non-zero, stop timer */
|
||||||
|
jsr @r1
|
||||||
|
nop
|
||||||
|
tst r0, r0
|
||||||
|
bt .shared5
|
||||||
|
mov.l .timer_stop_2, r1
|
||||||
jsr @r1
|
jsr @r1
|
||||||
nop
|
nop
|
||||||
|
|
||||||
|
/* Clear the flag and possibly stop the timer */
|
||||||
|
|
||||||
|
.shared5:
|
||||||
lds.l @r15+, pr
|
lds.l @r15+, pr
|
||||||
rts
|
rts
|
||||||
nop
|
nop
|
||||||
|
|
||||||
.zero 18
|
.gint_inth_callback_2:
|
||||||
|
.long _gint_inth_callback
|
||||||
.timer_clear:
|
.timer_clear:
|
||||||
.long _timer_clear /* gint's function from src/tmu/tmu.c */
|
.long _timer_clear
|
||||||
|
.timer_stop_2:
|
||||||
|
.long _timer_stop
|
||||||
|
|
||||||
/* THIRD GATE - All other ETMU entries, deferred to the previous ones */
|
/* THIRD GATE - All other ETMU entries, deferred to the previous ones */
|
||||||
_inth_etmux:
|
_inth_etmux:
|
||||||
|
@ -198,11 +206,11 @@ _inth_etmux:
|
||||||
nop
|
nop
|
||||||
|
|
||||||
/* Offset from VBR where extra timer 2 is located:
|
/* Offset from VBR where extra timer 2 is located:
|
||||||
- 0x600 to reach the interrupt handlers
|
* 0x600 to reach the interrupt handlers
|
||||||
- 0x040 to jump over the entry gate
|
* 0x040 to jump over the entry gate
|
||||||
- 0x840 to reach the handler of ETMU2
|
* 0x840 to reach the handler of ETMU2
|
||||||
- 0x004 to skip its first instructions (the size is hardcoded) */
|
* Skip over the first instructions */
|
||||||
1: .long 0xe84
|
1: .long 0xe80 + (.shared4 - _inth_etmu2)
|
||||||
|
|
||||||
.id_etmux:
|
.id_etmux:
|
||||||
.long 0 /* Timer ID */
|
.long 0 /* Timer ID */
|
||||||
|
|
|
@ -162,6 +162,22 @@ GMAPPED void timer_stop(int id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* timer_wait() - wait until a timer is stopped */
|
||||||
|
void timer_wait(int id)
|
||||||
|
{
|
||||||
|
if(id < 3)
|
||||||
|
{
|
||||||
|
tmu_t *T = &TMU[id];
|
||||||
|
/* Sleep if an interruption will wake us up */
|
||||||
|
while(*TSTR & (1 << id)) if(T->TCR.UNIE) sleep();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
etmu_t *T = &ETMU[id];
|
||||||
|
while(T->TSTR) if(T->TCR.UNIE) sleep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//---
|
//---
|
||||||
// Predefined timer callbacks
|
// Predefined timer callbacks
|
||||||
//---
|
//---
|
||||||
|
@ -178,15 +194,12 @@ GMAPPED int timer_timeout(volatile void *arg)
|
||||||
// Low-level functions
|
// Low-level functions
|
||||||
//---
|
//---
|
||||||
|
|
||||||
/* timer_clear() - clear an ETMU flag and possibly stop the timer
|
/* timer_clear() - clear an ETMU flag
|
||||||
@timer Timer ID, must be an ETMU
|
@timer Timer ID, must be an ETMU */
|
||||||
@stop Non-zero to stop the timer */
|
GMAPPED void timer_clear(int id)
|
||||||
GMAPPED void timer_clear(int id, int stop)
|
|
||||||
{
|
{
|
||||||
do ETMU[id-3].TCR.UNF = 0;
|
do ETMU[id-3].TCR.UNF = 0;
|
||||||
while(ETMU[id-3].TCR.UNF);
|
while(ETMU[id-3].TCR.UNF);
|
||||||
|
|
||||||
if(stop) timer_stop(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---
|
//---
|
||||||
|
@ -270,7 +283,7 @@ static void init(void)
|
||||||
gint_inthandler(0xc60, inth_etmu_help, 32);
|
gint_inthandler(0xc60, inth_etmu_help, 32);
|
||||||
|
|
||||||
/* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */
|
/* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */
|
||||||
gint_intlevel(0, 7);
|
gint_intlevel(0, 13);
|
||||||
gint_intlevel(1, 11);
|
gint_intlevel(1, 11);
|
||||||
gint_intlevel(2, 9);
|
gint_intlevel(2, 9);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue