forked from Lephenixnoir/gint
Compare commits
20 Commits
Author | SHA1 | Date |
---|---|---|
Yatis | 413c2b57f2 | |
Yatis | 6399ed6f40 | |
Yatis | 2c5cef3015 | |
Yatis | f02ca55429 | |
Lephe | ad6c108dfc | |
Lephe | 2c2d1513f9 | |
Lephe | 3694f20d56 | |
Lephe | 8ff7d89d33 | |
Lephe | 858ec8aa12 | |
Lephe | 19951ccf62 | |
Lephe | 3a15340335 | |
Lephe | e63ff8351b | |
Lephe | bc575f1599 | |
Lephe | 52d95e72ed | |
Lephe | 078edb50b2 | |
Lephe | 240f29f9d5 | |
Lephe | 2e8b1020cb | |
Lephe | 9b462deca1 | |
Lephe | e66b9083b4 | |
Lephe | 744d243265 |
15
TODO
15
TODO
|
@ -1,4 +1,5 @@
|
|||
Extensions on existing code:
|
||||
* project: add license file
|
||||
* kernel: group linker script symbols in a single header file
|
||||
* kernel: be consistent about *tlb_mapped_memory() in hw_detect()
|
||||
* bopti: try to display fullscreen images with TLB access + DMA on fxcg50
|
||||
|
@ -11,8 +12,22 @@ Extensions on existing code:
|
|||
* core: run destructors when a task-switch results in leaving the app
|
||||
* core rtc: use qdiv10 to massively improve division performance
|
||||
* topti: let the font specify letter and word spacing
|
||||
* thread:
|
||||
- provide better interface for thread creation:
|
||||
- created thread is unable the be in zombie state
|
||||
- created thread is linked to another thread (like a parent)
|
||||
- provide interface for the idle thread
|
||||
- allow menu-return
|
||||
- allow custom idle handler
|
||||
- provide better interface for the "main" thread:
|
||||
- allow custom jmp_buff, which will overwrite the died thread's
|
||||
context with the saved jmpbuf context and perform a
|
||||
longjmp() with it.
|
||||
- pseudo-preemption using pthread_yield() design(?)
|
||||
- move the keyboard driver into a thread(?)
|
||||
|
||||
Future directions.
|
||||
* A re-implementation of virtual timer
|
||||
* A complete file system abstraction
|
||||
* Integrate overclock management
|
||||
* Audio playback using TSWilliamson's libsnd method
|
||||
|
|
11
fx9860g.ld
11
fx9860g.ld
|
@ -185,6 +185,13 @@ SECTIONS
|
|||
|
||||
*(.ilram)
|
||||
|
||||
/* Code that must remain mapped is placed here */
|
||||
*(.gint.mapped)
|
||||
|
||||
/* thread kernel stack */
|
||||
_thread_kernel_stack_end = . ;
|
||||
. = . + 2048 ;
|
||||
_thread_kernel_stack_start = . ;
|
||||
. = ALIGN(16);
|
||||
} > ilram AT> rom
|
||||
|
||||
|
@ -222,13 +229,13 @@ SECTIONS
|
|||
|
||||
/* Code that must remain permanently mapped (.gint.mapped); relocated
|
||||
to start of user RAM at startup, accessed through P1 */
|
||||
.gint.mapped ALIGN(4) : ALIGN(4) {
|
||||
/*.gint.mapped ALIGN(4) : ALIGN(4) {
|
||||
_lgmapped = LOADADDR(.gint.mapped);
|
||||
*(.gint.mapped)
|
||||
. = ALIGN(16);
|
||||
} > rram AT> rom
|
||||
|
||||
_sgmapped = SIZEOF(.gint.mapped);
|
||||
_sgmapped = SIZEOF(.gint.mapped);*/
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -147,6 +147,10 @@ SECTIONS
|
|||
/* Code that must remain mapped is placed here */
|
||||
*(.gint.mapped)
|
||||
|
||||
/* thread kernel stack */
|
||||
_thread_kernel_stack_end = . ;
|
||||
. = . + 2048 ;
|
||||
_thread_kernel_stack_start = . ;
|
||||
. = ALIGN(16);
|
||||
} > ilram AT> rom
|
||||
|
||||
|
|
|
@ -67,6 +67,11 @@ const clock_frequency_t *clock_freq(void);
|
|||
function selects a timer with timer_setup() called with TIMER_ANY. */
|
||||
void sleep_us(uint64_t delay_us);
|
||||
|
||||
/* sleep_us_spin(): Actively sleep for a fixed duration in microseconds
|
||||
Like sleep_us(), but uses timer_spinwait() and does not rely on interrupts
|
||||
being enabled. Useful in timer code running without interrupts. */
|
||||
void sleep_us_spin(uint64_t delay_us);
|
||||
|
||||
/* sleep_ms(): Sleep for a fixed duration in milliseconds */
|
||||
#define sleep_ms(delay_ms) sleep_us((delay_ms) * 1000ull)
|
||||
|
||||
|
|
|
@ -189,6 +189,11 @@ typedef struct
|
|||
/* Number of total glyphs */
|
||||
uint32_t glyph_count;
|
||||
|
||||
/* Character spacing (usually 1) */
|
||||
uint8_t char_spacing;
|
||||
|
||||
uint :24;
|
||||
|
||||
struct {
|
||||
/* Unicode point of first character in block */
|
||||
uint start :20;
|
||||
|
|
|
@ -38,6 +38,9 @@ enum {
|
|||
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,
|
||||
};
|
||||
|
||||
//---
|
||||
|
|
|
@ -248,10 +248,10 @@ void getkey_repeat(int first, int next);
|
|||
|
||||
/* getkey_repeat_filter(): Set the repeat filter function
|
||||
|
||||
This function is called by getkey() and getkey_opt() every time a repeat
|
||||
event occurs when GETKEY_REPEAT_FILTER. The function can decide whether to
|
||||
keep, delay it or drop it entirely. It can also change the repeat delays
|
||||
with getkey_repeat() for fully custom repeat delay curves.
|
||||
The repeat filter is called by getkey() and getkey_opt() every time a repeat
|
||||
event occurs when GETKEY_REP_FILTER is set. The filter can decide whether to
|
||||
keep, delay or drop the event. It can also change the repeat delays with
|
||||
getkey_repeat() for fully custom repeat delay curves.
|
||||
|
||||
The time elapsed since the last accepted repeat is passed to the filter
|
||||
function; this time must be larger than the repeat time set with
|
||||
|
|
|
@ -54,7 +54,17 @@ typedef volatile struct
|
|||
uint32_t :4;
|
||||
uint32_t P1FC :4; /* Pphi divider 1 [*] */
|
||||
);
|
||||
pad(0x20);
|
||||
pad(0x4);
|
||||
|
||||
lword_union(FSICLKCR,
|
||||
uint32_t :16;
|
||||
uint32_t DIVB :6; /* Division ratio for port B */
|
||||
uint32_t :1;
|
||||
uint32_t CLKSTP :1; /* Clock Stop */
|
||||
uint32_t SRC :2; /* Clock source select */
|
||||
uint32_t DIVA :6; /* Division ratio for port A */
|
||||
);
|
||||
pad(0x18);
|
||||
|
||||
lword_union(PLLCR,
|
||||
uint32_t :17;
|
||||
|
@ -65,8 +75,17 @@ typedef volatile struct
|
|||
uint32_t CKOFF :1; /* CKO Output Stop */
|
||||
uint32_t :1;
|
||||
);
|
||||
pad(0x14);
|
||||
|
||||
lword_union(SPUCLKCR,
|
||||
uint32_t :23;
|
||||
uint32_t CLKSTP :1; /* Clock Stop */
|
||||
uint32_t _ :1; /* Unknown */
|
||||
uint32_t :1;
|
||||
uint32_t DIV :6; /* Division ratio */
|
||||
);
|
||||
pad(0x4);
|
||||
|
||||
pad(0x1c);
|
||||
lword_union(SSCGCR,
|
||||
uint32_t SSEN :1; /* Spread Spectrum Enable */
|
||||
uint32_t :31;
|
||||
|
|
|
@ -119,6 +119,7 @@ typedef struct
|
|||
// SH7305 Interrupt Controller. Refer to:
|
||||
// "Renesas SH7724 User's Manual: Hardware"
|
||||
// Section 13: "Interrupt Controller (INTC)"
|
||||
// Also CPU73050.dll was disassembled to find out the bits.
|
||||
//---
|
||||
|
||||
/* sh7305_intc_ipc_t - Interrupt Priority Controller
|
||||
|
@ -131,29 +132,29 @@ typedef volatile struct
|
|||
uint16_t TMU0_0 :4; /* TMU0 Channel 0 */
|
||||
uint16_t TMU0_1 :4; /* TMU0 Channel 1 */
|
||||
uint16_t TMU0_2 :4; /* TMU0 Channel 2 */
|
||||
uint16_t IrDA :4; /* Infrared Communication */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRB,
|
||||
uint16_t :4;
|
||||
uint16_t LCDC :4; /* LCD Controller */
|
||||
uint16_t DMAC1A :4; /* Direct Memory Access Controller 1 */
|
||||
uint16_t _ :4; /* Unknown (TODO) */
|
||||
uint16_t _LCDC :4; /* SH7724: LCD Controller */
|
||||
uint16_t _DMAC1A:4; /* SH7724: DMAC1 channels 0..3 */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRC,
|
||||
uint16_t TMU1_0 :4; /* TMU1 Channel 0 */
|
||||
uint16_t TMU1_1 :4; /* TMU1 Channel 1 */
|
||||
uint16_t TMU1_2 :4; /* TMU1 Channel 2 */
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
uint16_t SPU :4; /* SPU's DSP0 and DSP1 */
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRD,
|
||||
uint16_t :4;
|
||||
uint16_t MMCIF :4; /* MultiMedia Card Interface */
|
||||
uint16_t _MMCIF :4; /* SH7724: MultiMedia Card Interface */
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
);
|
||||
|
@ -171,12 +172,12 @@ typedef volatile struct
|
|||
uint16_t KEYSC :4; /* Key Scan Interface */
|
||||
uint16_t DMACOB :4; /* DMAC0 transfer/error info */
|
||||
uint16_t USB0_1 :4; /* USB controller */
|
||||
uint16_t CMT :4; /* Compare Match Timer */
|
||||
uint16_t _CMT :4; /* SH7724: Compare Match Timer */
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRG,
|
||||
uint16_t SCIF0 :4; /* SCIF0 transfer/error info */
|
||||
uint16_t _SCIF0 :4; /* SH7724: SCIF0 transfer/error info */
|
||||
uint16_t ETMU1 :4; /* Extra TMU 1 */
|
||||
uint16_t ETMU2 :4; /* Extra TMU 2 */
|
||||
uint16_t :4;
|
||||
|
@ -184,26 +185,26 @@ typedef volatile struct
|
|||
pad(2);
|
||||
|
||||
word_union(IPRH,
|
||||
uint16_t MSIOF0 :4; /* Clock-synchronized SCIF channel 0 */
|
||||
uint16_t MSIOF1 :4; /* Clock-synchronized SCIF channel 1 */
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
uint16_t _MSIOF0:4; /* SH7724: Sync SCIF channel 0 */
|
||||
uint16_t _MSIOF1:4; /* SH7724: Sync SCIF channel 1 */
|
||||
uint16_t _1 :4; /* Unknown (TODO) */
|
||||
uint16_t _2 :4; /* Unknown (TODO) */
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRI,
|
||||
uint16_t ETMU4 :4; /* Extra TMU 4 */
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
uint16_t _ :4; /* Unknown (TODO) */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRJ,
|
||||
uint16_t ETMU0 :4; /* Extra TMU 0 */
|
||||
uint16_t :4;
|
||||
uint16_t _ :4; /* Unknown (TODO) */
|
||||
uint16_t FSI :4; /* FIFO-Buffered Serial Interface */
|
||||
uint16_t SDHI1 :4; /* SD Card Host Interface channel 1 */
|
||||
uint16_t _SDHI1 :4; /* SH7724: SD Card Host Interface 1 */
|
||||
);
|
||||
pad(2);
|
||||
|
||||
|
@ -211,14 +212,14 @@ typedef volatile struct
|
|||
uint16_t RTC :4; /* Real-Time Clock */
|
||||
uint16_t DMAC1B :4; /* DMAC1 transfer/error info */
|
||||
uint16_t :4;
|
||||
uint16_t SDHI0 :4; /* SD Card Host Interface channel 0 */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRL,
|
||||
uint16_t ETMU5 :4; /* Extra TMU 5 */
|
||||
uint16_t _ :4; /* Unknown (TODO) */
|
||||
uint16_t :4;
|
||||
uint16_t TPU :4; /* Timer-Pulse Unit */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
@ -292,7 +293,7 @@ typedef struct
|
|||
// Forward definitions
|
||||
//---
|
||||
|
||||
/* Provided by core/gint.c */
|
||||
/* Provided by intc/intc.c */
|
||||
extern sh7705_intc_t SH7705_INTC;
|
||||
extern sh7305_intc_t SH7305_INTC;
|
||||
|
||||
|
|
|
@ -77,4 +77,79 @@ typedef struct
|
|||
|
||||
} GPACKED(4) utlb_data_t;
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
lword_union(PTEH,
|
||||
uint32_t VPN :22; /* Virtual Page Number */
|
||||
uint32_t :2;
|
||||
uint32_t ASID :8; /* Address Space Identifier */
|
||||
);
|
||||
|
||||
lword_union(PTEL,
|
||||
uint32_t :3;
|
||||
uint32_t PPN :19; /* Phusical Page Number */
|
||||
uint32_t :1;
|
||||
uint32_t V :1; /* Valid */
|
||||
uint32_t SZ1 :1; /* Size (bit 1) */
|
||||
uint32_t PR :2; /* Protection */
|
||||
uint32_t SZ0 :1; /* Size (bit 0) */
|
||||
uint32_t C :1; /* Cacheable */
|
||||
uint32_t D :1; /* Dirty */
|
||||
uint32_t SH :1; /* Shared */
|
||||
uint32_t WT :1; /* Write-through */
|
||||
);
|
||||
|
||||
uint32_t TTB;
|
||||
uint32_t TEA;
|
||||
|
||||
lword_union(MMUCR,
|
||||
uint32_t LRUI :6; /* Least-Recently Used ITLB */
|
||||
uint32_t :2;
|
||||
uint32_t URB :6; /* UTLB Replace Boundary */
|
||||
uint32_t :2;
|
||||
uint32_t URC :6; /* UTLB Replace Counter */
|
||||
uint32_t SQMD :1; /* Store Queue Mode */
|
||||
uint32_t SV :1; /* Single Virtual Memory Mode */
|
||||
uint32_t ME :1; /* TLB Extended Mode */
|
||||
uint32_t :4;
|
||||
uint32_t TI :1; /* TLB Invalidate */
|
||||
uint32_t :1;
|
||||
uint32_t AT :1; /* Address Translation */
|
||||
);
|
||||
pad(0x20);
|
||||
|
||||
lword_union(PTEA,
|
||||
uint32_t :18;
|
||||
uint32_t EPR :6;
|
||||
uint32_t ESZ :4;
|
||||
uint32_t :4;
|
||||
);
|
||||
pad(0x38);
|
||||
|
||||
lword_union(PASCR,
|
||||
uint32_t :24;
|
||||
uint32_t UBC :1; /* Control register area */
|
||||
uint32_t UB6 :1; /* Area 6 */
|
||||
uint32_t UB5 :1; /* Area 5 */
|
||||
uint32_t UB4 :1; /* Area 4 */
|
||||
uint32_t UB3 :1; /* Area 3 */
|
||||
uint32_t UB2 :1; /* Area 2 */
|
||||
uint32_t UB1 :1; /* Area 1 */
|
||||
uint32_t UB0 :1; /* Area 0 */
|
||||
);
|
||||
pad(4);
|
||||
|
||||
lword_union(IRMCR,
|
||||
uint32_t :27;
|
||||
uint32_t R2 :1; /* Re-fetch after Register 2 change */
|
||||
uint32_t R1 :1; /* Re-fetch after Register 1 change */
|
||||
uint32_t LT :1; /* Re-fetch after LDTLB */
|
||||
uint32_t MT :1; /* Re-fetch after writing TLB */
|
||||
uint32_t MC :1; /* Re-fetch after writing insn cache */
|
||||
);
|
||||
|
||||
} GPACKED(4) sh7305_mmu_t;
|
||||
|
||||
#define SH7305_MMU (*(sh7305_mmu_t *)0xff000000)
|
||||
|
||||
#endif /* GINT_MPU_MMU */
|
||||
|
|
|
@ -37,8 +37,8 @@ typedef volatile struct
|
|||
uint32_t RS :1;
|
||||
uint32_t IL :1;
|
||||
uint32_t SndCache :1;
|
||||
uint32_t _unknown1 :1;
|
||||
uint32_t :1;
|
||||
uint32_t FPU :1;
|
||||
|
||||
uint32_t :1;
|
||||
uint32_t INTC :1;
|
||||
|
@ -53,41 +53,44 @@ typedef volatile struct
|
|||
uint32_t CMT :1;
|
||||
uint32_t RWDT :1;
|
||||
uint32_t DMAC1 :1;
|
||||
uint32_t :1;
|
||||
uint32_t TMU1 :1;
|
||||
uint32_t SCIF0 :1;
|
||||
uint32_t SCIF1 :1;
|
||||
uint32_t :4;
|
||||
|
||||
uint32_t SCIF2 :1;
|
||||
uint32_t SCIF3 :1;
|
||||
uint32_t SCIF4 :1;
|
||||
uint32_t SCIF5 :1;
|
||||
uint32_t :1;
|
||||
uint32_t SCIF :1;
|
||||
uint32_t KEYSC :1;
|
||||
uint32_t RTC :1;
|
||||
uint32_t :2;
|
||||
uint32_t MSIOF0 :1;
|
||||
uint32_t MSIOF1 :1;
|
||||
uint32_t :1;
|
||||
);
|
||||
|
||||
/* Module Stop Control Register 1 */
|
||||
lword_union(MSTPCR1,
|
||||
uint32_t :19;
|
||||
uint32_t KEYSC :1;
|
||||
uint32_t RTC :1;
|
||||
uint32_t :1;
|
||||
uint32_t I2C0 :1;
|
||||
uint32_t I2C1 :1;
|
||||
uint32_t :8;
|
||||
);
|
||||
|
||||
/* Module Stop Control Register 2
|
||||
I stripped down this one to remove any fancy modules from the SH7724
|
||||
that are unlikely to even be present in the SH7305. */
|
||||
The list was established by Yatis. See <https://bible.planet-casio.
|
||||
com/yatis/hardware/sh7305/power.html>. */
|
||||
lword_union(MSTPCR2,
|
||||
uint32_t :2;
|
||||
uint32_t MMC :1;
|
||||
uint32_t :1;
|
||||
uint32_t ADC :1;
|
||||
uint32_t :6;
|
||||
uint32_t TPU :1;
|
||||
uint32_t :4;
|
||||
uint32_t USB0 :1;
|
||||
uint32_t :20;
|
||||
uint32_t :2;
|
||||
uint32_t SDC :1;
|
||||
uint32_t :2;
|
||||
uint32_t FLCTL :1;
|
||||
uint32_t ECC :1;
|
||||
uint32_t :1;
|
||||
uint32_t I2C :1;
|
||||
uint32_t :1;
|
||||
uint32_t FSI_SPU :1;
|
||||
uint32_t _unknown2 :1;
|
||||
uint32_t :1;
|
||||
uint32_t LCDC :1;
|
||||
uint32_t _unknown3 :1;
|
||||
uint32_t Cmod :1;
|
||||
uint32_t :1;
|
||||
uint32_t Cmod2A :1;
|
||||
uint32_t :2;
|
||||
);
|
||||
pad(4);
|
||||
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
//---
|
||||
// gint:mpu:spu - Sound Processing Unit and its DSPs
|
||||
//---
|
||||
|
||||
#ifndef GINT_MPU_SPU
|
||||
#define GINT_MPU_SPU
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
uint32_t PBANKC0; /* Program Bank Control 0 */
|
||||
uint32_t PBANKC1; /* Program Bank Control 1 */
|
||||
pad(0x8);
|
||||
|
||||
uint32_t XBANKC0; /* X Bank Control 0 */
|
||||
uint32_t XBANKC1; /* X Bank Control 1 */
|
||||
pad(0x10);
|
||||
|
||||
lword_union(SPUSRST, /* SPU Software Reset */
|
||||
uint32_t :24;
|
||||
uint32_t DB3 :1; /* DMABUF 3 */
|
||||
uint32_t DB2 :1; /* DMABUF 2 */
|
||||
uint32_t DB1 :1; /* DMABUF 1 */
|
||||
uint32_t DB0 :1; /* DMABUF 0 */
|
||||
uint32_t :3;
|
||||
uint32_t RST :1; /* Reset */
|
||||
);
|
||||
uint32_t const SPUADR; /* SPU address */
|
||||
uint32_t const ENDIAN; /* SuperHyway endian */
|
||||
pad(0x10);
|
||||
|
||||
uint32_t GCOM[8]; /* Global Common */
|
||||
pad(0x20);
|
||||
|
||||
uint32_t const DMABUF[4]; /* Inter-DSP Communication Buffer */
|
||||
|
||||
} GPACKED(4) spu_t;
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
uint32_t SBAR; /* Source base address */
|
||||
uint32_t SAR; /* Source address */
|
||||
uint32_t DBAR; /* Destination base address */
|
||||
uint32_t DAR; /* Destination address */
|
||||
uint32_t TCR; /* Transfer count */
|
||||
uint32_t SHPRI; /* SHway priority */
|
||||
uint32_t CHCR; /* Channel control */
|
||||
pad(0x4);
|
||||
|
||||
} GPACKED(4) spu_dsp_dma_t;
|
||||
|
||||
typedef volatile struct {
|
||||
uint32_t LSA; /* Loop start address */
|
||||
uint32_t LEA; /* Loop end address */
|
||||
pad(0x8);
|
||||
|
||||
} GPACKED(4) spu_dsp_loop_t;
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
uint32_t DSPRST; /* DSP full reset */
|
||||
uint32_t DSPCORERST; /* DSP core reset */
|
||||
uint32_t const DSPHOLD; /* DSP hold */
|
||||
uint32_t DSPRESTART; /* DSP restart */
|
||||
pad(0x8);
|
||||
|
||||
uint32_t IEMASKC; /* CPU interrupt source mask */
|
||||
uint32_t IMASKC; /* CPU interrupt signal mask */
|
||||
uint32_t IEVENTC; /* CPU interrupt source */
|
||||
uint32_t IEMASKD; /* DSP interrupt source mask */
|
||||
uint32_t IMASKD; /* DSP interrupt signal mask */
|
||||
uint32_t IESETD; /* DSP interrupt set */
|
||||
uint32_t IECLRD; /* DSP interrupt clear */
|
||||
uint32_t OR; /* DMAC operation */
|
||||
uint32_t COM[8]; /* CPU-DSP communication */
|
||||
uint32_t BTADRU; /* Bus-through address high */
|
||||
uint32_t BTADRL; /* Bus-through address low */
|
||||
uint32_t WDATU; /* Bus-through write data high */
|
||||
uint32_t WDATL; /* Bus-through write data low */
|
||||
uint32_t RDATU; /* Bus-through read data high */
|
||||
uint32_t RDATL; /* Bus-through read data low */
|
||||
uint32_t BTCTRL; /* Bus-through mode control */
|
||||
uint32_t SPUSTS; /* SPU status */
|
||||
pad(0x88);
|
||||
|
||||
spu_dsp_dma_t DMA[3];
|
||||
pad(0x20);
|
||||
spu_dsp_loop_t LP[3];
|
||||
|
||||
} GPACKED(4) spu_dsp_t;
|
||||
|
||||
#define SH7305_SPU (*(spu_t *)0xfe2ffc00)
|
||||
#define SH7305_DSP0 (*(spu_dsp_t *)0xfe2ffd00)
|
||||
#define SH7305_DSP1 (*(spu_dsp_t *)0xfe3ffd00)
|
||||
|
||||
#endif /* GINT_MPU_SPU */
|
|
@ -0,0 +1,51 @@
|
|||
#ifndef GINT_STD_SETJMP
|
||||
# define GINT_STD_SETJMP
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Custom(?) jmp_buf struct
|
||||
|
||||
@note:
|
||||
We save only r8 ~ r15 and SR / PC registers. The SR register is saved first
|
||||
because the longjump() can be involved with a different register bank.
|
||||
So, to avoid this, it's simpler to restore the saved SR first then restore
|
||||
all registers (see <src/setjmp/longjmp.S>). */
|
||||
struct __jmp_buf
|
||||
{
|
||||
uint32_t sr;
|
||||
uint32_t reg[8];
|
||||
uint32_t gbr;
|
||||
uint32_t macl;
|
||||
uint32_t mach;
|
||||
uint32_t pr;
|
||||
};
|
||||
|
||||
/* User jmp_buf alias */
|
||||
typedef struct __jmp_buf jmp_buf[1];
|
||||
|
||||
|
||||
|
||||
/* setjmp(): store the calling environment in ENV
|
||||
|
||||
This function saves various information about the calling environment
|
||||
(typically, the stack pointer, the instruction pointer, the values of some
|
||||
registers and the signal mask) in the buffer ENV for later use by longjmp().
|
||||
In this case, setjmp() returns 0 */
|
||||
extern int setjmp(jmp_buf env);
|
||||
|
||||
/* longjmp(): effectuate non-local goto using the saved ENV
|
||||
|
||||
This function uses the information saved in env to transfer control back to
|
||||
the point where setjmp() was called and to restore ("rewind") the stack to its
|
||||
state at the time of the setjmp() call.
|
||||
|
||||
Following a successful longjmp(), execution continues as if setjmp() had
|
||||
returned for a second time. This "fake" return can be distinguished from a
|
||||
true setjmp() call because the "fake return" returns the value provided in
|
||||
val. If the programmer mistakenly passes the value 0 in val, the "fake" return
|
||||
will instead return 1. */
|
||||
extern void longjmp(jmp_buf env, int val);
|
||||
|
||||
|
||||
#endif /* GINT_STD_SETJMP */
|
|
@ -19,10 +19,12 @@ void *calloc(size_t nmemb, size_t size);
|
|||
/* realloc(): Reallocate dynamic memory */
|
||||
void *realloc(void *ptr, size_t size);
|
||||
|
||||
#define RAND_MAX 0x7fffffff
|
||||
|
||||
/* srand(): Seed the PRNG */
|
||||
void srand(unsigned int seed);
|
||||
|
||||
/* rand(): Generate a pseudo-random number */
|
||||
/* rand(): Generate a pseudo-random number between 0 and RAND_MAX */
|
||||
int rand(void);
|
||||
|
||||
#endif /* GINT_STD_STDLIB */
|
||||
|
|
|
@ -0,0 +1,672 @@
|
|||
#ifndef GINT_THREAD
|
||||
# define GINT_THREAD
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* define the jmp_buf type */
|
||||
#include <gint/std/setjmp.h>
|
||||
|
||||
/* Define the default context switching frequency */
|
||||
#ifndef THREAD_SCHEDULER_FREQUENCY
|
||||
# define THREAD_SCHEDULER_FREQUENCY 16
|
||||
#endif
|
||||
|
||||
/* Define the default thread stack size */
|
||||
#ifndef THREAD_STACK_SIZE
|
||||
# define THREAD_STACK_SIZE (4 * 1024)
|
||||
#endif
|
||||
|
||||
/* Define the default kernel idle thread's stack size */
|
||||
#ifndef THREAD_IDLE_STACK_SIZE
|
||||
# define THREAD_IDLE_STACK_SIZE 32
|
||||
#endif
|
||||
|
||||
|
||||
//---
|
||||
// Thread attribute interface
|
||||
//---
|
||||
/* define the thread wartermark, used to check if the mutex is valid */
|
||||
#define THREAD_ATTR_WATERMARK (~0xdeadbeef)
|
||||
|
||||
/* define the fundamental data type and alias for thread attribute */
|
||||
struct thread_attr_s {
|
||||
struct {
|
||||
enum {
|
||||
THREAD_ATTR_JOINABLE = 0,
|
||||
THREAD_ATTR_DETACHED = 1,
|
||||
} detach;
|
||||
enum {
|
||||
THREAD_ATTR_LONGJMP_DISABLE = 0,
|
||||
THREAD_ATTR_LONGJMP_ENABLE = 1,
|
||||
} longjmp;
|
||||
} state;
|
||||
struct {
|
||||
uint32_t watermark;
|
||||
} private;
|
||||
};
|
||||
typedef struct thread_attr_s thread_attr_t;
|
||||
|
||||
/* thread_attr_init(): Initialize thread attribute object
|
||||
|
||||
This function initializes the thread attributes object pointed to by ATTR
|
||||
with default attribute values. After this call, individual attributes of the
|
||||
object can be set using various related functions formatted like
|
||||
"thread_attr_*()" and then the object can be used in one or more
|
||||
"thread_create()" calls that create threads.
|
||||
|
||||
@return
|
||||
* negative value if ATTR is NULL
|
||||
* 0 if success */
|
||||
extern int thread_attr_init(thread_attr_t *attr);
|
||||
|
||||
/* thread_attr_setdetachstate() and thread_attr_getdetachstate()
|
||||
|
||||
The "thread_attr_setdetachstate()" function sets the detach state attribute
|
||||
of the thread attributes object referred to by attr to the value specified
|
||||
in detachstate.
|
||||
|
||||
The detach state attribute determines whether a thread created using the
|
||||
thread attributes object attr will be created in a joinable or a detached
|
||||
state.
|
||||
|
||||
The following values may be specified in detachstate:
|
||||
- THREAD_ATTR_DETACHED
|
||||
Threads that are created using ATTR will be created in a detached state.
|
||||
- THREAD_ATTR_JOINABLE
|
||||
Threads that are created using attr will be created in a joinable state.
|
||||
|
||||
The default setting of the detach state attribute in a newly initialized
|
||||
thread attributes object is THREAD_ATTR_JOINABLE.
|
||||
|
||||
The "thread_attr_getdetachstate()" returns the detach state attribute of
|
||||
the thread attributes object attr in the buffer pointed to by detachstate.
|
||||
|
||||
@return
|
||||
* negative value if ATTR is NULL or uninitialized
|
||||
* 0 if success */
|
||||
extern int thread_attr_setdetachstate(thread_attr_t *attr, int detachstate);
|
||||
extern int thread_attr_getdetachstate(thread_attr_t *attr, int *detachstate);
|
||||
|
||||
|
||||
/* thread_attr_enablelongjmp() thread_attr_getlongjmpstatus()
|
||||
|
||||
This feature is CUSTOM to the Gint kernel. It will allow, when a thread with
|
||||
this attribute die, instead of release its memories and removed it from the
|
||||
scheduler, allow it to perform a "longjmp()" and return ("rewinds") to the
|
||||
"thread_create()" function involved to create the current thread. The
|
||||
original "thread_create()" will return the special value:
|
||||
THREAD_CREATE_LONGJMP_RETURN.
|
||||
|
||||
In the case of Gint, this feature is very useful because the "main" function
|
||||
can be threaded and we can return the to "dark-side" of the kernel to manage
|
||||
destructors or to recall the main function again. We cann allow this while
|
||||
continuing running (potential) threaded drivers that can continue working
|
||||
while being isolated from the Gint "bootstrap" part.
|
||||
|
||||
The following values may be specified in "status":
|
||||
- THREAD_ATTR_LONGJMP_ENABLE
|
||||
Enable the longjmp() when the thread come to the end.
|
||||
- THREAD_ATTR_LONGJMP_DISABLE
|
||||
Disable the longjmp() when the thread come to the end.
|
||||
|
||||
@return
|
||||
* negative value if ATTR is NULL or uninitialized
|
||||
* 0 if success */
|
||||
extern int thread_attr_enablelongjmp(thread_attr_t *attr, int status);
|
||||
extern int thread_attr_getlongjmpstatus(thread_attr_t *attr, int *status);
|
||||
|
||||
/* thread_attr_destroy(): Destroy thread attribute object
|
||||
|
||||
When a thread attributes object is no longer required, it should be destroyed
|
||||
using the "pthread_attr_destroy()" function. Destroying a thread attributes
|
||||
object has no effect on threads that were created using that object.
|
||||
|
||||
Once a thread attributes object has been destroyed, it can be reinitialized
|
||||
using "thread_attr_init()". Any other use of a destroyed thread attributes
|
||||
object has undefined results.
|
||||
|
||||
@return
|
||||
* negative value if ATTR is NULL or uninitialized
|
||||
* 0 if success */
|
||||
extern int thread_attr_destroy(thread_attr_t *attr);
|
||||
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Signals interface
|
||||
//
|
||||
// A “signal” is a software interrupt delivered to a process. The operating
|
||||
// system uses signals to report exceptional situations to an executing
|
||||
// program. Some signals report errors such as references to invalid
|
||||
// memory addresses; others report asynchronous events, such as
|
||||
// disconnection of a phone line.
|
||||
//
|
||||
// If you anticipate an event that causes signals, you can define a handler
|
||||
// function and tell the operating system to run it when that particular
|
||||
// type of signal arrives.
|
||||
//
|
||||
// Finally, one thread can send a signal to another process; this allows a
|
||||
// parent thread to abort a child, or two related thread to communicate
|
||||
// and synchronize.
|
||||
//
|
||||
//---
|
||||
/* Define thread signal set type */
|
||||
typedef uint32_t sigset_t;
|
||||
|
||||
/* Define signals handler type */
|
||||
typedef void (*sighandler_t)(int);
|
||||
|
||||
/* Define fake signal functions. */
|
||||
#define SIG_ERR ((sighandler_t) -1) /* Error return. */
|
||||
#define SIG_DFL ((sighandler_t) 0) /* Default action. */
|
||||
#define SIG_IGN ((sighandler_t) 1) /* Ignore signal. */
|
||||
|
||||
/* Define the number of signals availbable */
|
||||
#define NSIG 19
|
||||
|
||||
/* Define all signum
|
||||
(note: All signals are not currently supported, but will be in near future)*/
|
||||
#define SIGKILL 0 /* (unblockable) Killed */
|
||||
#define SIGSTOP 1 /* (unblockable) Stop */
|
||||
#define SIGTERM 2 /* (unblockable) Termination request. */
|
||||
#define SIGCONT 3 /* Continue. */
|
||||
#define SIGTRAP 4 /* Trace/breakpoint trap. */
|
||||
#define SIGILL 5 /* Illegal instruction. */
|
||||
#define SIGTSTP 6 /* Keyboard stop. */
|
||||
#define SIGABRT 7 /* Abnormal termination. */
|
||||
#define SIGCHLD 8 /* Child terminated or stopped. */
|
||||
#define SIGPOLL 9 /* Pollable event occurred (System V). */
|
||||
#define SIGVTALRM 10 /* Virtual timer expired. */
|
||||
#define SIGPROF 11 /* Profiling timer expired. */
|
||||
#define SIGUSR1 12 /* User-defined signal 1. */
|
||||
#define SIGUSR2 13 /* User-defined signal 2. */
|
||||
#define SIGHUP 14 /* hang up. */
|
||||
#define SIGINT 15 /* interruption */
|
||||
#define SIGBUS 16 /* bus error */
|
||||
#define SIGFPE 17 /* fata aritmetic error */
|
||||
#define SIGSEGV 18 /* segmentation violation */
|
||||
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// User thread interface
|
||||
//---
|
||||
/* define special return value with the thread_create() function */
|
||||
#define THREAD_CREATE_LONGJMP_RETURN (0xd1ceca5e)
|
||||
|
||||
/* Define thread ID alias */
|
||||
typedef uint32_t thread_t;
|
||||
|
||||
/* cpu_ctx: whole SH3-based CPU hardware context definition */
|
||||
struct cpu_ctx {
|
||||
uint32_t reg[16];
|
||||
uint32_t gbr;
|
||||
uint32_t macl;
|
||||
uint32_t mach;
|
||||
uint32_t ssr;
|
||||
uint32_t spc;
|
||||
uint32_t pr;
|
||||
};
|
||||
|
||||
/* struct thread: Thread structure definition */
|
||||
struct thread {
|
||||
/* hardware context */
|
||||
struct cpu_ctx context;
|
||||
|
||||
/* thread configuration */
|
||||
struct thread_attr_s attr;
|
||||
|
||||
/* signals information */
|
||||
struct {
|
||||
sighandler_t handler[NSIG];
|
||||
sigset_t pending;
|
||||
sigset_t blocking;
|
||||
} signals;
|
||||
|
||||
/* thread scheduler information */
|
||||
struct {
|
||||
/* thread status */
|
||||
enum {
|
||||
THREAD_STATUS_PAUSED = 0,
|
||||
THREAD_STATUS_RUNNING = 1,
|
||||
THREAD_STATUS_ZOMBIE = 2,
|
||||
THREAD_STATUS_DEAD = 3
|
||||
} status;
|
||||
|
||||
/* thread identifier */
|
||||
thread_t id;
|
||||
|
||||
/* hierarchical information */
|
||||
struct thread *next;
|
||||
} scheduler;
|
||||
|
||||
/* private information */
|
||||
struct {
|
||||
uintptr_t stack; /* original stack address */
|
||||
uintptr_t ret; /* saved exit value */
|
||||
jmp_buf jmpbuf; /* longjmp feature */
|
||||
struct thread *sibling; /* sibling thread list */
|
||||
struct thread *child; /* child thread list */
|
||||
struct thread *parent; /* thread parent address */
|
||||
} private;
|
||||
};
|
||||
|
||||
/* thread_create(): Create a new thread
|
||||
|
||||
This function creates a new thread which starts execution by invoking
|
||||
"start_routine()"; You can passe many argument as you want and
|
||||
all args is passed as argument of start_routine().
|
||||
|
||||
The new thread terminates in one of the following ways:
|
||||
- It calls "thread_exit()", specifying an exit status value that is
|
||||
available to another thread in the same process that calls
|
||||
"thread_join()"
|
||||
- It returns from start_routine(). This is equivalent to calling
|
||||
"thread_exit()" with the value supplied in the return statement.
|
||||
- It is canceled (see "thread_cancel()").
|
||||
- If one thread with the special THREAD_ATTR_MAIN_THREAD die. In this case,
|
||||
all thread created with this special (custom) attribute will be killed
|
||||
|
||||
The "attr" argument points to a pthread_attr_t structure whose contents
|
||||
are used at thread creation time to determine attributes for the new thread;
|
||||
this structure is initialized using "thread_attr_init()" and related
|
||||
functions. If "attr" is NULL, then the thread is created with default
|
||||
attributes.
|
||||
|
||||
Before returning, a successful call to "thread_create()" stores the ID of
|
||||
the new thread in the buffer pointed to by "thread"; this identifier is used
|
||||
to refer to the thread in subsequent calls to other "thread_*" functions.
|
||||
|
||||
@return:
|
||||
* negative value if error occurs
|
||||
* 0 if success
|
||||
*/
|
||||
extern int thread_create(thread_t *thread,
|
||||
thread_attr_t *attr, void *start_routine, ...);
|
||||
|
||||
/* Makes sure an argument is always provided, for va_arg() and for definite the
|
||||
last arguments sended by the user */
|
||||
#define THREAD_CREATE_ARG_END_WATERMARK (0xdeb0cad0)
|
||||
#define thread_create(...) thread_create(__VA_ARGS__,\
|
||||
THREAD_CREATE_ARG_END_WATERMARK)
|
||||
|
||||
/* thread_join(): Waits for the thread specified by thread to terminate
|
||||
|
||||
This function waits for the thread specified by thread to terminate.
|
||||
If that thread has already terminated, then "thread_join()" returns
|
||||
immediately. The thread specified by THREAD must be joinable.
|
||||
|
||||
If RETVAL is not NULL, then "thread_join()" copies the exit status of the
|
||||
target thread (i.e., the value that the target thread supplied to
|
||||
"thread_exit()") into the location pointed to by RETVAL. If the target thread
|
||||
was canceled, then THREAD_CANCELED is placed in the location pointed to by
|
||||
RETVAL.
|
||||
|
||||
If multiple threads simultaneously try to join with the same thread, the
|
||||
results are undefined. If the thread calling "thread_join()" is canceled,
|
||||
then the target thread will remain joinable (i.e., it will not be detached).
|
||||
|
||||
The "thread_tryjoin()" function have the same behaviour that the
|
||||
"thread_join()" excet that it will not block the current thread and returns
|
||||
directly if the wanted thread is busy or unjoinable.
|
||||
|
||||
@return:
|
||||
* negative value if error occurs
|
||||
* 0 if success */
|
||||
extern int thread_join(thread_t thread, void **retvel);
|
||||
extern int thread_tryjoin(thread_t thread, void **retval);
|
||||
|
||||
/* thread_exit(): Terminate the calling thread
|
||||
|
||||
This function will terminates the calling thread and returns a value via
|
||||
retval that (if the thread is joinable) is available to another thread in the
|
||||
same process that calls "thread_join()".
|
||||
|
||||
Any clean-up handlers established by pthread_cleanup_push(3) that have not
|
||||
yet been popped, are popped (in the reverse of the order in which they were
|
||||
pushed) and executed. If the thread has any thread-specific data, then, after
|
||||
the clean-up handlers have been executed, the corresponding destructor
|
||||
functions are called, in an unspecified order.
|
||||
|
||||
When a thread terminates, process-shared resources (e.g., mutexes, condition
|
||||
variables, semaphores, and file descriptors) are not released. */
|
||||
extern void thread_exit(void *retval);
|
||||
|
||||
/* thread_kill(): Send signal to a thread
|
||||
|
||||
This fucntion will raise any signals to any thread. The value of the thread
|
||||
can have special value:
|
||||
* if thread is positive then signal is sent to the thread with the ID
|
||||
specified by thread.
|
||||
* if thread is equals 0, then signal is sent to threads which have the
|
||||
calling has parent.
|
||||
|
||||
@return:
|
||||
* negative value if error occurs
|
||||
* 0 if success */
|
||||
extern int thread_kill(thread_t thread, int sig);
|
||||
|
||||
/* thread_signal(): sets the disposition of the signal signum to handler, which
|
||||
is either SIG_IGN, SIG_DFL, or the address of a
|
||||
programmer-defined function (a "signal handler").
|
||||
|
||||
This part provides a simple interface for establishing an action for a
|
||||
particular signal.
|
||||
|
||||
If the signal signum is delivered to the process, then one of the following
|
||||
happens:
|
||||
- If the disposition is set to SIG_IGN, then the signal is ignored.
|
||||
- If the disposition is set to SIG_DFL, then the default action associated
|
||||
with the signal
|
||||
- If the disposition is set to a function, then first either the disposition
|
||||
is reset to SIG_DFL, or the signal is blocked, and then handler is called
|
||||
with argument signum. If invocation of the handler caused the signal to
|
||||
be blocked, then the signal is unblocked upon return from the handler.
|
||||
|
||||
The signals SIGKILL and SIGSTOP cannot be caught or ignored.
|
||||
|
||||
@return
|
||||
the previous value of the signal handler, or SIG_ERR on error. */
|
||||
extern void (*thread_signal(int signum, void (*handler)(int)))(int);
|
||||
|
||||
/* thread_terminate(): KERNEL-ONLY: destroy a thread
|
||||
|
||||
This function will destroy a thread regardless of its attributes, scheduler
|
||||
status, ... This function is used by the kernel to remove definitively a
|
||||
thread. */
|
||||
extern int thread_terminate(struct thread *thread);
|
||||
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Thread mutex interface
|
||||
//
|
||||
// To have better control of resources and how threads access them, Gint
|
||||
// implements a "mutex" object, which can help avoir race conditions and
|
||||
// other concurrency issues. The term mutex refers to mutual exclusion.
|
||||
//---
|
||||
/* define the thread wartermark, used to check if the mutex is valid */
|
||||
#define THREAD_MUTEX_WATERMARK 0xdeadbeef
|
||||
|
||||
/* mutex returnable value */
|
||||
enum
|
||||
{
|
||||
thread_mutex_retval_success = 0,
|
||||
thread_mutex_retval_busy = 1,
|
||||
thread_mutex_retval_error = 2,
|
||||
thread_mutex_retval_nomem = 3,
|
||||
thread_mutex_retval_timeout = 4
|
||||
};
|
||||
|
||||
/* mutex type */
|
||||
enum
|
||||
{
|
||||
thread_mutex_type_plain = (1 << 1), /* non-recursive */
|
||||
thread_mutex_type_recursive = (1 << 2), /* support recursive */
|
||||
thread_mutex_type_timed = (2 << 3) /* support timeout */
|
||||
};
|
||||
|
||||
/* define the fundamental data type and alias for thread mutex */
|
||||
struct thread_mutex_s
|
||||
{
|
||||
uint32_t watermark;
|
||||
uint32_t lock;
|
||||
uint8_t type;
|
||||
thread_t owner;
|
||||
struct {
|
||||
int id;
|
||||
int abord;
|
||||
} timer;
|
||||
};
|
||||
typedef struct thread_mutex_s thread_mutex_t;
|
||||
|
||||
|
||||
/* thread_mutex_init(): Creates a new mutex object with type TYPE.
|
||||
|
||||
This function will initialize the mutex MUTEX using the type TYPE. The type
|
||||
define the behaviour of the mutex, basically, you have:
|
||||
- thread_mutex_type_plain
|
||||
A mutex that does not support timeout, or test and return
|
||||
- thread_mutex_type_recursive
|
||||
A mutex that support recursive locking, which mean that the the owning
|
||||
thread can lock it more than once without causing deadlock
|
||||
- thread_mutex_type_timed
|
||||
A mutex that supports timeout
|
||||
|
||||
Not all combinations of mutex types are valid for the TYPE argument.
|
||||
Valid uses of mutex types for the TYPE argument are:
|
||||
- thread_mutex_type_plain
|
||||
A non-recursive mutex that does not support timeout
|
||||
- thread_mutex_type_timed
|
||||
A non-recursive mutex that does support timeout
|
||||
- thread_mutex_type_plain | thread_mutex_type_timed
|
||||
A recursive mutex that does not support timeout
|
||||
- thread_mutex_type_timed | thread_mutex_type_timed
|
||||
A recursive mutex that does support timeout
|
||||
|
||||
@return
|
||||
* thread_mutex_retval_success If successful initialized
|
||||
* thread_mutex_retval_error If error occur */
|
||||
extern int thread_mutex_init(thread_mutex_t *mutex, int type);
|
||||
|
||||
/* thread_mutex_lock(): Block the current thread until the mutex is unlocked.
|
||||
|
||||
This function will bock the calling thread until the mutex is locked.
|
||||
The behaviour is undefined if the current thread has already locked the
|
||||
mutex and the mutex is not recursive.
|
||||
|
||||
Prior calls to "thread_mutex_unlock()" to the same mutex syncronize-with
|
||||
this operations (if this operation success), and all lock/unlock operations
|
||||
on any given mutex form a single total order (similar to the modification
|
||||
order of an atomic).
|
||||
|
||||
@return:
|
||||
* thread_mutex_retval_success If lock was obtained
|
||||
* thread_mutex_retval_error If error occur */
|
||||
extern int thread_mutex_lock(thread_mutex_t *mutex);
|
||||
|
||||
/* thread_mutex_timedlock(): Block the current thread until the mutex is
|
||||
locked or until the time TIME has been reached.
|
||||
|
||||
This function will bock the calling thread until the mutex is locked or if
|
||||
the time TIME (millisecond) has been reached. If the current has been
|
||||
already locked the mutex and the mutex is not recursive, or if the mutex
|
||||
does not support timeout an error will be returned.
|
||||
|
||||
Prior calls to "thread_mutex_unlock()" to the same mutex syncronize-with
|
||||
this operations (if this operation success), and all lock/unlock operations
|
||||
on any given mutex form a single total order (similar to the modification
|
||||
order of an atomic).
|
||||
|
||||
@return:
|
||||
* thread_mutex_retval_success if the lock was obtained
|
||||
* thread_mutex_retval_error if error occur */
|
||||
extern int thread_mutex_timedlock(thread_mutex_t *mutex, uint64_t delay_us);
|
||||
|
||||
/* thread_mutex_trylock(): Try to lock the mutex without blocking.
|
||||
|
||||
This function tries to lock the mutex without blocking. It return
|
||||
immediately if the mutex is already locked.
|
||||
|
||||
Prior calls to "thread_mutex_unlock()" to the same mutex syncronize-with
|
||||
this operations (if this operation success), and all lock/unlock operations
|
||||
on any given mutex form a single total order (similar to the modification
|
||||
order of an atomic).
|
||||
|
||||
@return:
|
||||
* thread_mutex_retval_success if the lock was obtained
|
||||
* thread_mutex_retval_busy if the mutex is already locked
|
||||
* thread_mutex_retval_error if error occur */
|
||||
extern int thread_mutex_trylock(thread_mutex_t *mutex);
|
||||
|
||||
/* thread_mutex_unlock(): Unlocks the mutex
|
||||
|
||||
This function synchronize-with subsequent "thread_mutex_lock()",
|
||||
"thread_mutext_trylock()" and "thread_mutex_timedlock()" calls on the same
|
||||
mutex. All lock/unlock operations on any given mutex form a signle total
|
||||
order (similar to the modification order of an atomic).
|
||||
|
||||
@return:
|
||||
* thread_mutex_retval_success if unlocked
|
||||
* thread_mutex_retval_busy if not locked but need other unlock call
|
||||
* thread_mutex_retval_error if error occur */
|
||||
extern int thread_mutex_unlock(thread_mutex_t *mutex);
|
||||
|
||||
/* thread_mutex_destroy(): Destroy the mutex.
|
||||
|
||||
This function will destroy the mutex. If there are any thread waiting on the
|
||||
mutex, the behaviour is undefined. */
|
||||
extern void thread_mutex_destroy(thread_mutex_t *mutex);
|
||||
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Thread atomic operation
|
||||
//---
|
||||
/* thread_atomic_start(): Start atomic operation
|
||||
|
||||
This function will block interruptions and exception until
|
||||
"thread_atomic_stop()" is called. This is really useful when you need to
|
||||
secure some tricky part of code (like driver kernel-level implementation).
|
||||
|
||||
But be carefull: your code executed after this function SHOULD be
|
||||
EXCEPTION-SAFE ! Otherwise, a crash will occur and Gint can do nothing to
|
||||
avoid it because is hardware specific. If you need to secure shared data,
|
||||
use mutex instead.
|
||||
|
||||
This implementation is recursive-safe and will return:
|
||||
* SR value when you enter in "atomic" operation (first call)
|
||||
* 0 if you are already in a "atomic" operation (x call)
|
||||
To return to the "normal" operation, you should call "thread_atomic_stop()"
|
||||
as many time as you have involved with "thread_atomic_start()". */
|
||||
extern uint32_t thread_atomic_start(void);
|
||||
|
||||
/* thread_atomic_stop(): Stop atomic opration
|
||||
|
||||
This function will try to return to the "normal" mode and will return:
|
||||
* negative value If error occur
|
||||
* 0 If you are alwayrs in "atomic" mode
|
||||
* the restored SR value If you are returned to the "clasic" mode */
|
||||
extern uint32_t thread_atomic_stop(void);
|
||||
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Signals management (kernel-level)
|
||||
//---
|
||||
enum {
|
||||
thread_signals_deliver_retval_running = 0,
|
||||
thread_signals_deliver_retval_stopped = 1,
|
||||
thread_signals_deliver_retval_dead = 2,
|
||||
};
|
||||
|
||||
/* Macros for constructing status values. */
|
||||
#define __W_EXITCODE(ret, sig) ((ret) << 8 | (sig))
|
||||
#define __W_STOPCODE(sig) ((sig) << 8 | 0x7f)
|
||||
|
||||
/* thread_signals_raise(): Raise a signal
|
||||
|
||||
This function will raise a signal for a given thread. This function is used
|
||||
to raise kernel-based signals like SIGKILL. */
|
||||
extern int thread_signals_raise(struct thread *thread, int sig);
|
||||
|
||||
/* thread_signals_deliver_pending(): Deliver all pending signals
|
||||
|
||||
This function is KERNEL-ONLY and SHOULD NEVER be called because it is
|
||||
exclusively reserved for the internal thread scheduler. (see
|
||||
<gint/thread/scheduler.c> for more information). */
|
||||
extern int thread_signals_pending_deliver(struct thread *thread);
|
||||
|
||||
/* thread_signals_replace(): Replace current signal handler
|
||||
|
||||
This function will replace the signum signal handler with the new handler.
|
||||
This part is used by the "thread_kill()" function. */
|
||||
extern void (*thread_signals_replace(struct thread *thread,
|
||||
int signum, void (*handler)(int)))(int);
|
||||
|
||||
/* thread_signals_sigreturn(): KERNEL-ONLY: Signals return handler
|
||||
|
||||
This function is involved when an custom signals handler come to the end. It
|
||||
will restore previous context and stack then it will invalidate the current
|
||||
thread to force the scheudler to not save the current thread context; this
|
||||
mecanism will restore the previous context. */
|
||||
extern void thread_signals_sigreturn(void);
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Idle thread interface
|
||||
//---
|
||||
/* thread_idle_initialize(): Initialize the idle thread */
|
||||
extern void thread_idle_init(void);
|
||||
extern void thread_idle_uninit(void);
|
||||
|
||||
/* thread_idle_get(): Return the idle thread address */
|
||||
extern struct thread *thread_idle_get(void);
|
||||
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Scheduler interface (kernel-only)
|
||||
//---
|
||||
/* thread_sched_initialize(): Initialize the scheduler */
|
||||
extern void thread_sched_init(void);
|
||||
extern void thread_sched_uninit(void);
|
||||
|
||||
/* thread_sched_start() / thread_sched_stop: Control the scheduler timer */
|
||||
extern void thread_sched_start(void);
|
||||
extern void thread_sched_stop(void);
|
||||
|
||||
/* thread_sched_add(): thread_sched_remove(): handle internal thread queue */
|
||||
extern int thread_sched_add(struct thread *thread);
|
||||
extern int thread_sched_remove(struct thread *thread);
|
||||
|
||||
/* thread_sched_find(): Find a thread strcuture using his ID */
|
||||
extern struct thread *thread_sched_find(thread_t thread);
|
||||
|
||||
/* thread_sched_get_current(): Get the current thread context */
|
||||
extern struct thread *thread_sched_get_current(void);
|
||||
|
||||
/* thread_sched_get_counter(): Return the number of thread in the queue */
|
||||
extern int thread_sched_get_counter(void);
|
||||
|
||||
/* thread_sched_invalidate(): Invalidate the current thread.
|
||||
|
||||
This function will invalidate the current thread, it means that the current
|
||||
thread will not be saved on the next schedule. This is really useful for the
|
||||
signals' management to restore previously saved thread context.
|
||||
|
||||
You SHOULD never use it. */
|
||||
extern void thread_sched_invalidate(void);
|
||||
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Kernel thread interface
|
||||
//---
|
||||
|
||||
/* thread_kernel_terminate_trampoline(): Termination trampoline code.
|
||||
|
||||
This function is automatically involved when a thread return from his main
|
||||
procedure and will invoke the "thread_exit()" function with the returned
|
||||
value.
|
||||
|
||||
You SHOULD never use it. */
|
||||
extern void thread_kernel_terminate_trampoline(void);
|
||||
|
||||
/* thread_kernel_yield(): Cause the calling thread to relinquish the CPU */
|
||||
extern void thread_kernel_yield(void);
|
||||
|
||||
/* thread_kernel_exit(): Terminate the calling thread */
|
||||
extern void thread_kernel_exit(void *retval);
|
||||
|
||||
#endif /* GINT_THREAD */
|
|
@ -41,7 +41,7 @@
|
|||
* Set a specific ID in timer_setup(), in which case the delay is no longer
|
||||
interpreter as count of µs, but as a TCOR value.
|
||||
* If this ID is a TMU, you can further add (with + or |) a prescaler
|
||||
specification, one of TIMER_Po_{4,16,64,256}.
|
||||
specification, one of TIMER_Pphi_{4,16,64,256}.
|
||||
* Regardless of how the timer was obtained, you can use timer_reload() to
|
||||
replace the value of TCOR.
|
||||
* Also note that TMU0, TMU1, TMU2 and the ETMU have respective interrupt
|
||||
|
@ -59,7 +59,7 @@
|
|||
|
||||
Standard TMU can count at different speeds. A fast speed offers more
|
||||
precision but a slower speed offers longer delays. gint automatically
|
||||
selects suitable speed by default.
|
||||
selects suitable speeds by default.
|
||||
|
||||
If you want something very particular, you can add (with + or |) a prescaler
|
||||
value to a chosen ID in timer_setup() to request that specific value. The
|
||||
|
@ -186,6 +186,12 @@ void timer_stop(int timer);
|
|||
it may have only paused. If the timer never stops, you're in trouble. */
|
||||
void timer_wait(int timer);
|
||||
|
||||
/* timer_spinwait(): Actively wait for a timer to raise UNF
|
||||
Waits until the timer raises UNF, without sleeping. This is useful for
|
||||
delays in driver code that is run when interrupts are disabled. This relies
|
||||
neither on the interrupt signal nor on the UNIE flag. */
|
||||
void timer_spinwait(int timer);
|
||||
|
||||
//---
|
||||
// Low-level functions
|
||||
//---
|
||||
|
@ -230,4 +236,51 @@ void timer_reload(int timer, uint32_t delay);
|
|||
bytes) and increments it. */
|
||||
int timer_timeout(volatile void *arg);
|
||||
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Debugging interface
|
||||
//
|
||||
// @note
|
||||
// This function is used internally by the thread scheduler to precalculate
|
||||
// timer information needed to switch context quickly. This is a draft
|
||||
// interface that can be grandly improved.
|
||||
//---
|
||||
/* Type of hardware information that can be get using the timer debugging
|
||||
interface */
|
||||
struct timer_debug_info
|
||||
{
|
||||
/* context related information */
|
||||
struct {
|
||||
struct {
|
||||
void *tstr;
|
||||
void *tcor;
|
||||
void *tcnt;
|
||||
void *tcr;
|
||||
} address;
|
||||
struct {
|
||||
uint32_t tcor;
|
||||
uint32_t tcnt;
|
||||
uint16_t tcr;
|
||||
} context;
|
||||
} hardware;
|
||||
|
||||
/* interrupt related information */
|
||||
struct {
|
||||
struct {
|
||||
struct {
|
||||
uint16_t unf;
|
||||
uint16_t unie;
|
||||
} tcr;
|
||||
struct {
|
||||
uint8_t str;
|
||||
} tstr;
|
||||
} mask;
|
||||
int id;
|
||||
} interrupt;
|
||||
};
|
||||
/* timer_get_hw_info(): Get timer hardware information */
|
||||
extern int timer_debug_get_hw_info(int id, struct timer_debug_info *info);
|
||||
|
||||
#endif /* GINT_TIMER */
|
||||
|
|
|
@ -123,7 +123,7 @@ $(call src2obj,../src/font8x9.png): ../src/font8x9.png
|
|||
$(call cmd_m,fxconv,font8x9.png) fxconv -f $< -o $@ \
|
||||
--cg --toolchain=$(CONFIG.TOOLCHAIN) name:gint_font8x9 \
|
||||
charset:print grid.size:8x11 grid.padding:1 grid.border:0 \
|
||||
proportional:true
|
||||
proportional:true height:9
|
||||
|
||||
# Version symbol. ld generates a .stack section for unknown reasons; I remove
|
||||
# it in the linker script.
|
||||
|
|
|
@ -56,7 +56,9 @@ static void sh7705_probe(void)
|
|||
/* Deduce the frequency of the main clocks. This value is ckio/3 */
|
||||
int ckio_3 = 9830400;
|
||||
|
||||
/* Exchange the setting values 2 and 3 */
|
||||
/* Exchange the setting values 2 and 3 (corresponding to /3 and /4)
|
||||
This means that /1, /2, /4 are now 0, 1, 2, which is perfect for a
|
||||
quick bit shift */
|
||||
idiv = idiv ^ (idiv >> 1);
|
||||
pdiv = pdiv ^ (pdiv >> 1);
|
||||
|
||||
|
@ -87,8 +89,8 @@ static void sh7305_probe(void)
|
|||
if(CPG.FLLFRQ.SELXM == 1) fll >>= 1;
|
||||
freq.FLL = fll;
|
||||
|
||||
/* On SH7724, the divider ratio is given by 1 / (setting + 1), but
|
||||
SH7305 behaves as 1 / (2^setting + 1). */
|
||||
/* On SH7724, the divider ratio is given by 1 / (setting + 1), but on
|
||||
the SH7305 it is 1 / (2^setting + 1). */
|
||||
|
||||
int divb = CPG.FRQCRA.BFC;
|
||||
int divi = CPG.FRQCRA.IFC;
|
||||
|
|
BIN
src/font8x9.png
BIN
src/font8x9.png
Binary file not shown.
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
@ -56,6 +56,9 @@ static struct info {
|
|||
{ IPRK, 0xf000, IMR10, 0x04, IPRA, 0x000f },
|
||||
{ IPRK, 0xf000, IMR10, 0x02, IPRA, 0x000f },
|
||||
{ IPRK, 0xf000, IMR10, 0x01, IPRA, 0x000f },
|
||||
/* SPU */
|
||||
{ IPRC, 0x000f, IMR3, 0x04, _ /* Not supported on SH3! */ },
|
||||
{ IPRC, 0x000f, IMR4, 0x08, _ },
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -36,4 +36,31 @@ void cpu_setCPUOPM(uint32_t CPUOPM);
|
|||
/* cpu_getCPUOPM(): Get the CPU OperatioN Mode register */
|
||||
uint32_t cpu_getCPUOPM(void);
|
||||
|
||||
//---
|
||||
// Status Register
|
||||
//---
|
||||
|
||||
/* Status Register bits */
|
||||
typedef lword_union(sr_t,
|
||||
uint32_t :1;
|
||||
uint32_t MD :1;
|
||||
uint32_t RB :1;
|
||||
uint32_t BL :1;
|
||||
uint32_t RC :12;
|
||||
uint32_t :3;
|
||||
uint32_t DSP :1;
|
||||
uint32_t DMY :1;
|
||||
uint32_t DMX :1;
|
||||
uint32_t M :1;
|
||||
uint32_t Q :1;
|
||||
uint32_t IMASK :4;
|
||||
uint32_t RF :2;
|
||||
uint32_t S :1;
|
||||
uint32_t T :1;
|
||||
);
|
||||
|
||||
/* Get and set sr through the sr_t type */
|
||||
sr_t cpu_getSR(void);
|
||||
void cpu_setSR(sr_t sr);
|
||||
|
||||
#endif /* GINT_CORE_CPU */
|
||||
|
|
|
@ -5,21 +5,23 @@
|
|||
.global _cpu_setVBR
|
||||
.global _cpu_setCPUOPM
|
||||
.global _cpu_getCPUOPM
|
||||
.global _cpu_getSR
|
||||
.global _cpu_setSR
|
||||
|
||||
/* cpu_setVBR(): Change VBR address */
|
||||
|
||||
.section .gint.mapped, "ax"
|
||||
_cpu_setVBR_reloc:
|
||||
mov.l r8, @-r15
|
||||
mov.l r9, @-r15
|
||||
sts.l pr, @-r15
|
||||
stc.l sr, @-r15
|
||||
|
||||
/* Block all interrupts by setting IMASK=15 */
|
||||
mov #0xf, r1
|
||||
shll2 r1
|
||||
shll2 r1
|
||||
mov #0xf, r9
|
||||
shll2 r9
|
||||
shll2 r9
|
||||
stc sr, r0
|
||||
or r1, r0
|
||||
or r9, r0
|
||||
ldc r0, sr
|
||||
|
||||
/* Set the new VBR address */
|
||||
|
@ -30,10 +32,17 @@ _cpu_setVBR_reloc:
|
|||
jsr @r5
|
||||
mov r6, r4
|
||||
|
||||
/* Enable interrupts again by restoring the status register */
|
||||
ldc.l @r15+, sr
|
||||
lds.l @r15+, pr
|
||||
/* Enable interrupts again */
|
||||
stc sr, r0
|
||||
not r9, r9
|
||||
and r9, r0
|
||||
ldc r0, sr
|
||||
|
||||
/* Return the previous VBR address */
|
||||
mov r8, r0
|
||||
|
||||
lds.l @r15+, pr
|
||||
mov.l @r15+, r9
|
||||
rts
|
||||
mov.l @r15+, r8
|
||||
|
||||
|
@ -68,5 +77,16 @@ _cpu_getCPUOPM:
|
|||
mov.l @r0, r0
|
||||
|
||||
.align 4
|
||||
|
||||
1: .long 0xff2f0000
|
||||
|
||||
/* cpu_getSR(): Get status register */
|
||||
_cpu_getSR:
|
||||
stc sr, r0
|
||||
rts
|
||||
nop
|
||||
|
||||
/* cpu_setSR(): Set status register */
|
||||
_cpu_setSR:
|
||||
ldc r4, sr
|
||||
rts
|
||||
nop
|
||||
|
|
|
@ -62,18 +62,38 @@ _gint_inth_7305:
|
|||
1: .long 0xff000028
|
||||
#endif
|
||||
|
||||
/* SH7305-TYPE INTERRUPT HANDLER ENTRY - 40 BYTES */
|
||||
/* SH7305-TYPE INTERRUPT HANDLER ENTRY - 54 BYTES (2 blocks in total) */
|
||||
|
||||
_gint_inth_7305:
|
||||
/* First, we need to check the thread scheduler.
|
||||
Due to the interrupt handler architecture, we cannot store this part
|
||||
here. So, we need to jump into the scheduler's checking procedure,
|
||||
but without modify the current user context.
|
||||
|
||||
@note
|
||||
PR will be updated when "jsr" instruction is executed, R15 will be
|
||||
lost if we push something into it ; So, let's send them using
|
||||
non-conventional method. (see <gint/thread/scheduler.S> for more
|
||||
information)
|
||||
|
||||
On SH4 processor, a special register named SGR save the value of R15
|
||||
when interrupts / exceptions occur, but the SH3 doesn't have it, this
|
||||
is why we save the stack value before using it. */
|
||||
mov r15, r1
|
||||
sts pr, r0
|
||||
mov.l r0, @-r15
|
||||
mov.l 1f, r2
|
||||
jsr @r2
|
||||
nop
|
||||
|
||||
/* Save caller-saved registers which might currently be in use by the
|
||||
interrupted function, as we don't know what the callback will do */
|
||||
sts.l pr, @-r15
|
||||
stc.l gbr, @-r15
|
||||
sts.l mach, @-r15
|
||||
sts.l macl, @-r15
|
||||
|
||||
/* Get the event code from the INTEVT register */
|
||||
mov.l 1f, r0
|
||||
mov.l 2f, r0
|
||||
mov.l @r0, r0
|
||||
|
||||
/* Interrupt codes start at 0x400 */
|
||||
|
@ -96,8 +116,15 @@ _gint_inth_7305:
|
|||
rte
|
||||
nop
|
||||
|
||||
.zero 24
|
||||
1: .long 0xff000028
|
||||
/* force 4-alignement */
|
||||
.zero 2
|
||||
|
||||
/* information */
|
||||
1: .long _thread_kernel_sched_interrupt_procedure
|
||||
2: .long 0xff000028
|
||||
|
||||
/* block padding */
|
||||
.zero 8
|
||||
.first_entry:
|
||||
|
||||
#ifdef FX9860G
|
||||
|
|
|
@ -16,12 +16,17 @@
|
|||
|
||||
static void kinit_cpu(void);
|
||||
|
||||
/* Forcefully pull in the INTC driver which gint cannot run without */
|
||||
extern gint_driver_t drv_intc;
|
||||
GUNUSED gint_driver_t *gint_required_intc = &drv_intc;
|
||||
|
||||
//---
|
||||
// Context for the CPU and registers not directly managed by a driver
|
||||
//---
|
||||
|
||||
typedef struct
|
||||
{
|
||||
sr_t SR;
|
||||
uint32_t VBR;
|
||||
uint32_t CPUOPM;
|
||||
} ctx_t;
|
||||
|
@ -31,11 +36,19 @@ GBSS static ctx_t sys_ctx, gint_ctx;
|
|||
|
||||
static void ctx_save(ctx_t *ctx)
|
||||
{
|
||||
if(isSH4()) ctx->CPUOPM = cpu_getCPUOPM();
|
||||
if(isSH4())
|
||||
{
|
||||
ctx->CPUOPM = cpu_getCPUOPM();
|
||||
ctx->SR = cpu_getSR();
|
||||
}
|
||||
}
|
||||
static void ctx_restore(ctx_t *ctx)
|
||||
{
|
||||
if(isSH4()) cpu_setCPUOPM(ctx->CPUOPM);
|
||||
if(isSH4())
|
||||
{
|
||||
cpu_setCPUOPM(ctx->CPUOPM);
|
||||
cpu_setSR(ctx->SR);
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -58,9 +71,8 @@ static void drivers_save_and_init(GUNUSED int zero)
|
|||
|
||||
for driver_asc(d)
|
||||
{
|
||||
if(d->ctx_save) d->ctx_save(d->sys_ctx);
|
||||
|
||||
if(isSH3() && d->driver_sh3) d->driver_sh3();
|
||||
if(d->ctx_save) d->ctx_save(d->sys_ctx);
|
||||
if(d->init) d->init();
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +112,15 @@ static void drivers_switch(int who)
|
|||
|
||||
static void kinit_cpu(void)
|
||||
{
|
||||
if(isSH4()) cpu_setCPUOPM(cpu_getCPUOPM() | 0x00000008);
|
||||
if(isSH4())
|
||||
{
|
||||
cpu_setCPUOPM(cpu_getCPUOPM() | 0x00000008);
|
||||
|
||||
/* Enable DSP mode on the CPU */
|
||||
sr_t SR = cpu_getSR();
|
||||
SR.DSP = 1;
|
||||
cpu_setSR(SR);
|
||||
}
|
||||
}
|
||||
|
||||
/* kinit(): Install and start gint */
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
#include <gint/gint.h>
|
||||
#include <gint/hardware.h>
|
||||
#include <gint/exc.h>
|
||||
#include <gint/thread.h>
|
||||
|
||||
#include <gint/display.h>
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
|
@ -61,7 +64,7 @@ static void regcpy(uint32_t * restrict l, int32_t s, uint32_t * restrict r)
|
|||
s -= 16;
|
||||
}
|
||||
}
|
||||
#define regcpy(l, s, r) regcpy(l, (int32_t)s, r)
|
||||
#define regcpy(l, s, r) regcpy(l, (intptr_t)s, r)
|
||||
|
||||
/* regclr(): Clear a memory region using symbol information
|
||||
@r Source pointer (base address)
|
||||
|
@ -77,7 +80,7 @@ static void regclr(uint32_t *r, int32_t s)
|
|||
s -= 16;
|
||||
}
|
||||
}
|
||||
#define regclr(r, s) regclr(r, (int32_t)s)
|
||||
#define regclr(r, s) regclr(r, (intptr_t)s)
|
||||
|
||||
/* callarray(): Call an array of functions (constructors or destructors)
|
||||
@f First element of array
|
||||
|
@ -113,16 +116,43 @@ int start(int isappli, int optnum)
|
|||
/* Detect hardware; this will mainly tell SH3 from SH4 on fx-9860G */
|
||||
hw_detect();
|
||||
|
||||
/* The dynamic TLB mechanism for old SH3-based fx-9860G (basically OS
|
||||
1.00 and the fx-9860G emulator) is not clear yet, so gint can't load
|
||||
pages dynamically. Load everything preventively (works only if the
|
||||
add-in is small enough) */
|
||||
#ifdef FX9860G
|
||||
if(isSH3())
|
||||
{
|
||||
/* Try to map every ROM address up to _srom */
|
||||
volatile uint8_t *x = (void *)0x00300000;
|
||||
uint32_t loaded = 0;
|
||||
|
||||
while(loaded < (uint32_t)&srom)
|
||||
{
|
||||
GUNUSED volatile uint8_t y = *x;
|
||||
loaded += 1024;
|
||||
x += 1024;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Load data sections and wipe the bss section. This has to be done
|
||||
first for static and global variables to be initialized */
|
||||
regcpy(&ldata, &sdata, &rdata);
|
||||
regcpy(&lilram, &silram, &rilram);
|
||||
regcpy(&lxram, &sxram, &rxram);
|
||||
regcpy(&lyram, &syram, &ryram);
|
||||
regclr(&rbss, &sbss);
|
||||
|
||||
/* Do not load data to ILRAM, XRAM or YRAM on SH3 - the areas don't
|
||||
exist. If you use them, you're responsible! */
|
||||
if(!isSH3())
|
||||
{
|
||||
regcpy(&lilram, &silram, &rilram);
|
||||
regcpy(&lxram, &sxram, &rxram);
|
||||
regcpy(&lyram, &syram, &ryram);
|
||||
}
|
||||
|
||||
#if 0
|
||||
#ifdef FX9860G
|
||||
/* Copy permanentely-mapped code to start of user RAM (on fx-CG 50 it
|
||||
/* Copy permanently-mapped code to start of user RAM (on fx-CG 50 it
|
||||
is loaded along ILRAM contents) */
|
||||
void *rgmapped = mmu_uram();
|
||||
regcpy(&lgmapped, &sgmapped, rgmapped);
|
||||
|
@ -134,6 +164,7 @@ int start(int isappli, int optnum)
|
|||
fixups[i] += (uint32_t)rgmapped;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Install gint, switch VBR and initialize drivers */
|
||||
kinit();
|
||||
|
@ -149,15 +180,36 @@ int start(int isappli, int optnum)
|
|||
|
||||
callarray(&bctors, &ectors);
|
||||
|
||||
int rc = 1;
|
||||
while(1)
|
||||
{
|
||||
/* "Pre-main" loop.
|
||||
We're trying to run the main function using a thread, if the thread
|
||||
creation fail or if we return from the thread creation using the
|
||||
longjmp() feature (see <gint/thread.h> for more information), we will
|
||||
use the "classic" way to do the job.
|
||||
|
||||
@note
|
||||
When we return from the "thread_create()" function with the longjmp()
|
||||
feature, we are always in a thread execution (the scheduler always
|
||||
performs context switch) */
|
||||
int rc = -1;
|
||||
thread_t thread;
|
||||
thread_attr_t attr;
|
||||
thread_attr_init(&attr);
|
||||
thread_attr_setdetachstate(&attr, THREAD_ATTR_DETACHED);
|
||||
thread_attr_enablelongjmp(&attr, THREAD_ATTR_LONGJMP_ENABLE);
|
||||
int retval = thread_create(&thread, &attr, &main, isappli, optnum);
|
||||
if (retval == 0)
|
||||
thread_join(thread, (void**)&rc);
|
||||
if (retval != 0 && (uint32_t)retval != THREAD_CREATE_LONGJMP_RETURN)
|
||||
rc = main(isappli, optnum);
|
||||
if(!gint_restart) break;
|
||||
|
||||
/* main loop */
|
||||
while (gint_restart == 0) {
|
||||
gint_osmenu();
|
||||
rc = main(isappli, optnum);
|
||||
}
|
||||
|
||||
callarray(&bdtors, &edtors);
|
||||
|
||||
/* Before leaving the application, we need to clean everything we
|
||||
changed to hardware settings and peripheral modules. The OS is bound
|
||||
to be confused (and hang, or crash, or any other kind of giving up)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
//---
|
||||
// gint:core:mmu - MMU-related definitions
|
||||
// gint:mmu:mmu - MMU driver definition and context management
|
||||
//---
|
||||
|
||||
#include <gint/mmu.h>
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/hardware.h>
|
||||
|
||||
//---
|
||||
|
@ -148,3 +149,54 @@ uint32_t utlb_translate(uint32_t page)
|
|||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//---
|
||||
// Initialization
|
||||
//---
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
/* Make writes to the control register area synchronous; this is needed
|
||||
for the SPU to operate properly */
|
||||
if(isSH4()) SH7305_MMU.PASCR.UBC = 1;
|
||||
}
|
||||
|
||||
//---
|
||||
// Context management
|
||||
//---
|
||||
|
||||
typedef struct {
|
||||
uint32_t PASCR;
|
||||
uint32_t IRMCR;
|
||||
} ctx_t;
|
||||
|
||||
GBSS static ctx_t sys_ctx, gint_ctx;
|
||||
|
||||
static void ctx_save(void *buf)
|
||||
{
|
||||
if(isSH3()) return;
|
||||
|
||||
ctx_t *ctx = buf;
|
||||
ctx->PASCR = SH7305_MMU.PASCR.lword;
|
||||
ctx->IRMCR = SH7305_MMU.IRMCR.lword;
|
||||
}
|
||||
|
||||
static void ctx_restore(void *buf)
|
||||
{
|
||||
if(isSH3()) return;
|
||||
|
||||
ctx_t *ctx = buf;
|
||||
SH7305_MMU.PASCR.lword = ctx->PASCR;
|
||||
SH7305_MMU.IRMCR.lword = ctx->IRMCR;
|
||||
}
|
||||
|
||||
gint_driver_t drv_mmu = {
|
||||
.name = "MMU",
|
||||
.init = init,
|
||||
.sys_ctx = &sys_ctx,
|
||||
.gint_ctx = &gint_ctx,
|
||||
.ctx_save = ctx_save,
|
||||
.ctx_restore = ctx_restore,
|
||||
};
|
||||
|
||||
GINT_DECLARE_DRIVER(1, drv_mmu);
|
|
@ -61,9 +61,8 @@ static void topti_render(int x, int y, char const *str_char, font_t const *f,
|
|||
/* Move to top row */
|
||||
uint16_t *target = gint_vram + 396 * y;
|
||||
|
||||
/* Character spacing and space waiting to be drawn */
|
||||
int space = 1;
|
||||
int active_space = 0;
|
||||
/* Character spacing waiting to be drawn, in pixels */
|
||||
int space = 0;
|
||||
|
||||
/* Read each character from the input string */
|
||||
while(1)
|
||||
|
@ -74,29 +73,23 @@ static void topti_render(int x, int y, char const *str_char, font_t const *f,
|
|||
int glyph = topti_glyph_index(f, code_point);
|
||||
if(glyph < 0) continue;
|
||||
|
||||
/* Draw the space if background is opaque */
|
||||
int prop_space = (code_point == ' ' && f->prop) ? 5 : 0;
|
||||
if(active_space || prop_space)
|
||||
{
|
||||
active_space += prop_space;
|
||||
if(bg >= 0) drect(x, y, x + active_space - 1,
|
||||
y + height - 1, bg);
|
||||
}
|
||||
x += active_space;
|
||||
if(prop_space) { active_space = space; continue; }
|
||||
int dataw = f->prop ? f->glyph_width[glyph] : f->width;
|
||||
|
||||
/* Draw character spacing if background is opaque */
|
||||
if(space && bg >= 0) drect(x, y, x+space-1, y+height-1, bg);
|
||||
x += space;
|
||||
if(x >= 396) break;
|
||||
|
||||
int index = topti_offset(f, glyph);
|
||||
|
||||
/* Compute horizontal intersection between glyph and screen */
|
||||
|
||||
int dataw = f->prop ? f->glyph_width[glyph] : f->width;
|
||||
int width = dataw, left = 0;
|
||||
|
||||
if(x + dataw <= 0)
|
||||
{
|
||||
x += dataw;
|
||||
active_space = space;
|
||||
space = f->char_spacing;
|
||||
continue;
|
||||
}
|
||||
if(x < 0) left = -x, width += x;
|
||||
|
@ -108,7 +101,7 @@ static void topti_render(int x, int y, char const *str_char, font_t const *f,
|
|||
dataw, fg, bg);
|
||||
|
||||
x += dataw;
|
||||
active_space = space;
|
||||
space = f->char_spacing;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,10 +114,10 @@ void dtext_opt(int x, int y, int fg, int bg, int halign, int valign,
|
|||
int w, h;
|
||||
dsize(str, topti_font, &w, &h);
|
||||
|
||||
if(halign == DTEXT_RIGHT) x -= w;
|
||||
if(halign == DTEXT_CENTER) x -= ((w+1) >> 1);
|
||||
if(valign == DTEXT_BOTTOM) y -= h;
|
||||
if(valign == DTEXT_MIDDLE) y -= ((h+1) >> 1);
|
||||
if(halign == DTEXT_RIGHT) x -= w - 1;
|
||||
if(halign == DTEXT_CENTER) x -= (w >> 1);
|
||||
if(valign == DTEXT_BOTTOM) y -= h - 1;
|
||||
if(valign == DTEXT_MIDDLE) y -= (h >> 1);
|
||||
}
|
||||
|
||||
topti_render(x, y, str, topti_font, fg, bg);
|
||||
|
|
|
@ -291,24 +291,3 @@ int bopti_clip(bopti_image_t const *img, struct rbox *r)
|
|||
/* Return non-zero if the result is empty */
|
||||
return (width <= 0 || height <= 0);
|
||||
}
|
||||
|
||||
void bopti_render_noclip(bopti_image_t const *img, struct rbox *r,
|
||||
uint32_t *v1, uint32_t *v2)
|
||||
{
|
||||
int left = r->left;
|
||||
|
||||
/* Start column and end column (both included) */
|
||||
r->left >>= 5;
|
||||
|
||||
if(r->columns == 1 && (r->visual_x & 31) + r->width <= 32)
|
||||
{
|
||||
r->x = (left & 31) - (r->visual_x & 31);
|
||||
bopti_render_scsp(img, r, v1, v2);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* x-coordinate of the first pixel of the first column */
|
||||
r->x = r->visual_x - (left & 31);
|
||||
bopti_render(img, r, v1, v2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ void gint_dvline(int y1, int y2, int x, int color)
|
|||
if((uint)x >= 128) return;
|
||||
if(y1 > y2) swap(y1, y2);
|
||||
if(y1 >= 64 || y2 < 0) return;
|
||||
if(y1 < 0) y1 = 0;
|
||||
if(y2 >= 64) y2 = 63;
|
||||
|
||||
uint32_t *base = gint_vram + (y1 << 2) + (x >> 5);
|
||||
uint32_t *lword = base + ((y2 - y1 + 1) << 2);
|
||||
|
|
|
@ -30,18 +30,24 @@ void masks(int x1, int x2, uint32_t *masks);
|
|||
@rbox Rendering box */
|
||||
int bopti_clip(bopti_image_t const *img, struct rbox *rbox);
|
||||
|
||||
/* bopti_render_noclip(): Render a bopti image without clipping
|
||||
This function is only ever slightly faster than bopti_render_clip(),
|
||||
eliminating two types of coordinate checks:
|
||||
1. The bounding box does not overflow from the image
|
||||
2. The final rendering does not overflow from the screen
|
||||
/* bopti_render(): Render a bopti image
|
||||
Copies an image into the VRAM. This function does not perform clipping;
|
||||
use bopti_clip() on the rbox before calling it if needed.
|
||||
|
||||
@x @y Location of the top-left corner
|
||||
@img Image encoded by [fxconv]
|
||||
@left @top @w @h Bounding box to render
|
||||
@v1 @v2 VRAMs (gray rendering is used if v2 != NULL) */
|
||||
void bopti_render_noclip(bopti_image_t const *img, struct rbox *rbox,
|
||||
uint32_t *v1, uint32_t *v2);
|
||||
@img Image encoded by [fxconv]
|
||||
@rbox Rendering box (may or may not be clipped)
|
||||
@v1 @v2 VRAMs (gray rendering is used if v2 != NULL) */
|
||||
void bopti_render(bopti_image_t const *img, struct rbox *rbox, uint32_t *v1,
|
||||
uint32_t *v2);
|
||||
|
||||
/* bopti_render_scsp(): Single-column single-position image renderer
|
||||
This function is a specialized version of bopti_render() that can be used
|
||||
when only a single column of the source image is used (all pixels to be
|
||||
rendered are in a single 32-aligned 32-wide pixel column of the source) and
|
||||
a single position of the VRAM is used (all pixels to be rendered end up in a
|
||||
single 32-aligned 32-wide pixel column of the VRAM). */
|
||||
void bopti_render_scsp(bopti_image_t const *img, struct rbox *rbox,
|
||||
uint32_t *v1, uint32_t *v2);
|
||||
|
||||
//---
|
||||
// Alternate rendering modes
|
||||
|
|
|
@ -133,7 +133,7 @@ void topti_render(int x, int y, char const *str_char, font_t const *f,
|
|||
free = topti_split(data+index, width, height, free, operators);
|
||||
|
||||
/* Potential space after the glyph */
|
||||
int space = (*str != 0);
|
||||
int space = (*str != 0) ? f->char_spacing : 0;
|
||||
free -= space;
|
||||
|
||||
if(free > 0) continue;
|
||||
|
@ -186,10 +186,10 @@ void dtext_opt(int x, int y, int fg, int bg, int halign, int valign,
|
|||
int w, h;
|
||||
dsize(str, topti_font, &w, &h);
|
||||
|
||||
if(halign == DTEXT_RIGHT) x -= w;
|
||||
if(halign == DTEXT_CENTER) x -= ((w+1) >> 1);
|
||||
if(valign == DTEXT_BOTTOM) y -= h;
|
||||
if(valign == DTEXT_MIDDLE) y -= ((h+1) >> 1);
|
||||
if(halign == DTEXT_RIGHT) x -= w - 1;
|
||||
if(halign == DTEXT_CENTER) x -= (w >> 1);
|
||||
if(valign == DTEXT_BOTTOM) y -= h - 1;
|
||||
if(valign == DTEXT_MIDDLE) y -= (h >> 1);
|
||||
}
|
||||
|
||||
topti_render(x, y, str, topti_font, topti_asm_text[fg],
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
|
||||
#include "../render/render.h"
|
||||
|
||||
/* TODO: These parameters will eventually be specified by the font */
|
||||
#define CHAR_SPACING 1
|
||||
#define PROP_SPACING 5
|
||||
|
||||
/* dfont(): Set the default font for text rendering */
|
||||
font_t const *dfont(font_t const * font)
|
||||
{
|
||||
|
@ -105,39 +101,34 @@ uint32_t topti_utf8_next(uint8_t const **str_pointer)
|
|||
void dsize(char const *str_char, font_t const * f, int *w, int *h)
|
||||
{
|
||||
uint8_t const *str = (void *)str_char;
|
||||
uint32_t code_point;
|
||||
|
||||
if(!f) f = topti_font;
|
||||
if(h) *h = f->line_height;
|
||||
if(!w) return;
|
||||
|
||||
/* Width for monospaced fonts is easy, unfortunately we still need to
|
||||
compute the length of [str]. Critical applications might do the
|
||||
product themselves to avoid this cost. */
|
||||
compute the length and group bytes into Unicode code points. */
|
||||
if(!f->prop)
|
||||
{
|
||||
int length = 0;
|
||||
while(*str++) length++;
|
||||
*w = (f->width + CHAR_SPACING) * length - CHAR_SPACING;
|
||||
while((code_point = topti_utf8_next(&str))) length++;
|
||||
|
||||
*w = (f->width + f->char_spacing) * length - f->char_spacing;
|
||||
return;
|
||||
}
|
||||
|
||||
/* For proportional fonts, fetch the width of each individual glyphs */
|
||||
int width = 0;
|
||||
uint32_t code_point;
|
||||
|
||||
while(1)
|
||||
{
|
||||
code_point = topti_utf8_next(&str);
|
||||
if(!code_point) break;
|
||||
|
||||
if(code_point == ' ')
|
||||
{
|
||||
width += PROP_SPACING + CHAR_SPACING;
|
||||
continue;
|
||||
}
|
||||
|
||||
int glyph = topti_glyph_index(f, code_point);
|
||||
if(glyph >= 0) width += f->glyph_width[glyph] + CHAR_SPACING;
|
||||
if(glyph < 0) continue;
|
||||
width += f->glyph_width[glyph] + f->char_spacing;
|
||||
}
|
||||
*w = width - CHAR_SPACING;
|
||||
*w = width - f->char_spacing;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
#include <gint/mpu/spu.h>
|
||||
#include <gint/mpu/cpg.h>
|
||||
#include <gint/mpu/power.h>
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/clock.h>
|
||||
#include <gint/intc.h>
|
||||
|
||||
#define SPU SH7305_SPU
|
||||
#define DSP0 SH7305_DSP0
|
||||
#define DSP1 SH7305_DSP1
|
||||
#define CPG SH7305_CPG
|
||||
#define POWER SH7305_POWER
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
/* Block SPU interrupts from DSP0, DSP1, and their DMA */
|
||||
intc_priority(INTC_SPU_DSP0, 0);
|
||||
intc_priority(INTC_SPU_DSP1, 0);
|
||||
|
||||
/* Stop both the SPU and FSI clocks */
|
||||
CPG.FSICLKCR.lword = 0x00000103;
|
||||
CPG.SPUCLKCR.lword = 0x00000100;
|
||||
/* Enable the FSI clock, then the SPU/SPURAM clock */
|
||||
CPG.FSICLKCR.CLKSTP = 0;
|
||||
CPG.SPUCLKCR.CLKSTP = 0;
|
||||
|
||||
/* Power the clocks through MSTPCR2 */
|
||||
POWER.MSTPCR2.FSI_SPU = 0;
|
||||
|
||||
/* Reset the SPU */
|
||||
SPU.SPUSRST.RST = 0;
|
||||
sleep_us_spin(1000);
|
||||
SPU.SPUSRST.RST = 1;
|
||||
sleep_us_spin(1000);
|
||||
|
||||
/* Initially give all P memory and X memory to DSP0 */
|
||||
SPU.PBANKC0 = 0x1f;
|
||||
SPU.PBANKC1 = 0x00;
|
||||
SPU.XBANKC0 = 0x7f;
|
||||
SPU.XBANKC1 = 0x00;
|
||||
|
||||
/* Perform full DSP resets */
|
||||
DSP0.DSPCORERST = 1;
|
||||
DSP1.DSPCORERST = 1;
|
||||
DSP0.DSPRST = 0;
|
||||
DSP1.DSPRST = 0;
|
||||
sleep_us_spin(1000);
|
||||
}
|
||||
|
||||
int spu_zero(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---
|
||||
// Hardware context
|
||||
//---
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t PBANKC0, PBANKC1;
|
||||
uint32_t XBANKC0, XBANKC1;
|
||||
} ctx_t;
|
||||
|
||||
GBSS static ctx_t sys_ctx, gint_ctx;
|
||||
|
||||
static void ctx_save(void *buf)
|
||||
{
|
||||
ctx_t *ctx = buf;
|
||||
ctx->PBANKC0 = SPU.PBANKC0;
|
||||
ctx->PBANKC1 = SPU.PBANKC1;
|
||||
ctx->XBANKC0 = SPU.XBANKC0;
|
||||
ctx->XBANKC1 = SPU.XBANKC1;
|
||||
}
|
||||
|
||||
static void ctx_restore(void *buf)
|
||||
{
|
||||
ctx_t *ctx = buf;
|
||||
SPU.PBANKC0 = ctx->PBANKC0;
|
||||
SPU.PBANKC1 = ctx->PBANKC1;
|
||||
SPU.XBANKC0 = ctx->XBANKC0;
|
||||
SPU.XBANKC1 = ctx->XBANKC1;
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver structure definition
|
||||
//---
|
||||
|
||||
gint_driver_t drv_spu = {
|
||||
.name = "SPU",
|
||||
.init = init,
|
||||
.sys_ctx = &sys_ctx,
|
||||
.gint_ctx = &gint_ctx,
|
||||
.ctx_save = ctx_save,
|
||||
.ctx_restore = ctx_restore,
|
||||
};
|
||||
|
||||
GINT_DECLARE_DRIVER(3, drv_spu);
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
** gint:std:setjmp - performing a nonlocal goto
|
||||
*/
|
||||
|
||||
.section .gint.mapped, "ax"
|
||||
.align 4
|
||||
|
||||
|
||||
.global _setjmp
|
||||
.type _setjmp, @function
|
||||
|
||||
/* setjmp(): saves various information about the calling environment */
|
||||
_setjmp:
|
||||
/* block interrupts / exceptions (this part should b exception-safe) */
|
||||
stc sr, r1
|
||||
mov r1, r3
|
||||
mov #0x10, r2
|
||||
shll8 r2
|
||||
shll16 r2
|
||||
or r2, r1
|
||||
ldc r1, sr
|
||||
|
||||
/* save current environment */
|
||||
add #52, r4
|
||||
sts.l pr, @-r4
|
||||
sts.l mach, @-r4
|
||||
sts.l macl, @-r4
|
||||
stc.l gbr, @-r4
|
||||
mov.l r15, @-r4
|
||||
mov.l r14, @-r4
|
||||
mov.l r13, @-r4
|
||||
mov.l r12, @-r4
|
||||
mov.l r11, @-r4
|
||||
mov.l r10, @-r4
|
||||
mov.l r9, @-r4
|
||||
mov.l r8, @-r4
|
||||
mov.l r3, @-r4 ! previous SR register status
|
||||
|
||||
/* restore sr then exit */
|
||||
ldc r3, sr
|
||||
rts
|
||||
mov #0, r0
|
||||
|
||||
|
||||
|
||||
|
||||
.global _longjmp
|
||||
.type _longjmp, @function
|
||||
|
||||
/* longjmp(): restore the saved environment */
|
||||
_longjmp:
|
||||
/* block interrupt */
|
||||
stc sr, r1
|
||||
mov #0x10, r2
|
||||
shll8 r2
|
||||
shll16 r2
|
||||
or r2, r1
|
||||
ldc r1, sr
|
||||
|
||||
/* check arg validity and save it into unbankable register to avoid
|
||||
error when the "old" SR register is restored */
|
||||
tst r5, r5
|
||||
mov r4, r8
|
||||
bf/s env_switch
|
||||
mov r5, r9
|
||||
mov #1, r9
|
||||
|
||||
env_switch:
|
||||
/* load the old SR regsiter first to force register bank switch (if
|
||||
occur) then move the context and the returned value into non-saved
|
||||
(by the setjmp context) registers. */
|
||||
ldc.l @r8+, sr
|
||||
mov r8, r4
|
||||
mov r9, r0
|
||||
|
||||
/* restore all old registers */
|
||||
mov.l @r4+, r8
|
||||
mov.l @r4+, r9
|
||||
mov.l @r4+, r10
|
||||
mov.l @r4+, r11
|
||||
mov.l @r4+, r12
|
||||
mov.l @r4+, r13
|
||||
mov.l @r4+, r14
|
||||
mov.l @r4+, r15
|
||||
ldc.l @r4+, gbr
|
||||
lds.l @r4+, macl
|
||||
lds.l @r4+, mach
|
||||
lds.l @r4+, pr
|
||||
|
||||
rts
|
||||
nop
|
|
@ -10,5 +10,5 @@ void srand(unsigned int seed)
|
|||
|
||||
int rand(void)
|
||||
{
|
||||
return tinymt32_generate_uint32(&random);
|
||||
return tinymt32_generate_uint32(&random) & 0x7fffffff;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
** gint:thread:atomic - Thread atomic helper
|
||||
*/
|
||||
|
||||
.section .gint.mapped, "ax"
|
||||
.align 4
|
||||
|
||||
.global _thread_atomic_start
|
||||
.type _thread_atomic_start, @function
|
||||
|
||||
/* thread_atomic_start(): Mask interrupts and exceptions */
|
||||
_thread_atomic_start:
|
||||
/* Check if the user is currently into an atomic state and update
|
||||
atomic counter. */
|
||||
mov.l thread_atomic_counter, r1
|
||||
mov.l @r1, r2
|
||||
tst r2, r2
|
||||
add #1, r2
|
||||
mov.l r2, @r1
|
||||
bf/s atomic_start_exit
|
||||
mov #-1, r0
|
||||
|
||||
/* Mask interruptions / exceptions using the IMASK and BL filed of SR */
|
||||
stc sr, r1
|
||||
mov r1, r0
|
||||
mov.l sr_mask, r2
|
||||
or r2, r1
|
||||
ldc r1, sr
|
||||
|
||||
/* Save "old" SR register. */
|
||||
mov.l thread_atomic_sr_save, r1
|
||||
mov.l r0, @r1
|
||||
|
||||
atomic_start_exit:
|
||||
rts
|
||||
nop
|
||||
|
||||
|
||||
.global _thread_atomic_stop
|
||||
.type _thread_atomic_stop, @function
|
||||
|
||||
/* thread_atomic_stop(): Unmask (if possible) interrupts / exceptions signals */
|
||||
_thread_atomic_stop:
|
||||
/* Check if the device is currently into an atomic operation, then
|
||||
update the counter and, if needed, restore the SR register. */
|
||||
mov.l thread_atomic_counter, r1
|
||||
mov.l @r1, r0
|
||||
tst r0, r0
|
||||
bt atomic_end_error
|
||||
cmp/eq #1, r0
|
||||
add #-1, r0
|
||||
mov.l r0, @r1
|
||||
bf/s atomic_end_exit
|
||||
mov #0, r0
|
||||
|
||||
/* Restore saved SR register data. */
|
||||
mov.l thread_atomic_sr_save, r1
|
||||
mov.l @r1, r0
|
||||
ldc r0, sr
|
||||
bra atomic_end_exit
|
||||
nop
|
||||
|
||||
atomic_end_error:
|
||||
mov #-1, r0
|
||||
|
||||
atomic_end_exit:
|
||||
rts
|
||||
nop
|
||||
|
||||
.align 4
|
||||
thread_atomic_counter: .long _thread_atomic_counter
|
||||
thread_atomic_sr_save: .long _thread_atomic_sr_save
|
||||
sr_mask: .long 0x000000f0
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Global symbols
|
||||
*/
|
||||
.global _thread_atomic_sr_save
|
||||
.type _thread_atomic_sr_save, @object
|
||||
.global _thread_atomic_counter
|
||||
.type _thread_atomic_counter, @object
|
||||
|
||||
.align 4
|
||||
_thread_atomic_sr_save: .long 0x00000000
|
||||
_thread_atomic_counter: .long 0x00000000
|
|
@ -0,0 +1,98 @@
|
|||
//---
|
||||
// gint:thread:attributes - Thread attribute helper
|
||||
//---
|
||||
#include <gint/thread.h>
|
||||
#include <gint/std/string.h>
|
||||
|
||||
//---
|
||||
// User interface
|
||||
//---
|
||||
/* thread_attr_init(): Initialize thread attribute object */
|
||||
int thread_attr_init(thread_attr_t *attr)
|
||||
{
|
||||
if (attr == NULL)
|
||||
return (-1);
|
||||
thread_atomic_start();
|
||||
memset(attr, 0x00, sizeof(struct thread_attr_s));
|
||||
attr->state.detach = THREAD_ATTR_JOINABLE;
|
||||
attr->state.longjmp = THREAD_ATTR_LONGJMP_DISABLE;
|
||||
attr->private. watermark = THREAD_ATTR_WATERMARK;
|
||||
thread_atomic_stop();
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* thread_attr_setdetachstate() and thread_attr_getdetachstate()
|
||||
|
||||
The "thread_attr_setdetachstate()" function sets the detach state attribute
|
||||
of the thread attributes object referred to by attr to the value specified
|
||||
in detachstate.
|
||||
|
||||
The "thread_attr_getdetachstate()" returns the detach state attribute of
|
||||
the thread attributes object attr in the buffer pointed to by detachstate.
|
||||
*/
|
||||
int thread_attr_setdetachstate(thread_attr_t *attr, int detachstate)
|
||||
{
|
||||
if (attr == NULL || attr->private.watermark != THREAD_ATTR_WATERMARK)
|
||||
return (-1);
|
||||
switch (detachstate) {
|
||||
case THREAD_ATTR_JOINABLE:
|
||||
case THREAD_ATTR_DETACHED:
|
||||
break;
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
thread_atomic_start();
|
||||
attr->state.detach = detachstate;
|
||||
thread_atomic_stop();
|
||||
return (0);
|
||||
}
|
||||
int thread_attr_getdetachstate(thread_attr_t *attr, int *detachstate)
|
||||
{
|
||||
if (attr == NULL || attr->private.watermark != THREAD_ATTR_WATERMARK)
|
||||
return (-1);
|
||||
thread_atomic_start();
|
||||
*detachstate = attr->state.detach;
|
||||
thread_atomic_stop();
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/* thread_attr_enablelongjmp() thread_attr_getlongjmpstatus()
|
||||
Enable / disable the custom longjmp feature */
|
||||
int thread_attr_enablelongjmp(thread_attr_t *attr, int status)
|
||||
{
|
||||
if (attr == NULL || attr->private.watermark != THREAD_ATTR_WATERMARK)
|
||||
return (-1);
|
||||
switch (status) {
|
||||
case THREAD_ATTR_LONGJMP_ENABLE:
|
||||
case THREAD_ATTR_LONGJMP_DISABLE:
|
||||
break;
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
thread_atomic_start();
|
||||
attr->state.longjmp = status;
|
||||
thread_atomic_stop();
|
||||
return (0);
|
||||
}
|
||||
int thread_attr_getlongjmpstatus(thread_attr_t *attr, int *status)
|
||||
{
|
||||
if (attr == NULL || attr->private.watermark != THREAD_ATTR_WATERMARK)
|
||||
return (-1);
|
||||
thread_atomic_start();
|
||||
*status = attr->state.longjmp;
|
||||
thread_atomic_stop();
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* thread_attr_destroy(): Destroy thread attribute object */
|
||||
int thread_attr_destroy(thread_attr_t *attr)
|
||||
{
|
||||
if (attr == NULL || attr->private.watermark != THREAD_ATTR_WATERMARK)
|
||||
return (-1);
|
||||
|
||||
thread_atomic_start();
|
||||
memset(attr, 0x00, sizeof(struct thread_attr_s));
|
||||
thread_atomic_stop();
|
||||
return (0);
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
//---
|
||||
// gint:thread:idle - Idle thread interface
|
||||
//---
|
||||
#include <gint/thread.h>
|
||||
#include <gint/std/string.h>
|
||||
|
||||
/* define private symbols */
|
||||
static struct thread thread_idle;
|
||||
static uint32_t thread_idle_stack[THREAD_IDLE_STACK_SIZE];
|
||||
|
||||
//---
|
||||
// Internal
|
||||
//---
|
||||
/* thread_idle_code(): Code executed by the idle thread */
|
||||
static void thread_idle_code(void)
|
||||
{
|
||||
while (1) {
|
||||
__asm__ volatile ("sleep");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Module primitive
|
||||
//---
|
||||
/* thread_idle_init(): Initialize the idle thread */
|
||||
void thread_idle_init(void)
|
||||
{
|
||||
memset(&thread_idle, 0x00, sizeof(struct thread));
|
||||
thread_idle.context.reg[15] = (uintptr_t)thread_idle_stack;
|
||||
thread_idle.context.reg[15] += (THREAD_IDLE_STACK_SIZE << 2);
|
||||
thread_idle.context.spc = (uintptr_t)&thread_idle_code;
|
||||
thread_idle.context.pr = (uintptr_t)0xa0000000;
|
||||
}
|
||||
void thread_idle_uninit(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// User interface
|
||||
//---
|
||||
/* thread_idle_get(): Return the idle thread */
|
||||
struct thread *thread_idle_get(void)
|
||||
{
|
||||
return (&thread_idle);
|
||||
}
|
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
** gint:thread:kernel - Thread Kernel inferface
|
||||
** This file contains all "kernel" low-level part of the thread module:
|
||||
** - thread_kernel_sched_procedure() Thread scheduler procedure
|
||||
** - thread_kernel_terminate_trampoline() Thread terminsation trampoline
|
||||
** - thread_kernel_yield() Relinquish the CPU
|
||||
** - thread_kernel_exit() Terminate the current thread
|
||||
*/
|
||||
|
||||
.section .gint.mapped, "ax"
|
||||
.align 4
|
||||
|
||||
.global _thread_kernel_sched_interrupt_procedure
|
||||
.type _thread_kernel_sched_interrupt_procedure, @function
|
||||
|
||||
/* thread_kernel_sched_procedure(): Scheduler entry
|
||||
|
||||
This "function" is involved when ANY interruption occur, this is the only way
|
||||
to add this scheduler without broke all Gint's VBR software architecture.
|
||||
|
||||
The scheduler use a ~128hz timer to schedule thread contexts. During the
|
||||
"driver" initialization (yes, the thread module is considered like a driver
|
||||
for Gint's eyes) one timer has been locked and all hardware information about
|
||||
it has been pre-calculated during this part.
|
||||
|
||||
@note
|
||||
Due to the current Gint's VBR architecture, this function is involved each
|
||||
time an interruption occur. By the fact that this function is involved like
|
||||
a common subroutine, PR register has been saved into the R0 register and the
|
||||
R15 register has been saved into the R1 register.
|
||||
|
||||
We are always with the "privileged" bank register configuration. So, we only
|
||||
can use r0~r7 register until the current thread context has been saved.
|
||||
|
||||
When involved, R0 content the PR register snapshot when interrupts occur,
|
||||
same for the R1 register which content the stack snapshot. (Yes, I know that
|
||||
the SH4-based MPU provide the SGR register which saves the stack snapshot
|
||||
when interrupts / exceptions occur, but we need to be SH3 compatible) */
|
||||
_thread_kernel_sched_interrupt_procedure:
|
||||
/* First, check if the interruption is the scheduler's timer */
|
||||
mov.l intevt_register, r2
|
||||
mov.l thread_sched_tmu_interrupt_id, r3
|
||||
mov.l @r2, r2
|
||||
mov.l @r3, r3
|
||||
cmp/eq r2, r3
|
||||
bt _thread_kernel_sched_entry
|
||||
rts
|
||||
nop
|
||||
|
||||
_thread_kernel_sched_entry:
|
||||
/* Stop the scheduler's timer to "avoid" counting the context
|
||||
switching time. */
|
||||
/* The scheduler can use ETMU instead of a classical TMU. ETMU have
|
||||
slow writting interraction, but have the same access size that the
|
||||
TMU, this is why we use slow operation here without checking the
|
||||
timer type. */
|
||||
mov.l thread_sched_tmu_tstr_addr, r2
|
||||
mov.l thread_sched_tmu_tstr_mask, r3
|
||||
mov.l @r2, r2
|
||||
mov.l @r3, r3
|
||||
mov.b @r2, r6
|
||||
not r3, r3
|
||||
and r3, r6
|
||||
timer_tstr_slow_stop:
|
||||
mov.b r6, @r2
|
||||
mov.b @r2, r7
|
||||
cmp/eq r6, r7
|
||||
bf timer_tstr_slow_stop
|
||||
|
||||
save_context:
|
||||
/* check if a current thread is running */
|
||||
mov.l thread_sched_current, r2
|
||||
mov.l @r2, r2
|
||||
tst r2, r2
|
||||
bt/s schedule_thread
|
||||
|
||||
/* save current thread context */
|
||||
add #88, r2
|
||||
mov.l r0, @-r2 /* R0 contains user PR register snapshot */
|
||||
stc.l spc, @-r2
|
||||
stc.l ssr, @-r2
|
||||
sts.l mach, @-r2
|
||||
sts.l macl, @-r2
|
||||
stc.l gbr, @-r2
|
||||
mov.l r1, @-r2 /* R1 contains user R15 register snapshot */
|
||||
mov.l r14, @-r2
|
||||
mov.l r13, @-r2
|
||||
mov.l r12, @-r2
|
||||
mov.l r11, @-r2
|
||||
mov.l r10, @-r2
|
||||
mov.l r9, @-r2
|
||||
mov.l r8, @-r2
|
||||
stc.l R7_BANK, @-r2
|
||||
stc.l R6_BANK, @-r2
|
||||
stc.l R5_BANK, @-r2
|
||||
stc.l R4_BANK, @-r2
|
||||
stc.l R3_BANK, @-r2
|
||||
stc.l R2_BANK, @-r2
|
||||
stc.l R1_BANK, @-r2
|
||||
stc.l R0_BANK, @-r2
|
||||
|
||||
schedule_thread:
|
||||
/* Call high-level abraction
|
||||
|
||||
We need to save the R1/R0 registers (that contains the user stack/PR
|
||||
register snapshot respectively) because if the scheduler fail, we
|
||||
SHOULD be able to restore the context before returning from here.
|
||||
|
||||
We alse need to swtich the stack to avoid undefined behaviour if the
|
||||
current thread is destroyed during the schedule (we currently always
|
||||
use the thread stack here) */
|
||||
mov.l thread_kernel_stack, r15
|
||||
mov.l r1, @-r15
|
||||
mov.l r0, @-r15
|
||||
mov.l thread_schedule, r0
|
||||
jsr @r0
|
||||
nop
|
||||
#ifdef THREAD_SCHEDULER_DEBUG
|
||||
mov.l thread_schedule_debug, r1
|
||||
jsr @r1
|
||||
mov r0, r4
|
||||
#endif
|
||||
tst r0, r0
|
||||
lds.l @r15+, pr
|
||||
bt/s scheduler_restart_timer
|
||||
mov.l @r15+, r15
|
||||
|
||||
|
||||
context_restore:
|
||||
/* update_current_thread */
|
||||
mov.l thread_sched_current, r1
|
||||
mov.l r0, @r1
|
||||
|
||||
/* restore the new context */
|
||||
ldc.l @r0+, R0_BANK
|
||||
ldc.l @r0+, R1_BANK
|
||||
ldc.l @r0+, R2_BANK
|
||||
ldc.l @r0+, R3_BANK
|
||||
ldc.l @r0+, R4_BANK
|
||||
ldc.l @r0+, R5_BANK
|
||||
ldc.l @r0+, R6_BANK
|
||||
ldc.l @r0+, R7_BANK
|
||||
mov.l @r0+, r8
|
||||
mov.l @r0+, r9
|
||||
mov.l @r0+, r10
|
||||
mov.l @r0+, r11
|
||||
mov.l @r0+, r12
|
||||
mov.l @r0+, r13
|
||||
mov.l @r0+, r14
|
||||
mov.l @r0+, r15
|
||||
ldc.l @r0+, gbr
|
||||
lds.l @r0+, macl
|
||||
lds.l @r0+, mach
|
||||
ldc.l @r0+, ssr
|
||||
ldc.l @r0+, spc
|
||||
lds.l @r0+, pr
|
||||
|
||||
scheduler_restart_timer:
|
||||
/* Check if the scheduler uses TMU or ETMU */
|
||||
mov.l thread_sched_gint_timer_id, r0
|
||||
mov.l @r0, r0
|
||||
mov #3, r1
|
||||
cmp/ge r1, r0
|
||||
|
||||
/* Get scheduler timer pre-calculated information */
|
||||
mov.l thread_sched_tmu_tcr_addr, r0
|
||||
mov.l thread_sched_tmu_tcr_mask, r1
|
||||
mov.l @r0, r0
|
||||
mov.l @r1, r1
|
||||
not r1, r1
|
||||
mov.l thread_sched_tmu_tcor_addr, r2
|
||||
mov.l thread_sched_tmu_tcnt_addr, r3
|
||||
mov.l @r2, r2
|
||||
mov.l @r3, r3
|
||||
mov.l thread_sched_tmu_tstr_addr, r4
|
||||
mov.l thread_sched_tmu_tstr_mask, r5
|
||||
mov.l @r4, r4
|
||||
bf/s tmu_restart_timer
|
||||
mov.l @r5, r5
|
||||
|
||||
etmu_restart_timer:
|
||||
/* clear interrupt flag (ETMU.TCR.UNF = 0) */
|
||||
mov.b @r0, r6
|
||||
and r1, r6
|
||||
etmu_tcr_slow_unf_clear:
|
||||
mov.b r6, @r0
|
||||
mov.b @r0, r7
|
||||
cmp/eq r6, r7
|
||||
bf etmu_tcr_slow_unf_clear
|
||||
|
||||
/* reload the timer counter (ETMU.TCNT = ETMU.TCOR) */
|
||||
mov.l @r2, r6
|
||||
etmu_slow_reload:
|
||||
mov.l r6, @r3
|
||||
mov.l @r3, r7
|
||||
cmp/eq r6, r7
|
||||
bf etmu_slow_reload
|
||||
|
||||
/* Check if we really need to restart the timer or not */
|
||||
mov.l thread_sched_counter, r0
|
||||
mov.l @r0, r0
|
||||
mov #2, r1
|
||||
cmp/ge r1, r0
|
||||
bf process_switch
|
||||
|
||||
/* Restart timer (ETMU.TSTR = 1) */
|
||||
mov.b @r4, r6
|
||||
or r5, r6
|
||||
etmu_tstr_slow_restart:
|
||||
mov.b r6, @r4
|
||||
mov.b @r4, r7
|
||||
cmp/eq r6, r7
|
||||
bf etmu_tstr_slow_restart
|
||||
bra process_switch
|
||||
nop
|
||||
|
||||
tmu_restart_timer:
|
||||
/* clear interrupt flag (TMU.TCR.UNF) */
|
||||
mov.w @r0, r6
|
||||
and r1, r6
|
||||
mov.w r6, @r0
|
||||
|
||||
/* reload the timer counter */
|
||||
mov.l @r2, r6
|
||||
mov.l r6, @r3
|
||||
|
||||
/* Check if we really need to restart the timer or not */
|
||||
mov.l thread_sched_counter, r0
|
||||
mov.l @r0, r0
|
||||
mov #2, r1
|
||||
cmp/ge r1, r0
|
||||
bf process_switch
|
||||
|
||||
/* Restart timer (TMU.TSTR) */
|
||||
mov.b @r4, r6
|
||||
or r5, r6
|
||||
mov.b r6, @r4
|
||||
|
||||
process_switch:
|
||||
rte
|
||||
nop
|
||||
|
||||
.align 4
|
||||
/* kernel information */
|
||||
thread_kernel_stack: .long _thread_kernel_stack_start
|
||||
|
||||
/* scheduler global information */
|
||||
thread_sched_current: .long _thread_sched_current
|
||||
thread_sched_counter: .long _thread_sched_counter
|
||||
|
||||
/* High-level functions */
|
||||
thread_schedule: .long _thread_schedule
|
||||
#ifdef THREAD_SCHEDULER_DEBUG
|
||||
thread_schedule_debug: .long _thread_schedule_debug
|
||||
#endif
|
||||
|
||||
/* Timer hardware pre-calculated information */
|
||||
thread_sched_gint_timer_id: .long _thread_sched_gint_timer_id
|
||||
thread_sched_tmu_interrupt_id: .long _thread_sched_tmu_interrupt_id
|
||||
thread_sched_tmu_tcr_addr: .long _thread_sched_tmu_tcr_addr
|
||||
thread_sched_tmu_tcr_mask: .long _thread_sched_tmu_tcr_mask
|
||||
thread_sched_tmu_tstr_mask: .long _thread_sched_tmu_tstr_mask
|
||||
thread_sched_tmu_tstr_addr: .long _thread_sched_tmu_tstr_addr
|
||||
thread_sched_tmu_tcor_addr: .long _thread_sched_tmu_tcor_addr
|
||||
thread_sched_tmu_tcnt_addr: .long _thread_sched_tmu_tcnt_addr
|
||||
|
||||
/* Other information */
|
||||
intevt_register: .long 0xff000028
|
||||
|
||||
|
||||
|
||||
|
||||
.global _thread_kernel_yield
|
||||
.type _thread_kernel_yield, @function
|
||||
|
||||
/* thread_kernel_yield(): Cause the calling thread to relinquish the CPU
|
||||
|
||||
This function will move the calling thread to the end of the queue for its
|
||||
static priority and new thread gets on run. */
|
||||
/* TODO preemption ? */
|
||||
_thread_kernel_yield:
|
||||
/* start atomic operation + bank switch*/
|
||||
stc sr, r0
|
||||
mov r0, r1
|
||||
mov #0x30, r2
|
||||
shll8 r2
|
||||
shll16 r2
|
||||
or r2, r1
|
||||
ldc r1, sr
|
||||
|
||||
/* prepare bank switch */
|
||||
stc R0_BANK, r0
|
||||
sts pr, r1
|
||||
ldc r0, ssr
|
||||
ldc r1, spc
|
||||
|
||||
/* simulate the interrupt by switching the register bank */
|
||||
mov #0x20, r1
|
||||
shll8 r1
|
||||
shll16 r1
|
||||
or r1, r0
|
||||
ldc r0, sr
|
||||
|
||||
/* prepare to jump into thread_kernel_scheudler_entry() */
|
||||
xor r0, r0
|
||||
mov r15, r1
|
||||
mov.l 1f, r2
|
||||
jsr @r2
|
||||
nop
|
||||
|
||||
pouet:
|
||||
sleep
|
||||
bra pouet
|
||||
nop
|
||||
|
||||
|
||||
|
||||
.global _thread_kernel_terminate_trampoline
|
||||
.type _thread_kernel_terminate_trampoline, @function
|
||||
|
||||
/* thread_kernel_terminate_trampoline()
|
||||
call the thread_exit function with the returned value.*/
|
||||
_thread_kernel_terminate_trampoline:
|
||||
mov.l 2f, r1
|
||||
jmp @r1
|
||||
mov r0, r4
|
||||
|
||||
.align 4
|
||||
1: .long _thread_kernel_sched_entry
|
||||
2: .long _thread_exit
|
|
@ -0,0 +1,238 @@
|
|||
//---
|
||||
// gint:thread:mutex - Thread mutex API
|
||||
//---
|
||||
#include <gint/thread.h>
|
||||
#include <gint/timer.h>
|
||||
#include <gint/std/string.h>
|
||||
|
||||
//---
|
||||
// Internal functions
|
||||
//---
|
||||
/* thread_mutex_timer_callback()
|
||||
|
||||
Involved when a "thread_timedlock()" timeout is reached. This function will
|
||||
request to abort the locking try. */
|
||||
static int thread_mutex_timer_callback(thread_mutex_t *mutex)
|
||||
{
|
||||
thread_atomic_start();
|
||||
mutex->timer.abord = 1;
|
||||
thread_atomic_stop();
|
||||
return (TIMER_STOP);
|
||||
}
|
||||
|
||||
/* thread_mutex_check_validity(): Check the validity of the mutex
|
||||
|
||||
This function will check the watermark and the thread owner. It returns the
|
||||
associated thread owner on success, NULL otherwise.
|
||||
|
||||
This function SHOUL be involved in "atomic" mode. */
|
||||
static struct thread *thread_mutex_validity_check(thread_mutex_t *mutex)
|
||||
{
|
||||
struct thread *thread;
|
||||
|
||||
if (mutex == NULL || mutex->watermark != THREAD_MUTEX_WATERMARK)
|
||||
return (NULL);
|
||||
thread = thread_sched_get_current();
|
||||
if (thread == NULL)
|
||||
return (NULL);
|
||||
return (thread);
|
||||
}
|
||||
|
||||
//---
|
||||
// Thread mutex API
|
||||
//---
|
||||
|
||||
/* thread_mutex_init(): Creates a new mutex object with type TYPE */
|
||||
int thread_mutex_init(thread_mutex_t *mutex, int type)
|
||||
{
|
||||
thread_atomic_start();
|
||||
if (mutex->watermark == THREAD_MUTEX_WATERMARK) {
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_success);
|
||||
}
|
||||
switch (type) {
|
||||
case thread_mutex_type_plain:
|
||||
case thread_mutex_type_timed:
|
||||
case thread_mutex_type_timed | thread_mutex_type_recursive:
|
||||
case thread_mutex_type_plain | thread_mutex_type_recursive:
|
||||
break;
|
||||
default:
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_error);
|
||||
}
|
||||
memset(mutex, 0x00, sizeof(thread_mutex_t));
|
||||
mutex->watermark = THREAD_MUTEX_WATERMARK;
|
||||
mutex->type = type;
|
||||
mutex->lock = 0;
|
||||
mutex->timer.id = -1;
|
||||
mutex->timer.abord = 0;
|
||||
mutex->owner = -1;
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_success);
|
||||
}
|
||||
|
||||
/* thread_mutex_lock(): Block the current thread until the mutex is locked */
|
||||
int thread_mutex_lock(thread_mutex_t *mutex)
|
||||
{
|
||||
struct thread *thread;
|
||||
|
||||
/* Check error */
|
||||
thread_atomic_start();
|
||||
thread = thread_mutex_validity_check(mutex);
|
||||
if (thread == NULL) {
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_error);
|
||||
}
|
||||
|
||||
/* Check if the mutex is recursive */
|
||||
if ((mutex->type & thread_mutex_type_recursive) != 0) {
|
||||
if (mutex->owner == thread->scheduler.id) {
|
||||
mutex->lock = mutex->lock + 1;
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_success);
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait util the mutex is locked */
|
||||
while (1) {
|
||||
/* Check if the mutex is unlocked */
|
||||
if (mutex->lock == 0) {
|
||||
mutex->owner = thread->scheduler.id;
|
||||
mutex->lock = 1;
|
||||
break;
|
||||
};
|
||||
|
||||
/* Force schedule */
|
||||
thread_kernel_yield();
|
||||
}
|
||||
|
||||
/* Lock and return */
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_success);
|
||||
}
|
||||
|
||||
int thread_mutex_timedlock(thread_mutex_t *mutex, uint64_t delay_us)
|
||||
{
|
||||
struct thread *thread;
|
||||
int retval;
|
||||
|
||||
/* Check error */
|
||||
thread_atomic_start();
|
||||
thread = thread_mutex_validity_check(mutex);
|
||||
if (thread == NULL || (mutex->type & thread_mutex_type_timed) == 0) {
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_error);
|
||||
}
|
||||
|
||||
/* Check if the mutex is recursive */
|
||||
if (mutex->type & thread_mutex_type_recursive) {
|
||||
if (mutex->owner == thread->scheduler.id) {
|
||||
mutex->lock = mutex->lock + 1;
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_success);
|
||||
}
|
||||
}
|
||||
|
||||
/* setup the timer */
|
||||
mutex->timer.abord = 0;
|
||||
mutex->timer.id = timer_setup(TIMER_ANY, delay_us,
|
||||
(void*)&thread_mutex_timer_callback, mutex);
|
||||
if (mutex->timer.id < 0) {
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_error);
|
||||
}
|
||||
|
||||
/* Wait util the mutex is unlocked or timeout */
|
||||
timer_start(mutex->timer.id);
|
||||
while (1) {
|
||||
/* Check timeout abord */
|
||||
if (mutex->timer.abord == 1) {
|
||||
retval = thread_mutex_retval_busy;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if the mutex is unlocked */
|
||||
if (mutex->lock == 0) {
|
||||
mutex->lock = 1;
|
||||
mutex->owner = thread->scheduler.id;
|
||||
retval = thread_mutex_retval_success;
|
||||
break;
|
||||
};
|
||||
|
||||
/* Force schedule */
|
||||
thread_kernel_yield();
|
||||
}
|
||||
|
||||
/* destroy the timer and return */
|
||||
timer_stop(mutex->timer.id);
|
||||
mutex->timer.id = -1;
|
||||
mutex->timer.abord = -1;
|
||||
thread_atomic_stop();
|
||||
return (retval);
|
||||
}
|
||||
|
||||
/* thread_mutext_trylock(): Try to lock the mutex without blocking. */
|
||||
int thread_mutex_trylock(thread_mutex_t *mutex)
|
||||
{
|
||||
struct thread *thread;
|
||||
|
||||
/* Check error */
|
||||
thread_atomic_start();
|
||||
thread = thread_mutex_validity_check(mutex);
|
||||
if (thread == NULL) {
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_error);
|
||||
}
|
||||
|
||||
/* Check recursive lock */
|
||||
if (mutex->type & thread_mutex_type_recursive) {
|
||||
if (mutex->owner == thread->scheduler.id) {
|
||||
mutex->lock = mutex->lock + 1;
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_success);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the mutex is unlocked */
|
||||
if (mutex->lock == 0) {
|
||||
mutex->owner = thread->scheduler.id;
|
||||
mutex->lock = 1;
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_success);
|
||||
};
|
||||
|
||||
/* the mutex is busy */
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_busy);
|
||||
}
|
||||
|
||||
/* thread_mutex_unlock(): Try to unlock the mutex */
|
||||
int thread_mutex_unlock(thread_mutex_t *mutex)
|
||||
{
|
||||
struct thread *thread;
|
||||
|
||||
thread_atomic_start();
|
||||
thread = thread_mutex_validity_check(mutex);
|
||||
if (thread == NULL
|
||||
|| mutex->owner != thread->scheduler.id
|
||||
|| mutex->lock == 0) {
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_error);
|
||||
}
|
||||
mutex->lock = mutex->lock - 1;
|
||||
thread_atomic_stop();
|
||||
return (thread_mutex_retval_success);
|
||||
}
|
||||
|
||||
/* thread_mutex_destroy(): Destroy the mutex object */
|
||||
void thread_mutex_destroy(thread_mutex_t *mutex)
|
||||
{
|
||||
thread_atomic_start();
|
||||
mutex->watermark = 0x00000000;
|
||||
mutex->type = 0;
|
||||
mutex->owner = -2;
|
||||
mutex->lock = 0;
|
||||
if (mutex->timer.id >= 0)
|
||||
timer_stop(mutex->timer.id);
|
||||
thread_atomic_stop();
|
||||
}
|
|
@ -0,0 +1,327 @@
|
|||
//---
|
||||
// gint:thread:scheduler - Scheduler module
|
||||
//---
|
||||
#include <gint/thread.h>
|
||||
#include <gint/timer.h>
|
||||
#include <gint/std/stdlib.h>
|
||||
|
||||
#include <gint/display.h>
|
||||
|
||||
|
||||
/* define symbols that will be used bu the kernel to communicate with us */
|
||||
struct thread *thread_sched_queue;
|
||||
struct thread *thread_sched_current;
|
||||
uint32_t thread_sched_counter;
|
||||
thread_t thread_sched_uuid;
|
||||
|
||||
/* define symbols used to pre-calculate scheudler timer related information */
|
||||
uint32_t thread_sched_tmu_interrupt_id;
|
||||
uint32_t thread_sched_tmu_tcr_addr;
|
||||
uint32_t thread_sched_tmu_tcr_mask;
|
||||
uint32_t thread_sched_tmu_tstr_mask;
|
||||
uint32_t thread_sched_tmu_tstr_addr;
|
||||
uint32_t thread_sched_tmu_tcor_addr;
|
||||
uint32_t thread_sched_tmu_tcnt_addr;
|
||||
int thread_sched_gint_timer_id;
|
||||
|
||||
|
||||
//---
|
||||
// Driver primitives
|
||||
//---
|
||||
|
||||
/* thread_sched_init(): Initialize the scheduler */
|
||||
void thread_sched_init(void)
|
||||
{
|
||||
struct timer_debug_info info;
|
||||
uint64_t delay;
|
||||
|
||||
/* initialize kernel information */
|
||||
thread_sched_queue = NULL;
|
||||
thread_sched_current = NULL;
|
||||
thread_sched_counter = 0;
|
||||
thread_sched_uuid = 0;
|
||||
|
||||
/* generate the delay in us */
|
||||
delay = 1000000 / THREAD_SCHEDULER_FREQUENCY;
|
||||
if(delay == 0)
|
||||
delay = 1;
|
||||
|
||||
/* try to lock one timer */
|
||||
thread_sched_gint_timer_id = timer_setup(TIMER_ANY, delay, NULL);
|
||||
if (thread_sched_gint_timer_id < 0)
|
||||
return;
|
||||
|
||||
/* pre-calculate timer information, used by the
|
||||
"thread_kernel_sched_enty()" kernel function (see
|
||||
<gint/thread/kernel.S> for more information ) */
|
||||
timer_debug_get_hw_info(thread_sched_gint_timer_id, &info);
|
||||
thread_sched_tmu_tcr_addr = (uintptr_t)info.hardware.address.tcr;
|
||||
thread_sched_tmu_tcr_mask = (uintptr_t)info.interrupt.mask.tcr.unf;
|
||||
thread_sched_tmu_tstr_addr = (uintptr_t)info.hardware.address.tstr;
|
||||
thread_sched_tmu_tstr_mask = (uintptr_t)info.interrupt.mask.tstr.str;
|
||||
thread_sched_tmu_tcor_addr = (uintptr_t)info.hardware.address.tcor;
|
||||
thread_sched_tmu_tcnt_addr = (uintptr_t)info.hardware.address.tcnt;
|
||||
thread_sched_tmu_interrupt_id = (uintptr_t)info.interrupt.id;
|
||||
}
|
||||
|
||||
/* thread_shced_uninit(): Uninitialize the scheduler */
|
||||
void thread_sched_uninit(void)
|
||||
{
|
||||
struct thread **thread;
|
||||
struct thread *tmp;
|
||||
|
||||
thread_atomic_start();
|
||||
thread_sched_stop();
|
||||
thread = &thread_sched_queue;
|
||||
while (*thread != NULL) {
|
||||
tmp = *thread;
|
||||
*thread = (*thread)->scheduler.next;
|
||||
//thread_kill(tmp, 0);
|
||||
thread_sched_remove(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// User interface
|
||||
//---
|
||||
/* thread_sched_start(): Start the scheduler timer */
|
||||
void thread_sched_start(void)
|
||||
{
|
||||
thread_atomic_start();
|
||||
timer_start(thread_sched_gint_timer_id);
|
||||
thread_atomic_stop();
|
||||
}
|
||||
|
||||
/* thread_shced_stop(): */
|
||||
void thread_sched_stop(void)
|
||||
{
|
||||
thread_atomic_start();
|
||||
timer_pause(thread_sched_gint_timer_id);
|
||||
thread_atomic_stop();
|
||||
}
|
||||
|
||||
/* thread_sched_add(): Add thread to the scheduler queue */
|
||||
int thread_sched_add(struct thread *thread)
|
||||
{
|
||||
thread_atomic_start();
|
||||
/* link the thread */
|
||||
thread->scheduler.id = thread_sched_uuid;
|
||||
thread->scheduler.status = THREAD_STATUS_RUNNING;
|
||||
thread->scheduler.next = thread_sched_queue;
|
||||
thread_sched_queue = thread;
|
||||
|
||||
/* update internal information */
|
||||
thread_sched_uuid = thread_sched_uuid + 1;
|
||||
thread_sched_counter = thread_sched_counter + 1;
|
||||
thread_atomic_stop();
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* thread_sched_remove(): Add thread to the scheduler queue */
|
||||
int thread_sched_remove(struct thread *thread)
|
||||
{
|
||||
struct thread **parent;
|
||||
|
||||
/* Try to find the thread's parent */
|
||||
thread_atomic_start();
|
||||
parent = &thread_sched_queue;
|
||||
while (*parent != NULL) {
|
||||
if (*parent == thread)
|
||||
break;
|
||||
parent = &(*parent)->scheduler.next;
|
||||
}
|
||||
if (*parent == NULL) {
|
||||
thread_atomic_stop();
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* unlink the thread */
|
||||
*parent = thread->scheduler.next;
|
||||
|
||||
/* remove scheduler information */
|
||||
thread->scheduler.id = -1;
|
||||
thread->scheduler.next = NULL;
|
||||
|
||||
/* update internal information */
|
||||
thread_sched_counter = thread_sched_counter - 1;
|
||||
thread_atomic_stop();
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* thread_sched_get_current(): Get the current thread */
|
||||
struct thread *thread_sched_get_current(void)
|
||||
{
|
||||
return(thread_sched_current);
|
||||
}
|
||||
|
||||
|
||||
//---
|
||||
// Kernel interface
|
||||
//---
|
||||
/* thread_sched_check(): Check thread validity
|
||||
|
||||
@return:
|
||||
* 0 can be used
|
||||
* -1 cannot be loaded
|
||||
* -2 has been removed
|
||||
*/
|
||||
static int thread_sched_check(struct thread *thread)
|
||||
{
|
||||
if (thread->scheduler.status != THREAD_STATUS_RUNNING)
|
||||
return (-1);
|
||||
switch (thread_signals_pending_deliver(thread)) {
|
||||
case thread_signals_deliver_retval_running: return (0);
|
||||
case thread_signals_deliver_retval_stopped: return (-1);
|
||||
case thread_signals_deliver_retval_dead:
|
||||
default:
|
||||
if (thread_terminate(thread) == 0)
|
||||
return (-2);
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* thread_schedule(): Schedule thread queue and return the next thread
|
||||
|
||||
This function SHOULD NOT be involved manually, it should be be involved only
|
||||
by the scheduler "handler" (see <gint/thread/kernel.S>). If you know what
|
||||
you are doing and whant to call this function, you SHOULD do it in "atomic"
|
||||
environment. */
|
||||
struct thread *thread_schedule(void)
|
||||
{
|
||||
struct thread *thread;
|
||||
struct thread *next;
|
||||
|
||||
/* Check potential error. */
|
||||
if (thread_sched_queue == NULL)
|
||||
return (NULL);
|
||||
|
||||
/* if we have no currently running thread, return directly */
|
||||
if (thread_sched_current == NULL)
|
||||
return (thread_sched_queue);
|
||||
|
||||
/* pre-calculate the next thread */
|
||||
next = thread_sched_current;
|
||||
if (thread_sched_current == NULL)
|
||||
next = thread_sched_queue;
|
||||
next = next->scheduler.next;
|
||||
if (next == NULL)
|
||||
next = thread_sched_queue;
|
||||
|
||||
do {
|
||||
/* Get the potential next thread because the current can be
|
||||
destroyed during operations */
|
||||
thread = next;
|
||||
next = next->scheduler.next;
|
||||
if (next == NULL)
|
||||
next = thread_sched_queue;
|
||||
|
||||
/* Check the thread validity */
|
||||
if (thread_sched_check(thread) == 0)
|
||||
return (thread);
|
||||
} while (thread != thread_sched_current);
|
||||
|
||||
/* If no thread has been found, load idle kernel thread which
|
||||
will only wait the next scheduler timer intervention.
|
||||
(see <gint/thread/thread.c> for more information). */
|
||||
return (thread_idle_get());
|
||||
}
|
||||
|
||||
/* thread_sched_find(): Find the thread using its identifier */
|
||||
struct thread *thread_sched_find(thread_t id)
|
||||
{
|
||||
struct thread *thread;
|
||||
|
||||
thread_atomic_start();
|
||||
thread = thread_sched_queue;
|
||||
while (thread != NULL) {
|
||||
if (thread->scheduler.id == id) {
|
||||
thread_atomic_stop();
|
||||
return (thread);
|
||||
}
|
||||
thread = thread->scheduler.next;
|
||||
}
|
||||
thread_atomic_stop();
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* invalidate the current thread to avoid context saving */
|
||||
/* TODO: find better way to do the job */
|
||||
/* TODO: doc */
|
||||
void thread_sched_invalidate(void)
|
||||
{
|
||||
thread_sched_current = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Debugging interface
|
||||
//---
|
||||
#ifdef THREAD_SCHEDULER_DEBUG
|
||||
#include <gint/display.h>
|
||||
|
||||
int context_switch_counter = 0;
|
||||
|
||||
/* thread_schedule_debug(): Debug scheduler, involved each context_switch */
|
||||
struct thread *thread_schedule_debug(struct thread *thread)
|
||||
{
|
||||
/* extern uint32_t thread_tmu_interrupt_id;
|
||||
extern uint32_t thread_tmu_tcr_addr;
|
||||
extern uint32_t thread_tmu_tcr_mask;
|
||||
extern uint32_t thread_tmu_tstr_mask;
|
||||
extern uint32_t thread_tmu_tstr_addr;
|
||||
|
||||
dclear(C_WHITE);
|
||||
dprint(1, 51, C_BLACK, "next process -> %p", thread);
|
||||
dprint(1, 61, C_BLACK, "|--spc: %p", thread->context.cpu.spc);
|
||||
dprint(1, 71, C_BLACK, "|--ssr: %p", thread->context.cpu.ssr);
|
||||
dprint(1, 81, C_BLACK, "|--r15: %p", thread->context.cpu.reg[15]);
|
||||
dprint(1, 91, C_BLACK, "|--@r0: %p", &thread->context.cpu.reg[0]);
|
||||
dprint(1, 101, C_BLACK, "|--tmu_event: %#x", thread_tmu_interrupt_id);
|
||||
dprint(1, 111, C_BLACK, "|--tmu_tcr_addr: %#x", thread_tmu_tcr_addr);
|
||||
dprint(1, 121, C_BLACK, "|--tmu_tcr_mask: %#x", thread_tmu_tcr_mask);
|
||||
dprint(1, 131, C_BLACK, "|--tmu_tstr_addr: %#x", thread_tmu_tstr_addr);
|
||||
dprint(1, 141, C_BLACK, "`--tmu_tstr_mask: %#x", thread_tmu_tstr_mask);
|
||||
*/
|
||||
|
||||
/*
|
||||
struct thread **tmp;
|
||||
int idx;
|
||||
dclear(C_WHITE);
|
||||
dprint(1, 51, C_BLACK, "new process -> %p", thread);
|
||||
dtext (1, 61, C_BLACK, "thread queue:");
|
||||
idx = 0;
|
||||
tmp = &thread_scheduler_queue;
|
||||
while (*tmp != NULL) {
|
||||
dprint(1, 71 + (10 * idx), C_BLACK, "[%d] - %p", idx, *tmp);
|
||||
tmp = &(*tmp)->private.next;
|
||||
idx = idx + 1;
|
||||
}
|
||||
dupdate();
|
||||
if (idx != 1)
|
||||
while (1);
|
||||
*/
|
||||
|
||||
context_switch_counter = context_switch_counter + 1;
|
||||
// if (context_switch_counter < 3)
|
||||
return (thread);
|
||||
|
||||
uint8_t tstr_before = ((uint8_t *)thread_sched_tmu_tstr_addr)[0];
|
||||
uint8_t tstr_after = ((uint8_t *)thread_sched_tmu_tstr_addr)[0] & (~thread_sched_tmu_tstr_mask);
|
||||
|
||||
dclear(C_WHITE);
|
||||
dprint(1, 11, C_BLACK, "next process -> %p", thread);
|
||||
dprint(1, 21, C_BLACK, "TMU -> ID: %#x", (uint8_t *)thread_sched_gint_timer_id);
|
||||
dprint(1, 31, C_BLACK, "TMU -> TSTR: %#x", tstr_before);
|
||||
dprint(1, 41, C_BLACK, "TMU -> TCOR: %#x", ((uint32_t*)thread_sched_tmu_tcor_addr)[0]);
|
||||
dprint(1, 51, C_BLACK, "TMU -> TCNT: %#x", ((uint32_t*)thread_sched_tmu_tcnt_addr)[0]);
|
||||
dprint(1, 71, C_BLACK, "TMU -> TSTR %#x", thread_sched_tmu_tstr_mask);
|
||||
dprint(1, 81, C_BLACK, "TMU -> TSTR %#x", tstr_after);
|
||||
dupdate();
|
||||
while (1);
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,166 @@
|
|||
//---
|
||||
// gint:thread:signal - Thread Signals management
|
||||
//---
|
||||
#include <gint/thread.h>
|
||||
#include <gint/std/string.h>
|
||||
|
||||
//---
|
||||
// internals functions
|
||||
//---
|
||||
/* thread_signals_deliver(): Deliver a signal */
|
||||
static int thread_signals_deliver(struct thread *thread, int sig)
|
||||
{
|
||||
// check if the signal is ignored
|
||||
if (thread->signals.handler[sig] == SIG_IGN)
|
||||
return (thread_signals_deliver_retval_running);
|
||||
|
||||
// Check custom signal managment
|
||||
if (thread->signals.handler[sig] != SIG_DFL) {
|
||||
/* save current context into user's stack and update stack */
|
||||
thread->context.reg[15] -= sizeof(struct cpu_ctx) + 3;
|
||||
thread->context.reg[15] = thread->context.reg[15] >> 2 << 2;
|
||||
memcpy((void*)(uintptr_t)thread->context.reg[15],
|
||||
&thread->context, sizeof(struct cpu_ctx));
|
||||
|
||||
/* clean context and set the handler */
|
||||
for (int i = 0 ; i < 14 ; i = i + 1)
|
||||
thread->context.reg[i] = 0x00000000;
|
||||
thread->context.gbr = 0x00000000;
|
||||
thread->context.macl = 0x00000000;
|
||||
thread->context.mach = 0x00000000;
|
||||
thread->context.ssr = 0x40000000;
|
||||
thread->context.spc = (uintptr_t)thread->signals.handler[sig];
|
||||
thread->context.pr = (uintptr_t)&thread_signals_sigreturn;
|
||||
|
||||
/* send the signal number */
|
||||
thread->context.reg[4] = sig;
|
||||
return (thread_signals_deliver_retval_running);
|
||||
}
|
||||
|
||||
/* default actions */
|
||||
switch (sig) {
|
||||
/* Stop the current thread (TODO: stop all child ?) */
|
||||
case SIGSTOP:
|
||||
thread->scheduler.status = THREAD_STATUS_PAUSED;
|
||||
return (thread_signals_deliver_retval_stopped);
|
||||
|
||||
/* Wake up the parent */
|
||||
case SIGCONT:
|
||||
if (thread->private.parent != NULL) {
|
||||
thread_signals_raise(
|
||||
thread->private.parent, SIGCHLD);
|
||||
}
|
||||
return (0);
|
||||
|
||||
/* Nothing, just wake up the process */
|
||||
case SIGCHLD:
|
||||
return (0);
|
||||
|
||||
/* Process terination (default) */
|
||||
case SIGTERM: // termination (TODO: cleanup part !!!)
|
||||
case SIGINT: // interruption
|
||||
case SIGHUP: // hang up
|
||||
case SIGKILL: // kill
|
||||
case SIGBUS: // bus error
|
||||
case SIGFPE: // fatal arithmetic error
|
||||
case SIGSEGV: // segmentation violation
|
||||
default:
|
||||
return (thread_signals_deliver_retval_dead);
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Kernel interface
|
||||
//---
|
||||
/* thread_signals_raise():Raise a signal */
|
||||
int thread_signals_raise(struct thread *thread, int sig)
|
||||
{
|
||||
if (sig >= NSIG)
|
||||
return (-1);
|
||||
thread_atomic_start();
|
||||
if (thread == NULL
|
||||
|| thread->scheduler.status == THREAD_STATUS_ZOMBIE) {
|
||||
thread_atomic_stop();
|
||||
return (-1);
|
||||
}
|
||||
thread->scheduler.status = THREAD_STATUS_RUNNING;
|
||||
thread->signals.pending |= 1 << sig;
|
||||
thread_atomic_stop();
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* thread_signals_deliver_pending(): Deliver pending signals */
|
||||
int thread_signals_pending_deliver(struct thread *thread)
|
||||
{
|
||||
sigset_t sig;
|
||||
sigset_t block;
|
||||
int retval;
|
||||
|
||||
retval = 0;
|
||||
thread_atomic_start();
|
||||
block = thread->signals.blocking;
|
||||
block = block & ~(1 << SIGKILL);
|
||||
block = block & ~(1 << SIGSTOP);
|
||||
block = block & ~(1 << SIGTERM);
|
||||
sig = thread->signals.pending & (~block);
|
||||
for (int i = 0; i < NSIG && retval == 0; ++i) {
|
||||
if ((sig & (1 << i)) == 0)
|
||||
continue;
|
||||
retval = thread_signals_deliver(thread, i);
|
||||
thread->signals.pending &= ~(1 << i);
|
||||
}
|
||||
thread_atomic_stop();
|
||||
return (retval);
|
||||
}
|
||||
|
||||
/* thread_signals_sigreturn(): Signals return handler */
|
||||
void thread_signals_sigreturn(void)
|
||||
{
|
||||
struct thread *thread;
|
||||
void *stack;
|
||||
|
||||
/* get the current thread */
|
||||
thread_atomic_start();
|
||||
thread = thread_sched_get_current();
|
||||
if (thread == NULL) {
|
||||
thread_atomic_stop();
|
||||
while (1) { thread_kernel_yield(); };
|
||||
}
|
||||
|
||||
/* dump old context */
|
||||
stack = (void*)(uintptr_t)thread->context.reg[15];
|
||||
memcpy(&thread->context, stack, sizeof(struct cpu_ctx));
|
||||
|
||||
/* restore stack */
|
||||
thread->context.reg[15] += (sizeof(struct cpu_ctx) + 3) >> 2 << 2;
|
||||
|
||||
/* Invalidate the thread to force the scheduler to not save the current
|
||||
context otherwise, the restored context will be overwritten. */
|
||||
thread_sched_invalidate();
|
||||
|
||||
/* force schedule, on the next schedule, we will return to the job */
|
||||
thread_atomic_stop();
|
||||
while (1) {
|
||||
thread_kernel_yield();
|
||||
}
|
||||
}
|
||||
|
||||
/* thread_signals_replace(): Replace current signal handler */
|
||||
void (*thread_signals_replace(struct thread *thread,
|
||||
int signum, void (*handler)(int)))(int)
|
||||
{
|
||||
void (*old)(int);
|
||||
|
||||
if (signum >= NSIG)
|
||||
return (SIG_ERR);
|
||||
thread_atomic_start();
|
||||
if (thread == NULL
|
||||
|| thread->scheduler.status == THREAD_STATUS_ZOMBIE) {
|
||||
thread_atomic_stop();
|
||||
return (SIG_ERR);
|
||||
}
|
||||
old = thread->signals.handler[signum];
|
||||
thread->signals.handler[signum] = handler;
|
||||
thread_atomic_stop();
|
||||
return (old);
|
||||
}
|
|
@ -0,0 +1,286 @@
|
|||
//---
|
||||
// gint:thread:thread - Thread management
|
||||
//---
|
||||
#include <gint/thread.h>
|
||||
#include <gint/timer.h>
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/std/setjmp.h>
|
||||
#include <gint/std/stdlib.h>
|
||||
#include <gint/std/string.h>
|
||||
|
||||
#include <gint/display.h>
|
||||
|
||||
#undef thread_create
|
||||
|
||||
//---
|
||||
// Internal thread actions
|
||||
//---
|
||||
|
||||
#if 0
|
||||
/* thread_kill_child()
|
||||
|
||||
If the THREAD_ATTR_MAIN_THREAD is set, this function is involved when the
|
||||
thread die. This function will kill all child thread generated by the parent
|
||||
*/
|
||||
/* TODO */
|
||||
static void thread_kill_child(struct thread **thread)
|
||||
{
|
||||
/* walking */
|
||||
if (*thread == NULL)
|
||||
return;
|
||||
thread_kill_child(&(*thread)->private.child);
|
||||
thread_kill_child(&(*thread)->private.sibling);
|
||||
|
||||
/* destroy the thread */
|
||||
/*thread_atomic_start();
|
||||
thread_kill(*thread, 0);
|
||||
thread_sched_remove(*thread);
|
||||
*thread = NULL;*/
|
||||
thread_atomic_stop();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// User thread interface
|
||||
//---
|
||||
|
||||
/* thread_create(): Create a new thread */
|
||||
int thread_create(thread_t *tid, thread_attr_t *attr, void *function, ...)
|
||||
{
|
||||
struct thread *thread;
|
||||
struct thread *parent;
|
||||
uint32_t argbuf[16];
|
||||
int arg_counter;
|
||||
uintptr_t arg;
|
||||
va_list ap;
|
||||
|
||||
/* Start the scheduler to avoid small freeze with the first thread */
|
||||
thread_atomic_start();
|
||||
thread_sched_start();
|
||||
|
||||
/* create the new thread */
|
||||
thread = (struct thread *)calloc(1, sizeof(struct thread));
|
||||
if (thread == NULL) {
|
||||
thread_atomic_stop();
|
||||
return (-1);
|
||||
}
|
||||
thread->private.stack = (uintptr_t)malloc(THREAD_STACK_SIZE + 4);
|
||||
if (thread->private.stack == 0x00000000) {
|
||||
free(thread);
|
||||
thread_atomic_stop();
|
||||
return (-1);
|
||||
}
|
||||
thread->context.reg[15] = ((thread->private.stack + 3) >> 2 << 2);
|
||||
thread->context.reg[15] += ((THREAD_STACK_SIZE + 3) >> 2 << 2);
|
||||
thread->context.pr = (uintptr_t)thread_kernel_terminate_trampoline;
|
||||
thread->context.spc = (uintptr_t)function;
|
||||
thread->context.ssr = 0x40000000;
|
||||
|
||||
/* dump arguments */
|
||||
va_start(ap, function);
|
||||
for (arg_counter = 0; arg_counter <= 16; ++arg_counter) {
|
||||
arg = va_arg(ap, uint32_t);
|
||||
if (arg == THREAD_CREATE_ARG_END_WATERMARK)
|
||||
break;
|
||||
if (arg_counter < 4) {
|
||||
thread->context.reg[arg_counter + 4] = arg;
|
||||
} else {
|
||||
argbuf[arg_counter - 4] = arg;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
if (arg_counter >= 4) {
|
||||
arg_counter -= 4;
|
||||
thread->context.reg[15] -= arg_counter << 2;
|
||||
memcpy((void*)(uintptr_t)thread->context.reg[15],
|
||||
argbuf, arg_counter << 2);
|
||||
}
|
||||
|
||||
/* dump attribute if needed */
|
||||
if (attr != NULL) {
|
||||
memcpy(&thread->attr, attr, sizeof(struct thread_attr_s));
|
||||
|
||||
/* check longjmp() feature */
|
||||
if (attr->state.longjmp == THREAD_ATTR_LONGJMP_ENABLE) {
|
||||
if (setjmp(thread->private.jmpbuf) != 0) {
|
||||
thread_atomic_stop();
|
||||
return (THREAD_CREATE_LONGJMP_RETURN);
|
||||
}
|
||||
}
|
||||
|
||||
/* check joinable state */
|
||||
if (attr->state.detach == THREAD_ATTR_JOINABLE) {
|
||||
parent = thread_sched_get_current();
|
||||
if (parent != NULL) {
|
||||
thread->private.parent = parent;
|
||||
thread->private.sibling = parent->private.child;
|
||||
parent->private.child = thread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* link the thread to the scheduler */
|
||||
thread_sched_add(thread);
|
||||
*tid = thread->scheduler.id;
|
||||
|
||||
/* initialize signals */
|
||||
for (int i = 0; i < NSIG; ++i)
|
||||
thread->signals.handler[i] = SIG_DFL;
|
||||
thread->signals.pending = 0x00000000;
|
||||
thread->signals.blocking = 0x00000000;
|
||||
thread_atomic_stop();
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* thread_tryjoin()
|
||||
Same behaviour than thread_join() except that if the thread is not in the
|
||||
ZOMBIE_STATE its return directly with an error. */
|
||||
int thread_tryjoin(thread_t id, void **retval)
|
||||
{
|
||||
struct thread *thread;
|
||||
|
||||
/* Try to find the thread */
|
||||
thread_atomic_start();
|
||||
thread = thread_sched_find(id);
|
||||
if (thread == NULL) {
|
||||
thread_atomic_stop();
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* check its status */
|
||||
if (thread->scheduler.status != THREAD_STATUS_ZOMBIE) {
|
||||
thread_atomic_stop();
|
||||
return (-2);
|
||||
}
|
||||
|
||||
/* destroy the thread */
|
||||
if (retval != NULL)
|
||||
*retval = (void*)((uintptr_t)thread->private.ret);
|
||||
thread_sched_remove(thread);
|
||||
free(thread);
|
||||
thread_atomic_stop();
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* thread_join(): Wait a thread terminason */
|
||||
int thread_join(thread_t id, void **retval)
|
||||
{
|
||||
while (thread_tryjoin(id, retval) != 0) {
|
||||
thread_kernel_yield();
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* thread_exit(): Terminate the calling thread */
|
||||
void thread_exit(void *retval)
|
||||
{
|
||||
struct thread *thread;
|
||||
|
||||
/* get the current thread */
|
||||
thread_atomic_start();
|
||||
thread = thread_sched_get_current();
|
||||
if (thread == NULL) {
|
||||
thread_atomic_stop();
|
||||
while (1) { thread_kernel_yield(); };
|
||||
}
|
||||
|
||||
/* Check thread longjmp() attribute feature */
|
||||
if (thread->attr.private.watermark == THREAD_ATTR_WATERMARK
|
||||
&& thread->attr.state.longjmp == THREAD_ATTR_LONGJMP_ENABLE) {
|
||||
longjmp(thread->private.jmpbuf, 1);
|
||||
}
|
||||
|
||||
/* block all signals */
|
||||
thread->signals.blocking = -1;
|
||||
thread->private.ret = (uintptr_t)retval;
|
||||
thread_signals_raise(thread, SIGTERM);
|
||||
thread_atomic_stop();
|
||||
|
||||
/* Wait and yield */
|
||||
while (1) {
|
||||
thread_kernel_yield();
|
||||
}
|
||||
}
|
||||
|
||||
/* thread_kill(): Send signals */
|
||||
/* TODO: hierarchical handling ! */
|
||||
int thread_kill(thread_t id, int sig)
|
||||
{
|
||||
struct thread *thread;
|
||||
int ret;
|
||||
|
||||
thread_atomic_start();
|
||||
thread = thread_sched_find(id);
|
||||
if (thread == NULL) {
|
||||
thread_atomic_stop();
|
||||
return (-1);
|
||||
}
|
||||
ret = thread_signals_raise(thread, sig);
|
||||
thread_atomic_stop();
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/* thread_signal(): set the disposition of the signal signum to handler */
|
||||
void (*thread_signal(int signum, void (*handler)(int)))(int)
|
||||
{
|
||||
struct thread *thread;
|
||||
void (*ret)(int);
|
||||
|
||||
thread_atomic_start();
|
||||
thread = thread_sched_get_current();
|
||||
if (thread == NULL) {
|
||||
thread_atomic_stop();
|
||||
return (SIG_ERR);
|
||||
}
|
||||
ret = thread_signals_replace(thread, signum, handler);
|
||||
thread_atomic_stop();
|
||||
return (ret);
|
||||
}
|
||||
|
||||
//---
|
||||
// Kernel interface
|
||||
//---
|
||||
|
||||
/* thread_terminate(): Terminate a thread */
|
||||
int thread_terminate(struct thread *thread)
|
||||
{
|
||||
/* freed the stack */
|
||||
if (thread->private.stack != 0x00000000)
|
||||
free((void*)thread->private.stack);
|
||||
|
||||
/* check is the thread is detached*/
|
||||
if (thread->attr.private.watermark == THREAD_ATTR_WATERMARK
|
||||
&& thread->attr.state.detach == THREAD_ATTR_DETACHED) {
|
||||
thread_sched_remove(thread);
|
||||
free(thread);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* the thread is joinable, wait until someone read its ret value
|
||||
(the thread will be destroyed in the "thread_tryjoin()" function ) */
|
||||
thread->scheduler.status = THREAD_STATUS_ZOMBIE;
|
||||
return (1);
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver part
|
||||
//---
|
||||
|
||||
/* init(): setup the scheduler */
|
||||
static void init(void)
|
||||
{
|
||||
/* initialize the scheduler */
|
||||
thread_sched_init();
|
||||
|
||||
/* intialize the idle thread */
|
||||
thread_idle_init();
|
||||
}
|
||||
|
||||
/* The thread scheduler is consider like a driver */
|
||||
gint_driver_t drv_thread = {
|
||||
.name = "THREAD",
|
||||
.init = init
|
||||
};
|
||||
GINT_DECLARE_DRIVER(4, drv_thread);
|
|
@ -5,8 +5,7 @@
|
|||
#include <gint/clock.h>
|
||||
#include <gint/timer.h>
|
||||
|
||||
/* sleep_us(): Sleep for a fixed duration in microseconds */
|
||||
void sleep_us(uint64_t delay_us)
|
||||
static void do_sleep(uint64_t delay_us, int spin)
|
||||
{
|
||||
volatile int flag = 0;
|
||||
|
||||
|
@ -14,5 +13,18 @@ void sleep_us(uint64_t delay_us)
|
|||
if(timer < 0) return;
|
||||
|
||||
timer_start(timer);
|
||||
timer_wait(timer);
|
||||
if(spin) timer_spinwait(timer);
|
||||
else timer_wait(timer);
|
||||
}
|
||||
|
||||
/* sleep_us(): Sleep for a fixed duration in microseconds */
|
||||
void sleep_us(uint64_t delay_us)
|
||||
{
|
||||
do_sleep(delay_us, 0);
|
||||
}
|
||||
|
||||
/* sleep_us_spin(): Actively sleep for a fixed duration in microseconds */
|
||||
void sleep_us_spin(uint64_t delay_us)
|
||||
{
|
||||
do_sleep(delay_us, 1);
|
||||
}
|
||||
|
|
|
@ -261,13 +261,13 @@ void timer_stop(int id)
|
|||
}
|
||||
}
|
||||
|
||||
/* timer_wait() - wait until a timer is stopped */
|
||||
/* timer_wait(): Wait for a timer to stop */
|
||||
void timer_wait(int id)
|
||||
{
|
||||
if(id < 3)
|
||||
{
|
||||
tmu_t *T = &TMU[id];
|
||||
/* Sleep if an interruption will wake us up */
|
||||
/* Sleep only if an interrupt will be there to wake us up */
|
||||
while(*TSTR & (1 << id)) if(T->TCR.UNIE) sleep();
|
||||
}
|
||||
else
|
||||
|
@ -277,6 +277,21 @@ void timer_wait(int id)
|
|||
}
|
||||
}
|
||||
|
||||
/* timer_spinwait(): Actively wait for a timer to raise UNF */
|
||||
void timer_spinwait(int id)
|
||||
{
|
||||
if(id < 3)
|
||||
{
|
||||
tmu_t *T = &TMU[id];
|
||||
while(!T->TCR.UNF) {}
|
||||
}
|
||||
else
|
||||
{
|
||||
etmu_t *T = &ETMU[id-3];
|
||||
while(!T->TCR.UNF) {}
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Predefined timer callbacks
|
||||
//---
|
||||
|
@ -289,6 +304,53 @@ int timer_timeout(void volatile *arg)
|
|||
return TIMER_STOP;
|
||||
}
|
||||
|
||||
//---
|
||||
// Debugging function
|
||||
//---
|
||||
/* timer_get_hw_info(): Get hardware information */
|
||||
int timer_debug_get_hw_info(int id, struct timer_debug_info *info)
|
||||
{
|
||||
uint16_t etmu_event[6] = { 0x9e0, 0xc20, 0xc40, 0x900, 0xd00, 0xfa0 };
|
||||
|
||||
/* check error */
|
||||
if (info == NULL && id >= timer_count())
|
||||
return (-1);
|
||||
|
||||
/* The timer should be installed... */
|
||||
if(!timers[id])
|
||||
return (-2);
|
||||
|
||||
/* check timer hardware type (ETMU or TMU) */
|
||||
if(id < 3) {
|
||||
tmu_t *T = &TMU[id];
|
||||
info->hardware.address.tstr = (void *)TSTR;
|
||||
info->hardware.address.tcor = (void *)&T->TCOR;
|
||||
info->hardware.address.tcnt = (void *)&T->TCNT;
|
||||
info->hardware.address.tcr = (void *)&T->TCR;
|
||||
info->hardware.context.tcor = T->TCOR;
|
||||
info->hardware.context.tcnt = T->TCNT;
|
||||
info->hardware.context.tcr = T->TCR.word;
|
||||
info->interrupt.mask.tcr.unf = 0x0100;
|
||||
info->interrupt.mask.tcr.unie = 0x0020;
|
||||
info->interrupt.mask.tstr.str = 1 << id;
|
||||
info->interrupt.id = 0x400 + (0x20 * id);
|
||||
} else {
|
||||
etmu_t *T = &ETMU[id - 3];
|
||||
info->hardware.address.tstr = (void *)&T->TSTR;
|
||||
info->hardware.address.tcor = (void *)&T->TCOR;
|
||||
info->hardware.address.tcnt = (void *)&T->TCNT;
|
||||
info->hardware.address.tcr = (void *)&T->TCR;
|
||||
info->hardware.context.tcor = T->TCOR;
|
||||
info->hardware.context.tcnt = T->TCNT;
|
||||
info->hardware.context.tcr = T->TCR.byte;
|
||||
info->interrupt.mask.tcr.unf = 0x0002;
|
||||
info->interrupt.mask.tcr.unie = 0x0001;
|
||||
info->interrupt.mask.tstr.str = 0x01;
|
||||
info->interrupt.id = etmu_event[id - 3];
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver initialization
|
||||
//---
|
||||
|
|
Loading…
Reference in New Issue