Què necessites per començar a programar a assemblador RISC-V

Darrera actualització: 08/10/2025
Autor: Isaac
  • Comprèn RV32I: registres, ABI i control de flux amb ecall.
  • Practica amb Jupiter i exercicis: negatiu, factors, cadenes, recursió.
  • Domina toolchain creuada, script denllaç i depuració amb objdump.

Assemblador RISC-V: requisits i primers passos

Si et pica la curiositat per l'assemblador i sents que RISC-V és el camí a seguir, ets al lloc adequat. Arrencar amb ASM a RISC-V és més assequible del que sembla si entens les eines, el model de programació i algunes regles clau de l'arquitectura.

En les línies que segueixen he unit el millor de diverses fonts: pràctiques amb simuladors tipus Júpiter, convencions del repertori base RV32I , exemples de bucles i recursió, trucades al sistema i fins i tot una mirada al disseny d'una CPU RISC-V a VHDL (amb ALU, control de memòria i màquina d'estats), a més d'un repàs de la to.

Què és l'assemblador RISC-V i com es diferencia del llenguatge màquina

Tot i que tots dos estan enganxats al maquinari , el llenguatge màquina és binari pur (uns i zeros) que la CPU interpreta directament, mentre que l'assemblador utilitza mnemònics i símbols més llegibles que després un assemblador tradueix a binari.

RISC-V defineix una ISA oberta amb un repertori base molt net. El perfil RV32I (32 bits) inclou 39 instruccions d'usuari amb una ortogonalitat notable, separant accés a memòria de còmput pur, i amb suport excel·lent a GCC/LLVM.

Registres, convenis i punt dentrada

A RV32I tens 32 registres de propòsit general (x0–x31) de 32 bits; x0 sempre llegeix com a 0 i no es pot escriure. A més, s'utilitzen àlies com a0–a7 (arguments), t0–t6 (temporals) o s0–s11 ​​(salvats), útils per seguir l'ABI.

Segons l'entorn o el simulador, el programa pot començar en una etiqueta concreta. A Jupiter els programes arrenquen a l'etiqueta global __start , que has de declarar com a visible (per exemple, amb .globl) per marcar el punt d'entrada.

Les etiquetes acaben en dos punts , només podeu posar una instrucció per línia i els comentaris es poden iniciar amb # o ; perquè l'assemblador els ignori.

Eines i simuladors: Júpiter i flux de treball bàsic

Per practicar sense complicacions, tens el simulador/assemblador Jupiter , una eina gràfica inspirada en SPIM/MARS/VENUS que facilita edició, acoblament i execució en un sol entorn.

A Jupiter pots crear, editar i esborrar fitxers a la pestanya Editor. Després de desar, assembleu amb F3 i executeu per depurar el flux instrucció a instrucció, usant les vistes de registres i memòria per entendre l'estat de la màquina.

Els programes han de finalitzar amb una trucada a l'entorn: ecall de sortida configurant a0 amb el codi 10 (exit). A RISC-V les ecall són equivalents a system calls o trampes a l'entorn/sistema.

Estructura mínima d'un programa i trucades al sistema

L'estructura típica en exemples acadèmics defineix un punt d'inici, fa la feina i acaba amb ecall. Els arguments d'ecall solen viatjar a a0–a2 i el selector de servei a a7, depenent de l'entorn.

En un Linux RISC-V, per exemple, podeu imprimir amb la syscall d'escriptura i sortir amb el codi adequat. Per a write s'utilitzen a0 (fd), a1 (buffer), a2 (longitud) i a7 amb el número de servei . Per acabar, es prepara a0 amb el codi de retorn i a7 amb el número d'èxit.

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

Si treballeu fora de Linux, com en simuladors educatius amb servei de IO propi , canvieu el nombre d'ecall i els registres segons documentació de l'entorn.

  Què són Els Fitxers IDML? Per a què serveix i com obrir un

Exercicis inicials per deixar-te anar amb condicionals, cicles i memòria

