Fundamentos de los Computadores Guión de la práctica 4. Fecha de entrega: ● Grupo A: 15/Enero ● Grupo B: 14/Enero ● Grupo C: 13/Enero Objetivo de la práctica En esta práctica se terminará la construcción del microprocesador ARC. La ruta de datos ya está diseñada: sus componentes se desarrollaron en las prácticas anteriores (ALU, banco de registros y PC). Ahora sólo hace falta crear la unidad de control y juntar todos los componentes para tener ARC funcionando. Normalmente, la integración de todas las partes suele ser la parte de un proyecto que más problemas genera. Unidad de control Se va a implementar una unidad de control cableada. Se utilizará un autómata con 4 estados. • • • • RESET: estado al que se pasa cuando se recibe un reset asíncrono (activo a nivel bajo). Al liberarse el reset, pasa incondicionalmente al estado de captura CAPTURA: se obtiene la próxima instrucción desde la memoria, y se escribe en el registro de instrucciones (IR). A continuación pasa siempre al estado de ejecución. EJECUCIÓN: se decodifica y se ejecuta la instrucción, esto es, se actualiza el registro de resultado, los flags y el PC. A continuación pasa siempre al estado de escritura. ESCRITURA: se escriben los resultados de la instrucción, bien en el banco de memoria o en la memoria. A continuación pasa siempre al estado de captura. A partir del estado actual, de la instrucción en ejecución (los contenidos del registro IR) y de los flags, se irán generando todas las señales de control. Estas señales son: • • • • • • • • • • • • • • IR_WE: habilitación de carga para el registro de instrucciones (IR). Se activa en el estado de captura. MEM_REQ: indica un acceso a memoria en el ciclo actual. Se activa en el estado de captura, y en el de escritura cuando la instrucción sea un load o un store MEM_WRITE: indica que el acceso a memoria señalado por MEM_REQ es de escritura. Se activa en el estado de escritura, cuando haya un store. PC_JUMP: se debe activar cuando haya una instrucción jmpl PC_CALL: se debe activar cuando haya una instrucción call PC_BRANCH: se debe activar cuando haya una instrucción de branch que sea efectiva (el valor de los flags se corresponda con es esperado por la instrucción en particular, p.ej. para be esta señal se activará solo si FLAG_Z vale uno) PC_UPDATE: se activa siempre al pasar por el estado de ejecución RD_FROM_SETHI: indica que la entrada al banco de registros se hace desde los datos inmediatos provenientes de la instrucción, que se pondrán en las posiciones más significativas. Se debe activar cuando haya una instrucción sethi RD_FROM_MEM: indica que la entrada al banco de registros se hace desde la memoria. Se debe activar cuando haya una instrucción load. REGS_WE: se debe activar al pasar por el estado de escritura, siempre que no se trate de una instrucción de store o de branch RD_ADDR: se debe utilizar el valor apuntado en la propia instrucción, salvo para el call, que siempre usa el registro 15. ALU_FUNC: se puede obtener como IR(24)&IR(21 downto 19), salvo en jmpl, load y store, que se debe fijar siempre el código de la suma. ALU_IMM: indica que para la operación de la ALU se emplea el dato inmediato presente en la instrucción. Se corresponde con IR(13). FLAGS_WE: se debe activar en el estado de ejecución sólo para las instrucciones tipo cc, que actúan sobre los códigos de condición. • • • RESULT_FROM_PC: indica que el registro de resultado se debe cargar no con los datos provenientes de la ALU, sino con el PC. Se activa con las instrucciones call y jmpl. RESULT_WE: carga del registro de resultado, se activa siempre en el estado de ejecución ADDR_FROM_PC: salvo en las instrucciones de acceso a memoria, load y store, esta señal estará activada para indicar que los datos para el bus de direcciones provienen del PC. Implementar el microprocesador ARC Una vez que se tienen todos los componentes, se deben juntar en el módulo ARC.VHD tal y como indica en el esquema situado al final de este guión. Para ello, se utilizará diseño jerárquico, donde los componentes que se instanciarán son: • • • • ALU.VHD (Prácticas anteriores) REGS.VHD (Prácticas anteriores) REG-PC.VHD (Prácticas anteriores) CTRL.VHD (apartado anterior) Mientras que el resto de componentes (registro de resultado, registro de estado (flags) y diversos multiplexores) se crearán empleando procesos y/o asignaciones concurrentes. Esto es porque son componentes muy sencillos, y probablemente no valga la pena utilizar diseño jerárquico en ellos. El microprocesador, ARC.VHD, tendrá la siguiente interfaz: entity ARC is port( end ARC; CLK nRST DATA_IN DATA_OUT ADDR WRITE MEM_WAIT REQ ); : : : : : : : : in STD_LOGIC; in STD_LOGIC; in STD_LOGIC_VECTOR(31 downto 0); out STD_LOGIC_VECTOR(31 downto 0); out STD_LOGIC_VECTOR(31 downto 0); out STD_LOGIC; in STD_LOGIC out STD_LOGIC Donde: • CLK es el reloj del sistema • nRST es la señal del reset. DATA_IN y DATA_OUT son los buses de datos de entrada y salida respectivamente ADDR es el bus de direcciones WRITE es la señal que indica que el ciclo actual de acceso a la memoria es de escritura REQ indica que en el ciclo actual hay un acceso a la memoria MEM_WAIT: Indica que la memoria está ocupada • • • • • El protocolo de acceso a la memoria es síncrono. Tras el flanco de subida que da comienzo a un ciclo de reloj, el microprocesador sube REQ para indicar que quiere acceder a la memoria. Si el acceso va a ser de lectura, se deja la señal WRITE a 0. La memoria prepara el dato y lo deja en DATA_IN, de tal manera que en el siguiente flanco de reloj el microprocesador lo capturará. Sin embargo, si el ciclo es de escritura, se pone a 1 la señal WRITE y se deja el dato de salida en DATA_OUT. La memoria lo guardará en el siguiente ciclo de reloj. Todo esto está resuelto con la codificación que se ha hecho de la unidad de control, por lo que en un principio no hay que hacer nada nuevo. Como probarlo todo Para probar el microprocesador se proporcionan dos archivos adicionales, ARC_TB.VHD y MEM.VHD. El primero es un sistema completo, que incluye ARC, las memorias y procesos para generar el reloj y el reset. Por otro lado, MEM.VHD es un sencillo modelo de memoria. Las pruebas se harán ejecutando un programa en el microprocesador, y viendo con el visor de formas de onda (Waveform Viewer) que los resultados son correctos. Por supuesto, también se puede modificar el código del testbench para observar estos resultados de una manera automatizada, pero esto no es necesario. Se deja al criterio del diseñador. Los pasos son los siguientes: • Crear el programa de pruebas, ensamblarlo y ejecutarlo en el ensamblador, y comprobar que en efecto funciona como se espera. • Renombrar el archivo .bin como prog.bin (es el nombre que espera MEM.VHD) y copiarlo en el directorio del proyecto de ModelSim • Ejecutar la simulación VHDL, y comprobar que los resultados son los esperados Esto es así porque la memoria se inicia a sí misma con los contenidos del archivo prog.bin, de tal manera que el microprocesador cuando empiece a leer de ella (a partir de la posición 0) comenzará a ejecutar el programa. Para comprobar que el microprocesador funciona correctamente, se recomienda hacer al menos dos pruebas: • • Una que ejecute varias veces cada instrucción, para ver que cada una por separado funciona bien Otra ya más completa, que sea ejecutar un algoritmo sencillo, para ver que programas completos funcionan bien. Un ejemplo típico de esto es la serie de Fibonacci, o multiplicar/dividir dos números, etc... Microprocesador básico vs microprocesador avanzado El ensamblador no sólo soporta las instrucciones básicas de ARC: • sethi, be, bcs, bneg, bvs, ba, call, jmpl, addcc, andcc, subcc, orcc, orncc, xorcc, srl Sino que soporta otras más: • add, sub, and, or, orn, xor, bne, bcc, bpos, bvc, sll, sra Los nuevos códigos de operación son: Instrucción add sub and or orn sll sra srl OP3 000000 000100 000001 000010 000110 100101 100111 100110 Instrucción bne bcc bpos bvc COND 01001 01101 01110 01111 Las instrucciones de salto en este caso son efectivas cuando su correspondiente flag es 0 (p.ej, bne salta cuando FLAG_Z es cero) Ejercicios • Ejercicio obligatorio: Implementar el microprocesador ARC, y probar su correcto funcionamiento. Se deberán entregar los siguientes archivos o ARC_TB.VHD o MEM.VHD o ARC.VHD o ALU.VHD o REGS.VHD o CTRL.VHD o REG-PC.VHD o Cualquier otro código VHDL que se haya desarrollado, aunque lo más habitual es que con los archivos anteriores sea suficiente para implementar ARC completamente. o Programa de prueba de todas las instrucciones, instr.asm o Programa de prueba de funcionalidad total, func.asm (serie de fibonacci, multiplicación de dos números, etc...) • Ejercicio optativo: Implementar ARC teniendo en cuenta para que pueda ejecutar el conjunto de instrucciones avanzado. Todos estos archivos se deben comprimir en un ZIP llamado FCO_4_Y_ZZ.ZIP (donde Y es el turno y Z es el grupo), que se debe dejar en el área de entrega de prácticas de la web de la Escuela, como se hizo con el resto de prácticas. PC_JUMP nRST PC_BRANCH CLK PC_CALL PC_UPDATE DATA_IN IR[21..0] & "0000000000" MEM_REQ RD_FROM_SETHI RD_FROM_SETHI IR[31..0] MEM_WRITE RD_FROM_MEM CTRL REGS_WE RD_FROM_MEM RD_ADDR REGS_WE CLK WE REGS RS1 ALU_FUNC ALU_IMM FLAG_N RD_IN RD_ADDR RD_ADDR RS1_ADDR IR[18..14] RS2_ADDR IR[4..0] RD_OUT FLAG_Z FLAGS_WE FLAG_V RESULT_FROM_PC FLAG_C RESULT_WE ADDR_FROM_PC IR_WE RS2 SXT(IR[12..0]) DATA_OUT DATA_IN ALU_IMM IR CLK OP1 OP2 ALU RES N Z FUNC V ALU_FUNC C FLAGS WE FLAGS_WE CLK PC_UPDATE FLAG_C FLAG_V FLAG_Z FLAG_N nRST JUMP_ADDR UPDATE nRST PC ADDR_FROM_PC JUMP BRANCH CALL PC RESULT_WE CLK IR[29..0] SHORT_DISP LONG_DISP CLK RESULT_FROM_PC ADDR IR_WE IR[31..0] IR[21..0] RESULT WE WE PC_JUMP PC_BRANCH PC_CALL