ZPU Evo(lution)

English


La ZPU es un microprocesador de 32 bits basado en pila, disenado originalmente por Øyvind Harboe de Zylin AS. La documentacion original se puede encontrar en el sitio web Zylin/OpenCore o Wikipedia. Es un microprocesador destinado a aplicaciones embebidas en FPGA con un uso minimo de elementos logicos y BRAM, a costa de la velocidad de ejecucion.

Zylin produjo dos disenos que hizo open source, a saber, las versiones Small y Medium de la ZPU. Disenos adicionales fueron producidos por desarrolladores externos como las variantes Flex y ZPUino, cada una ofreciendo mejoras al diseno original como interfaz Wishbone, rendimiento, etc.

Este documento describe otro diseno que denomino modelo ZPU Evo(lution), cuyo enfoque esta en el rendimiento, la conectividad y la expansion del conjunto de instrucciones. Esto surgio porque necesitaba una CPU para un emulador de un ordenador vintage que estoy desarrollando, que actuaria como procesador IO para proporcionar servicios de Menu, Perifericos y SD.

Un ejemplo del rendimiento de la ZPU Evo se puede observar usando CoreMark, que devuelve un valor de 22,2 a 100MHz en fabric Altera usando BRAM, y para Dhrystone 13,2 DMIPS. Las comparaciones pueden hacerse con los disenos ZPU originales en la galeria a continuacion, prestando atencion a la puntuacion CoreMark que parece ser el estandar de facto actual. La conectividad se manifiesta mediante la implementacion de buses de sistema y Wishbone, permitiendo la conexion de muchos dispositivos IP de codigo abierto. La expansion del conjunto de instrucciones se manifiesta mediante la inclusion de una cache L1 estrechamente acoplada donde se extraen multiples bytes de instrucciones y se ponen a disposicion de la CPU, que a su vez puede usarse para optimizacion (es decir, hasta 5 instrucciones IM ejecutadas en 1 ciclo) o para instrucciones multi-byte extendidas (es decir, implementacion de una instruccion LoaD Increment Repeat). Hay espacio para muchas mas mejoras como cache de pila, modo burst de SDRAM a L2, ejecucion paralela de instrucciones (es decir, and + neqbranch), que estan en mi lista.

La CPU

La ZPU Evo continua a partir de la ZPU Medium y Flex, y partes del codigo son similares, por ejemplo la decodificacion de instrucciones. Sin embargo, el diseno difiere debido al caching y la implementacion de un Memory Transaction Processor a traves del cual se encaminan todas las operaciones de Memoria/IO (excepto las lecturas directas de instrucciones si el bus de instrucciones de doble puerto esta habilitado). Las CPUs originales manejaban todos sus requisitos de memoria in situ o como parte de la maquina de estados, mientras que la Evo envia una solicitud al MXP cada vez que se requiere una operacion de memoria.

Las siguientes secciones indican algunas de las caracteristicas y cambios respecto a los disenos ZPU originales.

Estructura del bus

La ZPU tiene un espacio de direcciones lineal con todos los dispositivos de memoria e IO directamente direccionables dentro de este espacio. Los disenos ZPU existentes proporcionan un bus de sistema o un bus Wishbone, mientras que la Evo proporciona ambos. La ZPU Evo crea hasta dos regiones distintas dentro del espacio de direcciones segun la configuracion, para proporcionar un bus de sistema y un bus Wishbone.

Todos los modelos tienen el bus de sistema instanciado, que comienza en la direccion CPU 0 y se extiende hasta el limite impuesto por el bit de direccion maximo configurable (es decir, 0x000000 - 0xFFFFFF para 24 bits). Una region IO dedicada mapeada en memoria esta reservada en la parte superior del espacio de direcciones (es decir, 0xFF0000 - 0xFFFFFF).

Si se configura, un bus Wishbone puede ser instanciado, extendiendo el bit de direccion maximo en 1 (es decir, 0x1000000 - 0x1FFFFFF para el ejemplo de 24 bits). Esto crea efectivamente 2 regiones identicas, la inferior controlada mediante el bus de sistema, la superior mediante el bus Wishbone. Como en el bus de sistema, el area superior del espacio de direcciones Wishbone esta reservada para dispositivos IO.

