VxKernel 0.6.0-7 : First version of the timer API + update INTC API

@add
<> include/vhex/driver/mpu/sh/sh7305/intc
  | add interruption priority primitives
  | add all interruption configuration available
<> src/driver/mpu/sh/sh7305/intc
  | remove the install.c source file (merge its content in driver definition)
  | add proper priority primitive
  | add priority information

@update
<> include/vhex/timer
  | force TIMER_CONTINUE at 0
  | force TIMER_STOP at 1
<> src/driver/mpu/sh/sh7305/keysc
  | use the new interrupt priority primitives

@fix
<> src/driver/mpu/sh/sh7305/tmu
  | fix interrupt handler assembly crash for ETMUx
  | fix timer ID check security
  | fix timer available check
  | fix ETMUx interrupt handlers installation runtime patch
  | add timer interrupt level (enable interrupt)
<> src/modules/display/text/dtext
  | fix string cache reallocation
<> src/module/timer
  | fix driver flag check
  | fix module definition
This commit is contained in:
Yann MAGNIN 2022-06-14 21:56:06 +02:00
parent 0c50357b7d
commit 7aada07d23
10 changed files with 205 additions and 65 deletions

View File

@ -412,9 +412,57 @@ struct sh7305_intc
/* Provided by intc/intc.c */
extern struct sh7305_intc SH7305_INTC;
/* provide hardware-specific primitive */
//---
// Interrupt names
//---
/* Because of SH3/SH4 differences and the different ways interrupts are mapped
to bits of IPR and IMR registers, we define the following names for
interrupt signals, and their corresponding IPR and IMR bits are defined in
the INTC driver. */
enum {
/* Timer Unit [TMU] */
INTC_TMU_TUNI0,
INTC_TMU_TUNI1,
INTC_TMU_TUNI2,
/* Extra Timer Unit [TMU]; SH7305-only except for ETMU_TUNI0 */
INTC_ETMU_TUNI0,
INTC_ETMU_TUNI1,
INTC_ETMU_TUNI2,
INTC_ETMU_TUNI3,
INTC_ETMU_TUNI4,
INTC_ETMU_TUNI5,
/* DMA Controller [DMA0]; a single IPR is used for DEI0..3, and another
IPR is used for DEI4..5 and DADERR together */
INTC_DMA_DEI0,
INTC_DMA_DEI1,
INTC_DMA_DEI2,
INTC_DMA_DEI3,
INTC_DMA_DEI4,
INTC_DMA_DEI5,
INTC_DMA_DADERR,
/* Real-Time Clock [RTC]; a single IPR covers all 3 interrupts */
INTC_RTC_ATI,
INTC_RTC_PRI,
INTC_RTC_CUI,
/* SPU; interrupts from the DSPs and the SPU-bound DMA */
INTC_SPU_DSP0,
INTC_SPU_DSP1,
/* USB communication */
INTC_USB,
/* Kay Scan Interface [KEYSC]; SH7305-only */
INTC_KEYSC,
};
//---
// hardware-specific primitives
//---
/* sh7305_intc_install_inth() : install interrupt gate */
extern void *sh7305_intc_install_inth(int blockid, void *gate, size_t size);
/* sh7305_intc_priority() : set the interrupt name priority */
extern int sh7305_intc_priority(int intname, int level);
#endif /* __VHEX_ARCH_SH7305_INTC__ */

View File

@ -10,8 +10,8 @@
/* returned status from a callback */
enum {
TIMER_STOP,
TIMER_CONTINUE
TIMER_CONTINUE = 0,
TIMER_STOP = 1,
};
/* timer_configure(): Reserve and configure a timer

View File

@ -2,14 +2,4 @@
#include <vhex/defs/types.h>
#include <string.h>
/* sh7305_intc_install_inth() : install interrupt gate */
void *sh7305_intc_install_inth(int event_code, void *gate, size_t size)
{
extern uintptr_t vhex_vbr;
return memcpy(
(void*)((uintptr_t)&vhex_vbr + 0x600 + 0x40 + (event_code - 0x400)),
gate,
size
);
}

