/*---------------------------------------------------------------------------* Project: TwlSDK - library - dsp File: dsp_process.c Copyright 2008 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Date:: 2008-11-21#$ $Rev: 9387 $ $Author: kitase_hirotake $ *---------------------------------------------------------------------------*/ #include #include #include #include "dsp_coff.h" /*---------------------------------------------------------------------------*/ /* Constants */ #define DSP_DPRINTF(...) ((void) 0) //#define DSP_DPRINTF OS_TPrintf // Section enumeration callback in the process image typedef BOOL (*DSPSectionEnumCallback)(DSPProcessContext *, const COFFFileHeader *, const COFFSectionTable *); /*---------------------------------------------------------------------------*/ /* Variables */ // Process context currently running static DSPProcessContext *DSPiCurrentComponent = NULL; static PMExitCallbackInfo DSPiShutdownCallbackInfo[1]; static BOOL DSPiShutdownCallbackIsRegistered = FALSE; static PMSleepCallbackInfo DSPiPreSleepCallbackInfo[1]; static BOOL DSPiPreSleepCallbackIsRegistered = FALSE; /*---------------------------------------------------------------------------*/ /* Functions */ /*---------------------------------------------------------------------------* Name: DSP_EnumSections Description: Runs the predetermined processes by enumerating sections in the process image Arguments: context: DSPProcessContext structure passed to the callback function callback: Callback called for each section Returns: TRUE if all sections are enumerated; FALSE if ended prematurely. *---------------------------------------------------------------------------*/ BOOL DSP_EnumSections(DSPProcessContext *context, DSPSectionEnumCallback callback); /*---------------------------------------------------------------------------* Name: DSP_StopDSPComponent Description: Prepares to end DSP. Currently, only when stopping the DMA of DSP Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void DSP_StopDSPComponent(void) { DSPProcessContext *context = DSPiCurrentComponent; context->hookFactors = 0; DSP_SendData(DSP_PIPE_COMMAND_REGISTER, DSP_PIPE_FLAG_EXIT_OS); (void)DSP_RecvData(DSP_PIPE_COMMAND_REGISTER); } /*---------------------------------------------------------------------------* Name: DSPi_ShutdownCallback Description: Forced system shutdown termination callback. Arguments: name: Process name Search for processes registered last if NULL is specified Returns: DSPProcessContext structure that matches the specified name *---------------------------------------------------------------------------*/ static void DSPi_ShutdownCallback(void *arg) { (void)arg; for (;;) { DSPProcessContext *context = DSP_FindProcess(NULL); if (!context) { break; } // OS_TWarning("force-exit %s component.\n", context->name); DSP_QuitProcess(context); } } /*---------------------------------------------------------------------------* Name: DSPi_PreSleepCallback Description: Sleep warning callback. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ static void DSPi_PreSleepCallback(void *arg) { #pragma unused( arg ) OS_Panic("Could not sleep while DSP is processing.\n"); } /*---------------------------------------------------------------------------* Name: DSPi_MasterInterrupts Description: DSP interrupt process. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ static void DSPi_MasterInterrupts(void) { DSPProcessContext *context = DSPiCurrentComponent; if (context) { // An interrupt cause flag (IF) is only generated to the OS for the instance that either of the DSP interrupt causes first occurs. If there is the possibility that new DSP interrupt causes can occur while multiple DSP interrupt causes are being sequentially processed, it is necessary to repeat at that time without relying on the interrupt handler. // // // // for (;;) { // Batch determine messages that can be causes of DSP interrupts int ready = (reg_DSP_SEM | (((reg_DSP_PSTS >> REG_DSP_PSTS_RRI0_SHIFT) & 7) << 16)); int factors = (ready & context->hookFactors); if (factors == 0) { break; } else { // Process corresponding hook process in order // If group-registered, notify together one time only while (factors != 0) { int index = (int)MATH_CTZ((u32)factors); factors &= ~context->hookGroup[index]; (*context->hookFunction[index])(context->hookUserdata[index]); } } } } OS_SetIrqCheckFlag(OS_IE_DSP); } /*---------------------------------------------------------------------------* Name: DSPi_ReadProcessImage Description: Read data from the process image. Arguments: context: DSPProcessContext structure offset: Offset in image buffer: The buffer to transfer to length: Transfer size Returns: TRUE, if specified size is correctly read *---------------------------------------------------------------------------*/ static BOOL DSPi_ReadProcessImage(DSPProcessContext *context, int offset, void *buffer, int length) { return FS_SeekFile(context->image, offset, FS_SEEK_SET) && (FS_ReadFile(context->image, buffer, length) == length); } /*---------------------------------------------------------------------------* Name: DSPi_CommitWram Description: Assign specified segment to specific processor. Arguments: context: DSPProcessContext structure wram: Code/Data segment: Segment number to switch to: Processor to switch Returns: TRUE, if all slots are correctly switched *---------------------------------------------------------------------------*/ static BOOL DSPi_CommitWram(DSPProcessContext *context, MIWramPos wram, int segment, MIWramProc to) { BOOL retval = FALSE; int slot = DSP_GetProcessSlotFromSegment(context, wram, segment); // Disconnect from currently assigned processor if (!MI_IsWramSlotUsed(wram, slot) || MI_FreeWramSlot(wram, slot, MI_WRAM_SIZE_32KB, MI_GetWramBankMaster(wram, slot)) > 0) { // Assign to specified processor void *physicalAddr = (void *)MI_AllocWramSlot(wram, slot, MI_WRAM_SIZE_32KB, to); if (physicalAddr != 0) { // Move to predetermined offset for each processor vu8 *bank = &((vu8*)((wram == MI_WRAM_B) ? REG_MBK_B0_ADDR : REG_MBK_C0_ADDR))[slot]; if (to == MI_WRAM_ARM9) { *bank = (u8)((*bank & ~MI_WRAM_OFFSET_MASK_B) | (slot << MI_WRAM_OFFSET_SHIFT_B)); } else if (to == MI_WRAM_DSP) { *bank = (u8)((*bank & ~MI_WRAM_OFFSET_MASK_B) | (segment << MI_WRAM_OFFSET_SHIFT_B)); } retval = TRUE; } } return retval; } /*---------------------------------------------------------------------------* Name: DSPi_MapAndLoadProcessImageCallback Description: Callback to map and load with 1 pass of the process image. Arguments: context: DSPProcessContext structure header: COFF file header information section: Enumerated section Returns: TRUE, if enumeration is continued; FALSE, if acceptable to end *---------------------------------------------------------------------------*/ static BOOL DSPi_MapAndLoadProcessImageCallback(DSPProcessContext *context, const COFFFileHeader *header, const COFFSectionTable *section) { BOOL retval = TRUE; // Because there is possibility that single sections can be located at a maximum of four locations for code and data. // enum { placement_kind_max = 2, // { CODE, DATA } placement_code_page_max = 2, placement_data_page_max = 2, placement_max = placement_code_page_max + placement_data_page_max }; MIWramPos wrams[placement_max]; int addrs[placement_max]; BOOL nolds[placement_max]; int placement = 0; // Determine the page number to locate using the suffix of the section name // If the name field directly represents the string, it is located in only one position, but if it is a long name that must be referenced from the string table , it is possible to be located in multiple places. // const char *name = (const char *)section->Name; char longname[128]; if (*(u32*)name == 0) { u32 stringtable = header->PointerToSymbolTable + 0x12 * header->NumberOfSymbols; if(!DSPi_ReadProcessImage(context, (int)(stringtable + *(u32*)§ion->Name[4]), longname, sizeof(longname))) { retval = FALSE; return retval; } name = longname; } // Determine existence of section here that is a special landmark if (STD_CompareString(name, "SDK_USING_OS@d0") == 0) { context->flags |= DSP_PROCESS_FLAG_USING_OS; } // CODE section is WRAM-B if ((section->s_flags & COFF_SECTION_ATTR_MAPPED_IN_CODE_MEMORY) != 0) { int baseaddr = (int)(section->s_paddr * 2); BOOL noload = ((section->s_flags & COFF_SECTION_ATTR_NOLOAD_FOR_CODE_MEMORY) != 0); if (STD_StrStr(name, "@c0") != NULL) { wrams[placement] = MI_WRAM_B; addrs[placement] = baseaddr + DSP_WRAM_SLOT_SIZE * 4 * 0; nolds[placement] = noload; ++placement; } if (STD_StrStr(name, "@c1") != NULL) { wrams[placement] = MI_WRAM_B; addrs[placement] = baseaddr + DSP_WRAM_SLOT_SIZE * 4 * 1; nolds[placement] = noload; ++placement; } } // DATA section is WRAM-C if ((section->s_flags & COFF_SECTION_ATTR_MAPPED_IN_DATA_MEMORY) != 0) { int baseaddr = (int)(section->s_vaddr * 2); BOOL noload = ((section->s_flags & COFF_SECTION_ATTR_NOLOAD_FOR_DATA_MEMORY) != 0); if (STD_StrStr(name, "@d0") != NULL) { wrams[placement] = MI_WRAM_C; addrs[placement] = baseaddr + DSP_WRAM_SLOT_SIZE * 4 * 0; nolds[placement] = noload; ++placement; } if (STD_StrStr(name, "@d1") != NULL) { wrams[placement] = MI_WRAM_C; addrs[placement] = baseaddr + DSP_WRAM_SLOT_SIZE * 4 * 1; nolds[placement] = noload; ++placement; } } // Location destination information was listed up, so locate in each address { int i; for (i = 0; i < placement; ++i) { MIWramPos wram = wrams[i]; int dstofs = addrs[i]; int length = (int)section->s_size; int srcofs = (int)section->s_scnptr; // Split load so as not to overlap the slot boundary while (length > 0) { // Clip the size at the slot boundary int ceil = MATH_ROUNDUP(dstofs + 1, DSP_WRAM_SLOT_SIZE); int curlen = MATH_MIN(length, ceil - dstofs); BOOL newmapped = FALSE; // If the corresponding segment is not yet mapped, map and initialize here if (DSP_GetProcessSlotFromSegment(context, wram, dstofs / DSP_WRAM_SLOT_SIZE) == -1) { int segment = (dstofs / DSP_WRAM_SLOT_SIZE); u16 *slots = (wram == MI_WRAM_B) ? &context->slotB : &context->slotC; int *segbits = (wram == MI_WRAM_B) ? &context->segmentCode : &context->segmentData; int *map = (wram == MI_WRAM_B) ? context->slotOfSegmentCode : context->slotOfSegmentData; int slot = (int)MATH_CountTrailingZeros((u32)*slots); if (slot >= MI_WRAM_B_MAX_NUM) { retval = FALSE; break; } else { newmapped = TRUE; map[segment] = slot; *slots &= ~(1 << slot); *segbits |= (1 << segment); if (!DSPi_CommitWram(context, wram, segment, MI_WRAM_ARM9)) { retval = FALSE; break; } } MI_CpuFillFast(DSP_ConvertProcessAddressFromDSP(context, wram, segment * (DSP_WRAM_SLOT_SIZE / 2)), 0, DSP_WRAM_SLOT_SIZE); } // If noload is specified, ignore if (nolds[i]) { DSP_DPRINTF("$%04X (noload)\n", dstofs); } else { // Calculate the corresponding offset in this time's transfer range u8 *dstbuf = (u8*)DSP_ConvertProcessAddressFromDSP(context, wram, dstofs / 2); if (!DSPi_ReadProcessImage(context, srcofs, dstbuf, curlen)) { retval = FALSE; break; } DSP_DPRINTF("$%04X -> mem:%08X\n", dstofs, dstbuf); } srcofs += curlen; dstofs += curlen; length -= curlen; } } } return retval; } /*---------------------------------------------------------------------------* Name: DSP_InitProcessContext Description: Initializes the process information structure. Arguments: context: DSPProcessContext structure name: Process name or NULL Returns: None. *---------------------------------------------------------------------------*/ void DSP_InitProcessContext(DSPProcessContext *context, const char *name) { int i; int segment; MI_CpuFill8(context, 0, sizeof(*context)); context->next = NULL; context->flags = 0; (void)STD_CopyString(context->name, name ? name : ""); context->image = NULL; // Make the segment map empty context->segmentCode = 0; context->segmentData = 0; // Make the slot map empty for (segment = 0; segment < MI_WRAM_B_MAX_NUM; ++segment) { context->slotOfSegmentCode[segment] = -1; } for (segment = 0; segment < MI_WRAM_C_MAX_NUM; ++segment) { context->slotOfSegmentData[segment] = -1; } // Make the hook registration state empty context->hookFactors = 0; for (i = 0; i < DSP_HOOK_MAX; ++i) { context->hookFunction[i] = NULL; context->hookUserdata[i] = NULL; context->hookGroup[i] = 0; } } /*---------------------------------------------------------------------------* Name: DSP_EnumSections Description: Runs the predetermined processes by enumerating sections in the process image. Arguments: context: DSPProcessContext structure passed to the callback function callback: Callback called for each section Returns: TRUE if all sections are enumerated; FALSE if ended prematurely. *---------------------------------------------------------------------------*/ BOOL DSP_EnumSections(DSPProcessContext *context, DSPSectionEnumCallback callback) { BOOL retval = FALSE; // Copy once to temporary, considering the memory alignment of the image buffer COFFFileHeader header[1]; if (DSPi_ReadProcessImage(context, 0, header, sizeof(header))) { int base = (int)(sizeof(header) + header->SizeOfOptionalHeader); int index; for (index = 0; index < header->NumberOfSections; ++index) { COFFSectionTable section[1]; if (!DSPi_ReadProcessImage(context, (int)(base + index * sizeof(section)), section, (int)sizeof(section))) { break; } // Sort only sections with a size over 1 and no BLOCK-HEADER attribute if (((section->s_flags & COFF_SECTION_ATTR_BLOCK_HEADER) == 0) && (section->s_size != 0)) { if (callback && !(*callback)(context, header, section)) { break; } } } retval = (index >= header->NumberOfSections); } return retval; } /*---------------------------------------------------------------------------* Name: DSP_AttachProcessMemory Description: Assigns memory space continuously to unused regions of the process. Arguments: context: DSPProcessContext structure wram: Memory space to assign (MI_WRAM_B/MI_WRAM_C) slots: WRAM slot to assign Returns: Header address of the assigned DSP memory space, or 0 *---------------------------------------------------------------------------*/ u32 DSP_AttachProcessMemory(DSPProcessContext *context, MIWramPos wram, int slots) { u32 retval = 0; int *segbits = (wram == MI_WRAM_B) ? &context->segmentCode : &context->segmentData; int *map = (wram == MI_WRAM_B) ? context->slotOfSegmentCode : context->slotOfSegmentData; // Search for continuous unusued regions only for the necessary amount int need = (int)MATH_CountPopulation((u32)slots); u32 region = (u32)((1 << need) - 1); u32 space = (u32)(~*segbits & 0xFF); int segment = 0; for (segment = 0; segment < MI_WRAM_B_MAX_NUM; ++segment) { // There are various restrictions in the DSP page boundary (4 segments) so be careful to allocate without overlap // if ((((segment ^ (segment + need - 1)) & 4) == 0) && (((space >> segment) & region) == region)) { // If there is an adequate unused region existing, assign slots from a lower order retval = (u32)(DSP_ADDR_TO_DSP(DSP_WRAM_SLOT_SIZE) * segment); while (slots) { int slot = (int)MATH_CountTrailingZeros((u32)slots); map[segment] = slot; slots &= ~(1 << slot); *segbits |= (1 << segment); segment += 1; } break; } } return retval; } /*---------------------------------------------------------------------------* Name: DSP_DetachProcessMemory Description: Deallocates the WRAM slots assigned to the regions used by the process. Arguments: context: DSPProcessContext structure slots: Assigned WRAM slot Returns: None. *---------------------------------------------------------------------------*/ void DSP_DetachProcessMemory(DSPProcessContext *context, MIWramPos wram, int slots) { int *segbits = (wram == MI_WRAM_B) ? &context->segmentCode : &context->segmentData; int *map = (wram == MI_WRAM_B) ? context->slotOfSegmentCode : context->slotOfSegmentData; int segment; for (segment = 0; segment < MI_WRAM_B_MAX_NUM; ++segment) { if ((((1 << segment) & *segbits) != 0) && (((1 << map[segment]) & slots) != 0)) { *segbits &= ~(1 << segment); map[segment] = -1; } } } /*---------------------------------------------------------------------------* Name: DSP_SwitchProcessMemory Description: Switches DSP address management being used by a specified process. Arguments: context: DSPProcessContext structure wram: Memory space to switch (MI_WRAM_B/MI_WRAM_C) address: Header address to switch (Word units of the DSP memory space) length: Memory size to switch (Word units of the DSP memory space) to: New master processor Returns: TRUE, if all slots are correctly switched *---------------------------------------------------------------------------*/ BOOL DSP_SwitchProcessMemory(DSPProcessContext *context, MIWramPos wram, u32 address, u32 length, MIWramProc to) { address = DSP_ADDR_TO_ARM(address); length = DSP_ADDR_TO_ARM(MATH_MAX(length, 1)); { int *segbits = (wram == MI_WRAM_B) ? &context->segmentCode : &context->segmentData; int lower = (int)(address / DSP_WRAM_SLOT_SIZE); int upper = (int)((address + length - 1) / DSP_WRAM_SLOT_SIZE); int segment; for (segment = lower; segment <= upper; ++segment) { if ((*segbits & (1 << segment)) != 0) { if (!DSPi_CommitWram(context, wram, segment, to)) { return FALSE; } } } } return TRUE; } /*---------------------------------------------------------------------------* Name: DSP_MapAndLoadProcessImage Description: Maps and loads with one pass of the specified process image. Segment map does not need to be calculated Arguments: context: DSPProcessContext structure image: File handle that specifies the process image Referenced only in this function slotB: WRAM-B allowed for use for code memory slotC: WRAM-C allowed for use for data memory Returns: TRUE if all images are correctly loaded *---------------------------------------------------------------------------*/ static BOOL DSP_MapAndLoadProcessImage(DSPProcessContext *context, FSFile *image, int slotB, int slotC) { BOOL retval = FALSE; const u32 dspMemSize = DSP_ADDR_TO_DSP(DSP_WRAM_SLOT_SIZE) * MI_WRAM_B_MAX_NUM; // Map WRAM while actually loading the process image context->image = image; context->slotB = (u16)slotB; context->slotC = (u16)slotC; if (DSP_EnumSections(context, DSPi_MapAndLoadProcessImageCallback)) { DC_FlushRange((const void*)MI_GetWramMapStart_B(), MI_WRAM_B_SIZE); DC_FlushRange((const void*)MI_GetWramMapStart_C(), MI_WRAM_C_SIZE); // Switch all assigned WRAM slots to DSP if (DSP_SwitchProcessMemory(context, MI_WRAM_B, 0, dspMemSize, MI_WRAM_DSP) && DSP_SwitchProcessMemory(context, MI_WRAM_C, 0, dspMemSize, MI_WRAM_DSP)) { retval = TRUE; } } return retval; } /*---------------------------------------------------------------------------* Name: DSP_SetProcessHook Description: Sets the hook for DSP interrupt cause to the process. Arguments: context: DSPProcessContext structure factors: Bit set of the specified interrupt cause Bits 0 - 15 are semaphore; bits 16 - 18 are reply function: Hook function to call userdata: Arbitrary user-defined argument given to the hook function Returns: None. *---------------------------------------------------------------------------*/ void DSP_SetProcessHook(DSPProcessContext *context, int factors, DSPHookFunction function, void *userdata) { OSIntrMode bak_cpsr = OS_DisableInterrupts(); int i; for (i = 0; i < DSP_HOOK_MAX; ++i) { int bit = (1 << i); if ((bit & factors) != 0) { context->hookFunction[i] = function; context->hookUserdata[i] = userdata; context->hookGroup[i] = factors; } } { // Change the interrupt cause int modrep = (((factors >> 16) & 0x7) << REG_DSP_PCFG_PRIE0_SHIFT); int modsem = ((factors >> 0) & 0xFFFF); int currep = reg_DSP_PCFG; int cursem = reg_DSP_PMASK; if (function != NULL) { reg_DSP_PCFG = (u16)(currep | modrep); reg_DSP_PMASK = (u16)(cursem & ~modsem); context->hookFactors |= factors; } else { reg_DSP_PCFG = (u16)(currep & ~modrep); reg_DSP_PMASK = (u16)(cursem | modsem); context->hookFactors &= ~factors; } } (void)OS_RestoreInterrupts(bak_cpsr); } /*---------------------------------------------------------------------------* Name: DSP_HookPostStartProcess Description: Hooks immediately after loading the DSP process image. It is necessary for the DSP component developer to start the debugger Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ SDK_WEAK_SYMBOL void DSP_HookPostStartProcess(void) { } /*---------------------------------------------------------------------------* Name: DSP_ExecuteProcess Description: Loads the DSP process image and starts. Arguments: context: DSPProcessContext structure used in status management Referenced using the system until the process is destroyed image: File handle that specifies the process image Referenced only in this function slotB: WRAM-B allowed for use for code memory slotC: WRAM-C allowed for use for data memory Returns: None. *---------------------------------------------------------------------------*/ BOOL DSP_ExecuteProcess(DSPProcessContext *context, FSFile *image, int slotB, int slotC) { BOOL retval = FALSE; if (!FS_IsAvailable()) { OS_TWarning("FS is not initialized yet.\n"); FS_Init(FS_DMA_NOT_USE); } // Initialize DSP systems common to all components DSP_InitPipe(); OS_SetIrqFunction(OS_IE_DSP, DSPi_MasterInterrupts); DSP_SetProcessHook(context, DSP_HOOK_SEMAPHORE_(15) | DSP_HOOK_REPLY_(2), (DSPHookFunction)DSP_HookPipeNotification, NULL); (void)OS_EnableIrqMask(OS_IE_DSP); // Map the process image to memory and load if (!DSP_MapAndLoadProcessImage(context, image, slotB, slotC)) { OS_TWarning("you should check wram\n"); } else { OSIntrMode bak_cpsr = OS_DisableInterrupts(); // Register the context to the process list DSPProcessContext **pp = &DSPiCurrentComponent; for (pp = &DSPiCurrentComponent; *pp && (*pp != context); pp = &(*pp)->next) { } *pp = context; context->image = NULL; // Set the shutdown forced termination callback if (!DSPiShutdownCallbackIsRegistered) { PM_SetExitCallbackInfo(DSPiShutdownCallbackInfo, DSPi_ShutdownCallback, NULL); PMi_InsertPostExitCallbackEx(DSPiShutdownCallbackInfo, PM_CALLBACK_PRIORITY_DSP); DSPiShutdownCallbackIsRegistered = TRUE; } // Set the sleep warning callback if (!DSPiPreSleepCallbackIsRegistered) { PM_SetSleepCallbackInfo(DSPiPreSleepCallbackInfo, DSPi_PreSleepCallback, NULL); PMi_InsertPreSleepCallbackEx(DSPiPreSleepCallbackInfo, PM_CALLBACK_PRIORITY_DSP); DSPiPreSleepCallbackIsRegistered = TRUE; } // Set only the necessary interrupt cause as early as possible after starting up DSP_PowerOn(); DSP_ResetOffEx((u16)(context->hookFactors >> 16)); DSP_MaskSemaphore((u16)~(context->hookFactors >> 0)); // If the ARM side is paused immediately after startup, it is possible to reload and trace the same component // DSP_HookPostStartProcess(); // For components that spontaneously send commands from the DSP side without waiting for command from the ARM9 processor, if reloaded by the DSP debugger, the command value will be initialized to 0 and unread bits will remain on the ARM9 processor. // // // // This 0 is read and discarded in the library if ((context->flags & DSP_PROCESS_FLAG_USING_OS) != 0) { u16 id; for (id = 0; id < 3; ++id) { vu16 dummy; while (dummy = DSP_RecvDataCore(id), dummy != 1) { } } } // Just in case, check whether there is an interrupt that was not taken in the time from startup until setting DSPi_MasterInterrupts(); retval = TRUE; (void)OS_RestoreInterrupts(bak_cpsr); } return retval; } /*---------------------------------------------------------------------------* Name: DSP_QuitProcess Description: Deallocates memory by ending the DSP process. Arguments: context: DSPProcessContext structure used in status management Returns: None. *---------------------------------------------------------------------------*/ void DSP_QuitProcess(DSPProcessContext *context) { OSIntrMode bak_cpsr; // First, stop the TEAK DMA DSP_StopDSPComponent(); bak_cpsr = OS_DisableInterrupts(); // Stop the DSP DSP_ResetOn(); DSP_PowerOff(); // Invalidate interrupts (void)OS_DisableIrqMask(OS_IE_DSP); OS_SetIrqFunction(OS_IE_DSP, NULL); // Return all WRAM that were being used (void)MI_FreeWram(MI_WRAM_B, MI_WRAM_DSP); (void)MI_FreeWram(MI_WRAM_C, MI_WRAM_DSP); { // Release context from the processor list DSPProcessContext **pp = &DSPiCurrentComponent; for (pp = &DSPiCurrentComponent; *pp; pp = &(*pp)->next) { if (*pp == context) { *pp = (*pp)->next; break; } } context->next = NULL; } (void)context; (void)OS_RestoreInterrupts(bak_cpsr); // Delete the warning callback when sleeping PM_DeletePreSleepCallback(DSPiPreSleepCallbackInfo); DSPiPreSleepCallbackIsRegistered = FALSE; } /*---------------------------------------------------------------------------* Name: DSP_FindProcess Description: Search for the process being run. Arguments: name: Process name Search for processes registered last if NULL is specified Returns: DSPProcessContext structure that matches the specified name *---------------------------------------------------------------------------*/ DSPProcessContext* DSP_FindProcess(const char *name) { DSPProcessContext *ptr = NULL; OSIntrMode bak_cpsr = OS_DisableInterrupts(); ptr = DSPiCurrentComponent; if (name) { for (; ptr && (STD_CompareString(ptr->name, name) != 0); ptr = ptr->next) { } } (void)OS_RestoreInterrupts(bak_cpsr); return ptr; } /*---------------------------------------------------------------------------* Name: DSP_ReadProcessPipe Description: Receives from pipe associated with predetermined port of the process. Arguments: context: DSPProcessContext structure port: Reception source port buffer: Received data length: Received size (Byte Units) Returns: None. *---------------------------------------------------------------------------*/ void DSP_ReadProcessPipe(DSPProcessContext *context, int port, void *buffer, u32 length) { DSPPipe input[1]; (void)DSP_LoadPipe(input, port, DSP_PIPE_INPUT); DSP_ReadPipe(input, buffer, (DSPByte)length); (void)context; } /*---------------------------------------------------------------------------* Name: DSP_WriteProcessPipe Description: Sends to the pipe associated with predetermined port of the process. Arguments: context: DSPProcessContext structure port: Reception destination port buffer: Send data length: Send size (Byte Units) Returns: None. *---------------------------------------------------------------------------*/ void DSP_WriteProcessPipe(DSPProcessContext *context, int port, const void *buffer, u32 length) { DSPPipe output[1]; (void)DSP_LoadPipe(output, port, DSP_PIPE_OUTPUT); DSP_WritePipe(output, buffer, (DSPByte)length); (void)context; } #if defined(DSP_SUPPORT_OBSOLETE_LOADER) /*---------------------------------------------------------------------------* * The following shows candidate interfaces for termination because they are considered not to be currently in use *---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------* Name: DSPi_MapProcessSegmentCallback Description: Callback to calculate segment maps occupied by the process. Arguments: context: DSPProcessContext structure header: COFF file header information section: Enumerated section Returns: TRUE, if enumeration is continued; FALSE, if acceptable to end *---------------------------------------------------------------------------*/ static BOOL DSPi_MapProcessSegmentCallback(DSPProcessContext *context, const COFFFileHeader *header, const COFFSectionTable *section) { (void)header; // TODO: You might achieve higher speeds by avoiding division if((section->s_flags & COFF_SECTION_ATTR_MAPPED_IN_CODE_MEMORY) != 0) { u32 addr = DSP_ADDR_TO_ARM(section->s_paddr); int lower = (int)(addr / DSP_WRAM_SLOT_SIZE); int upper = (int)((addr + section->s_size - 1) / DSP_WRAM_SLOT_SIZE); int segment; for(segment = lower; segment <= upper; ++segment) { context->segmentCode |= (1 << segment); } } else if((section->s_flags & COFF_SECTION_ATTR_MAPPED_IN_DATA_MEMORY) != 0) { u32 addr = DSP_ADDR_TO_ARM(section->s_vaddr); int lower = (int)(addr / DSP_WRAM_SLOT_SIZE); int upper = (int)((addr + section->s_size - 1) / DSP_WRAM_SLOT_SIZE); int segment; for(segment = lower; segment <= upper; ++segment) { context->segmentData |= (1 << segment); } } return TRUE; } /*---------------------------------------------------------------------------* Name: DSPi_MapProcessSlotDefault Description: Initializes the slot map using empty numbers from the lower order. Arguments: context: DSPProcessContext structure slotB: WRAM-B allowed for use for code memory slotC: WRAM-C allowed for use for data memory Returns: TRUE if the slot map that satisfies the conditions can be allocated. *---------------------------------------------------------------------------*/ static BOOL DSPi_MapProcessSlotDefault(DSPProcessContext *context, int slotB, int slotC) { BOOL retval = TRUE; int segment; for (segment = 0; segment < MI_WRAM_B_MAX_NUM; ++segment) { if ((context->segmentCode & (1 << segment)) != 0) { int slot = (int)MATH_CountTrailingZeros((u32)slotB); if (slot >= MI_WRAM_B_MAX_NUM) { retval = FALSE; break; } context->slotOfSegmentCode[segment] = slot; slotB &= ~(1 << slot); } } for (segment = 0; segment < MI_WRAM_C_MAX_NUM; ++segment) { if ((context->segmentData & (1 << segment)) != 0) { int slot = (int)MATH_CountTrailingZeros((u32)slotC); if (slot >= MI_WRAM_C_MAX_NUM) { retval = FALSE; break; } context->slotOfSegmentData[segment] = slot; slotC &= ~(1 << slot); } } return retval; } /*---------------------------------------------------------------------------* Name: DSP_IsProcessMemoryReady Description: Checks whether the WRAM slot that should be assigned to the process is being used. Arguments: context: DSPProcessContext structure Returns: TRUE, if all WRAM slots are not being used but are in a prepared state *---------------------------------------------------------------------------*/ static BOOL DSP_IsProcessMemoryReady(DSPProcessContext *context) { BOOL retval = TRUE; int segment; for (segment = 0; segment < MI_WRAM_B_MAX_NUM; ++segment) { if ((context->segmentCode & (1 << segment)) != 0) { int slot = context->slotOfSegmentCode[segment]; if (MI_IsWramSlotUsed(MI_WRAM_B, slot)) { OS_TWarning("slot:%d for DSP:%05X is now used by someone.\n", slot, segment * DSP_WRAM_SLOT_SIZE); retval = FALSE; break; } } } for (segment = 0; segment < MI_WRAM_C_MAX_NUM; ++segment) { if ((context->segmentData & (1 << segment)) != 0) { int slot = context->slotOfSegmentData[segment]; if (MI_IsWramSlotUsed(MI_WRAM_C, slot)) { OS_TWarning("slot:%d for DSP:%05X is now used by someone.\n", slot, segment * DSP_WRAM_SLOT_SIZE); retval = FALSE; break; } } } return retval; } /*---------------------------------------------------------------------------* Name: DSPi_LoadProcessImageCallback Description: Loads the process image using the group method. Arguments: context: DSPProcessContext structure header: COFF file header information section: Enumerated section Returns: TRUE, if enumeration is continued; FALSE, if acceptable to end *---------------------------------------------------------------------------*/ static BOOL DSPi_LoadProcessImageCallback(DSPProcessContext *context, const COFFFileHeader *header, const COFFSectionTable *section) { BOOL retval = TRUE; MIWramPos wram = MI_WRAM_A; int dstofs = 0; BOOL noload = FALSE; (void)header; // CODE section is WRAM-B if ((section->s_flags & COFF_SECTION_ATTR_MAPPED_IN_CODE_MEMORY) != 0) { wram = MI_WRAM_B; dstofs = (int)(section->s_paddr * 2); if ((section->s_flags & COFF_SECTION_ATTR_NOLOAD_FOR_CODE_MEMORY) != 0) { noload = TRUE; } } // DATA section is WRAM-C else if ((section->s_flags & COFF_SECTION_ATTR_MAPPED_IN_DATA_MEMORY) != 0) { wram = MI_WRAM_C; dstofs = (int)(section->s_vaddr * 2); if ((section->s_flags & COFF_SECTION_ATTR_NOLOAD_FOR_DATA_MEMORY) != 0) { noload = TRUE; } } // Select the appropriate WRAM slot for each segment // (The constant "no WRAM" does not exist, so substitute WRAM-A) if (wram != MI_WRAM_A) { // If noload is specified, ignore if (noload) { DSP_DPRINTF("$%04X (noload)\n", dstofs); } else { // Split load so as not to overlap the slot boundary int length = (int)section->s_size; int srcofs = (int)section->s_scnptr; while (length > 0) { // Clip the size at the slot boundary int ceil = MATH_ROUNDUP(dstofs + 1, DSP_WRAM_SLOT_SIZE); int curlen = MATH_MIN(length, ceil - dstofs); // Calculate the corresponding offset in this time's transfer range u8 *dstbuf = (u8*)DSP_ConvertProcessAddressFromDSP(context, wram, dstofs/2); if (!DSPi_ReadProcessImage(context, srcofs, dstbuf, length)) { retval = FALSE; break; } DSP_DPRINTF("$%04X -> mem:%08X\n", dstofs, dstbuf); srcofs += curlen; dstofs += curlen; length -= curlen; } } } return retval; } /*---------------------------------------------------------------------------* Name: DSP_MapProcessSegment Description: Calculates segment maps occupied by the process. Arguments: context: DSPProcessContext structure Returns: None. *---------------------------------------------------------------------------*/ void DSP_MapProcessSegment(DSPProcessContext *context) { (void)DSP_EnumSections(context, DSPi_MapProcessSegmentCallback); } /*---------------------------------------------------------------------------* Name: DSP_LoadProcessImage Description: Loads the specified process image. The segment map must already be calculated. Arguments: context: DSPProcessContext structure Returns: TRUE if all images are correctly loaded *---------------------------------------------------------------------------*/ BOOL DSP_LoadProcessImage(DSPProcessContext *context) { BOOL retval = FALSE; // Check whether it is really possible to use the WRAM slot that should be assigned to DSP if (DSP_IsProcessMemoryReady(context)) { const u32 dspMemSize = DSP_ADDR_TO_DSP(DSP_WRAM_SLOT_SIZE) * MI_WRAM_B_MAX_NUM; // Switch all assigned WRAM slots to ARM9 if (DSP_SwitchProcessMemory(context, MI_WRAM_B, 0, dspMemSize, MI_WRAM_ARM9) && DSP_SwitchProcessMemory(context, MI_WRAM_C, 0, dspMemSize, MI_WRAM_ARM9)) { // Actually load the process image and flush to WRAM if (DSP_EnumSections(context, DSPi_LoadProcessImageCallback)) { DC_FlushRange((const void*)MI_GetWramMapStart_B(), MI_WRAM_B_SIZE); DC_FlushRange((const void*)MI_GetWramMapStart_C(), MI_WRAM_C_SIZE); // Switch all assigned WRAM slots to DSP if (DSP_SwitchProcessMemory(context, MI_WRAM_B, 0, dspMemSize, MI_WRAM_DSP) && DSP_SwitchProcessMemory(context, MI_WRAM_C, 0, dspMemSize, MI_WRAM_DSP)) { retval = TRUE; } } } } return retval; } /*---------------------------------------------------------------------------* Name: DSP_StartupProcess Description: Calculates the process image segment map and loads to WRAM. Version combining the DSP_MapProcessSegment and the DSP_LoadProcessImage functions. Arguments: context: DSPProcessContext structure image: File handle that specifies the process image Referenced only in this function slotB: WRAM-B allowed for use for code memory slotC: WRAM-C allowed for use for data memory slotMapper: Algorithm that assigns WRAM slots to segments If NULL is specified, the optimum default process is selected Returns: TRUE if all images are correctly loaded *---------------------------------------------------------------------------*/ BOOL DSP_StartupProcess(DSPProcessContext *context, FSFile *image, int slotB, int slotC, BOOL (*slotMapper)(DSPProcessContext *, int, int)) { BOOL retval = FALSE; if (!slotMapper) { slotMapper = DSPi_MapProcessSlotDefault; } if (!FS_IsAvailable()) { OS_TWarning("FS is not initialized yet.\n"); FS_Init(FS_DMA_NOT_USE); } context->image = image; DSP_MapProcessSegment(context); if (!(*slotMapper)(context, slotB, slotC) || !DSP_LoadProcessImage(context)) { OS_TWarning("you should check wram\n"); } else { retval = TRUE; } context->image = NULL; return retval; } #endif /*---------------------------------------------------------------------------* End of file *---------------------------------------------------------------------------*/