/* This file is part of test microkernel for OpenRISC 1000. */
|
/* This file is part of test microkernel for OpenRISC 1000. */
|
/* (C) 2000 Damjan Lampret, lampret@opencores.org */
|
/* (C) 2000 Damjan Lampret, lampret@opencores.org */
|
|
|
#include "support.h"
|
#include "support.h"
|
#include "spr_defs.h"
|
#include "spr_defs.h"
|
#include "uos.h"
|
#include "uos.h"
|
#include "ipc.h"
|
#include "ipc.h"
|
#include "int.h"
|
#include "int.h"
|
|
|
/* External functions prototypes */
|
/* External functions prototypes */
|
int tick_init(unsigned long period, void (* inf)(void));
|
int tick_init(unsigned long period, void (* inf)(void));
|
|
|
/* Pointers to contexts used by except_or32.S routines */
|
/* Pointers to contexts used by except_or32.S routines */
|
unsigned long *task_context;
|
unsigned long *task_context;
|
unsigned long *kernel_context;
|
unsigned long *kernel_context;
|
|
|
/* TCBs for all tasks in the system */
|
/* TCBs for all tasks in the system */
|
struct tcb tasks[MAX_TASKS+1];
|
struct tcb tasks[MAX_TASKS+1];
|
|
|
/* Stacks for the tasks (stacks[0] is kernel stack) */
|
/* Stacks for the tasks (stacks[0] is kernel stack) */
|
unsigned char stacks[MAX_TASKS+1][STACK_SIZE];
|
unsigned char stacks[MAX_TASKS+1][STACK_SIZE];
|
|
|
/* MCBs for IPC messages */
|
/* MCBs for IPC messages */
|
struct mcb msgs[MAX_MSGS];
|
struct mcb msgs[MAX_MSGS];
|
|
|
/* Pointer to linked list of free MCBs. */
|
/* Pointer to linked list of free MCBs. */
|
struct mcb *free_mcbs;
|
struct mcb *free_mcbs;
|
|
|
/* TID of the current user task */
|
/* TID of the current user task */
|
tid_t curtask = 0;
|
tid_t curtask = 0;
|
|
|
/* Statistics */
|
/* Statistics */
|
int kernel_sched_cnt = 0;
|
int kernel_sched_cnt = 0;
|
int kernel_syscall_cnt = 0;
|
int kernel_syscall_cnt = 0;
|
|
|
/* Timestamp via or1ksim (CPU cycle number). */
|
/* Timestamp via or1ksim (CPU cycle number). */
|
unsigned long timestamp()
|
unsigned long timestamp()
|
{
|
{
|
register unsigned long cycles asm("r3");
|
register unsigned long cycles asm("r3");
|
asm("l.sys 201");
|
asm("l.sys 201");
|
return cycles;
|
return cycles;
|
}
|
}
|
|
|
/* Standard function for filling memory with a constant byte. */
|
/* Standard function for filling memory with a constant byte. */
|
void *memset(void *dst, int c, size_t size)
|
void *memset(void *dst, int c, size_t size)
|
{
|
{
|
char *tmp = dst;
|
char *tmp = dst;
|
|
|
for(;tmp && (tmp < (char *)dst + size); tmp++)
|
for(;tmp && (tmp < (char *)dst + size); tmp++)
|
*(char *)tmp = (char)c;
|
*(char *)tmp = (char)c;
|
|
|
return dst;
|
return dst;
|
}
|
}
|
|
|
/* Traverse linked list of MCBs and show individual messages. */
|
/* Traverse linked list of MCBs and show individual messages. */
|
void kernel_show_mcbs(struct mcb *mcb)
|
void kernel_show_mcbs(struct mcb *mcb)
|
{
|
{
|
for(;mcb; mcb = mcb->next) {
|
for(;mcb; mcb = mcb->next) {
|
printf("MCB len=%u origintask=%u ", mcb->length, mcb->origin);
|
printf("MCB len=%u origintask=%u ", mcb->length, mcb->origin);
|
printf("msg:%s\n", mcb->msg);
|
printf("msg:%s\n", mcb->msg);
|
}
|
}
|
}
|
}
|
|
|
/* Show all contexts. */
|
/* Show all contexts. */
|
void kernel_show_contexts()
|
void kernel_show_contexts()
|
{
|
{
|
int i;
|
int i;
|
tid_t t;
|
tid_t t;
|
|
|
for(t = 1; t <= MAX_TASKS; t++) {
|
for(t = 1; t <= MAX_TASKS; t++) {
|
printf("\ntask TID=%d: PC=0x%x ", t, (unsigned)tasks[t].regs.pc & ~0x3);
|
printf("\ntask TID=%d: PC=0x%x ", t, (unsigned)tasks[t].regs.pc & ~0x3);
|
printf("SP(r1)=0x%x ", (unsigned)tasks[t].regs.sp);
|
printf("SP(r1)=0x%x ", (unsigned)tasks[t].regs.sp);
|
printf("SR[IEE]=%d\n", (unsigned)tasks[t].regs.sr & SPR_SR_IEE);
|
printf("SR[IEE]=%d\n", (unsigned)tasks[t].regs.sr & SPR_SR_IEE);
|
printf("SR[TEE]=%d\n", (unsigned)tasks[t].regs.sr & SPR_SR_TEE);
|
printf("SR[TEE]=%d\n", (unsigned)tasks[t].regs.sr & SPR_SR_TEE);
|
printf("SR[SM]=%d\n", (unsigned)tasks[t].regs.sr & SPR_SR_SM);
|
printf("SR[SM]=%d\n", (unsigned)tasks[t].regs.sr & SPR_SR_SM);
|
for(i = 1; i < GPRS; i++) {
|
for(i = 1; i < GPRS; i++) {
|
if (i % 4 == 0)
|
if (i % 4 == 0)
|
printf("\n");
|
printf("\n");
|
printf("r%d=0x%.8x ", i, (unsigned)tasks[t].regs.gprs[i]);
|
printf("r%d=0x%.8x ", i, (unsigned)tasks[t].regs.gprs[i]);
|
}
|
}
|
printf("\n");
|
printf("\n");
|
kernel_show_mcbs(tasks[t].waiting_msgs);
|
kernel_show_mcbs(tasks[t].waiting_msgs);
|
}
|
}
|
printf("\n");
|
printf("\n");
|
}
|
}
|
|
|
/* Simple round-robin scheduler that directly calls dispatcher. It is
|
/* Simple round-robin scheduler that directly calls dispatcher. It is
|
called by low level external interrupt exception handler or by
|
called by low level external interrupt exception handler or by
|
kernel_syscall if KERNEL_SYSCALL_SCHED is defined. */
|
kernel_syscall if KERNEL_SYSCALL_SCHED is defined. */
|
void kernel_sched()
|
void kernel_sched()
|
{
|
{
|
if ((++curtask > MAX_TASKS) || !(tasks[curtask].regs.pc & ~0x3))
|
if ((++curtask > MAX_TASKS) || !(tasks[curtask].regs.pc & ~0x3))
|
curtask = 1;
|
curtask = 1;
|
task_context = (unsigned long *)&tasks[curtask].regs;
|
task_context = (unsigned long *)&tasks[curtask].regs;
|
|
|
#if KERNEL_OUTPUT
|
#if KERNEL_OUTPUT
|
printf("kernel_sched(): entry number %d, ", ++kernel_sched_cnt);
|
printf("kernel_sched(): entry number %d, ", ++kernel_sched_cnt);
|
printf("dispatching task TID=%d, time %u cycles", curtask, timestamp());
|
printf("dispatching task TID=%d, time %u cycles", curtask, timestamp());
|
|
|
kernel_show_contexts();
|
kernel_show_contexts();
|
#endif
|
#endif
|
|
|
dispatch();
|
dispatch();
|
}
|
}
|
|
|
/* System call uos_msgsnd. */
|
/* System call uos_msgsnd. */
|
int uos_msgsnd(tid_t desttask, char *buf, int len)
|
int uos_msgsnd(tid_t desttask, char *buf, int len)
|
{
|
{
|
asm("l.sys 1");
|
asm("l.sys 1");
|
asm("l.nop");
|
asm("l.nop");
|
}
|
}
|
|
|
/* System call uos_msgrcv. */
|
/* System call uos_msgrcv. */
|
int uos_msgrcv(tid_t origintask, char *buf, int len)
|
int uos_msgrcv(tid_t origintask, char *buf, int len)
|
{
|
{
|
asm("l.sys 2");
|
asm("l.sys 2");
|
asm("l.nop");
|
asm("l.nop");
|
}
|
}
|
|
|
/* Handles system call uos_msgsnd. */
|
/* Handles system call uos_msgsnd. */
|
void kernel_msgsnd(tid_t tid)
|
void kernel_msgsnd(tid_t tid)
|
{
|
{
|
struct mcb *mcb;
|
struct mcb *mcb;
|
struct mcb **dstmq;
|
struct mcb **dstmq;
|
struct tcb *task;
|
struct tcb *task;
|
|
|
task = &tasks[tid];
|
task = &tasks[tid];
|
|
|
/* Sanity checks. */
|
/* Sanity checks. */
|
|
|
/* Does destination task exist? */
|
/* Does destination task exist? */
|
if (!task->regs.gprs[1] || (task->regs.gprs[1] > MAX_TASKS)) {
|
if (!task->regs.gprs[1] || (task->regs.gprs[1] > MAX_TASKS)) {
|
task->regs.gprs[9] = IPC_ENOTASK;
|
task->regs.gprs[9] = IPC_ENOTASK;
|
return;
|
return;
|
}
|
}
|
|
|
/* Are there any free MCBs? */
|
/* Are there any free MCBs? */
|
if (!free_mcbs) {
|
if (!free_mcbs) {
|
task->regs.gprs[9] = IPC_EOUTOFMCBS;
|
task->regs.gprs[9] = IPC_EOUTOFMCBS;
|
return;
|
return;
|
}
|
}
|
|
|
/* Is message too big to fit into MCB's message buffer? */
|
/* Is message too big to fit into MCB's message buffer? */
|
if (task->regs.gprs[3] > MAX_MSGLEN) {
|
if (task->regs.gprs[3] > MAX_MSGLEN) {
|
task->regs.gprs[9] = IPC_ETOOBIG;
|
task->regs.gprs[9] = IPC_ETOOBIG;
|
return;
|
return;
|
}
|
}
|
|
|
/* OK, send the message. */
|
/* OK, send the message. */
|
|
|
/* First, allocate MCB. */
|
/* First, allocate MCB. */
|
mcb = free_mcbs;
|
mcb = free_mcbs;
|
free_mcbs = mcb->next;
|
free_mcbs = mcb->next;
|
|
|
/* Second, copy message to the MCB. */
|
/* Second, copy message to the MCB. */
|
memcpy(mcb->msg, (void *)task->regs.gprs[2], task->regs.gprs[3]);
|
memcpy(mcb->msg, (void *)task->regs.gprs[2], task->regs.gprs[3]);
|
mcb->origin = tid;
|
mcb->origin = tid;
|
mcb->length = task->regs.gprs[3];
|
mcb->length = task->regs.gprs[3];
|
mcb->next = NULL;
|
mcb->next = NULL;
|
|
|
/* Insert MCB into destination task's message queue at
|
/* Insert MCB into destination task's message queue at
|
the end. */
|
the end. */
|
dstmq = &tasks[task->regs.gprs[1]].waiting_msgs;
|
dstmq = &tasks[task->regs.gprs[1]].waiting_msgs;
|
for(;*dstmq;)
|
for(;*dstmq;)
|
dstmq = &((*dstmq)->next);
|
dstmq = &((*dstmq)->next);
|
*dstmq = mcb;
|
*dstmq = mcb;
|
|
|
task->regs.gprs[9] = IPC_NOERR;
|
task->regs.gprs[9] = IPC_NOERR;
|
return;
|
return;
|
}
|
}
|
|
|
/* Handles system call uos_msgrcv. */
|
/* Handles system call uos_msgrcv. */
|
void kernel_msgrcv(tid_t tid)
|
void kernel_msgrcv(tid_t tid)
|
{
|
{
|
struct mcb *mcb;
|
struct mcb *mcb;
|
struct mcb *curmsg, **linkp;
|
struct mcb *curmsg, **linkp;
|
struct tcb *task;
|
struct tcb *task;
|
|
|
task = &tasks[tid];
|
task = &tasks[tid];
|
|
|
/* Sanity checks. */
|
/* Sanity checks. */
|
|
|
/* Does origin task exist? */
|
/* Does origin task exist? */
|
if (task->regs.gprs[1] > MAX_TASKS) {
|
if (task->regs.gprs[1] > MAX_TASKS) {
|
task->regs.gprs[9] = IPC_ENOTASK;
|
task->regs.gprs[9] = IPC_ENOTASK;
|
return;
|
return;
|
}
|
}
|
|
|
/* Are there any messages waiting for reception? */
|
/* Are there any messages waiting for reception? */
|
if (!task->waiting_msgs) {
|
if (!task->waiting_msgs) {
|
task->regs.gprs[9] = IPC_ENOMSGS;
|
task->regs.gprs[9] = IPC_ENOMSGS;
|
return;
|
return;
|
}
|
}
|
|
|
/* OK, receive the message. */
|
/* OK, receive the message. */
|
|
|
/* Search waiting messages for one coming from origintask. If
|
/* Search waiting messages for one coming from origintask. If
|
origintask is zero then grab the first message. */
|
origintask is zero then grab the first message. */
|
curmsg = task->waiting_msgs;
|
curmsg = task->waiting_msgs;
|
linkp = &task->waiting_msgs;
|
linkp = &task->waiting_msgs;
|
for(;task->regs.gprs[1] && curmsg->next && curmsg->origin != task->regs.gprs[1];) {
|
for(;task->regs.gprs[1] && curmsg->next && curmsg->origin != task->regs.gprs[1];) {
|
linkp = &curmsg->next;
|
linkp = &curmsg->next;
|
curmsg = curmsg->next;
|
curmsg = curmsg->next;
|
}
|
}
|
|
|
/* Is receive buffer too small for receiving message? */
|
/* Is receive buffer too small for receiving message? */
|
if (task->regs.gprs[3] < curmsg->length) {
|
if (task->regs.gprs[3] < curmsg->length) {
|
task->regs.gprs[9] = IPC_ETOOBIG;
|
task->regs.gprs[9] = IPC_ETOOBIG;
|
return;
|
return;
|
}
|
}
|
|
|
/* Now copy the message from the MCB. */
|
/* Now copy the message from the MCB. */
|
memcpy((void *)task->regs.gprs[2], curmsg->msg, task->regs.gprs[3]);
|
memcpy((void *)task->regs.gprs[2], curmsg->msg, task->regs.gprs[3]);
|
|
|
/* Remove MCB from task's waiting queue and place it
|
/* Remove MCB from task's waiting queue and place it
|
back into free MCBs queue. */
|
back into free MCBs queue. */
|
*linkp = curmsg->next;
|
*linkp = curmsg->next;
|
curmsg->next = free_mcbs;
|
curmsg->next = free_mcbs;
|
free_mcbs = curmsg;
|
free_mcbs = curmsg;
|
|
|
task->regs.gprs[9] = IPC_NOERR;
|
task->regs.gprs[9] = IPC_NOERR;
|
return;
|
return;
|
}
|
}
|
|
|
/* Handles all uOS system calls. It is called by low level system call
|
/* Handles all uOS system calls. It is called by low level system call
|
exception handler. */
|
exception handler. */
|
void kernel_syscall()
|
void kernel_syscall()
|
{
|
{
|
unsigned short syscall_num;
|
unsigned short syscall_num;
|
|
|
#if KERNEL_OUTPUT
|
#if KERNEL_OUTPUT
|
printf("kernel_syscall(): entry number %d, ", ++kernel_syscall_cnt);
|
printf("kernel_syscall(): entry number %d, ", ++kernel_syscall_cnt);
|
printf("current TID=%d, time %u cycles", curtask, timestamp());
|
printf("current TID=%d, time %u cycles", curtask, timestamp());
|
|
|
kernel_show_contexts();
|
kernel_show_contexts();
|
#endif
|
#endif
|
syscall_num = *(unsigned short *)((tasks[curtask].regs.pc & ~0x3) - 6);
|
syscall_num = *(unsigned short *)((tasks[curtask].regs.pc & ~0x3) - 6);
|
|
|
switch(syscall_num) {
|
switch(syscall_num) {
|
case IPC_MSGSND:
|
case IPC_MSGSND:
|
kernel_msgsnd(curtask);
|
kernel_msgsnd(curtask);
|
break;
|
break;
|
case IPC_MSGRCV:
|
case IPC_MSGRCV:
|
kernel_msgrcv(curtask);
|
kernel_msgrcv(curtask);
|
break;
|
break;
|
default:
|
default:
|
printf("kernel_syscall(): unknown syscall (%u)\n", syscall_num);
|
printf("kernel_syscall(): unknown syscall (%u)\n", syscall_num);
|
}
|
}
|
|
|
#if KERNEL_SYSCALL_SCHED
|
#if KERNEL_SYSCALL_SCHED
|
kernel_sched();
|
kernel_sched();
|
#endif
|
#endif
|
dispatch();
|
dispatch();
|
}
|
}
|
|
|
/* Called by reset exception handler to initialize the kernel and start
|
/* Called by reset exception handler to initialize the kernel and start
|
rolling first task. */
|
rolling first task. */
|
int kernel_init()
|
int kernel_init()
|
{
|
{
|
tid_t t;
|
tid_t t;
|
int i;
|
int i;
|
|
|
printf("Initializing kernel:\n");
|
printf("Initializing kernel:\n");
|
|
|
printf(" Clearing kernel structures...\n");
|
printf(" Clearing kernel structures...\n");
|
memset(tasks, 0, sizeof(tasks));
|
memset(tasks, 0, sizeof(tasks));
|
memset(stacks, 0, sizeof(stacks));
|
memset(stacks, 0, sizeof(stacks));
|
memset(msgs, 0, sizeof(msgs));
|
memset(msgs, 0, sizeof(msgs));
|
|
|
printf(" Initializing MCBs... %d MCB(s)\n", MAX_MSGS);
|
printf(" Initializing MCBs... %d MCB(s)\n", MAX_MSGS);
|
for(i = 0; i < (MAX_MSGS - 1); i++)
|
for(i = 0; i < (MAX_MSGS - 1); i++)
|
msgs[i].next = &msgs[i+1];
|
msgs[i].next = &msgs[i+1];
|
free_mcbs = &msgs[0];
|
free_mcbs = &msgs[0];
|
|
|
printf(" Initializing TCBs... %d user task(s)\n", MAX_TASKS);
|
printf(" Initializing TCBs... %d user task(s)\n", MAX_TASKS);
|
|
|
tasks_entries();
|
tasks_entries();
|
|
|
for(t = 0; t <= MAX_TASKS; t++) {
|
for(t = 0; t <= MAX_TASKS; t++) {
|
tasks[t].regs.sp = (unsigned long)stacks[t] + STACK_SIZE - 4;
|
tasks[t].regs.sp = (unsigned long)stacks[t] + STACK_SIZE - 4;
|
/* Disable EXR for kernel context */
|
/* Disable EXR for kernel context */
|
tasks[t].regs.sr |= (t == 0 ? SPR_SR_SM : SPR_SR_TEE | SPR_SR_IEE);
|
tasks[t].regs.sr |= (t == 0 ? SPR_SR_SM : SPR_SR_TEE | SPR_SR_IEE);
|
tasks[t].regs.gprs[1] = t;
|
tasks[t].regs.gprs[1] = t;
|
}
|
}
|
|
|
/* First task runs in seprvisor mode */
|
/* First task runs in seprvisor mode */
|
tasks[1].regs.sr |= SPR_SR_SM;
|
tasks[1].regs.sr |= SPR_SR_SM;
|
|
|
/* TID=0 is reserved for kernel use */
|
/* TID=0 is reserved for kernel use */
|
kernel_context = (unsigned long *)&tasks[0].regs;
|
kernel_context = (unsigned long *)&tasks[0].regs;
|
|
|
/* First task to be scheduled is task TID=1 */
|
/* First task to be scheduled is task TID=1 */
|
task_context = (unsigned long *)&tasks[1].regs;
|
task_context = (unsigned long *)&tasks[1].regs;
|
|
|
/* Initialize initrrupt controller */
|
/* Initialize initrrupt controller */
|
int_init();
|
int_init();
|
|
|
printf(" Exceptions will be enabled when first task is dispatched.\n");
|
printf(" Exceptions will be enabled when first task is dispatched.\n");
|
printf("Kernel initalized. Starting first user task.\n");
|
printf("Kernel initalized. Starting first user task.\n");
|
|
|
#if KERNEL_SYSCALL_SCHED
|
#if KERNEL_SYSCALL_SCHED
|
kernel_sched(); /* Lets schedule and dispatch our first task */
|
kernel_sched(); /* Lets schedule and dispatch our first task */
|
#else
|
#else
|
tick_init(TICK_PERIOD, kernel_sched);
|
tick_init(TICK_PERIOD, kernel_sched);
|
kernel_sched(); /* Lets schedule and dispatch our first task */
|
kernel_sched(); /* Lets schedule and dispatch our first task */
|
#endif
|
#endif
|
/* ... */ /* We never get here */
|
/* ... */ /* We never get here */
|
}
|
}
|
|
|
int main ()
|
int main ()
|
{
|
{
|
kernel_init();
|
kernel_init();
|
return 0;
|
return 0;
|
}
|
}
|
|
|