Un escalfament típic és detectar si un sencer és negatiu. Pots tornar 0 si és positiu i 1 si és negatiu ; amb RV32I, una comparació amb 0 i un set-on-less-than resol la papereta en una sola instrucció ben pensada.

Un altre exercici molt útil és llistar factors d'un nombre: recorre des d'1 fins a n, imprimeix els divisors i torna quants n'hi va haver . Practicaràs branques condicionals, divisions (o restes repetides), i bucles amb sumes i comparacions.

Treballar amb cadenes us obliga a manejar memòria: visitar cada caràcter d'un string en memòria i in-place convertir minúscules a majúscules si encaixen en el rang ASCII. En acabar, torna l'adreça original del string.

Bucles, funcions i recursió: factorial, fibonacci i torres de Hanoi

En dissenyar bucles, pensa en tres blocs: condició, cos i pas. Amb beq/bne/bge i salts incondicionals jal/j es construeixen while/for sense misteri, recolzant-te en addi i comparacions.

.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

En trucades a funció, respecta l'ABI: guardarà si encadenaràs més trucades , preserva s0–s11 ​​si els modifiques i fes servir l'stack amb sp movent-se en múltiples de paraula.

Factorial és el clàssic de recursió: cas base n==0 retorna 1 ; altrament, crida a factorial(n-1) i multiplica per n. Protegera i registres salvats a l'stack abans de la trucada i restaura'ls en tornar.

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

Fibonacci serveix per practicar tant recursió amb dues trucades com una versió iterativa eficient amb variables acumuladores. I si vols un repte de control de flux i paràmetres, tradueix a assemblador una solució de torres de Hanoi amb quatre arguments: discos, torre origen, destí i auxiliar; respecta l'ordre de trucades i mostra cada moviment.

Accés a memòria, arrays i manipulació de strings

A RISC-V l'accés a memòria es fa amb load/store: lw/sw per a paraules, lh/sh per a mitges paraules i lb/sb per a bytes , amb variants amb signe o sense a les càrregues (lb vs lbu, lh vs lhu).

  Com instal·lar HScope a Windows: guia completa i compatibilitat

Per recórrer arrays de sencers utilitza desplaçaments de 4 bytes per índex; en cadenes de text, avança de byte en byte fins a trobar el terminador si el conveni ho requereix (p. ex., \0). Recordeu desar adreces base i manejar punters amb addi/auipc/la segons el cas.

Dissenyar una CPU RV32I des de zero: visió d'alt nivell

Si et ve de gust baixar al silici, un projecte didàctic construeix una CPU RV32I a VHDL, sintetitzable a FPGA de gamma mitjana-baixa. Inclou ROM de programa, RAM de dades i un GPIO senzill per encendre un LED.

El nucli implementa el repertori base (sense extensions M/A/C ni CSRs), utilitza un bus d'adreces de 32 bits i permet accessos de 8/16/32 bits a memòria amb extensió de signe quan escau. El disseny separa clarament registres, ALU, controlador de memòria i màquina destats.

ALU, desplaçaments i idea de «càrrega retardada»

L'ALU es descriu de forma combinacional amb operacions com suma, resta, XOR, OR, AND, comparacions (signed i unsigned) i desplaçaments lògics/arimètics.

Per estalviar LUTs a FPGA, els desplaçaments multi-bit s'implementen iterant desplaçaments d'1 bit controlats per la màquina d'estats : consumeixes diversos cicles, però reduïm recursos lògics.

En circuits síncrons, els canvis s'observen en flancs de rellotge. El concepte de «delayed load» recorda que allò seleccionat per un multiplexor impacta al registre en el cicle següent , aspecte clau en dissenyar la màquina d'estats de fetch-decode-execute.

Controlador de memòria i mapa: ROM, RAM i GPIO

Un bloc de memòria integra ROM i RAM en un espai contigu, simplificant la interfície del processador . El controlador rep AddressIn (32 bits), DataIn, l'amplada (byte/half/word), el senyal d'extensió de signe, WE (lectura/escriptura) i Start per iniciar transaccions.

