871 lines
19 KiB
C
871 lines
19 KiB
C
/*
|
||
* chopstx.c - Threads and only threads.
|
||
*
|
||
* Copyright (C) 2013 Flying Stone Technology
|
||
* Author: NIIBE Yutaka <gniibe@fsij.org>
|
||
*
|
||
* This file is a part of Chopstx, a thread library for embedded.
|
||
*
|
||
* Chopstx is free software: you can redistribute it and/or modify it
|
||
* under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation, either version 3 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* Chopstx is distributed in the hope that it will be useful, but
|
||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
* General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
*
|
||
* As additional permission under GNU GPL version 3 section 7, you may
|
||
* distribute non-source form of the Program without the copy of the
|
||
* GNU GPL normally required by section 4, provided you inform the
|
||
* receipents of GNU GPL by a written offer.
|
||
*
|
||
*/
|
||
|
||
#include <stdint.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <chopstx.h>
|
||
|
||
/* RUNNING: the current thread. */
|
||
struct chx_thread *running;
|
||
|
||
/* Use this when we support round robin scheduling. */
|
||
#define PREEMPTION_USEC (1000*MHZ) /* 1ms */
|
||
|
||
/* Double linked list operations. */
|
||
struct chx_dll {
|
||
struct chx_thread *next, *prev;
|
||
};
|
||
|
||
|
||
/* READY: priority queue. */
|
||
struct chx_ready {
|
||
struct chx_thread *next, *prev;
|
||
struct chx_spinlock lock;
|
||
};
|
||
|
||
static struct chx_ready q_ready;
|
||
|
||
struct chx_timer {
|
||
struct chx_thread *next, *prev;
|
||
struct chx_spinlock lock;
|
||
};
|
||
|
||
/* threads waiting for timer. */
|
||
static struct chx_timer q_timer;
|
||
|
||
/* XXX: q_exit; Queue for threads already exited. */
|
||
|
||
/* Forward declaration(s). */
|
||
static void chx_request_preemption (void);
|
||
|
||
|
||
/**************/
|
||
static void chx_LOCK (struct chx_spinlock *lk)
|
||
{
|
||
(void)lk;
|
||
}
|
||
|
||
static void chx_UNLOCK (struct chx_spinlock *lk)
|
||
{
|
||
(void)lk;
|
||
}
|
||
|
||
/* The thread context: specific to ARM Cortex-M3 now. */
|
||
struct tcontext {
|
||
uint32_t reg[9]; /* r4, r5, r6, r7, r8, r9, r10, r11, r13 */
|
||
};
|
||
|
||
/* Saved registers on the stack. */
|
||
struct chx_stack_regs {
|
||
uint32_t reg[8]; /* r0, r1, r2, r3, r12, lr, pc, xpsr */
|
||
};
|
||
|
||
/*
|
||
* Constants for ARM.
|
||
*/
|
||
#define REG_SP 8
|
||
|
||
#define REG_R0 0
|
||
#define REG_LR 5
|
||
#define REG_PC 6
|
||
#define REG_XPSR 7
|
||
|
||
#define INITIAL_XPSR 0x01000000 /* T=1 */
|
||
|
||
/*
|
||
* NVIC: Nested Vectored Interrupt Controller
|
||
*/
|
||
struct NVIC {
|
||
uint32_t ISER[8];
|
||
uint32_t unused1[24];
|
||
uint32_t ICER[8];
|
||
uint32_t unused2[24];
|
||
uint32_t ISPR[8];
|
||
uint32_t unused3[24];
|
||
uint32_t ICPR[8];
|
||
uint32_t unused4[24];
|
||
uint32_t IABR[8];
|
||
uint32_t unused5[56];
|
||
uint32_t IPR[60];
|
||
};
|
||
|
||
static struct NVIC *const NVICBase = (struct NVIC *const)0xE000E100;
|
||
#define NVIC_ISER(n) (NVICBase->ISER[n >> 5])
|
||
#define NVIC_ICER(n) (NVICBase->ICER[n >> 5])
|
||
#define NVIC_ICPR(n) (NVICBase->ICPR[n >> 5])
|
||
#define NVIC_IPR(n) (NVICBase->IPR[n >> 2])
|
||
|
||
#define USB_LP_CAN1_RX0_IRQn 20
|
||
|
||
/*
|
||
* SysTick registers.
|
||
*/
|
||
static volatile uint32_t *const SYST_CSR = (uint32_t *const)0xE000E010;
|
||
static volatile uint32_t *const SYST_RVR = (uint32_t *const)0xE000E014;
|
||
static volatile uint32_t *const SYST_CVR = (uint32_t *const)0xE000E018;
|
||
|
||
#define MHZ 72
|
||
|
||
static uint32_t usec_to_ticks (uint32_t usec)
|
||
{
|
||
return usec * MHZ;
|
||
}
|
||
/**************/
|
||
|
||
struct chx_thread {
|
||
struct chx_thread *next, *prev;
|
||
struct tcontext tc;
|
||
uint16_t prio;
|
||
uint16_t prio_orig;
|
||
uint32_t v;
|
||
struct chx_mtx *mutex_list;
|
||
} __attribute__((packed));
|
||
|
||
|
||
/*
|
||
* Double linked list handling.
|
||
*/
|
||
|
||
static int
|
||
ll_empty (void *head)
|
||
{
|
||
struct chx_thread *l = (struct chx_thread *)head;
|
||
|
||
return (struct chx_thread *)l == l->next;
|
||
}
|
||
|
||
static struct chx_thread *
|
||
ll_dequeue (struct chx_thread *tp)
|
||
{
|
||
struct chx_thread *tp0 = tp;
|
||
|
||
tp->next->prev = tp->prev;
|
||
tp->prev->next = tp->next;
|
||
return tp0;
|
||
}
|
||
|
||
static void
|
||
ll_insert (struct chx_thread *tp0, void *head)
|
||
{
|
||
struct chx_thread *tp = (struct chx_thread *)head;
|
||
|
||
tp0->next = tp;
|
||
tp0->prev = tp->prev;
|
||
tp->prev->next = tp0;
|
||
tp->prev = tp0;
|
||
}
|
||
|
||
|
||
static struct chx_thread *
|
||
ll_pop (void *head)
|
||
{
|
||
struct chx_thread *l = (struct chx_thread *)head;
|
||
struct chx_thread *tp0 = l->next;
|
||
|
||
if (tp0 == l)
|
||
return NULL;
|
||
|
||
return ll_dequeue (tp0);
|
||
}
|
||
|
||
static void
|
||
ll_prio_push (struct chx_thread *tp0, void *head)
|
||
{
|
||
struct chx_thread *l = (struct chx_thread *)head;
|
||
struct chx_thread *tp;
|
||
|
||
for (tp = l->next; tp != l; tp = tp->next)
|
||
if (tp->prio <= tp0->prio)
|
||
break;
|
||
|
||
ll_insert (tp0, tp);
|
||
}
|
||
|
||
static void
|
||
ll_prio_enqueue (struct chx_thread *tp0, void *head)
|
||
{
|
||
struct chx_thread *l = (struct chx_thread *)head;
|
||
struct chx_thread *tp;
|
||
|
||
for (tp = l->next; tp != l; tp = tp->next)
|
||
if (tp->prio < tp0->prio)
|
||
break;
|
||
|
||
ll_insert (tp0, tp);
|
||
}
|
||
|
||
|
||
/*
|
||
* Thread status encoded in ->v.
|
||
*/
|
||
#define THREAD_WAIT_MTX 0x00000001
|
||
#define THREAD_WAIT_CND 0x00000002
|
||
#define THREAD_WAITTIME 0x00000003
|
||
|
||
#define THREAD_RUNNING 0x00000000
|
||
#define THREAD_WAIT_INT 0x00000004
|
||
#define THREAD_EXITED 0x00000008
|
||
#define THREAD_READY 0x0000000C
|
||
|
||
static uint32_t
|
||
chx_ready_pop (void)
|
||
{
|
||
struct chx_thread *tp;
|
||
|
||
chx_LOCK (&q_ready.lock);
|
||
tp = ll_pop (&q_ready);
|
||
if (tp)
|
||
tp->v = THREAD_RUNNING;
|
||
chx_UNLOCK (&q_ready.lock);
|
||
|
||
return (uint32_t)tp;
|
||
}
|
||
|
||
|
||
static void
|
||
chx_ready_push (struct chx_thread *t)
|
||
{
|
||
chx_LOCK (&q_ready.lock);
|
||
t->v = THREAD_READY;
|
||
ll_prio_push (t, &q_ready);
|
||
chx_UNLOCK (&q_ready.lock);
|
||
}
|
||
|
||
|
||
static void
|
||
chx_ready_enqueue (struct chx_thread *t)
|
||
{
|
||
chx_LOCK (&q_ready.lock);
|
||
t->v = THREAD_READY;
|
||
ll_prio_enqueue (t, &q_ready);
|
||
chx_UNLOCK (&q_ready.lock);
|
||
}
|
||
|
||
/* Registers on stack (PSP): r0, r1, r2, r3, r12, lr, pc, xpsr */
|
||
static void __attribute__ ((naked,used))
|
||
sched (void)
|
||
{
|
||
register uint32_t r0 asm ("r0");
|
||
|
||
asm volatile ("cpsid i" : : : "memory");
|
||
|
||
r0 = chx_ready_pop ();
|
||
|
||
asm volatile (/* Now, r0 points to the thread to be switched. */
|
||
/* Put it to *running. */
|
||
"ldr r1, =running\n\t"
|
||
/* Update running. */
|
||
"str r0, [r1]\n\t"
|
||
"cbz r0, 3f\n\t"
|
||
/**/
|
||
"str r0, [r0]\n\t"
|
||
"str r0, [r0, 4]\n\t"
|
||
"cpsie i\n\t" /* Unmask interrupts. */
|
||
"add r0, #8\n\t"
|
||
"ldm r0!, {r4, r5, r6, r7}\n\t"
|
||
"ldr r8, [r0], 4\n\t"
|
||
"ldr r9, [r0], 4\n\t"
|
||
"ldr r10, [r0], 4\n\t"
|
||
"ldr r11, [r0], 4\n\t"
|
||
"ldr r1, [r0]\n\t"
|
||
"msr PSP, r1\n\t"
|
||
"mov r0, #-1\n\t"
|
||
"sub r0, #2\n\t" /* EXC_RETURN to a thread with PSP */
|
||
"bx r0\n"
|
||
"3:\n\t"
|
||
"cpsie i\n\t" /* Unmask interrupts. */
|
||
/* Spawn an IDLE thread. */
|
||
"ldr r0, =__main_stack_end__\n\t"
|
||
"msr MSP, r0\n\t"
|
||
"mov r0, #0\n\t"
|
||
"mov r1, #0\n\t"
|
||
"ldr r2, =idle\n\t" /* PC = idle */
|
||
"mov r3, #0x01000000\n\t" /* xPSR = T-flag set (Thumb) */
|
||
"push {r0, r1, r2, r3}\n\t"
|
||
"mov r0, #0\n\t"
|
||
"mov r1, #0\n\t"
|
||
"mov r2, #0\n\t"
|
||
"mov r3, #0\n\t"
|
||
"push {r0, r1, r2, r3}\n"
|
||
"mov r0, #-1\n\t"
|
||
"sub r0, #6\n\t" /* EXC_RETURN to a thread with MSP */
|
||
"bx r0\n"
|
||
: /* no output */ : "r" (r0) : "memory");
|
||
}
|
||
|
||
void __attribute__ ((naked))
|
||
preempt (void)
|
||
{
|
||
register uint32_t r0 asm ("r0");
|
||
|
||
asm volatile ("ldr r1, =running\n\t"
|
||
"ldr r0, [r1]\n\t"
|
||
"cbnz r0, 0f\n\t"
|
||
/* It's idle which was preempted. */
|
||
"ldr r1, =__main_stack_end__\n\t"
|
||
"msr MSP, r1\n\t"
|
||
"b sched\n"
|
||
"0:\n\t"
|
||
"ldr r2, [r0, 48]\n\t" /* Check ->v to avoid RACE. */
|
||
"cbz r2, 1f\n\t"
|
||
/* RUNNING is busy on transition, do nothing. */
|
||
"bx lr\n"
|
||
"1:\n\t"
|
||
"add r2, r0, #8\n\t"
|
||
/* Save registers onto CHX_THREAD struct. */
|
||
"stm r2!, {r4, r5, r6, r7}\n\t"
|
||
"mov r3, r8\n\t"
|
||
"mov r4, r9\n\t"
|
||
"mov r5, r10\n\t"
|
||
"mov r6, r11\n\t"
|
||
"mrs r7, PSP\n\t" /* r13(=SP) in user space. */
|
||
"stm r2, {r3, r4, r5, r6, r7}"
|
||
: "=r" (r0): /* no input */ : "memory");
|
||
|
||
asm volatile ("cpsid i" : : : "memory");
|
||
chx_ready_push ((struct chx_thread *)r0);
|
||
asm volatile ("ldr r1, =running\n\t"
|
||
"mov r2, #0\n\t"
|
||
"str r2, [r1]\n\t" /* running := NULL */
|
||
"cpsie i" /* Unmask interrupts. */
|
||
: /* no output */ : /* no input */ : "memory");
|
||
|
||
asm volatile ("b sched"
|
||
: /* no output */: /* no input */ : "memory");
|
||
}
|
||
|
||
|
||
/* system call: sched */
|
||
void __attribute__ ((naked))
|
||
svc (void)
|
||
{
|
||
register uint32_t r0 asm ("r0");
|
||
register uint32_t orig_r0 asm ("r2");
|
||
|
||
asm volatile ("ldr r1, =running\n\t"
|
||
"ldr r0, [r1]\n\t"
|
||
"add r2, r0, #8\n\t"
|
||
/* Save registers onto CHX_THREAD struct. */
|
||
"stm r2!, {r4, r5, r6, r7}\n\t"
|
||
"mov r3, r8\n\t"
|
||
"mov r4, r9\n\t"
|
||
"mov r5, r10\n\t"
|
||
"mov r6, r11\n\t"
|
||
"mrs r7, PSP\n\t" /* r13(=SP) in user space. */
|
||
"stm r2, {r3, r4, r5, r6, r7}\n\t"
|
||
"ldr r2, [r7]"
|
||
: "=r" (r0), "=r" (orig_r0) : /* no input */ : "memory");
|
||
|
||
if (orig_r0)
|
||
{
|
||
asm volatile ("cpsid i" : : : "memory");
|
||
chx_ready_enqueue ((struct chx_thread *)r0);
|
||
asm volatile ("ldr r1, =running\n\t"
|
||
"mov r2, #0\n\t"
|
||
"str r2, [r1]\n\t" /* running := NULL */
|
||
"cpsie i" /* Unmask interrupts. */
|
||
: /* no output */ : /* no input */ : "memory");
|
||
}
|
||
|
||
asm volatile ("b sched"
|
||
: /* no output */: /* no input */ : "memory");
|
||
}
|
||
|
||
|
||
static void
|
||
chx_set_timer (struct chx_thread *q, uint32_t ticks)
|
||
{
|
||
if (q == (struct chx_thread *)&q_timer)
|
||
{
|
||
*SYST_RVR = ticks;
|
||
*SYST_CVR = 0; /* write (any) to clear the counter to reload. */
|
||
*SYST_RVR = 0;
|
||
}
|
||
else
|
||
q->v = (ticks<<8)|THREAD_WAITTIME;
|
||
}
|
||
|
||
static void
|
||
chx_timer_insert (struct chx_thread *tp, uint32_t usec)
|
||
{
|
||
uint32_t ticks = usec_to_ticks (usec);
|
||
uint32_t next_ticks = *SYST_CVR;
|
||
struct chx_thread *q;
|
||
|
||
asm volatile ("cpsid i" : : : "memory");
|
||
chx_LOCK (&q_timer.lock);
|
||
|
||
for (q = q_timer.next; q != (struct chx_thread *)&q_timer; q = q->next)
|
||
{
|
||
if (ticks < next_ticks)
|
||
{
|
||
ll_insert (tp, q);
|
||
chx_set_timer (tp->prev, ticks);
|
||
chx_set_timer (tp, (next_ticks - ticks));
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
ticks -= next_ticks;
|
||
next_ticks = (q->v >> 8);
|
||
}
|
||
}
|
||
|
||
if (q == (struct chx_thread *)&q_timer)
|
||
{
|
||
ll_insert (tp, q);
|
||
chx_set_timer (tp->prev, ticks);
|
||
chx_set_timer (tp, 1); /* Non-zero for the last entry. */
|
||
}
|
||
chx_UNLOCK (&q_timer.lock);
|
||
asm volatile ("cpsie i" : : : "memory");
|
||
}
|
||
|
||
|
||
void
|
||
chx_timer_expired (void)
|
||
{
|
||
struct chx_thread *t;
|
||
|
||
asm volatile ("cpsid i" : : : "memory");
|
||
chx_LOCK (&q_timer.lock);
|
||
if ((t = ll_pop (&q_timer)))
|
||
{
|
||
uint32_t next_tick = t->v >> 8;
|
||
|
||
chx_ready_enqueue (t);
|
||
|
||
if (!ll_empty (&q_timer))
|
||
{
|
||
struct chx_thread *t_next;
|
||
|
||
for (t = q_timer.next;
|
||
t != (struct chx_thread *)&q_timer && next_tick == 0;
|
||
t = t_next)
|
||
{
|
||
next_tick = (t->v >> 8);
|
||
t_next = t->next;
|
||
ll_dequeue (t);
|
||
chx_ready_enqueue (t);
|
||
}
|
||
|
||
if (!ll_empty (&q_timer))
|
||
chx_set_timer ((struct chx_thread *)&q_timer, next_tick);
|
||
}
|
||
}
|
||
|
||
chx_request_preemption ();
|
||
chx_UNLOCK (&q_timer.lock);
|
||
asm volatile ("cpsie i" : : : "memory");
|
||
}
|
||
|
||
|
||
static void
|
||
chx_enable_intr (uint8_t irq_num)
|
||
{
|
||
NVIC_ISER (irq_num) = 1 << (irq_num & 0x1f);
|
||
}
|
||
|
||
static void
|
||
chx_disable_intr (uint8_t irq_num)
|
||
{
|
||
NVIC_ICER (irq_num) = 1 << (irq_num & 0x1f);
|
||
}
|
||
|
||
#define INTR_PRIO (11<<4)
|
||
|
||
static void
|
||
chx_set_intr_prio (uint8_t n)
|
||
{
|
||
unsigned int sh = (n & 3) << 3;
|
||
|
||
NVIC_IPR (n) = (NVIC_IPR(n) & ~(0xFF << sh)) | (INTR_PRIO << sh);
|
||
}
|
||
|
||
static chopstix_intr_t *intr_top;
|
||
|
||
void
|
||
chx_handle_intr (void)
|
||
{
|
||
chopstix_intr_t *intr;
|
||
register uint32_t irq_num;
|
||
|
||
asm volatile ("cpsid i\n\t"
|
||
"mrs %0, IPSR\n\t"
|
||
"sub %0, #16" /* Exception # - 16 = interrupt number. */
|
||
: "=r" (irq_num) : /* no input */ : "memory");
|
||
chx_disable_intr (irq_num);
|
||
for (intr = intr_top; intr; intr = intr->next)
|
||
if (intr->irq_num == irq_num)
|
||
break;
|
||
|
||
if (intr && intr->t && intr->t->v == THREAD_WAIT_INT)
|
||
{
|
||
intr->ready++;
|
||
chx_ready_enqueue (intr->t);
|
||
chx_request_preemption ();
|
||
}
|
||
asm volatile ("cpsie i" : : : "memory");
|
||
}
|
||
|
||
void
|
||
chx_systick_init (void)
|
||
{
|
||
*SYST_RVR = 0;
|
||
*SYST_CVR = 0;
|
||
*SYST_CSR = 7;
|
||
}
|
||
|
||
static uint32_t *const SHPR3 = (uint32_t *const)0xE000ED20;
|
||
#define INTR_PRIO_PENDSV (15<<4)
|
||
|
||
#define PRIO_DEFAULT 1
|
||
|
||
void
|
||
chx_init (struct chx_thread *tp)
|
||
{
|
||
*SHPR3 = (INTR_PRIO_PENDSV << 16);
|
||
|
||
memset (&tp->tc, 0, sizeof (tp->tc));
|
||
q_ready.next = q_ready.prev = (struct chx_thread *)&q_ready;
|
||
q_timer.next = q_timer.prev = (struct chx_thread *)&q_timer;
|
||
tp->prio_orig = tp->prio = PRIO_DEFAULT;
|
||
tp->next = tp->prev = tp;
|
||
tp->mutex_list = NULL;
|
||
tp->v = THREAD_RUNNING;
|
||
|
||
running = tp;
|
||
}
|
||
|
||
|
||
static void
|
||
chx_request_preemption (void)
|
||
{
|
||
static volatile uint32_t *const ICSR = (uint32_t *const)0xE000ED04;
|
||
|
||
*ICSR = (1 << 28);
|
||
asm volatile ("" : : : "memory");
|
||
}
|
||
|
||
static void
|
||
chx_sched (void)
|
||
{
|
||
register uint32_t r0 asm ("r0") = 0;
|
||
|
||
asm volatile ("svc #0" : : "r" (r0) : "memory");
|
||
}
|
||
|
||
static void
|
||
chx_yield (void)
|
||
{
|
||
register uint32_t r0 asm ("r0") = 1;
|
||
|
||
asm volatile ("svc #0" : : "r" (r0) : "memory");
|
||
}
|
||
|
||
void
|
||
chopstx_attr_init (chopstx_attr_t *attr)
|
||
{
|
||
attr->prio = PRIO_DEFAULT;
|
||
attr->addr = 0;
|
||
attr->size = 0;
|
||
}
|
||
|
||
void
|
||
chopstx_attr_setschedparam (chopstx_attr_t *attr, uint8_t prio)
|
||
{
|
||
attr->prio = prio;
|
||
}
|
||
|
||
void
|
||
chopstx_attr_setstack (chopstx_attr_t *attr, uint32_t addr, size_t size)
|
||
{
|
||
attr->addr = addr;
|
||
attr->size = size;
|
||
}
|
||
|
||
void
|
||
chopstx_create (chopstx_t *thd, const chopstx_attr_t *attr,
|
||
void *(thread_entry) (void *), void *arg)
|
||
{
|
||
struct chx_thread *tp;
|
||
void *stack;
|
||
struct chx_stack_regs *p;
|
||
|
||
if (attr->size < sizeof (struct chx_thread) + 8 * sizeof (uint32_t))
|
||
return;
|
||
|
||
stack = (void *)(attr->addr + attr->size - sizeof (struct chx_thread)
|
||
- sizeof (struct chx_stack_regs));
|
||
memset (stack, 0, sizeof (struct chx_stack_regs));
|
||
p = (struct chx_stack_regs *)stack;
|
||
p->reg[REG_R0] = (uint32_t)arg;
|
||
p->reg[REG_LR] = 0; /* XXX: address of exit??? */
|
||
p->reg[REG_PC] = (uint32_t)thread_entry;
|
||
p->reg[REG_XPSR] = INITIAL_XPSR;
|
||
|
||
tp = (struct chx_thread *)(stack + sizeof (struct chx_stack_regs));
|
||
memset (&tp->tc, 0, sizeof (tp->tc));
|
||
tp->prio_orig = tp->prio = attr->prio;
|
||
tp->tc.reg[REG_SP] = (uint32_t)stack;
|
||
tp->next = tp->prev = tp;
|
||
tp->mutex_list = NULL;
|
||
tp->v = THREAD_EXITED;
|
||
*thd = (uint32_t)tp;
|
||
|
||
asm volatile ("cpsid i" : : : "memory");
|
||
chx_ready_enqueue (tp);
|
||
asm volatile ("cpsie i" : : : "memory");
|
||
if (tp->prio > running->prio)
|
||
chx_yield ();
|
||
}
|
||
|
||
|
||
void
|
||
chopstx_usleep (uint32_t usec)
|
||
{
|
||
while (usec)
|
||
{
|
||
uint32_t usec0 = (usec > 200*1000) ? 200*1000: usec;
|
||
|
||
chx_timer_insert (running, usec0);
|
||
chx_sched ();
|
||
|
||
usec -= usec0;
|
||
}
|
||
}
|
||
|
||
|
||
void
|
||
chopstx_mutex_init (chopstx_mutex_t *mutex)
|
||
{
|
||
mutex->q.next = mutex->q.prev = (struct chx_thread *)mutex;
|
||
mutex->list = NULL;
|
||
}
|
||
|
||
void
|
||
chopstx_mutex_lock (chopstx_mutex_t *mutex)
|
||
{
|
||
while (1)
|
||
{
|
||
struct chx_thread *t = running;
|
||
chopstx_mutex_t *m;
|
||
struct chx_thread *owner;
|
||
|
||
asm volatile ("cpsid i" : : : "memory");
|
||
chx_LOCK (&mutex->lock);
|
||
if (mutex->owner == NULL)
|
||
{
|
||
/* The mutex is acquired. */
|
||
mutex->owner = t;
|
||
mutex->list = t->mutex_list;
|
||
t->mutex_list = mutex;
|
||
chx_UNLOCK (&mutex->lock);
|
||
asm volatile ("cpsie i" : : : "memory");
|
||
return;
|
||
}
|
||
|
||
m = mutex;
|
||
owner = m->owner;
|
||
while (1)
|
||
{
|
||
owner->prio = t->prio;
|
||
if (owner->v == THREAD_READY)
|
||
{
|
||
ll_prio_enqueue (ll_dequeue (owner), &q_ready);
|
||
break;
|
||
}
|
||
else if ((owner->v & 0x03) == THREAD_WAIT_MTX)
|
||
{
|
||
m = (chopstx_mutex_t *)(owner->v & ~0x03);
|
||
|
||
ll_prio_enqueue (ll_dequeue (owner), m);
|
||
owner = m->owner;
|
||
continue;
|
||
}
|
||
else if ((owner->v & 0x03) == THREAD_WAIT_CND)
|
||
{
|
||
chopstx_cond_t *cnd = (chopstx_cond_t *)(owner->v & ~0x03);
|
||
|
||
ll_prio_enqueue (ll_dequeue (owner), cnd);
|
||
break;
|
||
}
|
||
else
|
||
break;
|
||
/* XXX: RUNNING and SMP??? */
|
||
}
|
||
|
||
ll_prio_enqueue (t, &mutex->q);
|
||
t->v = (uint32_t)mutex | THREAD_WAIT_MTX;
|
||
chx_UNLOCK (&mutex->lock);
|
||
asm volatile ("cpsie i" : : : "memory");
|
||
chx_sched ();
|
||
}
|
||
}
|
||
|
||
void
|
||
chopstx_mutex_unlock (chopstx_mutex_t *mutex)
|
||
{
|
||
struct chx_thread *t;
|
||
int yield = 0;
|
||
|
||
asm volatile ("cpsid i" : : : "memory");
|
||
chx_LOCK (&mutex->lock);
|
||
mutex->owner = NULL;
|
||
running->mutex_list = mutex->list;
|
||
mutex->list = NULL;
|
||
|
||
t = ll_pop (&mutex->q);
|
||
if (t)
|
||
{
|
||
uint16_t newprio = running->prio_orig;
|
||
chopstx_mutex_t *m;
|
||
|
||
chx_ready_enqueue (t);
|
||
|
||
/* Examine mutexes we hold, and determine new priority for running. */
|
||
for (m = running->mutex_list; m; m = m->list)
|
||
if (!ll_empty (&m->q) && m->q.next->prio > newprio)
|
||
newprio = m->q.next->prio;
|
||
/* Then, assign it. */
|
||
running->prio = newprio;
|
||
|
||
if (t->prio > running->prio)
|
||
yield = 1;
|
||
}
|
||
|
||
chx_UNLOCK (&mutex->lock);
|
||
asm volatile ("cpsie i" : : : "memory");
|
||
if (yield)
|
||
chx_yield ();
|
||
}
|
||
|
||
|
||
void
|
||
chopstx_cond_init (chopstx_cond_t *cond)
|
||
{
|
||
cond->q.next = cond->q.prev = (struct chx_thread *)cond;
|
||
}
|
||
|
||
void
|
||
chopstx_cond_wait (chopstx_cond_t *cond, chopstx_mutex_t *mutex)
|
||
{
|
||
struct chx_thread *t = running;
|
||
|
||
if (mutex)
|
||
chopstx_mutex_unlock (mutex);
|
||
|
||
asm volatile ("cpsid i" : : : "memory");
|
||
chx_LOCK (&cond->lock);
|
||
ll_prio_enqueue (t, &cond->q);
|
||
t->v = (uint32_t)cond | THREAD_WAIT_CND;
|
||
chx_UNLOCK (&cond->lock);
|
||
asm volatile ("cpsie i" : : : "memory");
|
||
|
||
chx_sched ();
|
||
|
||
if (mutex)
|
||
chopstx_mutex_lock (mutex);
|
||
}
|
||
|
||
void
|
||
chopstx_cond_signal (chopstx_cond_t *cond)
|
||
{
|
||
struct chx_thread *t;
|
||
int yield = 0;
|
||
|
||
asm volatile ("cpsid i" : : : "memory");
|
||
chx_LOCK (&cond->lock);
|
||
t = ll_pop (&cond->q);
|
||
if (t)
|
||
{
|
||
chx_ready_enqueue (t);
|
||
if (t->prio > running->prio)
|
||
yield = 1;
|
||
}
|
||
chx_UNLOCK (&cond->lock);
|
||
asm volatile ("cpsie i" : : : "memory");
|
||
|
||
if (yield)
|
||
chx_yield ();
|
||
}
|
||
|
||
void
|
||
chopstx_cond_broadcast (chopstx_cond_t *cond)
|
||
{
|
||
struct chx_thread *t;
|
||
int yield = 1;
|
||
|
||
asm volatile ("cpsid i" : : : "memory");
|
||
chx_LOCK (&cond->lock);
|
||
while ((t = ll_pop (&cond->q)))
|
||
{
|
||
chx_ready_enqueue (t);
|
||
if (t->prio > running->prio)
|
||
yield = 1;
|
||
}
|
||
chx_UNLOCK (&cond->lock);
|
||
asm volatile ("cpsie i" : : : "memory");
|
||
if (yield)
|
||
chx_yield ();
|
||
}
|
||
|
||
|
||
void
|
||
chopstx_intr_register (chopstix_intr_t *intr, uint8_t irq_num)
|
||
{
|
||
intr->irq_num = irq_num;
|
||
intr->t = running;
|
||
intr->ready = 0;
|
||
asm volatile ("cpsid i" : : : "memory");
|
||
chx_disable_intr (irq_num);
|
||
chx_set_intr_prio (irq_num);
|
||
intr->next = intr_top;
|
||
intr_top = intr;
|
||
asm volatile ("cpsie i" : : : "memory");
|
||
}
|
||
|
||
|
||
void
|
||
chopstx_wait_intr (chopstix_intr_t *intr)
|
||
{
|
||
asm volatile ("cpsid i" : : : "memory");
|
||
chx_enable_intr (intr->irq_num);
|
||
while (intr->ready == 0)
|
||
{
|
||
intr->t = running;
|
||
running->v = THREAD_WAIT_INT;
|
||
asm volatile ("cpsie i" : : : "memory");
|
||
chx_sched ();
|
||
asm volatile ("cpsid i" : : : "memory");
|
||
}
|
||
intr->ready--;
|
||
asm volatile ("cpsie i" : : : "memory");
|
||
}
|