繁体   English   中英

裸机 RISC-V CPU - 处理器如何知道从哪个地址开始获取指令?

[英]Bare metal RISC-V CPU - how does the processor know which address to start fetching instructions from?

我正在设计自己的 RISC-V CPU,并且已经能够实现一些指令代码。

我已经安装了 RV32I 版本的 GCC 编译器,所以我现在可以使用汇编程序riscv32-unknown-elf-as

我试图用一条指令组装一个程序:

# simple.asm
add x5,x6,x7

我用汇编器编译它,然后用这个命令运行 objdump:

riscv32-unknown-elf-as simple.asm -o simple
riscv32-unknown-elf-objdump -D simple

这将打印出以下内容:

new:     file format elf32-littleriscv


Disassembly of section .text:

00000000 <.text>:
   0:   007302b3                add     t0,t1,t2

Disassembly of section .riscv.attributes:

00000000 <.riscv.attributes>:
   0:   2d41                    jal     0x690
   2:   0000                    unimp
   4:   7200                    flw     fs0,32(a2)
   6:   7369                    lui     t1,0xffffa
   8:   01007663                bgeu    zero,a6,0x14
   c:   00000023                sb      zero,0(zero) # 0x0
  10:   7205                    lui     tp,0xfffe1
  12:   3376                    fld     ft6,376(sp)
  14:   6932                    flw     fs2,12(sp)
  16:   7032                    flw     ft0,44(sp)
  18:   5f30                    lw      a2,120(a4)
  1a:   326d                    jal     0xfffff9c4
  1c:   3070                    fld     fa2,224(s0)
  1e:   615f 7032 5f30          0x5f307032615f
  24:   3266                    fld     ft4,120(sp)
  26:   3070                    fld     fa2,224(s0)
  28:   645f 7032 0030          0x307032645f

我的问题是:

  1. 这里发生了什么? 我以为我会有一个简单的单行十六进制,但还有很多事情要做。
  2. 如何指示我的处理器开始读取某个内存地址处的指令? 看起来 objdump 也不知道指令从哪里开始。

明确地说,此时我将我的处理器视为裸机。 我想象我将在处理器中硬编码指令从内存地址 X 开始,数据在内存地址 Y 处可用,堆栈在内存地址 Z 处可用。这是正确的吗? 或者这是错误的方法?

处理器如何知道从哪个地址开始获取指令?

实际的 CPU 本身会有一些硬连线地址,它可以在复位/上电时从中获取。 通常,系统会在该物理地址处设计有 ROM 或闪存。 (并且可能有一个 ELF 程序加载器的代码,它将尊重 ELF 入口点元数据,或者您可以在二进制文件的开头将一个平面二进制文件与正确的代码链接起来。)

这里发生了什么? 我以为我会有一个简单的单行十六进制,但还有很多事情要做。

您的objdump -D反汇编所有 ELF 部分,而不仅仅是 .text。 如您所见,.text 部分中只有一条指令,如果您使用了 objdump -d,您会看到这样的内容。 (我通常使用objdump -drwC ,尽管-w no line-wrapping 可能与 RISC-V 无关,不像 x86,其中单个 insn 可能很长。)

是否可以将我上面编译的文件按原样传递给我的处理器?

不是你想的那样。 另请注意,您为输出选择了错误的文件名。 as 生成目标文件(通常为 .o),而不是可执行文件。 您可以将ld链接到一个平面二进制文件中,或者将objcopy.text部分链接和objcopy

(理论上,您可以将整个 ELF 可执行文件甚至目标文件放入 ROM 中,这样.text部分恰好从 CPU 获取的位置开始,但不会查看元数据字节。因此,ELF 入口点地址元数据在ELF 可执行文件将无关紧要。)

.o和可执行文件之间的区别:a .o只有重定位元数据供链接器填充实际地址,绝对用于la伪指令,或相对用于auipc的情况,例如多个.o文件,其中一个文件从其他。 (否则可以在组装时计算相对位移,而不是在链接时计算。)

因此,如果您的代码使用任何内存地址标签,则需要链接器在代码中填写这些重定位条目。 然后你可以从链接的 ELF 可执行文件中objcopy一些部分。 或者使用链接器脚本来设置平面二进制文件的布局。

对于只有add ,没有la或任何内容的简单情况,没有重定位条目,因此.o的文本部分与链接的可执行文件中的相同。

使用objcopy也很棘手的是静态数据,例如.data.bss部分。 如果您.text部分复制到平面二进制文件,则在任何地方都不会有数据。 (但在 ROM 中,您需要一个启动函数,将.data静态初始值设定项从 ROM 复制到 RAM,并将.bss空间归零。如果您想编写 asm 源以具有正常外观的.data部分非零值,您希望您的构建脚本计算出要复制的大小,以便您的启动函数可以使用它,而不必手动执行所有这些操作。)

@PeterCordes 的回答让我走上了正确的道路。 我终于想出了如何生成我可以使用的原始内存转储文件。

步骤如下:

  1. 修改了程序集文件以具有.text.data部分以及_start标签。 我的simple.asm文件现在如下所示:

     .globl _start .text _start: add x5,x6,x7 .data L1: .word 27
  2. 使用以下命令将.asm组装到.o文件:

     riscv32-unknown-elf-as simple.asm -o simple.o
  3. 为特定处理器创建链接描述文件。 我关注了这个惊人的视频,该视频介绍了从头开始创建链接器脚本的过程。 现在,我只需要.text.data部分。 所以我的链接器脚本( mycpu.ld )如下所示:

     OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv") ENTRY(_start) MEMORY { DATA (rwx) : ORIGIN = 0x0, LENGTH = 0x80 INST (rx) : ORIGIN = 0x80, LENGTH = 0x80 } SECTIONS { .data : { *(.data) }> DATA .text : { *(.text) }> INST }
  4. 使用riscv32-unknown-elf-gcc生成 ELF 文件,它会自动调用riscv32-unknown-elf-ld

     riscv32-unknown-elf-gcc -nostdlib -T mycpu.ld -o simple.elf simple.o
  5. .elf文件创建一个原始二进制或十六进制文件,我将用它来填充内存的内容。

     riscv32-unknown-elf-objcopy -O binary simple.elf simple.hex

最终的simple.hex包含以下内容(使用hexyl ):

┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 1b 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │•0000000┊00000000│
│00000010│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000020│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000030│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000040│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000050│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000060│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000070│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000080│ b3 02 73 00             ┊                         │וs0    ┊        │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘

其中b3027300add x5,x6,x7的十六进制值。

就是这样! 非常感谢@PeterCordes 的帮助! :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM