NOT WORKING!!!!
This commit is contained in:
parent
24a458858d
commit
2475039543
|
@ -0,0 +1,101 @@
|
|||
/*-------------------------------------------------------
|
||||
|
|
||||
| bifrost_thread.h
|
||||
|
|
||||
| Contains Bifrost threading APIs,
|
||||
|
|
||||
|--------------------------------------------------------
|
||||
|
|
||||
| Copyright ( C ) 2016 Microsoft Corp.
|
||||
| All Rights Reserved
|
||||
| Confidential and Proprietary
|
||||
|
|
||||
|--------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "linked_list.h"
|
||||
#include "bifrost_sim.h"
|
||||
#include "avl_tree.h"
|
||||
|
||||
#define STATE_NUM (5)
|
||||
|
||||
typedef enum {
|
||||
NEW = 0 ,
|
||||
BLOCK = 1,
|
||||
RUN = 4,
|
||||
READY = 3,
|
||||
EXIT = 2
|
||||
} hw_thread_state_t;
|
||||
|
||||
#define PRIORITY_LEVEL_NUM (5)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HIGHEST = 0,
|
||||
HIGH = 1,
|
||||
MEDIUM = 2,
|
||||
LOW = 3,
|
||||
LOWEST = 4,
|
||||
} hw_thread_priority_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT32 pc;
|
||||
UINT32 sp;
|
||||
UINT32 a2;
|
||||
UINT32 a3;
|
||||
UINT32 a4;
|
||||
UINT32 a5;
|
||||
UINT32 a6;
|
||||
UINT32 a7;
|
||||
UINT32 a8;
|
||||
UINT32 a9;
|
||||
UINT32 a10;
|
||||
UINT32 a11;
|
||||
UINT32 a12;
|
||||
UINT32 a13;
|
||||
UINT32 a14;
|
||||
UINT32 a15;
|
||||
UINT32 ps;
|
||||
UINT32 epc;
|
||||
} __attribute__((packed)) hw_regs_t;
|
||||
|
||||
typedef struct {
|
||||
linked_list_node_t list_node;
|
||||
avl_tree_node_t tree_node;
|
||||
UINT32 thread_id;
|
||||
UINT32 core_id;
|
||||
hw_thread_priority_t priority;
|
||||
void* stack_ptr;
|
||||
UINT32 stack_size;
|
||||
hw_regs_t regs;
|
||||
void (*proc)(void*);
|
||||
void* args;
|
||||
hw_thread_state_t state;
|
||||
INT32 exit_code;
|
||||
HW_MAILBOX mailbox;
|
||||
_Bool initialized;
|
||||
} hw_tcb_t;
|
||||
|
||||
int hw_thread_create(void (*proc)(void*),
|
||||
void* args,
|
||||
hw_thread_priority_t priority,
|
||||
UINT32 stack_size,
|
||||
UINT32* thread_id);
|
||||
|
||||
int hw_thread_start(UINT32 thread_id);
|
||||
|
||||
int hw_thread_block(UINT32 thread_id);
|
||||
|
||||
int hw_thread_resume(UINT32 thread_id);
|
||||
|
||||
int hw_thread_destroy(UINT32 thread_id);
|
||||
|
||||
int hw_thread_get_exit_code(UINT32 thread_id, INT32 *exit_code);
|
||||
|
||||
void hw_thread_exit(INT32 exit_code);
|
||||
|
||||
void hw_thread_init();
|
||||
|
|
@ -0,0 +1,449 @@
|
|||
#include "bifrost_thread.h"
|
||||
#include "avl_tree.h"
|
||||
#include <stdint.h>
|
||||
|
||||
// global structures
|
||||
static hw_tcb_t *g_core_current_thread[HW_PROC_CNT];
|
||||
|
||||
extern UINT32 ts_arch_context_switch_ASM(void *curTestRegs, void *targetTestRegs);
|
||||
extern UINT32 ts_arch_resume_ASM(void *curTestRegs, void *targetTestRegs);
|
||||
|
||||
static HW_LOCK g_avl_lock;
|
||||
static avl_tree_t g_global_thread_tree;
|
||||
|
||||
static HW_LOCK g_core_lock[HW_PROC_CNT];
|
||||
static linked_list_t g_core_thread_list[HW_PROC_CNT][STATE_NUM - 1][PRIORITY_LEVEL_NUM];
|
||||
|
||||
static UINT32 g_thread_id_count;
|
||||
static HW_LOCK g_thread_id_count_lock;
|
||||
static _Bool initialized = false;
|
||||
|
||||
static INT32 _thread_id_compare(avl_tree_node_t *tree_node, avl_tree_node_t *my_node)
|
||||
{
|
||||
hw_tcb_t *tcb = OBTAIN_STRUCT_ADDR(tree_node, tree_node, hw_tcb_t);
|
||||
hw_tcb_t *my_tcb = OBTAIN_STRUCT_ADDR(my_node, tree_node, hw_tcb_t);
|
||||
return tcb->thread_id - my_tcb->thread_id;
|
||||
}
|
||||
|
||||
static _Bool _thread_id_equal(linked_list_node_t *list_node, linked_list_node_t *my_node)
|
||||
{
|
||||
hw_tcb_t *tcb = OBTAIN_STRUCT_ADDR(list_node, list_node, hw_tcb_t);
|
||||
hw_tcb_t *my_tcb = OBTAIN_STRUCT_ADDR(my_node, list_node, hw_tcb_t);
|
||||
return tcb->thread_id == my_tcb->thread_id;
|
||||
}
|
||||
|
||||
static hw_tcb_t *_get_tcb_by_id(uint32_t thread_id)
|
||||
{
|
||||
avl_tree_node_t *temp;
|
||||
hw_tcb_t clone, *result;
|
||||
clone.thread_id = thread_id;
|
||||
temp = avl_tree_search(&g_global_thread_tree, &clone.tree_node);
|
||||
return temp == NULL ? NULL : OBTAIN_STRUCT_ADDR(temp, tree_node, hw_tcb_t);
|
||||
}
|
||||
|
||||
static void _adjust_queue(int core_num, hw_thread_state_t old, hw_thread_state_t new, hw_thread_state_t cond)
|
||||
{
|
||||
for (int priority = 0; priority < PRIORITY_LEVEL_NUM; priority++)
|
||||
{
|
||||
linked_list_t *list = &g_core_thread_list[core_num][old][priority];
|
||||
linked_list_node_t *cur = linked_list_first(list);
|
||||
uint32_t index = 0;
|
||||
while (cur != NULL)
|
||||
{
|
||||
hw_tcb_t *tcb = OBTAIN_STRUCT_ADDR(cur, list_node, hw_tcb_t);
|
||||
cur = linked_list_next(cur);
|
||||
if (tcb->state == cond)
|
||||
{
|
||||
linked_list_remove(list, index);
|
||||
linked_list_push_back(&g_core_thread_list[core_num][new][priority], &tcb->list_node);
|
||||
index--;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// THE NULL PROC
|
||||
//
|
||||
static UINT32 _null_proc_id;
|
||||
static void _null_proc(void* par)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
hw_printf("==NULL PROC==\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Absolutely unexposed routine. Only extern
|
||||
// DPC Level
|
||||
static UINT64 _dummy_regs[16];
|
||||
|
||||
void hw_thread_schedule()
|
||||
{
|
||||
if(initialized)
|
||||
{
|
||||
const int core_num = hw_getCoreNum();
|
||||
hw_tcb_t *old_tcb = g_core_current_thread[core_num];
|
||||
hw_lock(&g_core_lock[core_num], false);
|
||||
// move anything ready from new queue to ready queue
|
||||
_adjust_queue(core_num, NEW, READY, READY);
|
||||
|
||||
// move anything blocked from ready queue to block queue
|
||||
_adjust_queue(core_num, READY, BLOCK, BLOCK);
|
||||
|
||||
// move anything ready from block queue to ready queue
|
||||
_adjust_queue(core_num, BLOCK, READY, READY);
|
||||
|
||||
// check current thread -> block to block queue, run to ready queue, exit to exit queue
|
||||
if (old_tcb != NULL)
|
||||
{
|
||||
if (old_tcb->state == BLOCK)
|
||||
{
|
||||
linked_list_push_back(&g_core_thread_list[core_num][BLOCK][old_tcb->priority], &old_tcb->list_node);
|
||||
}
|
||||
else if (old_tcb->state == RUN)
|
||||
{
|
||||
old_tcb->state = READY;
|
||||
linked_list_push_back(&g_core_thread_list[core_num][READY][old_tcb->priority], &old_tcb->list_node);
|
||||
}
|
||||
else if (old_tcb->state == EXIT)
|
||||
{
|
||||
linked_list_push_back(&g_core_thread_list[core_num][EXIT][old_tcb->priority], &old_tcb->list_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CRITICAL ERROR
|
||||
// DISCARD THE ERRONEOUS THREAD FOREVER
|
||||
}
|
||||
}
|
||||
|
||||
// pick a new thread and run
|
||||
for (int i = 0; i < PRIORITY_LEVEL_NUM; i++)
|
||||
{
|
||||
linked_list_node_t *front;
|
||||
front = linked_list_pop_front(&g_core_thread_list[core_num][READY][i]);
|
||||
if (front != NULL)
|
||||
{
|
||||
g_core_current_thread[core_num] = OBTAIN_STRUCT_ADDR(front, list_node, hw_tcb_t);
|
||||
g_core_current_thread[core_num]->state = RUN;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
hw_unlock(&g_core_lock[core_num], false);
|
||||
|
||||
if(g_core_current_thread[core_num] != NULL)
|
||||
{
|
||||
hw_printf("SELECTED: %d\n", g_core_current_thread[core_num]->thread_id);
|
||||
// Spills window registers to the current test's stack
|
||||
xthal_window_spill();
|
||||
if (g_core_current_thread[core_num]->initialized)
|
||||
{
|
||||
// Transfer control with a jump to prevent doing anything with window registers
|
||||
__asm volatile("mov a2, %0 \n" \
|
||||
"mov a3, %1 \n" \
|
||||
"j ts_arch_resume_ASM"
|
||||
::"a" (old_tcb == NULL ? &_dummy_regs[0] : &old_tcb->regs), "a" (&g_core_current_thread[core_num]->regs)
|
||||
: "a2", "a3");
|
||||
}
|
||||
else
|
||||
{
|
||||
g_core_current_thread[core_num]->initialized = true;
|
||||
_xtos_ints_on(0x00200002);
|
||||
// Transfer control with a jump to prevent doing anything with window registers
|
||||
__asm volatile("mov a2, %0 \n" \
|
||||
"mov a3, %1 \n" \
|
||||
"j ts_arch_context_switch_ASM"
|
||||
::"a" (old_tcb == NULL ? &_dummy_regs[0] : &old_tcb->regs), "a" (&g_core_current_thread[core_num]->regs)
|
||||
: "a2", "a3");
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
extern HW_RESULT hw_int_set(HW_INT_VECTOR_MASK mask);
|
||||
|
||||
static void _yield()
|
||||
{
|
||||
hw_int_set(1 << HW_INT_SW_5);
|
||||
}
|
||||
|
||||
static void _ensure_priority(hw_tcb_t *cur, hw_tcb_t *next)
|
||||
{
|
||||
if (cur != NULL && next != NULL)
|
||||
{
|
||||
if (cur->priority > next->priority)
|
||||
{
|
||||
_yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hw_thread_init()
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
g_thread_id_count_lock = 0;
|
||||
g_thread_id_count = 0;
|
||||
g_avl_lock = 0;
|
||||
avl_tree_init(&g_global_thread_tree, _thread_id_compare);
|
||||
for (int i = 0; i < HW_PROC_CNT; i++)
|
||||
{
|
||||
g_core_current_thread[i] = NULL;
|
||||
g_core_lock[i] = 0;
|
||||
for (int j = 0; j < STATE_NUM; j++)
|
||||
{
|
||||
for (int k = 0; k < PRIORITY_LEVEL_NUM; k++)
|
||||
{
|
||||
linked_list_init(&g_core_thread_list[i][j][k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
initialized = true;
|
||||
hw_thread_create(_null_proc,NULL, LOW, 0x2000, &_null_proc_id);
|
||||
hw_thread_start(_null_proc_id);
|
||||
}
|
||||
}
|
||||
|
||||
int hw_thread_create(void (*proc)(void *),
|
||||
void *args,
|
||||
hw_thread_priority_t priority,
|
||||
UINT32 stack_size,
|
||||
UINT32 *thread_id)
|
||||
{
|
||||
|
||||
if (!initialized)
|
||||
return THREAD_STATUS_UNINITIALIZED;
|
||||
if (proc == NULL)
|
||||
return THREAD_STATUS_INVALID_ARGUMENT;
|
||||
// make one allocation to save time
|
||||
hw_tcb_t *tcb = (hw_tcb_t *) hw_alloc(sizeof(hw_tcb_t) + stack_size);
|
||||
if (tcb == NULL)
|
||||
return THREAD_STATUS_OUT_OF_MEMORY;
|
||||
HW_INT_VECTOR_MASK irql;
|
||||
|
||||
tcb->proc = proc;
|
||||
tcb->stack_ptr = (char *) tcb + sizeof(hw_tcb_t) + stack_size;
|
||||
tcb->args = args;
|
||||
tcb->priority = priority;
|
||||
tcb->stack_size = stack_size;
|
||||
tcb->state = NEW;
|
||||
tcb->core_id = hw_getCoreNum();
|
||||
tcb->exit_code = 0;
|
||||
tcb->initialized = false;
|
||||
|
||||
tcb->regs.pc = proc;
|
||||
tcb->regs.sp = tcb->stack_ptr;
|
||||
tcb->regs.ps = PS_WOE_MASK | PS_UM_MASK | PS_EXCM_MASK | (1 << PS_CALLINC_SHIFT);
|
||||
|
||||
irql = hw_lock_irq_save(&g_thread_id_count_lock);
|
||||
tcb->thread_id = g_thread_id_count;
|
||||
g_thread_id_count++;
|
||||
hw_unlock_irq_restore(&g_thread_id_count_lock, irql);
|
||||
|
||||
if (thread_id != NULL)
|
||||
{
|
||||
*thread_id = tcb->thread_id;
|
||||
}
|
||||
|
||||
// write per core table
|
||||
irql = hw_lock_irq_save(&g_core_lock[tcb->core_id]);
|
||||
linked_list_push_back(&g_core_thread_list[tcb->core_id][tcb->state][tcb->priority],
|
||||
&tcb->list_node);
|
||||
hw_unlock_irq_restore(&g_core_lock[tcb->core_id], irql);
|
||||
|
||||
// write avl tree
|
||||
hw_lock(&g_avl_lock, false);
|
||||
avl_tree_insert(&g_global_thread_tree, &tcb->tree_node);
|
||||
hw_unlock(&g_avl_lock, false);
|
||||
|
||||
return THREAD_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int hw_thread_start(UINT32 thread_id)
|
||||
{
|
||||
if (!initialized)
|
||||
return THREAD_STATUS_UNINITIALIZED;
|
||||
int result = THREAD_STATUS_SUCCESS;
|
||||
hw_tcb_t *target = NULL;
|
||||
|
||||
hw_lock(&g_avl_lock, false);
|
||||
target = _get_tcb_by_id(thread_id);
|
||||
if (target == NULL)
|
||||
{
|
||||
result = THREAD_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target->state == NEW)
|
||||
{
|
||||
target->state = READY;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = THREAD_STATUS_ACTION_NOT_APPLICABLE;
|
||||
}
|
||||
}
|
||||
hw_unlock(&g_avl_lock, false);
|
||||
|
||||
_ensure_priority(g_core_current_thread[hw_getCoreNum()], target);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int hw_thread_destroy(UINT32 thread_id)
|
||||
{
|
||||
if (!initialized)
|
||||
return THREAD_STATUS_UNINITIALIZED;
|
||||
// make one allocation to save time
|
||||
hw_tcb_t *tcb;
|
||||
int result = THREAD_STATUS_SUCCESS;
|
||||
HW_INT_VECTOR_MASK irql;
|
||||
|
||||
// detach tcb
|
||||
hw_lock(&g_avl_lock, false);
|
||||
tcb = _get_tcb_by_id(thread_id);
|
||||
if (tcb == NULL)
|
||||
{
|
||||
result = THREAD_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tcb->state == EXIT)
|
||||
{
|
||||
avl_tree_delete(&g_global_thread_tree, &tcb->tree_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = THREAD_STATUS_ACTION_NOT_APPLICABLE;
|
||||
}
|
||||
}
|
||||
hw_unlock(&g_avl_lock, false);
|
||||
|
||||
|
||||
if (result == THREAD_STATUS_SUCCESS)
|
||||
{
|
||||
irql = hw_lock_irq_save(&g_core_lock[tcb->core_id]);
|
||||
linked_list_remove(&g_core_thread_list[tcb->core_id][tcb->state][tcb->priority],
|
||||
linked_list_search(&g_core_thread_list[tcb->core_id][tcb->state][tcb->priority],
|
||||
&tcb->list_node, _thread_id_equal));
|
||||
hw_unlock_irq_restore(&g_core_lock[tcb->core_id], irql);
|
||||
|
||||
hw_free(tcb);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int hw_thread_get_exit_code(UINT32 thread_id, INT32 *exit_code)
|
||||
{
|
||||
if (!initialized)
|
||||
return THREAD_STATUS_UNINITIALIZED;
|
||||
if (exit_code == NULL)
|
||||
return THREAD_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
int result = THREAD_STATUS_SUCCESS;
|
||||
hw_tcb_t *target = NULL;
|
||||
|
||||
hw_lock(&g_avl_lock, false);
|
||||
target = _get_tcb_by_id(thread_id);
|
||||
if (target == NULL)
|
||||
{
|
||||
result = THREAD_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target->state == EXIT)
|
||||
{
|
||||
*exit_code = target->exit_code;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = THREAD_STATUS_ACTION_NOT_APPLICABLE;
|
||||
}
|
||||
}
|
||||
hw_unlock(&g_avl_lock, false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void hw_thread_exit(INT32 exit_code)
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
HW_INT_VECTOR_MASK irql;
|
||||
int core_num = hw_getCoreNum();
|
||||
irql = hw_lock_irq_save(&g_core_lock[core_num]);
|
||||
g_core_current_thread[core_num]->state = EXIT;
|
||||
g_core_current_thread[core_num]->exit_code = exit_code;
|
||||
hw_unlock_irq_restore(&g_core_lock[core_num], irql);
|
||||
_yield();
|
||||
}
|
||||
}
|
||||
|
||||
int hw_thread_block(UINT32 thread_id)
|
||||
{
|
||||
if (!initialized)
|
||||
return THREAD_STATUS_UNINITIALIZED;
|
||||
int result = THREAD_STATUS_SUCCESS;
|
||||
hw_tcb_t *target = NULL;
|
||||
|
||||
hw_lock(&g_avl_lock, false);
|
||||
target = _get_tcb_by_id(thread_id);
|
||||
if (target == NULL)
|
||||
{
|
||||
return THREAD_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target->state == RUN || target->state == READY)
|
||||
{
|
||||
target->state = BLOCK;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = THREAD_STATUS_ACTION_NOT_APPLICABLE;
|
||||
}
|
||||
}
|
||||
hw_unlock(&g_avl_lock, false);
|
||||
hw_tcb_t *cur_tcb = g_core_current_thread[hw_getCoreNum()];
|
||||
if (cur_tcb != NULL && thread_id == cur_tcb->thread_id)
|
||||
{
|
||||
_yield();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int hw_thread_resume(UINT32 thread_id)
|
||||
{
|
||||
if (!initialized)
|
||||
return THREAD_STATUS_UNINITIALIZED;
|
||||
int result = THREAD_STATUS_SUCCESS;
|
||||
hw_tcb_t *target = NULL;
|
||||
|
||||
hw_lock(&g_avl_lock, false);
|
||||
target = _get_tcb_by_id(thread_id);
|
||||
if (target == NULL)
|
||||
{
|
||||
result = THREAD_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target->state == BLOCK)
|
||||
{
|
||||
target->state = READY;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = THREAD_STATUS_ACTION_NOT_APPLICABLE;
|
||||
}
|
||||
}
|
||||
hw_unlock(&g_avl_lock, false);
|
||||
|
||||
_ensure_priority(g_core_current_thread[hw_getCoreNum()], target);
|
||||
|
||||
return result;
|
||||
}
|
Loading…
Reference in New Issue