Ano ang kailangan mo upang simulan ang programming sa RISC-V assembler

Huling pag-update: 08/10/2025
May-akda: Isaac
  • Kasama ang RV32I: mga rehistro, ABI, at kontrol sa daloy na may ecall.
  • Magsanay sa Jupiter at mga ehersisyo: negatibo, mga kadahilanan, chain, recursion.
  • Master cross toolchain, script pag-link at pag-debug gamit ang objdump.

RISC-V Assembler: Mga Kinakailangan at Pagsisimula

Kung gusto mong malaman ang tungkol sa assembler at sa tingin mo na ang RISC-V ay ang paraan upang pumunta, napunta ka sa tamang lugar. Ang pagsisimula sa ASM sa RISC-V ay mas abot-kaya kaysa sa tila Kung naiintindihan mo ang mga tool, ang modelo ng programming at ilang mahahalagang tuntunin ng arkitektura.

Sa mga sumusunod na linya, pinagsama ko ang pinakamahusay sa ilang mga mapagkukunan: mga kasanayan sa mga simulator ng uri ng Jupiter, convention ng RV32I base repertoire, mga halimbawa ng loop at recursion, system call, at kahit isang pagtingin sa RISC-V na disenyo ng CPU sa VHDL (na may ALU, memory control, at state machine), kasama ang isang pagsusuri ng cross-toolchain at linking script.

Ano ang RISC-V assembler at paano ito naiiba sa wika ng makina?

Bagama't kapwa nakadikit sa hardware, Ang wika ng makina ay purong binary (mga isa at mga zero) na direktang binibigyang kahulugan ng CPU, habang ang assembler ay gumagamit ng mnemonics at mga simbolo mas nababasa kaysa sa isang assembler pagkatapos ay isinasalin sa binary.

Tinutukoy ng RISC-V ang isang bukas na ISA na may napakalinis na base repertoire. Ang RV32I (32-bit) na profile ay may kasamang 39 na tagubilin ng user na may kahanga-hangang orthogonality, na naghihiwalay sa pag-access ng memorya mula sa purong computation, at may mahusay na suporta sa GCC/LLVM.

Mga rekord, kasunduan at entry point

Sa RV32I mayroon ka 32 pangkalahatang layunin na rehistro (x0–x31) 32-bit; Ang x0 ay palaging binabasa bilang 0 at hindi maaaring isulat sa. Ang mga alias gaya ng a0–a7 (mga argumento), t0–t6 (pansamantala), o s0–s11 ​​​​(naka-save) ay kapaki-pakinabang din para sa pagsunod sa ABI.

Depende sa kapaligiran o simulator, maaaring magsimula ang programa sa isang partikular na label. Sa Jupiter, nagsisimula ang mga programa sa pandaigdigang tag na __start., na dapat mong ideklara bilang nakikita (halimbawa, gamit ang .globl) upang markahan ang entry point.

ang nagtatapos ang mga tag sa colon, maaari ka lamang maglagay ng isang pagtuturo sa bawat linya at ang mga komento ay maaaring magsimula sa # o ; kaya hindi sila pinapansin ng assembler.

Mga Tool at Simulator: Jupiter at Basic Workflow

Upang magsanay nang walang komplikasyon, mayroon kang Jupiter simulator/assembler, isang graphical na tool na inspirasyon ng SPIM/MARS/VENUS na nagpapadali sa pag-edit, pagpupulong at pagpapatupad sa isang kapaligiran.

Sa Jupiter maaari kang lumikha, mag-edit at magtanggal ng mga file sa tab na Editor. Pagkatapos mag-save, mag-assemble gamit ang F3 at tumakbo upang i-debug ang daloy ng pagtuturo sa pamamagitan ng pagtuturo, gamit ang rehistro at memory view upang maunawaan ang estado ng makina.

Ang mga programa ay dapat magtapos sa isang tawag sa kapaligiran: exit call setting a0 na may code 10 (lumabas). Sa RISC-V, ang mga ecall ay katumbas ng mga system call o mga bitag sa kapaligiran/system.

Minimum na istraktura ng isang programa at mga tawag sa system

Ang karaniwang istraktura sa mga halimbawang pang-akademiko ay tumutukoy sa isang panimulang punto, ginagawa ang gawain, at nagtatapos sa isang ecall. Ang mga argumento ng ecall ay karaniwang naglalakbay sa a0–a2 at ang tagapili ng serbisyo sa a7, depende sa kapaligiran.