Un tercer bus puede configurarse, destinado solo a lecturas de instrucciones. Este bus tipicamente refleja el bus de sistema en la region de memoria pero se considera conectado a memoria de acceso rapido para la lectura de instrucciones sin necesidad de Cache L2. Esto seria tipicamente el segundo puerto de un bloque BRAM de doble puerto con el primer puerto conectado al bus de sistema.

Cache L1

Para obtener rendimiento pero especialmente para optimizaciones de instrucciones e instrucciones extendidas, una cache L1 esta implementada usando registros. El uso de registros consume espacio de fabric y por lo tanto debe ser muy pequena, pero permite acceso aleatorio en un solo ciclo, necesario por ejemplo para compactar una carga IM de 32 bits (que puede ser de 5 instrucciones) en un solo ciclo. Tambien para instrucciones extendidas, el primer byte indica una instruccion extendida y los siguientes 1-5 bytes definen la instruccion que se ejecuta entonces en un solo ciclo.

Cache L2

La BRAM interna (Block RAM integrada dentro del FPGA) no necesita una Cache L2 ya que su tiempo de acceso es de 1-2 ciclos. Como la BRAM es un recurso limitado, se asume que se usara RAM externa o SDRAM, que es mucho mas lenta y necesita ser cacheada para aumentar el rendimiento. La Cache L2 se usa para este proposito, para leer anticipadamente un bloque de RAM externa y alimentar la Cache L1 segun sea necesario. Del analisis, los programas C generados por GCC son tipicamente bucles y llamadas dentro de un area local (a menos que se usen bibliotecas grandes), por lo que implementar una cache de mapeo directo simple entre RAM externa y BRAM (usada para la Cache L2) indexada relativamente al Program Counter es suficiente para evitar el bloqueo de la CPU en la mayoria de los casos.

Conjunto de instrucciones

Una caracteristica de la ZPU es su uso de un conjunto fijo minimo de instrucciones implementadas en hardware y un conjunto soft de instrucciones adicionales implementadas en pseudo-microcodigo (es decir, el conjunto fijo de instrucciones). Esto se logra mediante vectores de 32 bytes en la region 0x0000 - 0x0400 y cada instruccion soft se bifurca al vector si no esta implementada en hardware. El beneficio es la reduccion de recursos FPGA, pero la penalizacion es el rendimiento.

La ZPU Evo implementa todas las instrucciones en hardware, pero esto puede ajustarse en la configuracion para usar instrucciones soft si es necesario para conservar recursos FPGA. Esto permite un equilibrio entre recursos y rendimiento. Sin embargo, en ultima instancia, si los recursos son escasos, el uso de los modelos Small/Flex de la ZPU puede ser una mejor opcion.

Ademas de las instrucciones originales, existe un mecanismo para extender el conjunto de instrucciones usando instrucciones multi-byte con el formato:

Extend Instruction,<new insn[7:2]+ParamSize[1:0]>,[byte],[byte],[byte],[byte]

Donde ParamSize =

  • 00 - Sin bytes de parametro,
  • 01 - Parametro de 8 bits,
  • 10 - Parametro de 16 bits,
  • 11 - Parametro de 32 bits

Algunas instrucciones extendidas estan en desarrollo (es decir, LDIR); un valor de opcode exacto y un conjunto de instrucciones extendido aun no se han definido completamente. El ensamblador GNU AS se actualizara con estas instrucciones para que puedan invocarse dentro de un programa C y, eventualmente, si son beneficiosas para C, se migraran al compilador GCC (es decir, ADD32/DIV32/MULT32/LDIR/LDDR).

Conjunto de instrucciones implementado

La tabla completa del conjunto de instrucciones es identica a la version en ingles y esta documentada alli.

Tabla de comparacion de instrucciones implementadas

alt text

Hardware Variable Byte Write

En los disenos ZPU originales, habia margen pero no implementacion para permitir a la ZPU realizar escrituras de byte/media palabra/palabra completa. La CPU debia realizar operaciones siempre alineadas a palabra de 32 bits o ejecutar la operacion en microcodigo.

En la Evo, se ha implementado hardware (seleccionable en tiempo de build) para permitir escrituras de byte y media palabra, asi como operaciones hardware de Read-Update-Write. Si la logica hardware de byte/media palabra no esta activada, se recurre a la logica de Read-Update-Write de palabra de 32 bits. Ambos metodos tienen ventajas de rendimiento, siendo el ultimo 3 ciclos mas lento.

