最近,我厌倦了每天在工作中埋头苦干。我已经决定尝试一些低级的东西,我听过很多人谈论这个叫做RISC-V的新的伟大的架构,我一直喜欢低级的东西,所以我决定尝试一下它。
顾名思义,RISC-V是一种RISC架构,它只有几条指令,并且被设计成可扩展的。您可以使用基本实现做任何事情,但是由于它非常有限,大多数实现都使用扩展来添加附加功能(如整数乘法)。可以在此处找到分机列表。这使得ISA非常容易实现和为其实现软件。
在这篇文章中,我将解释从汇编语言到c语言需要什么,并打印一条hello world消息。
当RISC-V CPU启动时,其所有HART(硬件线程)从地址0x80000000开始执行代码。理论上,我们只需写入UART地址就可以将内容显示在屏幕上,但是由于我不喜欢编写大量的汇编,所以让我们先跳到C,然后显示我们的消息。要跳转到C,我们需要设置堆栈并停放除一个之外的所有HART。为每个HART和PARKS不需要的HART设置堆栈的整个汇编代码如下所示:
.equSTACK_SIZE,1024.global_start_start:#Setup Stacks Per HART csrr t0,mhartid#读取当前HART ID slli t0,t0,10#将HART ID左移1024la sp,stacks+STACK_SIZE#将初始堆栈指针#设置到堆栈空间的末尾add sp,sp,t0#将当前HART堆栈指针#移动到其在堆栈空间中的位置#具有ID!=0 csrr a0的公园Harts,我们不在HART 0上#我们停HART j回车#跳到cpark:WFI j…。
在liker脚本中,我们告诉链接器将_start符号放在0x80000000,Enter符号是我们在C中的入口点。
太棒了!我们用C语言,因为我们将使用QEMU来运行代码,获得IO的最简单的方法是使用UART,这是一个相当大的标准,我可以写一整篇关于它的文章,但是现在我们只需要知道,根据QEMU的源代码,它的内存映射在地址0x10000000,写入它会让事情出现在屏幕上。
UART有一些寄存器来控制它,目前我们只关心位于距UART基地址偏移0x05处的线路状态寄存器(或LSR)(寄存器的完整列表可以在这里找到)。LSR可以告诉我们是否可以将数据写入UART缓冲区,这个寄存器的可能值在这里。
static int putchar(Uint8_T Ch){static uint8_t thr=0x00;static uint8_t lsr=0x05;static uint8_t lsr_ri=0x40;while((UART[lsr]&;lsr_ri)==0);return UART[thr]=ch;}。
UART变量被全局定义为易失性变量,因此编译器不会试图将其优化出来。
因为这段代码只写一个字符,所以我们需要创建一个帮助器函数来写整行。
现在我们可以调用看跌期权,希望我们能在屏幕上看到一些东西:
tyfinf unsign char uint8_t;static volatiluint8_t*uart=(void*)0x10000000;static int putchar(Char Ch){static uint8_t thr=0x00;static uint8_t lsr=0x05;static uint8_t lsr_ri=0x40;while((UART[lsr]&;lsr_ri)==0);返回UART[thr]=。putchar(';\n';);}void enter(){put(";Hello RISC-V";);}。
我们可以使用GCC riscv64-UNKNOWN-ELF-GCC-MARCH=rv32imac-mabi=ilp32-T default.lds-o out-nostdlib-fno-builtin start.s main.c编译它。s start.s是我们的汇编源,main.c是我们的c代码。
这应该会产生一个名为out的文件,该文件可以使用qemu运行:qemu-system-riscv32-noraphic-smp 4-Machine virt-bios non-kernel out。
这可能不是很多,但对于编写整个内核来说,这是一个很好的开始。在未来,我将很乐意介绍如下内容:中断内存管理、非特权ISA。