在RISC-V中创建启动程序 [英] Creating A Boot Program in RISC-V

查看:741
本文介绍了在RISC-V中创建启动程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为基于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.在此处找到.我正在使用.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)?

编辑 无需告诉我有关行号的信息,我现在意识到它是十六进制的. 因此,最后一个问题仍然存在.我如何对此表示解决方案

我有一个原始的hifive1板.对于原始板,入门指南说明:

HiFive1板在SPI闪存(0x20000000)的开头随附有可修改的引导加载程序.在该程序执行结束时,核心跳到代码的主要用户部分0x20400000.

对于版本b,它说:

HiFive1 Rev B板在SPI闪存(0x20000000)的开头随附有可修改的引导加载程序.在该程序执行结束时,核心跳转到代码的主要用户部分0x20010000.

两个芯片的ram显示为0x80000000,(外部)闪存显示为0x20000000.假设这是他们将闪光灯放置在rev B板上的接口.

第一个程序.

novectors.s

.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或其他仿真器进行仿真,并查看发生的一切,并且您不能砌成砖块,也不能抽烟,因为它是Sims.

注意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汇编程序的细微差别而言,或者甚至最好使用尽可能少的特定于汇编程序/工具链的东西,因为它可能会更改和/或有一天您可能会更改工具. YMMV

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并在其中具有入口点.

编辑

就拿到我的rev b版.

您可以查看入门指南,以了解如何指定板子 使用他们的沙箱.但这不是必需的,如果您有(gnu) 支持rv32i或更多您可以构建的rv32imac的工具链 没有其他外部依赖项的程序.

工具链本身并不了解一块板,也不知道一块板.

sifive文档说:

在SPI Flash(0x20000000)开始时,HiFive1 Rev B板附带了可修改的引导加载程序.该程序执行结束时,核心跳到代码的主要用户部分0x20010000.

这就是我们需要的关键信息,还有内存映射中内存映射的内存地址空间,用于部分0x80000000 0x4000字节的sram.

novectors.s

.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 

现在,从理论上讲,您可以使用他们谈论的riscv64-unknown-elf,即使他们想针对rv32而不是rv64进行构建.我也可以尝试.

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的第一条说明必须为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线上,引导加载程序所到达的地址与修订b 0x20010000不在同一地址0x20400000.因此,只要更改一次内存映射,就可以为开发板构建相同的版本.

如果您或读者希望拥有一个修订版,请回溯到修订版,它是经过修改的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值没有任何影响.导致阅读更多的入门手册.

I am trying to create a boot program for RISC-V based boards. I am following this guide, and adapting it for riscv. osdev

The problem I'm having is translating this instruction. times 510 -( $ - $$ ) db 0

The best I could think of is to just fill 63ish lines of this .8byte 0 But this doesn't seem very feasible.

Here is the Full code.

#################################
########### 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

EDIT

I am using the gcc toolchain for risc. Found here. I am using the .rept directive.

Here is the updated code.

#################################
########### 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

The hex dump is below:

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

Here is where I can see that I messed up the endianness of the code obviously. However, I have a new question. What the heck is going on with the left column of the hex dump?? I understand that the * means its filled to 0. However the line goes 0 to 10 then from 210 to 218 why did it increase by 10 first, then 8 at the end? why do i have a blank line (218)?

EDIT No need to tell me about the line numbering, I realized now its hex. So the last question remains. How do I indicate to this board that the program is a boot program. Is there a magic number? I could not find any indication on any of their documentation.

解决方案

I have an original hifive1 board. For the original board the getting started guide says this:

The HiFive1 Board is shipped with a modifiable boot loader at the beginning of SPI Flash (0x20000000). At the end of this program's execution the core jumps to the main user portion of code at 0x20400000.

For the rev b board it says this:

The HiFive1 Rev B Board is shipped with a modifiable boot loader at the beginning of SPI Flash (0x20000000). At the end of this program's execution the core jumps to the main user portion of code at 0x20010000.

Both chips show 0x80000000 for ram and 0x20000000 for (external) flash. Assume that is the interface where they put the flash on the rev B board.

First program.

novectors.s

.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);
}

memmap

MEMORY
{
    ram : ORIGIN = 0x80000000, LENGTH = 0x4000
}
SECTIONS
{
    .text : { *(.text*) } > ram
    .rodata : { *(.rodata*) } > ram
    .bss : { *(.bss*) } > ram
}

build

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 

