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 freq
feature/tflite-kpu
sunnycase 2018-11-12 16:12:51 +08:00 committed by GitHub
parent 3277fbc06a
commit be6ac3011e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1043 additions and 68 deletions

View File

@ -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)

View File

@ -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)

View File

@ -20,7 +20,7 @@ add_compile_flags(BOTH
-fdata-sections
-fstrict-volatile-bitfields
-fno-zero-initialized-in-bss
-Os
-O2
-ggdb
)

View File

@ -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()

View File

@ -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)

View File

@ -51,6 +51,7 @@ SECTIONS
.text.start :
{
KEEP( *(.text.start) )
KEEP( *(.text.systick) )
} > ram : DATA
.init :

View File

@ -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)

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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)

View File

@ -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));

View File

@ -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. */

View 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:

View File

@ -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 */

View File

@ -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>();

View File

@ -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 !");
}

View File

@ -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)
;

View File

@ -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

View File

@ -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 )
{

22
lib/posix/CMakeLists.txt Normal file
View File

@ -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)

18
lib/posix/include/utils.h Normal file
View File

@ -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);

47
lib/posix/memory.cpp Normal file
View File

@ -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);
}
}

28
lib/posix/posix_conf.cpp Normal file
View File

@ -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;
}

328
lib/posix/pthread.cpp Normal file
View File

@ -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;
}

236
lib/posix/pthread_cond.cpp Normal file
View File

@ -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;
}

235
lib/posix/pthread_mutex.cpp Normal file
View File

@ -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;
}

23
lib/posix/utils.cpp Normal file
View File

@ -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);
}