Hardware Debug Serializer

Para depurar la CPU o simplemente proporcionar informacion operativa interna de bajo nivel, se implementa un modulo de depuracion UART con buffer. Actualmente esta destinado solo a la salida, pero se conectara al IOCP para depuracion in-situ cuando la simulacion/Signal-Tap no este disponible.

Integradas en el RTL de la CPU hay instrucciones seleccionables activadas por nivel que envian informacion de instantanea al serializador. Las instrucciones se expanden, serializan y envian a un terminal conectado.

 
000477 01ffec 00001ae4 00000000 70.17 04770484 046c047c 08f0046c 0b848015 17700500 05000500 05001188 11ef2004

Break Point - Illegal instruction
PC          Stack    TOS          NOS           Insn     Signals      Signals       Signals       Signals      L1 Insn Q    L1 Insn Q    L1 Insn Q    L1 Insn Q

Toda la informacion critica como la instruccion actualmente en ejecucion (o no, si esta bloqueada), senales/flags, contenidos de cache L1/L2 y contenidos de memoria pueden ser emitidos como salida.

Restricciones de temporizado

Esto es un trabajo en progreso. El diseno se actualiza progresivamente y/o se anaden restricciones para que el temporizado se cumpla plenamente. Actualmente a 100MHz hay slack negativo, aunque el diseno es totalmente funcional. Esto se corregira en el futuro para que el temporizado analizado por TimeQuest se cumpla.

System On a Chip

Para proporcionar un marco de trabajo funcional en el que la ZPU Evo pueda ser utilizada, se ha creado un wrapper System On a Chip que permite la instanciacion de varios dispositivos (por ejemplo, UART/tarjeta SD).

Como parte del desarrollo, los modelos ZPU Small/Medium/Flex se han integrado en el marco, permitiendo la eleccion de la CPU cuando el espacio de fabric es limitado o cuando se comparan CPUs, aunque funcionalidades como Wishbone no estan disponibles en los modelos ZPU originales.

El SoC actualmente implementa (en el arbol de build):

