diff --git a/CMakeLists.txt b/CMakeLists.txt index b985ef3..fe98b79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ set(SOURCES_COMMON src/cpu/atomic.c src/cpu/cpu.c src/cpu/registers.s + src/cpu/sleep.c src/dma/dma.c src/dma/inth.s src/dma/memcpy.c diff --git a/include/gint/clock.h b/include/gint/clock.h index c6fc0c9..f02afc1 100644 --- a/include/gint/clock.h +++ b/include/gint/clock.h @@ -6,6 +6,9 @@ #define GINT_CLOCK #include +/* This header used to expose the sleep() function; include to + ensure this is still the case */ +#include //--- // Clock signals @@ -55,12 +58,6 @@ const clock_frequency_t *clock_freq(void); // Sleep functions //--- -/* sleep() - halt the processor until an event occurs - The function stops the processor until an interrupt is accepted; the - duration is not known in advance. This function should be used when the - add-in is idle, for instance while waiting for keyboard input. */ -#define sleep() __asm__("sleep") - /* sleep_us(): Sleep for a fixed duration in microseconds Stops the processor until the specified delay in microseconds has elapsed. (The processor will still wake up occasionally to handle interrupts.) This diff --git a/include/gint/cpu.h b/include/gint/cpu.h index 3c16b1c..ffad918 100644 --- a/include/gint/cpu.h +++ b/include/gint/cpu.h @@ -79,6 +79,34 @@ typedef lword_union(cpu_sr_t, cpu_sr_t cpu_getSR(void); void cpu_setSR(cpu_sr_t sr); +//--- +// Miscellaneous functions +//--- + +/* sleep(): Put the processor to sleep + + This function uses the [sleep] instruction to put the processor in sleep + mode. This allows reduced energy consumption while waiting for interrupts. + + In some certain situations, sleeping would block the process that is being + waited for (generally when on-chip memory is involved). When this occurs, + this function will not sleep at all and instead return instantly. + + The indented use for sleep() is *always* in some sort of loop, as there is + no guarantee about time or interrupts elapsed before this function + returns. */ +void sleep(void); + +/* sleep_block(): Block processor sleep + + This function blocks processor sleep until sleep_unblock() is called. The + driver maintains a counter of the number of blocking sources, therefore it + is important that *every* sleep_block() is followed by a sleep_unblock(). */ +void sleep_block(void); + +/* sleep_unblock(): Cancel a processor sleep block */ +void sleep_unblock(void); + //--- // Configuration //--- diff --git a/src/cpu/sleep.c b/src/cpu/sleep.c new file mode 100644 index 0000000..2e0fad8 --- /dev/null +++ b/src/cpu/sleep.c @@ -0,0 +1,22 @@ +#include + +volatile int cpu_sleep_block_counter = 0; + +void sleep(void) +{ + if(cpu_sleep_block_counter <= 0) __asm__("sleep"); +} + +void sleep_block(void) +{ + cpu_atomic_start(); + cpu_sleep_block_counter++; + cpu_atomic_end(); +} + +void sleep_unblock(void) +{ + cpu_atomic_start(); + cpu_sleep_block_counter--; + cpu_atomic_end(); +} diff --git a/src/dma/dma.c b/src/dma/dma.c index 2cdc59f..141b05b 100644 --- a/src/dma/dma.c +++ b/src/dma/dma.c @@ -16,6 +16,8 @@ typedef volatile sh7305_dma_channel_t channel_t; /* Callbacks for all channels */ static gint_call_t dma_callbacks[6] = { 0 }; +/* Sleep blocking flags for all channels */ +static bool dma_sleep_blocking[6] = { 0 }; /* dma_channel(): Get address of a DMA channel */ static channel_t *dma_channel(int channel) @@ -103,6 +105,14 @@ static int dma_setup(int channel, dma_size_t size, uint blocks, DMA.OR.AE = 0; DMA.OR.NMIF = 0; + /* Block sleep when the transfer involves on-chip memory */ + dma_sleep_blocking[channel] = false; + + if(ch->SAR >= 0xe5007000 && ch->SAR <= 0xe5204000) + dma_sleep_blocking[channel] = true; + if(ch->DAR >= 0xe5007000 && ch->DAR <= 0xe5204000) + dma_sleep_blocking[channel] = true; + return 0; } @@ -115,6 +125,9 @@ bool dma_transfer_async(int channel, dma_size_t size, uint blocks, dma_callbacks[channel] = callback; + if(dma_sleep_blocking[channel]) + sleep_block(); + /* Enable channel, starting the DMA transfer. */ channel_t *ch = dma_channel(channel); ch->CHCR.DE = 1; @@ -131,6 +144,9 @@ static void dma_interrupt_transfer_ended(int channel) DMA.OR.AE = 0; DMA.OR.NMIF = 0; + if(dma_sleep_blocking[channel]) + sleep_unblock(); + if(dma_callbacks[channel].function) { gint_call(dma_callbacks[channel]); @@ -151,18 +167,10 @@ void dma_transfer_wait(int channel) channel_t *ch = dma_channel(channel); if(!ch) return; - /* Wait for the channel to be disabled by the interrupt handler. - When the source or the destination of the transfer is X, Y or IL - memory, refrain from sleeping as this also stops the transfer! */ - int onchip = 0; - - if(ch->SAR >= 0xe5007000 && ch->SAR < 0xe5204000) onchip = 1; - if(ch->DAR >= 0xe5007000 && ch->DAR < 0xe5204000) onchip = 1; - while(ch->CHCR.DE && !ch->CHCR.TE) { /* Sleep only if the interrupt is enabled to wake us up */ - if(!onchip && ch->CHCR.IE) sleep(); + if(ch->CHCR.IE) sleep(); } }