View File

@ -36,7 +36,94 @@ struct intc_ctx {
sh7305_intc_nmifcr_t nmifcr;
};
/* hardware configuration call */
//---
// hardware-level API
//---
/* Interrupt IPR and IMR positions. The order of entries is as in the named
list of interrupt signals in <gint/intc.h>. */
/* Friendly names for IPR and IMR register numbers */
enum{ IPRA, IPRB, IPRC, IPRD, IPRE, IPRF, IPRG, IPRH, IPRI, IPRJ, IPRK, IPRL };
enum{ IMR0, IMR1, IMR2, IMR3, IMR4, IMR5, IMR6, IMR7, IMR8, IMR9, IMR10 };
#define _ 0,0
static struct info {
uint16_t IPR4, IPR4bits, IMR, IMRbits;
} const info[] = {
/* Standard TMU */
{ IPRA, 0xf000, IMR4, 0x10 },
{ IPRA, 0x0f00, IMR4, 0x20 },
{ IPRA, 0x00f0, IMR4, 0x40 },
/* ETMU */
{ IPRJ, 0xf000, IMR6, 0x08 },
{ IPRG, 0x0f00, IMR5, 0x02 },
{ IPRG, 0x00f0, IMR5, 0x04 },
{ IPRE, 0x00f0, IMR2, 0x01 },
{ IPRI, 0xf000, IMR6, 0x10 },
{ IPRL, 0xf000, IMR8, 0x02 },
/* DMA */
{ IPRE, 0xf000, IMR1, 0x01 },
{ IPRE, 0xf000, IMR1, 0x02 },
{ IPRE, 0xf000, IMR1, 0x04 },
{ IPRE, 0xf000, IMR1, 0x08 },
{ IPRF, 0x0f00, IMR5, 0x10 },
{ IPRF, 0x0f00, IMR5, 0x20 },
{ IPRF, 0x0f00, IMR5, 0x40 },
/* RTC */
{ IPRK, 0xf000, IMR10, 0x04 },
{ IPRK, 0xf000, IMR10, 0x02 },
{ IPRK, 0xf000, IMR10, 0x01 },
/* SPU */
{ IPRC, 0x000f, IMR3, 0x04 },
{ IPRC, 0x000f, IMR4, 0x08 },
/* USB */
{ IPRF, 0x00f0, IMR9, 0x02 },
/* KEYSC */
{ IPRF, 0xf000, IMR5, 0x80 }
};
/* sh7305_intc_priority() : set the interruption level */
int sh7305_intc_priority(int intname, int level)
{
struct info const *i = &info[intname];
int IPRn = i->IPR4, IPRbits = i->IPR4bits;
/* Bit-shift for the mask */
int shift = 0;
while(IPRbits >>= 4) shift += 4;
uint16_t volatile *IPR = &SH7305_INTC.IPR[IPRn << 1];
int oldlevel = (*IPR >> shift) & 0xf;
*IPR = (*IPR & ~(0xf << shift)) | (level << shift);
if(level > 0) {
uint8_t volatile *MSKCLR = SH7305_INTC.MSKCLR;
MSKCLR[4*i->IMR] = i->IMRbits;
} else {
uint8_t volatile *MSK = SH7305_INTC.MSK;
MSK[4*i->IMR] = i->IMRbits;
}
return oldlevel;
}
/* sh7305_intc_install_inth() : install interrupt gate */
void *sh7305_intc_install_inth(int event_code, void *gate, size_t size)
{
extern uintptr_t vhex_vbr;
return memcpy(
(void*)((uintptr_t)&vhex_vbr + 0x600 + 0x40 + (event_code - 0x400)),
gate,
size
);
}
//---
// hardware configuration call
//---
/* __intc_configure() : configure the Interrupt Controller
@ -97,7 +184,7 @@ static void __intc_configure(struct intc_ctx *state)
state->intmsk00.IRQ2 = 1;
state->intmsk00.IRQ3 = 1;
/* NMI Interrupt flagsi (clear) */
/* NMI Interrupt flags (clear) */
state->nmifcr.NMIFL = 0;
}

