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

Pla*_*ove 3 assembly cpu-architecture elf riscv riscv32

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

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

我正在尝试仅用一条指令来汇编一个程序:

# simple.asm
add x5,x6,x7
Run Code Online (Sandbox Code Playgroud)

我使用汇编器编译它,然后使用以下命令运行 objdump:

# simple.asm
add x5,x6,x7
Run Code Online (Sandbox Code Playgroud)

这会打印出以下内容:

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
Run Code Online (Sandbox Code Playgroud)

我的问题是:

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

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

Pla*_*ove 7

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

\n

步骤如下:

\n
    \n
  1. 修改了程序集文件,使其具有.textand.data部分和_start标签。我的simple.asm文件现在如下所示:

    \n
    .globl _start\n\n.text\n_start:\n  add x5,x6,x7\n\n.data\nL1: .word 27\n
    Run Code Online (Sandbox Code Playgroud)\n
  2. \n
  3. 使用以下命令将其组装.asm到文件中:.o

    \n
    riscv32-unknown-elf-as simple.asm -o simple.o\n
    Run Code Online (Sandbox Code Playgroud)\n
  4. \n
  5. 为特定处理器创建链接描述文件。我观看了这个精彩的视频,该视频演示了从头开始创建链接器脚本的过程。现在,我只需要.text.data部分。所以我的链接器脚本(mycpu.ld)如下所示:

    \n
    OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv")\nENTRY(_start)\n\nMEMORY\n{\n  DATA (rwx) : ORIGIN = 0x0, LENGTH = 0x80\n  INST (rx) : ORIGIN = 0x80, LENGTH = 0x80\n}\n\nSECTIONS\n{\n  .data :\n  {\n    *(.data)\n  }> DATA\n\n  .text :\n  {\n    *(.text)\n  }> INST\n}\n\n
    Run Code Online (Sandbox Code Playgroud)\n
  6. \n
  7. 生成 ELF 文件,使用riscv32-unknown-elf-gcc该文件自动调用riscv32-unknown-elf-ld

    \n
    riscv32-unknown-elf-gcc -nostdlib -T mycpu.ld -o simple.elf simple.o\n
    Run Code Online (Sandbox Code Playgroud)\n
  8. \n
  9. 从该文件创建一个原始二进制或十六进制文件.elf,我将用它来填充内存的内容。

    \n
    riscv32-unknown-elf-objcopy -O binary simple.elf simple.hex\n
    Run Code Online (Sandbox Code Playgroud)\n
  10. \n
\n

Finalsimple.hex包含以下内容(使用hexyl):

\n
\xe2\x94\x8c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x90\n\xe2\x94\x8200000000\xe2\x94\x82 1b 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x82\xe2\x80\xa20000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000010\xe2\x94\x82 00 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x8200000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000020\xe2\x94\x82 00 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x8200000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000030\xe2\x94\x82 00 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x8200000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000040\xe2\x94\x82 00 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x8200000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000050\xe2\x94\x82 00 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x8200000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000060\xe2\x94\x82 00 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x8200000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000070\xe2\x94\x82 00 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x8200000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000080\xe2\x94\x82 b3 02 73 00             \xe2\x94\x8a                         \xe2\x94\x82\xc3\x97\xe2\x80\xa2s0    \xe2\x94\x8a        \xe2\x94\x82\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x98\n
Run Code Online (Sandbox Code Playgroud)\n

其中b3027300是 的十六进制值add x5,x6,x7

\n

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

\n


Pet*_*des 5

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

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

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

objdump -D反汇编了所有 ELF 部分,而不仅仅是 .text。正如您所看到的,.text 部分中只有一条指令,如果您使用 objdump -d 这就是您所看到的。(我通常使用objdump -drwC,尽管-w没有换行可能与 RISC-V 无关,这与 x86 不同,其中单个 insn 可能很长。)

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

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

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

.oa和可执行文件之间的区别:a.o只是具有重定位元数据,供链接器填充实际地址,绝对地址用于la伪指令,或者相对于多个文件(其中一个文件引用另一个文件的符号)的auipc情况。.o(否则,可以在汇编时计算相对位移,而不是留给链接时计算。)

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

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

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