In theory you can use riscv32-whatever-whatever (riscv32-unknown-elf, etc). As this code is generic enough. Also note I am using the minimal rv32i, you can probably use rv32imac.

Check the disassembly:

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>

Being rv32i it is all 32 bit instructions and that is fine. This program is intended to be loaded into ram and run there with a debugger, I use openocd and telnet in.

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger

Then

halt
load_image notmain.elf
resume 0x80000000

in the telnet window.

Then you can halt again.

80000024:   00040513            mv  x10,x8
80000028:   fe9ff0ef            jal x1,80000010 <dummy>
8000002c:   00140413            addi    x8,x8,1
80000030:   ff5ff06f            j   80000024 <notmain+0x10>

You can examine either x8 or x10 to see that it counted:

resume
halt

and examine the registers again they should have incremented. First program running, moving on.

Second program use this linker script instead:

memmap

MEMORY
{
    rom : ORIGIN = 0x20010000, LENGTH = 0x4000
    ram : ORIGIN = 0x80000000, LENGTH = 0x4000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}

examine disassembly.

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>

It appears to be position independent so it should have just worked as was with the other linker script but best to use the correct addresses.

My notes say:

flash protect 0 64 last off
program notmain.elf verify
resume 0x20010000

And now you should be able to reset or power cycle the board, connect with openocd in a way that doesn't reset (or does if you wish) and then you don't need to load anything it should have run their bootloader that then launched your bootloader at that address (jumped to it as they mention). Examine r8 or r10 (r10 for this abi is the first parameter passed, so even if your gcc builds using something other than r8, r10 should still reflect the counter) resume, halt, reg, resume, halt, reg ...

Before overwriting their bootloader at 0x20000000 I would dump it and make sure you have a good copy of it, and or perhaps they have a copy on their website. Then you can change the linker script to 0x20000000. Before I would do that personally I would disassemble and examine their bootloader and find out what if anything it is doing, is it worth keeping, etc. Their text says "modifiable"

I cut my risc-v teeth on the hifive1 board, but have moved on to sim open source cores, the hifive boards are pretty expensive. I also made a minimal pcb and put down some sifive parts, was going to only run out of ram, etc, but my board was too minimal and I didn't go back and try again, little support on their forums for pcb work and their docs left something to be desired. I assume when the rev b boards are generally available Ill pick one up. will see. good luck.

The point being there are a number of cores out there that you can sim with verilator or other and see everything going on, and you can't brick nor let smoke out because it is a sim.

Note 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 

and you can see it uses the compressed instructions where it can

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>

Also it is pretty easy to write your own emulator. Depends on how you want to stage learning this platform. How much is mastering the instruction set vs the toolchain vs a specific chip and its peripherals.

You definitely want the risc-v documents from riscv.org that match the version supported by the core, lots of internal core registers and stuff plus the instruction set(s). As well as the getting started and the chip doc for the chip in question, if you want to do your own thing. If you want to play in one of their sandboxes and use some third party libraries, then you need to learn their sandbox and play in their sandbox rather than do your own thing. Looks like you are wanting to do your own thing.

Note I am using the current version of gcc/binutils from gnu mainline sources, hand built.

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'.

The above code worked fine years ago against the original hifive1 and this style tends to work for major revs of gnu and I have used this toolchain against other riscv cores, so even if yours is older it should still work. The most important thing is matching the arch (-march) to the instruction sets supported by the core, or at least a subset rv32i should be supported by all cores, compressed and multiply and such are not always supported.

My openocd config file for the first board

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 in one terminal/window then telnet localhost 4444 in another.

Now as far as the gnu assembler nuances you are asking about see the gnu assembler, or even better use as little assembler/toolchain specific stuff as you can as it may change and/or you may change tools some day. YMMV

The gnu tools don't know this board from a hole in the wall. you tell the gnu tools about the processor core architecture and in the linker script the memory map. Your code, directly or indirectly (if you use someone elses bootstrap and linker script) must match the boot properties of the processor core be it a risc-v from sifive or some arm core or mips or x86, etc.

Vector table or not, execute at some address, etc. In the above case their bootloader jumps to 0x20010000 so you need to put the first instruction at 0x20010000 which is done by having that instruction be the first one in the bootstrap source, and if not specified in the linker script by having that object first on the ld command line, and by examining the disassembly to confirm it worked before ever attempting to run it on the hardware.

The riscv cores I have used don't have a vector table, for reset they simply start execution at some address. So you would use the same approach if you didn't have a pre-bootloader jump to you. For other architectures not risc-v the construction of the program for the board/platform would vary if it is a jump to an address thing vs a vector table thing.

