Alumnos:
- Collante, Gerardo
- Quinteros Castilla, Nicolás
Profesores:
- Ing. Rodríguez, Martín
- Ing. Pereyra, Martín
- Ing. Pinzani, Paulo
Con el nombre de MIPS (siglas de Microprocessor without Interlocked Pipeline Stages) se conoce a toda una familia de microprocesadores de arquitectura RISC (Reduced Instruction Set Computer) desarrollados por MIPS Technologies.
En este trabajo práctico se solicita implementar en el lenguaje de descripción de hardware Verilog
(usado para modelar sistemas electrónicos usualmente) el pipeline del procesador MIPS.
Se solicita implementar las siguientes etapas del procesador:
-
IF
(Instruction Fetch): búsqueda de la instrucción en la memoria del programa. -
ID
(Instruction Decode): decodificación de la instrucción y lectura de registros. -
EX
(Execute): ejecución de la instrucción propiamente dicha. -
MEM
(Memory Access): lectura o escritura desde/hacía la memoria de datos. -
WB
(Write Back): escritura de resultados en los registros.
Se solicita implementar las siguientes instrucciones:
-
R-Type
:SLL, SRL, SRA, SLLV, SRLV, SRAV, ADDU, SUBU, AND, OR, XOR, NOR, SLT
-
I-Type
:LB, LH, LW, LWU, LBU, LHU, SB, SH, SW, ADDI, ANDI, ORI, XORI, LUI, SLQTI, BEQ, BNE, J, JAL
-
J-Type
:JR, JALR
El procesador debe tener soporte para los siguientes tipos:
-
Estructurales: se producen cuando dos instrucciones tratan de utilizar el mismo recurso en el mismo ciclo.
-
Datos: se intenta utilizar un dato antes de que esté preparado. Mantenimiento del orden estricto de lecturas y escrituras.
-
Control: intentar tomar una decisión sobre una condición todavía no evaluada.
Por tanto se deberá implementar una unidad de cortocircuitos y una unidad de detección de riesgos.
El programa a ejecutar debe ser cargado en la memoria de programa mediante un archivo ensamblado, por tanto debe crearse un ensamblador.
Creado en Python, se encarga de traducir las instrucciones en .asm al código MIPS32bits correspondiente.
Se realizará una breve descripción de cada una de las etapas del procesador.
La etapa de Instruction Fetch corresponde a la búsqueda de la instrucción a la memoria para posteriormente ejecutarla.
Esquemático general e I/O
-
INCR
: Incrementa el valor delPC
. -
PC_MUX
: Este módulo a través del selector{J,branch}
decide cual será el próximo valor delPC
, ya sea el siguientePC
incrementado, el valor siguiente de PC o el PC del salto o la ramificación. -
PC
: Asigna el valor alPC
, además lo envía aINCR
yMEM
. -
MEM
: El módulo posee un arreglo de instrucciones que fueron cargadas previamente, entonces elPC
sirve como índice para obtener la instrucción. -
IF_ID
: Es el buffer de salida, por tanto en el próximo flanco positivo declk
, se cargaran los valores haciendo avanzar la instrucción al siguiente stage. Recordemos que en el momento que la instrucción se encuentra disponible en algún buffer de salida también lo está para el siguiente stage. Además posee las entradasJ
yB
, para el caso donde sea necesario flushear una instrucción y evitar que esta entre al pipeline ya sea por un salto o ramificación. También vale mencionar la entradastall
, que nos sirve para conservar los valores en el buffer aunque éste se encuentre habilitado realizando una parada del pipeline. Esta operación es necesaria para las instruccioneslw
ysw
.
El módulo DECODE
nos provee funcionalidades para decodificar una instrucción, leer y escribir en GPR y finalmente controlar otras etapas a través de líneas de control.
-
REG
: Es el módulo encargado de almacenar valores en registros de propósito genereal. Para ello se vale de un arreglo de 32 posiciones de 32 bits cada uno. Para la lectura de valores, i.e.,A
yB
se indexa conrs
yrt
respectivamente. Sin embargo para escribir en la memoria es necesario queregwrite
esté en alto, y así en el rise edge delclk
se escribirán los datos dewritedata
en la pocisión indexada porrd
. -
CONTROL
: Nos permite controlar los futuros stages a través de líneas de control que seránEX
,M
yWB
respectivamente, en función del opcode de la instrucción. Cada línea de control tiene una función especifica que se define en la siguiente Tabla 1{reference-type="ref" reference="tab:ctl2-table"}. -
JR_UNIT
: Unidad encargada de verificar si la instrucción esJR
, en caso que lo sea se encarga de switchear a través de unmux
los 26 bits LSB por el valor indicado en el operando. Además cambia elopcode
al de una operaciónJ
(recordemos que la instrucciónJR
poseeopcode = b000000
, por tanto podría ser malinterpretada como una operaciónNOP
). -
BRANCH_UNIT
: Unidad encargada de realizar los cálculos para ver si una rama es tomada o no. Se apoya en multiplexores controlados a través de laBRANCH FORWARDING UNIT
con el próposito de evitar peligros de datos. -
S-EXTEND
: Incrementa el tamaño de la instrucción de 16bits a 32bits, replicando el bit de mayor orden en[31:16]
. -
ID_EX
: Buffer de salida encargado de cargar los valores para la siguiente etapa, recordemos que se activa por rise edge declk
.
Este módulo es el encargado de realizar las diferentes operaciones con los operandos en la ALU.
Esquemático general e I/O
-
ALU_CONTROL
: este módulo nos es útil para controlar la operación ejecutada por la ALU. Para esta labor se sirve delopcode
yfunct
de la instrucción. -
ALU_MUX
: laFU
(Forwarding Unit) es la encargada de los controles de peligros así como también la encargada de decidir los valores que operará laALU
ya queA
está definida porFORWARD_MUX_A
yB
si bien se define porsel
(EX[0]
),b
es provisto porFORWARD_MUX_B
. -
ALU
: laALU
es capaz de ejecutar todas las operaciones indicadas en la sección 2.2{reference-type="ref" reference="section:instr"}, ya sean de R-type como de I-type. -
`BOTTOM_MUX
-
JAL UNIT
: unidad especial encargada de detectar una instrucciónJAL
con el objetivo de cargar elPC
en el registro$ra
(recordar que esta instrucción es complementaria a la instrucciónJR $ra
), apoyandose en el moduloalu_result_mux
que es un multiplexor entre la salida de la ALU y el valor delPC
. En caso que no sea detectado todo sigue su curso normal. -
EX_MEM
: Buffer de salida del módulo. -
FORWARD_MUX_A
-
FORWARD_MUX_B
-
FORWARDING UNIT
Esquemático general e I/O
En determinadas ocasiones en una etapa necesitamos un dato que aún no se ha terminado de procesar en otra, o quizás si pero aún el dato no ha sido guardado. Esto puede llevar a lo que se denomina parada o stall del pipeline, ya que necesitamos uno o más ciclos de reloj para obtener el dato. Esto acomete contra nuestro objetivo de rendimiento, por tanto necesitamos una manera de combatirlo.
Esto significa e.g. que cuando una instrucción intenta usar un registro en su etapa EX
, una instrucción anterior intenta escribir en su etapa WB
, pero en realidad necesitamos los valores como entradas a la ALU
más que en memoria.
Peligro EX
if(EX/MEM.RegWrite and (EX/MEM.RegisterRd != 0)
and (EX/MEM.RegisterRd = ID/EX.RegisterRs)) ForwardA = 10
if(EX/MEM.RegWrite) and (EX/MEM.RegisterRd != 0)
and (EX/MEM.RegisterRd = ID/EX.RegisterRt)) ForwardB = 10
Peligro MEM
if (MEM/WB.RegWrite && (MEM/WB.RegisterRd != 0)
&& not(EX/MEM.RegWrite && (EX/MEM.RegisterRd != 0)
&& (EX/MEM.RegisterRd != ID/EX.RegisterRs))
&& (MEM/WB.RegisterRd = ID/EX.RegisterRs)) ForwardA = 01
if (MEM/WB.RegWrite && (MEM/WB.RegisterRd != 0)
&& not(EX/MEM.RegWrite && (EX/MEM.RegisterRd != 0)
&& (EX/MEM.RegisterRd != ID/EX.RegisterRt))
&& (MEM/WB.RegisterRd = ID/EX.RegisterRt)) ForwardB = 01
Estas condiciones serán implementadas a través de dos multiplexores, ForwardA
y ForwardB
.
BOTTOM_MUX
: Este módulo es útil para obtenerrd
en el bufferEX_MEM
, debido a que utiliza como selector aEX[3]
que equivale aRegDst
. Cuando este valor es 1, entonces seleccionard
como salida del multiplexor.
Esquemático general e I/O
-
D_MEM
: Equivale a la memoria RAM, posee un arreglo de 128 posiciones de 32 bits cada una. Su funcionamiento es sencillo,Address
funciona como índice del arreglo y tanto para leer como para escribir debemos setear los valoresMemWrite
comoMemRead
respectivamente como vimos en la Tabla 1{reference-type="ref" reference="tab:ctl2-table"}. -
MEM_WB
: Buffer de salida del stage.
El funcionamiento de este módulo es muy simple, básicamente decide a través del selector MemtoReg
si WriteData
será el valor leído desde memoria o el resultado de la ALU
.
Esquemático general e I/O
Debido a la imposibilidad de utilizar la placa físicamente se solicitó la creación de una unidad de debugging cuyo propósito era el de emular el paso a paso del pipeline siendo cada paso accionado a través de un botón.
Esto generaba un pulso enable
que es entrada de cada uno de los buffers de salida de cada etapa. Así para avanzar en el pipeline era condición necesaria que la señal enable
estuviera en alto además de la señal del clk
.
Su tarea es detectar un peligro de datos que sucedería en el caso que una función lw
o sw
necesite un dato que aún no se encuentra disponible.
En el caso que eso ocurra se realiza una parada congelando el buffer IF_ID
para que no avance permitiendo así avanzar a las demás etapas del pipeline y se comprueban varias condiciones:
if(IdExMemRead &&
(IdExRegRt == IfIdRegRs)
|| (IdExRegRt == IfIdRegRt))
stall the pipeline
Se tomó la decisión de ejecutar las operaciones de rama en el stage ID
debido a que esto sólo necesitaba realizar en una ocasión flush, en vez de tres que es lo que hubiera sucedido si realizabamos estas operaciones en el stage MEM
como se propone en el libro Arquitectura de Computadoras de Hennesy & Patterson.
El inconveniente con realizar estas operaciones en ID
es que esto puede llevar a riesgos de datos que debemos solucionar.
Básicamente los riesgos que podemos sufrir son desde los buffers EX_MEM
, MEM_WB
e incluso ID_EX
(pero este caso no será analizado porque es salvado por la HDU
).
- Forwarding desde
EX_MEM
Podemos observar que la operación 2 (or
) realiza una operación y guarda el resultado en el operando $v0
, pero el inconveniente es que aún ese resultado no se guardó y beq
lo necesita, por tanto hacemos forwarding.
En este caso, en el momento que el resultado es guardado es solicitado por bne
, por tanto debemos realizar un forwarding desde MEM_WB
.
El funcionamiento de la BFU
se resume en el siguiente gráfico donde observamos que es la encargada a través de los selectores de los multiplexores de elegir los operandos reg1
y reg2
que serán necesarios en el cálculo para decidir si la rama será tomada o no.
Este reporte nos sirve para interpretar cuestiones referentes al tiempo que tardan las diferentes compuertas (y su respectivo fanout) en ejecutar las instrucciones para darnos una idea si nuestro diseño una vez sintetizado y quemado en la placa real tendrá el comportamiento deseado.
En nuestro caso con un clk de
- Parámetros:
-
Setup: incluye todas las verificaciones relacionadas al análisis de máximo delay, configuración, recuperación y verificación de datos.
- WNS (Worst Negative Slack): Este valor corresponde a la peor holgura de tiempo de todos los timing paths para el análisis de máximo delay.
-
TNS (Total Negative Slack): Este valor corresponde a la suma de todas las violaciones de WNS considerando la peor violación de cada timing path endpoint. Su valor es
$0ns$ cuando todas las constraints son encontradas para el analisis de máximo delay, o negativo cuando hay algunas violaciones. -
Failing Endpoints: es el número total de endpoints con una violación (
$WNS<0ns$ ).
-
Hold: incluye todas las verificaciones relacionadas al análisis de tiempo mínimo: retención, recuperación y verificación de datos.
- WHS (Worst Hold Slack): Este valor corresponde a la peor holgura de tiempo de todos los timing paths para el análisis de mínimo retardo.
-
THS (Total Hold Slack): Este valor corresponde a la suma de todas las violaciones de WHS considerando la peor violación de cada timing path endpoint. Su valor es
$0ns$ cuando todas las constraints son encontradas para el analisis de máximo delay, o negativo cuando hay algunas violaciones.
-
Pulse Width: incluye todas las verificaciones relacionadas relacionadas al límite de switching de pines:
- WPWS (Worst Pulse Width Slack): este valor corresponde a la peor holgura de todas las verificaciones de timing listadas arriba cuando se usan los delays mínimos y máximos.
- TPWS (Total Pulse Width Slack): este vañpr es la suma de todas las violaciones WPWS, cuando consideramos solamente la peor violación de cada pin en el diseño.
-
Failing Endpoints: Este es el número total de pines con una violación (
$WPWS<0ns$ ).
-
Setup: incluye todas las verificaciones relacionadas al análisis de máximo delay, configuración, recuperación y verificación de datos.
Así en síntesis comprobamos los valores obtenidos son correctos y en teoría al probar nuestro diseño en la placa no tendremos problemas de timing.
Un gran punto de este trabajo práctico es que al tener que crear todas las partes concernientes al funcionamiento de un computador a bajo nivel (desde el ensamblador hasta el pipeline) esto da un enfoque global de todo el proceso, que resulta muy beneficioso para la comprensión de su funcionamiento.
Este conocimiento a su vez nos es provechoso para asentar las bases teóricas de la materia a través de la práctica. A medida que se avanzaba en la implementación surgieron problemas que requirieron de ingenio para su resolución, así se logró profundizar en situaciones que quizás de manera solamente teórica hubieran sido pasadas por alto.
En últimas palabras, el trabajo da un cierre a la materia que reúne todo el contenido de la misma atando todos los cabos a través de la práctica.