😺

comelangでpicoのリアルタイムOS作りました。

2025/01/09に公開

タイマー割り込みでタスク遷移いけました。minuxの方がラウンドロビンでminux2の方がタイマー割り込みです。minux2の方はとりあえずタスクスィッチに成功しただけで、R4-R11のレジスタの保存がまだです。PICOで動きcomelangで記述してます。

#include <comelang.h>
#include "hardware/timer.h"

#define TASK_MAX 2
#define STACK_SIZE 8048

volatile uint32_t SP,PC, R4, R5, R6, R7, R8, R9, R10, R11;
volatile uint32_t *O, *P, *Q;

struct sTask
{
    uint32_t sp;
    uint32_t pc;
    uint32_t r4;
    uint32_t r5;
    uint32_t r6;
    uint32_t r7;
    uint32_t r8;
    uint32_t r9;
    uint32_t r10;
    uint32_t r11;
};

struct sTask gTask[TASK_MAX];
uint16_t gStackArea[TASK_MAX][STACK_SIZE];

int gNumTasks = 0;
int gCurrentTask = 0;


void task1()
{
    while(1) {
        puts("TASK1");
        sleep_ms(1000);
    }
}

void task2()
{
    while(1) {
        puts("TASK2");
        sleep_ms(1000);
    }
}

void save_context()
{
/*
    asm volatile (
        "push {r4}\n"
        : 
        :
        : "r4", "memory"
    );
    asm volatile (
        "push {r5}\n"
        : 
        :
        : "r5", "memory"
    );
    asm volatile (
        "push {r6}\n"
        : 
        :
        : "r6", "memory"
    );
    asm volatile (
        "push {r7}\n"
        : 
        :
        : "r7", "memory"
    );
    asm volatile (
        "mov r4, r8\n"
        "push {r4}\n"
        : 
        :
        : "r4", "r8", "memory"
    );
    asm volatile (
        "mov r4, r9\n"
        "push {r4}\n"
        : 
        :
        : "r4", "r9", "memory"
    );
    asm volatile (
        "mov r4, r10\n"
        "push {r4}\n"
        : 
        :
        : "r4", "r10", "memory"
    );
    asm volatile (
        "mov r4, r11\n"
        "push {r4}\n"
        : 
        :
        : "r4", "r11", "memory"
    );
*/
    asm volatile (
        "mrs r1, psp\n"
        "ldr r0, =SP; \n"
        "str r1, [r0]; \n"
        : 
        :
        : "r0", "r1"
    );
    
    gTask[gCurrentTask].sp = SP; 
}
    
void restore_context()
{
    SP = gTask[gCurrentTask].sp;
    
    PC = *((uint32_t*)SP +6);
printf("PC6 %d\n", PC);
    
    asm volatile (
        "ldr r0, =SP; \n"
        "ldr r3, [r0]; \n"
        "msr psp, r3; \n"
        :
        :
        : "r0", "r3"
    );
/*
    asm volatile (
        "ldr r0, =PC; \n"
        "ldr r4, [r0]; \n"
        "bx r4; \n"
        :
        :
        : "r0", "r4"
    );
*/
/*
    asm volatile (
        "pop {r4}; \n"
        "mov r11, r4;\n"
        :
        :
        : "r4", "r11"
    );
    asm volatile (
        "pop {r4}; \n"
        "mov r10, r4;\n"
        :
        :
        : "r4", "r10"
    );
    asm volatile (
        "pop {r4}; \n"
        "mov r9, r4;\n"
        :
        :
        : "r4", "r9"
    );
    asm volatile (
        "pop {r4}; \n"
        "mov r8, r4;\n"
        :
        :
        : "r4", "r8"
    );
    asm volatile (
        "pop {r4}; \n"
        "mov r7, r4;\n"
        :
        :
        : "r4", "r7"
    );
    asm volatile (
        "pop {r4}; \n"
        "mov r6, r4;\n"
        :
        :
        : "r4", "r6"
    );
    asm volatile (
        "pop {r4}; \n"
        "mov r5, r4;\n"
        :
        :
        : "r4", "r5"
    );
    asm volatile (
        "pop {r4}; \n"
        :
        :
        : "r4"
    );
*/
}

bool timer_callback(struct repeating_timer *t) 
{
    save_context();
     
    gCurrentTask = (gCurrentTask + 1) % TASK_MAX;
    
    restore_context();
    
    return true;
}

void init_task(void (*fun)())
{
    volatile uint32_t* stack_end = (uint32_t*)(&gStackArea[gNumTasks][STACK_SIZE-1]);

    *(--stack_end) = 0x01000000;  // xPSR
    *(--stack_end) = (uint32_t)fun; // PC
    *(--stack_end) = 0xFFFFFFFD;  // LR (例外リターン)
    int i;
    for (i = 0; i < 5; i++) {
        *(--stack_end) = 0;
    }
    memset(gTask + gNumTasks, 0, sizeof(sTask));
    gTask[gNumTasks].sp = (uint32_t)stack_end;
printf("init task3 SP %d\n", gTask[gNumTasks].sp);
    
    gNumTasks++;
/*
R0
R1
R2
R3
R12
LR(呼び出し元の戻りアドレス)
PC(割り込み発生時のプログラムカウンタ)
xPSR
*/
}

int main() 
{
    stdio_init_all();
    sleep_ms(5000);
    
printf("task1 %d task2 %d\n", task1, task2);
    
    init_task(task1);
    init_task(task2);
    
puts("1");
    
    PC = gTask[gCurrentTask].pc;
    SP = gTask[gCurrentTask].sp;
    
    struct repeating_timer timer;
    add_repeating_timer_ms(2000, timer_callback, NULL, &timer);
    
    //restore_context();
    
puts("2");

    asm volatile (
        "ldr r0, =SP; \n"
        "ldr r4, [r0]; \n"
        "msr psp, r4\n"
        "mov r0, #0x02\n"
        "msr CONTROL, r0\n"
        "isb\n"
        :
        : 
        : "r0","r4"            // 使用するレジスタ
    );
    
    task1();
    

/*
リンクレジスタ(LR)の内容 割り込み時に LR には特別な値が設定されます:

EXC_RETURN と呼ばれる特別な値で、割り込みから復帰するための情報を含みます。
一般的な値:
0xFFFFFFF9: メインスタックポインタ(MSP)を使用して復帰
0xFFFFFFFD: プロセススタックポインタ(PSP)を使用して復帰
このため、割り込み発生時点の PC(呼び出し元のアドレス) は、スタックに保存されますが、LR には保存されません。
割り込みが発生すると、スタックには次のようにデータが保存されます(スタックトップから順に):
R0
R1
R2
R3
R12
LR(呼び出し元の戻りアドレス)
PC(割り込み発生時のプログラムカウンタ)
xPSR
*/
    
    while (true) {
    }
    
    return 0;
}

ソースはこれです。R4-R11レジスタの保存と復帰がまだです。でもタスク遷移は動いてます。
https://github.com/ab25cq/comelang
のminuxがラウンドロビン、minux2がタイマー割り込みのOSです。
comelangで記述してます。

Discussion