Quan l'operació acaba, ReadyOut es posa a 1 i, si ha estat lectura , DataOut conté la dada (estès amb signe quan es demana). Si ha estat escriptura, la dada queda allotjada a la RAM.

Un exemple de mapa pràctic: ROM de 0x0000 a 0x0FFF , un byte GPIO a 0x1000 (bit 0 a un pin) i RAM de 0x1001 a 0x1FFF . Amb això pots fer un blinker escrivint i alternant el bit de sortida.

Registres, multiplexors i màquina d'estats

La CPU defineix 32 registres de propòsit general instanciats amb arrays a VHDL, amb un descodificador per seleccionar la destinació d'escriptura des de l'ALU i conservar-ne la resta.

Multiplexors governen entrades de l'ALU (operands i operació), els senyals cap al controlador de memòria (amples, adreça, control d'inici i lectura/escriptura) i els registres especials: PC, IR i un comptador auxiliar per a desplaçaments iteratius.

La màquina d'estats arrenca amb reset , fa fetch de la instrucció apuntada per PC (lectura de 4 bytes), la càrrega en IR en estar llesta i passa als nodes d'execució: ALU (una instrucció en 1 cicle excepte desplaçaments), càrrega/emmagatzematge, bifurcacions i salts, a més d'instruccions especials com ebreak.

  Guia completa: solucions per escoltar i accelerar àudios de WhatsApp al Windows 11

Toolchain creuada, enllaçat i depuració

Per generar binaris RV32I, utilitza un GCC creuat (target riscv32-none-elf) . Compiles fonts C/C++/ASM, enllaçes amb un script que defineix el mapa de memòria i converteixes la sortida a la manera que el teu ROM/FPGA espera.

Un script d'enllaç senzill pot situar .text a ROM des de 0x0000 i .data a RAM des de 0x1004 (si 0x1000–0x1003 l'ocupen registres GPIO). La rutina d'inici pot ser «naked» i col·locar l' stack pointer al final de RAM (p. ex., 0x1FFC) abans de trucar a main.

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

Amb riscv32-none-elf-objdump pots desassemblar l'ELF i comprovar adreces ; per exemple, veuràs l' arrencada a 0x00000000 amb instruccions com lui/addi/jal i la transició al teu main. Per a simulació VHDL, GHDL genera traces que pots obrir amb GtkWave.

Després de verificar en simulació, porta el disseny a una FPGA (Quartus o una altra toolchain). Si la RAM s'infereix com a blocs interns i el codi és RTL clar , hauríeu de sintetitzar sense sorpreses, fins i tot en dispositius veterans.

Recordatoris pràctics i errors típics en començar

No oblidis que x0 sempre és zero ; escriure aquí no té efecte i llegir-lo torna 0. Fes-lo servir a favor en addicions, comparacions i neteges de registre.

Quan implementis funcions, salva ra i els registres sN que modifiquis , i gestiona l'stack amb sumes/restes a sp alineades a paraula. En tornar, restaura a l'ordre invers i salta amb jr ra.

En simuladors com Jupiter, comprova que __start és global i que acabes amb ecall correcte (a0=10 per sortir). Si alguna cosa no arrenca, revisa l'etiqueta, la globalitat i la recompila (F3).

En exercicis amb IO, respecta el protocol de l'entorn : quins registres porten paràmetres, el número de servei i si s'espera adreça o valor immediat. Fes servir la documentació del simulador o del sistema operatiu.

Amb una base clara de ISA (RV32I, registres i ABI), un simulador còmode com Jupiter, i exemples creixents (negatiu, factors, majúscules, bucles, factorial, fibonacci i Hanoi), l'assemblador RISC-V deixa de ser un mur i es converteix en un terreny sucós per entendre com pensa una. I si t'animes a baixar a VHDL, veuràs com encaixen ALU, memòria i control : des del fetch d'instruccions i la càrrega diferida fins a la interfície de memòria i un mapa amb ROM, RAM i GPIO que et deixa fer parpellejar un LED amb el teu processador.

Els Millors Programes Per Programar
Article relacionat:
Els 7 Millors Programes Per Programar