¿Cómo puedo solucionar el retraso entre la instrucción y el contador de programa?

Estoy diseñando el procesador MIPS, esto incluye la memoria de datos y la memoria de instrucciones para la prueba. Tuve un problema con la síntesis de IM cubierto en esta pregunta ( ¿Cómo hacer una memoria de instrucciones sintetizable en SystemVerilog? ) y después de reescribirlo como se recomienda (ff con una casedeclaración) ocurrió otro problema.

El problema: la instrucción en la CPU tiene un ciclo de retraso desde el contador de programa. Por lo tanto, cuando llega la instrucción de bifurcación (número i ), el Contador de programa ya está ( i+1 ) y luego viene instr ( i+1 ) y PC es PC_branch . ¿Cómo puedo arreglarlo? El retraso es causado por este ff en IM. El código y las formas de onda se encuentran a continuación.

El código:

always_ff @(posedge clk) begin
case (addr)      
        32'd0 : rom_ff <= 32'h2408000F; // a = F
        32'd1 : rom_ff <= 32'h240A0000; // res = 0

        32'd2 : rom_ff <= 32'h01485021; // (*) res = res + a
        32'd3 : rom_ff <= 32'h2508FFFF; // a = a - 1

        32'd4 : rom_ff <= 32'h1500FFFD; // if (a != 0) goto (*)
        32'd5 : rom_ff <= 32'hAC0A0ADD; 
        default : rom_ff <= 32'h0;
    endcase
end

La forma de onda de las señales de la CPU:ingrese la descripción de la imagen aquí

Respuestas (2)

Lo que ha encontrado allí son los problemas que vienen con la canalización de instrucciones . Supongo que va a sintetizar su procesador para ejecutarlo en un FPGA en algún momento, por lo que, lamentablemente, eliminar el registro entre la memoria de instrucciones y el resto de la CPU no es una opción.

Para solucionar esto, tienes tres opciones.

  • Ignora el problema. Después de todo, su procesador todavía se comporta de manera determinista; el único problema es que ejecutará una instrucción adicional después de una bifurcación. Esto se denomina ranura de retardo de bifurcación , lo que significa que la instrucción que viene después de una bifurcación se ejecutará como si estuviera realmente antes de la bifurcación. Muchos procesadores tienen este tipo de ranura de retardo y los compiladores pueden manejarlos perfectamente. Los primeros procesadores MIPS también tenían ranuras de retardo.
  • Detener incondicionalmente la tubería después de que se haya obtenido una rama. Esto significa que detiene el contador del programa durante un ciclo e inserta un NOP en la tubería cada vez que encuentra una bifurcación, lo que oculta efectivamente la ranura de retraso.
  • Lave la tubería cuando se toma una rama. Esto significa que su procesador continúa ejecutando el flujo de instrucciones normalmente hasta que detecta que tiene que saltar. En este punto, desecha todas las instrucciones posteriores al salto que ya ha obtenido. En su caso, solo habría una sola instrucción para tirar.

También puede echar un vistazo a cómo se organizan las canalizaciones RISC clásicas y qué peligro hay en una canalización . Una vez que implemente los accesos a la memoria en su procesador, también puede encontrarse con peligros causados ​​por los datos cargados desde la memoria que no están disponibles lo suficientemente rápido. Puede resolver estos problemas de manera similar, ya sea deteniendo la canalización o aceptando que tiene los llamados intervalos de retraso de carga, lo que significa que los datos cargados desde la memoria no están disponibles inmediatamente para la instrucción que sigue a la carga. Algunos procesadores también tienen este tipo de ranura de retardo, aunque es menos común que las ranuras de retardo de rama.

¿Está bien si agrego una instrucción 32'h0 (burbuja) después de la bifurcación?
@katzesaal Si 32'h0 es un NOP, entonces sí, ¡eso funcionaría perfectamente bien! Luego, solo llena el espacio de demora con un NOP. También puede extraer una instrucción anterior a la bifurcación en la ranura de retraso (siempre que la bifurcación no dependa de su resultado). Entonces ni siquiera estás desperdiciando un ciclo de reloj.
Guau, este truco con un instr en lugar de NOP es genial) Gracias por la respuesta.
  • Otra buena opción es utilizar la predicción de bifurcaciones . Hay muchos algoritmos de predicción de ramas que puede investigar. Muchos procesadores modernos utilizan la predicción de bifurcaciones. Entonces, si la rama predicha es la correcta, ganas y ahorras ciclos. Si la rama predicha es incorrecta, debe borrar los datos.

  • La ejecución fuera de orden se puede utilizar para mantener ocupada la CPU. En esta técnica, se evitan muchos ciclos inactivos/desperdiciados. El orden de ejecución de las instrucciones no es el orden original en la ejecución fuera de orden.

Otras lecturas: