Guía · RISC-V

Riesgos RAW, forwarding y burbujas

Cuándo una dependencia obliga a esperar, qué caminos de forwarding la resuelven, por qué la carga-uso siempre cuesta un ciclo y cómo se evita reordenando.

Revisada el con las herramientas del sitio.

Un riesgo RAW (read after write, lectura después de escritura) aparece cuando una instrucción lee un registro que una instrucción anterior todavía no ha escrito. En un pipeline de 5 etapas es el riesgo más frecuente, porque los registros se leen en ID pero se escriben en WB, tres etapas después.

El problema: sub necesita x1 antes de tiempo

add  x1, x2, x3
sub  x4, x1, x5

add calcula x1 en EX (ciclo 3) y lo escribe en WB (ciclo 5). sub lee sus registros en ID en el ciclo 3. Si no se hace nada, leería el valor antiguo de x1. La solución más sencilla es detener sub en ID hasta que el valor esté en el banco de registros:

Etapa123456789
IFI0I1———————
ID—I0I1I1I1I1———
EX——I0burb.burb.burb.I1——
MEM———I0burb.burb.burb.I1—
WB————I0burb.burb.burb.I1
Sin forwarding: 9 ciclos, 3 burbujas, 0 instrucciones descartadas. I0 = add x1, x2, x3 · I1 = sub x4, x1, x5

En el simulador, sin forwarding, sub espera hasta que add sale de WB: 3 burbujas. Muchos libros, entre ellos Patterson y Hennessy, suponen que el banco de registros se escribe en la primera mitad del ciclo y se lee en la segunda; con esa hipótesis sub puede leer en el mismo ciclo del WB y la espera es de 2 burbujas. El simulador no modela esa división del ciclo, así que sus cifras sin forwarding son una unidad mayores.

Forwarding: usar el resultado antes de escribirlo

El resultado de add ya existe al final de su EX, guardado en el registro de segmentación EX/MEM. El forwarding añade multiplexores a la entrada de la ALU para tomar el operando directamente de ahí en lugar del banco de registros:

Etapa123456
IFI0I1————
ID—I0I1———
EX——I0I1——
MEM———I0I1—
WB————I0I1
Con forwarding: 6 ciclos, 0 burbujas, 0 instrucciones descartadas. I0 = add x1, x2, x3 · I1 = sub x4, x1, x5

Sin burbujas. Si entre las dos instrucciones hay otra, el valor se toma un ciclo más tarde, del registro MEM/WB. Estos son los dos caminos de forwarding del pipeline clásico:

  • EX/MEM → EX: la productora está justo delante (distancia 1).
  • MEM/WB → EX: hay una instrucción en medio (distancia 2).
add  x1, x2, x3
addi x9, x9, 1
sub  x4, x1, x5

Con forwarding esta secuencia no tiene burbujas; sin forwarding, el simulador inserta 2 (compruébalo).

La excepción: carga seguida de uso

lw   x1, 0(x2)
add  x3, x1, x4

Una carga no tiene el dato al final de EX, sino al final de MEM, porque en EX solo calcula la dirección. add lo necesita al principio de su EX, que sin espera coincidiría con el MEM de lw. Ningún camino de forwarding puede enviar un dato hacia atrás en el tiempo, así que hace falta una burbuja; después el valor llega desde MEM/WB:

Etapa1234567
IFI0I1—————
ID—I0I1I1———
EX——I0burb.I1——
MEM———I0burb.I1—
WB————I0burb.I1
Con forwarding: 7 ciclos, 1 burbujas, 0 instrucciones descartadas. I0 = lw x1, 0(x2) · I1 = add x3, x1, x4

La unidad de detección de riesgos lo detecta en ID: si la instrucción que está en EX es una carga y su rd coincide con rs1 o rs2 de la que está en ID, detiene el pipeline un ciclo. Eso es lo que implementa el simulador.

Evitar la burbuja reordenando

Si hay una instrucción independiente cerca, se puede colocar entre la carga y su uso. Esta secuencia tiene una burbuja de carga-uso entre lw y add:

Etapa123456789
IFI0I1I2I2I3————
ID—I0I1I1I2I3———
EX——I0burb.I1I2I3——
MEM———I0burb.I1I2I3—
WB————I0burb.I1I2I3
Con forwarding: 9 ciclos, 1 burbujas, 0 instrucciones descartadas. I0 = lw t0, 0(a0) · I1 = add t1, t0, t0 · I2 = addi t2, a1, 4 · I3 = sw t1, 0(a2)

addi no depende de lw, así que la adelantamos:

Etapa12345678
IFI0I1I2I3————
ID—I0I1I2I3———
EX——I0I1I2I3——
MEM———I0I1I2I3—
WB————I0I1I2I3
Con forwarding: 8 ciclos, 0 burbujas, 0 instrucciones descartadas. I0 = lw t0, 0(a0) · I1 = addi t2, a1, 4 · I2 = add t1, t0, t0 · I3 = sw t1, 0(a2)

9 ciclos pasan a 8, el mínimo para 4 instrucciones. El resultado del programa no cambia porque addi no lee ni escribe t0 ni t1. Los compiladores hacen esta planificación de instrucciones de forma automática.

¿Y los riesgos WAR y WAW?

Escritura después de lectura (WAR) y escritura después de escritura (WAW) no aparecen en este pipeline: las instrucciones avanzan en orden, todas leen sus registros en ID y todas escriben en WB. Solo importan en procesadores que ejecutan fuera de orden.

Para practicar

El ejercicio de carga-uso pide contar ciclos con y sin forwarding, y el de reordenación comprueba la última sección.

Practica lo que has leído

Cuenta los ciclos de un riesgo carga-uso

En el cauce de 5 etapas del simulador, ¿cuántos ciclos tarda esta secuencia con forwarding y sin forwarding? ¿Cuántas burbujas hay con forwarding?

lw   t0, 0(a0)
add  t1, t0, t0
sub  t2, t1, a1
Abrir en la herramienta

Reordenar para eliminar la burbuja

En el orden original (lw, add, addi, sw) hay una burbuja de carga-uso y la secuencia tarda 9 ciclos con forwarding. Aquí addi se ha movido entre lw y add. ¿Cuántos ciclos tarda ahora con forwarding y cuántas burbujas quedan?

lw   t0, 0(a0)
addi t2, a1, 4
add  t1, t0, t0
sw   t1, 0(a2)
Abrir en la herramienta

Referencias

  • D. A. Patterson, J. L. Hennessy — Computer Organization and Design RISC-V Edition: The Hardware/Software Interface (Morgan Kaufmann) — capítulo del procesador: pipeline de 5 etapas, riesgos de datos y de control, forwarding y detección de carga-uso
  • Ripes — graphical processor simulator and assembly editor for RISC-V — simulador externo con memoria y saltos reales, útil para ir más allá de este sitio