簡體   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