View File

@ -79,7 +79,7 @@ static void __keysc_configure(struct keysc_ctx *ctx)
ctx->keysc.KYINDR.KYDIR0 = 1;
/* set the interrupt level (max) */
ctx->iprf = 0b1111;
sh7305_intc_priority(INTC_KEYSC, 15);
/* intall the KEYSC interupt handler */
extern void sh7305_keysc_inth(void);
@ -97,7 +97,6 @@ static void __keysc_hsave(struct keysc_ctx *ctx)
ctx->keysc.KIUINTERVALREG = SH7305_KEYSC.KIUINTERVALREG;
ctx->keysc.KYOUTDR.word = SH7305_KEYSC.KYOUTDR.word;
ctx->keysc.KYINDR.word = SH7305_KEYSC.KYINDR.word;
ctx->iprf = SH7305_INTC._IPRX->IPRF.KEYSC;
}
/* __keysc_hrestore() : restore hadware information */
@ -105,7 +104,6 @@ static void __keysc_hrestore(struct keysc_ctx *ctx)
{
/* force mask the SH7305_KEYSC module, otherwise the module will crash */
SH7305_INTC._MSK->IMR5.KEYSC = 1;
SH7305_INTC._IPRX->IPRF.KEYSC = 0b0000;
/* restore registers */
SH7305_KEYSC.KIUCNTREG.word = ctx->keysc.KIUCNTREG.word;
@ -118,7 +116,6 @@ static void __keysc_hrestore(struct keysc_ctx *ctx)
SH7305_KEYSC.KYINDR.word = ctx->keysc.KYINDR.word;
/* enable keysc interuption */
SH7305_INTC._IPRX->IPRF.KEYSC = ctx->iprf;
SH7305_INTC._MSKCLR->IMR5.KEYSC = 1;
}

View File

@ -36,36 +36,37 @@ _sh7305_inth_etmu4:
/* Clear interrupt flag in TCR using slow write */
mov r0, r1
mov.l @(4, r1), r3
1: mov.b @r3, r0
TCR_clear:
mov.b @r3, r0
tst #0x02, r0
and #0xfd, r0
bf/s 1b
bf/s TCR_clear
mov.b r0, @r3
/* Invoke callback */
mov.l .sh7305_inth_callback, r8
mov.l @r8, r8
mov.l sh7305_inth_callback, r8
jsr @r8
mov.l @r1, r4
tst r0, r0
bt 2f
bt ETMUx_shared_exit
/* If return value is non-zero, stop the timer with another callback */
mov.l .timer_stop, r0
mov.l timer_stop, r0
mov.l r0, @r15
jsr @r8
mov r15, r4
2: add #20, r15
ETMUx_shared_exit:
add #20, r15
lds.l @r15+, pr
rts
mov.l @r15+, r8
.zero 26
.zero 28
.timer_stop:
timer_stop:
.long _sh7305_tmu_stop
.sh7305_inth_callback:
sh7305_inth_callback:
.long _sh7305_inth_callback
.storage_etmu4:
.long _sh7305_tmu_callbacks + 140
@ -82,24 +83,25 @@ _sh7305_inth_etmu4:
! address during runtime
! <> the timer ID is set during the installation of the handler
_sh7305_inth_etmux:
/* Dynamically compute the target of the jump */
stc vbr, r3
mov.l 1f, r2
mov.l etmu4_inth_addr, r3
mov.w etmu4_inth_soff, r2
add r2, r3
nop
mova .storage_etmux, r0
mov.w .id_etmux, r2
mova storage_etmux, r0
mov.w etmux_id, r2
jmp @r3
nop
nop
nop
.id_etmux:
.word 0 /* Timer ID */
etmux_id:
.word 0x0000 ! Timer ID */
/* Offset from VBR where ETMU4 is located; set during configure */
1: .long (.shared - _sh7305_inth_etmu4)
etmu4_inth_soff:
.word (.shared - _sh7305_inth_etmu4) ! ETMU4 shared code off
.storage_etmux:
.long _sh7305_tmu_callbacks
.long 0 /* TCR address */
etmu4_inth_addr:
.long 0x00000000 ! ETMU4 shared code addr
storage_etmux:
.long 0x00000000 ! _sh7305_tmu_callbacks
.long 0x00000000 ! TCR address

