我正在尝试为基于RISC-V的板创建启动程序。
我正在遵循本指南,并对其进行调整以适应riscv。
osdev
我遇到的问题是翻译此指令。times 510 -( $ - $$ ) db 0
我能想到的最好的办法就是只填写该.8byte 0
的63行
但这似乎不太可行。
这是完整代码。
#################################
########### Boot Init ###########
#################################
.section .text
start: # begins the program
nop # the do nothing instruction
j start # loops back to start
# Todo:: figure out the size of the np and j instruction
# The intent of this portion is to fill the remaning bin file with 0's up until the last two bytes
.section .rodata
.4byte 0 # size of start instructions + this
.8byte 0 # begins the zero's, currently 510 byte
.8byte 0
# repeat 60ish times
.8byte 0x0000000000aa55 # fills the last two bytes with the universal
# 55aa to indicate boot program
编辑
我正在使用gcc工具链进行risc。找到here。我正在使用
.rept
指令。这是更新的代码。
#################################
########### Boot Init ###########
#################################
start: # begins the program
nop # the do nothing instruction
j start # loops back to start
# Todo:: figure out the size of the np and j instruction
# The intent of this portion is to fill the file with 0's up until the last few bytes
.rept 63
.8byte 0
.endr
.4byte 0 # size of start instructions + this
.8byte 0 # begins the zero's, currently 510 byte
.8byte 0
.8byte 0x0000000000aa55 # fills the last two bytes with the universal
# 55aa to indicate boot program
十六进制转储如下:
00000000 01 00 fd bf 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000210 55 aa 00 00 00 00 00 00 |U.......|
00000218
在这里,我可以看到我显然弄乱了代码的字节序。
但是,我有一个新问题。十六进制转储的左列到底发生了什么??我知道*表示填充为0。但是该行从0变为10,然后从210变为218,为什么它先增加10,然后又增加8?为什么我会有空白行(218)?
编辑
无需告诉我有关行号的信息,我现在意识到它是十六进制的。
因此,最后一个问题仍然存在。如何向board指示该程序是引导程序。有魔术数字吗?我在他们的任何文档中都找不到任何指示。
最佳答案
编辑刚刚得到了我的修订版。
您可以查看入门指南,了解如何指定板
使用他们的沙箱。但这不是必需的,如果您有(gnu)
支持rv32i或超过您可以构建的rv32imac的工具链
没有其他外部依赖项的程序。
工具链本身并不知道一个板与另一个板,一个芯片与另一个板。
单据说明:
在SPI Flash(0x20000000)开始时,HiFive1 Rev B板附带了可修改的引导加载程序。在该程序执行结束时,核心跳转到代码的主要用户部分0x20010000。
这就是我们需要的关键信息,还有内存映射中内存地址空间,用于内存部分0x80000000 0x4000字节。
无向量
.globl _start
_start:
lui x2,0x80004
jal notmain
j .
.globl dummy
dummy:
ret
.globl PUT32
PUT32:
sw x11,(x10)
ret
.globl GET32
GET32:
lw x10,(x10)
ret
notmain.c
void PUT32( unsigned int, unsigned int);
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );
#define GPIOBASE 0x10012000
#define GPIO_VALUE (GPIOBASE+0x00)
#define GPIO_INPUT_EN (GPIOBASE+0x04)
#define GPIO_OUTPUT_EN (GPIOBASE+0x08)
#define GPIO_PORT (GPIOBASE+0x0C)
#define GPIO_PUE (GPIOBASE+0x10)
#define GPIO_OUT_XOR (GPIOBASE+0x40)
int notmain ( void )
{
unsigned int rx;
PUT32(GPIO_OUTPUT_EN,(1<<19)|(1<<21)|(1<<22));
PUT32(GPIO_PORT,(1<<19)|(1<<21)|(1<<22));
PUT32(GPIO_OUT_XOR,0);
while(1)
{
PUT32(GPIO_PORT,(1<<19)|(1<<21)|(1<<22));
for(rx=0;rx<2000000;rx++) dummy(rx);
PUT32(GPIO_PORT,0);
for(rx=0;rx<2000000;rx++) dummy(rx);
}
return(0);
}
内存映射
MEMORY
{
rom : ORIGIN = 0x20010000, LENGTH = 0x1000
ram : ORIGIN = 0x80000000, LENGTH = 0x4000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
建立
riscv32-none-elf-as -march=rv32imac -mabi=ilp32 novectors.s -o novectors.o
riscv32-none-elf-gcc -march=rv32imac -mabi=ilp32 -Wall -O2 -nostdlib -nostartfiles -ffreestanding -c notmain.c -o notmain.o
riscv32-none-elf-ld novectors.o notmain.o -T memmap -o notmain.elf
riscv32-none-elf-objdump -D notmain.elf > notmain.list
riscv32-none-elf-objcopy notmain.elf -O ihex notmain.hex
riscv32-none-elf-objcopy notmain.elf -O binary notmain.bin
从理论上讲,即使他们想为rv32而不是rv64进行构建,也可以使用他们谈论的riscv64-unknown-elf ...我也可以尝试...
notmain.list
Disassembly of section .text:
20010000 <_start>:
20010000: 80004137 lui x2,0x80004
20010004: 010000ef jal x1,20010014 <notmain>
20010008: a001 j 20010008 <_start+0x8>
2001000a <dummy>:
2001000a: 8082 ret
2001000c <PUT32>:
2001000c: c10c sw x11,0(x10)
2001000e: 8082 ret
20010010 <GET32>:
20010010: 4108 lw x10,0(x10)
20010012: 8082 ret
20010014 <notmain>:
20010014: 1141 addi x2,x2,-16
20010016: c04a sw x18,0(x2)
20010018: 10012937 lui x18,0x10012
2001001c: 00890513 addi x10,x18,8 # 10012008 <_start-0xfffdff8>
20010020: 006805b7 lui x11,0x680
20010024: c606 sw x1,12(x2)
20010026: c226 sw x9,4(x2)
20010028: c422 sw x8,8(x2)
2001002a: 37cd jal 2001000c <PUT32>
2001002c: 00c90513 addi x10,x18,12
20010030: 006805b7 lui x11,0x680
20010034: 3fe1 jal 2001000c <PUT32>
20010036: 04090513 addi x10,x18,64
2001003a: 4581 li x11,0
2001003c: 001e84b7 lui x9,0x1e8
20010040: 37f1 jal 2001000c <PUT32>
20010042: 0931 addi x18,x18,12
20010044: 48048493 addi x9,x9,1152 # 1e8480 <_start-0x1fe27b80>
20010048: 006805b7 lui x11,0x680
2001004c: 854a mv x10,x18
2001004e: 3f7d jal 2001000c <PUT32>
20010050: 4401 li x8,0
20010052: 8522 mv x10,x8
20010054: 0405 addi x8,x8,1
20010056: 3f55 jal 2001000a <dummy>
20010058: fe941de3 bne x8,x9,20010052 <notmain+0x3e>
2001005c: 4581 li x11,0
2001005e: 854a mv x10,x18
20010060: 3775 jal 2001000c <PUT32>
20010062: 4401 li x8,0
20010064: 8522 mv x10,x8
20010066: 0405 addi x8,x8,1
20010068: 374d jal 2001000a <dummy>
2001006a: fe941de3 bne x8,x9,20010064 <notmain+0x50>
2001006e: bfe9 j 20010048 <notmain+0x34>
在尝试将程序加载到设备上之前,请务必进行检查,对于出厂时的此板/芯片,我们所需的进入代码,novectors.s的第一条说明必须位于0x20010000。是的。
notmain.hex
:020000042001D9
:1000000037410080EF00000101A082800CC1828096
:100010000841828041114AC0372901101305890027
:10002000B705680006C626C222C4CD371305C9002D
:10003000B7056800E13F130509048145B7841E0038
:10004000F137310993840448B70568004A857D3F3C
:10005000014422850504553FE31D94FE81454A85F0
:1000600075370144228505044D37E31D94FEE9BF31
:0400000520010000D6
:00000001FF
将notmain.hex复制到已装载的HiFive介质。现在,这花了我一两个小时的时间,试图在我开始时找出十六进制文件,在这里它没有用。下载了他们通过挖出的sdk而发现的elf2hex,但这与fpga工作很不相干。弄清楚了,他们正在做的只是riscv ... objcopy -O ihex,就像我尝试过的一样。现在就可以了。我收到了fail.txt文件,说它之前无法连接到CPU。不知道我做了什么或没有做这项工作。
从理论上讲,您可以将十六进制文件剪切并粘贴到上面,然后保存并复制。为什么没有人提供十六进制文件示例,您必须正确安装75项特殊的东西并运行构建,而不是在此提供包含中间文件的完整示例。我当然会在该平台的示例中做到这一点。或至少以上一种。
与其以彩虹为主导的闪烁模式,不如上面的规则使之以“白色”闪烁的方式正常闪烁。
注意LED指示灯在版本板上的同一GPIO线上,引导加载程序所到达的地址与版本0x20010000不在同一地址0x20400000。因此,可以通过更改memmap来为开发板构建相同的版本。
如果您或读者想要返回到修订版,则如果它有一个修订版,则它是经过修改的openocd,在撰写本文时,它位于github riscv用户riscv-openocd项目中。正常的./bootstrap、./configure,获取工具,并且在tcl目录中有上面显示的riscv openocd配置文件
interface ftdi
ftdi_device_desc "Dual RS232-HS"
ftdi_vid_pid 0x0403 0x6010
关键是rev2开发板lsusb:
Bus 001 Device 018: ID 1366:1051 SEGGER
并且在openocd配置文件中没有对那些pid / vid值产生任何影响。导致阅读更多的入门手册。
首先回答/在上面编辑之前
我有一个原始的hifive1板,您是怎么得到转速b的?
无论如何...
对于原始板,入门指南说:
在SPI Flash(0x20000000)开始时,HiFive1板附带了可修改的引导加载程序。在该程序执行结束时,核心跳转到代码的主要用户部分0x20400000。
对于版本b,它说:
在SPI Flash(0x20000000)开始时,HiFive1 Rev B板附带了可修改的引导加载程序。在该程序执行结束时,核心跳转到代码的主要用户部分0x20010000。
两种芯片均显示ram的0x80000000和(外部)闪存的0x20000000。假设这是他们将闪光灯放置在rev B板上的接口。
第一个程序。
无向量
.globl _start
_start:
lui x2,0x80004
jal notmain
sbreak
j .
.globl dummy
dummy:
ret
notmain.c
void dummy ( unsigned int );
int notmain ( void )
{
unsigned int ra;
for(ra=0;;ra++) dummy(ra);
return(0);
}
内存映射
MEMORY
{
ram : ORIGIN = 0x80000000, LENGTH = 0x4000
}
SECTIONS
{
.text : { *(.text*) } > ram
.rodata : { *(.rodata*) } > ram
.bss : { *(.bss*) } > ram
}
建立
riscv32-none-elf-as -march=rv32i -mabi=ilp32 novectors.s -o novectors.o
riscv32-none-elf-gcc -march=rv32i -mabi=ilp32 -Wall -O2 -nostdlib -nostartfiles -ffreestanding -c notmain.c -o notmain.o
riscv32-none-elf-ld novectors.o notmain.o -T memmap -o notmain.elf
riscv32-none-elf-objdump -D notmain.elf > notmain.list
riscv32-none-elf-objcopy notmain.elf -O binary notmain.bin
从理论上讲,您可以使用riscv32-whatever-whatever(riscv32-unknown-elf等)。由于此代码足够通用。另请注意,我使用的是最小的rv32i,您可能可以在整个辣酱玉米饼馅中使用rv32imac。
检查拆卸:
Disassembly of section .text:
80000000 <_start>:
80000000: 80004137 lui x2,0x80004
80000004: 010000ef jal x1,80000014 <notmain>
80000008: 00100073 ebreak
8000000c: 0000006f j 8000000c <_start+0xc>
80000010 <dummy>:
80000010: 00008067 ret
80000014 <notmain>:
80000014: ff010113 addi x2,x2,-16 # 80003ff0 <notmain+0x3fdc>
80000018: 00812423 sw x8,8(x2)
8000001c: 00112623 sw x1,12(x2)
80000020: 00000413 li x8,0
80000024: 00040513 mv x10,x8
80000028: fe9ff0ef jal x1,80000010 <dummy>
8000002c: 00140413 addi x8,x8,1
80000030: ff5ff06f j 80000024 <notmain+0x10>
作为rv32i,它都是32位指令,这很好。该程序旨在加载到ram中并使用调试器在其中运行,我在其中使用openocd和telnet。
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
然后
halt
load_image notmain.elf
resume 0x80000000
在telnet窗口中
然后,您可以再次停止。
80000024: 00040513 mv x10,x8
80000028: fe9ff0ef jal x1,80000010 <dummy>
8000002c: 00140413 addi x8,x8,1
80000030: ff5ff06f j 80000024 <notmain+0x10>
您可以检查x8或x10以查看其计数
resume
halt
并再次检查它们应该增加的寄存器。第一个程序运行,继续。
第二个程序改为使用此链接描述文件
内存映射
MEMORY
{
rom : ORIGIN = 0x20010000, LENGTH = 0x4000
ram : ORIGIN = 0x80000000, LENGTH = 0x4000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
检查拆卸。
Disassembly of section .text:
20010000 <_start>:
20010000: 80004137 lui x2,0x80004
20010004: 010000ef jal x1,20010014 <notmain>
20010008: 00100073 ebreak
2001000c: 0000006f j 2001000c <_start+0xc>
20010010 <dummy>:
20010010: 00008067 ret
20010014 <notmain>:
20010014: ff010113 addi x2,x2,-16 # 80003ff0 <notmain+0x5fff3fdc>
20010018: 00812423 sw x8,8(x2)
2001001c: 00112623 sw x1,12(x2)
20010020: 00000413 li x8,0
20010024: 00040513 mv x10,x8
20010028: fe9ff0ef jal x1,20010010 <dummy>
2001002c: 00140413 addi x8,x8,1
20010030: ff5ff06f j 20010024 <notmain+0x10>
它似乎是位置无关的,因此它应该与其他链接程序脚本一样工作,但最好使用正确的地址。
我的笔记说:
flash protect 0 64 last off
program notmain.elf verify
resume 0x20010000
现在您应该能够重置或重启电路板,以不重置的方式连接openocd(或者根据需要进行连接),然后就不需要加载任何东西,它应该运行其引导加载程序,然后在以下位置启动您的引导加载程序该地址(就像他们提到的那样跳转到该地址)。检查r8或r10(此abi的r10是第一个传递的参数,因此,即使您的gcc使用r8以外的其他东西构建,r10仍应反映计数器)resume,halt,reg,resume,halt,reg ...
在以0x20000000覆盖他们的引导加载程序之前,我将其转储并确保您有一个良好的副本,或者也许他们在其网站上有一个副本。然后,您可以将链接描述文件更改为0x20000000。在我亲自进行操作之前,我将分解并检查他们的引导加载程序,以了解它在做什么,是否值得保留等。他们的文字说“可修改”
我在hifive1板上割下了risc-v齿,但是已经转向SIM开源内核,hifive板非常贵。我还做了一个最小的PCB,放下了一些零件,只用完了ram等,但是我的板子太小了,我没有回去再试一次,在他们的论坛上对于pcb工作和文档的支持很少。留下了一些不足之处。我认为,当普遍可用的修订版时,我会选择其中之一。等着瞧。祝好运。
指向那里有很多核心,您可以使用verilator或其他仿真器进行仿真,并查看发生的一切,并且您不能砌砖也不放过烟熏,因为它是Sim。
注意rv32ic
riscv32-none-elf-as -march=rv32ic -mabi=ilp32 novectors.s -o novectors.o
riscv32-none-elf-gcc -march=rv32ic -mabi=ilp32 -Wall -O2 -nostdlib -nostartfiles -ffreestanding -c notmain.c -o notmain.o
riscv32-none-elf-ld novectors.o notmain.o -T memmap -o notmain.elf
riscv32-none-elf-objdump -D notmain.elf > notmain.list
riscv32-none-elf-objcopy notmain.elf -O binary notmain.bin
并且您可以看到它使用压缩的指令
20010000 <_start>:
20010000: 80004137 lui x2,0x80004
20010004: 00a000ef jal x1,2001000e <notmain>
20010008: 9002 ebreak
2001000a: a001 j 2001000a <_start+0xa>
2001000c <dummy>:
2001000c: 8082 ret
2001000e <notmain>:
2001000e: 1141 addi x2,x2,-16
20010010: c422 sw x8,8(x2)
20010012: c606 sw x1,12(x2)
20010014: 4401 li x8,0
20010016: 8522 mv x10,x8
20010018: 3fd5 jal 2001000c <dummy>
2001001a: 0405 addi x8,x8,1
2001001c: bfed j 20010016 <notmain+0x8>
编写自己的模拟器也很容易。取决于您要如何逐步学习该平台。掌握指令集,工具链,特定芯片及其外围设备的掌握程度。
您绝对希望riscv.org提供的risc-v文档与内核支持的版本,大量内部内核寄存器和内容以及指令集相匹配。以及相关芯片的入门和芯片文档。如果您想做自己的事。如果您想在他们的沙箱中玩游戏并使用一些第三方库,那么您需要学习他们的沙箱并在他们的沙箱中玩,而不是自己做。看起来您想做自己的事。
注意我使用的是来自gnu主线资源的最新版本的gcc / binutils。
riscv32-none-elf-gcc --version
riscv32-none-elf-gcc (GCC) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
riscv32-none-elf-as --version
GNU assembler (GNU Binutils) 2.32
Copyright (C) 2019 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or later.
This program has absolutely no warranty.
This assembler was configured for a target of `riscv32-none-elf'.
上面的代码在几年前与原始的hifive1配合良好,并且这种样式通常适用于gnu的主要版本,并且我已将此工具链与其他riscv内核一起使用,因此即使您的riscv内核较旧,它也仍然可以工作。最重要的是将arch(-march)与内核支持的指令集进行匹配,或者所有内核至少应支持rv32i子集,并且压缩和乘法并不总是受支持。
我的第一块板的openocd配置文件
adapter_khz 10000
interface ftdi
ftdi_device_desc "Dual RS232-HS"
ftdi_vid_pid 0x0403 0x6010
ftdi_layout_init 0x0008 0x001b
ftdi_layout_signal nSRST -oe 0x0020 -data 0x0020
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
$_TARGETNAME configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1
flash bank onboard_spi_flash fespi 0x20000000 0 0 0 $_TARGETNAME
init
在一个终端/窗口中打开openocd -f riscv.cfg,然后在另一个终端/窗口中打开telnet localhost 4444。
现在,就您要询问的gnu汇编程序的细微差别而言,或者甚至最好使用尽可能少的特定于汇编程序/工具链的东西,并且可能有一天会更改和/或您可能会更换工具。青年汽车
GNU工具从墙上的洞不知道这块板。您可以向gnu工具介绍处理器核心体系结构,并在链接描述文件中告知内存映射。您的代码直接或间接(如果您使用别人的引导程序和链接程序脚本)必须与处理器内核的引导属性相匹配,无论它是sifive或某些arm内核或mips或x86等的risc-v。在上面的情况下,它们的引导程序会跳转到0x20010000,因此您需要将第一条指令放在0x20010000上,这是通过将该指令作为引导源中的第一条指令来完成的,如果未在链接描述文件中指定首先在ld命令行上放置该对象,然后在尝试在硬件上运行该对象之前检查反汇编以确认其是否有效。我使用的riscv内核没有向量表,要进行重置,它们只是从某个地址开始执行。因此,如果没有预引导程序跳转到您,则将使用相同的方法。对于不是risc-v的其他体系结构,如果是跳转到地址表还是向量表表,则用于板/平台的程序的构造将有所不同。
现在说,如果您正在使用他们的沙盒,那么这是一个沙盒问题,而不是gnu工具链问题。
主板文档和/或网站在其文档中表示,主板在您发现内存映射的FE310-G002文档中使用了FE310-G002芯片。它还表明这是一个risc-v架构,从那里您可以进入riscv.org基础并获得该架构的文档,该文档将告诉您它是如何启动的。然后回到FE310-G002,它会告诉您从MSEL引脚进行的引导过程。您需要检查原理图。所以现实是他们的文档确实通过提供您需要提供给gnu的信息来告诉您如何指示这是一个引导加载程序。
说...需要/需要一些实验。可以/容易地编写一个简单的位置无限循环,为0x00000000构建,但根据其文档在0x20010000处加载,并与openocd一起检查程序计数器以查看它是否确实基于0x20010000。因此,您可以假定,最终在出厂时,该板通过MSEL选择的任何一种都可以通过其引导程序进入您的引导程序。
嗯:
上电时,内核的重置向量为0x1004。
而且,它还为每个MSEL表带选项指示了不同的第一指令地址。因此,如果您要接管他们的引导程序,并根据说明文件将其替换为您自己的引导程序,则将链接为0x20000000并在此处有入口点。
关于assembly - 在RISC-V中创建 bootstrap ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58574924/