Sa isang Linux RISC-V, halimbawa, maaari kang mag-print gamit ang write syscall at lumabas gamit ang naaangkop na code. Para sa pagsulat ng a0 (fd), a1 (buffer), a2 (haba) at a7 ay ginagamit kasama ang numero ng serbisyo. Sa wakas, ang a0 ay nakatakda sa return code at a7 sa exit number.

# Ejemplo mínimo (Linux RISC-V) para escribir y salir
.global _start
_start:
  addi a0, x0, 1        # fd = 1 (stdout)
  la   a1, msg          # a1 = &msg
  addi a2, x0, 12       # a2 = longitud
  addi a7, x0, 64       # write
  ecall

  addi a0, x0, 0        # return code
  addi a7, x0, 93       # exit
  ecall

.data
msg: .ascii "Hola mundo\n"

Kung nagtatrabaho ka sa labas ng Linux, tulad ng sa mga simulator na pang-edukasyon na may sariling serbisyo ng IoT, baguhin ang numero ng ecall at magrehistro ayon sa dokumentasyon ng kapaligiran.

  Kunin ang Windows 8.1 ISO Files [USB and DVD Setup]

Mga paunang pagsasanay upang matulungan kang maging komportable sa mga kondisyon, loop, at memorya

Ang karaniwang warm-up ay ang pagtukoy kung negatibo ang isang integer. Maaari mong ibalik ang 0 kung ito ay positibo at 1 kung ito ay negatibo.; sa RV32I, isang paghahambing sa 0 at isang set-on-less-than ay nireresolba ang isyu sa isang pinag-isipang pagtuturo.

Ang isa pang napaka-kapaki-pakinabang na ehersisyo ay ang paglista ng mga salik ng isang numero: bumabagtas mula 1 hanggang n, ini-print ang mga divisors, at ibinabalik kung ilan ang mayroonMagsasanay ka ng mga conditional branch, dibisyon (o paulit-ulit na pagbabawas), at mga loop na may karagdagan at paghahambing.

Ang pagtatrabaho sa mga string ay pinipilit kang pamahalaan ang memorya: Bisitahin ang bawat character ng isang string sa memorya at in-place na i-convert ang lowercase sa uppercase kung magkasya ang mga ito sa saklaw ng ASCII. Sa pagkumpleto, ibinabalik nito ang orihinal na address ng string.

Mga loop, function at recursion: factorial, Fibonacci at Tower of Hanoi

Kapag nagdidisenyo ng mga loop, isipin ang tatlong bloke: kundisyon, katawan, at hakbang. Sa beq/bne/bge at unconditional jumps jal/j while/for are built walang misteryo, umaasa sa addi at paghahambing.

.text
.globl __start
__start:
  li t0, 0        # i
  li t1, 10       # max
cond:
  bge t0, t1, end # si i >= max, salta
  # cuerpo: usar a0/a1 segun servicio IO del entorno
  addi t0, t0, 1  # i++
  j cond
end:
  li a0, 10
  ecall

Sa mga function call, igalang ang ABI: i-save kung magkakaroon ka ng mas maraming tawag, pinapanatili ang s0–s11 ​​kung babaguhin mo ang mga ito, at ginagamit ang stack na may sp na gumagalaw sa multiple ng isang salita.

Ang Factorial ay ang klasikong recursion: base case n==0 ay nagbabalik ng 1; kung hindi, tawagan ang factorial(n-1) at i-multiply sa n. Protektahan ang ra at mga rehistro na naka-save sa stack bago ang tawag at ibalik ang mga ito sa pagbabalik.

factorial:
  beq a0, x0, base
  addi sp, sp, -8
  sw   ra, 4(sp)
  sw   s0, 0(sp)
  mv   s0, a0
  addi a0, a0, -1
  jal  factorial
  mul  a0, a0, s0
  lw   s0, 0(sp)
  lw   ra, 4(sp)
  addi sp, sp, 8
  jr   ra
base:
  li a0, 1
  jr ra

Ang Fibonacci ay kapaki-pakinabang para sa pagsasanay pareho recursion na may dalawang tawag bilang isang mahusay na umuulit na bersyon na may mga variable ng accumulator. At kung gusto mo ng hamon sa pagkontrol ng daloy at parameter, magsalin ng solusyon sa assembler Mga tore ng Hanoi na may apat na argumento: mga disk, pinagmulan, patutunguhan, at pantulong na tore; nirerespeto nito ang order ng tawag at ipinapakita ang bawat galaw.