Now saying that, if you are using their sandbox then this is a sandbox question not a gnu toolchain question.

It is in their documentation the board documentation and/or website indicates that the rev b board uses the FE310-G002 chip in the FE310-G002 documentation you find the memory map. It also indicates this is a risc-v architecture and from that you go to the riscv.org foundation and get the documentation for that architecture which tells you how it boots. And back in the FE310-G002 it tells you the boot process from the MSEL pins. Which you would need to examine the schematics. So the reality is their documentation does tell you how to indicate that this is a bootloader program, by providing the information you need to give to gnu.

Saying that...some experimenting is desired/required. It is possible/easy to write a simple position infinite loop, build for 0x00000000 but load at 0x20010000 based on their documentation and come in with openocd to examine the program counter to see if it really is 0x20010000 based. From that you can assume that ultimately as shipped the board works its way through their bootloader into yours through whatever MSEL selection.

Hmmm:

On power-on, the core’s reset vector is 0x1004 .

And it goes further to indicate the different first instruction addresses for each of the MSEL strap options. So if you were to take over their bootloader and replace it with your own based on the documentation you would link for 0x20000000 and have the entry point there.

Edit

Just got my rev b board.

You can look at the getting started guide to see how to specify the board using their sandbox. But that is not required, if you have a (gnu) toolchain that supports rv32i or more than that rv32imac you can build programs with no other outside dependencies.

The toolchain itself doesn't know one board from another, one chip from another.

The sifive documentation says:

The HiFive1 Rev B Board is shipped with a modifiable boot loader at the begnning of SPI Flash (0x20000000). At the end of this program’s execution the core jumps to the main user portion of code at 0x20010000.

And that is the critical information we need, plus the address space for memory in the memory map for the part 0x80000000 0x4000 bytes of sram.

novectors.s

.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);
}

memmap

MEMORY
{
    rom : ORIGIN = 0x20010000, LENGTH = 0x1000
    ram : ORIGIN = 0x80000000, LENGTH = 0x4000
}
SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}

build

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 

Now in theory you can use the riscv64-unknown-elf they talk about even though they want to build for rv32 not rv64. I can try that too.

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>

Important to check before you try to load the program onto the device, our desired entry code, first instructions of novectors.s need to be at 0x20010000 for this board/chip as shipped (factory bootloader). And it is.

notmain.hex

:020000042001D9
:1000000037410080EF00000101A082800CC1828096
:100010000841828041114AC0372901101305890027
:10002000B705680006C626C222C4CD371305C9002D
:10003000B7056800E13F130509048145B7841E0038
:10004000F137310993840448B70568004A857D3F3C
:10005000014422850504553FE31D94FE81454A85F0
:1000600075370144228505044D37E31D94FEE9BF31
:0400000520010000D6
:00000001FF

Copy notmain.hex to the mounted HiFive media. Now this cost me an hour or two trying to figure out the hex file as I started, here, it didn't work. Downloaded their sdk dug through that found an elf2hex but that was a bad tangent that was for fpga work it appears. Figured it out and all they are doing is riscv...objcopy -O ihex just like I have, tried it one more time. And now it works. I was getting a fail.txt saying it couldn't connect to the cpu before. Don't know what I did or didn't do to make this work.

In theory you can cut and paste the hex file above and save it and copy it. Why does nobody have an example hex file, you gotta have the 75 special things installed right and run a build rather than also provide here is a complete example with intermediate files. I certainly will do this in my examples for this platform. Or at least the one above.

Instead of their rainbow led blinking pattern, the above will make it blink "white" on and off at a regular rate.

Note LEDs are on the same GPIO lines on the rev a board, the bootloader lands on a different address 0x20400000 than the rev b 0x20010000. So the same can be built for the rev a board with that one memmap change.

If you or the reader wants to go back to a rev a if they have one it is a modified openocd, which at the time of this writing is at github riscv user riscv-openocd project. the normal ./bootstrap, ./configure, make to get the tools and in the tcl dir there is the riscv openocd config file shown above

interface ftdi
ftdi_device_desc "Dual RS232-HS"
ftdi_vid_pid 0x0403 0x6010

was the key, the rev2 board lsusb:

Bus 001 Device 018: ID 1366:1051 SEGGER 

and no hits on those pid/vid values in the openocd config files. Leading to reading more of the getting started manual.

这篇关于在RISC-V中创建启动程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