Apuntes de Verilog
Tema 8. Arquitectura de Ordenadores
Procesador MIPS para instrucciones beq-bne
Procesador MIPS para instrucciones beq-bne
Las instrucciones beq y bne son saltos condicionales, donde beq significa Branch if Equal, es decir, "salta si son iguales" y bne significa Branch if Not Equal, "salta si no son iguales".
Se utilizan para cambiar el flujo del programa dependiendo de si dos registros contienen valores iguales o diferentes.
Las instrucciones beq y bne tienen el mismo formato que las tipo I.
Formato: | opcode (6) | rs (5) | rt (5) | offset (16) |
Longitud total: 32 bits (como todas las instrucciones MIPS).
La instrucción beq compara dos registros. Si los valores son iguales, el programa salta a una dirección específica (normalmente una etiqueta en el código). Si no son iguales, continúa ejecutando la siguiente instrucción.
li $t0, 10 # Carga 10 en $t0
li $t1, 10 # Carga 10 en $t1
beq $t0, $t1, igual # si el contenido de $t0 es igual al de $t1, salta a la etiqueta igual.
li $a0, 1 # Esta línea se salta si $t0 == $t1
igual:
li $a0, 2 # Se ejecuta si se hizo el salto
Suponer que el PC actual = 0x00400020
Instrucción:
beq $s1, $s0, 4
El offset = 4
Se desplaza a la izquierda 2 bits → 4 × 4 = 16 bytes
Nueva dirección = 0x00400020 + 4 + 16 = 0x00400034
👉 Si $s1 == $s0, el salto va a la dirección 0x00400034
👉 Si no son iguales, ejecuta la siguiente instrucción en 0x00400024
La Unidad de Control detecta opcode = 000100 → activa la señal Branch.
Se leen los valores de los registros rs y rt del Register File.
La ALU los resta (A - B):
Si el resultado es 0, la señal Zero = 1
La Unidad de Control combina Zero y Branch → si ambas son 1, activa el salto.
El nuevo valor del Program Counter se calcula en el sumador PC_BRANCH:
PCBranch = (PC + 4) + (SignExtImm << 2)
El multiplexor PC_MUX elige entre:
PC + 4 (sin salto)
PC_Branch (con salto)
La instrucción bne compara dos registros. Si los valores son distintos, el programa salta a una dirección específica (normalmente una etiqueta en el código). Si son iguales, continúa ejecutando la siguiente instrucción.
li $t0, 10 # Carga 10 en $t0
li $t1, 10 # Carga 10 en $t1
bne $t0, $t1, loop # si el contenido de $t0 es diferente al de $t1, salta a la etiqueta loop.
li $a0, 1 # Esta línea se salta si $t0 != $t1
loop:
li $a0, 2 # Se ejecuta si se hizo el salto
Suponer que el PC actual = 0x00400020
Instrucción:
bne $s1, $s0, 4
El offset = 4
Se desplaza a la izquierda 2 bits → 4 × 4 = 16 bytes
Nueva dirección = 0x00400020 + 4 + 16 = 0x00400034
👉 Si $s1 != $s0, el salto va a la dirección 0x00400034
👉 Si son iguales, ejecuta la siguiente instrucción en 0x00400024
La Unidad de Control detecta opcode = 000101 → activa la señal Branch.
Se leen los valores de los registros rs y rt del Register File.
La ALU los resta (A - B):
Si el resultado es 0, significa que son iguales →Zero = 1
Si el resultado no es 0, significa que son distintos →Zero = 0
La lógica de control genera la señal pc_source de forma diferente a beq:
En beq: PCSrc = Branch AND Zero
En bne: PCSrc = Branch AND (NOT Zero)
Si pc_source = 1 el nuevo valor del Program Counter se calcula en el sumador PC_BRANCH:
PCBranch = (PC + 4) + (SignExtImm << 2)
El multiplexor PC_MUX elige entre:
PC + 4 (sin salto)
PC_Branch (con salto)
Hasta aquí hemos visto que son y cómo funcionan las instrucciones beq y bne, a continuación vamos a ver los cambios en el datapath y la Unidad de Control para procesar dic has instrucciones.
Para ejecutar las instrucciones beq y bne, necesitamos añadir tres módulos al datapath tipo-R que son: el módulo shift_left_2.v, el sumador PC_BRANCH y el multiplexor PC_MUX.
Modificaciones al datapath
El módulo SHIFT_IMM desplaza a la izquierda el valor de IMM, esto es: SignExtImm << 2 recuerda que desplazar a la izquierda dos bits es igual que multiplicar x 4
El sumador PC_BRANCH realiza la suma PCBranch = (PC + 4) + (SignExtImm << 2) para calcular la dirección del salto.
Y el multiplexor PC_MUX selecciona entre PC_BRANCH y PC_P4 dependiendo de si hay o no hay salto. Esto se hace con la señal de selección pc_source.
En cuanto a la Unidad de Control, es necesario añadir tres puertas lógicas para implementar la función pc_source_out = (w_branch & zero) | (w_branch_ne & ~zero);
Modificaciones a la Unidad de Control
La imagen siguiente muestra el esquema general del procesador MIPS para instrucciones beq y bne.
Top module de un procesador MIPS para instrucciones beq y bne
Para que no quede muy extenso, solamente voy a poner el código Verilog de módulo top y el testbench.
Ponemos unas instrucciones de prueba en módulo Instruction Memory...
...y ejecutamos la simulación en Vivado.
Comprobamos que efectivamente el procesador funciona como se espera; la primera instrucción beq $s1 $s0 0x0005 se ignora pues los registros $s0 y $s1 contienen datos distintos, mientras que la segunda instrucción beq $s2 $s3 0x0003 sí se tiene en cuenta, pues los registros $s2 y $s3 contienen ambos el mismo dato 0x0000_000A lo que provoca un salto de tres instrucciones hasta la posición 40 y en la siguiente instrucción finaliza el programa.
De igual manera que en el ejemplo anterior, cargamos unas instrucciones bne de prueba en módulo Instruction Memory...
...y ejecutamos la simulación en Vivado.
Comprobamos que efectivamente el procesador funciona como debe; la primera instrucción bne $s1 $s0 0x0003 salta 3 instrucciones por ser distinto el contenido de los registros s $s0 y $s1, mientras que la segunda instrucción bne $s2 $s3 0x0005 se ignora, ya que los registros $s2 y $s3 contienen ambos el mismo dato 0x0000_000A. El programa continua en la siguiente y última instrucción finalizando así el programa.
MIPS32 Single-Cycle Processor, thanks to Mr Moamen Wael for sharing!
MIPS Converter, herramienta online para codificar instrucciones
En este blog:
Puedes descargar las fuentes aquí abajo.