Access sa memorya, mga array, at pagmamanipula ng string

Sa RISC-V, ang pag-access sa memorya ay ginagawa gamit ang pag-load/store: lw/sw para sa mga salita, lh/sh para sa kalahating salita, at lb/sb para sa byte, na may pinirmahan o hindi nalagdaan na mga variant sa mga pagsingil (lb vs lbu, lh vs lhu).

  Project Zomboid sa Android: Maaari mo ba itong i-play sa mobile?

Upang lampasan ang mga integer array, gumamit ng 4-byte offset bawat index; para sa mga string ng teksto, umuusad ng byte byte hanggang sa matagpuan nito ang terminator kung kinakailangan ito ng kumbensyon (hal., \0). Tandaan na i-save ang mga base address at pangasiwaan ang mga pointer na may addi/auipc/la kung naaangkop.

Pagdidisenyo ng isang RV32I CPU mula sa Scratch: Isang Pangkalahatang-ideya ng Mataas na Antas

Kung gusto mong pumunta sa silicon, isang proyektong pang-edukasyon ay bubuo ng isang RV32I CPU sa VHDL, synthesizable sa FPGA Low-mid range. May kasamang program ROM, data RAM, at isang simpleng GPIO para sa pag-iilaw ng LED.

Ang kernel ay nagpapatupad ng base repertoire (walang M/A/C extension o CSRs), gumagamit ng 32-bit address bus at nagbibigay-daan sa 8-/16-/32-bit sign-extended memory access kung saan naaangkop. Malinaw na pinaghihiwalay ng disenyo ang mga register, ALU, memory controller, at state machine.

ALU, mga shift, at ang ideya ng "naantala ang pag-load"

Ang ALU ay inilarawan sa kumbinasyon ng mga operasyon tulad ng karagdagan, pagbabawas, XOR, O, AT, mga paghahambing (nalagdaan at hindi nilagdaan) at lohikal/aritmetikong pagbabago.

Upang i-save ang mga LUT sa FPGA, ipinatupad ang mga multi-bit na shift inuulit ang mga 1-bit na shift na kinokontrol ng makina ng estado: gumagamit ka ng ilang mga cycle, ngunit binabawasan mo ang mga lohikal na mapagkukunan.

Sa mga kasabay na circuit, ang mga pagbabago ay sinusunod sa mga gilid ng orasan. Ang konsepto ng "naantala na pagkarga" ay nagpapaalala na ang pinipili ng isang multiplexer ay nakakaapekto sa rehistro sa susunod na cycle, isang pangunahing aspeto kapag nagdidisenyo ng fetch-decode-execute state machine.

Memory controller at mapa: ROM, RAM at GPIO

Ang isang bloke ng memorya ay nagsasama ng ROM at RAM sa isang magkadikit na espasyo, pinapasimple ang interface ng processorAng controller ay tumatanggap ng AddressIn (32 bits), DataIn, ang lapad (byte/half/word), ang sign extension signal, WE (read/write), at Start to initiate transactions.

Nang matapos ang operasyon, Nakatakda ang ReadyOut sa 1 at, kung nabasa na ito, DataOut ay naglalaman ng data (sign-extended kapag hiniling). Kung ito ay nakasulat, ang data ay nananatili sa RAM.

Isang halimbawa ng praktikal na mapa: ROM mula 0x0000 hanggang 0x0FFF, isang GPIO byte sa 0x1000 (bit 0 hanggang isang pin) at RAM mula 0x1001 hanggang 0x1FFFSa pamamagitan nito maaari kang gumawa ng isang blinker sa pamamagitan ng pagsulat at pag-togg sa output bit.

Mga register, multiplexer at state machine

Tinutukoy ng CPU ang 32 pangkalahatang layunin na mga rehistro na na-instantiated na may mga array sa VHDL, na may a decoder upang piliin ang patutunguhan ng pagsusulat mula sa ALU at panatilihin ang natitira.

Pinamamahalaan ng mga multiplexer ang mga input ng ALU (operand at operasyon), ang mga signal sa memory controller (mga lapad, address, simulang kontrolin at basahin/sulat) at ang mga espesyal na rehistro: PC, IR at isang pantulong na counter para sa mga umuulit na pagbabago.

