编写一个简单的riscv模拟器(三)
目录
- 添加mtimer
- 移植rtthread
- 验证
之前两篇文章已经实现了裸机程序的运行并打印log,这篇文章来实现操作系统。
编写一个简单的riscv模拟器(二)
编写一个简单的riscv模拟器(一)
要实现操作系统在cpu方面主要是实现tick时钟,能周期性产生中断就行了,剩下的都在软件的改动。
添加mtimer
仿照print外设设置timer外设的寄存器地址
#define BASE_ADDR 0x40001000
#define SIZE 0x4
实现按字 半字 字节访问寄存器
#define dev_wr(addr) g_map[(addr-BASE_ADDR) >> 2]
#define dev_wrb(addr) ((uint8_t *)g_map)[(addr-BASE_ADDR)]
#define dev_wrh(addr) ((uint16_t *)g_map)[(addr-BASE_ADDR) >> 1]
定义全局变量,模拟timer寄存器
static uint32_t g_map[1];
实现对timer的读写运行初始化等函数,这里用一个额外的线程来模拟timer运行,中断间隔为1ms
static uint32_t read(uint32_t addr) {if (!(addr & 0x3)) {return dev_wr(addr);} else if (!(addr & 0x1)) {return dev_wrh(addr);} else {return dev_wrb(addr);}
}static void write(uint32_t addr, uint32_t data) {// printf("print: write data=%x\n", data);if (!(addr & 0x3)) {dev_wr(addr) = data;} else if (!(addr & 0x1)) {dev_wrh(addr) = data;} else {dev_wrb(addr) = data;}
}typedef struct {uint32_t ins_count;pthread_mutex_t lock; /* 互斥锁定义 */
} mtimer_t;static mtimer_t g_self;static void run(riscv_t *riscv, const struct device_t* device) {int irq = 0;// printf("timer_run...\n");pthread_mutex_lock(&g_self.lock);dev_wr(BASE_ADDR) += g_self.ins_count;if (g_self.ins_count) {irq = 1;}g_self.ins_count = 0;pthread_mutex_unlock(&g_self.lock);if (irq) {riscv->mip |= MIP_MTIP;}
}static void *timer_thread(void *arg) {while (1) {milliseconds_sleep(1);pthread_mutex_lock(&g_self.lock);g_self.ins_count++;pthread_mutex_unlock(&g_self.lock);}return NULL;
}static void init(const struct device_t* device) {pthread_t tid;pthread_mutex_init(&g_self.lock, NULL);dev_wr(BASE_ADDR) = 0x0;pthread_create(&tid, NULL, timer_thread, NULL);
}const static device_t g_timer = {.addr = BASE_ADDR,.size = SIZE,.read = read,.write = write,.run = run,.init = init,
};const device_t* timer_dev() {return &g_timer;
}
注册timer外设,这样模拟器启动之后就会周期性产生mtimer中断了
riscv_register_device(&riscv, timer_dev());
移植rtthread
添加rtthread相关源文件参加编译
"riscv/rtthread/libcpu/risc-v/common/atomic_riscv.c",
"riscv/rtthread/libcpu/risc-v/common/context_gcc.S",
"riscv/rtthread/libcpu/risc-v/common/cpuport.c",
"riscv/rtthread/libcpu/risc-v/common/interrupt_gcc.S",
"riscv/rtthread/libcpu/risc-v/common/trap_common.c",
"riscv/rtthread/src/clock.c",
"riscv/rtthread/src/components.c",
"riscv/rtthread/src/cpu.c",
"riscv/rtthread/src/idle.c",
"riscv/rtthread/src/ipc.c",
"riscv/rtthread/src/irq.c",
"riscv/rtthread/src/kservice.c",
"riscv/rtthread/src/mem.c",
"riscv/rtthread/src/memheap.c",
"riscv/rtthread/src/mempool.c",
"riscv/rtthread/src/object.c",
"riscv/rtthread/src/scheduler_comm.c",
# "riscv/rtthread/src/scheduler_mp.c",
"riscv/rtthread/src/scheduler_up.c",
"riscv/rtthread/src/signal.c",
"riscv/rtthread/src/slab.c",
"riscv/rtthread/src/thread.c",
"riscv/rtthread/src/timer.c",
"riscv/rtthread/src/klibc/kstdio.c",
"riscv/rtthread/src/klibc/kstring.c",
"riscv/rtthread/components/drivers/core/device.c"
修改start.S 跳转到entry,因为rtthread是从这个函数开始启动的,然后会以一个线程的方式调用main函数
la a0, SW_handlercsrw mtvec, a0/* Call global constructors */# la a0, __libc_fini_array# call __libc_init_array# j mainj entry
添加 rttread_irq.c文件,编写如下代码,用以支持在操作系统下的中断处理,这里注册了mtimer中断的回调函数,同时打开了全局中断和mtimer中断
#include "stddef.h"
#include "head.h"
#include "rtthread.h"
#include "rt_hw_stack_frame.h"static const char *g_fault_cause[]={"Instruction address misaligned",// 0"Instruction access fault",// 1"Illegal instruction",// 2"Breakpoint",// 3"Load address misaligned",// 4"Load access fault",// 5"Store address misaligned",// 6"Store access fault",// 7"Environment call from U-mode",// 8"Environment call from S-mode",// 9"","Environment call from M-mode",// 11"Instruction page fault",// 12"Load page fault",// 13"","Store page fault",// 15
};static void trap_print(uint32_t irq_num, rt_hw_stack_frame_t *stack) {my_printf("fault reason is %s\n", g_fault_cause[irq_num]);my_printf("x1(ra): %08x\n", stack->ra);my_printf("x3(gp): %08x\n", stack->gp);my_printf("x4(tp): %08x\n", stack->tp);my_printf("x5(t0): %08x\n", stack->t0);my_printf("x6(t1): %08x\n", stack->t1);my_printf("x7(t2): %08x\n", stack->t2);my_printf("x8(s0|fp): %08x\n", stack->s0_fp);my_printf("x9(s1): %08x\n", stack->s1);my_printf("x10(a0): %08x\n", stack->a0);my_printf("x11(a1): %08x\n", stack->a1);my_printf("x12(a2): %08x\n", stack->a2);my_printf("x13(a3): %08x\n", stack->a3);my_printf("x14(a4): %08x\n", stack->a4);my_printf("x15(a5): %08x\n", stack->a5);my_printf("x16(a6): %08x\n", stack->a6);my_printf("x17(a7): %08x\n", stack->a7);my_printf("x18(s2): %08x\n", stack->s2);my_printf("x19(s3): %08x\n", stack->s3);my_printf("x20(s4): %08x\n", stack->s4);my_printf("x21(s5): %08x\n", stack->s5);my_printf("x22(s6): %08x\n", stack->s6);my_printf("x23(s7): %08x\n", stack->s7);my_printf("x24(s8): %08x\n", stack->s8);my_printf("x25(s9): %08x\n", stack->s9);my_printf("x26(s10): %08x\n", stack->s10);my_printf("x27(s11): %08x\n", stack->s11);my_printf("x28(t3): %08x\n", stack->t3);my_printf("x29(t4): %08x\n", stack->t4);my_printf("x30(t5): %08x\n", stack->t5);my_printf("x31(t6): %08x\n", stack->t6);my_printf("mstatus: %08x\n", stack->mstatus);my_printf("mepc: %08x\n", stack->epc);// my_printf("mtval: %08x\n", read_csr(mtval));// 函数调用错误,直接跳过这个函数if(irq_num==1){my_printf("skip the function call, return to %08x\n", stack->ra);stack->epc = stack->ra;}// write_csr(mscratch, 0xffffffff);
}void handle_trap(size_t mcause, size_t mepc, size_t sp) {if (mcause & 0x80000000) {my_printf("interupt mcause=%08x, mepc=%08x, sp=%08x\n", mcause, mepc, sp);} else {trap_print(mcause, (rt_hw_stack_frame_t*)sp);while (1);}
}static void tick_handler(int vec, void* par) {(void)vec;(void)par;rt_tick_increase();// my_printf("tick irq %d\n", vec);
}static uint32_t g_mem_heap[200 * 1024 / 4];
void rt_hw_board_init(void) {set_csr(mie, (1 << 3) | (1 << 7));set_csr(mip, (1 << 3));rt_system_heap_init(g_mem_heap, g_mem_heap + sizeof(g_mem_heap) / 4);rt_hw_interrupt_init();rt_hw_interrupt_install(7, tick_handler, 0, "tick_handler");
}
修改atomic_riscv.c,因为我们的模拟器不支持原子指令,所以用关中断的方式来实现原子操作
rt_atomic_t rt_hw_atomic_exchange(volatile rt_atomic_t *ptr, rt_atomic_t val)
{rt_atomic_t result = 0;
#if __riscv_xlen == 32// asm volatile ("amoswap.w %0, %1, (%2)" : "=r"(result) : "r"(val), "r"(ptr) : "memory");rt_base_t irq = rt_hw_local_irq_disable();result = *ptr;*ptr = val;rt_hw_local_irq_enable(irq);
#elif __riscv_xlen == 64asm volatile ("amoswap.d %0, %1, (%2)" : "=r"(result) : "r"(val), "r"(ptr) : "memory");
#endifreturn result;
}rt_atomic_t rt_hw_atomic_add(volatile rt_atomic_t *ptr, rt_atomic_t val)
{rt_atomic_t result = 0;
#if __riscv_xlen == 32// asm volatile ("amoadd.w %0, %1, (%2)" : "=r"(result) : "r"(val), "r"(ptr) : "memory");rt_base_t irq = rt_hw_local_irq_disable();(*ptr) += val;result = *ptr;rt_hw_local_irq_enable(irq);
#elif __riscv_xlen == 64asm volatile ("amoadd.d %0, %1, (%2)" : "=r"(result) : "r"(val), "r"(ptr) : "memory");
#endifreturn result;
}rt_atomic_t rt_hw_atomic_sub(volatile rt_atomic_t *ptr, rt_atomic_t val)
{rt_atomic_t result = 0;val = -val;
#if __riscv_xlen == 32// asm volatile ("amoadd.w %0, %1, (%2)" : "=r"(result) : "r"(val), "r"(ptr) : "memory");rt_base_t irq = rt_hw_local_irq_disable();(*ptr) += val;result = *ptr;rt_hw_local_irq_enable(irq);
#elif __riscv_xlen == 64asm volatile ("amoadd.d %0, %1, (%2)" : "=r"(result) : "r"(val), "r"(ptr) : "memory");
#endifreturn result;
}rt_atomic_t rt_hw_atomic_xor(volatile rt_atomic_t *ptr, rt_atomic_t val)
{rt_atomic_t result = 0;
#if __riscv_xlen == 32// asm volatile ("amoxor.w %0, %1, (%2)" : "=r"(result) : "r"(val), "r"(ptr) : "memory");rt_base_t irq = rt_hw_local_irq_disable();(*ptr) ^= val;result = *ptr;rt_hw_local_irq_enable(irq);
#elif __riscv_xlen == 64asm volatile ("amoxor.d %0, %1, (%2)" : "=r"(result) : "r"(val), "r"(ptr) : "memory");
#endifreturn result;
}rt_atomic_t rt_hw_atomic_and(volatile rt_atomic_t *ptr, rt_atomic_t val)
{rt_atomic_t result = 0;
#if __riscv_xlen == 32// asm volatile ("amoand.w %0, %1, (%2)" : "=r"(result) : "r"(val), "r"(ptr) : "memory");rt_base_t irq = rt_hw_local_irq_disable();(*ptr) &= val;result = *ptr;rt_hw_local_irq_enable(irq);
#elif __riscv_xlen == 64asm volatile ("amoand.d %0, %1, (%2)" : "=r"(result) : "r"(val), "r"(ptr) : "memory");
#endifreturn result;
}rt_atomic_t rt_hw_atomic_or(volatile rt_atomic_t *ptr, rt_atomic_t val)
{rt_atomic_t result = 0;
#if __riscv_xlen == 32// asm volatile ("amoor.w %0, %1, (%2)" : "=r"(result) : "r"(val), "r"(ptr) : "memory");rt_base_t irq = rt_hw_local_irq_disable();(*ptr) |= val;result = *ptr;rt_hw_local_irq_enable(irq);
#elif __riscv_xlen == 64asm volatile ("amoor.d %0, %1, (%2)" : "=r"(result) : "r"(val), "r"(ptr) : "memory");
#endifreturn result;
}rt_atomic_t rt_hw_atomic_load(volatile rt_atomic_t *ptr)
{rt_atomic_t result = 0;
#if __riscv_xlen == 32// asm volatile ("amoxor.w %0, x0, (%1)" : "=r"(result) : "r"(ptr) : "memory");rt_base_t irq = rt_hw_local_irq_disable();result = *ptr;rt_hw_local_irq_enable(irq);
#elif __riscv_xlen == 64asm volatile ("amoxor.d %0, x0, (%1)" : "=r"(result) : "r"(ptr) : "memory");
#endifreturn result;
}void rt_hw_atomic_store(volatile rt_atomic_t *ptr, rt_atomic_t val)
{rt_atomic_t result = 0;
#if __riscv_xlen == 32// asm volatile ("amoswap.w %0, %1, (%2)" : "=r"(result) : "r"(val), "r"(ptr) : "memory");rt_base_t irq = rt_hw_local_irq_disable();(*ptr) = val;result = *ptr;(void)result;rt_hw_local_irq_enable(irq);
#elif __riscv_xlen == 64asm volatile ("amoswap.d %0, %1, (%2)" : "=r"(result) : "r"(val), "r"(ptr) : "memory");
#endif
}rt_atomic_t rt_hw_atomic_flag_test_and_set(volatile rt_atomic_t *ptr)
{rt_atomic_t result = 0;rt_atomic_t temp = 1;
#if __riscv_xlen == 32// asm volatile ("amoor.w %0, %1, (%2)" : "=r"(result) : "r"(temp), "r"(ptr) : "memory");rt_base_t irq = rt_hw_local_irq_disable();(*ptr) |= 1;result = *ptr;(void)temp;rt_hw_local_irq_enable(irq);
#elif __riscv_xlen == 64asm volatile ("amoor.d %0, %1, (%2)" : "=r"(result) : "r"(temp), "r"(ptr) : "memory");
#endifreturn result;
}void rt_hw_atomic_flag_clear(volatile rt_atomic_t *ptr)
{rt_atomic_t result = 0;
#if __riscv_xlen == 32// asm volatile ("amoand.w %0, x0, (%1)" : "=r"(result) :"r"(ptr) : "memory");rt_base_t irq = rt_hw_local_irq_disable();(*ptr) = 0;result = *ptr;(void)result;rt_hw_local_irq_enable(irq);
#elif __riscv_xlen == 64asm volatile ("amoand.d %0, x0, (%1)" : "=r"(result) :"r"(ptr) : "memory");
#endif
}rt_atomic_t rt_hw_atomic_compare_exchange_strong(volatile rt_atomic_t *ptr, rt_atomic_t *old, rt_atomic_t desired)
{rt_atomic_t tmp = *old;rt_atomic_t result = 0;
#if __riscv_xlen == 32// asm volatile(// " fence iorw, ow\n"// "1: lr.w.aq %[result], (%[ptr])\n"// " bne %[result], %[tmp], 2f\n"// " sc.w.rl %[tmp], %[desired], (%[ptr])\n"// " bnez %[tmp], 1b\n"// " li %[result], 1\n"// " j 3f\n"// " 2:sw %[result], (%[old])\n"// " li %[result], 0\n"// " 3:\n"// : [result]"+r" (result), [tmp]"+r" (tmp), [ptr]"+r" (ptr)// : [desired]"r" (desired), [old]"r"(old)// : "memory");(void)tmp;(void)ptr;(void)desired;
#elif __riscv_xlen == 64asm volatile(" fence iorw, ow\n""1: lr.d.aq %[result], (%[ptr])\n"" bne %[result], %[tmp], 2f\n"" sc.d.rl %[tmp], %[desired], (%[ptr])\n"" bnez %[tmp], 1b\n"" li %[result], 1\n"" j 3f\n"" 2:sd %[result], (%[old])\n"" li %[result], 0\n"" 3:\n": [result]"+r" (result), [tmp]"+r" (tmp), [ptr]"+r" (ptr): [desired]"r" (desired), [old]"r"(old): "memory");
#endifreturn result;
}
修改interrupt_gcc.S文件,添加以下代码,实现中断回调
.globl rt_hw_do_after_save_above.type rt_hw_do_after_save_above,@function
rt_hw_do_after_save_above:addi sp, sp, -4STORE ra, 0 * REGBYTES(sp)csrr a0, mcausecsrr a1, mepcmv a2, spcall rt_rv32_system_irq_handlerLOAD ra, 0 * REGBYTES(sp)addi sp, sp, 4ret
验证
编写测试代码
char g_string[] = "string from ram";void task1(void* par) {(void)par;while (1) {my_printf("task1: %s\n", g_string);rt_thread_delay(500);}
}int main()
{my_printf("Hello World! %s\n", "Andy");// cpu_test();rt_thread_t t = rt_thread_create("task1", task1, 0, 1024, 10, 10);(void)t;rt_thread_startup(t);my_printf("enter while 1\n");while (1) {my_printf("main: %s\n", g_string);rt_thread_delay(1000);}return 0;
}
启动模拟器即可看到类似打印
\ | /
- RT - Thread Nano Operating System/ | \ 4.1.0 build Jul 15 2025 18:512006 - 2024 Copyright by RT-Thread team
Hello World! Andy
enter while 1
main: string from ram
task1: string from ram
task1: string from ram
main: string from ram
task1: string from ram
task1: string from ram
main: string from ram
task1: string from ram
task1: string from ram
源码地址