Este artículo se centra en aprender cómo se diseña un núcleo de microcontrolador, y está destinado solo para uso educativo. Por favor visite www.zilog.com y compruebe la línea de productos del fabricante para seleccionar un microcontrolador que se ajuste a las necesidades de su proyecto (¡desde Bises Z8 de ocho bits! y eZ80 elogia al ZNEO32 basado en ARM Cortex-M3 de 32 bits! que incluye capacidades avanzadas de control del motor).
Mi historia de amor con microcontroladores y microprocesadores comenzó en 1988 cuando estaba trabajando para obtener un título técnico en CEFET-PR (una escuela secundaria/técnica y universidad brasileña de cuatro años ubicada en Curitiba). Comencé aprendiendo los conceptos básicos mientras exploraba el clásico Zilog Z-80 (Figura 1a).
FIGURA 1A. El Zilog Z-80A (cortesía de Wikimedia Commons).
Avance rápido a través de una carrera de programación que incluyó la autoría de algunos libros sobre programación de microcontroladores (ver Recursos), comenzar una pequeña casa de diseño (ScTec) y terminar un programa de postgrado en CEFET-SC (otra universidad brasileña ubicada en Florianópolis). Esto fue en 2008, cuando tuve más contacto con la lógica programable y el VHDL y mi curiosidad alcanzó su punto máximo. Años más tarde, en 2016, encontré un kit de FPGA (Matriz de Compuerta Programable en Campo) muy asequible y decidí darle una oportunidad, y comencé a aprender más sobre la tecnología FPGA.
¿Qué sería mejor que diseñar un núcleo blando para aprender más sobre VHDL (lenguaje de descripción de hardware VHSIC), FPGA y núcleos de microprocesador? Terminé eligiendo un pariente moderno de la Z-80: ¡el Zilog Z8 Encore! (alias eZ8; figura 1b).
FIGURA 1B. Zilog eZ8.
Es un núcleo de microcontrolador de ocho bits con un conjunto de instrucciones simple pero potente y un depurador en chip muy agradable. Con su ligero entorno de desarrollo integrado (IDE) y compilador ANSI C gratuito, es un excelente proyecto para aprender (y también enseñar) sobre sistemas embebidos.
Antes de sumergirnos en las profundidades de la operación principal, VHDL y FPGAs, echemos un vistazo al Zilog Z8 Encore. función.
FIGURA 1C. FPz8 en un FPGA.
¡Zilog Z8 Encore!
El eZ8 es una familia de microcontroladores de ocho bits basada en la exitosa familia Z8 de Zilog y en la gran herencia del Z-80. Cuenta con una máquina Harvard CISC con hasta 4.096 bytes de RAM (registro de archivos y área de registros de funciones especiales), hasta 64 KB de memoria de programa (generalmente memoria Flash) y hasta 64 KB de memoria de datos (RAM). El núcleo eZ8 también incluye un controlador de interrupciones vectorizado con prioridad programable y un depurador en chip que se comunica con el ordenador host mediante comunicación en serie asíncrona. Estos microcontroladores están equipados con un conjunto de periféricos muy agradable, que van desde temporizadores versátiles de 16 bits hasta temporizadores de control de motor, desde múltiples UARTs (listos para IrDA) hasta dispositivos USB y mucho más (visita www.zilog.com para comprobar la línea completa de productos).
Una característica importante del modelo de programación eZ8 es la falta de un acumulador fijo. En su lugar, cualquiera de las 4096 direcciones RAM posibles puede funcionar como acumuladores. La CPU trata su RAM principal (el archivo y el área de registros de funciones especiales SFRs) como un gran conjunto de registros de CPU. Para lograrlo, la RAM se divide en grupos de registros (hay 256 grupos de 16 registros de trabajo cada uno). Una instrucción generalmente funciona dentro de un solo grupo de registro de trabajo, que es seleccionado por un SFR llamado RP (puntero de registro). Tenga en cuenta que todos los SFR se encuentran en la última página de la RAM (direcciones que van desde 0xF00 hasta 0xFFF).
Con respecto al conjunto de instrucciones, hay 83 instrucciones diferentes divididas en dos páginas de código operativo. Comprende instrucciones habituales para operaciones básicas como suma, resta, operaciones lógicas, instrucciones de manipulación de datos, instrucciones de desplazamiento, instrucciones de cambio de flujo, algunas instrucciones de 16 bits, pruebas y manipulación de bits, multiplicación de 8×8, etc.
El área de memoria del programa está organizada de manera que las primeras direcciones se dediquen a fines especiales. Las direcciones 0x0000 y 0x0001 están dedicadas a las opciones de configuración; las direcciones 0x0002 y 0x0003 almacenan el vector de restablecimiento, y así sucesivamente. La tabla 1 muestra la organización de la memoria del programa.
0x0000 | Option bytes |
0x0002 | Reset vector |
0x0004 | WDT vector |
0x0006 | Illegal instruction vector |
0x0008 to 0x0037 | Interrupt vectors |
0x0038 to 0xFFFF | User program memory area |
TABLE 1. Simplified program memory organization.
Algunos dispositivos también incluyen un segundo espacio de datos (hasta 65.536 direcciones) al que solo se puede acceder mediante instrucciones LDE/LDEI. Esta área se puede usar para almacenar datos menos utilizados (ya que leer/escribir en ella es más lento que el área de RAM/SFR).
FPz8
La primera implementación de FPz8 utiliza un enfoque de diseño muy conservador y cableado con dos buses principales: uno para memoria de programa y otro para memoria de registro. Como elegí no incluir un área de memoria de datos, las instrucciones LDE / LDEI no se implementan.
Los buses de memoria de programa comprenden un bus de direcciones de instrucciones (IAB) de 16 bits, un bus de datos de instrucciones de ocho bits (IDB para leer datos de la memoria del programa), un bus de datos de escritura de instrucciones de ocho bits (IWDB para escribir datos en la memoria del programa) y una señal PGM_WR que controla la escritura en la memoria del programa. FPz8 incluye 16.384 bytes de memoria de programa implementados utilizando memoria RAM de bloque síncrono (lo que significa que el contenido de la memoria de programa se pierde cuando el dispositivo se apaga).
Los cinco buses de área de registro comprenden tres para el área de registro de archivos (RAM de usuario) y otros dos dedicados a registros de funciones especiales. Hay un bus principal de direcciones de registro de archivos de 12 bits (FRAB), un bus de datos de entrada de registro de archivos de ocho bits (FRIDB), un bus de datos de salida de registro de archivos de ocho bits (FRODB), un bus de datos de entrada de registro de ocho bits (RIDB) y, finalmente, un bus de datos de salida de registro de ocho bits (RODB) para escribir en SFRs. La FPz8 incluye 2.048 bytes de memoria RAM de usuario implementada utilizando memoria RAM de bloque síncrono.
La Figura 2 muestra un diagrama de bloques de la FPz8; puede ver la CPU, dos unidades de memoria (una para el almacenamiento de programas y la otra para el almacenamiento de datos), y también un módulo de temporizador externo.
FIGURA 2. Diagrama de bloques FPz8.
Tenga en cuenta que no estoy utilizando autobuses bidireccionales para ninguna interconexión en este proyecto. Los autobuses unidireccionales son más fáciles de usar, aunque son menos eficientes en cuanto al espacio.
La descripción VHDL del FPz8 es grande y un poco compleja, por lo que voy a dividir su funcionamiento en algunos módulos para facilitar la comprensión:
- Motor de cola de instrucciones
- Decodificación de instrucciones
- Procesamiento de interrupciones
- Depurador
Motor de cola de instrucciones
Obtener instrucciones es una tarea principal para cualquier CPU. La arquitectura Harvard del FPz8 permite la obtención simultánea y el acceso a datos (debido a buses separados para instrucciones y datos). Eso significa que la CPU puede obtener una nueva instrucción mientras otra está leyendo o escribiendo en la memoria de datos.
El eZ8 tiene una palabra de instrucción de longitud variable (la longitud de la instrucción varía de un byte a cinco bytes); algunas instrucciones son largas, pero se ejecutan más rápido que otras. De esta manera, una instrucción BRK tiene una longitud de un byte y se ejecuta en dos ciclos,mientras que una LDX IM, ER1 tiene cuatro bytes de largo y se ejecuta en dos ciclos de reloj.
Entonces, ¿cómo podemos decodificar con éxito todas estas instrucciones? Con una cola de instrucciones, es decir, un mecanismo que sigue obteniendo bytes de la memoria del programa y almacenándolos en una matriz de ocho bytes:
if (CAN_FETCH= ‘1’) y luego
if (IQUEUE.FETCH_STATE=F_ADDR) luego
FETCH_ADDR: = PC;
IAB <= PC;
IQUEUE.WRPOS: = 0;
IQUEUE.RDPOS: = 0;
IQUEUE.CNT := 0;
IQUEUE.FETCH_STATE: = F_READ;
else
if (IQUEUE.FULL= ‘0’) luego
IQUEUE.COLA (IQUEUE.WRPOS): = IDB;
FETCH_ADDR: = FETCH_ADDR + 1;
IAB <= FETCH_ADDR;
IQUEUE.WRPOS: = IQUEUE.WRPOS + 1;
IQUEUE.CNT: = IQUEUE.CNT + 1;
end if;
end if;
end if;
if (IQUEUE.CNT = 7) luego IQUEUE.FULL: = ‘1’; else IQUEUE.FULL: = ‘0’;
end if;
LISTADO 1. Motor de cola de instrucciones.
La obtención se controla mediante una señal de activación principal (CAN_FETCH) que se puede desactivar en algunos casos especiales (procesamiento de interrupciones, instrucciones LDC/LDCI o acceso al depurador). También hay una estructura (IQUEUE) que almacena varios parámetros internos (estado de obtención, punteros de lectura y escritura, matriz de cola en sí, un contador y un indicador completo).
El contador de colas (CNT) se utiliza para identificar el número de bytes disponibles para su uso (lectura) en la cola. La etapa de decodificador utiliza este número para verificar que el número deseado de bytes para la instrucción ya está disponible en la cola.
Decodificación de instrucciones
Aquí es donde ocurre la magia real. El decodificador de instrucciones lee los opcodes de la cola de instrucciones y los traduce en las operaciones correspondientes.
El diseño del decodificador de instrucciones comenzó calculando la relación entre todas las instrucciones y los modos de direccionamiento. A primera vista, es fácil ver que algunas instrucciones (Figura 3) están agrupadas por columna (DJNZ, JR cc,X, LD r1,IM, JP cc,DA e INC r1). Decodificar una instrucción INC r1 es simple: En estas instrucciones de un solo byte, el mordisco alto especifica el registro de origen/destino y el mordisco inferior especifica la instrucción en sí (0xE).
FIGURA 3. Códigos de operación por grupos.
La mayoría de las instrucciones se pueden clasificar de acuerdo con algunas reglas básicas:
- Las columnas (el mordisco inferior de un código operativo) generalmente especifican un modo de direccionamiento: Las instrucciones de la columna 0x9, por ejemplo, usan principalmente el modo de direccionamiento IM,ER1 y tienen cuatro bytes de longitud (el segundo byte es el operando inmediato y los dos últimos bytes son la dirección extendida de destino).
- Las filas (el mordisco más alto de un código operativo) generalmente especifican una operación: Las instrucciones de fila 0x0 son principalmente operaciones de suma; las instrucciones de fila 0x2 son principalmente operaciones de resta, y así sucesivamente.
Si miramos la fila 0x1, podemos ver que las columnas 0x0 y 0x1 son instrucciones RLC, y las columnas 0x2 hasta 0x9 son instrucciones ADC. Por lo tanto, podemos diseñar un ALU que tome un mordisco como entrada (el mordisco más alto del código operativo) y lo decodifique en consecuencia. Si bien esto funcionaría para las columnas 0x2 a 0x9, necesitaríamos otro enfoque para las dos primeras columnas.
Es por eso que terminé escribiendo dos unidades: una ALU que se concentra en la mayoría de las instrucciones aritméticas y lógicas; y una segunda unidad (Unidad Lógica 2, o LU2) que realiza otras operaciones mostradas en las columnas 0x0 y 0x1 (no todas las operaciones que se ven en esas columnas son realizadas por LU2). Los códigos de operación para ALU y LU2 se eligieron para que coincidieran con las filas de opcode que se muestran en la Figura 3.
Otro detalle importante es que todas las instrucciones dentro de la misma columna y grupo tienen el mismo tamaño en bytes, por lo que se pueden decodificar en la misma sección del decodificador.
El diseño del decodificador hace uso de una gran máquina de estados finitos (FSM) que avanza en cada tick de reloj. Cada instrucción comienza en la estadística CPU_DECOD. Aquí es donde el decodificador realmente decodifica los opcodes, prepara buses y señales de soporte internas, y pasos hacia otros estados de ejecución. Entre todos esos estados, dos son ampliamente utilizados por muchas instrucciones: CPU_OMA y CPU_OMA2. ¿Puedes adivinar por qué? Si lo has dicho porque están relacionados con ALU y LU2, ¡tienes toda la razón!
OMA es la abreviatura de One Memory Access y es el último estado para todas las instrucciones relacionadas con ALU (ADD, ADC, ADDX, ADCX, SUB, SBC, SUBX, SBCX, OR, ORX, AND, ANDX, XOR, XORX, CP, CPC, TCM, TCMX, TM, TMX, y algunas variantes de LD y LDX). Por otro lado, CPU_OMA2 es el último estado para todas las instrucciones relacionadas con LU2 (RLC, INC, DEC, DA, COM, RL, CLR, RRC, SRA, SRL, RR y SWAP).
Ahora, echemos un vistazo al estado CPU_DECOD. Consulte la Figura 4.
FIGURA 4. CPU_DECOD estado.
Dentro del estado CPU_DECOD, podemos ver que se lleva a cabo mucha acción. Al principio, algunas variables temporales se inicializan a una condición predeterminada. Tenga en cuenta que NUM_BYTES es muy importante, ya que controla cuántos bytes consumió el decodificador de instrucciones. Su valor se utiliza en la última parte de esta etapa para incrementar el PC (contador de programas), avanzar el puntero de lectura de la cola y disminuir el número de bytes disponibles en la cola.
Siguiendo la sección de inicialización, podemos ver la sección de procesamiento de interrupciones. Es responsable de detectar cualquier interrupción pendiente y prepara la CPU en consecuencia. Cubriré esto en la siguiente sección.
El bloque de decodificación de instrucciones real comprueba si un modo de bajo consumo no está activo y también si el modo depurador está desactivado (OCDCR.DBGMODE = 0). O bien, mientras estaba en modo de depuración, se emitió un comando de depuración de un solo paso (OCDCR.DBGMODE = 1 y TOC.SINGLE_STEP = 1). A continuación, comprueba los bytes disponibles en la cola y procede con la decodificación.
Algunas instrucciones (principalmente las de un solo bit) se completan dentro del estado CPU_DECOD, mientras que otras necesitan varios estados hasta que se completen por completo.
Tenga en cuenta que alguna decodificación de instrucciones puede hacer uso de varias funciones y procedimientos escritos especialmente para FPz8:
- DATAWRITE-Este procedimiento prepara buses para una operación de escritura. Selecciona si el destino es un SFR interno, un SFR externo o una ubicación de RAM de usuario.
- DATAREAD – Esta es una función recíproca para DATAWRITE. Se utiliza para leer una dirección de origen y elige automáticamente si se trata de un SFR interno, un SFR externo o una ubicación de RAM de usuario.
- CONDITIONCODE: Se usa para instrucciones condicionales (como JR y JP). Toma un código de condición de cuatro bits, lo prueba y devuelve el resultado.
- DIRECCIONADOR4, DIRECCIONADOR8 y DIRECCIONADOR12-Estas funciones devuelven una dirección de 12 bits desde una fuente de cuatro, ocho o 12 bits. Utilizan el contenido del registro RP para generar la dirección final de 12 bits. DIRECCIONADOR8 y DIRECCIONADOR12 también comprueban si hay algún modo de direccionamiento escapado.
- ADDER16 – Este es un adder de 16 bits para el cálculo de desplazamiento de direcciones. Toma un operando con signo de ocho bits, el signo lo extiende, lo agrega a la dirección de 16 bits y devuelve el resultado.
- ALU y LU2-Estos fueron discutidos previamente, y realizan la mayoría de las operaciones aritméticas y lógicas.
Procesamiento de interrupciones
Como dije antes, eZ8 tiene un controlador de interrupciones vectorizado con prioridad programable. Al principio, pensé que esta sección no sería tan difícil porque las interrupciones no son gran cosa, ¿verdad? Bueno, cuando empecé a descubrir cómo hacer todas las tareas necesarias (guardar contexto, vectorizar, administrar prioridades, etc.), me di cuenta de que sería más difícil de lo que pensé al principio. Después de un par de horas, se me ocurrió el diseño actual.
El sistema de interrupciones de FPz8 terminó siendo simple. Tiene ocho entradas (INT0 a INT7); una habilitación de interrupción global (bit IRQE ubicado en el registro IRQCTL); dos registros para establecer prioridades (IRQ0ENH e IRQ0ENL); y un registro para banderas de interrupción (IRQ0). El diseño hace uso de una cadena IF anidada que genera una dirección vectorial al detectar un evento de interrupción con respecto a una interrupción habilitada.
La figura 5 muestra una vista comprimida del sistema de interrupciones. Nota hay una primera instrucción IF con el símbolo ATM_COUNTER. Este es un contador simple utilizado por la instrucción ATM (desactiva las interrupciones durante tres ciclos de instrucción, permitiendo operaciones atómicas).
FIGURA 5. Sistema de interrupción FPz8.
Un último comentario con respecto a las interrupciones: Las muestras del registro de bandera de interrupción (IRQ0) interrumpen las entradas de cada borde ascendente del reloj del sistema. También hay dos variables de búfer (IRQ0_LATCH y OLD_IRQ0) que almacenan el estado actual y último de las banderas. Esto permite la detección de bordes de interrupción y también sincroniza las entradas externas con el reloj interno (las FPGA no funcionan bien con señales internas asíncronas).
Depurador en chip
Esta es probablemente la característica más genial de este softcore, ya que permite un entorno de desarrollo integrado comercial (IDE; como ZDS-II de Zilog) para comunicar, programar y depurar software que se ejecuta en la FPz8. El depurador en chip (OCD) se compone de un UART con capacidad de autobaud y un procesador de comandos conectado a él. El UART realiza la comunicación en serie con un PC host y entrega comandos y datos a la máquina de estado del depurador que procesa comandos de depuración (el FSM de procesamiento de comandos del depurador se encuentra dentro del estado CPU_DECOD).
FIGURA 6. Depurador en chip UART (tenga en cuenta el sincronizador DBG_RX).
My OCD design implementa casi todos los comandos disponibles en el hardware real, excepto los relacionados con la memoria de datos (comandos de depuración 0x0C y 0x0D); el contador de tiempo de ejecución de lectura (0x3); y la memoria de programa de lectura CRC (0x0E).
Una cosa que me gustaría destacar es que se necesita cuidado al tratar con señales asíncronas dentro de FPGAs. Mi primer diseño no tuvo en cuenta eso mientras procesaba la señal de entrada DBG_RX. El resultado fue absolutamente extraño. Mi diseño había funcionado a la perfección en la simulación. Lo descargué en una FPGA y comencé a jugar con la interfaz serie de depuración utilizando un terminal serie (mi placa FPGA tiene un convertidor USB SERIE incorporado).
Para mi sorpresa, aunque la mayoría de las veces podía enviar comandos con éxito y recibir los resultados esperados, a veces el diseño simplemente se congelaba y dejaba de responder. Un reinicio suave haría que las cosas volvieran a su correcto funcionamiento, pero eso me intrigaba. ¿Qué estaba pasando?
Después de muchas pruebas y algunas búsquedas en Google, descubrí que posiblemente estaba relacionado con los bordes asíncronos de la señal de entrada en serie. Luego incluí un pestillo en cascada para sincronizar la señal con mi reloj interno y todos los problemas desaparecieron. Esa es una forma difícil de aprender que siempre debe sincronizar las señales externas antes de alimentarlas en una lógica compleja.
Debo decir que depurar y refinar el código del depurador fue la parte más difícil de este proyecto; principalmente porque interactúa con todos los demás subsistemas, incluidos los buses, el decodificador y la cola de instrucciones.
Sintetizando y probando
Una vez compilado completamente (usé Quartus II v9.1 sp2), el núcleo FPz8 utilizó 4900 elementos lógicos, 523 registros, 147456 bits de memoria en el chip y un multiplicador de nueve bits integrado. En general, la FPz8 utiliza el 80% de los recursos disponibles de la EP4CE6. Si bien esto es mucho, todavía hay unos 1200 elementos lógicos disponibles para periféricos (mi simple temporizador de 16 bits suma alrededor de 120 elementos lógicos y 61 registros). Incluso cabe en el FPGA Cyclone IV más pequeño, el EP4CE6, que es el montado en la mini placa de bajo costo que utilicé aquí (Figura 7).
FIGURA 7. Mini placa Altera Cyclone IV EP4CE6.
Las características de la mini placa (junto con el dispositivo EP4CE6): una memoria de configuración en serie EPCS4 (montada en la parte inferior); un chip convertidor serie a USB FTDI, así como un módulo oscilador de cristal de 50 MHz; algunos botones; LED; y encabezados de pines para acceder a pines FPGA. No hay un Blaster USB integrado (para programación FPGA), pero el paquete que compré también incluía un dongle de programación externo.
En cuanto a las pruebas del mundo real, no hace falta decir que el FPz8 no funcionó la primera vez. Después de pensar un poco y leer los mensajes de salida del compilador, me di cuenta de que probablemente era un problema de tiempo. Este es un dilema muy común al diseñar con lógica programable, pero como este fue mi segundo diseño de FPGA, no le presté suficiente atención.
Comprobando los mensajes de análisis de temporización, pude ver una advertencia de que el reloj máximo debería estar alrededor de 24 MHz. Al principio, intenté usar un divisor por 2 para generar un reloj de CPU de 25 MHz, pero no era confiable. Luego usé un divisor por 3. Todo empezó a funcionar a la perfección!
Es por eso que FPz8 se ejecuta actualmente a 16.666 MHz. Es posible alcanzar velocidades más altas utilizando uno de los PLL internos para multiplicar / dividir el reloj principal con el fin de obtener un reloj resultante inferior a 24 MHz, pero superior a 16.666 MHz.
Programar y depurar
Usar FPz8 es muy simple y directo. Una vez que el diseño se descarga en la FPGA, la CPU comenzará a ejecutar cualquier programa cargado en la memoria. Puede suministrar un archivo hexadecimal y usar el Administrador de complementos MegaWizard para cambiar el archivo de inicialización de memoria del programa. De esta manera, el código de la aplicación comenzará a ejecutarse después de una señal de reinicio.
Puede usar el IDE Zilog ZDS-II para escribir código Ensamblado o C, y generar los archivos hexadecimales necesarios (generalmente selecciono el Z8F1622 como mi dispositivo de destino, ya que también tiene 2 KB de RAM y 16 KB de memoria de programa). Gracias al depurador en chip, también es posible utilizar el IDE ZDS-II para descargar código al FPz8 mediante una conexión de depuración en serie (USB, en nuestro caso).
Antes de conectarse, asegúrese de que la configuración del depurador sea la misma que en la Figura 8. Desmarque la opción » Usar borrado de página antes de parpadear «y seleccione» SerialSmartCable » como herramienta de depuración actual. No olvide comprobar también si el puerto COM virtual del FTDI está seleccionado correctamente como puerto de depuración (use el botón de configuración). También puede configurar la velocidad de comunicación deseada; 115,200 bps funciona muy bien para mí.
FIGURA 8. Configuración del depurador.
Tenga en cuenta que al conectarse a FPz8, el IDE ZDS-II mostrará un mensaje de advertencia que le informará de que el dispositivo de destino no es el mismo que el proyecto. Eso sucede porque no implementé algunas áreas de memoria de identificación. Simplemente ignore la advertencia y continúe con la sesión de depuración.
Una vez descargado correctamente el código, puede iniciar la aplicación (botón IR), instrucciones de pasos, inspeccionar o editar registros, establecer puntos de interrupción, etc. Al igual que con cualquier otro depurador bueno, puede, por ejemplo, seleccionar el registro de PAOUT (en el grupo de PUERTOS) e incluso cambiar el estado de los LED conectados a PAOUT.
Se pueden encontrar algunos ejemplos simples de código C en las descargas.
Solo tenga en cuenta que el FPz8 tiene una memoria de programa volátil. Por lo tanto, cualquier programa descargado en él se pierde cuando la FPGA se apaga.
Cierre
Este proyecto me llevó un par de semanas completarlo, pero fue encantador investigar y diseñar un núcleo de microcontrolador.
Espero que este proyecto pueda ser útil para cualquiera que quiera aprender sobre conceptos básicos de computación, microcontroladores, programación integrada y/o VHDL. Creo que, si se combina con una placa FPGA de bajo costo, la FPz8 puede proporcionar una fantástica herramienta de aprendizaje (y enseñanza). ¡Diviértete! NV
CEFET-PR:
www.utfpr.edu.br
ScTec:
www.sctec.com.br
HCS08 Desatado:
https://www.amazon.com/HCS08-Unleashed-Designers-Guide-Microcontrollers/dp/1419685929
Manual de CPU Zilog eZ8 (UM0128):
www.zilog.com/docs/UM0128.pdf
Zilog Z8F64xx Product Specification (PS0199):
www.zilog.com/docs/z8encore/PS0199.pdf
Zilog ZDS II IDE User Manual (UM0130):
www.zilog.com/docs/devtools/UM0130.pdf
Zilog ZDS-II Software Download:
https://www.zilog.com/index.php?option=com_zcm&task=view&soft_id=7&Itemid=74
Zilog Microcontroller Product Line:
http://zilog.com/index.php?option=com_product&task=product&businessLine=1&id=2&parent_id=2&Itemid=56
Project Files available at:
https://github.com/fabiopjve/VHDL/tree/master/FPz8
FPz8 at Opencores.org:
http://opencores.org/project,fpz8