scieee AI-readable full text Open interactive document viewer

Implementación de un procesador RISC-V en una FPGA de tipo ARTIX-7. Dotar al procesador de mecanismos de cifrado de memoria.

Guaitoune Akdi, Badr; Ledesma Ventura, Daniel

Abstract

A medida que avance el tiempo y aparezcan más memorias RAM no volátiles, es decir, aquellas memorias que, aunque se les interrumpa el flujo de corriente no pierden la información grabada en ellas, al contrario de lo que pasa en las memorias volátiles, será posible el análisis de los datos grabados en ellas, incluyendo aquellos datos de carácter privado como contraseñas o datos bancarios. El objetivo del proyecto es dotar al procesador RISC-V de los mecanismos adecuados para que los datos en memoria se encuentren cifrados imposibilitando dicho análisis, para ello, manejaremos un encriptado a través de registros mediados por claves generadas aleatoriamente. Para ello, hemos tenido que adquirir unos conocimientos previos, como entender la arquitectura interna del RISC-V, ver sus componentes y como se comunican y entender la gestión que hace de la memoria interna con sus señales.

Full text

- 1 - IMPLEMENTACIÓN DE UN PROCESADOR RISC-V EN UNA FPGA DE TIPO ARTIX-7. DOTAR AL PROCESADOR DE MECANISMOS DE CIFRADO DE MEMORIA. RISC-V PROCCESSOR IMPLEMENTATION OVER FPGA AND MEMORY ENCRYPTION TRABAJO FIN DE GRADO CURSO 2020-2021 AUTORES: BADR GUAITOUNE AKDI DANIEL LEDESMA VENTURA DIRECTORES: JUAN CARLOS FABERO JIMÉNEZ KATZALIN OLCOZ HERRERO GRADO EN INGENIERÍA DE COMPUTADORES FACULTAD DE INFORMÁTICA UNIVERSIDAD COMPLUTENSE DE MADRID - 2 - IMPLEMENTACIÓN DE UN PROCESADOR RISC-V EN UNA FPGA DE TIPO ARTIX-7. DOTAR AL PROCESADOR DE MECANISMOS DE CIFRADO DE MEMORIA RISC-V PROCCESSOR IMPLEMENTATION OVER FPGA AND MEMORY ENCRYPTION TRABAJO DE FIN DE GRADO EN INGENIERÍA DE COMPUTADORES DEPARTAMENTO ARQUITECTURA DE COMPUTADORES Y AUTOMÁTICA AUTORES: BADR GUAITOUNE AKDI DANIEL LEDESMA VENTURA DIRECTORES: JUAN CARLOS FABERO JIMÉNEZ KATZALIN OLCOZ HERRERO CONVOCATORIA: JUNIO CALIFICACIÓN: GRADO EN INGENIERÍA DE COMPUTADORES FACULTAD DE INFORMÁTICA UNIVERSIDAD COMPLUTENSE DE MADRID 1 DE JUNIO - 3 - - 4 - AGRADECIMIENTOS Nos gustaría agradecer tanto a familia, compañeros, como amigos que nos han apoyado a lo largo de esta travesía que es estudiar. Así como a Juan Carlos Fabero Y Katzalin Olcoz que nos han ayudado resolviendo las dudas que nos han ido surgiendo durante la elaboración de este TFG. - 5 - - 6 - ÍNDICE DE CONTENIDOS Índice de contenidos ................................................................................................................. - 6 - Índice de figuras ....................................................................................................................... - 9 - Índice de tablas ...................................................................................................................... - 10 - Resumen ............................................................................................................................... - 11 - Abstract ................................................................................................................................ - 12 - Capítulo 1 - Introducción ....................................................................................................... - 13 - 1.1 Motivación ...................................................................................................................... - 13 - 1.2 Objetivos ......................................................................................................................... - 14 - 1.3 Plan de Trabajo ............................................................................................................... - 15 - Capítulo 2 - Introduction..................................................................................................... - 17 - 2.1 Motivation ....................................................................................................................... - 17 - 2.2 Objectives ....................................................................................................................... - 18 - 2.3 Work Plan ....................................................................................................................... - 19 - Capítulo 3 - Estado de la cuestión. ........................................................................................... - 21 - 3.1 Memorias no volátiles ..................................................................................................... - 21 - 3.2 AUGE de RISC-V .......................................................................................................... - 21 - 3.3 Nexys 4 DDR. ................................................................................................................. - 22 - Capítulo 4 - Herramientas utilizadas .................................................................................. - 23 - 4.1 Instalación VSCode ........................................................................................................ - 23 - 4.1.1 VSCode .................................................................................................................... - 23 - 4.1.2 PlatformIO ............................................................................................................... - 25 - 4.2 Instalación Vivado .......................................................................................................... - 27 - - 7 - 4.3 Leds y Switches ejecutado en SoC-SweRVolf-Nexys4DDR ......................................... - 31 - 4.3.1 Código ...................................................................................................................... - 31 - 4.3.2 Compilado, Volcado y Ejecución ............................................................................ - 32 - Capítulo 5 - Conocimientos previos ......................................................................................... - 36 - 5.1 RISC-V ........................................................................................................................... - 36 - 5.2 SweRV EH1 .................................................................................................................... - 37 - 5.2.1 Características .......................................................................................................... - 37 - 5.2.2 Núcleo ...................................................................................................................... - 38 - 5.3 SwerRVolf SoC .............................................................................................................. - 39 - 5.3.1 Versión 0.7 ............................................................................................................... - 39 - 5.3.2 Características .......................................................................................................... - 40 - 5.3.3 Mapa de Memoria .................................................................................................... - 41 - 5.4 BUS AXI ......................................................................................................................... - 42 - 5.4.1 Características .......................................................................................................... - 42 - 5.4.2 Arquitectura ............................................................................................................. - 42 - 5.4.3 Read ......................................................................................................................... - 46 - 5.4.4 Write ........................................................................................................................ - 48 - Capítulo 6 - Diseño y Desarrollo ............................................................................................. - 50 - 6.1 Módulo de Cifrado Combinacional (Puertas NOT) ........................................................ - 50 - 6.1.1 “Linker “ .................................................................................................................. - 50 - 6.1.2 “Litedram_top.v” ..................................................................................................... - 53 - 6.1.3 Resultado .................................................................................................................. - 55 - 6.2 Módulo de Cifrado Secuencial(“Pipeline”) .................................................................... - 56 - 6.2.1 “Pipeline” ................................................................................................................. - 57 - - 8 - 6.2.2 “Pipeline” de Cifrado o “Cypherpipe” ..................................................................... - 60 - 6.2.3 Resultado .................................................................................................................. - 62 - Capítulo 7 - Conclusiones y trabajo futuro ....................................................................... - 73 - 7.1 Conclusiones ................................................................................................................... - 73 - 7.2 Trabajo Futuro ................................................................................................................ - 73 - Capítulo 8 - Conclusions and future work ......................................................................... - 75 - 8.1 Conclusions ..................................................................................................................... - 75 - 8.2 Future Work .................................................................................................................... - 75 - Capítulo 9 - Aportaciones .................................................................................................. - 77 - 9.1 Daniel Ledesma Ventura ................................................................................................ - 77 - 9.2 Badr Guaitoune Akdi ...................................................................................................... - 78 - Capítulo 10 - Bibliografía..................................................................................................... - 80 - - 9 - ÍNDICE DE FIGURAS Figura 1: VSCode Parte 1 ......................................................................................................................... - 23 - Figura 2: VSCode Parte 2 ......................................................................................................................... - 24 - Figura 3: VSCode Parte 3 ......................................................................................................................... - 24 - Figura 4: PlatformIO Parte 1 ................................................................................................................... - 25 - Figura 5: PlatformIO Parte 2 ................................................................................................................... - 25 - Figura 6: PlatformIO Parte 3 ................................................................................................................... - 26 - Figura 7: PlatformIO Parte 4 ................................................................................................................... - 26 - Figura 8: PlatformIO Parte 5 ................................................................................................................... - 26 - Figura 9: PlatformIO Parte 6 ................................................................................................................... - 27 - Figura 10: Vivado Parte 1 ........................................................................................................................ - 28 - Figura 11: Vivado Parte 2 ........................................................................................................................ - 28 - Figura 12: Vivado Parte 3 ........................................................................................................................ - 29 - Figura 13: Vivado Parte 4 ........................................................................................................................ - 29 - Figura 14: Vivado Parte 5 ........................................................................................................................ - 30 - Figura 15: Vivado Parte 6 ........................................................................................................................ - 30 - Figura 16: Vivado Parte 7 ........................................................................................................................ - 30 - Figura 17: Volcado 1................................................................................................................................ - 32 - Figura 18. Volcado 2 ................................................................................................................................ - 33 - Figura 19. Ejecución 1 ............................................................................................................................. - 33 - Figura 20: Placa Nexys ............................................................................................................................ - 34 - Figura 21: SweRV EH1 Núcleo ................................................................................................................. - 38 - Figura 22: SweRVolf SoC ......................................................................................................................... - 39 - Figura 23: AXI, Read1 .............................................................................................................................. - 46 - Figura 24: AXI, Read2 .............................................................................................................................. - 46 - Figura 25: Write 1 ................................................................................................................................... - 48 - Figura 26: AXI, Write2 ............................................................................................................................. - 48 - Figura 27. Secciones RAM ....................................................................................................................... - 51 - Figura 28: Resultado ............................................................................................................................... - 56 - Figura 29: Testbench Cifrado .................................................................................................................. - 59 - Figura 30. Testbench Cifrado 2 ............................................................................................................... - 62 - Figura 31: Ejecución Cifrado ................................................................................................................... - 68 - Figura 32: Resultados Cifrado ................................................................................................................. - 69 - - 16 - - 17 - Capítulo 2 - Introduction The fundamental pillars on which this work is based are the FPGA’s and the RISC-V. In 1984 Ross Freeman and Bernard Vonderschmitt invent FPGA’s as an evolution of CPLD. FPGA’S architecture is based on little blocks that reproduce logic operations and the interconexion freedom between those blocks, provide the FPGA of a big flexibility and versatility. Another of the most important points to keep in mind, is the reprogramming capacity allowing a wide use ranging from controllers, encoders…right up to the VLSI circuit prototyping. In 2010 Berkeley University decided to found their own ISA, so as to stop relying on commercial repertories. Five years later, RISC_V Foundation was founded, with the goal of supervise and control the ISA developed by UB. SwerRVolf is a SoC developed by CHIP-ALLIANCE, implemented in verilog. This SoC provided with the core EH1 SweRV Core, it is developed under the RISC-V (ISA) specification and prepared for dumping on a FPGA-Artix7. 2.1 Motivation In the new information age, we are increasingly faced with the need to use encryption mechanisms to keep certain data safe. Although there is a wide range of tools to carry out the encryption, they are mostly software with the inconvenience that if the password is cracked all the data becomes automatically exposed. Given this situation, the major tech firms have decided to add to their processors a hardware dedicated to the security and data encryption. - 18 - In the market, we can find processors with these features, such as the AMD ZEN that have an ARM coprocessor called “AMD Secure Processor”, which provides a safe environment to prevent vulnerabilities and theft of information. [1] 2.2 Objectives The final goal is to provide the RISC-V processor with memory encryption mechanisms, but, to do this, we have had to set some previous objectives as we were advancing in the achievement of the project. • Preparation of the work environment, installation of Vivado and Visual Studio Code. • Adaptation to the differences between Verilog and VHDL. • Understand the RISC-V architecture. • Understand the initial code, see its structure, and how it is organized. • In-depth study of the AXI4 BUS. • Distinguish signals to be treated for read/write encryption. • Development of the encryption module. • Keep a version control, so as not to lose detail of the modifications made. • Performance, posible errors and system outputs (both for read and write) check. • Write the TFG report. - 19 - 2.3 Work Plan The work has been divided into several stages, • Information search, as well as documentation of EH1 SweRV Core, FPGA-Artix7, and the AXI communication protocol. • Code analysis and SoC implementation to understand the architecture used between the core and the memory controller. • Design and development of multi-stage encryption. • Implementation and delivery of results on FPGA. • Correction of errors. • Preparation of the report. - 20 - - 21 - Capítulo 3 - Estado de la cuestión. En este capítulo se procede a explicar cómo se encuentra el área de investigación de las memorias no volátiles, así como el auge de RISC-V. 3.1 Memorias no volátiles Como bien hemos comentado antes, las memorias no volátiles son aquellas que cuando dejan de ser alimentadas por la corriente eléctrica no pierden los datos que tienen guardados al contrario que las volátiles. Es por este motivo, de la importancia que radica en este tipo de memorias no volátiles, lo que cambia ahora es que hay memorias no volátiles que se usan para memoria principal, como las flash. Sólo se necesitará de un flujo constante de corriente eléctrica para realizar la escritura o lectura en la memoria. Algunas de las memorias no volátiles más conocidas son el CD, CD-ROM, el DVD, los discos duros… 3.2 AUGE de RISC-V El auge de RISC-V se debe a varios factores importantes. Uno de estos factores es su rasgo de identidad más importante “Open Source”, que proporciona la posibilidad de adquirir IP’s avanzadas sin necesidad de pagarlas, reduciendo con ello el riesgo y la inversión inicial para el desarrollo de hardware. [2] Otra característica a tener en cuenta y de gran importancia es su repertorio de instrucciones o ISA, al basarse en RISC (Reduced Instruction Set Computer), facilita el desarrollo software, ahora si añadimos su bajo coste y consumo reducido se ha convertido en una de las mejores alternativas para el IoT (Internet de las cosas). - 22 - 3.3 Nexys 4 DDR. Es la placa sobre la que volcamos nuestro proyecto y con las que hemos trabajado a lo largo del TFG. Esta placa ha sido proporcionada por la Facultad de Informática de la Universidad Complutense de Madrid exclusivamente para la realización del TFG. Dicha placa (Nexys 4 DDR) se trata de una FPGA programable preparada para utilizarse (ready-to-use) de la familia Artix-7 creada por Xilinx. Algunas de sus principales características son que posee casi dieciséis mil slices lógicos, cada uno con LUTs de seis entradas y ocho flip-flops, una DDR2 de ciento veintiocho MB, seis cuadros de manejo de reloj, cada uno con un PLL (phase-locked loop), conversor analógicodigital, y elementos hardware como leds, switches, sensor de temperatura o conector USB. Para consultar las especificaciones más en detalle o consultar el manual de referencia, aconsejamos visitar la web que proporciona ambos. [3] [4] Para testear el funcionamiento de la placa, hemos realizado pruebas con elementos hardware de la FPGA como los leds. Internamente, más centrados en la ejecución de las pruebas que nos interesan, hemos probado metiendo datos no cifrados y sacarlos cifrados para comprobar que funciona bien el cifrado e introducir datos encriptados y sacarlos desencriptados para ver, así como funciona tanto la encriptación como la desencriptación. - 23 - Capítulo 4 - Herramientas utilizadas A continuación, empezamos con un manual de instalación de los programas utilizados para tener el entorno de desarrollo listo. 4.1 Instalación VSCode Una de las herramientas utilizadas para trabajar es Visual Studio Code, herramienta muy útil con una amplia variedad de opciones y extensiones para trabajar en distintos entornos, mediante su extensión PlatformIO, para instalarlas deberemos de seguir los siguientes pasos: 4.1.1 VSCode Para empezar, deberemos entrar al siguiente enlace https://code.visualstudio.com/Download y descargar la versión acorde según las especificaciones de nuestro sistema. Para empezar, para instalarlo en el sistema operativo WINDOWS estos son los pasos: Una vez abierto el .exe descargado, deberemos aceptar el acuerdo de licencia. Figura 1: VSCode Parte 1 - 24 - Acto seguido, deberemos agregar al PATH del sistema el programa, y opcionalmente, si lo deseamos, podemos añadir al escritorio un acceso directo para que nos sea más cómodo abrir la aplicación. Finalmente, nos saldrá el menú con las opciones elegidas y seleccionamos Instalar para completar el proceso de descarga Figura 2: VSCode Parte 2 Figura 3: VSCode Parte 3 - 25 - En cambio, en el sistema operativo LINUX, para instalarlo, bastará con abrir un terminal e insertar lo siguiente en la línea de comandos: sudo snap install --classic code # or code-insiders Una vez en este punto, en ambos sistemas los siguientes pasos son iguales. 4.1.2 PlatformIO Una vez tengamos instalado Visual Studio Code, procederemos a instalar la extensión PlatformIO. El primer paso es seleccionar la opción de extensiones en el menú situado en la parte izquierda del programa. Acto seguido, nos aparece una barra de búsqueda, escribimos “PlatformIO”, seleccionamos el que aparece en la figura y le damos a “Install” para proceder a instalarlo. Extensiones Figura 4: PlatformIO Parte 1 Figura 5: PlatformIO Parte 2 - 32 - 4.3.2 Compilado, Volcado y Ejecución El IDE PlatformIO nos permite de forma cómoda realizar la compilación del archivo “LedsSwitches.S” que contiene el código mostrado anteriormente, también tiene integrado la herramienta de depuración “PIO Debug” que nos será útil para acceder a direcciones de memoria y ver los datos de la sección “.data”. 4.3.2.1 Volcado del Bitstream: Antes de poder ejecutar el código debemos volcar en la placa en la placa el SoCSweRVolf que está contenido en el archivo con extensión “.bit” generado previamente por Vivado. Para ello tenemos que añadirlo, accedemos a nuestro proyecto y buscamos el archivo de configuración platformio.io. Figura 17: Volcado 1 En el punto “board_build.bitstream_file = path” introducción el path donde se aloja nuestro bitstream. Acto seguido hacemos “click” en el icono de PlatformIO, abrimos la carpeta Platform y finalmente hacemos “click” en la opción “Upload Bitstream”. - 33 - Figura 18. Volcado 2 4.3.2.2 Ejecución del Código sobre el RISC-V: Para poder ejecutar el código lo único que tenemos que hacer es “click” en la pestaña “Run and Debug” de PlatformIO y acto seguido “Start Debugging”. Figura 19. Ejecución 1 - 34 - Como vemos en la imagen, el código se ha ejecutado perfectamente, le hemos puesto un “break” en el inicio para poder inspeccionar la memoria y confirmar que se han volcado los datos de la sección “.data”. La plataforma nos da una serie de datos que tenemos que tener en cuenta para el desarrollo del TFG, el archivo “.ld”, que ejecuta por defecto pone la sección “.data” después de la sección “.text”, esto tenemos que cambiarlo ya que nuestra sección “.data” tiene que ser fija para poder encriptarla. También vemos que las direcciones no empiezan en la 0X0000 por lo que suponemos en la parte inicial de la RAM se guarda algún código por defecto que no podemos tocar y evitar cualquier conflicto con nuestra sección de datos. 4.3.2.3 Placa Nexys 4 en ejecución. Visión de la ejecución del programa en la placa: Figura 20: Placa Nexys - 35 - - 36 - Capítulo 5 - Conocimientos previos En este capítulo procedemos a explicar los conceptos necesarios para poder entender el funcionamiento del proyecto desarrollado, así como el propio desarrollo del Trabajo de Fin de Grado. 5.1 RISC-V RISC-V es una arquitectura diseñada bajo un conjunto de instrucciones o ISA de tipo RISC (Reduced Instruction Set Computer) de código abierto. Podemos encontrar 4 repertorios de instrucciones correspondientes a esta arquitectura RV32, RV32E, RV64I, RV128I que son conocidas como ISA base, para las cuales existe una gran variedad de extensiones estandarizadas que pueden funcionar con todas las bases sin generar ningún conflicto. Extensiones: M, Un, F, F, G, Q, L, C, B, J, J, P, P, N En el TFG nos centraremos en RV32IMC, siendo esta la arquitectura de nuestro Core. • RV32I: Instrucciones enteras-base, 32-bits. • M: Extensión estándar para Multiplicación de Entero y División. • C: Extensión estándar para instrucciones comprimidas. - 37 - - 37 - 5.2 SweRV EH1 SweRV EH1 es un núcleo de CPU (“Central Processing Unit”) de 32-bits en “Mmode” (modo máquina) que puede soportar extensiones de RISC-V I(“Integer”), C (“Compressed Instruction”), M (“Multiplication and Division”). 5.2.1 Características Las principales características son: • RV32 (IMC) RISC-V con predictor de saltos. • Closely-Coupled opcional en memoria de datos e instrucciones con protección ECC. • Cache de datos asociativa de vías opcional con bit de paridad o protección ECC. • Controlador de interrupciones programable que soporta hasta 255 interrupciones externas. • Cuatro interfaces de bus para la búsqueda de instrucciones, acceso a datos, acceso a “debug” y al DMA externo y a memorias Closely-Coupled (configurables 64-bit AXI4 o AHB-LITE). • Unidad de “debug” del núcleo con la especificación de RISC-V. • Frecuencia de 1GHz (para tecnología de 28nm). - 38 - - 38 - 5.2.2 Núcleo El núcleo SweRV EH1 es una CPU de 32-bits sencillo (Figura 21), superescalar con doble emisión(“dual-issue”), de instrucciones, 2 tuberías(“pipeline”) de 9-etapas capaces de soportar 4 unidades aritmeticológicas (ALU’s) en ambas tuberías. Una vía del “pipeline” soporta la lógica de “loads/stores” mientras la otra dispone de un multiplicador de 3 ciclos de latencia. [5] Por último, este procesador también está dotado de un divisor fuera del “pipeline” cuya latencia es de 34-ciclos. Ref:LinkImagen Figura 21: SweRV EH1 Núcleo - 39 - - 39 - 5.3 SwerRVolf SoC SwerRVolf es un SoC (“System on Chip”) basado en FuseSoc (gestor de paquetes y herramienta de construcción HDL). Ha sido desarrollado por la empresa Chipallianse y se distribuye a través del repertorio publico https://github.com/chipsalliance/Cores-SweRVolf. SwerRVolf cuenta con dos versiones, una versión base distribuida por Chipallianse y otra extendida (la 0.7) que ha sido suministrada por la Universidad Complutense de Madrid, sobre la que se desarrolla nuestro proyecto. 5.3.1 Versión 0.7 Esta versión contiene todas las características que venían integradas en el SweRV EH1 Core Complex(Figura 18 sin color rojo) que son la Boot-ROM, System-Ctrl, SPI1 y la UART. Para la versión 0.7 le han añadido el SPI2, TIMER y una GPIO sencilla para la comunicación con el exterior (Figura 22 color rojo). Ref: LinkImagen2 Figura 22: SweRVolf SoC - 40 - - 40 - 5.3.2 Características Las principales características son: ▪ System-Ctrl: El controlador del sistema tiene la funcionalidad de mantener el registro con la versión del SoC, el estado de la RAM, el timer del core... [6] ▪ SPI: Dispone de dos controladores SPI obtenidos de [7] cuyos registros SPI_SPCR, SPI_SPSR, SPI_SPDR, SPI_SPER, SPI_SPS están mapeados entre las direcciones 0x80001040 - 0x8000107F y 0x80001100 -0x8000113F. ▪ Timer: Temporizador obtenido de [8] cuyos registros están mapeados entre las direcciones 0x80001200 - 0x800012FF. ▪ UART: Un controlador de UART obtenido de [9] cuyos registros están mapeados entre las direcciones 0x80002000 - 0x80002FFF. ▪ Boot ROM: Contiene un first-stage bootloader, que después de reiniciar el sistema, el SoC empezará a leer instrucciones desde aquí cuyo mapeo de memoria está entre las direcciones 0x80000000 -0x80000FFF. ▪ RAM: Aunque no contiene un controlador de memoria reserva los primeros 128MB del mapa de memoria (0x00000000-0x07FFFFFF) y lo expone a través de un bus AXI. - 41 - - 41 - 5.3.3 Mapa de Memoria System Address Boot ROM 0x80000000 - 0x80000FFF System Controller 0x80001000 - 0x8000103F SPI1 0x80001040 - 0x8000107F SPI2 0x80001100 - 0x8000113F Timer 0x80001200 - 0x8000123F GPIO 0x80001400 - 0x8000143F UART 0x80002000 - 0x80002FFF RAM 0x00000000-0x07FFFFFF Tabla 1. Mapa Memoria SweRVolf - 48 - - 48 - 5.4.4 Write Como podemos observar (Figura 25) en el proceso de escritura el maestro inicia la transacción de información estableciendo la dirección de escritura, el dato a escribir y las señales de control correspondientes. Acto seguido el esclavo transmite una respuesta de confirmación Un ejemplo más detallado de lo que podría ser una lectura en AXI se muestra en la Figura 26 Figura 26: AXI, Write2 Figura 25: Write 1 - 49 - - 49 - Primer Canal: “Write Address Channel” ➢ La fuente (maestro en este caso) proporciona la dirección de lectura ARADDR. ⮚ La señal de control ARSIZE que indica cuantos bytes se quieren leer. ⮚ La señal de control ARLEN que indica la longitud de la ráfaga, si es de tipo INCR. ⮚ La señal de control ARBURST que indica el tipo de ráfaga FIXED, INCR. La fuente mantiene la información del canal hasta que se completa el mecanismo de control Handshake AW (VALID) / AW (READY) durante un ciclo. Segundo Canal: “Write Data Channel” ⮚ La fuente (en este caso el maestro) escribe los datos correspondientes en el bus WDADA. ⮚ La señal de control RLAST indica cuando la última trama de datos enviada es la última. La fuente mantiene la información en el canal hasta que se completa el mecanismo de control Handshake W (VALID)/W (READY). Tercer Canal: “Write Response Channel” ⮚ La señal de control RRESP indica el estado de la transferencia. La fuente (en este caso el esclavo) mantiene la información en el canal hasta que se completa el mecanismo de control Handshake B (VALID)/B (READY). Gran parte de la información recabada en este documento proviene de la documentación de la RVFPGA, sus citas son: Página web de referencia: [11] Infosheet que contiene la información del curso de Rvfpga en el que indica como registrarte para acceder a los recursos del proyecto para descargarse el datasheet utilizado del que hemos recabado información en este apartado: [12] - 50 - - 50 - Capítulo 6 - Diseño y Desarrollo En este capítulo procedemos a explicar el flujo de diseño y desarrollo del mecanismo de cifrado integrado en nuestro SoC, en él destacamos un desarrollo secuencial en el que se parte de modelos más sencillos, para que la integración con el SoC sea más sencilla y directa. 6.1 Módulo de Cifrado Combinacional (Puertas NOT) Primeros diseñaremos un módulo que sea capaz de invertir los valores en memoria de una sección específica, en nuestro caso la sección “.data”. 6.1.1 “Linker “ El “linker”, es un script de enlazado y nos proporciona la capacidad de definir las secciones en las se divide nuestro programa. Destacamos las siguientes secciones: ▪ “.text”: Sección de instrucciones. ▪ “.data”: Sección de datos. ▪ “.rodata”: Sección de constantes. ▪ “.bss”: Sección de variables globales no inicializadas. Para crear nuestro propio “Linker” partimos de un ejecutable generado a partir de un script de enlazado genérico, que viene con las herramientas de PlatformIO. Este ejecutable(“.elf”) ya se encuentra divido en secciones y PlatformIO nos proporciona otra herramienta para poder leer este tipo de archivos - “readelf”. Ejecutamos la herramienta: a. PIO guarda los “.elf” en el “path = “proyecto/.pio/build/rvfpga/ firmware.elf”. b. La herramienta “readelf” se encuentra en “/.platformio/packages/toolchainriscv/riscv64-unknown-elf/bin”. c. Ejecutamos el comando “readelf -S path”. - 51 - - 51 - Como podemos observar (Figura 27) en las primeras posiciones de la RAM se guarda información del sistema dentro de secciones que se crean automáticamente. Si observamos la sección “. shstrtab” vemos que empieza en la dirección 0x00000 con un desplazamiento (“offset”) de 425 bytes y una ocupación(“size”) de 79 bytes, corresponde con la sección más alejada al inicio de RAM, por lo que a partir de aquí podemos empezar a definir nuestras secciones. Figura 27. Secciones RAM Hemos decidido dejar un pequeño margen y empezar en la dirección 0x00001000. En el script de enlazado (“.ld”) definimos dos secciones de memoria, una que permita las operaciones de leer(“r”) y escribir(“w”) que corresponderá con la sección “.data”, y la otra permite las operaciones de (“r”), (“w”) y ejecutar(“x”). - 52 - - 52 - 6.1.1.1 Código OUTPUT_ARCH( "riscv" ) ENTRY(_start) MEMORY { ram_data (rw) : org = 0x00001000, len = 0x00000FFF ram_trb (rwx) : org = 0x00002000, len = 0x07FFDFFF } SECTIONS { .data : { *(.data) } > ram_data .text : { *(.text) } > ram_trb .rodata : { *(.rodata) } > ram_trb .bss : { *(.bss) } > ram_trb .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } .debug 0 : { *(.debug) } .line 0 : { *(.line) } .debug_srcinfo 0 : { *(.debug_srcinfo) } .debug_sfnames 0 : { *(.debug_sfnames) } .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } - 53 - - 53 - .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } .debug_weaknames 0 : { *(.debug_weaknames) } .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } 6.1.2 “Litedram_top.v” El archivo “litedram_top.v” es donde tenemos que añadir toda nuestra lógica de cifrado y gestión de señales de control del BUS-AXI. En él observamos las señales correspondientes a los 5 canales que explicamos anteriormente, que se conectan a la CPU mediante un “axi_cdc_intf” (“A clock domain crossing on AXI interface, es decir, un cruce de dominio de reloj en la interfaz AXI”) que se encarga de sincronizar los relojes de la CPU y la “ddram”. El diseño de este módulo consiste en invertir todos bits del bus de datos cuando el valor de las direcciones sobre las que se va a operar se encuentra dentro del intervalo de nuestra sección “.data”. - 54 - - 54 - 6.1.2.1 Ejecución: 1. Localizamos las señales sobre las que vamos a operar y definimos nuevas auxiliares que nos permitan desviar el flujo hacia nuestro módulo de cifrado combinacional. //AXI write address channel input wire [31:0] i_awaddr, //AXI write data channel input wire [63:0] i_wdata, //AXI read address channel input wire [31:0] i_araddr, //AXI read data channel output wire [63:0] o_rdata, wire [63:0] aux_i_wdata; wire [63:0] aux_o_rdata; wire aux_o_ready; 2. Diseñamos un módulo en verilog que compruebe si la dirección se encuentra en el rango deseado y si es así la salida sea el bus invertido (operación NOT). module encrypt ( input wire i_ready, input wire [31:0] i_addr, input wire [63:0] i_data, output wire [63:0] o_data ); wire[63:0] data_encrypt; - 55 - - 55 - assign data_encrypt [63:32] = i_data[63:32]; assign data_encrypt [31:0] = ~i_data[31:0]; assign o_data = (i_addr >= 32'h00001000) && (i_addr <= 32'h00001FFF) && (i_ready == 1'd1)? data_encrypt: i_data; endmodule 3. Instanciamos el módulo en “litedram_top.v” y en la instancia de “litedram_core ldc” cambiamos ciertas señales para desviar el flujo. encrypt data_i (.i_ready(1'd1), .i_addr(i_awaddr), .i_data(i_wdata), .o_data(aux_ i_wdata)); //encrypt data_o (.i_ready(aux_o_ready), .i_addr(i_araddr), .i_data(aux_o_rdata), .o_data(o_rdata)); Dejamos la desencriptación comentada para poder observan en tiempo de ejecución, si la memoria se ha encriptado correctamente o no. 6.1.3 Resultado Como vemos (Figura 28) la memoria se ha cifrado correctamente invirtiendo los valores de las dos variables de la sección data: ● “num1”: valor instancia – “d.word” 0xFF : valor memoria – “d.word” 0x00FFFF ● “num2”: valor instancia – “d.word” 0x11 : valor memoria – “d.word” 0xEEFFFF - 56 - - 56 - Figura 28: Resultado 6.2 Módulo de Cifrado Secuencial(“Pipeline”) Para dotar al procesador de un cifrado coherente, es decir, que cumpla con cierto grado de complejidad su función y mantenga los datos en memora a salvo, es necesario equiparlo con un módulo que sea capaz de realizar varias operaciones sobre el bus datos de forma secuencial (en más de un ciclo de reloj). En lo referente al diseño, nuestro objetivo es crear el mecanismo para poder integrar algoritmos de cifrado más complejos, que por lo general requieren de varios ciclos de trabajo. Hemos dividido el trabajo en varios puntos: 1. Diseño de un “pipeline” para poder retrasar las señales de control, los ciclos necesarios requeridos por el algoritmo de cifrado. 2. Diseño de un algoritmo de cifrado genérico, sencillo pero que tarde varios ciclos en ejecutarse para poder probar la solución. 3. Integración de los módulos de cifrado con el resto del SoC. - 57 - - 57 - 6.2.1 “Pipeline” En el diseño del “pipeline” hay que tener en cuenta que, en el protocolo AXI el receptor de la información no siempre está preparado para recibir datos, por lo que tenemos que dotar al “pipe” de un mecanismo de parada con el fin de sincronizarlo con el receptor. 6.2.1.1 Ejecución: 1. Diseño del “pipe” con una señal de control “ready” que lo para. module pipeline #(parameter STAGES = 3, WIDTH = 32, INIT = 0 ) ( input wire ready, input wire user_clk, input wire user_rst, input wire [WIDTH-1:0] i_pipe, output wire [WIDTH-1:0] o_pipe ); reg [WIDTH-1:0] pipe [STAGES-1:0]; always @ (posedge user_clk or posedge user_rst) begin if (user_rst) pipe[0] <= INIT; else if(ready == 1) pipe[0] <= i_pipe; end assign o_pipe = pipe[STAGES-1]; generate - 64 - - 64 - 2. Instanciamos el “cypherpipe” y el “pipeline” para el canal de escritura (“Write Data Channel”). cypherpipe #( .STAGES(3), .WIDTH(32), .INIT(0) ) data_i ( .size(awsize_w), .ready(1), .user_clk(user_clk), .user_rst(user_rst), .i_pipe( (addr_w == 1'b1) && (i_wvalid == 1) ? i_wdata : 0), .o_pipe(ip_wdata) ); pipeline #( .STAGES(3), .WIDTH(1), .INIT(0) ) wvalid_i ( .ready(1), .user_clk(user_clk), .user_rst(user_rst), .i_pipe( (addr_w == 1'b1) && (i_wvalid == 1) ? i_wvalid : 0), .o_pipe(ip_wvalid) ); pipeline #( .STAGES(3), .WIDTH(8), .INIT(0) ) wstrb_i ( .ready(1), .user_clk(user_clk), .user_rst(user_rst), .i_pipe( (addr_w == 1'b1) && (i_wvalid == 1) ? i_wstrb : 0), .o_pipe(ip_wstrb) - 65 - - 65 - ); pipeline #( .STAGES(3), .WIDTH(1), .INIT(0) ) wlast_i ( .ready(1), .user_clk(user_clk), .user_rst(user_rst), .i_pipe( (addr_w == 1'b1) && (i_wvalid == 1) ? i_wlast : 0), .o_pipe(ip_wlast) ); “Ready” tiene valor constante 1 porque en este caso la memoria siempre está lista para escribir. 3. Instanciamos el “pipeline” para el canal de lectura (“Read Data Channel”). pipeline #( .STAGES(3), .WIDTH(32), .INIT(0) ) data_opp ( .ready(ip_ready), .user_clk(user_clk), .user_rst(user_rst), .i_pipe((addr_r == 1'b1) && (op_rvalid == 1) ? op_rdata[31:0] : 0), .o_pipe(opp_rdata) ); pipeline #( .STAGES(3), .WIDTH(ID_WIDTH), .INIT(0) ) rid_o ( .ready(ip_ready), .user_clk(user_clk), .user_rst(user_rst), - 66 - - 66 - .i_pipe((addr_r == 1'b1) && (op_rvalid == 1) ? op_rid : 0), .o_pipe(opp_rid) ); pipeline #( .STAGES(3), .WIDTH(2), .INIT(0) ) rresp_o ( .ready(ip_ready), .user_clk(user_clk), .user_rst(user_rst), .i_pipe((addr_r == 1'b1) && (op_rvalid == 1) ? op_rresp : 0), .o_pipe(opp_rresp) ); pipeline #( .STAGES(3), .WIDTH(1), .INIT(0) ) rlast_o( .ready(ip_ready), .user_clk(user_clk), .user_rst(user_rst), .i_pipe((addr_r == 1'b1) && (op_rvalid == 1) ? op_rlast : 0), .o_pipe(opp_rlast) ); pipeline #( .STAGES(3), .WIDTH(1), .INIT(0) ) rvalid_o ( .ready(ip_ready), .user_clk(user_clk), .user_rst(user_rst), .i_pipe((addr_r == 1'b1) && (op_rvalid == 1) ? op_rvalid : 0), .o_pipe(opp_rvalid) ); - 67 - - 67 - 4. Generamos dos multiplexores de control, para desviar los datos y las señales por el “pipe” solo cuando haga falta, es decir, cuando las direcciones de memoria coincidan con nuestra sección “.data”. always @ * begin if ((i_awaddr >= 32'h00001000) && (i_awaddr <= 32'h00001FFF) && i_awvalid == 1) begin addr_w <= 1'b1; awsize_w <= i_awsize; end else if( i_awvalid == 1 ) begin addr_w <= 1'b0; end end always @ * begin if ((i_araddr >= 32'h00001000) && (i_araddr <= 32'h00001FFF) &&i_arvalid == 1) begin addr_r <= 1'b1; arsize_r <= i_arsize; end else if( i_arvalid == 1 ) begin addr_r <= 1'b0; end end 5. En la instancia del controlador (“litedram_core ldc”) de memoria modificamos algunas señales. .user_port_axi_0_wdata ((addr_w == 1'b1)? ip_wdata : i_wdata ), .user_port_axi_0_wstrb ((addr_w == 1'b1)? ip_wstrb : i_wstrb ), .user_port_axi_0_wlast ((addr_w == 1'b1)? ip_wlast : i_wlast ), .user_port_axi_0_wvalid ((addr_w == 1'b1)? ip_wvalid : i_wvalid), .user_port_axi_0_wready (op_wready), .user_port_axi_0_rdata (op_rdata), - 68 - - 68 - .user_port_axi_0_rresp (op_rresp), .user_port_axi_0_rlast (op_rlast), .user_port_axi_0_rid (op_rid), .user_port_axi_0_rvalid (op_rvalid), .user_port_axi_0_rready (ip_ready) 6. Por último, para conectar las señales de salida en la lectura añadimos. assign o_rdata = (addr_r == 1'b1) ? {op_rdata[63:32],opp_rdata} : op_rdata; assign o_rid = (addr_r == 1'b1) ? opp_rid : op_rid; assign o_rresp = (addr_r == 1'b1) ? opp_rresp : op_rresp; assign o_rlast = (addr_r == 1'b1) ? opp_rlast : op_rlast; assign o_rvalid = (addr_r == 1'b1) ? opp_rvalid : op_rvalid; Resultado: Como vemos (Figura 31) se cifran los datos correctamente y la ejecución se lleva a cabo de forma correcta. Figura 31: Ejecución Cifrado Hay que tener en cuenta que al contrario que en el cifrado combinacional ahora solo se cifran los bytes que se escriben y no el bus completo. - 69 - - 69 - 6.2.3.2 Cypherpipe” en Lectura y “Cypherpipe” en Escritura Para integrar el “cypherpipe” en lectura lo único que tenemos que hacer es modificar la siguiente línea. cypherpipe #( .STAGES(3), .WIDTH(32), .INIT(0) ) data_opp ( .size(i_arsize), .ready(ip_ready), .user_clk(user_clk), .user_rst(user_rst), .i_pipe((addr_r == 1'b1) && (op_rvalid == 1) ? op_rdata[31:0] : 0), .o_pipe(opp_rdata) ); Cambiamos el “pipeline” del bus de datos por el “cypherpipe” en la operación de lectura. Resultado: Como vemos (Figura32) los datos salen en el formato original porque ahora se cifran y descifran. Figura 32: Resultados Cifrado - 70 - - 70 - Aunque el funcionamiento a simple vista parece correcto, solo funciona en modo “debug” esto es ejecutando el código paso a paso. Cuando ejecutamos el código sin ningún “break” se produce una excepción interna y el registro pc se reinicia. A simple vista parece que tenemos un problema de retardos en la operación de lectura a través del “cypherpipe”. Para asegurarnos de que así vamos a realizar una serie de pruebas para confirmar que se produce un error en el “Timing Setup” ya que las herramientas de Xilinx no nos proporcionan ninguna pista sobre lo que está ocurriendo. Las pruebas que vamos a realizar son: 1. Reducimos la lógica de cifrado a un clave contante sin registros. 2. Reducimos la operación de “xor” a solo el bit menos significativo. 3. Acotamos la dirección de memoria para cifrar un solo dato. Al realizar las pruebas descritas anteriormente nos dimos cuenta que al añadir cualquier tipo de operación sobre el canal de lectura producía este error ya que si instanciamos el pipeline normal funcionaba correctamente. Para solucionar este error decidimos modificar la frecuencia de reloj de la “ddram”. En el módulo “litedram_core.v” se generan los relojes del SoC pero nos encontramos un dificultad añadida, pudimos localizar el “use_clk” que es el reloj de 100MHz del sistema, pero la “ddram” genera tres relojes más y ante la falta de documentación al respecto cualquier modificación de estos seria al azar. Código: PLLE2_ADV #( .CLKFBOUT_MULT(5'd16), .CLKIN1_PERIOD(10.0), .CLKOUT0_DIVIDE(5'd16), .CLKOUT0_PHASE(1'd0), .CLKOUT1_DIVIDE(4'd8), .CLKOUT1_PHASE(1'd0), .CLKOUT2_DIVIDE(4'd8), .CLKOUT2_PHASE(7'd90), - 71 - - 71 - .DIVCLK_DIVIDE(1'd1), .REF_JITTER1(0.01), .STARTUP_WAIT("FALSE") ) PLLE2_ADV ( .CLKFBIN(vns_pll_fb0), .CLKIN1(soc_s7pll0_clkin), .RST(soc_sys_pll_reset), .CLKFBOUT(vns_pll_fb0), .CLKOUT0(soc_s7pll0_clkout0), //user_clk .CLKOUT1(soc_s7pll0_clkout1), .CLKOUT2(soc_s7pll0_clkout2), .LOCKED(soc_sys_pll_locked) ); PLLE2_ADV #( .CLKFBOUT_MULT(5'd16), .CLKIN1_PERIOD(10.0), .CLKOUT0_DIVIDE(4'd8), .CLKOUT0_PHASE(1'd0), .DIVCLK_DIVIDE(1'd1), .REF_JITTER1(0.01), .STARTUP_WAIT("FALSE") ) PLLE2_ADV_1 ( .CLKFBIN(vns_pll_fb1), .CLKIN1(soc_s7pll1_clkin), .RST(soc_iodelay_pll_reset), .CLKFBOUT(vns_pll_fb1), .CLKOUT0(soc_s7pll1_clkout), .LOCKED(soc_iodelay_pll_locked) ); - 72 - - 72 - La única alternativa que tenemos en este caso es modificar la frecuencia de reloj de la CPU que se instancia en el módulo “clk_gen_nexys.v”, que recibe una frecuencia de 100Mhz y genera una frecuencia de 50Mhz. Código: PLLE2_BASE #(.BANDWIDTH("OPTIMIZED"), .CLKFBOUT_MULT(16), .CLKIN1_PERIOD(10.0), //100MHz .CLKOUT0_DIVIDE(32), .DIVCLK_DIVIDE(1), .STARTUP_WAIT("FALSE")) PLLE2_BASE_inst (.CLKOUT0(o_clk_core), .CLKOUT1(), .CLKOUT2(), .CLKOUT3(), .CLKOUT4(), .CLKOUT5(), .CLKFBOUT(clkfb), .LOCKED(locked), .CLKIN1(i_clk), .PWRDWN(1'b0), .RST(i_rst), .CLKFBIN(clkfb)); La modificación es sencilla, se cambia el factor de multiplicación CLKFBOUT_MULT (16) a CLKFBOUT_MULT (8), generando una frecuencia de reloj de 25Mhz. Y así conseguimos que todo funcione correctamente. - 73 - - 73 - Capítulo 7 - Conclusiones y trabajo futuro 7.1 Conclusiones Finalmente, después de realizar todo el proyecto y la documentación de este hemos sacado las siguientes conclusiones. Primero: hemos aprendido todos los conceptos necesarios para poder trabajar en este proyecto, desde entender la arquitectura RISC-V, pues hemos conseguido entender la distribución del código fuente, los distintos módulos que contiene, los relojes, comunicación CPU-Memoria, etc. Hay que tener en cuenta que esta ha sido la parte más complicada, ya que el código verilog no está documentado ni comentado. Segundo: también hay que destacar los conocimientos obtenidos sobre el bus AXI, necesario para la comunicación entre la CPU y el Core. Tercero: En tercer lugar, en el proyecto hemos podido aprender verilog, generación de testbenchs para simular nuestro diseño, las herramientas como PlatformIO que generan un entorno cómodo de trabajo, la lectura de documentación oficial… Por último, hay que destacar la necesidad de documentar el desarrollo de cualquier proyecto. Añadiendo comentarios si es código, y explicaciones en caso de ser la lógica del proyecto ya que como hemos visto en este caso ahorra gran cantidad de tiempo y facilita el trabajo en grupo. 7.2 Trabajo Futuro En nuestro proyecto nos hemos centrado especialmente en desarrollar el mecanismo que permita integrar algoritmos de cifrados complejos en el SoC. En nuestro caso el algoritmo de cifrado es muy sencillo, un buen trabajo de futuro seria cambiar nuestro cifrado por un algoritmo más efectivo como DES, RSA. Por último, un aspecto importante a mejorar es la generación de números aleatorios y la velocidad de la CPU, para el primero se puede añadir hardware especializado como TrueRNG v3, para la velocidad se podría intentar reducir el reloj de la memoria ddram en vez del reloj de la CPU. - 80 - - 80 - Capítulo 10 - Bibliografía [1] Hardzone, «Funcionamiento AMD,» [En línea]. Available: https://hardzone.es/reportajes/quees/amd-psp-procesador/. [2] RISC-V, «RISC-V,» [En línea]. Available: https://riscv.org/. [3] D. Reference, «Manual Referencia Nexys 4 DDR,» [En línea]. Available: https://reference.digilentinc.com/_media/reference/programmable-logic/nexys-4ddr/nexys4ddr_rm.pdf. [4] D. Reference, «Nexys 4 DDR,» [En línea]. Available: https://reference.digilentinc.com/programmable-logic/nexys-4-ddr/reference-manual. [5] ChipsAlliance, «SweRV EH1 Core,» [En línea]. Available: https://github.com/chipsalliance/CoresSweRV/blob/master/docs/RISC-V_SweRV_EH1_PRM.pdf. [6] C. Alliance, «Repositorio Github,» [En línea]. Available: https://github.com/chipsalliance/CoresSweRVolf. [7] OpenCores, «SPI Core,» [En línea]. Available: https://opencores.org/projects/simple_spi. [8] O. Cores, «PWM/Timer,» [En línea]. Available: https://opencores.org/projects/ptc. [9] O. Cores, «UART,» [En línea]. Available: https://opencores.org/projects/uart16550. [10] ARM, «AXI-4 Bus,» [En línea]. Available: http://www.gstitt.ece.ufl.edu/courses/fall15/eel4720_5721/labs/refs/AXI4_specification.pdf. [11] I. U. Programme, «RVfpga: Understanding Computer Architecture,» [En línea]. Available: https://university.imgtec.com/rvfpga/. [12] I. U. Programme, «RVFPGA: The Complete Course in Understanding Computer Architecture,» [En línea]. Available: https://cdn2.imgtec.com/iup-downloads/IUP_INFOSHEET_RVfpga_English.pdf.