Add tensorflow lite support (#29)
* Add tensorflow lite headers & lib * pthread initial works * Fix * Fix * test pass * More pthread * Fix * Use O3 to compile tf lite * Add demo * Fix * Fix * Fix idle task creation * Add keywordspot * Fix link error when program is large * Auto detect mingw32-make for win32 * Clean demo code * Revert pll2 freqfeature/tflite-kpu
parent
3277fbc06a
commit
be6ac3011e
|
@ -22,5 +22,4 @@ add_subdirectory(third_party)
|
|||
|
||||
# compile project
|
||||
add_source_files(src/${PROJ}/*.c src/${PROJ}/*.s src/${PROJ}/*.S src/${PROJ}/*.cpp)
|
||||
include(./cmake/executable.cmake)
|
||||
|
||||
include(./cmake/executable.cmake)
|
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.0)
|
||||
cmake_policy(VERSION 3.10)
|
||||
cmake_policy(VERSION 3.0)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/macros.cmake)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/macros.internal.cmake)
|
||||
|
@ -37,7 +37,5 @@ include(${CMAKE_CURRENT_LIST_DIR}/compile-flags.cmake)
|
|||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/fix-9985.cmake)
|
||||
|
||||
#add_source_files(${CMAKE_CURRENT_LIST_DIR}/../lib/bsp/crt.S)
|
||||
|
||||
removeDuplicateSubstring(${CMAKE_C_FLAGS} CMAKE_C_FLAGS)
|
||||
removeDuplicateSubstring(${CMAKE_CXX_FLAGS} CMAKE_CXX_FLAGS)
|
||||
|
|
|
@ -20,7 +20,7 @@ add_compile_flags(BOTH
|
|||
-fdata-sections
|
||||
-fstrict-volatile-bitfields
|
||||
-fno-zero-initialized-in-bss
|
||||
-Os
|
||||
-O2
|
||||
-ggdb
|
||||
)
|
||||
|
||||
|
|
|
@ -23,10 +23,14 @@ set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE C)
|
|||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
-Wl,--start-group
|
||||
m freertos c bsp drivers
|
||||
m freertos atomic bsp c stdc++ drivers
|
||||
-Wl,--end-group
|
||||
)
|
||||
|
||||
if (EXISTS ${SDK_ROOT}/src/${PROJ}/project.cmake)
|
||||
include(${SDK_ROOT}/src/${PROJ}/project.cmake)
|
||||
endif ()
|
||||
|
||||
IF(SUFFIX)
|
||||
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SUFFIX ${SUFFIX})
|
||||
ENDIF()
|
||||
|
|
|
@ -26,6 +26,9 @@ global_set(CMAKE_AR "${TOOLCHAIN}/riscv64-unknown-elf-ar${EXT}")
|
|||
global_set(CMAKE_OBJCOPY "${TOOLCHAIN}/riscv64-unknown-elf-objcopy${EXT}")
|
||||
global_set(CMAKE_SIZE "${TOOLCHAIN}/riscv64-unknown-elf-size${EXT}")
|
||||
global_set(CMAKE_OBJDUMP "${TOOLCHAIN}/riscv64-unknown-elf-objdump${EXT}")
|
||||
if (WIN32)
|
||||
global_set(CMAKE_MAKE_PROGRAM "${TOOLCHAIN}/mingw32-make${EXT}")
|
||||
endif ()
|
||||
|
||||
execute_process(COMMAND ${CMAKE_C_COMPILER} -print-file-name=crt0.o OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE CRT0_OBJ)
|
||||
execute_process(COMMAND ${CMAKE_C_COMPILER} -print-file-name=crtbegin.o OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE CRTBEGIN_OBJ)
|
||||
|
|
|
@ -51,6 +51,7 @@ SECTIONS
|
|||
.text.start :
|
||||
{
|
||||
KEEP( *(.text.start) )
|
||||
KEEP( *(.text.systick) )
|
||||
} > ram : DATA
|
||||
|
||||
.init :
|
||||
|
|
|
@ -5,4 +5,5 @@ INCLUDE_DIRECTORIES(${SDK_ROOT}/third_party)
|
|||
ADD_SUBDIRECTORY(hal)
|
||||
ADD_SUBDIRECTORY(freertos)
|
||||
ADD_SUBDIRECTORY(bsp)
|
||||
ADD_SUBDIRECTORY(drivers)
|
||||
ADD_SUBDIRECTORY(drivers)
|
||||
ADD_SUBDIRECTORY(posix)
|
|
@ -16,8 +16,10 @@
|
|||
#define _BSP_PLATFORM_H
|
||||
|
||||
#ifdef __INTELLISENSE__
|
||||
#define __freertos__ 1
|
||||
#define __attribute__(x)
|
||||
#define _HAS_CXX17 1
|
||||
#define noexcept
|
||||
#endif
|
||||
|
||||
/* clang-format off */
|
||||
|
|
|
@ -468,16 +468,4 @@ _init:
|
|||
_fini:
|
||||
ret
|
||||
.size _init, .-_init
|
||||
.size _fini, .-_fini
|
||||
|
||||
.section ".tdata.begin"
|
||||
.globl _tdata_begin
|
||||
_tdata_begin:
|
||||
|
||||
.section ".tdata.end"
|
||||
.globl _tdata_end
|
||||
_tdata_end:
|
||||
|
||||
.section ".tbss.end"
|
||||
.globl _tbss_end
|
||||
_tbss_end:
|
||||
.size _fini, .-_fini
|
|
@ -30,8 +30,6 @@ extern uint8_t __bss_start[];
|
|||
extern uint8_t __bss_end[];
|
||||
extern uint8_t _tls_data[];
|
||||
|
||||
extern __thread uint8_t _tdata_begin[], _tdata_end[], _tbss_end[];
|
||||
|
||||
extern int main(int argc, char* argv[]);
|
||||
extern void __libc_init_array(void);
|
||||
extern void __libc_fini_array(void);
|
||||
|
@ -43,19 +41,6 @@ static void setup_clocks()
|
|||
sysctl_pll_set_freq(SYSCTL_PLL2, PLL2_OUTPUT_FREQ);
|
||||
}
|
||||
|
||||
static void init_tls(void)
|
||||
{
|
||||
register void* thread_pointer asm("tp");
|
||||
|
||||
size_t tdata_size = _tdata_end - _tdata_begin;
|
||||
|
||||
memcpy(thread_pointer, _tls_data, tdata_size);
|
||||
|
||||
size_t tbss_size = _tbss_end - _tdata_end;
|
||||
|
||||
memset(thread_pointer + tdata_size, 0, tbss_size);
|
||||
}
|
||||
|
||||
static void init_bss(void)
|
||||
{
|
||||
memset(__bss_start, 0, __bss_end - __bss_start);
|
||||
|
@ -63,9 +48,6 @@ static void init_bss(void)
|
|||
|
||||
void _init_bsp(int core_id, int number_of_cores)
|
||||
{
|
||||
/* Initialize thread local data */
|
||||
init_tls();
|
||||
|
||||
if (core_id == 0)
|
||||
{
|
||||
/* Initialize bss data to 0 */
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "syscalls/syscalls.h"
|
||||
#include <atomic.h>
|
||||
#include <clint.h>
|
||||
#include <devices.h>
|
||||
|
@ -28,11 +29,10 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/file.h>
|
||||
#include "syscalls/syscalls.h"
|
||||
#include <sysctl.h>
|
||||
#include <syslog.h>
|
||||
#include <uarths.h>
|
||||
|
@ -248,7 +248,14 @@ static int sys_close(int file)
|
|||
* Otherwise, -1 shall be returned and errno set to indicate
|
||||
* the error.
|
||||
*/
|
||||
return io_close(file);
|
||||
if (STDOUT_FILENO == file || STDERR_FILENO == file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return io_close(file);
|
||||
}
|
||||
}
|
||||
|
||||
static int sys_gettimeofday(struct timeval *tp, void *tzp)
|
||||
|
|
|
@ -52,11 +52,14 @@ int sys_open(const char *name, int flags, int mode)
|
|||
return -1;
|
||||
}
|
||||
|
||||
off_t sys_lseek(int fildes, off_t offset, int whence)
|
||||
off_t sys_lseek(int fd, off_t offset, int whence)
|
||||
{
|
||||
if (STDOUT_FILENO == fd || STDERR_FILENO == fd)
|
||||
return -1;
|
||||
|
||||
try
|
||||
{
|
||||
auto &obj = system_handle_to_object(fildes);
|
||||
auto &obj = system_handle_to_object(fd);
|
||||
if (auto f = obj.as<filesystem_file>())
|
||||
{
|
||||
if (whence == SEEK_SET)
|
||||
|
@ -87,6 +90,9 @@ off_t sys_lseek(int fildes, off_t offset, int whence)
|
|||
|
||||
int sys_fstat(int fd, struct kernel_stat *buf)
|
||||
{
|
||||
if (STDOUT_FILENO == fd || STDERR_FILENO == fd)
|
||||
return 0;
|
||||
|
||||
try
|
||||
{
|
||||
memset(buf, 0, sizeof(struct kernel_stat));
|
||||
|
|
|
@ -72,7 +72,14 @@
|
|||
#define configUSE_16_BIT_TICKS 0
|
||||
#define configIDLE_SHOULD_YIELD 0
|
||||
#define configQUEUE_REGISTRY_SIZE 8
|
||||
#define configCHECK_FOR_STACK_OVERFLOW 0
|
||||
|
||||
/* TLS */
|
||||
enum
|
||||
{
|
||||
PTHREAD_TLS_INDEX = 0
|
||||
};
|
||||
|
||||
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 1
|
||||
|
||||
/* mutex */
|
||||
#define configUSE_MUTEXES 1
|
||||
|
@ -87,13 +94,12 @@
|
|||
/* memory */
|
||||
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 1024 )
|
||||
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 1024 * 1024 ) )
|
||||
#define configSUPPORT_STATIC_ALLOCATION 0
|
||||
#define configSUPPORT_STATIC_ALLOCATION 1
|
||||
#define configSUPPORT_DYNAMIC_ALLOCATION 1
|
||||
|
||||
#define configUSE_APPLICATION_TASK_TAG 0
|
||||
#define configUSE_APPLICATION_TASK_TAG 1
|
||||
#define configUSE_COUNTING_SEMAPHORES 1
|
||||
#define configUSE_TICKLESS_IDLE 1
|
||||
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0
|
||||
#define configGENERATE_RUN_TIME_STATS 0
|
||||
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
|
||||
|
||||
|
@ -109,7 +115,7 @@
|
|||
|
||||
/* Main task */
|
||||
#define configMAIN_TASK_PRIORITY 1
|
||||
#define configMAIN_TASK_STACK_SIZE 4096
|
||||
#define configMAIN_TASK_STACK_SIZE (4096 * 2)
|
||||
|
||||
/* Set the following definitions to 1 to include the API function, or zero to exclude the API function. */
|
||||
#define INCLUDE_vTaskPrioritySet 1
|
||||
|
@ -122,6 +128,11 @@
|
|||
#define INCLUDE_eTaskGetState 1
|
||||
#define INCLUDE_xTaskAbortDelay 1
|
||||
|
||||
#define INCLUDE_xSemaphoreGetMutexHolder 1
|
||||
|
||||
/* Diagnostics */
|
||||
#define configCHECK_FOR_STACK_OVERFLOW 1
|
||||
|
||||
/* configASSERT behaviour */
|
||||
extern void vPortFatal(const char* file, int line, const char* message);
|
||||
/* Normal assert() semantics without relying on the provision of an assert.h header file. */
|
||||
|
|
|
@ -76,6 +76,8 @@ class semaphore_lock
|
|||
{
|
||||
public:
|
||||
semaphore_lock(SemaphoreHandle_t semaphore) noexcept;
|
||||
semaphore_lock(semaphore_lock &) = delete;
|
||||
semaphore_lock &operator=(semaphore_lock &) = delete;
|
||||
~semaphore_lock();
|
||||
|
||||
private:
|
||||
|
|
|
@ -61,13 +61,13 @@
|
|||
#if( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH < 0 ) )
|
||||
|
||||
/* Only the current stack state is to be checked. */
|
||||
#define taskCHECK_FOR_STACK_OVERFLOW() \
|
||||
{ \
|
||||
/* Is the currently saved stack pointer within the stack limit? */ \
|
||||
if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack ) \
|
||||
{ \
|
||||
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \
|
||||
} \
|
||||
#define taskCHECK_FOR_STACK_OVERFLOW() \
|
||||
{ \
|
||||
/* Is the currently saved stack pointer within the stack limit? */ \
|
||||
if( pxCurrentTCB[uxPsrId]->pxTopOfStack <= pxCurrentTCB[uxPsrId]->pxStack ) \
|
||||
{ \
|
||||
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB[uxPsrId], pxCurrentTCB[uxPsrId]->pcTaskName ); \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */
|
||||
|
|
|
@ -192,6 +192,7 @@ static void dma_add_free();
|
|||
|
||||
int io_read(handle_t file, uint8_t *buffer, size_t len)
|
||||
{
|
||||
configASSERT(file >= HANDLE_OFFSET);
|
||||
_file *rfile = (_file *)handles_[file - HANDLE_OFFSET];
|
||||
/* clang-format off */
|
||||
DEFINE_READ_PROXY(uart_driver)
|
||||
|
@ -260,6 +261,7 @@ int io_close(handle_t file)
|
|||
{
|
||||
if (file)
|
||||
{
|
||||
configASSERT(file >= HANDLE_OFFSET);
|
||||
_file *rfile = (_file *)handles_[file - HANDLE_OFFSET];
|
||||
io_free(rfile);
|
||||
atomic_set(handles_ + file - HANDLE_OFFSET, 0);
|
||||
|
@ -270,6 +272,7 @@ int io_close(handle_t file)
|
|||
|
||||
int io_write(handle_t file, const uint8_t *buffer, size_t len)
|
||||
{
|
||||
configASSERT(file >= HANDLE_OFFSET);
|
||||
_file *rfile = (_file *)handles_[file - HANDLE_OFFSET];
|
||||
/* clang-format off */
|
||||
DEFINE_WRITE_PROXY(uart_driver)
|
||||
|
@ -294,13 +297,13 @@ int io_control(handle_t file, uint32_t control_code, const uint8_t *write_buffer
|
|||
/* Device IO Implementation Helper Macros */
|
||||
|
||||
#define COMMON_ENTRY(t) \
|
||||
configASSERT(file); \
|
||||
configASSERT(file >= HANDLE_OFFSET); \
|
||||
_file *rfile = (_file *)handles_[file - HANDLE_OFFSET]; \
|
||||
configASSERT(rfile && rfile->object.is<t##_driver>()); \
|
||||
auto t = rfile->object.as<t##_driver>();
|
||||
|
||||
#define COMMON_ENTRY_FILE(file, t) \
|
||||
configASSERT(file); \
|
||||
configASSERT(file >= HANDLE_OFFSET); \
|
||||
_file *rfile = (_file *)handles_[file - HANDLE_OFFSET]; \
|
||||
configASSERT(rfile && rfile->object.is<t##_driver>()); \
|
||||
auto t = rfile->object.as<t##_driver>();
|
||||
|
|
|
@ -28,6 +28,9 @@ typedef struct
|
|||
int ret;
|
||||
} main_thunk_param_t;
|
||||
|
||||
static StaticTask_t s_idle_task;
|
||||
static StackType_t s_idle_task_stack[configMINIMAL_STACK_SIZE];
|
||||
|
||||
void start_scheduler(int core_id);
|
||||
|
||||
static void main_thunk(void *p)
|
||||
|
@ -91,3 +94,23 @@ void start_scheduler(int core_id)
|
|||
void vApplicationIdleHook(void)
|
||||
{
|
||||
}
|
||||
|
||||
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize)
|
||||
{
|
||||
/* Pass out a pointer to the StaticTask_t structure in which the Idle task's
|
||||
state will be stored. */
|
||||
*ppxIdleTaskTCBBuffer = &s_idle_task;
|
||||
|
||||
/* Pass out the array that will be used as the Idle task's stack. */
|
||||
*ppxIdleTaskStackBuffer = s_idle_task_stack;
|
||||
|
||||
/* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
|
||||
Note that, as the array is necessarily of type StackType_t,
|
||||
configMINIMAL_STACK_SIZE is specified in words, not bytes. */
|
||||
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
|
||||
}
|
||||
|
||||
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
|
||||
{
|
||||
configASSERT(!"Stackoverflow !");
|
||||
}
|
||||
|
|
|
@ -199,6 +199,8 @@ void vPortFatal(const char* file, int line, const char* message)
|
|||
portDISABLE_INTERRUPTS();
|
||||
corelock_lock(&xCoreLock);
|
||||
LOGE("FreeRTOS", "(%s:%d) %s", file, line, message);
|
||||
while (1)
|
||||
;
|
||||
exit(-1);
|
||||
while (1)
|
||||
;
|
||||
|
|
|
@ -273,14 +273,15 @@
|
|||
mret
|
||||
.endm
|
||||
|
||||
xPortSysTickInt:
|
||||
portSAVE_CONTEXT
|
||||
jal vPortSysTickHandler
|
||||
portRESTORE_CONTEXT
|
||||
|
||||
xPortStartScheduler:
|
||||
jal vPortSetupTimer
|
||||
portRESTORE_CONTEXT
|
||||
|
||||
vPortEndScheduler:
|
||||
ret
|
||||
|
||||
.section .text.systick, "ax", @progbits
|
||||
xPortSysTickInt:
|
||||
portSAVE_CONTEXT
|
||||
call vPortSysTickHandler
|
||||
portRESTORE_CONTEXT
|
|
@ -638,7 +638,7 @@ static void prvAddNewTaskToReadyList( UBaseType_t xProcessorId, TCB_t *pxNewTCB
|
|||
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
|
||||
|
||||
prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL );
|
||||
prvAddNewTaskToReadyList( pxNewTCB );
|
||||
prvAddNewTaskToReadyList( uxPortGetProcessorId(), pxNewTCB );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1962,7 +1962,7 @@ BaseType_t xReturn;
|
|||
UBaseType_t uxPsrId = uxPortGetProcessorId();
|
||||
|
||||
/* Add the idle task at the lowest priority. */
|
||||
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||
#if( configSUPPORT_STATIC_ALLOCATION == 1 && 0)
|
||||
{
|
||||
StaticTask_t *pxIdleTaskTCBBuffer = NULL;
|
||||
StackType_t *pxIdleTaskStackBuffer = NULL;
|
||||
|
@ -2833,12 +2833,13 @@ UBaseType_t uxPsrId = uxPortGetProcessorId();
|
|||
void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction )
|
||||
{
|
||||
TCB_t *xTCB;
|
||||
UBaseType_t uxPsrId = uxPortGetProcessorId();
|
||||
|
||||
/* If xTask is NULL then it is the task hook of the calling task that is
|
||||
getting set. */
|
||||
if( xTask == NULL )
|
||||
{
|
||||
xTCB = ( TCB_t * ) pxCurrentTCB;
|
||||
xTCB = ( TCB_t * ) pxCurrentTCB[uxPsrId];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2860,12 +2861,13 @@ UBaseType_t uxPsrId = uxPortGetProcessorId();
|
|||
TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask )
|
||||
{
|
||||
TCB_t *xTCB;
|
||||
UBaseType_t uxPsrId = uxPortGetProcessorId();
|
||||
TaskHookFunction_t xReturn;
|
||||
|
||||
/* If xTask is NULL then we are setting our own task hook. */
|
||||
if( xTask == NULL )
|
||||
{
|
||||
xTCB = ( TCB_t * ) pxCurrentTCB;
|
||||
xTCB = ( TCB_t * ) pxCurrentTCB[uxPsrId];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2891,12 +2893,13 @@ UBaseType_t uxPsrId = uxPortGetProcessorId();
|
|||
BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter )
|
||||
{
|
||||
TCB_t *xTCB;
|
||||
UBaseType_t uxPsrId = uxPortGetProcessorId();
|
||||
BaseType_t xReturn;
|
||||
|
||||
/* If xTask is NULL then we are calling our own task hook. */
|
||||
if( xTask == NULL )
|
||||
{
|
||||
xTCB = ( TCB_t * ) pxCurrentTCB;
|
||||
xTCB = ( TCB_t * ) pxCurrentTCB[uxPsrId];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3464,6 +3467,7 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
|
|||
void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue )
|
||||
{
|
||||
TCB_t *pxTCB;
|
||||
UBaseType_t uxPsrId = uxPortGetProcessorId();
|
||||
|
||||
if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS )
|
||||
{
|
||||
|
@ -3481,6 +3485,7 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
|
|||
{
|
||||
void *pvReturn = NULL;
|
||||
TCB_t *pxTCB;
|
||||
UBaseType_t uxPsrId = uxPortGetProcessorId();
|
||||
|
||||
if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS )
|
||||
{
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
#project(bsp)
|
||||
|
||||
# create bsp library
|
||||
FILE(GLOB_RECURSE LIB_SRC_NOASM
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.c"
|
||||
)
|
||||
|
||||
FILE(GLOB_RECURSE ASSEMBLY_FILES
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.s"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.S"
|
||||
)
|
||||
SET(LIB_SRC ${LIB_SRC_NOASM} ${ASSEMBLY_FILES})
|
||||
|
||||
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
|
||||
SET_PROPERTY(SOURCE ${ASSEMBLY_FILES} PROPERTY LANGUAGE C)
|
||||
SET_SOURCE_FILES_PROPERTIES(${ASSEMBLY_FILES} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp -D __riscv64")
|
||||
|
||||
ADD_LIBRARY(posix STATIC ${LIB_SRC})
|
||||
SET_TARGET_PROPERTIES(posix PROPERTIES LINKER_LANGUAGE C)
|
||||
TARGET_LINK_LIBRARIES(posix PRIVATE freertos)
|
|
@ -0,0 +1,18 @@
|
|||
/* Copyright 2018 Canaan Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <cstdint>
|
||||
#include <sys/time.h>
|
||||
|
||||
uint32_t timespec_to_ticks(const struct timespec &ts);
|
|
@ -0,0 +1,47 @@
|
|||
/* Copyright 2018 Canaan Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <FreeRTOS.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void *_aligned_malloc(size_t size, size_t alignment);
|
||||
void _aligned_free(void *ptr);
|
||||
}
|
||||
|
||||
void *_aligned_malloc(size_t size, size_t alignment)
|
||||
{
|
||||
auto offset = alignment - 1 + sizeof(void *);
|
||||
auto p_head = malloc(size + offset);
|
||||
if (p_head)
|
||||
{
|
||||
auto p_ret = (uintptr_t(p_head) + offset) & ~(alignment - 1);
|
||||
auto p_link = reinterpret_cast<void **>(p_ret - sizeof(void *));
|
||||
*p_link = p_head;
|
||||
return reinterpret_cast<void *>(p_ret);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void _aligned_free(void *ptr)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
auto puc = uintptr_t(ptr);
|
||||
auto p_link = reinterpret_cast<void **>(puc - sizeof(void *));
|
||||
free(*p_link);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/* Copyright 2018 Canaan Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <sys/unistd.h>
|
||||
|
||||
long sysconf(int __name)
|
||||
{
|
||||
switch (__name)
|
||||
{
|
||||
case _SC_NPROCESSORS_CONF:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,328 @@
|
|||
/* Copyright 2018 Canaan Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <FreeRTOS.h>
|
||||
#include <atomic.h>
|
||||
#include <atomic>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <kernel/driver_impl.hpp>
|
||||
#include <platform.h>
|
||||
#include <pthread.h>
|
||||
#include <semphr.h>
|
||||
#include <task.h>
|
||||
#include <unordered_map>
|
||||
|
||||
// Workaround for keeping pthread functions
|
||||
void *g_pthread_keep[] = {
|
||||
(void *)pthread_cond_init,
|
||||
(void *)pthread_mutex_init,
|
||||
(void *)pthread_self
|
||||
};
|
||||
|
||||
static const pthread_attr_t s_default_thread_attributes = {
|
||||
.stacksize = 4096,
|
||||
.schedparam = { .sched_priority = tskIDLE_PRIORITY },
|
||||
.detachstate = PTHREAD_CREATE_JOINABLE
|
||||
};
|
||||
|
||||
struct k_pthread_key
|
||||
{
|
||||
void (*destructor)(void *);
|
||||
};
|
||||
|
||||
struct k_pthread_tls
|
||||
{
|
||||
std::unordered_map<pthread_key_t, uintptr_t> storage;
|
||||
};
|
||||
|
||||
struct k_pthread
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
StaticSemaphore_t join_mutex;
|
||||
StaticSemaphore_t join_barrier;
|
||||
void *(*startroutine)(void *);
|
||||
void *arg;
|
||||
TaskHandle_t handle;
|
||||
void *ret;
|
||||
|
||||
k_pthread(pthread_attr_t attr, void *(*startroutine)(void *), void *arg) noexcept
|
||||
: attr(attr), startroutine(startroutine), arg(arg)
|
||||
{
|
||||
if (attr.detachstate == PTHREAD_CREATE_JOINABLE)
|
||||
{
|
||||
xSemaphoreCreateMutexStatic(&join_mutex);
|
||||
xSemaphoreCreateBinaryStatic(&join_barrier);
|
||||
}
|
||||
}
|
||||
|
||||
BaseType_t create() noexcept
|
||||
{
|
||||
auto ret = xTaskCreate(thread_thunk, "posix", (uint16_t)(attr.stacksize / sizeof(StackType_t)), this, attr.schedparam.sched_priority, &handle);
|
||||
if (ret == pdPASS)
|
||||
{
|
||||
/* Store the pointer to the thread object in the task tag. */
|
||||
vTaskSetApplicationTaskTag(handle, (TaskHookFunction_t)this);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cancel() noexcept
|
||||
{
|
||||
vTaskSuspendAll();
|
||||
on_exit();
|
||||
xTaskResumeAll();
|
||||
}
|
||||
|
||||
private:
|
||||
static void thread_thunk(void *arg)
|
||||
{
|
||||
k_pthread *k_thread = reinterpret_cast<k_pthread *>(arg);
|
||||
k_thread->ret = k_thread->startroutine(k_thread->arg);
|
||||
|
||||
k_thread->on_exit();
|
||||
}
|
||||
|
||||
void on_exit()
|
||||
{
|
||||
/* If this thread is joinable, wait for a call to pthread_join. */
|
||||
if (attr.detachstate == PTHREAD_CREATE_JOINABLE)
|
||||
{
|
||||
xSemaphoreGive(&join_barrier);
|
||||
/* Suspend until the call to pthread_join. The caller of pthread_join
|
||||
* will perform cleanup. */
|
||||
vTaskSuspend(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For a detached thread, perform cleanup of thread object. */
|
||||
delete this;
|
||||
delete reinterpret_cast<k_pthread_tls *>(pvTaskGetThreadLocalStoragePointer(NULL, PTHREAD_TLS_INDEX));
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*startroutine)(void *), void *arg)
|
||||
{
|
||||
int iStatus = 0;
|
||||
k_pthread *k_thrd = NULL;
|
||||
|
||||
/* Allocate memory for new thread object. */
|
||||
k_thrd = new (std::nothrow) k_pthread(attr ? *attr : s_default_thread_attributes, startroutine, arg);
|
||||
|
||||
if (!k_thrd)
|
||||
{
|
||||
/* No memory. */
|
||||
iStatus = EAGAIN;
|
||||
}
|
||||
|
||||
if (iStatus == 0)
|
||||
{
|
||||
/* Suspend all tasks to create a critical section. This ensures that
|
||||
* the new thread doesn't exit before a tag is assigned. */
|
||||
vTaskSuspendAll();
|
||||
|
||||
/* Create the FreeRTOS task that will run the pthread. */
|
||||
if (k_thrd->create() != pdPASS)
|
||||
{
|
||||
/* Task creation failed, no memory. */
|
||||
delete k_thrd;
|
||||
iStatus = EAGAIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set the thread object for the user. */
|
||||
*thread = reinterpret_cast<uintptr_t>(k_thrd);
|
||||
}
|
||||
|
||||
/* End the critical section. */
|
||||
xTaskResumeAll();
|
||||
}
|
||||
|
||||
return iStatus;
|
||||
}
|
||||
|
||||
int pthread_join(pthread_t pthread, void **retval)
|
||||
{
|
||||
int iStatus = 0;
|
||||
k_pthread *k_thrd = reinterpret_cast<k_pthread *>(pthread);
|
||||
|
||||
/* Make sure pthread is joinable. Otherwise, this function would block
|
||||
* forever waiting for an unjoinable thread. */
|
||||
if (k_thrd->attr.detachstate != PTHREAD_CREATE_JOINABLE)
|
||||
{
|
||||
iStatus = EDEADLK;
|
||||
}
|
||||
|
||||
/* Only one thread may attempt to join another. Lock the join mutex
|
||||
* to prevent other threads from calling pthread_join on the same thread. */
|
||||
if (iStatus == 0)
|
||||
{
|
||||
if (xSemaphoreTake(&k_thrd->join_mutex, 0) != pdPASS)
|
||||
{
|
||||
/* Another thread has already joined the requested thread, which would
|
||||
* cause this thread to wait forever. */
|
||||
iStatus = EDEADLK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Attempting to join the calling thread would cause a deadlock. */
|
||||
if (iStatus == 0)
|
||||
{
|
||||
if (pthread_equal(pthread_self(), pthread) != 0)
|
||||
{
|
||||
iStatus = EDEADLK;
|
||||
}
|
||||
}
|
||||
|
||||
if (iStatus == 0)
|
||||
{
|
||||
/* Wait for the joining thread to finish. Because this call waits forever,
|
||||
* it should never fail. */
|
||||
(void)xSemaphoreTake(&k_thrd->join_barrier, portMAX_DELAY);
|
||||
|
||||
/* Create a critical section to clean up the joined thread. */
|
||||
vTaskSuspendAll();
|
||||
|
||||
/* Release xJoinBarrier and delete it. */
|
||||
(void)xSemaphoreGive(&k_thrd->join_barrier);
|
||||
vSemaphoreDelete(&k_thrd->join_barrier);
|
||||
|
||||
/* Release xJoinMutex and delete it. */
|
||||
(void)xSemaphoreGive(&k_thrd->join_mutex);
|
||||
vSemaphoreDelete(&k_thrd->join_mutex);
|
||||
|
||||
/* Set the return value. */
|
||||
if (retval != NULL)
|
||||
{
|
||||
*retval = k_thrd->ret;
|
||||
}
|
||||
|
||||
/* Free the thread object. */
|
||||
delete reinterpret_cast<k_pthread_tls *>(pvTaskGetThreadLocalStoragePointer(k_thrd->handle, PTHREAD_TLS_INDEX));
|
||||
/* Delete the FreeRTOS task that ran the thread. */
|
||||
vTaskDelete(k_thrd->handle);
|
||||
delete k_thrd;
|
||||
|
||||
/* End the critical section. */
|
||||
xTaskResumeAll();
|
||||
}
|
||||
|
||||
return iStatus;
|
||||
}
|
||||
|
||||
pthread_t pthread_self(void)
|
||||
{
|
||||
/* Return a reference to this pthread object, which is stored in the
|
||||
* FreeRTOS task tag. */
|
||||
return (uintptr_t)xTaskGetApplicationTaskTag(NULL);
|
||||
}
|
||||
|
||||
int pthread_cancel(pthread_t pthread)
|
||||
{
|
||||
k_pthread *k_thrd = reinterpret_cast<k_pthread *>(pthread);
|
||||
|
||||
k_thrd->cancel();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_key_create(pthread_key_t *__key, void (*__destructor)(void *))
|
||||
{
|
||||
auto k_key = new (std::nothrow) k_pthread_key;
|
||||
if (k_key)
|
||||
{
|
||||
k_key->destructor = __destructor;
|
||||
|
||||
*__key = reinterpret_cast<uintptr_t>(k_key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
int pthread_key_delete(pthread_key_t key)
|
||||
{
|
||||
auto k_key = reinterpret_cast<k_pthread_key *>(key);
|
||||
delete k_key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *pthread_getspecific(pthread_key_t key)
|
||||
{
|
||||
auto tls = reinterpret_cast<k_pthread_tls *>(pvTaskGetThreadLocalStoragePointer(NULL, PTHREAD_TLS_INDEX));
|
||||
|
||||
if (tls)
|
||||
{
|
||||
auto it = tls->storage.find(key);
|
||||
if (it != tls->storage.end())
|
||||
return reinterpret_cast<void *>(it->second);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int pthread_setspecific(pthread_key_t key, const void *value)
|
||||
{
|
||||
auto tls = reinterpret_cast<k_pthread_tls *>(pvTaskGetThreadLocalStoragePointer(NULL, PTHREAD_TLS_INDEX));
|
||||
if (!tls)
|
||||
{
|
||||
tls = new (std::nothrow) k_pthread_tls;
|
||||
if (!tls)
|
||||
return ENOMEM;
|
||||
vTaskSetThreadLocalStoragePointer(NULL, PTHREAD_TLS_INDEX, tls);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
tls->storage[key] = uintptr_t(value);
|
||||
return 0;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (atomic_read(&once_control->init_executed) == 1)
|
||||
return 0;
|
||||
|
||||
if (atomic_cas(&once_control->init_executed, 0, 2) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
init_routine();
|
||||
atomic_set(&once_control->init_executed, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_equal(pthread_t t1, pthread_t t2)
|
||||
{
|
||||
int iStatus = 0;
|
||||
|
||||
/* Compare the thread IDs. */
|
||||
if (t1 && t2)
|
||||
{
|
||||
iStatus = (t1 == t2);
|
||||
}
|
||||
|
||||
return iStatus;
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
/* Copyright 2018 Canaan Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "utils.h"
|
||||
#include <FreeRTOS.h>
|
||||
#include <atomic>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <kernel/driver_impl.hpp>
|
||||
#include <platform.h>
|
||||
#include <pthread.h>
|
||||
#include <semphr.h>
|
||||
#include <task.h>
|
||||
|
||||
using namespace sys;
|
||||
|
||||
static const pthread_condattr_t s_default_cond_attributes = {
|
||||
.is_initialized = true,
|
||||
.clock = portMAX_DELAY
|
||||
};
|
||||
|
||||
struct k_pthread_cond
|
||||
{
|
||||
StaticSemaphore_t mutex;
|
||||
StaticSemaphore_t wait_semphr;
|
||||
uint32_t waiting_threads;
|
||||
|
||||
k_pthread_cond() noexcept
|
||||
: waiting_threads(0)
|
||||
{
|
||||
xSemaphoreCreateMutexStatic(&mutex);
|
||||
xSemaphoreCreateCountingStatic(UINT_MAX, 0U, &wait_semphr);
|
||||
}
|
||||
|
||||
~k_pthread_cond()
|
||||
{
|
||||
vSemaphoreDelete(&mutex);
|
||||
vSemaphoreDelete(&wait_semphr);
|
||||
}
|
||||
|
||||
semaphore_lock lock() noexcept
|
||||
{
|
||||
return { &mutex };
|
||||
}
|
||||
|
||||
void give() noexcept
|
||||
{
|
||||
xSemaphoreGive(&wait_semphr);
|
||||
waiting_threads--;
|
||||
}
|
||||
};
|
||||
|
||||
static void pthread_cond_init_if_static(pthread_cond_t *cond)
|
||||
{
|
||||
if (*cond == PTHREAD_COND_INITIALIZER)
|
||||
{
|
||||
configASSERT(pthread_cond_init(cond, nullptr) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
int pthread_condattr_init(pthread_condattr_t *__attr)
|
||||
{
|
||||
*__attr = s_default_cond_attributes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_condattr_destroy(pthread_condattr_t *__attr)
|
||||
{
|
||||
__attr->is_initialized = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_condattr_getclock(const pthread_condattr_t *__attr, clockid_t *__clock_id)
|
||||
{
|
||||
*__clock_id = __attr->clock;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_condattr_setclock(pthread_condattr_t *__attr, clockid_t __clock_id)
|
||||
{
|
||||
__attr->clock = __clock_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_condattr_getpshared(const pthread_condattr_t *__attr, int *__pshared)
|
||||
{
|
||||
*__pshared = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_condattr_setpshared(pthread_condattr_t *__attr, int __pshared)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
|
||||
{
|
||||
int iStatus = 0;
|
||||
k_pthread_cond *k_cond = nullptr;
|
||||
|
||||
/* Silence warnings about unused parameters. */
|
||||
(void)attr;
|
||||
|
||||
k_cond = new (std::nothrow) k_pthread_cond();
|
||||
|
||||
if (!k_cond)
|
||||
{
|
||||
iStatus = ENOMEM;
|
||||
}
|
||||
|
||||
if (iStatus == 0)
|
||||
{
|
||||
/* Set the output. */
|
||||
*cond = reinterpret_cast<uintptr_t>(k_cond);
|
||||
}
|
||||
|
||||
return iStatus;
|
||||
}
|
||||
|
||||
int pthread_cond_destroy(pthread_cond_t *cond)
|
||||
{
|
||||
k_pthread_cond *k_cond = reinterpret_cast<k_pthread_cond *>(*cond);
|
||||
|
||||
delete k_cond;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
|
||||
{
|
||||
return pthread_cond_timedwait(cond, mutex, NULL);
|
||||
}
|
||||
|
||||
int pthread_cond_signal(pthread_cond_t *cond)
|
||||
{
|
||||
pthread_cond_init_if_static(cond);
|
||||
k_pthread_cond *k_cond = reinterpret_cast<k_pthread_cond *>(*cond);
|
||||
|
||||
/* Check that at least one thread is waiting for a signal. */
|
||||
if (k_cond->waiting_threads)
|
||||
{
|
||||
/* Lock xCondMutex to protect access to iWaitingThreads.
|
||||
* This call will never fail because it blocks forever. */
|
||||
auto lock = k_cond->lock();
|
||||
xSemaphoreTake(&k_cond->mutex, portMAX_DELAY);
|
||||
|
||||
/* Check again that at least one thread is waiting for a signal after
|
||||
* taking xCondMutex. If so, unblock it. */
|
||||
if (k_cond->waiting_threads)
|
||||
k_cond->give();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
|
||||
{
|
||||
int iStatus = 0;
|
||||
pthread_cond_init_if_static(cond);
|
||||
k_pthread_cond *k_cond = reinterpret_cast<k_pthread_cond *>(*cond);
|
||||
TickType_t xDelay = portMAX_DELAY;
|
||||
|
||||
/* Convert abstime to a delay in TickType_t if provided. */
|
||||
if (abstime != NULL)
|
||||
{
|
||||
xDelay = timespec_to_ticks(*abstime);
|
||||
}
|
||||
|
||||
/* Increase the counter of threads blocking on condition variable, then
|
||||
* unlock mutex. */
|
||||
if (iStatus == 0)
|
||||
{
|
||||
{
|
||||
auto lock = k_cond->lock();
|
||||
k_cond->waiting_threads++;
|
||||
}
|
||||
|
||||
iStatus = pthread_mutex_unlock(mutex);
|
||||
}
|
||||
|
||||
/* Wait on the condition variable. */
|
||||
if (iStatus == 0)
|
||||
{
|
||||
if (xSemaphoreTake(&k_cond->wait_semphr, xDelay) == pdPASS)
|
||||
{
|
||||
/* When successful, relock mutex. */
|
||||
iStatus = pthread_mutex_lock(mutex);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Timeout. Relock mutex and decrement number of waiting threads. */
|
||||
iStatus = ETIMEDOUT;
|
||||
(void)pthread_mutex_lock(mutex);
|
||||
|
||||
{
|
||||
auto lock = k_cond->lock();
|
||||
k_cond->waiting_threads--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iStatus;
|
||||
}
|
||||
|
||||
int pthread_cond_broadcast(pthread_cond_t *cond)
|
||||
{
|
||||
int i = 0;
|
||||
pthread_cond_init_if_static(cond);
|
||||
k_pthread_cond *k_cond = reinterpret_cast<k_pthread_cond *>(*cond);
|
||||
|
||||
/* Lock xCondMutex to protect access to iWaitingThreads.
|
||||
* This call will never fail because it blocks forever. */
|
||||
auto locker = k_cond->lock();
|
||||
|
||||
/* Unblock all threads waiting on this condition variable. */
|
||||
for (i = 0; i < k_cond->waiting_threads; i++)
|
||||
{
|
||||
xSemaphoreGive(&k_cond->wait_semphr);
|
||||
}
|
||||
|
||||
/* All threads were unblocked, set waiting threads to 0. */
|
||||
k_cond->waiting_threads = 0;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
/* Copyright 2018 Canaan Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "utils.h"
|
||||
#include <FreeRTOS.h>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <memory>
|
||||
#include <platform.h>
|
||||
#include <pthread.h>
|
||||
#include <semphr.h>
|
||||
#include <task.h>
|
||||
|
||||
static const pthread_mutexattr_t s_default_mutex_attributes = {
|
||||
.is_initialized = true,
|
||||
.type = PTHREAD_MUTEX_DEFAULT,
|
||||
.recursive = 0
|
||||
};
|
||||
|
||||
struct k_pthread_mutex
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
StaticSemaphore_t semphr;
|
||||
TaskHandle_t owner;
|
||||
|
||||
k_pthread_mutex(pthread_mutexattr_t attr) noexcept
|
||||
: attr(attr)
|
||||
{
|
||||
if (attr.type == PTHREAD_MUTEX_RECURSIVE)
|
||||
xSemaphoreCreateRecursiveMutexStatic(&semphr);
|
||||
else
|
||||
xSemaphoreCreateMutexStatic(&semphr);
|
||||
}
|
||||
|
||||
void give() noexcept
|
||||
{
|
||||
if (attr.type == PTHREAD_MUTEX_RECURSIVE)
|
||||
xSemaphoreGiveRecursive(&semphr);
|
||||
else
|
||||
xSemaphoreGive(&semphr);
|
||||
}
|
||||
|
||||
void update_owner() noexcept
|
||||
{
|
||||
owner = xSemaphoreGetMutexHolder(&semphr);
|
||||
}
|
||||
};
|
||||
|
||||
static void pthread_mutex_init_if_static(pthread_mutex_t *mutex)
|
||||
{
|
||||
if (*mutex == PTHREAD_MUTEX_INITIALIZER)
|
||||
{
|
||||
configASSERT(pthread_mutex_init(mutex, nullptr) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
int pthread_mutexattr_init(pthread_mutexattr_t *__attr)
|
||||
{
|
||||
*__attr = s_default_mutex_attributes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_mutexattr_destroy(pthread_mutexattr_t *__attr)
|
||||
{
|
||||
__attr->is_initialized = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *__attr, int *__pshared)
|
||||
{
|
||||
*__pshared = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_mutexattr_setpshared(pthread_mutexattr_t *__attr, int __pshared)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_mutexattr_gettype(const pthread_mutexattr_t * __attr, int *__kind)
|
||||
{
|
||||
*__kind = __attr->type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_mutexattr_settype(pthread_mutexattr_t * __attr, int __kind)
|
||||
{
|
||||
__attr->type = __kind;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
|
||||
{
|
||||
int iStatus = 0;
|
||||
k_pthread_mutex *k_mutex = nullptr;
|
||||
|
||||
k_mutex = new (std::nothrow) k_pthread_mutex(attr ? *attr : s_default_mutex_attributes);
|
||||
|
||||
if (!k_mutex)
|
||||
{
|
||||
iStatus = ENOMEM;
|
||||
}
|
||||
|
||||
if (iStatus == 0)
|
||||
{
|
||||
/* Set the output. */
|
||||
*mutex = reinterpret_cast<uintptr_t>(k_mutex);
|
||||
}
|
||||
|
||||
return iStatus;
|
||||
}
|
||||
|
||||
int pthread_mutex_destroy(pthread_mutex_t *mutex)
|
||||
{
|
||||
k_pthread_mutex *k_mutex = reinterpret_cast<k_pthread_mutex *>(*mutex);
|
||||
|
||||
/* Free resources in use by the mutex. */
|
||||
if (k_mutex->owner == NULL)
|
||||
{
|
||||
delete k_mutex;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_mutex_lock(pthread_mutex_t *mutex)
|
||||
{
|
||||
return pthread_mutex_timedlock(mutex, NULL);
|
||||
}
|
||||
|
||||
int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime)
|
||||
{
|
||||
pthread_mutex_init_if_static(mutex);
|
||||
|
||||
int iStatus = 0;
|
||||
k_pthread_mutex *k_mutex = reinterpret_cast<k_pthread_mutex *>(*mutex);
|
||||
TickType_t xDelay = portMAX_DELAY;
|
||||
BaseType_t xFreeRTOSMutexTakeStatus = pdFALSE;
|
||||
|
||||
/* Convert abstime to a delay in TickType_t if provided. */
|
||||
if (abstime != NULL)
|
||||
xDelay = timespec_to_ticks(*abstime);
|
||||
|
||||
/* Check if trying to lock a currently owned mutex. */
|
||||
if ((iStatus == 0) && (k_mutex->attr.type == PTHREAD_MUTEX_ERRORCHECK) && /* Only PTHREAD_MUTEX_ERRORCHECK type detects deadlock. */
|
||||
(k_mutex->owner == xTaskGetCurrentTaskHandle())) /* Check if locking a currently owned mutex. */
|
||||
{
|
||||
iStatus = EDEADLK;
|
||||
}
|
||||
|
||||
if (iStatus == 0)
|
||||
{
|
||||
/* Call the correct FreeRTOS mutex take function based on mutex type. */
|
||||
if (k_mutex->attr.type == PTHREAD_MUTEX_RECURSIVE)
|
||||
{
|
||||
xFreeRTOSMutexTakeStatus = xSemaphoreTakeRecursive(&k_mutex->semphr, xDelay);
|
||||
}
|
||||
else
|
||||
{
|
||||
xFreeRTOSMutexTakeStatus = xSemaphoreTake(&k_mutex->semphr, xDelay);
|
||||
}
|
||||
|
||||
/* If the mutex was successfully taken, set its owner. */
|
||||
if (xFreeRTOSMutexTakeStatus == pdPASS)
|
||||
{
|
||||
k_mutex->owner = xTaskGetCurrentTaskHandle();
|
||||
}
|
||||
/* Otherwise, the mutex take timed out. */
|
||||
else
|
||||
{
|
||||
iStatus = ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
return iStatus;
|
||||
}
|
||||
|
||||
int pthread_mutex_trylock(pthread_mutex_t *mutex)
|
||||
{
|
||||
int iStatus = 0;
|
||||
struct timespec xTimeout = {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = 0
|
||||
};
|
||||
|
||||
/* Attempt to lock with no timeout. */
|
||||
iStatus = pthread_mutex_timedlock(mutex, &xTimeout);
|
||||
|
||||
/* POSIX specifies that this function should return EBUSY instead of
|
||||
* ETIMEDOUT for attempting to lock a locked mutex. */
|
||||
if (iStatus == ETIMEDOUT)
|
||||
{
|
||||
iStatus = EBUSY;
|
||||
}
|
||||
|
||||
return iStatus;
|
||||
}
|
||||
|
||||
int pthread_mutex_unlock(pthread_mutex_t *mutex)
|
||||
{
|
||||
pthread_mutex_init_if_static(mutex);
|
||||
|
||||
int iStatus = 0;
|
||||
k_pthread_mutex *k_mutex = reinterpret_cast<k_pthread_mutex *>(*mutex);
|
||||
|
||||
/* Check if trying to unlock an unowned mutex. */
|
||||
if (((k_mutex->attr.type == PTHREAD_MUTEX_ERRORCHECK) || (k_mutex->attr.type == PTHREAD_MUTEX_RECURSIVE)) && (k_mutex->owner != xTaskGetCurrentTaskHandle()))
|
||||
{
|
||||
iStatus = EPERM;
|
||||
}
|
||||
|
||||
if (iStatus == 0)
|
||||
{
|
||||
/* Call the correct FreeRTOS mutex unlock function based on mutex type. */
|
||||
k_mutex->give();
|
||||
|
||||
/* Update the owner of the mutex. A recursive mutex may still have an
|
||||
* owner, so it should be updated with xSemaphoreGetMutexHolder. */
|
||||
k_mutex->update_owner();
|
||||
}
|
||||
|
||||
return iStatus;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/* Copyright 2018 Canaan Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "utils.h"
|
||||
#include <FreeRTOS.h>
|
||||
|
||||
uint32_t timespec_to_ticks(const struct timespec &ts)
|
||||
{
|
||||
uint64_t nsec_ms = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
||||
|
||||
return pdMS_TO_TICKS(nsec_ms);
|
||||
}
|
Loading…
Reference in New Issue