View File

@ -11,8 +11,8 @@
timer_call_t sh7305_tmu_callbacks[9];
/* Arrays of standard and extra timers */
static tmu_t *TMU = SH7305_TMU.TMU;
static etmu_t *ETMU = SH7305_ETMU;
tmu_t *TMU = SH7305_TMU.TMU;
etmu_t *ETMU = SH7305_ETMU;
/* TSTR register for standard timers */
static volatile uint8_t *TSTR = &SH7305_TMU.TSTR;
@ -73,7 +73,7 @@ static int conf(tid_t id, uint32_t delay, int clock, timer_call_t call)
@state 0 to start the timer, 1 to stop it (nothing else!) */
static int sh7305_tmu_control(tid_t id, int state)
{
if (id < 0 || id > 9)
if (id < 0 || id >= 9)
return (-1);
if(id < 3) {
@ -87,16 +87,15 @@ static int sh7305_tmu_control(tid_t id, int state)
/* available(): Check if a timer is available (UNIE cleared, not running) */
static int available(int id)
{
if (id < 0 || id > 9)
if (id < 0 || id >= 9)
return (-1);
if(id < 3) {
tmu_t *T = &TMU[id];
return !T->TCR.UNIE && !(*TSTR & (1 << id));
} else {
etmu_t *T = &ETMU[id-3];
return !T->TCR.UNIE && !T->TSTR;
}
etmu_t *T = &ETMU[id-3];
return !T->TCR.UNIE && !T->TSTR;
}
/* sh7305_tmu_delay() - compute a delay constant from a duration in seconds */
@ -131,18 +130,18 @@ static int stop_callback(void)
// Public timer-API
//---
int sh7305_tmu_configure(uint64_t delay, timer_call_t call)
tid_t sh7305_tmu_configure(uint64_t delay, timer_call_t call)
{
int clock = 0;
/* Default behavior for the callback */
if(!call.function) call = TIMER_CALL(stop_callback);
if(!call.function) call = TIMER_CALL(&stop_callback);
/* Find a matching timer, starting from the slowest timers with the
smallest interrupt priorities all the way up to TMU0 */
for(int id = 8; id >= 0; id--)
{
if(available(id) != 0)
if(available(id) == 0)
continue;
/* If ID is a TMU, choose a timer prescaler. Assuming the worst
@ -166,7 +165,6 @@ int sh7305_tmu_configure(uint64_t delay, timer_call_t call)
return conf(id, delay, clock, call);
}
return (-1);
}
@ -180,7 +178,7 @@ int sh7305_tmu_start(tid_t id)
int sh7305_tmu_reload(tid_t id, uint64_t delay)
{
//FIXME
if (id < 0 || id > 9)
if (id < 0 || id >= 9)
return (-1);
if(id < 3) {
@ -200,7 +198,7 @@ int sh7305_tmu_pause(tid_t id)
/* sh7305_tmu_stop() - stop and free a timer */
int sh7305_tmu_stop(tid_t id)
{
if (id < 0 || id > 9)
if (id < 0 || id >= 9)
return (-1);
/* Stop the timer and disable UNIE to indicate that it's free */
@ -226,7 +224,7 @@ int sh7305_tmu_stop(tid_t id)
/* sh7305_tmu_wait(): Wait for a timer to stop */
int sh7305_tmu_wait(tid_t id)
{
if (id < 0 || id > 9)
if (id < 0 || id >= 9)
return (-1);
if(id < 3) {
@ -242,7 +240,7 @@ int sh7305_tmu_wait(tid_t id)
/* sh7305_tmu_spinwait(): Start a timer and actively wait for UNF */
int sh7305_tmu_spinwait(tid_t id)
{
if (id < 0 || id > 9)
if (id < 0 || id >= 9)
return (-1);
if(id < 3) {
@ -307,15 +305,28 @@ static void __tmu_configure(struct tmu_ctx *s)
/* install the default interrupt handler */
h = sh7305_intc_install_inth(etmu_evt[i], &sh7305_inth_etmux, 32);
/* Distance from VBR handler to ETMU4, used to jump */
*(uint32_t *)(h + 20) += (uintptr_t)h4 - (uintptr_t)cpu_get_vbr();
/* Timer ID, used for sh7305_tmu_stop() after the callback */
*(uint16_t *)(h + 18) = i;
*(uint16_t *)(h + 16) = i + 3;
/* ETMU4 interrupt handler address, used to jump */
*(uint32_t *)(h + 20) = (uintptr_t)h4;
/* Pointer to the callback */
*(void **)(h + 24) += i;
*(uint32_t *)(h + 24) = (uintptr_t)&sh7305_tmu_callbacks[i + 3];
/* TCR address to acknowledge the interrupt */
*(void volatile **)(h + 28) = &ETMU[i].TCR;
*(uint32_t *)(h + 28) = (uintptr_t)&ETMU[i].TCR;
}
/* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */
sh7305_intc_priority(INTC_TMU_TUNI0, 13);
sh7305_intc_priority(INTC_TMU_TUNI1, 11);
sh7305_intc_priority(INTC_TMU_TUNI2, 9);
/* Enable the extra TMUs at level 7 */
sh7305_intc_priority(INTC_ETMU_TUNI0, 7);
sh7305_intc_priority(INTC_ETMU_TUNI1, 7);
sh7305_intc_priority(INTC_ETMU_TUNI2, 7);
sh7305_intc_priority(INTC_ETMU_TUNI3, 7);
sh7305_intc_priority(INTC_ETMU_TUNI4, 7);
sh7305_intc_priority(INTC_ETMU_TUNI5, 7);
}
/* __tmu_hsave() : save hardware information */

View File

@ -11,6 +11,7 @@
VINLINE void r61524_clear_surface(struct dshader_surface *surface)
{
//TODO: check cache behaviour:
// > ILRAM !!
// > full xram then yram
// > xram and yram
// > yram and xram

View File

@ -81,15 +81,17 @@ static char *dtext_info_register(char const * const str)
dtext_info.pool.text[i].size = 32;
}
}
len = strlen(str);
if (len >= dtext_info.pool.text[dtext_info.pool.idx].size) {
dtext_info.pool.text[dtext_info.pool.idx].size *= 2;
dtext_info.pool.text[dtext_info.pool.idx].size = len;
dtext_info.pool.text[dtext_info.pool.idx].raw = reallocarray(
dtext_info.pool.text[dtext_info.pool.idx].raw,
dtext_info.pool.text[dtext_info.pool.idx].size,
sizeof(char)
);
}
strcpy(dtext_info.pool.text[dtext_info.pool.idx].raw, str);
return dtext_info.pool.text[dtext_info.pool.idx].raw;
}
@ -98,6 +100,7 @@ static char *dtext_info_register(char const * const str)
// Public API
//---
/* dtext_opt(): Display a string of text */
did_t dtext_opt(
int x, int y,

View File

@ -84,7 +84,7 @@ static void __timer_init(void)
struct vhex_driver *driver = vhex_driver_table();
for (int i = 0; i < vhex_driver_count(); ++i) {
if (driver[i].flags.DISPLAY) {
if (driver[i].flags.TIMER) {
memcpy(
&timer_info.driver,
driver[i].module_data,
@ -110,3 +110,4 @@ struct vhex_module mod_timer = {
.init = &__timer_init,
.quit = &__timer_quit,
};
VHEX_DECLARE_MODULE(02, mod_timer);