Component Option Comment
CPU Yes ZPU Small, Medium, Flex, Evo or Evo Minimal.
Wishbone Bus Yes 32 bit Wishbone bus.
(SB) BRAM Yes Implement a configurable block of BRAM as the boot loader and stack.
Instruction Bus BRAM Yes Enable a separate bus (or Dual-Port) to the boot code implemented in BRAM.
(SB) RAM Yes Implement a block of BRAM as RAM, separate from the BRAM used for the boot loader/stack.
(SB) SDRAM Yes Implement an SDRAM controller on the system bus.
(WB) SDRAM Yes Implement an SDRAM controller over the Wishbone bus.
(WB) RAM Yes Implement a block of BRAM as RAM over the Wishbone bus.
(WB) I2C Yes Implements an I2C Controller over the Wishbone bus.
(SB) Timer 0 No Implements a hardware 12bit Second, 18bit milliSec and 24bit uSec down counter with interrupt, a 32bit milliSec up counter with interrupt and a YMD HMS Real Time Clock.
(SB) Timer 1 Yes A selectable number of pre-scaled 32bit down counters.
(SB) UART 0 No A cached UART used for monitor output and command input/program load.
(SB) UART 1 No A cached UART used for software (C program)/hardware (ZPU debug serializer) output.
(SB) Interrupt Controller Yes A prioritized configurable (# of inputs) interrupt controller.
(SB) PS2 Yes A PS2 Keyboard and Mouse controller.
(SB) SPI Yes A configurable number of Serial Peripheral Interface controllers.
(SB) SD Yes A configurable number of hardware based SPI SD controllers.
(SB) IOCTL Yes An IOCTL bus controller for MiSTer HPS-to-FPGA communication.
(SB) SOCCFG Yes A set of registers to indicate configuration of the ZPU and SoC to the controlling program.

Dentro de la configuracion del SoC, elementos como la direccion de inicio de la pila, el vector de reset, inicio/fin de IO (SB) y (WB) pueden establecerse. Con el bus Wishbone, es muy sencillo anadir dispositivos IP OpenCore adicionales; para el bus de sistema, puede requerirse algo de trabajo de adaptacion ya que los dispositivos IP OpenCore utilizan senales diferentes.

SDRAM

El SoC soporta SDRAM a traves de dos caminos independientes: un controlador de bus de sistema (SB) (SOC_IMPL_SDRAM) y un controlador de bus Wishbone (WB) (SOC_IMPL_WB_SDRAM). Ambos estan deshabilitados por defecto y pueden habilitarse independientemente. Los parametros de temporizado de SDRAM (tRCD, tRP, tRFC, tREF) y la geometria (Filas, Columnas, Bancos, Ancho de datos) son todos configurables en zpu_soc_pkg.vhd. El controlador WB SDRAM es una variante con cache y acceso burst y se recomienda cuando ambos buses estan activos y el ancho de banda de la RAM externa es critico.

Software

El software suministrado comprende:

  1. Un cargador de arranque, I/O Control Program (IOCP). Es mas que un cargador de arranque; en su forma basica puede arrancar una aplicacion desde una tarjeta SD, o puede incluir herramientas de monitor en linea de comandos y una funcion de carga serial.
  2. Una aplicacion, ZPUTA (ZPU Test Application). Es una suite de pruebas y puede organizarse como aplicacion unica o dividirse en un sistema operativo basado en disco donde todas las funciones se almacenan en la tarjeta SD. ZPUTA puede arrancarse mediante IOCP o funcionar de forma autonoma como unico programa en ROM/BRAM.
  3. Un sistema operativo basado en disco, zOS (ZPU Operating System). Una version de ZPUTA orientada al codigo de produccion, con todas las funciones como aplicaciones en disco.
  4. Funciones de biblioteca en C para apoyar el desarrollo de aplicaciones, incluyendo bibliotecas de terceros, por ejemplo FatFS de El. Chan.

21/04/2020: El software para la ZPU se ha unificado con el tranZPUter y se mantiene en el repositorio zSoft.

Configuracion

Esta seccion muestra como configurar la ZPU y el SoC, ya sea para usar la ZPU por separado o como parte del SoC suministrado.

Configurar la CPU


La CPU es configurable a traves del archivo de configuracion ‘cpu/zpu_pkg.vhd’. Define esencialmente el tamano del bus de direcciones y que funcionalidades de hardware habilitar. La siguiente tabla describe las opciones configurables.

   Configuration Variable Model Values Description
  EVO_USE_INSN_BUS Evo true/false Use a separate instruction bus to connect to the BRAM memory. All other Memory and I/O operations will go over the normal bus. This option is primarily used with Dual Port BRAM, one side connected to the Instruction Bus the other side to the standard bus and will give a significant performance boost when the executed code is in this memory.
  EVO_USE_HW_BYTE_WRITE Evo true/false This option implements hardware writing of bytes, reads are always 32bit and aligned.
  EVO_USE_HW_WORD_WRITE Evo true/false This option implements hardware writing of 16bit words, reads are always 32bit and aligned.
  EVO_USE_WB_BUS Evo true/false Implement the wishbone interface in addition to the system bus.
  DEBUG_CPU All true/false Enable CPU debugging output.
  DEBUG_LEVEL All 0 to 5 Level of debugging output. 0 = Basic, such as Breakpoint, 1 =+ Executing Instructions, 2 =+ L1 Cache contents, 3 =+ L2 Cache contents, 4 =+ Memory contents, 5=+ Everything else.
  DEBUG_MAX_TX_FIFO_BITS All 2 .. ~16 Size of UART TX Fifo for debug output.
  DEBUG_MAX_FIFO_BITS All 2 .. ~16 Size of debug output data records fifo.
  DEBUG_TX_BAUD_RATE All Any Baud integer value This option sets the output Baud rate of the debug serializer transmitter, ie. 115200
  maxAddrBit All <16..31n> + WB_ACTIVE This option sets the width of the address bus.

Configurar el SoC


El System on a Chip es configurable a traves del archivo de configuracion ‘zpu_soc_pkg.vhd’. Las tablas de configuracion completas para la seleccion de CPU, frecuencias, IDs de CPU, configuracion de cache, ajustes de dispositivos IO, opciones SoC, BRAM, RAM, SDRAM, Wishbone SDRAM, Instruction BRAM, inicializacion de CPU e instrucciones de hardware especificas de Evo son identicas a la version en ingles y se explican en detalle alli.

Build

Esta seccion muestra como realizar un build basico y asume que la placa de desarrollo de destino es la QMTECH Cyclone V board. Hay muchas opciones de configuracion, pero se tratan por separado.

La forma recomendada de construir la ZPU es el script de instalacion automatizada para su plataforma (vea Instalacion y build automatizados justo debajo) – instala toda la cadena de herramientas (Intel Quartus Prime Lite 17.1 y, cuando es necesario, Docker), clona el repositorio, le permite elegir la placa de destino y la variante de CPU, y construye el bitstream FPGA hasta completar, sin necesidad de conocimientos profundos de las herramientas. Los pasos manuales de Build del software y Build del bitstream RTL mas abajo son para usuarios avanzados y reconstrucciones parciales.


Instalacion y build automatizados (recomendado)

Si solo desea clonar el repositorio y producir un bitstream FPGA sin aprender toda la cadena de herramientas, utilice los scripts de instalacion incluidos. Instalan todo lo necesario (Intel Quartus Prime Lite 17.1 y, cuando es necesario, Docker), clonan el repositorio si aun no esta dentro de un checkout, le permiten elegir la placa de destino y la CPU, y construyen el bitstream hasta completar. Cada script es autonomo: copie solo el unico archivo para su plataforma y ejecutelo. Han sido verificados de extremo a extremo en Windows (nativo), macOS (Intel, via Docker) y Linux.
Script Plataforma Notas
setup_ZPU_windows.cmd Windows 10 / 11 Recomendado en Windows – lanzador de doble clic. Ejecuta la instalacion PowerShell, mantiene la ventana abierta y registra todo en setup_ZPU_log.txt. Instalacion nativa, sin WSL.
setup_ZPU_windows_native.ps1 Windows 10 / 11 El script PowerShell subyacente (ejecutelo directamente desde una ventana PowerShell abierta si lo prefiere). Instala Git for Windows + Quartus 17.1 mediante winget / el instalador de Intel.
setup_ZPU.sh Linux / macOS Quartus nativo en Linux (por defecto); una imagen Docker de Quartus sin interfaz grafica y autonoma en macOS (no existe Quartus nativo para macOS – solo Macs Intel, ya que Quartus es x86-64).
build_zpu.sh todas Wrapper de build portable usado por los scripts de instalacion. Reejecutelo en cualquier momento para reconstruir: ./build_zpu.sh [BOARD] [CPU].

Windows 10 / 11 (nativo, sin WSL) – haga doble clic en setup_ZPU_windows.cmd, o desde una ventana PowerShell abierta:

# opcional: construir un repo especifico (por defecto es el publico https://git.eaw.app/eaw/zpu.git)
$env:ZPU_REPO_URL = "https://git.eaw.app/eaw/zpu.git"
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\setup_ZPU_windows_native.ps1

El script usa winget para instalar Git for Windows (que proporciona el bash + coreutils que ejecutan build_zpu.sh) e instala Quartus 17.1 (~1,9 GB) de forma nativa en C:\altera\17.1 – sin WSL, Docker ni reinicio. La fase de copia de archivos de la instalacion de Quartus puede parecer congelada durante varios minutos pero no lo esta; dejela terminar.

macOS / Linux – desde un terminal:

chmod +x setup_ZPU.sh
./setup_ZPU.sh

En Linux esto instala Quartus de forma nativa en ~/altera/17.1. En un Mac Intel construye una imagen Docker de Quartus sin interfaz grafica (Docker Desktop debe estar instalado y en ejecucion) porque Intel no proporciona Quartus nativo para macOS.

Cada script pregunta que placa de desarrollo y variante de CPU construir (por defecto QMV / EVO) y, salvo que se ejecute dentro de un checkout, donde clonar el repositorio. Al finalizar tendra, en el directorio build/:

build/<BOARD>_<CPU>.sof     # bitstream de configuracion del FPGA (p. ej. build/E115_EVO.sof)
build/<BOARD>_<CPU>.rbf     # binario en bruto, comprimido (para HPS/u-boot o configuracion serial)
Placas y variantes de CPU
Placas CPUs
QMV (por defecto, QMTECH Cyclone V), DE10_nano, DE0_nano, E115 (Cyclone IV E EP4CE115), CYC1000 (Cyclone 10 LP) EVO (por defecto), EVO_MINIMAL, FLEX, MEDIUM, SMALL
Reconstruir mas tarde (sin volver a ejecutar la instalacion)

Una vez instalado Quartus, reejecute directamente el wrapper portable. En Windows Git Bash, ponga primero las herramientas de Quartus en el PATH:

# Solo en Windows Git-Bash:
export PATH="/c/altera/17.1/quartus/bin64":$PATH

./build_zpu.sh                    # por defecto -> QMV_EVO
./build_zpu.sh E115 EVO           # placa + CPU
./build_zpu.sh QMV_EVO            # forma combinada BOARD_CPU
./build_zpu.sh --list             # lista todas las placas / CPUs
Variables de entorno utiles (todas opcionales)
Variable Proposito Valor por defecto
ZPU_REPO_URL Repositorio a clonar https://git.eaw.app/eaw/zpu.git (publico)
ZPU_BOARD / ZPU_CPU Omitir las preguntas de placa / CPU (pregunta)
ZPU_RTL_METHOD Forzar el metodo de Quartus: native o docker native en Linux/Windows, docker en macOS
ZPU_DIR Construir en un checkout existente en lugar de clonar (clona)
ZPU_QUARTUS_BIN Apuntar a un bin / bin64 de Quartus existente autodeteccion

Por ejemplo, para forzar la imagen Docker sin interfaz grafica en cualquier host, u omitir las preguntas:

ZPU_RTL_METHOD=docker ./build_zpu.sh E115 EVO     # fuerza la imagen Docker sin interfaz grafica (zpu-quartus:17.1)
ZPU_BOARD=E115 ZPU_CPU=EVO ./setup_ZPU.sh         # omite las preguntas de placa/CPU

El resto de esta seccion documenta el build manual subyacente para quienes prefieren ejecutar las herramientas a mano.


Build del software

Jenkins puede utilizarse para automatizar el build, pero para una compilacion sencilla utilice el script build.sh y el sistema jerarquico de Makefile siguiendo las instrucciones basicas a continuacion.

  1. Descargar e instalar la ZPU GCC ToolChain. Instalar en /opt o un directorio comun similar.
  2. Configurar la ruta de la variable de entorno.
     export PATH=$PATH:/opt/zpu/bin
    
  3. Clonar el repositorio ZPU Evo.
  4. Editar el archivo <zpu evo dir>/software/zputa/zputa.h y seleccionar las funciones a incorporar en la imagen core ZPUTA (por defecto, todas las funciones se construyen como applets, pero se ignoran cuando estan incorporadas en la imagen core ZPUTA). Se selecciona una funcion estableciendo BUILTIN_ a '1'; establecer a '0' si no se desea incorporarla.
  5. Decidir que mapa de memoria se desea y si ZPUTA debe ser una aplicacion o un cargador de arranque, e introducir el comando de build.
     cd <zpu evo dir>/software
     ./build.sh -I 3 -O zputa -o 2 -B 0x1000 -A 0xC000
    
  6. Insertar una tarjeta SD en el sistema, formatearla como FAT32 y copiar los archivos en ella.
     cd build/SD
     cp -r * <abs path to SD card, ie. /media/psmart/ZPU>
    


Build del bitstream RTL

Para crear el bitstream del FPGA (conversion de HDL a un mapa de configuracion para el FPGA), hay dos metodos:

  1. Instalar Intel Quartus Prime 17.1 o posterior.
  2. Abrir Quartus Prime y cargar el proyecto (File -> Open Project) y seleccionar <zpu evo dir>/build/QMV_zpu.qpf
  3. Compilar (Processing -> Start)

    alternativamente:-

  1. Instalar Intel Quartus Prime 17.1 o posterior.
  2. Utilizar el sistema de build Makefile con los siguientes comandos.
     cd <zpu evo dir>/build
     make QMV_EVO
    

Las instrucciones de build para ZPU Small, Medium, Flex y Evo y las notas sobre la configuracion de una nueva placa de desarrollo se encuentran en la version en ingles de este documento.