Ang makina ng estado ay nagsisimula sa i-reset ang, kinukuha ang pagtuturo na itinuro ng PC (4-byte reading), nilo-load ito sa IR kapag handa na at pumasa sa mga execution node: ALU (isang pagtuturo sa 1 cycle maliban sa mga shift), load/store, branch at jumps, bilang karagdagan sa mga espesyal na tagubilin tulad ng ebreak.

  Paano Mag-set Up ng Bluetooth sa Windows 10

Cross-toolchain, pag-link, at pag-debug

Upang makabuo ng RV32I binaries, gumamit ng a Cross GCC (target riscv32-none-elf). Mag-compile ka ng C/C++/ASM source, mag-link sa isang script na tumutukoy sa memory map, at i-convert ang output sa form na inaasahan ng iyong ROM/FPGA.

Ang isang simpleng hook script ay maaaring ilagay .text sa ROM mula sa 0x0000 at .data sa RAM mula sa 0x1004 (kung ang 0x1000–0x1003 ay inookupahan ng mga rehistro ng GPIO). Ang startup routine ay maaaring "hubad" at ilagay ang stack pointer sa dulo ng RAM (hal. 0x1FFC) bago tumawag sa main.

/* Mapa simple
 * ROM: 0x00000000 - 0x00000FFF
 * GPIO: 0x00001000 - 0x00001003
 * RAM: 0x00001004 - 0x00001FFF
 */
SECTIONS {
  . = 0x00000000;
  .text : { *(.startup) *(.text) *(.text.*) *(.rodata*) }
  . = 0x00001004;
  .data : { *(.data) *(.data.*) }
}

Sa riscv32-none-elf-objdump magagawa mo i-disassemble ang ELF at suriin ang mga address; halimbawa, makikita mo ang boot sa 0x00000000 na may mga tagubilin tulad ng lui/addi/jal at ang paglipat sa iyong pangunahing. Para sa simulation ng VHDL, ang GHDL ay bumubuo ng mga bakas na maaari mong buksan gamit ang GtkWave.

Pagkatapos ma-verify sa simulation, dalhin ang disenyo sa isang FPGA (Quartus o iba pang toolchain). Kung ang RAM ay hinuhulaan bilang panloob na mga bloke at ang code ay malinaw na RTL, dapat kang mag-synthesize nang walang mga sorpresa, kahit na sa mga beteranong device.

Mga praktikal na paalala at karaniwang pagkakamali kapag nagsisimula

Huwag kalimutan iyan Ang x0 ay palaging zero; walang epekto ang pagsulat dito, at ang pagbabasa nito ay nagbabalik ng 0. Gamitin ito sa iyong kalamangan sa mga karagdagan, paghahambing, at paglilinis ng registry.

Kapag nagpatupad ka ng mga feature, i-save ang ra at sN records na iyong binago, at pinamamahalaan ang stack na may mga dagdag/pagbabawas na nakahanay sa salita sa sp. Sa pagbabalik, ito ay nagbabalik sa reverse order at tumalon kasama si jr ra.

Sa mga simulator tulad ng Jupiter, suriin iyon Ang __start ay pandaigdigan at tinatapos mo ito sa ecall tama (a0=10 upang lumabas). Kung may hindi nagsimula, suriin ang label, ang globality at recompile (F3).

Sa mga pagsasanay sa IO, igalang ang protocol ng kapaligiran: na nagrerehistro ay may mga parameter, numero ng serbisyo, at kung inaasahan ang isang address o agarang halaga. Gamitin ang simulator o dokumentasyon ng operating system.

Sa isang malinaw na base ng ISA (RV32I, mga rehistro at ABI), isang komportableng simulator tulad ng Jupiter, at dumaraming mga halimbawa (negatibo, mga kadahilanan, uppercase, mga loop, factorial, Fibonacci at Hanoi), ang RISC-V assembler ay huminto sa pagiging isang pader at nagiging isang makatas na lupain upang maunawaan kung paano iniisip ng isang CPU. At kung maglakas-loob kang bumaba sa VHDL, makikita mo kung paano magkasya ang ALU, memorya at kontrol.: mula sa pagkuha ng pagtuturo at tamad na pag-load hanggang sa mga interface ng memorya at isang mapa na may ROM, RAM, at GPIO na hinahayaan kang mag-blink ng LED gamit ang sarili mong processor.

Ang Pinakamahusay na Programa sa Programa
Kaugnay na artikulo:
Ang 7 Pinakamahusay na Programa sa Programa