View
222
Download
3
Category
Preview:
Citation preview
Coprocesador FPU (Floating-Point Unit)
Titulación: Ingeniería en Automática y Electrónica Industrial
AUTOR: Pere Blanch Aubia
DIRECTOR: Enric Cantó Navarro
DATA: Junio 2012
Índice
1 Introducción .................................................................................... 4
1.1 Objetivos .................................................................................................. 4
1.2 Definiciones ............................................................................................. 7
1.2.1 Ordenadores personales Vs sistemas embedidos............................... 7
1.2.2 Unidad de Proceso Central (CPU) ..................................................... 7
1.2.3 Circuito Integrado para Aplicaciones especificas (ASIC) ................. 8
1.2.4 Field Programmable Gate Array (FPGA) .......................................... 9
1.2.5 Hardware Description Language (HDL) ......................................... 14
1.2.6 IP Cores ........................................................................................... 15
1.3 Software Principal ................................................................................ 16
1.3.1 Xilinx Platform Studio (XPS) ......................................................... 16
1.3.2 Embedded Development Kit (EDK)................................................ 16
1.3.3 Integrated software environment (ISE) ........................................... 16
1.3.4 Software Development Kit (SDK) .................................................. 16
1.3.5 ModelSim SE Plus 6.2c ................................................................... 24
1.4 Avnet Spartan-3 Development Board ................................................... 25
2 Memoria Descriptiva .................................................................... 28
2.1 IEEE 754 ................................................................................................ 28
2.2 Operación Intf ....................................................................................... 31
2.2.1 Comportamiento Copro VS Microblaze ......................................... 31
2.2.2 Adaptación en Copro ....................................................................... 41
2.3 Operación Add/Sub .............................................................................. 41
2.3.1 Comportamiento Copro VS Microblaze .......................................... 42
2.3.2 Adaptación en Copro ....................................................................... 49
2.4 Operación Mul ...................................................................................... 49
2.4.1 Comportamiento Copro VS Microblaze .......................................... 49
2.4.2 Adaptación en Copro ....................................................................... 58
2.5 Operación Div ....................................................................................... 59
2.5.1 Comportamiento Copro VS Microblaze .......................................... 59
2.5.2 Adaptación en Copro ....................................................................... 71
2.6 Ensayo con Avnet Spartan-3 Development Board ............................ 72
2.6.1 Configuración .................................................................................. 72
2.6.2 Señal de reloj ................................................................................... 73
2.6.3 Memoria SRAM .............................................................................. 73
2.6.4 Conexión y grabación ...................................................................... 73
2.6.5 Comunicación R232 ........................................................................ 74
2.7 Resultados experimentales ................................................................... 75
2.7.1 Comprobación funcional ................................................................. 75
2.7.2 Comprobación de la acceleración .................................................... 78
2.8 Conclusiones .......................................................................................... 83
2.9 Propuestas ............................................................................................. 83
3 Anexos ............................................................................................ 84
3.1 Anexos de Software .............................................................................. 84
3.1.1 Programa C utilizado en la comprobación funcional. ..................... 84
3.1.2 Programa C utilizado en la comprobación de la aceleración........... 86
3.1.3 Programa C utilizado en el desarrollo de las operaciones de int2f,
suma, resta, multiplicación y división. . .................................................................. 90
3.2 Anexos de Hardware ............................................................................ 92
3.2.1 Adaptación del código VHDL ......................................................... 92
3.2.2 Código VHDL completo ................................................................. 98
4 Bibliografia .................................................................................. 142
1 Introducción
1.1 Objetivos
El objetivo final es diseñar una unidad de operaciones en coma flotante vectorial,
en inglés Vector Floating Point Unit (VFPU), de tal modo que produzca el mismo
resultado que la unidad de operaciones en coma flotante del microprocesador softcore de
32 bits Microblaze de Xilinx. La VFPU tiene asociada una circuiteria para acceder a
memoria autónomamente con la finalidad de acelerar el procesado de vectores con
flotantes. La FPU de Microblaze necesita que el microprocesador lea y escriba datos a
memoria mientras que la VFPU accede a memoria por si sola, lo que permite acelerar el
cálculo con vectores. Finalmente, decir que la VFPU, la cual llamaremos Copro en todo
este trabajo, está en fase de desarrollo y experimenta problemas en el cálculo del sticky
bit y el redondeo cuando se realizan operaciones y se comparan los resultados con los de
la FPU de Microblaze. Esto básicamente es debido a que el cálculo del sticky bit no esta
estandarizado y por esta razón es difícil analizar el cálculo que realizan otras FPUs. Por
tanto, el principal objetivo de este trabajo esta formado de dos partes:
Investigar como la unidad de operaciones en coma flotante del
microprocesador softcore de 32 bits Microblaze calcula el sticky bit y hace
el redondeo cuando realiza operaciones en coma flotante.
Modificar la unidad de operaciones en coma flotante vectorial
implementada en una FPGA y diseñada por Enrique Cantó, de tal modo
que su comportamiento tanto en el cálculo del sticky bit como en el
redondeo sea el mismo que el del microprocesador softcore de 32 bits
Microblaze de Xilinx.
Con este fin, se definen los siguientes objetivos secundarios:
Estudio de FPGAs (Field Programmable Gate Array)
Estudio de las herramientas de simulación VHDL (ModelSim) y de síntesis
e implementación (ISE).
Estudio de la placa de desarrollo Avnet Spartan-3A Evaluation Kit.
Estudio del estándar de la IEEE para aritmética en coma flotante (IEEE
754)
Simulación funcional, síntesis, implementación y verificación del diseño
sobre un dispositivo FPGA.
Ejecución y comprobación del funcionamiento del coprocesador utilizando
la placa de desarrollo Avnet Spartan-3A Evaluation Kit.
Para una mejor comprensión del trabajo realizado a continuación se mostrarán
diferentes figuras de un diagrama de bloques de la arquitectura de un sistema digital con
microprocesador. Estas contendran el sistema original, el sistema modificado, en el cual
se podrá ver la VFPU y otros componentes necesarios para su funcionamiento, y
finalmente un pequeño “zoom” de la VFPU para ver las partes que integra.
Figura 1: Sistema digital con el microprocesdor Microblaze de 32bits
En la figura 1 se puede ver el diagrama original de un sistema digital con
microprocesador montado en una FPGA de Xilinx. Este está formado por controladores
de bus DLMB e ILMB, un controlador de interrupciones designado para un puerto de
comunicación serie, una memoria interna, un MDM, en inglés Machine Debug Module,
el cual permite debugar aplicaciones ejecutadas por el microprocesador, un timer, un
generador de señal de reloj conectado a un oscilador de 50MHz externo a la FPGA, un
switch de reset, un controlador de un display de led de 7 segmentos y un
microprocesador de 32bits llamado Microblaze el cual tiene una unidad de operaciones
con flotantes (FPU).
Figura 2: Sistema digital modificado con el microprocesdor Microblaze de 32bits
Utilizando como base el sistema de la figura 1, la figura 2 muestra como el sistema
original se ha modificado creando una VFPU, la cual está conectada a un controlador
EMC permitiendo que la unidad VFPU pueda acceder a una memoria externa SRAM de
manera autónoma. El sistema se ha modificado de tal modo que el microprocesador
pueda también acceder a la memoria externa SRAM.
Figura 3: Vista de la unidad de operaciones en coma flotante vectorial y las partes que la integran
La figura 3 muestra la VFPU formada por un circuiteria, la cual hace posible que
esta acceda a la memoria SRAM externa de manera autónoma, y una FPU. También se
puede ver las partes que integra la FPU. Estas son un bloque divisor, un multiplicador,
un sumardor/restador, un convertidor entero a flotante, un bloque que hace el valor
absoluto, un bloque que hace la negación y un bloque comparador. Esta VFPU es la que
se ha utilizado para desarrollar este trabajo.
1.2 Definiciones
1.2.1 Ordenadores personales Vs sistemas embedidos
Los ordenadores personales, personal computers en inglés, se han creado para
ejecutar un amplio número de aplicaciones diferentes las cuales, algunas de ellas,
requieren la utilización de muchos recursos de dicho ordenador. Por esta razón, los PCs
se han fabricado con procesadores de alto rendimiento, memorias de gran capacidad y
otro tipo de periféricos como puertos USB, tarjetas de video, tarjetas de sonido, discos
duros… Estas aplicaciones dependen del sistema operativo el cual se encarga de manejar
la memoria, controlar periféricos, manejar archivos de sistema e interrupciones y
administrar tareas.
En cambio, un sistema embebido es todo lo contrario a un ordenador personal, es
decir, es un sistema digital con procesador diseñado y programado para reunir los
requisitos de una aplicación específica como en el caso de reproductores de música,
impresoras, escaners… Estas aplicaciones pueden no requerir un sistema operativo o un
sistema operativo simple y personalizado. Algunos aspectos importantes sobre los
sistemas embebidos respecto a los ordenaros personales son la reducción del tamaño, del
coste y del consumo de energía. Un sistema embebido normalmente esta compuesto de
un microprocesador de bajo coste, algo de memoria y algunos periféricos. A veces, un
sistema embebido puede incluir periféricos personalizados o coprocesadores de
operaciones en coma flotante, en inglés Floating Point Unit (FPU), para acelerar la
ejecución de la aplicación.
Los microprocesadores son el celebro de los sistemas digitales con procesador
sobre los cuales se ejecutan las instrucciones que forman el sistema operativo y otras
tareas más específicas. Los microprocesadores embebidos no tienen las mismas
prestaciones que los microprocesadores utilizados en los ordenadores personales pero
son suficientemente potentes para ejecutar la aplicación específica de un sistema
embebido. Hoy en día existen en el mercado una gran variedad de procesadores
embebidos los cuales tienen diferentes prestaciones. Entonces, antes de diseñar un
sistema embebido hay que hacer una buena elección del microprocesador para obtener
un mejor rendimiento del sistema.
1.2.2 Unidad de Proceso Central (CPU)
La CPU, también conocida como procesador o microprocesador, es el componente
principal de un sistema computador. Su misión es ejecutar las instrucciones que forman
los programas y gestionar los datos. Habitualmente, la CPU la cual está fabricada en un
chip es un único trozo de silicio que contiene millones de componentes electrónicos.
Las partes típicas de una CPU son:
La unidad aritmético lógica (ALU): ejecuta operaciones aritméticas y
lógicas.
La unidad de control (CU): responsable de extraer las instrucciones de la
memoria, decodificarlas y ejecutarlas, haciendo uso de la ALU cuando sea
necesario.
Registros los cuales proporcionan almacenamiento interno a la CPU.
Interconexiones CPU que son mecanismos que proporcionan comunicación
entre la unidad de control, la ALU y los registros.
Tipos de CPUs:
Básicamente nos encontramos con dos tipos de diseño de las CPUs: RISC
(Reduced-Instruction-Set Computing) y CISC (complex-instruction-set computing).
Las CPUs RISC se basan en la idea de que la mayoría de las instrucciones para
realizar procesos en el computador son relativamente simples por lo que se minimiza el
número de instrucciones y su complejidad a la hora de diseñar la CPU. Algunos
ejemplos de arquitectura RISC son el SPARC de Sun Microsystem's, el microprocesador
Alpha diseñado por la antigua Digital, hoy absorbida por Compaq y los Motorola 88000
y PowerPC. Estas CPUs se suelen emplear en aplicaciones industriales y profesionales
por su gran rendimiento y fiabilidad.
Al contrario, las CPUs CISC tienen una gran cantidad de instrucciones y por tanto
son muy rápidos procesando código complejo. Fue la primera tecnología de CPUs con la
que la máquina PC se dio a conocer mundialmente. Su sistema de trabajo se basa en la
microprogramación. Dicha técnica consiste en hacer que cada instrucción sea
interpretada por un microprograma localizado en una sección de memoria de la CPU. A
su vez las instrucciones compuestas se decodifican para ser ejecutadas por el procesador.
Las instrucciones estan almacenadas en una ROM interna y las operaciones se realizan a
ritmo de los ciclos de un reloj.
Considerando la extraordinaria cantidad de instrucciones que la CPU puede
manejar, la construcción de una CPU con arquitectura CISC es realmente compleja. A
este grupo pertenecen los procesadores populares utilizados en ordenadores personales
de escritorio y ordenadores portátiles.
El origen de la arquitectura CISC se remonta a los inicios de la programación
ubicada en los años 60 y 70. Para contrarrestar la crisis del software de ese entonces,
empresas electrónicas fabricantes de hardware pensaron que una buena solución seria
crear una CPU con un amplio y detallado manejo de instrucciones, a fin de que los
programas fueran más sencillos. Los programadores en consecuencia crearon multitud
de programas para esa arquitectura. La posterior masificación de los ordenadores
personales, permitió que el mercado fuera luego repleto de software creado para
procesadores CISC.
Las principales ventajas de la tecnología CISC destacan las siguientes: 1. Reduce
la dificultad de crear compiladores. 2. Permite reducir el coste total del sistema. 3.
Reduce los costes de creación de Software. 4. Mejora la compactación de código. 5.
Facilita la depuración de errores (debugging).
1.2.3 Circuito Integrado para Aplicaciones especificas (ASIC)
Un ASIC es circuito integrado personalizado creado para una aplicación particular.
Debido a la reducción del tamaño de los elementos y a la mejora en las herramientas de
diseño a lo largo de los años, la complejidad máxima (y, por lo tanto, la funcionalidad)
ha crecido desde 5000 puertas lógicas hasta por encima de los 100 millones. Los ASIC
modernos a menudo incluyen procesadores de 32 bits, bloques de memoria ROM,
EEPROM, Flash y otros bloques. Los diseñadores de ASIC digital utilizan lenguajes
HDL, como Verilog o VHDL, para describir la funcionalidad de los ASIC.
Las FPGA son la alternativa moderna al prototipado de sistemas ASIC ya que,
debido a su característica de fácil reprogramación, permiten la utilización del mismo
dispositivo para muchas aplicaciones diferentes. Para diseños y/o volúmenes de
producción menores, las FPGA pueden ser menos costosas que un diseño ASIC. El coste
de preparación de una fábrica para producir un ASIC particular puede oscilar entre los
cientos de miles de euros.
El término general application specific integrated circuit incluye las FPGA, pero
la mayoría de diseñadores utilizan ASIC solamente para dispositivos no programables.
1.2.4 Field Programmable Gate Array (FPGA)
Se trata de un dispositivo semiconductor que contiene bloques lógicos
programables (CLB en inglés), interconexiones programables y bloques de entrada/
salida configurables. Cada fabricante tiene su propia arquitectura de FPGA pero en
rasgos generales, todas las FPGAs, con sus variaciones, tienen una arquitectura similar
al de la figura siguiente.
Figura 4: Matriz de bloques lógicos programables
Los bloques lógicos pueden ser programados para imitar la funcionalidad de
puertas lógicas básicas como and, or, xor, not o funciones combinacionales más
complejas como decodificadores, funciones matemáticas simples o multiplexores para
enrutar la lógica de dentro el bloque hacia o desde fuentes externas. En la mayoría de
FPGAs, estos bloques lógicos incluyen a su vez elementos de memoria como simples
flip-flop o bloques de memoria más completos. Un ejemplo seria el circuito de la
siguiente figura.
Figura 5: Bloque programable
La red de conexiones internas programables permite al diseñador del sistema
conectar los bloques lógicos de la FPGA según sus necesidades. Tras el proceso de
fabricación, estos bloques y conexiones lógicas pueden ser programadas por el cliente/
diseñador para que la FPGA pueda ejecutar cualquier función lógica que se precise. La
figura 6 muestra un ejemplo de la red usada para conectar los diferentes bloques lógicos.
Las líneas más largas se usan para conectar aquellos bloques que están físicamente
situados lejos dentro de la FPGA, mientras que las líneas cortas se utilizan para conectar
bloques lógicos que están situados más cerca dentro de la FPGA. Muy a menudo
también hay una matriz de interruptores, switch matrix en inglés, para conectar las
líneas largas con las líneas cortas de manera específica. Para conectar múltiples bloques
lógicos a una línea larga se utiliza un buffer tri-estado creando así un bus de
comunicaciones. Las líneas largas están especialmente diseñadas para tener una
impedancia muy baja lo que significa que el tiempo de propagación es muy rápido. Estas
están también conectadas a un buffer de reloj y a los elementos que funcionan con reloj,
como los flip-flops de cada uno de los bloques lógicos. Así es como la señal de reloj se
distribuye por toda la FPGA.
En los ASICs, la mayoría de retardos vienen dados por la lógica del diseño porque
la lógica esta conectada con líneas de metal que provocan retardos. Sin embargo, en las
FPGAs, lo que provoca el retardo son las interconexiones. Para conectar diferentes
bloques lógicos distribuidos dentro de la FPGA requiere conexiones a través de muchos
transistores y matrices de interruptores los cuales introducen un retardo extra.
Figura 6: Red de conexiones internas programables
Las celdas de entrada y salida configurables, como el que se puede ver es la figura
7, sirven para llevar señales dentro y fuera de la FPGA. Estas celdas están formadas de
un buffer de entrada y otro de salida con control de salida triestado y colector abierto.
Normalmente hay resistores pull up y a veces pull down los cuales se pueden utilizar
para terminar señales y buses sin que se tengan de utilizar resistores externos a la FPGA.
Normalmente, la polaridad de la salida puede ser programada para que sea activa a
nivel alto o activa a nivel bajo y también, en muchas ocasiones, el slew rate de las
salidas se puede programar para obtener tiempos de subida o bajada más rápidos o más
lentos.
En el circuito se puede ver que hay flip-flops tanto para señales configuradas como
salidas como para señales configuradas como estradas. En el caso de las salidas, estos
flip-flops transmiten las señales controladas por reloj hacia la salida de manera muy
rápida sin que se ocasionen demasiados retardos proporcionando una mejor
comunicación con dispositivos externos. En el caso de las entradas, estos flip-flops
reducen el tiempo de la señal cuando entra en un flip-flop aumentando el tiempo de
espera requerido en las FPGAs.
Figura 7: Celdas de entrada y salida configurables
Programacion FPGAs (SRAM vs Antifuse vs Flash)
Hay 3 tecnologías para programar una FPGA. Programación SRAM la cual usa un
bit de RAM estática para cada uno de los elementos de programación. Para escribir un
cero se abre el interruptor y para escribir un uno se cierra el interruptor. Otro método es
el antifusible el cual consiste en un estructura microscópica que, a diferencia de los
fusible convencionales, normalmente esta abierto (no hace conexión). Una cantidad
considerable de corriente durante la programación del dispositivo provoca que el
antifusible se cierre. Un tercer método y relativamente nuevo es el de utilizar bits
EPROM para cada uno de los elementos de programación.
Las ventajas de las FPGAs que se basan en programación SRAM, la cual es la más
extendida hasta el momento, es que se utiliza un proceso de fabricación estándar el cual
proporciona un rendimiento mejor. Como que las SRAM son programables, las FPGAs
se pueden reprogramar infinitas veces, incluso si están en el sistema, es decir, sin tener
que desconectar el dispositivo, igual que si se escribiese en una SRAM normal. Los
dispositivos que utilizan este método pueden utilizar en el diseño la SRAM interna como
pequeñas memorias.
Las desventajas de las FPGAs que se basan en programación SRAM es que son
volátiles lo que significa que una caída de tensión, por muy pequeña que sea, puede
corromper el contenido del dispositivo. Las FPGAs que utilizan esta tecnología tienen
unos retardos debido al largo enrutamiento aunque este es más pequeño que el retardo
producido por otra tecnología. Una buena noticia es que la continua mejora de la
tecnología SRAM ha hecho este retardo sea casi insignificante. Las FPGAs SRAM
pueden consumir bastante potencia y son menos seguras que otras FPGAs que utilizan
otras tecnologías ya que tienen que ser reprogramadas cada vez que el sistema es
alimentado. Esto hace que el flujo de bits de programación (bitstream en inglés) esté al
alcance de cualquiera durante la programación, sin embargo, se puede encontrar en el
mercado FPGAs SRAM personalizadas con llaves de encriptación para la programación.
Otra desventaja es que los errores de bits son más comunes en FPGAs SRAM que en
otros dispositivos de tecnologías diferentes.
La conclusión final de la esta tecnología de programación es que las ventajas de
las FPGAs SRAM sobrepasan sus desventajas haciendo que esta tecnología sea la
predominante en el mercado.
Las ventajas de las FPGAs que se basan en programación antifusible son que no
son volátiles y que los retardos de enrutamiento son muy pequeños, por tanto, se puede
decir que son bastante rápidas. Las FPGAs antifusible tienden a consumir poco y son
buenas para guardar los diseños en el interior del mismo dispositivo ya que no se tienen
que reprogramar cada vez que el dispositivo se alimente.
La principal desventaja de las FPGAs que se basan en programación antifusible es
el proceso de fabricación complejo. Otras desventajas es que se necesita un programador
externo para programarlas y, una vez han sido programadas, estas ya no se pueden borrar
para ser reprogramadas. Pero como se ha dicho de buen principio el proceso de
fabricación complejo y no estándar ha sido la razón por la cual esta tecnología haya
mejorado más lentamente que las de SRAM.
Finalmente tenemos las FPGAs que combinan los dos métodos anteriores. La
FPGAs que se basan en programación Flash. Son no volátiles como la antifusible pero
se pueden programar tantas veces como se quiere como las FPGAs SRAM. También
utilizan un proceso de fabricación estándar como la SRAM. Son relativamente rápidas,
sin embargo, no están tan extendidas como las SRAM. En cualquier caso esto podría
cambiar en cualquier momento.
Ejemplos de familias de FPGA.
Fabricantes de familias de FPGAs SRAM:
Altera Stratix II and Cyclone II families
Atmel AT6000 and AT40K families
Lattice LatticeEC and LatticeECP families
Xilinx Spartan-3 and Virtex-4 families
Fabricantes de familias de FPGAs antifusible:
Actel SX and Axcelerator families
Quicklogic Eclipse II family
Fabricantes de familias de FPGAs Flash:
Actel ProASIC family
Fabricantes de familias de FPGAs hibridas Flash/SRAM:
Lattice LatticeXP family
Uno de los principales fabricantes de FPGAs es la empresa Xilinx la cual fue
fundada por Ross Freeman (el inventor de las FPGA), Bernie Vonderscmitt (pionero del
concepto de compañía fabless) y Jim Barnett en 1984 y con base en Silicon Valley,
California. Al año siguiente desarrollaron su primera FPGA, el modelo XC2064. Hoy en
día, los headquaters están en San José, California, mientras que la sucursal Europea se
halla en Dublín, Irlanda y la asiática en Singapur.
Algunas de la FPGAs Virtex-4/5 de Xilinx ofrecen un microprocesador hardcore
de 32 bits denominado PowerPC del cual existen dos versiones, el 405 y el 440. El
PowerPC-405 es procesador little-endian el cual se caracteriza por utilizar la técnica
pipeline de 5 etapas, cache y una unidad de manejo de memoria, en inglés Memory
Manegement Unit (MMU). El PowerPC-440 es una versión mejorada del 405 el cual
esta diseñado siguiendo la arquitectura superescalar y utiliza la técnica pipeline de 7
etapas. La arquitectura de los procesadores PowerPC 405 y 440 soportan FPU auxiliares
u otro tipo de coprocesadores. La más nueva versión de FPGAs de Xilinx Zynq-7000
integran un procesador dual core ARM Cortex-A9. De hecho, los procesadores ARM se
han convertido el estándar de los microprocesadores de 32 bits montados en ASICs.
Xilinx también proporciona Microblaze, un microprocesador softcore de 32 bits
pensado para sistemas embebidos implementados en cualquiera de sus FPGAs. Este
microprocesador se puede configurar para utilizar técnica pipeline de 3 a 5 etapas, se
puede activar una cache lógica, una MMU, una FPU de precisión simple…
Diseño de FPGA
Para definir el comportamiento de una FPGA el usuario proporciona un diseño
hardware. Esto se consigue con un lenguaje de programación llamado HDL (hardware
description language en inglés) el cual ayuda al diseñador a hacer una descripción del
circuito hardware mediante programación software. Los lenguajes más comunes son
VHDL y Verilog. Una vez completados los procesos de diseño y validación, se genera el
fichero binario usado para reconfigurar el dispositivo FPGA.
Para simplificar el diseño de sistemas complejos en FPGAs, existen librerías de
funciones complejas predefinidas y circuitos que han sido testeados y optimizados para
agilizar el proceso de diseño. Estos circuitos predefinidos se llaman IP Cores y se
pueden conseguir gratuitamente o comprándolos a fabricantes de FPGA o a proveedores
de IP Cores pero lo más normal es que no sean gratuitos, y típicamente protegidos bajo
licencias de propiedad. Otros circuitos predefinidos están disponibles en comunidades de
desarrolladores como OpenCores (típicamente gratuitos).Una sitio web muy conocido
donde los usuarios registrados comparten sus IP Cores es http://opencores.org
1.2.5 Hardware Description Language (HDL)
Se trata de un lenguaje para la descripción formal de circuitos electrónicos. Puede
describir tanto la operación del circuito, su diseño y testeo para verificar su
funcionamiento mediante simulación. Un HDL es una expresión estándar basada en
texto del comportamiento temporal y/o la estructura circuital de un sistema electrónico.
En contraste con un lenguaje de programación software, la sintaxis y la semántica de un
HDL incluyen notaciones explícitas para expresar tiempo y concurrencia, que son
atributos principales del hardware. Aquellos lenguajes cuya única característica es la de
expresar la conectividad del circuito entre una serie de bloques se clasifican como
lenguajes netlist.
Los HDL son utilizados para escribir especificaciones ejecutables de algunos
circuitos hardware. Un programa de simulación, diseñado para implementar la
semántica de las declaraciones del lenguaje, combinado con la simulación del progreso
del tiempo, proporciona al diseñador de hardware la habilidad de modelar un dispositivo
hardware antes de ser creado físicamente.
Un programa software llamado sintetizador puede deducir operaciones lógicas
hardware de las declaraciones del lenguaje HDL y producir una netlist equivalente de
primitivas hardware genéricas para implementar el comportamiento especificado. Esto
suele requerir que el sintetizador ignore la expresión de cualquier construcción temporal
en el texto.
Diseño con HDL
Los lenguajes HDL se utilizan para diseñar dos tipos de sistemas. Primero, son
utilizados para diseñar un circuito integrado personalizado (ASIC), como un procesador
u otro tipo de chip de lógica digital. En este caso, un HDL especifica un modelo del
comportamiento de un circuito antes que el circuito sea diseñado y fabricado. El
resultado final es un chip de silicio que será producido en una fábrica.
En segundo lugar, son utilizados para la programación de dispositivos lógicos
programables (PLD), como las FPGA. El código HDL es introducido en un compilador
lógico, y la salida se carga en el dispositivo programable. La característica especial de
este proceso y de la lógica programable en general es la posibilidad de alterar el código,
compilarlo y cargarlo en el mismo dispositivo programable varias veces para que este
pueda ser testeado.
1.2.6 IP Cores
En un diseño electrónico una IP Core (intellectual property core) es una unidad
lógica, celda o diseño reutilizable que tiene la finalizar de realizar una tarea especifica.
Una IP core puede ser propiedad de un grupo o de una única persona física y se utilizan
como bloques dentro de diseños para chips ASIC o para diseños lógicos con FPGAs. Las
IP cores se venden en el mercado a través de empresas especializas en diseñar estas
unidades. Recientemente, empresas que fabrican y venden FPGA han empezado a
ofrecer sus propias IP cores. Las IP cores reducen el tiempo de diseño así como la
necesidad de mano de obra a los diseñadores que trabajan con FPGAs. Una de las
principales ventajas de estas unidades es que han sido previamente diseñadas y
verificadas. También, pueden ser modificadas lo que significa que se puede añadir así
como eliminar parte de la funcionalidad inicial con el fin de que se adapte a las
necesidades del sistema que se este creando. Otra ventaja es que son portables entre
diferentes fabricantes.
Un problema de las IP cores es que a veces pueden ser caras. Otro aspecto a tener
en cuenta son las características eléctricas, como puede ser tiempo o consumo, ya que
estas pueden ser optimizadas hasta cierto punto pero las características actuales
dependen del uso en un dispositivo en particular y también dependen de la lógica a la
cual están conectadas. La compra de IP cores a terceros puede causar resultados no
esperados especialmente cuando ha sido integrada en un diseño propio. Estos resultados
adversos pueden ser causados por falta de velocidad y/o exceso de consumo.
En cuanto a los tipos que existen, se puede decir que existen dos grupos. Las
unidades soft cores y las hard cores.
Soft cores
Las IP cores se ofrecen sintetizables RTL. RTL, en inglés register transfer
language, es un término utilizado para describir representaciones intermedias muy
cercanas al lenguaje ensamblador. Las IP cores sintetizables se distribuyen en un
lenguaje de descripción de hardware como Verilog o VHDL. Estos son análogos a otros
lenguajes de nivel alto como C en el campo de programación de computadores. Las IP
cores se distribuyen a los fabricantes de chips en formato RTL permitiendo al diseñador
de chips hacer modificaciones a nivel funcional, aunque la mayoría de distribuidores de
estas unidades no aseguran un buen funcionamiento de las IP cores que han sido
modificadas ni tampoco soporte a los diseñadores que han modificado estas unidades.
A veces las IP cores se ofrecen como netlists a nivel de puertas genéricas (en
ingles generic gate-level netlists). Un netlist es una representación algebraica booleana
de la función lógica de las IP cores implementadas en puertas genéricas o celdas
estándar para procesos específicos. Una IP core implementada en puertas genéricas es
portable a cualquier tecnología de procesos, mientras que, los netlist son análogos al
código ensamblador utilizado para programación de computadores, es decir, un netlist
proporciona al distribuidor de IP cores una protección razonable contra ingeniería
inversa.
Hard cores
Estas IP cores ofrecen una predicción mayor del rendimiento del chip en cuanto a
área y rendimiento de la respuesta. Esto es gracias a la naturaleza de su representación a
bajo nivel. La lógica analógica y mixta (digital-analógica) son generalmente definidas
mediante descripciones físicas y a bajo nivel. Por esta razón, las IP cores analógicas
(SerDes, PLLs, DAC, ADC, etc.) se distribuyen a los fabricantes de chip en formato de
trazado de transistores como GDSII. El formato GDSII, en inglés Graphic Data System,
es un formato de fichero de datos que desde hace años ha sido en la industria de los
circuitos integrados el estándar para el intercambio de datos entre herramientas de diseño
físico de chips. A veces, las IP cores digitales también se ofrecen en este formato.
Estas IP cores, ya sean analógicas o digitales, se llaman hard IP cores porque su
función no puede ser modificada por los diseñadores de chips.
1.3 Software Principal
1.3.1 Xilinx Platform Studio (XPS)
El Xilinx Platform Studio es una interfaz gráfica que conjuntamente con otras
herramientas software hace posible desarrollar sistemas embebidos personalizados.
Xilinx Platform Studio integra todos los procesos necesarios para un diseño completo.
Estos procesos constan de las siguientes etapas: diseño, depuración y verificación.
El diseñador utiliza XPS para configurar y crear las especificaciones de su sistema
embebido (procesador, controlador de memoria, periféricos de entrada/salida…). Luego
XPS convierte las especificaciones del diseñador en una descripción sintetizable RTL
(Verilog or VHDL) y escribe un conjunto de scripts para automatizar la implementación
del sistema embebido (de RTL a un archivo bitstream).
Para crear un sistema embebido con procesador hay que seguir los pasos
siguientes:
1.- Diseñar la plataforma hardware que puede estar formada de uno o más
procesadores, buses y periféricos.
2.- Crear un software el cual correrá dentro de sistema.
3.- Simular el sistema completo.
4.- Depurar y verificar que el resultado sea el esperado.
1.3.2 Embedded Development Kit (EDK)
EDK, conocido también como Embedded Development Kit, esta formado por una
serie de herramientas software e IP Cores para el diseño de sistemas embebidos con
procesador y soporta procesadores IBM PowerPCTM
y procesadores Xilinx
MicroBlazeTM
.
1.3.3 Integrated software environment (ISE)
ISE es el pilar del diseño lógico para FPGAs de Xilinx que permite la síntesis y el
análisis de diseños HDL (Hardware Description Language). Como que el diseño lógico
de una FPGA es complejo y además hay que tener muchos aspectos en cuenta, ISE
proporcionar una serie de herramientas las cuales permiten al desarrollador del sistema
sintetizar (“compilar”) sus diseños, realizar análisis de tiempos, examinar diagramas
RTL, simular la reacción del diseño a diferentes estímulos, comprobar enrutamiento y
emplazamiento lógico, programación y configuración del dispositivo con la ayuda de un
programa especialmente creado para este propósito.
1.3.4 Software Development Kit (SDK)
SDK es un entorno de desarrollo integrado, complementario a XPS, el cual se
utiliza para escribir, compilar y también depurar aplicaciones en un lenguaje bastante
común como C/C++ ampliamente utilizado en sistemas embebidos. SDK usa la
estructura opensource EclipseTM
lo que hace que esta herramienta resulte familiar a
muchos de los miembros del equipo de diseño de un sistema embebido con procesador.
El diagrama siguiente muestra la relación que hay entre los entornos de desarrollo
XPS, EDK, ISE y SDK.
Figura 8
Creación de un sistema embebido con XPS
XPS proporciona 3 opciones para abrir un proyecto. La primera opción es un
asistente llamado Base System Builder wizard el cual facilita la tarea de crear la base del
sistema embebido que se quiere diseñar, pero esto no es posible para cualquier tipo de
placa sino que solo con algunos modelos de placas de determinados fabricantes, o
para un modelo genérico de placa. La segunda opción que proporciona el XPS es la
posibilidad de crear un proyecto en blanco para que el diseñador pueda crear la base del
sistema manualmente y de la manera que más le convenga. Obviamente, esta opción es
para usuarios avanzados los cuales tienen un buen conocimiento de la herramienta.
Finalmente, la última opción es la de abrir un proyecto ya existente ya sea para terminar
el diseño el cual no fue finalizado con anterioridad o para hacer cualquier tipo de
modificaciones. La figura 6 muestra la ventana emergente cada vez que se arranca el
XPS.
Figura 9
Los pasos los cuales el asistente ira preguntando si se selecciona la primera
opción son:
El nombre del proyecto y directorio en el cual se almacenaran todos los
archivos necesarios.
El tipo de placa que vamos a utilizar para hacer las pruebas. En este
proyecto se utiliza una AVnet Spartan-3 Development Board.
El tipo de procesador (IBM PowerPCTM
o Xilinx MicroBlazeTM
.)
Configuración del procesador (frecuencia de reloj…)
Configuración de la interfaz entrada-salida. En este caso se pueden utilizar
IP cores ya existentes que pueden ser proporcionados por la misma
empresa que nos proporciona la placa o comprándolos a terceros.
Periféricos internos.
Finalmente, especificando como se pretende utilizar el sistema, BSB crea
un software de prueba para probar la memoria, los periféricos…
A finalizar estos pasos se obtiene una pantalla similar a la que se puede ver en la
figura siguiente.
Figura 10: Ventana principal de XPS.
A continuación se describirá la distribución de la información del sistema
a) Project Información
Esta sección esta formada por tres pestañas las cuales contienen toda información
sobre nuestro sistema y control sobre el mismo. Estas pestañas son Project, Applications
y IP Catalog.
Pestaña Project: Aquí podemos encontrar la lista de referencias de todos los archivos
que contiene nuestro proyecto. Esta lista esta formada de tres sublistas.
Archivos de proyecto (Project Files): contiene todos los archivos que
hacen referencia al proyecto, por ejemplo, archivos de especificación
Hardware del microprocesador, en inglés Microprocessor Hardware
Specification (MHS) files, archivos de especificación Software del
microprocesador, en inglés Microprocessor Software Specification (MSS)
files, Archivos de limitaciones impuestas por el usuario, en inglés User
Contraints File( UCF) files, archivos de comandos iMPACT, archivos de
opciones de implementación…
Opciones de proyecto (Project Options): contiene todo las opciones
configurables del proyecto, por ejemplo, Dispositivo, netlist,
implementación, archivos de configuración.
Archivos de referencia (Referente Files): contiene todo los archivos log y
de salida generados por proceso de implementación del XPS.
A continuación se muestra un listado de todos los archivos de configuración, de
especificación, de salida… de nuestro proyecto.
Figura 11: Detalle de la pestaña Project
Pestaña Applications: Esta pestaña muestra una lista de las aplicaciones de software y
sus configuraciones, los archivos .h y los códigos fuente asociados a cada proyecto de
aplicación. En esta pestaña se puede.
Crear y añadir un proyecto de aplicación de software, compilarlo y cargarlo
al bloque de memoria RAM.
Configurar las opciones de compilador.
Añadir códigos fuentes así como archivos .h al proyecto.
La pestaña Applications sirve de mucha ayuda cuando se quiere generar un
software de prueba para testear nuestro sistema. Mediante un lenguaje conocido como es
C o C++ se pueden crear las instrucciones las cuales queremos que se ejecuten en el
sistema embebido. Un ejemplo claro para este proyecto seria crear instrucciones las
cuales estén formadas por operaciones en como flotante. De esto modo se ve de una
manera muy sencilla si el diseño del coprocesador matemático de operaciones en como
flotante funciona correctamente.
Figura 12: Detalle de la pestaña Applications
Pestaña IP Catalog: Esta pestaña muestra un catálogo de todos los periféricos (IP cores)
utilizados en el diseño. Estos pueden se creados por otros diseñadores o creados por el
mismos diseñador del sistema. En caso de que se hayan obtenido de otros diseñadores,
los periféricos pueden ser libres o pueden tener licencia lo que significa que hay que
comprar la licencia para utilizarlos. Cuando se abre un proyecto solo se muestran todos
aquellos periféricos que son compatibles con los dispositivos de arquitectura Xilinx. De
todos los periféricos disponibles en la lista se puede ver la versión, el estado (activo…),
bloqueo (sin licencia, bloqueado, no bloqueado), soporte para procesador y finalmente
un corta descripción. También dándole al botón derecho de ratón aparece un menú el
cual proporciona datos adicionales como cambios de versiones, hoja de datos y archivo
de descripción de periféricos del microprocesador (MPD).
Figura 13: Detalle de la pestaña IP Catalog
b) System Assembly View
Esta sección llamada vista del montaje del sistema, en inglés System Assembly
View, es donde se puede configurar los elementos de los bloques del sistema. Aquí se
puede ver una lista de todos los elementos que forma el diseño (Bus interfaces, Puertos,
Direcciones) la cual muestra información sobre el mismo y además permite editar la
parte hardware de una manera muy sencilla.
La información en esta vista puede representarse en modo jerárquico o modo
plano. El modo jerárquico es el modo por defecto de esta vista y muestra una lista de las
instancias de las IP cores. Además proporciona menús desplegables para acceder a
diferentes zonas configurables del sistema hardware. En cambio, el modo plano permite
ordenar cualquier información mediante cualquier columna.
Seleccionando esta vista, a la izquierda hay una subsección llamada panel de
conexiones, en inglés connectivity panel, la cual muestra una representación grafica de
las conexiones hardware del diseño.
Un línea vertical representa un bus, y una de horizontal representa un bus
interface a un IP core.
Solo se muestran todas aquellas conexiones las cuales son compatibles, es
decir que si entre buses la conexión es compatible, esta se va a mostrar, en
caso contrario no será así.
Las líneas y los conectores siguen un código de colores para indicar su
compatibilidad.
Los símbolos de las conexiones indican si un bloque IP core es bus master
o esclavo.
Un conector hueco representa una conexión la cual esta desactivada y un
conector lleno representa una conexión la cual esta activada. Para habilitar
o deshabilitar una conexión solo hay que darle con el puntero del ratón al
símbolo de conector.
Figura 14
c) Block Diagram
Esta sección llamada diagrama de bloques, en inglés block diagram, muestra de
manera gráfica la distribución de los elementos utilizados en el diseño. Los elementos
pueden ser procesador, memorias, controladores, buses internos los cuales se representan
en diferentes colores dependiendo del tipo.
Figura 15
d) Platform studio
La sección platform studio proporciona un diagrama de flujo del diseño del
sistema embebido con enlaces a una ayuda. Si en cualquier momento no se tiene claro
como continuar o se necesita más información sobre como realizar un proceso, esta
sección puede servir de mucha ayuda.
e) Console window
La sección console window proporciona información de todas las herramientas
llamadas durante la ejecución de las mismas. En la siguiente figura se puede ver que esta
console window esta formada de tres pestañas (Output, Warning, Error) las cuales
mostraran información de salida, avisos y errores respectivamente durante la
compilación del programa, sintentización del diseño y grabación del dispositivo.
Figura 16: Detalle de la pestañas de la consola
1.3.5 ModelSim SE Plus 6.2c
ModelSim SE (Special Edition) es un entorno de simulación y depurado
desarrollado por Mentor Graphics Corporation. Trabaja en entornos basados en Unix,
Linux y Windows, y combina un elevado rendimiento con un poderoso e intuitivo GUI
(interfaz gráfica de usuario). Permite simular diseños escritos en verilog, vhdl y SystemC
(o incluso netlist). Los límites entre lenguajes están impuestos al nivel de cada unidad de
diseño. Por lo tanto, cualquier instancia en la jerarquía del diseño puede corresponder a
una unidad de diseño escrita en otro lenguaje sin ninguna restricción. A partir de los
ficheros fuente de los IP cores o de los diferentes modelos de simulación obtenidos
durante el proceso de síntesis e implementación con el entorno ISE, la herramienta
ModelSim permite depurar el diseño mediante simulación funcional y simulación post-
layout entre otras.
Figura 17: Ventana principal del ModelSim
A continuación se describirá las secciones de este entorno de simulación.
a) Workspace
Esta sección esta formada de 4 pestañas. Las pestañas Library, Objects and
Locals, Wave and Visualizador de código
La pestaña Library contiene una lista con todas las librarías que describen el
comportamiento del sistema. La descripción esta hecha mediante lenguajes como
verilog, vhdl o SystemC. Algunas de estas librerías son accesibles, es decir, que el código
es abierto y además pueden ser modificadas bajo responsabilidad del diseñador. En
cambio, hay otro tipo de librerías las cuales no son accesibles ya que protegidas bajo
diferentes licencias sobre la patente y derechos de copyright por propiedad intelectual
pero, esto no significa que no se puedan utilizar ya que si se adquiere las licencias
correspondientes, estas se pueden utilizar el cualquier diseño. Finalmente, las librerías
creadas por el diseñador también aparecen en esta sección. Una característica importante
de este visualizador de librerías es que desglosa una librería en unidades de diseño
siendo más fácil simular y depurar diferentes partes del diseño.
La pestaña Sim aparece cuando se carga una unidad de diseño y contiene todas
las librerías además de los archivos de descripción de aquella unidad de diseño.
La pestaña files aparece cuando se carga una unidad de diseño y contiene todos
los archivos además de los path de la unidad de diseño cargada.
b) Objects and locals
Muestra todas las señales y variables que se usan dentro de un proceso, entidad o
incluso, dentro de una librería. Están aparecen al seleccionar un archivo o librería en la
pestaña Sim.
c) Wave
Esta sección se muestra un gráfico con las formas de onda de todas las señales y
variables que se quieren visualizar, además de su valor. Estas solo se muestran dentro de
un rango de tiempo que es definido por el usuario y son cargadas directamente desde la
ventana Object and locals. El usuario puede aumentar o disminuir el enfoque para ver
con más detalle el cambio de valor de estas.
d) Visualizor de código
Esta sección muestra el código de todos aquellos archivos que se quieren
visualizar y que sean accesibles. Sin embargo, también es posible utilizar otros
visualizadores conocidos como UltraEDIT32 o el Notepad de Windows.
1.4 Avnet Spartan-3 Development Board
La placa de desarrollo Spartan-3, en inglés Spartan3 development Board, la cual
ha sido diseñada y fabricada por Avnet, proporciona una plataforma hardware estable
para desarrollar y testear diseños con FPGAs de Xilinx que son consideradas las que
tiene un coste por puerta lógica y coste por pin de entrada/ salida más barato. El
dispositivo FPGA XC3S1500/2000 montado en el módulo electrónico ofrece un entorno
de prototipado para implementar periféricos que proporcionen audio y video,
comunicaciones, entrada/salida y capacidad de almacenamiento. El módulo esta provisto
de conectores de expansión los cuales proporcionan conectividad a módulos de
expansión Avnet personalizados y existentes en el mercado. Además, este también tiene
dos conectores de expansión Avbus con pines de entrada/salida dedicados
proporcionando así un máximo rendimiento cuando son utilizados con módulos Avnet.
El tipo de aplicaciones que se pueden implementar con este módulo va desde
aplicaciones del sector de la automoción, industria, medicina y científica, testeo, video
digital y redes de comunicación.
Las Característica básicas de esta placa de desarrollo son:
FPGA
Xilinx XC3S1500/2000-FG676 Spartan-3 FPGA
I/O Peripherals
2x16 character LCD
128x64 OSRAM OLED graphical display
DB15 & video DAC
Audio Codec
PS2 keyboard and mouse ports
8-position DIP switch
2 push-buttons
8 discrete LEDs
Piezo buzzer
3, 140-pin general purpose I/O expansion connectors (AvBus)
Up to 30 LVDS pairs
1, 50-pin 0.1" header for easy I/O access
Memory
Micron DDR SDRAM (32 MB)
16 MB FLASH
2 MB SRAM
Communication
RS-232 serial port
10/100 Ethernet
USB 2.0
Configuration
Xilinx platform FLASH configuration PROM(s)
Parallel IV cable support for JTAG
Fly-wire support for Parallel III and MultiLINXTM
Figura 18: Diagrama de bloques de la placa de desarrollo Spartan-3
Figura 19: Vista principal de la placa de desarrollo Spartan-3
2 Memoria Descriptiva
2.1 IEEE 754
La codificación de números reales utilizando lógica binaria es significativamente
más compleja que el caso de los naturales o enteros. Parte de esta complejidad deriva del
hecho de que si bien al utilizar un número finito de bits es posible representar un
intervalo concreto de números enteros o naturales, en el caso de los números reales esta
técnica no es posible.
Dado que entre dos números reales existe un número infinito de números, no es
posible representar todos los números en un intervalo concreto sino que se representan
únicamente un subconjunto de los números contenidos en dicho intervalo. Esta
propiedad plantea un inconveniente que se debe tener en cuenta no sólo en el diseño de
los circuitos digitales capaces de procesar números reales, sino incluso en los programas
que utilizan este tipo de números.
Supongamos que se operan dos números reales representados en forma binaria y
que el resultado no corresponde con uno de los números que pueden ser representados.
Esta situación es perfectamente posible dado que entre dos números hay infinitos
números reales. La única solución posible en lógica digital consiste en representar este
resultado por el número real más próximo en la codificación. La consecuencia inmediata
es que se ha introducido un error en la representación de este resultado. En general,
cualquier número real fruto de una operación tiene potencialmente el mismo problema.
En algunos casos este error no existe porque el número sí puede ser representado de
forma digital, pero en general, la manipulación de números reales puede introducir un
error. Este posible error introducido por la representación adquiere especial relevancia
en aquellos programas que realizan cálculos masivos con números reales. Existen
técnicas de programación orientadas a minimizar el error producido cuando se
manipulan números reales.
El estándar IEEE 754 ha sido definido por el Instituto de Ingenieros Eléctricos y
Electrónicos (Institute of Electrical and Electronics Engineers, IEEE) y establece el
formato básico para representar números en coma flotante, también llamados números
reales, en computadoras digitales incluyendo números normalizados, no normalizados, el
cero y valores especiales como infinito y NaN. Además, especifica cuatro modos de
redondeo y cinco excepciones (incluyendo cuándo ocurren dichas excepciones y qué
sucede en esos momentos).
IEEE 754 también especifica cuatro formatos para la representación de valores en
coma flotante: precisión simple (32 bits), precisión doble (64 bits), precisión simple
extendida (≥ 43 bits, no usada normalmente) y precisión doble extendida (≥ 79 bits,
usualmente implementada con 80 bits).
Tanto Copro como Microblaze “soft processor core” trabajan con 32 bits, es decir,
que utilizan precisión simple para representar los números en coma flotante.
Precisión simple
Como se ha mencionado anteriormente, en precisión simple se usan 32 bits (4
bytes) para representar un número real: 1 bit para el signo (s) del número, 23 bits para la
mantisa (m) y 8 bits para el exponente (exp), los cuales se distribuyen de la siguiente
forma:
Figura 20: Representación de un número real con precisión simple en el estándar 754.
El bit de signo (S) es 0 para números positivos y 1 para números
negativos.
La mantisa (m) esta formada de 23 bits que se obtienen transformando el
número decimal a binario y cogiendo los 23 bits de la derecha de la coma
si esta es colocada a la parte derecha de primer 1 de más hacia la izquierda
del número representado en binario. Si no hay suficiente bits a la parte
derecha de la coma, los que faltan hasta 23 se rellenan con 0.
Ejemplo 1: 8d=1000bmoviendo la coma detrás del 1 de mas a la izquierda
(1.000)m=000 0000 0000 0000 0000 0000
Ejemplo 2: 0.1d=0.0001100110011…moviendo la coma detrás del 1 de la
izquierda (1.100 1100 1100 1100 1100 1100)m=100 1100 1100 1100 1100
1100
Ejemplo 3: 8.5d=1000.1moviendo la como detrás del 1 de la izquierda
(1.000 1)m=000 1000 0000 0000 0000 0000
El exponente se representa como 127 número de desplazamientos de la
coma en la mantisa. + si el desplazamiento se hace hacia la izquierda y - en
caso contrario.
Si nos fijamos en los ejemplos del punto anterior tenemos:
Ejemplo 1: 8d=1000b3 desplazamientos hacia la izquierda, por lo tanto
exp=127+3=130d= 10000010b
Ejemplo 2: 0.1d=0.0001100110011…4 desplazamientos hacia la derecha,
por lo tanto exp=127-4=123d= 01111011 b
Ejemplo 3: 8.5d=1000.13 desplazamientos hacia la izquierda, por lo tanto
exp=127+3=130d= 10000010b
Otro aspecto que el estándar IEEE 754 especifica es el método de redondeo los
cuales están englobados en dos grupos.
Redondeo lo más cerca posible
Redondeo al valor más cercano: Este método de redondeo utilizado por
Microblaze “soft processor core” y consiste en redondear el número a su
valor más próximo dejando el ultimo digito a cero.
A continuación se puede ver un claro ejemplo utilizando números reales
Ejemplo 1: 8,87 se redondea a 8,90
Ejemplo 2: 8,83 se redondea a 8,80
Redondeo al valor más cercano lejos de cero: Este método de redondeo
consiste en redondear el número a su valor más próximo pero con la
particularidad de estar lo más lejos de cero posible.
A continuación se puede ver un claro ejemplo utilizando números reales
Ejemplo 1: 8,87 se redondea a 8,90 y 8,83 se redondea a 8,90
Ejemplo 2: -8,87 se redondea a -8,90 y -8,83 se redondea a -8,90
Redondeo directo
Redondeo hacia 0 – redondeo al valor entero más cercano cero sin tener
en cuenta la parte decimal.
Redondeo hacia +∞ – redondeo al valor más cercano al entero superior.
Redondeo hacia −∞ – redondeo al valor más cercano al entero inferior.
2.2 Operación Intf
Generalmente, las variables y constantes que intervienen en una expresión de un
programa informático suelen ser del mismo tipo. Sin embargo, el lenguaje C permite
utilizar variables y constantes de distinto tipo en una misma expresión. Obviamente, en
el lenguaje C se hacen una serie de conversiones de tipos al calcular el resultado de una
expresión si los tipos de las variables son diferentes. Por tanto, una de las operaciones
que se ha implementado en Copro es la de convertir un número entero a un número en
coma flotante representado del mismo modo que describe el estándar IEEE 754. Esta
misma operación también puede ser realizada por Microblaze. El rango del números
enteros con signo que se puede representar en binario natural de 32 bits es de –[(2 31)-
1] hasta (2 31)-1, es decir, de -2147483647 hasta 2147483647. Este rango convertido
en numeración en como flotante es de 0xCF000000 hasta 0x4F000000.
2.2.1 Comportamiento Copro VS Microblaze
En el apartado 2.2 de este proyecto se dice de que el código fuente de Microblaze
no es Open source pero en ningún momento se menciona que Microblaze pone a
disposición del usuario señales de entrada, de salida e intermedias utilizadas en su
código. Desafortunadamente, en el caso de la conversión entero flotante no se tiene
acceso a ninguna señal intermedia del código fuente que Microblaze utiliza para realizar
dicha conversión, pero si que se tiene acceso a señales de entrada, salida e intermedias
de otras operaciones como por ejemplo la operación multiplicación. Entonces, para
poder ver el resultado final de una conversión entero flotante realizada por Microblaze
se va a utilizar el siguiente código en C el cual será ejecutado por Microblaze.
En el código anterior se puede ver que primero se declaran 3 vectores de dos
elementos cado uno. Uno de ellos, llamémoslo vector “a”, que contenga enteros los
cuales serán los datos de entrada de Copro. Los otros dos vestores, llamémoslos vectores
“ax” y “b”, que contengan flotantes los cuales serán los datos de entrada de Microblaze.
Los números de estos dos vectores (”ax” y “b”) están representados en entero pero
mediante un cast se indica que estos sean transformados y almacenados en coma flotante
de tal modo que, Microblaze tendrá que hacer la conversión entero a flotante antes de
almacenar los datos en memoria. Finalmente, realizando multiplicaciones uno a uno
entre los elementos de los vectores que almacenan flotantes y visualizando las señales de
const int a[2]={0x1AAAAFFF,0x01AAAFFF};
const float b[2]={0x1AAAAFFF,0x01AAAFFF };
const float ax[2]={0x2,0x3};
int *p1=(int*)0x2F0000;
float *p2=(float*)0x2E0000;
float *p3=(float*)0x2D0000;
float *p4=(float*)0x2C0000;
for(int j=0; j<2; j++)
{p1[j]=a[j];}
for(int j=0; j<2; j++)
{p2[j]=ax[j];
p3[j]=b[j];}
for(int j=0; j<2; j++)
{p4[j]=p2[j]*p3[j]; }
entrada de dicha operación, se puede ver como Microblaze ha convertido los enteros de
los vectores “ax” y “b” a flotante.
El resultado de la ejecución del código anterior tras la compilación del proyecto y
visualización de la señales tanto de Copro como Microblaze es el mostrado en las dos
figuras siguientes.
Figura 21: Datos de entrada (vector a) y salida de Copro cuando realiza un conversión entero flotante
Figura 22: Datos de entrada de Microblaze (Vectores ax y b)
En la figura 21 se puede ver como los datos de entrada de Copro están en el mismo
formato que en el código, es decir, en entero porque nos que interesa ver como Copro
hace la conversión entero flotante. Además, también se puede ver los datos de salida
cuando dicha conversión ya ha sido realizada. Entonces, estos serán los datos que se
utilizarán para compararlos con los datos de las conversiones hechas por Microblaze.
Por otro lado, en la figura 22 se puede ver como los datos de los vectores ax y b
han sido convertidos a flotante por Microblaze. El valor 2 en decimal (0x2 en hexa)
corresponde a 40000000 en flotante, el valor 3 en decimal (0x3 en hexa) corresponde a
40400000 en flotante, el valor 447393791 (0x1AAAAFFF en hexa) corresponde a
4DD55580 en flotante y el valor 27963391 (0x01AAAFFF en hexa) corresponde a
4BD55800 en flotante. Es necesario mencionar que estos cuatro números de los vectores
ax y b están representados siguiendo el estándar IEEE 754 una vez convertidos por
Microblaze.
Llegado a este punto, se supondrá que Microblaze utiliza el mismo método que
Copro al realizar una conversión de entero a flotante. Obviamente, se verificará si esta
hipótesis es cierta y en caso de que no lo sea se ajustará Copro para que sea así. El
método general el cual sigue Copro para realizar la operación entero a flotante es el de
transformar el operando en un número basado en exponente, mantisa y signo.
Seguidamente se muestra un ejemplo basado en números enteros representados en base
dos.
Operando A=2; Operando B=8; Operando C=100;
En el caso del operando A la mantisa y el exponente valen 1.
Operando A=1* 2^1
En el caso del operando B la mantisa vale 1 y el exponente vale 3.
Operando B= 1*2^3
En el caso del operando C la mantisa vale 1,5625 y el exponente vale
6.
Operando B= 1,5625 *2^6
El método inicial utilizado por Copro es el siguiente.
Sea el número N=0x4AAAAFFF (0100 1010 1010 1010 1010 1111 1111 1111b)
siendo el bit de más peso (MSB) el de más a la izquierda, primero se obtiene el signo el
cual viene dado por el bit 31 que es el MSB. Si este es 0 el bit de signo vale 0 que
significa número positivo. En caso contrario, el bit de signo vale 1 y significa número
negativo y además se cambia el signo de N del siguiente modo int=0-N. Luego se mira
si el bit 30 o 29 o 28 o 27 de int es uno. Con esto se obtendrá un índice que podrá tomar
cualquier valor entre 0 y 4.
0 si ni el bit 30 ni el 29 ni el 28 ni el 27 valen 1
1 si ni el bit 30 ni el 29 ni el 28 valen 1 y el 27 vale 1
2 si ni el bit 30 ni el 29 valen 1 y el 28 vale 1
3 si el bit 30 no vale 1 y el 29 vale 1
4 si el bit 30 vale 1
En esto caso particular el índice (idx) toma el valor 4. Después, se calcula el
exponente desnormalizado del número flotante del siguiente modo:
ExpD=127+26+idx=0x9D y finalmente se desplazan todos los bits de N hacia la derecha
tanta veces como indique el índice idx para obtener la mantisa desnormalizada.
Introducimos 4 ceros 0100 1010 1010 1010 1010 1111 1111 1111
Ceros introducidos 0000 0100 1010 1010 1010 1010 1111 1111 1111
Una vez llegado a este punto hay que formar el número desnomalizado el cual está
formado por el bit de signo, 8 bits para representar el exponente desnormalizado y 28
bits para representar la mantisa desnormalizada. En este caso particular, el signo es 0, el
exponente es 0x9D y la mantisa esta formada por los 28 bits de más peso de N después
del desplazamiento. Se puede ver a simple vista que al hacer el desplazamiento hay
algunos bits que van a desaparecer, en este caso particular son los bits coloreados en
rosa, y esto solo ocurre cuando el número a convertir es muy grande. En el caso de
Copro estos bits se van a despreciar pero más adelante en este apartado se comprobará si
Microblaze ignora estos bits también. El número desnormalizado es:
‘0’&0x9D&0x4AAAAFF.
El paso siguiente es el de normalizar el número cogiendo la mantisa
desnomalizada e introduciendo tantos ceros por la derecha como sea necesario hasta que
el 1 de más hacia la izquierda se coloque al bit de más peso.
Mantisa desnormaliza=0100 1010 1010 1010 1010 1111 1111 Introducir 1 cero
Mantisa normaliza =1001 0101 0101 0101 0101 1111 1110Cero introducido
El siguiente paso es coger los 24 bits de mayor peso de la mantisa y redondear con
los bits n+3, n+2 y n+1 (bits coloreados en magenta). El método de redondeo utilizado
es el siguiente.
Se suma uno a la mantisa si el número binario representado por los
bits n+3, n+2 y n+1 es mayor o igual a 100b(4d).
La mantisa se deja igual si el número binario representado por los
bits n+3, n+2 y n+1 es menor o igual a 011b(3d).
Por tanto, la mantisa normalizada es 1001 0101 0101 0101 0110 0000 y el
exponente normalizado es el exponente desnormalizado + 1 – el numero de ceros
introducciones por la derecha de la mantisa desnormalizada ExpN=ExpD+1-1=0x9D.
Una vez la mantisa y el exponente están normalizados se genera el número flotante
normalizado concatenando el bit de signo, exponente normalizado y los 23 bits de menos
peso de la mantisa normalizada ya que el bit de mayor peso es el llamado bit oculto (bit
coloreado en rojo), el cual siempre vale 1 a excepción del numero 0 donde vale 0, y
nunca se utiliza en la representación.
Signo Exponente Mantisa Número representado en
hexadecimal
0 10011101 1001 0101 0101 0101 0110 0000 0x4E955560
A continuación se simulará la conversión entero a flotante tanto en Copro como en
Microblaze de algunos números comprendidos entre 0x01AAAFFF y 0x4AAAAF30 ya
que en este rango de números es donde primero, se pueden perder bits los cuales se
utilizan para calcular el sticky bit y segundo, los bits los cuales se utilizan para calcular
el bit de redondeo pueden ser diferentes de 0.
Antes de empezar utilizaremos las abreviaciones m(0) para definir el bit menos
significativo (LSB) de la mantisa normalizada y R,G,SB para definir los bits de
redondeo, en inglés Round Bit, el bit de guarda, en inglés, Guard Bit y Sticky Bit
respectivamente.
Caso 1: rango de 0x01AAAFFF a 0x01AAAFF0
Número entero Bits
pedidos
Bit
m(0),R,
G,SB
Bit
redond
eo
Copro
Número
flotante Copro
Número
flotante
Microblaze
Diferencia
0x01AAAFFF No hay 1 100 1 0x4BD557800 0x4BD55800 No hay
0x01AAAFFE No hay 1 000 0 0x4BD557FF 0x4BD557FF No hay
0x01AAAFFD No hay 0 100 1 0x4BD557FF 0x4BD557FE Si hay
0x01AAAFFC No hay 0 000 0 0x4BD557FE 0x4BD557FE No hay
0x01AAAFFB No hay 1 100 1 0x4BD557FE 0x4BD557FE No hay
0x01AAAFFA No hay 1 000 0 0x4BD557FD 0x4BD557FD No hay
0x01AAAFF9 No hay 0 100 1 0x4BD557FD 0x4BD557FC Si hay
0x01AAAFF8 No hay 0 000 0 0x4BD557FC 0x4BD557FC No hay
0x01AAAFF7 No hay 1 100 1 0x4BD557FC 0x4BD557FC No hay
0x01AAAFF6 No hay 1 000 0 0x4BD557FB 0x4BD557FB No hay
0x01AAAFF5 No hay 0 100 1 0x4BD557FB 0x4BD557FA Si hay
0x01AAAFF4 No hay 0 000 0 0x4BD557FA 0x4BD557FA No hay
0x01AAAFF3 No hay 1 100 1 0x4BD557FA 0x4BD557FA No hay
0x01AAAFF2 No hay 1 000 0 0x4BD557F9 0x4BD557F9 No hay
0x01AAAFF1 No hay 0 100 1 0x4BD557F9 0x4BD557F8 Si hay
0x01AAAFF0 No hay 0 000 0 0x4BD557F8 0x4BD557F8 No hay
Caso 2: rango de 0x02AAAFFF a 0x02AAAFF0
Número entero Bits
pedidos
Bit
m(0),R,
G,SB
Bit
redond
eo
Copro
Número
flotante Copro
Número
flotante
Microblaze
Diferencia
0x02AAAFFF No hay 1 110 1 0x4C2AAC00 0x4C2AAC00 No hay
0x02AAAFFE No hay 1 100 1 0x4C2AAC00 0x4C2AAC00 No hay
0x02AAAFFD No hay 1 010 0 0x4C2AABFF 0x4C2AABFF No hay
0x02AAAFFC No hay 1 000 0 0x4C2AABFF 0x4C2AABFF No hay
0x02AAAFFB No hay 0 110 1 0x4C2AABFF 0x4C2AABFF No hay
0x02AAAFFA No hay 0 100 1 0x4C2AABFF 0x4C2AABFE Si hay
0x02AAAFF9 No hay 0 010 0 0x4C2AABFE 0x4C2AABFE No hay
0x02AAAFF8 No hay 0 000 0 0x4C2AABFE 0x4C2AABFE No hay
0x02AAAFF7 No hay 1 110 1 0x4C2AABFE 0x4C2AABFE No hay
0x02AAAFF6 No hay 1 100 1 0x4C2AABFE 0x4C2AABFE No hay
0x02AAAFF5 No hay 1 010 0 0x4C2AABFD 0x4C2AABFD No hay
0x02AAAFF4 No hay 1 000 0 0x4C2AABFD 0x4C2AABFD No hay
0x02AAAFF3 No hay 0 110 1 0x4C2AABFD 0x4C2AABFD No hay
0x02AAAFF2 No hay 0 100 1 0x4C2AABFD 0x4C2AABFC Si hay
0x02AAAFF1 No hay 0 010 0 0x4C2AABFC 0x4C2AABFC No hay
0x02AAAFF0 No hay 0 000 0 0x4C2AABFC 0x4C2AABFC No hay
Caso 3: rango de 0x04AAAFFF a 0x04AAAFF0
Número entero Bits
pedidos
Bit
m(0),R,
G,SB
Bit
redond
eo
Copro
Número
flotante Copro
Número
flotante
Microblaze
Diferencia
0x04AAAFFF No hay 1 111 1 0x4C955600 0x4C955600 No hay
0x04AAAFFE No hay 1 110 1 0x4C955600 0x4C955600 No hay
0x04AAAFFD No hay 1 101 1 0x4C955600 0x4C955600 No hay
0x04AAAFFC No hay 1 100 1 0x4C955600 0x4C955600 No hay
0x04AAAFFB No hay 1 011 0 0x4C9555FF 0x4C9555FF No hay
0x04AAAFFA No hay 1 010 0 0x4C9555FF 0x4C9555FF No hay
0x04AAAFF9 No hay 1 001 0 0x4C9555FF 0x4C9555FF No hay
0x04AAAFF8 No hay 1 000 0 0x4C9555FF 0x4C9555FF No hay
0x04AAAFF7 No hay 0 111 1 0x4C9555FF 0x4C9555FF No hay
0x04AAAFF6 No hay 0 110 1 0x4C9555FF 0x4C9555FF No hay
0x04AAAFF5 No hay 0 101 1 0x4C9555FF 0x4C9555FF No hay
0x04AAAFF4 No hay 0 100 1 0x4C9555FF 0x4C9555FE Si hay
0x04AAAFF3 No hay 0 011 0 0x4C9555FE 0x4C9555FE No hay
0x04AAAFF2 No hay 0 010 0 0x4C9555FE 0x4C9555FE No hay
0x04AAAFF1 No hay 0 001 0 0x4C9555FE 0x4C9555FE No hay
0x04AAAFF0 No hay 0 000 0 0x4C9555FE 0x4C9555FE No hay
En los 3 casos anteriores se puede ver que hay algunas diferencias entre la
conversión hecha por Copro y Microblaze. Esta diferencia esta en el cálculo del bit de
redondeo ya que de momento no se pierden bits los cuales se utilizan para calcular el
sticky bit. Si se analizan las tablas anteriores se puede deducir que Microblaze utiliza el
siguiente método para el cálculo del bit de redondeo.
Se suma uno a la mantisa si el número binario representado por los
bits R, G y SB es mayor a 100b(4d).
Se suma uno a la mantisa si el número binario representado por los
bits R, G y SB es igual a 100b(4d) y el bit de menos peso de la
mantisa normalizada (m(0)) es igual a 1.
La mantisa se deja igual si el número binario representado por los
bits R, G y SB es igual a 100b(4d) y el bit de menos peso de la
matisa normalizada (m(0)) es igual a 0
La mantisa se deja igual si el número binario representado por los
bits R, G y SB es menor o igual a 011b(3d).
A continuación, los números enteros a convertir serán suficientemente mayores
para que se pierdan bits durante la conversión. Esto permitirá ver el comportamiento de
Microblaze cuando esto sucede y también se podrá comparar con el comportamiento de
Copro.
Caso 4: rango de 0x08AAAFFF a 0x08AAAFE0
Número entero Bits
pedidos
Bit
m(0),R,
G,SB
Bit
redond
eo
Copro
Número flotante
Copro
Número flotante
Microblaze
Diferencia
0x08AAAFFF 1 1 111 1 0x4D0AAB00 0x4D0AAB00 No hay
0x08AAAFFE 0 1 111 1 0x4D0AAB00 0x4D0AAB00 No hay
0x08AAAFFD 1 1 110 1 0x4D0AAB00 0x4D0AAB00 No hay
0x08AAAFFC 0 1 110 1 0x4D0AAB00 0x4D0AAB00 No hay
0x08AAAFFB 1 1 101 1 0x4D0AAB00 0x4D0AAB00 No hay
0x08AAAFFA 0 1 101 1 0x4D0AAB00 0x4D0AAB00 No hay
0x08AAAFF9 1 1 100 1 0x4D0AAB00 0x4D0AAB00 No hay
0x08AAAFF8 0 1 100 1 0x4D0AAB00 0x4D0AAB00 No hay
0x08AAAFF7 1 1 011 0 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFF6 0 1 011 0 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFF5 1 1 010 0 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFF4 0 1 010 0 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFF3 1 1 001 0 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFF2 0 1 001 0 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFF1 1 1 000 0 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFF0 0 1 000 0 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFEF 1 0 111 1 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFEE 0 0 111 1 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFED 1 0 110 1 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFEC 0 0 110 1 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFEB 1 0 101 1 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFEA 0 0 101 1 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFE9 1 0 100 1 0x4D0AAAFF 0x4D0AAAFF No hay
0x08AAAFE8 0 0 100 1 0x4D0AAAFF 0x4D0AAAFE Si hay
0x08AAAFE7 1 0 011 0 0x4D0AAAFE 0x4D0AAAFE No hay
0x08AAAFE6 0 0 011 0 0x4D0AAAFE 0x4D0AAAFE No hay
0x08AAAFE5 1 0 010 0 0x4D0AAAFE 0x4D0AAAFE No hay
0x08AAAFE4 0 0 010 0 0x4D0AAAFE 0x4D0AAAFE No hay
0x08AAAFE3 1 0 001 0 0x4D0AAAFE 0x4D0AAAFE No hay
0x08AAAFE2 0 0 001 0 0x4D0AAAFE 0x4D0AAAFE No hay
0x08AAAFE1 1 0 000 0 0x4D0AAAFE 0x4D0AAAFE No hay
0x08AAAFE0 0 0 000 0 0x4D0AAAFE 0x4D0AAAFE No hay
Caso 5: rango de 0x1AAAAFFF a 0x1AAAAFCC
Número entero Bits
pedidos
Bit
m(0),R,
G,SB
Bit
redond
eo
Número flotante
Copro
Número flotante
Microblaze
Diferencia
Copro
0x1AAAAFFF 11 1 111 1 0x4DD55580 0x4DD55580 No hay
0x1AAAAFFE 10 1 111 1 0x4DD55580 0x4DD55580 No hay
0x1AAAAFFD 01 1 111 1 0x4DD55580 0x4DD55580 No hay
0x1AAAAFFC 00 1 111 1 0x4DD55580 0x4DD55580 No hay
0x1AAAAFFB 11 1 110 1 0x4DD55580 0x4DD55580 No hay
0x1AAAAFFA 10 1 110 1 0x4DD55580 0x4DD55580 No hay
0x1AAAAFF9 01 1 110 1 0x4DD55580 0x4DD55580 No hay
0x1AAAAFF8 00 1 110 1 0x4DD55580 0x4DD55580 No hay
0x1AAAAFF3 11 1 100 1 0x4DD55580 0x4DD55580 No hay
0x1AAAAFF2 10 1 100 1 0x4DD55580 0x4DD55580 No hay
0x1AAAAFF1 01 1 100 1 0x4DD55580 0x4DD55580 No hay
0x1AAAAFF0 00 1 100 1 0x4DD55580 0x4DD55580 No hay
0x1AAAAFEF 11 1 011 0 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFEE 10 1 011 0 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFED 01 1 011 0 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFEC 00 1 011 0 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFDF 11 0 111 1 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFDE 10 0 111 1 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFDD 01 0 111 1 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFDC 00 0 111 1 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFDB 11 0 110 1 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFDA 10 0 110 1 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFD9 01 0 110 1 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFD8 00 0 110 1 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFD3 11 0 100 1 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFD2 10 0 100 1 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFD1 01 0 100 1 0x4DD5557F 0x4DD5557F No hay
0x1AAAAFD0 00 0 100 1 0x4DD5557F 0x4DD5557E Si hay
0x1AAAAFCF 11 0 011 0 0x4DD5557E 0x4DD5557E No hay
0x1AAAAFCE 10 0 011 0 0x4DD5557E 0x4DD5557E No hay
0x1AAAAFCD 01 0 011 0 0x4DD5557E 0x4DD5557E No hay
0x1AAAAFCC 00 0 011 0 0x4DD5557E 0x4DD5557E No hay
Caso 6: rango de 0x2AAAAFFC a 0x2AAAAF9A
Número entero Bits
pedidos
Bit
m(0),R,
G,SB
Bit
redond
eo
Copro
Número flotante
Copro
Número flotante
Microblaze
Diferencia
0x2AAAAFFC 100 1 111 1 0x4E2AAAC0 0x4E2AAAC0 No hay
0x2AAAAFFA 010 1 111 1 0x4E2AAAC0 0x4E2AAAC0 No hay
0x2AAAAFF9 001 1 111 1 0x4E2AAAC0 0x4E2AAAC0 No hay
0x2AAAAFF8 000 1 111 1 0x4E2AAAC0 0x4E2AAAC0 No hay
0x2AAAAFE7 111 1 100 1 0x4E2AAAC0 0x4E2AAAC0 No hay
0x2AAAAFE6 110 1 100 1 0x4E2AAAC0 0x4E2AAAC0 No hay
0x2AAAAFE5 101 1 100 1 0x4E2AAAC0 0x4E2AAAC0 No hay
0x2AAAAFE4 100 1 100 1 0x4E2AAAC0 0x4E2AAAC0 No hay
0x2AAAAFE3 011 1 100 1 0x4E2AAAC0 0x4E2AAAC0 No hay
0x2AAAAFE2 010 1 100 1 0x4E2AAAC0 0x4E2AAAC0 No hay
0x2AAAAFE1 001 1 100 1 0x4E2AAAC0 0x4E2AAAC0 No hay
0x2AAAAFE0 000 1 100 1 0x4E2AAAC0 0x4E2AAAC0 No hay
0x2AAAAFDC 100 1 011 0 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFDA 010 1 011 0 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFD9 001 1 011 0 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFD8 000 1 011 0 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFBC 100 0 111 1 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFBA 010 0 111 1 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFB9 001 0 111 1 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFB8 000 0 111 1 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFA7 111 0 100 1 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFA6 110 0 100 1 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFA5 101 0 100 1 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFA4 100 0 100 1 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFA3 011 0 100 1 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFA2 010 0 100 1 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFA1 001 0 100 1 0x4E2AAABF 0x4E2AAABF No hay
0x2AAAAFA0 000 0 100 1 0x4E2AAABF 0x4E2AAABE Si hay
0x2AAAAF9C 100 0 011 0 0x4E2AAABE 0x4E2AAABE No hay
0x2AAAAF9A 010 0 011 0 0x4E2AAABE 0x4E2AAABE No hay
0x2AAAAF99 001 0 011 0 0x4E2AAABE 0x4E2AAABE No hay
0x2AAAAF98 000 0 011 0 0x4E2AAABE 0x4E2AAABE No hay
Caso 7: rango de 0x4AAAAFF8 a 0x4AAAAF30
Número entero Bits
pedidos
Bit
m(0),R,
G,SB
Bit
redond
eo
Copro
Número flotante
Copro
Número flotante
Microblaze
Diferencia
0x4AAAAFF8 1000 1 111 1 0x4E955560 0x4E955560 No hay
0x4AAAAFF4 0100 1 111 1 0x4E955560 0x4E955560 No hay
0x4AAAAFF2 0010 1 111 1 0x4E955560 0x4E955560 No hay
0x4AAAAFF1 0001 1 111 1 0x4E955560 0x4E955560 No hay
0x4AAAAFF0 0000 1 111 1 0x4E955560 0x4E955560 No hay
0x4AAAAFC8 1000 1 100 1 0x4E955560 0x4E955560 No hay
0x4AAAAFC4 0100 1 100 1 0x4E955560 0x4E955560 No hay
0x4AAAAFC2 0010 1 100 1 0x4E955560 0x4E955560 No hay
0x4AAAAFC1 0001 1 100 1 0x4E955560 0x4E955560 No hay
0x4AAAAFC0 0000 1 100 1 0x4E955560 0x4E955560 No hay
0x4AAAAFB8 1000 1 011 0 0x4E95555F 0x4E95555F No hay
0x4AAAAFB4 0100 1 011 0 0x4E95555F 0x4E95555F No hay
0x4AAAAFB2 0010 1 011 0 0x4E95555F 0x4E95555F No hay
0x4AAAAFB1 0001 1 011 0 0x4E95555F 0x4E95555F No hay
0x4AAAAFB0 0000 1 011 0 0x4E95555F 0x4E95555F No hay
0x4AAAAF78 1000 0 111 1 0x4E95555F 0x4E95555F No hay
0x4AAAAF74 0100 0 111 1 0x4E95555F 0x4E95555F No hay
0x4AAAAF72 0010 0 111 1 0x4E95555F 0x4E95555F No hay
0x4AAAAF71 0001 0 111 1 0x4E95555F 0x4E95555F No hay
0x4AAAAF70 0000 0 111 1 0x4E95555F 0x4E95555F No hay
0x4AAAAF48 1000 0 100 1 0x4E95555F 0x4E95555F No hay
0x4AAAAF44 0100 0 100 1 0x4E95555F 0x4E95555F No hay
0x4AAAAF42 0010 0 100 1 0x4E95555F 0x4E95555F No hay
0x4AAAAF41 0001 0 100 1 0x4E95555F 0x4E95555F No hay
0x4AAAAF40 0000 0 100 1 0x4E95555F 0x4E95555E Si hay
0x4AAAAF38 1000 0 011 0 0x4E95555E 0x4E95555E No hay
0x4AAAAF34 0100 0 011 0 0x4E95555E 0x4E95555E No hay
0x4AAAAF32 0010 0 011 0 0x4E95555E 0x4E95555E No hay
0x4AAAAF31 0001 0 011 0 0x4E95555E 0x4E95555E No hay
0x4AAAAF30 0000 0 011 0 0x4E95555E 0x4E95555E No hay
En los casos 1, 2 y 3 ya se ha visto que el punto crítico es cuando los bits R, G y
SB valen 100, entonces el redondeo depende del bit de menos peso de la mantisa, es
decir, solo hay redondeo si m(0) es 1. En los casos 4, 5, 6 y 7 se puede ver que cuando
los bits R, G y SB valen 100 y cualquiera de los bits perdidos vale 1, entonces hay
redondeo contradiciendo la observación de los casos 1, 2 y 3. Con estas dos
observaciones se deduce que Microblaze calcula el sticky bit haciendo la OR entre los
bits perdidos y el bit de menos peso de la mantisa desnormalizada. Por lo tanto, si los
bits R, G y SB valen 100 y cualquiera de los bits perdidos vale 1, SB pasa a valer 1
convirtiendo R, G y SB en 101 y por ser mayor que 100 se hace redondeo.
2.2.2 Adaptación en Copro
Una vez se ha completado el análisis de la operación conversión entero a flotante
realizada con los dos procesadores se va a hacer una lista de las diferencias que hay.
La primera diferencia entre Microblaze y Copro que se ha encontrado esta en el
método del cálculo del redondeo. La segunda diferencia esta en el cálculo del sticky bit.
Se ha comprobado que Microblaze pone el sticky bit a 1 si alguno de los bits perdidos
vale 1, en caso contrario este vale 0.
En cuanto a la primera diferencia se refiere, Copro se ha modificado para que su
comportamiento sea el siguiente:
Se suma uno a la mantisa si el número binario representado por los
bits R, G y SB es mayor a 100b(4d).
Se suma uno a la mantisa si el número binario representado por los
bits R, G y SB es igual a 100b(4d) y el bit de menos peso de la
mantisa normalizada (m(0)) es igual a 1.
La mantisa se deja igual si el número binario representado por los
bits R, G y SB es igual a 100b(4d) y el bit de menos peso de la
mantisa normalizada (m(0)) es igual a 0
La mantisa se deja igual si el número binario representado por los
bits R, G y SB es menor o igual a 011b(3d).
En cuanto a la segunda diferencia se refiere, Copro se ha modificado de tal modo
que si alguno de los bits perdidos vale 1 se hará la OR exclusiva de los bits perdidos y el
bit de menos peso de la mantisa desnomalizada.
2.3 Operación Add/Sub
Otras operaciones que se puede realizar tanto con Copro como con Microblaze son
las operaciones suma y resta de números representados en coma flotante. Estas dos
operaciones utilizan un algoritmo de cálculo muy parecido y por esa razón se verán
juntas en este apartado. El rango de números en coma flotante que se puede representar
en 32 bits para estas operaciones es de 0xFF800000 hasta 0x7F800000.
2.3.1 Comportamiento Copro VS Microblaze
El planteamiento en este apartado va a ser diferente que en el apartado 3.2.1 ya que
en esta caso Microblaze pone a disposición del usuario señales de entrada, de salida e
intermedias utilizadas en su código fuente cuando realiza cualquier de estas dos
operaciones. Entonces, primero se mostraran la señales más significativas de Microblaze
así como el código en C ejecutado por el mismo para poder ver los valores que toman
dichas señales, luego, se explicará como Copro hace la operación suma y resta y
finalmente, se observarán algunos casos claves y algunas de las señales más
significativas utilizadas por Microblaze cuando realiza dichas operaciones y se
compararán con las de Copro.
Las señales más significativas de Microblaze son:
ex_start_fpu: início operación (1 bit)
fpu_op: identificador operación (3 bits)
ex_op1: operando 1 (32 bits)
ex_op2: operando 2 (32 bits)
mem_manta_2: mantisa operando 2 (24 bits)
mem_mantb_2: mantisa operando 1 (24 bits)
mem_manta_3: mantisa adaptada operando 2 (27 bits)
mem_mantb_3: mantisa adaptada operando 1 (27 bits)
mem_manta_sticky_3_cmb: sticky bit (1 bit)
mem_mant_addsub_res_4: mantisa resultado (27 bits)
fpu_result: resultado final (32 bits)
El código utilizado es:
El método general el cual se basa Copro para realizar la operación suma y resta es
el de transformar los operandos en números basados en exponente y mantisa, igualar
exponentes y finalmente hacer la suma o la resta de mantisas. Seguidamente se muestra
un ejemplo basado en números enteros.
Operando A=2000; Operando B=200; Resultado = Operando A + Operando B
Se identifica el Operando mayor y se transforma en un número
formado por un exponente y una mantisa.
const float b[2]={0x4B800000, 0x4B800000};
const float ax[2]={0x3FC00001, 0x3FC00000};
float *p2=(float*)0x2E0000;
float *p3=(float*)0x2D0000;
float *p4=(float*)0x2C0000;
for(int j=0; j<2; j++)
{p2[j]=ax[j];
p3[j]=b[j];}
for(int j=0; j<2; j++)
{p4[j]=p2[j]+p3[j]; }
Operando A=1,953125 * 2^10
Se transforma el Operando B en un número formado por un
exponente y una mantisa pero el exponente tiene que ser del mismo
orden que el del operando mayor. En este caso es el Operando A.
Operando B=0,1953125 * 2^10
Se deja el exponente el cual es común para los dos operandos y se
suman o restan las mantisas. En esta caso particular
Exponente=2^10
Mantisa=1,953125 + 0,1953125 =2,1484375
Resultado=2,1484375 * 2^10
Una vez visto el método general pasaremos a ver el método inicial para hacer
sumas y restas en Copro. Hace falta remarcar que si no se encuentra ninguna diferencia
entre los métodos de Copro y Microblaze, el método inicial de Copro pasará a ser el
método final.
El primer paso consiste en hacer una comprobación de posibles excepciones en el
siguiente orden.
Si cualquier de los operandos A y/o B son un NAN (not a number)
entonces, el resultado de la operación es NAN.
Si no, si el operando A y B son infinitos del mismo signo el resultado final
es infinito. En caso de que sean de signo diferente el resultado final es un
NAN.
Finalmente, si no se cumple ninguna excepción anterior se comprueba si el
operando A o el operando B es infinito y ninguno de ellos es NAN, el
resultado final es infinito.
Seguidamente, si no se da ninguna de las excepciones previas se procede del
siguiente modo.
Sea el operando A= 0x4B800000 (0100 1011 1000 0000 0000 0000 0000 0000b) y
B= 0x3FC00001 (0011 1111 1100 0000 0000 0000 0000 0001b) siendo el bit de más
peso (MSB) el de más a la izquierda, primero se obtiene el signo de los dos operandos el
cual viene dado por el bit 31 que es el MSB y se hace la XOR entre ellos para saber si
los operandos tienen el mismo signo o signo diferente (sig_dif=sig_a XOR sig_b). A
continuación se restan los exponentes y las mantisas de los dos operandos. En este caso
particular los valores que se obtienen son:
Signo Exponente Mantisa Numero representado en
hexadecimal
0 1001 0111 1000 0000 0000 0000 0000 0000 0x4B800000
0 0111 1111 1100 0000 0000 0000 0000 0001 0x3FC00001
Sig_dif = sig_a XOR sig_b = 0 xor 0 = 0 (1 bit)
Exp_dif = exp_a – exp_b = 0x97-0x7F =0x18 (9 bits)
man_dif = man_a – man_b =0x800000 – 0xC00001 =0x1BFFFFF (25 bits)
Una vez calculada la diferencia entre los signos, mantisas y exponentes de los dos
operandos, se averigua que operando, en valor absoluto, es más grande. Esto se decide
comprobando si el bit de más peso de la señal Exp_dif es igual a 1 o, si este es 0, se
comprueba si el bit de más peso de la señal man_dif es igual a 1. En cualquier de estos
dos casos se puede afirmar que |b|>|a|, en caso contrario |a|>|b|.
En el caso de que |a|>|b|, el signo y el exponente del resultado de la operación son
directamente el signo y el exponente del operando A. El valor absoluto de la diferencia
de exponentes es directamente la diferencia de exponentes. Finalmente, antes de sumar o
restar las mantisas hay que tener en cuenta el sticky bit el cual se obtendrá de la mantisa
del operando mas pequeño, por lo tanto, la mantisa más grande (man_high) será la
mantisa del operando A y la más pequeña (man_low) será la mantisa del operando B.
Sin embargo, si |b|>|a|, el signo de la operación se calcula del siguiente modo
sig_res=(not mode and sig_b) or (mode and not sig_b). Si la operación es suma, la señal
mode es igual a 0 pero si es una resta, mode es igual a 1. El exponente del resultado de la
operación viene dado por el exponente del operando B. El valor absoluto de la diferencia
de exponentes se calcula cambiando de signo el resultado de dicha diferencia.
Finalmente, antes de sumar o restar las mantisas hay que tener en cuenta el sticky bit el
cual se obtendrá de la mantisa del operando mas pequeño, por lo tanto, en esta caso la
mantisa más grande (man_high) será la mantisa del operando B y la más pequeña
(man_low) será la mantisa del operando A. Otro aspecto a tener en cuenta en cualquier
de los dos casos anteriores es que si el valor absoluto de la diferencia de exponentes es
mayor que 25 se deja en 25, es decir, que Exp_absdif nunca debería tomar un valor
mayor a 25 ya que así se agiliza el cálculo del sticky bit como se va a ver a continuación.
Antes de hacer la suma o resta de las mantisas, hay que adaptarlas.
Por un lado, la mantisa man_high se adapta añadiendo un cero a la parte izquierda
el cual será el bit de carry y luego se añaden 3 ceros más a la parte derecha, es decir, que
la mantisa man_high pasa a tener un total de 28 bits. Por el otro lado, la mantisa
man_low se utiliza para calcular el sticky bit porque es la mantisa la cual puede sufrir un
desplazamiento de bits debido a la introducción de ceros por izquierda si el valor
absoluto de la diferencia de exponentes es diferente de cero. Entonces, si el valor
absoluto de la diferencia de exponentes es inferior a 3 el sticky bit es cero, si es mayor o
igual a 3 se comprueba tantos bits de la man_low como indique dicha deferencia
empezando por el bit 3 y si alguno de ellos es 1 el sticky bit vale 1. Luego, se añaden 3
ceros a la parte derecha de man_low y se introducen tantos ceros por la izquierda como
indique el valor absoluto de la diferencia de exponentes. Seguidamente, se forma la
mantisa man_low con la concatenación de un bit de carry a cero, los 26 bits de más peso
de la mantisa man_low después del desplazamiento si lo hay y el sticky bit.
Finalmente se suma o resta las mantisas dependiendo del modo de operación y con
el resultado se mira si el bit de carry es 1. Si es así, se hace mantisa(1)=mantisa(1) or
mantisa(0). Esto se hace para no perder el valor de sticky bit cuando se normaliza el
número. Luego, este resultado conjuntamente con el signo y el exponente se forma el
número desnormalizado.
En este caso particular se puede ver que |a|>|b| con lo cual se obtienen los
siguientes valores:
Exp_absdif= exp_a = 0x18h=24d como que 24 no es > 25 Exp_absdif=24d
Sig_res= sig_a = 0
Exp_res = exp_a = 0x97h
Man_high= ‘0’& Man_high & ”000”= 0x4000000
Man_low= 0xC00001
Siendo Exp_absdif=24 se mira si algún bit de Man_low, empezando por el bit 3, es
igual a 1. En este caso esto es cierto, por lo tanto, sticky bit=1.
Man_low_desplazada = shift to right(Man_low & ”000”, Exp_absdif)
Introducimos 24 ceros 1100 0000 0000 0000 0000 0001 000
Ceros introducidos000 0000 0000 0000 0000 0000 0110
Man_low_desplazada = 0x0000006
Bits perdidos0 0000 0000 0000 0000 0001 000
Sticky bit = OR (Bits perdidos) = 1
Man_low =’0’& 26 bits de más peso de desplazada & sticky bit
Man_low = 0000 0000 0000 0000 0000 0000 0111 = 0x0000007
Man=Man_high + Man_low = 0x4000000 + 0x0000007 = 0x4000007
Finalmente el número desnormalizado es: ‘0’&0x97&0x4000007.
Ahora, se tiene que normalizar el número tal y como se ha hecho en el apartado de
la operación de conversión entero a flotante. Esto se hace separando la mantisa e
introduciendo tantos ceros por la derecha como sea necesario para dejar a uno al bit de
más peso.
Mantisa desnormaliza=0100 0000 0000 0000 0000 0000 0111 Introducir 1 cero
Mantisa normalizada=1000 0000 0000 0000 0000 0000 1110Cero introducido
El siguiente paso es coger los 24 bits de mayor peso de la mantisa y redondear con
los bits n+3, n+2 y n+1 (bits coloreados en magenta). El método de redondeo utilizado
es el siguiente.
Se suma uno a la mantisa si el número binario representado por los
bits n+3, n+2 y n+1 es mayor o igual a 100b(4d).
La mantisa se deja igual si el número binario representado por los
bits n+3, n+2 y n+1 es menor o igual a 011b(3d).
Por tanto, la mantisa normalizada es 1000 0000 0000 0000 0000 0001 y el
exponente normalizado es el exponente desnormalizado + 1 – el numero de ceros
introducciones por la derecha de la mantisa desnormalizada ExpN=ExpD+1-1=0x97.
Una vez la mantisa y el exponente están normalizados se genera el número flotante
normalizado concatenando el bit de signo, exponente normalizados y los 23 bits de
menos peso de la mantisa normalizada ya que el bit de mayor peso llamado bit oculto
(bit coloreado en rojo) nunca se utiliza en la representación. Este bit se sabe que siempre
vale 1 a excepción del número 0.
Signo Exponente Mantisa Numero representado en
hexadecimal
0 10010111 1000 0000 0000 0000 0000 0001 0x4B800001
A continuación se verán algunas operaciones realizadas por Microblaze de las
cuales se podrá deducir su comportamiento y compararlo con el de Copro.
Caso A:
Operando 1 =0x4B800000
Operando 2 =0x3FC00000
Figura 23: Señales de Microblaze cuando realiza la suma de 0x4B800000 y 0x3FC00000.
En la figura 23 se puede ver que la diferencia de exponentes representada por la
señal men_exp_absubb es calculada del mismo modo que se calcula en Copro.
men_exp_absubb = exp_1 – exp_2 = 0x97-0x7F =0x18
También se ve claramente que las mantisas 1 y 2 vienen representadas por
mem_mantb_2 =0x800000 y mem_manta_2=0xC00000 respectivamente. Entonces, se
puede deducir que la mantisa adaptada B se forma del siguiente modo.
mem_manta_3 = ‘0’& mem_mantb_2 & ”000”= 0x4000000
Por otro lado, la mantisa adaptada A es un poco más compleja de calcular ya que
esta sufre un dezplazamiento y, con los bits desplazados, se calcula el sticky bit.
Entonces, se supondrá que se calcula del mismo modo que en Copro pero, en este caso,
en vez de utilizar las señales de en Copro se utilizaran las señales de Microblaze.
mem_manta_2’ = shift to right(mem_manta_2 & ”000”, men_exp_absubb)
Introducimos 24 ceros 1100 0000 0000 0000 0000 0000 000
Ceros introducidos 000 0000 0000 0000 0000 0000 0110
mem_manta_2’ = 0x0000006
Bits perdidos0 0000 0000 0000 0000 0000 000
Sticky bit = OR (Bits perdidos) = 0 el sticky bit se puede ver claramente en la
figura anterior que también vale 0
mem_mantb_3 =’0’& 26 bits de más peso de mem_manta_2’ & sticky bit
mem_mantb_3=0x0000006
Seguidamente, se suman las mantisas adaptadas y se pone el resultado en
men_mant_addsu_res_4=0x4000006.
Se puede ver que el resultado de la suma de mantisas adaptadas es el mismo que el
obtenido por Microblaze, por tanto, se da por válido el procedimiento seguido hasta
ahora por Copro. El número desnormalizado obtenido hasta ahora es:
‘0’&0x97&0x4000006.
Finalmente, este número tiene que ser normalizado y como que la etapa de
normalización en Copro siempre es la misma, no importa el tipo de operación que sea,
en el caso Mircroblaze se hará la misma hipótesis. Entonces, es resultado final obtenido
es mismo que el obtenido en Copro
Signo Exponente Mantisa Numero representado en
hexadecimal
0 10010111 1000 0000 0000 0000 0000 0001 0x4B800001
Caso B:
Operando 1 =0x4B800000
Operando 2 =0x3FC00001
Figura 24: Señales de Microblaze cuando realiza la suma de 0x4B800000 y 0x3FC00001.
En la figura 24 se puede observar que el cálculo es prácticamente el mismo que
en el caso anterior con la única variación de que el sticky bit toma el valor 1. Entonces,
siguiendo los mismos pasos que en Copro pero utilizando las señales de Microblaze se
puede ver que el resultado final es el mismo.
mem_manta_2’ = shift to right(mem_manta_2 & ”000”, men_exp_absubb)
Introducimos 24 ceros 1100 0000 0000 0000 0000 0001 000
Ceros introducidos 000 0000 0000 0000 0000 0000 0110
mem_manta_2’ = 0x0000006
Bits perdidos0 0000 0000 0000 0000 0001 000
Sticky bit= OR(Bits perdidos)=1
mem_mantb_3 =’0’& 26 bits de más peso de mem_manta_2’ & sticky bit
mem_mantb_3=0x0000007
Por tanto, se puede afirmar que sticky bit vale 1 si algún de los bits perdidos es
uno, en caso contrario este toma el valor 0.
Caso C:
Operando 1 =0x4B800000
Operando 2 =0x3F800000
Figura 25: Señales de Microblaze cuando realiza la suma de 0x4B800000 y 0x3FC00000.
Caso D:
Operando 1 =0x4B800000
Operando 2 =0x3F800001
Figura 26: Señales de Microblaze cuando realiza la suma de 0x4B800000 y 0x3FC00000.
Caso E:
Operando 1 =0x4B800001
Operando 2 =0x3F800000
Figura 27: Señales de Microblaze cuando realiza la suma de 0x4B800000 y 0x3FC00000.
En las figuras 25, 26 y 27 se vuelve a confirmar la hipótesis que ya se había
confirmado en el apartado 3.2.1 donde se deduce claramente como Microblaze hace el
cálculo del bit de redondeo.
En el caso C los bits m(0) R,G,SB valen 0100 respectivamente por lo tanto, a m(0)
no se le va a sumar 1 por que este vale 0 y el conjunto de bit R,G,SB es igual a 100, es
decir, no va haber redondeo. Al concatenar el bit de signo, el exponente y la mantisa
normalizada el resultado final de la suma es 0x4B800000.
En el caso D se puede ver el contrario del caso C ya que los bits m(0) R,G,SB
valen 1100 respectivamente por lo tanto, a m(0) se le va a sumar 1 por que este vale 1 y
el conjunto de bit R,G,SB es igual a 100, es decir, si va haber redondeo. Al concatenar el
bit de signo, el exponente y la mantisa normalizada la cual se la ha sumado el resultado
final de la suma es 0x4B800001.
Finalmente, en el caso E se puede ver que si R,G,SB son igual o mayor que 101 se
suma 1 a m(0) independientemente del valor que tenga m(0), es decir, también va haber
redondeo. En esta caso m(0) es 1 y al concatenar el bit de signo, el exponente y la
mantisa normalizada la cual se le ha sumado 1 el resultado final de la suma es
0x4B800002.
2.3.2 Adaptación en Copro
Como ya se ha visto en el caso de la conversión entero a flotante una de las
diferencias que hay esta en el modo en que Microblaze hace el cálculo del bit de
redondeo, por lo tanto, Copro se ha modificado para que su comportamiento en dicho
cálculo sea el siguiente:
Se suma uno a la mantisa si el número binario representado por los
bits R, G y SB es mayor a 100b(4d).
Se suma uno a la mantisa si el número binario representado por los
bits R, G y SB es igual a 100b(4d) y el bit de menos peso de la
mantisa normalizada (m(0)) es igual a 1.
La mantisa se deja igual si el número binario representado por los
bits R, G y SB es igual a 100b(4d) y el bit de menos peso de la
matisa normalizada (m(0)) es igual a 0
La mantisa se deja igual si el número binario representado por los
bits R, G y SB es menor o igual a 011b(3d).
2.4 Operación Mul
La operación multiplicación es un poco más compleja de realizar en comparación
con las operaciones de conversión entero flotante, suma y resta. Por esa razón tarda
algunos ciclos de reloj más en ejecutarse. Al igual que las operaciones vistas hasta ahora
esta se puede realizar tanto con Copro como con Microblaze utilizando números
representados en coma flotante. El rango de números en coma flotante que se puede
representar en 32 bits para esta operación es de 0xFF800000 hasta 0x7F800000.
2.4.1 Comportamiento Copro VS Microblaze
El planteamiento en este apartado va a ser el mismo que en el apartado 3.3.1 ya
que en este caso Microblaze también pone a disposición del usuario señales de entrada,
de salida e intermedias utilizadas en su código fuente cuando realiza dicha operación.
Entonces, primero se mostraran las señales más significativas de Microblaze así como el
código en C ejecutado por el mismo para poder visualizar dichas señales, luego, se
explicará como Copro hace la operación multiplicación y finalmente, se observarán
algunos casos claves y algunas de las señales más significativas utilizadas por
Microblaze cuando realiza dicha operación y se compararán con las de Copro.
Las señales más significativas de Microblaze son:
ex_start_fpu: início operación (1 bit)
ex_fpu_op: identificador operación (3 bits)
ex_op1: operando 1 (32 bits)
ex_op2: operando 2 (32 bits)
ex_manta_1: mantisa operando 2 (24 bits)
ex_mantb_1: mantisa operando 1 (24 bits)
ex_a_oper: parte alta mantisa 1(18bits)
ex_b_oper: parte baja mantisa 1(18bits)
ex_c_oper: parte alta mantisa 2(18bits)
ex_d_oper: parte baja mantisa 2(18bits)
mem_prod_bd_3: multiplica entre ex_b_oper y ex_d_oper (32 bits)
mem_prod_ad_3: multiplica entre ex_a_oper y ex_d_oper (24 bits)
mem_prod_bc_3: multiplica entre ex_b_oper y ex_c_oper (24 bits)
mem_prod_ac_3: multiplica entre ex_a_oper y ex_c_oper (16 bits)
mem_mul_sticky_bit_vec_5_cmb: vector sticky bit (21 bits)
mem_mul_sticky_bit_5_cmb: sticky bit (1 bit)
mem_mul_res_4: mantisa desnormalizada (27 bits)
fpu_result: resultado final (32 bits)
El código utilizado es:
El método general el cual se basa Copro para realizar la operación mulplicación es
el de transformar los operandos en números basados en exponente y mantisa, multiplicar
const float b[2]={0x41200001, 0x4B800000};
const float ax[2]={0x41200001, 0x3FC00000};
float *p2=(float*)0x2E0000;
float *p3=(float*)0x2D0000;
float *p4=(float*)0x2C0000;
for(int j=0; j<2; j++)
{p2[j]=ax[j];
p3[j]=b[j];}
for(int j=0; j<2; j++)
{p4[j]=p2[j]*p3[j]; }
las mantisas y finalmente sumar los exponentes. Seguidamente se muestra un ejemplo
basado en números enteros.
Operando A=2; Operando B=200; Resultado = Operando A * Operando B
Se transforman los operandos A y B en números formados por un
exponente y una mantisa.
Operando A=1 * 2^1
Operando B=1,5625 * 2^7
Se suman los exponentes y si multiplican las mantisas.
Exponente=(2^1) + (2^7)= 2^8
Mantisa=1 * 1,5625 = 1,5625
Resultado=1,5625 * 2^8
Una vez visto el método general pasaremos a ver el método inicial para hacer
multiplicaciones en Copro. El primer paso consiste en hacer multiplicaciones parciales
de las mantisas. Estas se separan en dos partes y se multiplican entre ellas como si de
una multiplicación numérica se tratará pero, antes de empezar con el cálculo se hacen
una serie de comprobaciones de los contenidos de los operandos para detectar posibles
excepciones.
Inicialmente se hace la comprobación de excepciones en el siguiente orden.
Si cualquier de los operandos A y/o B son un NAN (not a number)
entonces, el resultado de la operación es NAN.
Si no, si el operando A es infinito y el operando B es cero o viceversa, el
resultado final es un NAN.
Si no, si entre el operando A y el operando B uno toma cualquier valor
diferente de infinito o NAN y el otro 0, el resultado final es cero.
Finalmente, si no se cumple ninguna excepción anterior se comprueba si el
operando A y/o el operando B es infinito, el resultado final es infinito.
En caso de que no se de ninguna de estas excepciones se procede del siguiente
modo.
Sea el operando A= 0x41200001 (0100 0001 0010 0000 0000 0000 0000 0001b) y
B= 0x41200001 (0100 0001 0010 0000 0000 0000 0000 0001b) siendo el bit de más
peso (MSB) el de más a la izquierda, primero se obtiene el signo de los dos operandos el
cual viene dado por el bit 31 que es el MSB y se hace la XOR entre ellos para saber el
signo del resultado de la operación (signo_s12=sig_a XOR sig_b). A continuación se
calcula el exponente de la operación del siguiente modo exp_s12=exp_a + exp_b –127.
Seguidamente, se hace la multiplicación parcial de las mantisas multiplicando la parte
baja de los dos operandos, la parte alta del operando 1 con la parte baja del operando 2,
la parte alta del operando 2 con la parte baja del operando 1 y finalmente la parte alta de
los dos operandos. En este caso particular los valores que se obtienen son:
Signo Exponente Mantisa Numero representado en
hexadecimal
0 1000 0010 1010 0000 0000 0000 0000 0001 0x41200001
0 1000 0010 1010 0000 0000 0000 0000 0001 0x41200001
signo_s12 = sig_a XOR sig_b = 0 xor 0 = 0 (1 bit)
exp_s12 = exp_a + exp_b -127 = 0x82+0x82 -127 =0x85 (8 bits)
ab00_s12 = op_a (11downto0) * op_b (11downto0) = 0x000001 (24 bits)
ab01_s12 =’1’&op_a (22downto12) * op_b (11downto) = 0x000A00 (24 bits)
ab10_s12 =op_a (11downto) * ’1’&op_b (22downto12) = 0x000A00 (24 bits)
ab11_s12 =’1’&op_a (22downto12) * ’1’&op_b (22downto12) = 0x640000 (24
bits)
Una vez obtenidos los valores de las multiplicaciones parciales, se calcula la
mantisa sumando dichos valores del siguiente modo.
r0 = ab00_s12(11 downto 0)
r1=("00"&ab00_s12(23downto12))+("00"&ab01_s12(11downto0))+("00"&ab10_
s12(11 downto 0))
r2=r1(13 downto 12)+("00"&ab01_s12(23 downto 12))+("00"&ab10_s12(23
downto 12)) + ("00"&ab11_s12(11 downto 0))
r3 = r2(13 downto 12) + ab11_s12(23 downto 12)
r = r3 & r2 (11 downto 0) & r1(11 downto 0) & r0=0x640001400001
Finalmente, se obtiene una mantisa de 48 bits la cual se tiene de truncar ya que
Copro trabaja con mantisas desnormalizadas de 28 bits. Entonces, los 28 bits de más
peso son los que forman la mantisa desnormalizada y los 20 bits de menos peso se
desprecian sin tener en cuenta el sticky bit.
Man = r(47 downto 20) = 0x6400014 (28 bits)
Una vez llegado ha este punto se forma el número desnormalizado concatenando
el signo, exponente y mantisa obtenido. Esto es: ‘0’&0x85&0x6400014.
Ahora, se tiene que normalizar el número tal y como se ha hecho en los apartados
3.2.1 y 3.3.1. Esto se hace separando la mantisa e introduciendo tantos ceros por la
derecha como sea necesario para dejar un uno al bit de más peso.
Mantisa desnormaliza=0110 0100 0000 0000 0000 0001 0100 Introducir 1 cero
Mantisa normalizada=1100 1000 0000 0000 0000 0010 1000Cero introducido
El siguiente paso es coger los 24 bits de mayor peso de la mantisa y redondear con
los bits n+3, n+2 y n+1 (bits coloreados en magenta). El método de redondeo utilizado
es el siguiente.
Se suma uno a la mantisa si el número binario representado por los
bits n+3, n+2 y n+1 es mayor o igual a 100b(4d).
La mantisa se deja igual si el número binario representado por los
bits n+3, n+2 y n+1 es menor o igual a 011b(3d).
Por tanto, la mantisa normalizada es 1100 1000 0000 0000 0000 0010 y el
exponente normalizado es el exponente desnormalizado + 1 – el numero de ceros
introducciones por la derecha de la mantisa desnormalizada ExpN=ExpD+1-1=0x85.
Una vez la mantisa y el exponente están normalizados se genera el número flotante
normalizado concatenando el bit de signo, exponente normalizado y los 23 bits de menos
peso de la mantisa normalizada ya que el bit de mayor peso llamado bit oculto (bit
coloreado en rojo) nunca se utiliza en la representación. Este bit se sabe que siempre
vale 1 a excepción del número 0.
Signo Exponente Mantisa Numero representado en
hexadecimal
0 10000101 1100 1000 0000 0000 0000 0010 0x42C80002
A continuación se verán algunas operaciones realizadas por Microblaze de las
cuales se podrá deducir su comportamiento y compararlo con el de Copro.
Caso A:
Operando 1 =0x41200001
Operando 2 =0x41200001
Figura 28: Señales de Microblaze cuando realiza la multiplicación de 0x41200001 y 0x41200001.
La figura 28 muestra el cálculo realizado por Microblaze con los mismos
operandos utilizados en la descripción del comportamiento de Copro cuando realiza la
operación multiplicación. Para empezar, se puede ver que las multiplicaciones parciales
en Microblaze tienen una pequeña diferencia respecto a las multiplicaciones parciales en
Copro. En Copro la parte alta y la parte baja de la mantisa de los operandos están
formadan por un vector de 12 bits cada uno mientras que en Microblaze, la parte alta
está formada por un vector de 8 bits y la parte baja por uno de 16 bits pero esto no tiene
ninguna implicación significativa si la suma de la multiplicaciones parciales se realiza
de forma correcta. El resultado de dichas multiplicaciones en Microblaze es:
mem_prod_ad_2 =’1’&ex_op1 (22downto16) * ex_op2 (15downto0) = 0x0000A0
mem_prod_bd_2 = ex_op1 (15downto0) * ex_op2 (15downto0) = 0x00000001
mem_prod_ac_2= ex_op1 (15downto0) * ’1’&ex_op2 (22downto15) = 0x0000A0
mem_prod_bc_2=’1’&ex_op1 (22downto16) * ’1’&ex_op2 (22downto16) =
0x6400
Un aspecto importante a tener en cuenta es valor que toma la mantisa
desnomalizada en Microblaze. Esta es de 0x6400015 mientras que en Copro es
0x6400014. Esto hace pensar que Microblaze hace de algun modo el cálculo del sticky
bit y por esa razón, la mantisa de este ejemplo calculada por Microblaze difiere un poco
respecto a la calculada por Copro. Por lo tanto, se va hacer la hipótesis de que dicho
cálculo se hace con los bits que no van a forma parte de la mantisa desnormalizada.
A continuación se muestra el vector de bits utilizado para el cálculo del sticky bit
ya que así lo indica el nombe utilizado por Microblaze. también se muestra la mantisa
desnomalizada con el sticky bit ya aplicado.
mem_mul_sticky_bit_5_cmb=0x00001
mem_mul_res_2= r(47 downto 20) = 0x6400015 (28 bits)
Al no tener ninguna señal que represente el exponente, se supondrá que
Microblaze calcula el exponente del mismo modo que Copro. Entonces, el número
desnormalizado obtenido hasta ahora es: ‘0’&0x85&0x6400015.
Finalmente, este número tiene que ser normalizado y como que la etapa de
normalización en Copro siempre es la misma, no importa el tipo de operación que sea,
aquí se hará la misma hypótesis para Microblaze. Entonces, se puede ver que el conjunto
de bit R, G y SB es mayor que 100 por lo tanto se suma 1 a m(0). El resultado final
obtenido es siguiente
Signo Exponente Mantisa Número representado en
hexadecimal
0 10000101 1100 1000 0000 0000 0000 0011 0x42C80003
Ahora si se comparan los resultados de las operaciones realizadas con los dos
procesadores, se puede ver que son diferentes. 0x42C80003 en Microblaze frente a
0x42C80002 en Copro.
Para confimar la hipótesis se planteará un caso nuevo en el cual se va a modificar
los operandos de tal modo que los 20 bits de menos pesos de la mantisa de 48 bits van a
ser 0. Entonces si el sticky bit toma el valor 0 se podrá dar por buena la hipótesis hecha
en este caso. El operando 1 tendra el valor 0x4FFFFFF0 y el operando 2 el valor
0x3F900000.
Caso B:
Operando 1 =0x4FFFFFF0
Operando 2 =0x3F900000
Figura 29: Señales de Microblaze cuando realiza la multiplicación de 0x4FFFFFF0 y 0x3F900000.
La figura 29 es perfecta para confirmar que el cálculo del sticky bit se hace con los
bits que no se van a tener en cuenta al formar la mantisa desnormalizada. Además,
conjuntamente con la figura 28, se deduce que el sticky bit toma el valor 1 si alguno de
los bits mencionados anteriormente vale 1, en caso contrario el sticky bit toma el valor 0.
Seguidamente, se mostraran algunos casos más para ver como Microblaze hace el
cálculo del redondeo.
Caso C:
Operando 1 =0x4FFFFFE8
Operando 2 =0x3F900000
Figura 30: Señales de Microblaze cuando realiza la multiplicación de 0x4FFFFFE8 y 0x3F900000.
Caso D:
Operando 1 =0x4FFFFFF8
Operando 2 =0x3F900000
Figura 31: Señales de Microblaze cuando realiza la multiplicación de 0x4FFFFFF8 y 0x3F900000.
Caso E:
Operando 1 =0x4FFFFFFA
Operando 2 =0x3F900000
Figura 32: Señales de Microblaze cuando realiza la multiplicación de 0x4FFFFFFA y 0x3F900000.
En las figuras 30,31 y 32, se confirma una vez más la hipótesis que ya se había
confirmado en el apartado 3.2.1 y 3.3.1 donde se deduce claramente como Microblaze
hace el cálculo del bit de redondeo.
Se puede ver que en la figura 30 los bits m(0), R, G y SB valen 0100
respectivamente por lo tanto, a m(0) no se le va a sumar 1 por que este vale 0 y el
conjunto de bit R, G y SB es igual a 100, es decir, no va haber redondeo.
Mantisa desnormaliza=100 0111 1111 1111 1111 1001 0100 Introducir 0 cero
Mantisa desnormaliza=100 0111 1111 1111 1111 1001 0100
Mantisa normalizada=1000 1111 1111 1111 1111 0010
Al no tener ninguna señal que represente el exponente, se supondrá que
Microblaze calcula el exponente del mismo modo que Copro. Entonces, el exponente
desnormalizado es exp_op1+exp_op2–127ExpD=0x9F+0x7F-0x7F=0x9F. Ahora hay
que normalizar el exponente del siguiente modo: exponente desnormalizado + 1 – el
número de ceros introducciones por la derecha de la mantisa desnormalizada hasta que
el bit de más peso sea 1. Se puede ver claramente que en este caso el bit de más peso de
mantisa desnomalizada es 1, por lo tanto, no será necesario introducir ceros por la
derecha de la mantisa desnomalizada ExpN=ExpD+1-0=0xA0.
Al concatenar el bit de signo, el exponente normalizado y la mantisa normalizada
se obtiene el número ‘0’&0xA0&0x8FFFF2 el cual se tiene de agrupar de tal modo que
represente un número definido por la norma IEEE 754.
Signo Exponente Mantisa Numero representado en
hexadecimal
0 10100000 1000 1111 1111 1111 1111 0010 0x500FFFF2
El bit coloreado en rojo es el bit oculto el cual no se utiliza en las representaciones,
por lo tanto el resultado final de la multiplicación es 0x500FFFF2.
En la figura 31 se puede ver el contrario de la figura 27 ya que los bits m(0), R, G
y SB valen 1100 respectivamente por lo tanto, a m(0) se le va a sumar 1 por que este
vale 1 y el conjunto de bit R, G y SB es igual a 100, es decir, si va haber redondeo.
Mantisa desnormaliza=100 0111 1111 1111 1111 1101 1100 Introducir 0 cero
Mantisa desnormaliza=100 0111 1111 1111 1111 1101 1100
Mantisa normalizada=1000 1111 1111 1111 1111 1100
Al no tener ninguna señal que represente el exponente, se supondrá que
Microblaze calcula el exponente del mismo modo que Copro. Entonces, siguiendo los
pasos seguidos en el caso C se obtiene que el exponente desnormalizado es
exp_op1+exp_op2–127ExpD=0x9F+0x7F-0x7F=0x9F y que exponente normalizado
es ExpN=ExpD+1-0=0xA0.
Al concatenar el bit de signo, el exponente normalizado y la mantisa normalizada
se obtiene el número ‘0’&0xA0&0x8FFFFC el cual se tiene de agrupar de tal modo que
forme un número definido por la norma IEEE 754.
Signo Exponente Mantisa Número representado en
hexadecimal
0 10100000 1000 1111 1111 1111 1111 1100 0x500FFFFC
El bit coloreado en rojo es el bit oculto el cual no se utiliza en las representaciones,
por lo tanto el resultado final de la multiplicación es 0x500FFFFC.
Finalmente, en el caso E se puede ver que si R, G y SB son igual o mayor que 101
se suma 1 a m(0) independientemente del valor que tenga m(0), es decir, también va
haber redondeo.
Mantisa desnormaliza=100 0111 1111 1111 1111 1110 0101 Introducir 0 cero
Mantisa desnormaliza=100 0111 1111 1111 1111 1110 0101
Mantisa normalizada=1000 1111 1111 1111 1111 1101
Al no tener ninguna señal que represente el exponente, se supondrá que
Microblaze calcula el exponente del mismo modo que Copro. Entonces, siguiendo los
pasos seguidos en el caso C y D se obtiene que el exponente desnormalizado es
exp_op1+exp_op2–127ExpD=0x9F+0x7F-0x7F=0x9F y que exponente normalizado
es ExpN=ExpD+1-0=0xA0.
Al concatenar el bit de signo, el exponente normalizado y la mantisa normalizada
se obtiene el número ‘0’&0xA0&0x8FFFFD el cual se tiene de agrupar de tal modo que
quede un numero definido por la norma IEEE 754.
Signo Exponente Mantisa Número representado en
hexadecimal
0 10100000 1000 1111 1111 1111 1111 1101 0x500FFFFD
El bit coloreado en rojo es el bit oculto el cual no se utiliza en las representaciones,
por lo tanto el resultado final de la multiplicación es 0x500FFFFD.
2.4.2 Adaptación en Copro
En este caso se harán dos adaptaciones.
La primera adaptación es la de tener en cuenta el sticky bit tal y como hace
Microblaze, es decir, que una vez hecha la multiplicación parcial con la cual se obtendrá
una mantisa de 48 bits, se cogerán los 28 bits de más peso para formar la mantisa
desnomalizada y los 20 bits restantes serán lo que se utilizarán para el cálculo del sticky
bit. Este tomará el valor 1 si alguno de estos 20 bits es 1, en caso contrario, tomará el
valor 0.
La segunda adaptación será totalmente igual que la vista en los casos de las
operaciones entero a flotante, suma y resta. La modificación es la siguiente:
Se suma uno a la mantisa si el número binario representado por los
bits R, G y SB es mayor a 100b(4d).
Se suma uno a la mantisa si el número binario representado por los
bits R, G y SB es igual a 100b(4d) y el bit de menos peso de la
mantisa normalizada (m(0)) es igual a 1.
La mantisa se deja igual si el número binario representado por los
bits R, G y SB es igual a 100b(4d) y el bit de menos peso de la
mantisa normalizada (m(0)) es igual a 0
La mantisa se deja igual si el número binario representado por los bits R, G y SB
es menor o igual a 011b(3d).
2.5 Operación Div
La operación división es la más compleja que se verá en este trabajo. Esta puede
tarda 2, 3 o 10 veces más en ejecutarse que cualquiera de las operaciones vistas
anteriormente. Al igual que las operaciones conversión entero a flotante, suma, resta y
multiplicación, esta también se puede realizar tanto con Copro como con Microblaze
utilizando números representados en coma flotante. El rango de números en coma
flotante que se puede representar en 32 bits para esta operación es de 0xFF800000 hasta
0x7F800000.
2.5.1 Comportamiento Copro VS Microblaze
El planteamiento en este apartado va a ser igual que en los apartados de la suma y
resta y multiplicación ya que en este caso Microblaze también pone a disposición del
usuario señales de entrada, de salida e intermedias utilizadas en su código cuando realiza
dicha operación. Entonces, primero se mostraran la señales más significativas de
Microblaze así como el código en C ejecutado por Microblaze para poder visualizar
dichas señales, luego, se explicará como Copro hace la operación división y finalmente
se observarán algunos casos claves y algunas de las señales más significativas utilizadas
por Microblaze cuando realiza dichas operaciones y se compararán con las de Copro.
Las señales más significativas de Microblaze son:
Mem_start_div: inicio operación (1 bit)
ex_fpu_op: identificador operación (3 bits)
ex_op1: operando 1 (32 bits)
ex_op2: operando 2 (32 bits)
ex_manta_1: mantisa operando 2 (24 bits)
ex_mantb_1: mantisa operando 1 (24 bits)
mem_q: cociente (25bits)
mem_r: dividendo (25bits)
mem_b: divisor (25bits)
mem_diff_cmb: resta (25bits)
mem_div_sticky_bit_cmb: sticky bit (21 bits)
mem_div_res_4: mantisa resultado (27 bits)
fpu_result: resultado final (32 bits)
El código utilizado es:
El método general el cual se basa Copro para realizar la operación division es
bastente similar al de la multiplicación con la diferencia que las mantisas se dividen y
los exponentes se restan. Seguidamente se muestra un ejemplo basado en números
enteros.
Operando A=2; Operando B=200; Resultado = Operando A / Operando B
Se transforman los operandos A y B en números formados por un
exponente y una mantisa.
Operando A=1 * 2^1
Operando B=1,5625 * 2^7
Se suman los exponentes y si multiplican las mantisas.
Exponente=(2^1) - (2^7)= 2^-6
Mantisa=1 /1,5625 = 0,64
Resultado=0,64* 2^-6
El método inicial a seguir para realizar divisiones en Copro es:
Inicialmente se hace la comprobación de posibles excepciones en el siguiente
orden.
Si cualquier de los operandos A y/o B son un NAN (not a number)
entonces, el resultado de la operación es NAN.
Si no, si los dos operandos A y B son infinitos o cero el resultado final es
un NAN.
Si no, si el operando A es infinito y el operando B es cero el resultado final
es infinito con el mismo signo que el operando a.
Finalmente, si no se cumple ninguna excepción anterior se comprueba si el
operando A es 0 o si el operando B es infinito. En cualquier caso el
resultado final es 0.
En caso de que no se de ninguna de estas excepciones se procede del siguiente
modo.
Sea el operando A= 0x42F80000 (0100 0010 1111 1000 0000 0000 0000 0000b) y
B= 0x40000000 (0100 0000 0000 0000 0000 0000 0000 0000b) siendo el bit de más
const float b[2]={0x42F80000, 0x4B800000};
const float ax[2]={0x40000000, 0x3FC00000};
float *p2=(float*)0x2E0000;
float *p3=(float*)0x2D0000;
float *p4=(float*)0x2C0000;
for(int j=0; j<2; j++)
{p2[j]=ax[j];
p3[j]=b[j];}
for(int j=0; j<2; j++)
{p4[j]=p2[j]/p3[j]; }
peso (MSB) el de más a la izquierda, primero se obtiene el signo de los dos operandos el
cual viene dado por el bit 31 que es el MSB y se hace la XOR entre ellos para saber el
signo del resultado de la operación (sig_s12=sig_a XOR sig_b). A continuación se
calcula el exponente de la operación del siguiente modo exp_s12=exp_a - exp_b +127.
Luego, se separan las mantisas de los dos operando tal y como se indica a continuación.
a_divsqrt= ‘1’ & op_a (22downto0) y b_divsqrt= ‘1’ & op_b (22downto0) y
seguidamente, se pone la señal del sticky bit a 1 si el exponente de la operación
calculado es inferior a 31, en caso contrario, esta toma el valor 0.
Una vez llegado a este punto, es momento de empezar la división. Para realizar
divisiones en diseños digitales existen diferentes algoritmos los cuales se pueden
clasificar en dos categorías. Algoritmos lentos y algoritmos rápidos. En la categoría de
lentos podemos encontrar el algoritmo Restoring Division, Non Restoring Division y
SRT, por otro lado, en la categoría de rápidos podemos encontrar el algoritmo de Newto-
Raphson y Goldschmidt.
El algoritmo Restoring Division consiste en restar el dividendo con el divisor. Si
el resultado de esta resta es negativo se pone un 0 al bit de más peso de la señal q
llamada cociente y se deshace el cambio de la resta sumando el resultado de la resta con
el divisor. En caso contrario, se pone un 1 al bit de más peso de q y no se deshace el
cambio de la resta. Finalmente, sea cual sea el caso, se multiplica por dos el contenido
de la resta, es decir, desplazamos un bits hacia la izquierda la variable P que contiene el
resultado de la resta entre dividendo y divisor. Esta interación se repite n veces hasta que
se hayan recorrido todos los bits del cociente.
Algoritmo Restoring Division
P: dividendo; D: divisor
for i = n-1..0 // n: número de bit a recorrer
P:= P – D // Restar dividendo con divisor
if P >= 0 then
q(i) := 1 // Almacenar 1 a q
else q(i) := 0 // Almacenar 0 a q
P := P + D // Deshacer cambio
end if
P=2P // Desplazar dividendo nuevo un bit a la izquierda
end do
En el caso de Copro se utiliza un algoritmo basado en el Restoring Division
algorithm pero con algunas modificaciones para que sea más eficiente y rápido. La
primera modificación consiste en añadir una condición de finalización de la división si el
resto es 0 ya que si esto sucede las iteraciones siguentes serian completamente iguales.
Consecuentemente, el cociente se inicializa a 0 porque si la división es interrumpida por
obtener un resto a cero, los bits restantes hasta el bit de menos peso van a ser cero. La
segunda modificación es utilizar una variable intermedia la cual tomará el valor de la
resta y se copiará al dividiendo si, y solo si, el signo de dicha resta es positiva. Con esta
modificación se optimizará el diseño ahorrando lógica digital para deshacer el resultado
de la resta de P y D en caso de que este sea negativo.
Algoritmo Restoring Division modificado
Cmp:resta; P: dividendo; D: divisor
i=n-1 // n el número de bits de q
q=0 // Inicialización de q
do
Cmp:= P – D // Restar dividendo con divisor
if Cmp >= 0 then
q(i) := 1 // Almacenar 1 a q
P=Cmp // Nuevo dividendo
else do nothing
end if
P=2P // Desplazar dividendo nuevo un bit a la izquierda
i=i--
While (cmp!=0 and i >=0)
En este caso particular los valores que se obtienen son:
Signo Exponente Mantisa Número representado en
hexadecimal
0 1000 0101 1111 1000 0000 0000 0000 0000 0x42F80000
0 1000 0000 1000 0000 0000 0000 0000 0000 0x40000000
signo_s12 = sig_a XOR sig_b = 0 xor 0 = 0 (1 bit)
exp_s12 = exp_a + exp_b -127 = 0x85-0x80 -127 =0x84 (8 bits)
a_divsqrt= ‘1’ & op_a (22downto0)= 0xF80000 (24 bits)
b_divsqrt= ‘1’ & op_b (22downto0)= 0x800000 (24 bits)
r=”00” & a_divsqrt =0x0F80000 (26 bits)
b=”00” & b_divsqrt =0x0800000 (26 bits)
Como se puede apreciar el dividendo (r) y el divisor (b) se les ha añadido dos
ceros a la izquierda para detectar los resultados negativos al restar.
q=0x0000000 (26 bits)
q es el cociente y se inicializa a cero.
Iteración número 1
index=26 bit de q a rellenar
Cmp=(r-b)= 0x0780000
Como que cmp’left=0 se pone el bit de más peso de Q a 1
q=0x2000000
Cogemos los 26 bits de menos peso de la resta en r y b (Cmp) y los pones a r
r= 0x0780000
Se desplaza r en un bit hacia la izquierda
r=0x0F00000
Iteración número 2
index=25 bit de q a rellenar
Cmp=(r-b)= 0x0700000
Como que cmp’left=0 se pone el bit 25 de Q a 1
q=0x3000000
Cogemos los 26 bits de menos peso de la resta en r y b (Cmp) y los pones a r
r= 0x0700000
Se desplaza r en un bit hacia la izquierda
r=0x0E00000
Iteración número 3
index=24 bit de q a rellenar
Cmp=(r-b)= 0x0600000
Como que cmp’left=0 se pone el bit 24 de Q a 1
q=0x3800000
Cogemos los 26 bits de menos peso de la resta en r y b (Cmp) y los pones a r
r= 0x0600000
Se desplaza r en un bit hacia la izquierda
r=0x0C00000
Iteración número 4
index=23 bit de q a rellenar
Cmp=(r-b)= 0x0400000
Como que cmp’left=0 se pone el bit 23 de Q a 1
q=0x3C00000
Cogemos los 26 bits de menos peso de la resta en r y b (Cmp) y los pones a r
r= 0x0400000
Se desplaza r en un bit hacia la izquierda
r=0x0800000
Iteración número 5
index=22 bit de q a rellenar
Cmp=(r-b)= 0x0000000
Como que cmp’left=0 se pone el bit 22 de Q a 1
q=0x3E00000
Cogemos los 26 bits de menos peso de la resta en r y b (Cmp) y los pones a r
r= 0x0000000
Se desplaza r en un bit hacia la izquierda
r=0x0000000
Y esta es la ultima iteración ya que el resultado de la resta entre dividendo y
divisor es cero.
A continuación se genera la mantisa desnormalizada concatenando q y el sticky bit
tal y como se muestra a continuación.
Q_divsqrt=q&SB=0x3E00000&’1’=0x7C00001
Luego se forma el número desnormalizado. Se puede ver que se ha añadido un
zero a la izquierda de la mantisa desnomalizada para que esta esté formada de 28 bits.
Res=sig & exp & ‘0’& Q_divsqrt= 0 1000 0100 0111 1100 0000 0000 0000 0000
0001=’0’&0x84&0x7C00001
Ahora, se tiene que normalizar el número tal y como se ha hecho en los apartados
3.2.1, 3.3.1 y 3.4.1. Esto se hace separando la mantisa e introduciendo tantos ceros por la
derecha como sea necesario para dejar un uno al bit de más peso.
Mantisa desnormaliza=0111 1100 0000 0000 0000 0000 0001 Introducir 1 cero
Mantisa normalizada=1111 1000 0000 0000 0000 0000 0010Cero introducido
El siguiente paso es coger los 24 bits de mayor peso de la mantisa y redondear con
los bits n+3, n+2 y n+1 (bits coloreados en magenta). El método de redondeo utilizado
es el siguiente.
Se suma uno a la mantisa si el número binario representado por los
bits n+3, n+2 y n+1 es mayor o igual a 100b(4d).
La mantisa se deja igual si el número binario representado por los
bits n+3, n+2 y n+1 es menor o igual a 011b(3d).
Por tanto, la mantisa normalizada es 1111 1000 0000 0000 0000 0000 y el
exponente normalizado es el exponente desnormalizado + 1 – el numero de ceros
introducciones por la derecha de la mantisa desnormalizada ExpN=ExpD+1-1=0x84.
Una vez la mantisa y el exponente están normalizados se genera el número flotante
normalizado concatenando el bit de signo, exponente normalizado y los 23 bits de menos
peso de la mantisa normalizada ya que el bit de mayor peso llamado bit oculto (bit
coloreado en rojo) nunca se utiliza en la representación. Este bit se sabe que siempre
vale 1 a excepción del número 0.
Signo Exponente Mantisa Número representado en
hexadecimal
0 10000100 1111 1000 0000 0000 0000 0000 0x42780000
A continuación se verán algunas operaciones realizadas por Microblaze de las
cuales se podrá deducir su comportamiento y compararlo con el de Copro. También es
importante mencionar que no se va a mostrar todas las iteraciones que se realicen en las
operaciones si no que en algunos casos se mostraran la primeras y las últimas y, en otros
casos solo se mostraran la últimas iteraciones.
Caso A:
Operando 1 =0x42F80000
Operando 2 =0x40000000
Figura 33: Señales de Microblaze cuando realiza la división de 0x42F80000 y 0x40000000.
Figura 34: Señales de Microblaze cuando realiza la división de 0x42F80000 y 0x40000000.
Comparando los resultados de las dos figuras anteriores y el análisis de la división
realizada por Copro en este mismo apartado, se puede apreciar muy bien que los
comportamientos de los dos procesadores son bastante diferentes. Por un lado, Copro
necesita 5 iteraciones para completar la división de las mantisas ya que cuando el resto
es 0, este finaliza dicha división, mientras que Microblaze hace tantas iteraciones como
bits tenga la señal men_q la cual es el cociente. Entonces, una primera conclusión que se
puede extraer es que el método que utiliza Copro es más rápido si la división es exacta.
Otra diferencia que se puede ver es el valor que toma la mantisa resultante después
de hacer la división y concaternarla con el sticky bit. En el caso de Microblaze la
mantisa resultante toma el valor 0x7C00002 y en el caso de Copro la mantisa toma el
valor 0x7C00001. Mirando la figura 30, se puede ver que el sticky bit vale 0 si 24 bits de
menos peso de r son cero, en caso contrario, este toma el valor 1 mientras que, en el caso
de Copro, el sticky bit toma el valor 0 o 1 dependiendo del exponente resultante.
Entonces, este será el primer cambio que se va a aplicar a Copro para el cálculo del
sticky bit sea igual que el de Microblaze. Pero, aunque se aplicase este cambio los
resultados de las mantisas resultantes de los dos procesadores serian todavía diferente.
Entonces esto hace pensar en que los dos procesadores podrían utilizar diferentes
algoritmos de cálculo para la división, por lo tanto se propondrá la hipótesis de que
Microblaze utiliza el algoritmo Non Restoring Division.
El paso siguiente es hacer un análisis como el que se ha hecho para Copro en este
mismos apartado pero siguiendo el algoritmo Non Restoring Division. La particularidad
que tiene el algoritmo Non Restoring Division es que cada bit del cociente se rellena con
1 o –1 dependiendo de si la resta entre dividendo y divisor es positiva o negativa. Luego
el cociente se normaliza dejándolo con 0 y 1.
Algoritmo Non Restoring Division
r: dividendo; b: divisor
i=n //n el número de bits de q’
i=i--
do if r >= 0 then
q’(i) := 1 //Almacenar 1 a q’
else q’(i) := -1 //Almacenar -1 a q’
end if
r:=r – b //Restar dividendo con divisor
r=2*r
i=i--
While ( i >=0)
Finalmente se normaliza el cociente q’ formado por -1 y 1 al cociente normalizado
q formado por 0 y 1. Para normalizar, Se cambian los -1 por 0, se desplaza un bit hacia
la izquierda de la nueva q y se le suma 1.
1,1' q ; q’ formada por -1 y 1
1,0'' q ; q’’ cambiando -1 por 0
1*2 '' qq
Volviendo al ejemplo numérico pero aplicado al algoritmo Non Restoring Division
tenemos:
Sea el operando A= 0x42F80000 (0100 0010 1111 1000 0000 0000 0000 0000b) y
B= 0x40000000 (0100 0000 0000 0000 0000 0000 0000 0000b) siendo el bit de más
peso (MSB) el de más a la izquierda, primero se obtiene el signo de los dos operandos el
cual viene dado por el bit 31 que es el MSB y se hace la XOR entre ellos para saber el
signo del resultado de la operación (sig_s12=sig_a XOR sig_b). A continuación se
calcula el exponente de la operación del siguiente modo exp_s12=exp_a - exp_b +127.
Luego, se separan las mantisas de los dos operando tal y como se indica a continuación.
a_divsqrt= ‘1’ & op_a (22downto0) y b_divsqrt= ‘1’ & op_b (22downto0).
En este caso particular los valores que se obtienen son:
Signo Exponente Mantisa Número representado en
hexadecimal
0 1000 0101 1111 1000 0000 0000 0000 0000 0x42F80000
0 1000 0000 1000 0000 0000 0000 0000 0000 0x40000000
signo_s12 = sig_a XOR sig_b = 0 xor 0 = 0 (1 bit)
exp_s12 = exp_a + exp_b -127 = 0x85-0x80 -127 =0x84 (8 bits)
a_divsqrt= ‘1’ & op_a (22downto0)= 0xF80000 (24 bits)
b_divsqrt= ‘1’ & op_b (22downto0)= 0x800000 (24 bits)
r=’0’ & a_divsqrt =0x0F80000 (25 bits)
b=’0’ & b_divsqrt =0x0800000 (25 bits)
Una vez llegado a este punto, es momento de empezar la división.
Como se puede apreciar el dividendo (r) y el divisor (b) se les ha añadido 1 cero a
la izquierda tal y como lo hace Microblaze para detectar los resultados negativos al hacer
la resta entre r y b. En cuanto al cociente q, este también tiene 25 bits y no se inicializa a
cero ya que se van hacer tantas iteraciones como bits existan en q.
i=25
Se decrementa i
b=0x0800000
r=0x0F80000
Iteración número 1
i=24 bit de q’ a rellenar
Como que r>=0 se pone el bit 24 de q’(i) a 1 y r=r-b= 0x0780000
Luego se desplaza un bit de r hacia la izquierda
r=2*r=0x0F00000
q’=1
Se decrementa i
Iteración número 2
i=23 bit de q’ a rellenar
Como que r>=0 se pone el bit 23 de q’(i) a 1 y r=r-b= 0x0700000
Luego se desplaza un bit de r hacia la izquierda
r=2*r=0x0E00000
q’=11
Se decrementa i
Iteración número 3
i=22 bit de q’ a rellenar
Como que r>=0 se pone el bit 22 de q’(i) a 1 y r=r-b= 0x0600000
Luego se desplaza un bit de r hacia la izquierda
r=2*r=0x0C00000
q’=111
Se decrementa i
Iteración número 4
i=21 bit de q’ a rellenar
Como que r>=0 se pone el bit 21 de q’(i) a 1 y r=r-b= 0x0400000
Luego se desplaza un bit de r hacia la izquierda
r=2*r=0x0800000
q’=1111
Se decrementa i
Iteración número 5
i=20 bit de q’ a rellenar
Como que r>=0 se pone el bit 20 de q’(i) a 1 y r=r-b= 0x0000000
Luego se desplaza un bit de r hacia la izquierda
r=2*r=0x0000000
q’=1 1111
Se decrementa i
Iteración número 6
i=19 bit de q’ a rellenar
Como que r>=0 se pone el bit 19 de q’(i) a 1 y r=r-b= 0x1000000
Luego se desplaza un bit de r hacia la izquierda
r=2*r=0x0000000
q’=1 1111 1
Se decrementa i
Iteración número 7
i=18 bit de q’ a rellenar
Aunque r sea igual que cero se sabe que en la iteración anterior era inferior a 0
Entonces, como que r<0 se pone el bit 18 de q’(i) a -1 y r=r-b= 0x1000000
Luego se desplaza un bit de r hacia la izquierda
r=2*r=0x0000000
q’=1 1111 1-1
Se decrementa i
De aquí hacia delante, todas las iteraciones son iguales, es decir, con el resultado
de la resta negativa, por lo tanto, todos los siguientes bits de q’ serán puestos a –1.
q ’ = 1 1111 1-1-1-1 -1-1-1-1 -1-1-1-1 -1-1-1-1 -1-1-1-1
Ahora hay que normaliza q’ siguiendo los pasos descritos anteriormente, es decir,
cambiando los -1 de q’ por 0, con este paso se obtine q’’. Luego se introduce un 0 por la
derecha de q’’ y finalmente se le suma 1. En este caso particular, los valores que se
obtiene son los siguientes.
q’{-1,1} = 11 1111 -1-1-1-1 -1-1-1-1 -1-1-1-1 -1-1-1-1 -1-1-1-1
q’’{0,1} = 11 1111 0000 0000 0000 0000 0000
q{0,1} = 11 1110 0000 0000 0000 0000 0001
q{0,1} = 0x3E0001
A continuación se genera la mantisa desnormalizada concatenando q y el sticky bit
tal y como se muestra a continuación.
Q_divsqrt=q&SB=0x3E00001&’0’=0x7C00002
Una vez obtenida la mantisa desnomalizada se puede comprobar que es igual a la
obtenida por Microblaze en la figura 34, por lo tanto podemos afirma que Microblaze
utiliza el algoritmo Non Restoring Division.
Siguiendo en este ejemplo y en cuanto a la etapa normalizadora se refiere, se
puede ver que el resultado final de la división realizada por los dos procesadores es el
mismo, hecho que hace pensar que dicha etapa sea la misma que la que se ha utilizado
en las operaciones anteriores. Aunque esto no se puede afirmar ahora mismo, se
trabajara con esta hipótesis la cual se confirmará a continuación con más ejemplos.
Caso B:
Operando 1 =0x4FFFFFF0
Operando 2 =0x3F9000002
Figura 35: Señales de Microblaze cuando realiza la división de 0x4FFFFFF0 y 0x3F9000002.
Partiendo del punto donde la única operación que queda es normalizar el
siguiente número:
Res=sig & exp & ‘0’ & Q_divsqrt= 0 1001 1111 0111 0001 1100 0111 0001 0011
1101=’0’&0x9F&0x71C713D
Se puede ver que los bits m(0), R, G y SB valen 1101 respectivamente por lo tanto,
a m(0) se le va a sumar 1 por que el conjunto de bit R, G y SB es superior a 100, es
decir, va haber redondeo.
Mantisa desnormaliza=0111 0001 1100 0111 0001 0011 1101 Introducir 1 cero
Mantisa desnormaliza=1110 0011 1000 1110 0010 0111 1010
Mantisa normalizada=1110 0011 1000 1110 0010 1000
Al no tener ninguna señal que represente el exponente, se supondrá que
Microblaze calcula el exponente del mismo modo que Copro. Entonces, el exponente
desnormalizado es exp_op1-exp_op2+127ExpD=0x9F-0x7F+0x7F=0x9F. Ahora hay
que normalizar el exponente del siguiente modo: exponente desnormalizado + 1 – el
número de ceros introducciones por la derecha de la mantisa desnormalizada hasta que
el bit de más peso sea 1. Se puede ver claramente que en este caso el bit de más peso de
mantisa desnomalizada es 0, por lo tanto, será necesario introducir 1 cero por la derecha
de la mantisa desnomalizada ExpN=ExpD+1-1=0x9F.
Al concatenar el bit de signo, el exponente normalizado y la mantisa normalizada
se obtiene el número ‘0’&0x9F &0xE38E28 el cual se tiene de agrupar de tal modo que
represente un número definido por la norma IEEE 754.
Signo Exponente Mantisa Número representado en
hexadecimal
0 10011111 1110 0011 1000 1110 0010 1000 0x4FE38E28
El bit coloreado en rojo es el bit oculto el cual no se utiliza en las representaciones,
por lo tanto el resultado final de la multiplicación es 0x4FE38E28.
Caso C:
Operando 1 =0x42EEE000
Operando 2 =0x40000003
Figura 36: Señales de Microblaze cuando realiza la división de 0x42EEE000 y 0x40000003.
En este caso se va a proceder del mismo modo que el caso anterior, es decir,
teniendo en cuenta que la única operación que queda es normalizar el siguiente número:
Res=sig & exp & ‘0’ & Q_divsqrt= 0 1001 1111 0111 0111 0110 1111 1111 1101
0011=’0’&0x84&0x776FFD3
se puede ver que los bits m(0), R, G y SB valen 0011 respectivamente por lo tanto,
a m(0) no se le va a sumar 1 por que el conjunto de bit R, G y SB es inferior a 100, es
decir, no va haber redondeo.
Mantisa desnormaliza=0111 0111 0110 1111 1111 1101 0011 Introducir 1 cero
Mantisa desnormaliza=1110 1110 1101 1111 1111 1010 0110
Mantisa normalizada=1110 1110 1101 1111 1111 1010
Al no tener ninguna señal que represente el exponente, se supondrá que
Microblaze calcula el exponente del mismo modo que Copro. Entonces, el exponente
desnormalizado es exp_op1-exp_op2+127ExpD=0x85-0x80+0x7F=0x84. Ahora hay
que normalizar el exponente del siguiente modo: exponente desnormalizado + 1 – el
número de ceros introducciones por la derecha de la mantisa desnormalizada hasta que
el bit de más peso sea 1. Se puede ver claramente que en este caso el bit de más peso de
mantisa desnomalizada es 0, por lo tanto, será necesario introducir 1 cero por la derecha
de la mantisa desnomalizada ExpN=ExpD+1-1=0x84.
Al concatenar el bit de signo, el exponente normalizado y la mantisa normalizada
se obtiene el número ‘0’&0x84 &0xEEDFFA el cual se tiene de agrupar de tal modo que
represente un número definido por la norma IEEE 754.
Signo Exponente Mantisa Número representado en
hexadecimal
0 10000100 1110 1110 1101 1111 1111 1010 0x426EDFFA
El bit coloreado en rojo es el bit oculto el cual no se utiliza en las representaciones,
por lo tanto el resultado final de la multiplicación es 0x426EDFFA.
2.5.2 Adaptación en Copro
Una vez se ha completado el análisis de la operación división realizada con los dos
procesadores se va a hacer una lista de las diferencias que hay y cuales de ellas se van a
aplicar.
La primera diferencia esta en el cálculo del sticky bit. Se ha comprobado que
Microblaze pone el sticky bit a 0 si los 24 bits de menos peso de la variable que
representa la resta entre dividendo y divisor son 0, en caso contrario, el sticky bit vale 1.
La segunda diferencia la cual se ha demostrado es que los dos procesadores utilizan un
algoritmo diferente para realizar la operación división. Finalmente, la última diferencia
entre Microblaze y Copro esta en el un método de redondeo como ya se había
demostrado en los otros apartados,
De las 3 diferencias encontradas, solamente se van aplicar dos de ellas, el sticky bit
y el redondeo. En cuanto al algoritmo utilizado por los procesadores, no se va a adaptara
Copro al algoritmo Non Restoring Division porque el Restoring Division modificado
utilizado en Copro tiene unas mejores prestaciones que el Non Restoring Division y
además es bastante más rápido en caso de la división sea exacta.
Entonces, como se ha mencionado anteriormente, la primera adaptación es la de
tener en cuenta el sticky bit tal y como hace Microblaze, es decir, poner el sticky bit a 0
si los 24 bits de menos peso de la variable que representa la resta entre dividendo y
divisor son 0, en caso contrario, el sticky bit vale 1.
La segunda adaptación será totalmente igual que la vista en los casos de la
operación entero a flotante, suma y resta y multiplicación. La modificación es la
siguiente:
Se suma uno a la mantisa si el número binario representado por los
bits R, G y SB es mayor a 100b(4d).
Se suma uno a la mantisa si el número binario representado por los
bits R, G y SB es igual a 100b(4d) y el bit de menos peso de la
mantisa normalizada (m(0)) es igual a 1.
La mantisa se deja igual si el número binario representado por los
bits R, G y SB es igual a 100b(4d) y el bit de menos peso de la
mantisa normalizada (m(0)) es igual a 0
La mantisa se deja igual si el número binario representado por los bits R, G y SB
es menor o igual a 011b(3d).
2.6 Ensayo con Avnet Spartan-3 Development Board
Hasta ahora se ha trabajado con el entorno de simulación y depurado ModelSim
SE 6.2 plus para ver los resultados de las operaciones realizadas tanto por Microblaze
como Copro. De este modo es muy sencillo ver como trabajan los dos procesadores en
cada ciclo de reloj facilitando el estudio del comportamiento de Microblaze y el ajuste
del diseño de Copro con la finalidad de que los dos procesadores se comporten del
mismo modo.
La placa de dessarrollo Avnet Spartan-3, la cual se usa en este proyecto, utiliza la
un dispositivo FPGA XC3S 2000 formado por 2 millones de puertas lógicas
programables. Esta misma placa de desarrollo esta en el mercado pero con una FPGA
XC3S 1500 la cual esta formada por 1,5 millones de puertas lógicas programables. Las
características principales de los dispositivos disponibles son:
FPGA Logic
Gates
Logic
Cells
Array CLB
(4 celdas lógicas) BRAM
(2KB) Mult DCM
Fil Col Total CLBs
XC3S1500 1.5M 29,952 64 52 3,328 32 32 4
XC3S2000 2M 46,080 80 64 5,120 40 40 4
Entonces, para probar un diseño en un entorno real con la placa de desarrollo
Avnet Spartan-3 hay que seguir una serie de pasos. El primer paso es sintetizar el diseño
de Copro con el entorno XPS (Xilinx Platform Studio). La sintetización de un diseño
basado en FPGA es el proceso de generar un fichero de formato bitstream el cual
contiene la implementación del diseño a grabar en la FPGA.
En este proyecto, se ha visto como se ejecuta un programa en C en los
procesadores Microblaze y Copro con la ayuda del simulador. El siguiente paso es
compilar el programa en C a ejecutar y añadirlo al fichero resultante de la sintetización
del diseño de tal modo que al cargar el fichero resultante en la FPGA, esta ya contenga
el diseño más el programa a ejecutar.
Finalmente, antes de verificar el sistema hay que descargar el diseño además del
programa a ejecutar en la FPGA de placa de desarrollo. Esto se hace mediante un
programador USB-JTAG el cual permite grabar FPGAs placa de desarrollo desde el
mismo entorno XPS (Xilinx Platform Studio).
2.6.1 Configuración
La placa de desarrollo soporta tanto el modo Boundary-Scan como Master/Slave
serie o paralelo usando las memorias PROM de la placa. Programar el dispositivo FPGA
mediante Boundary-Scan requiere el uso de un cable JTAG. La placa contiene los
conectores J1 y J5 para la conexión de los cables Parallel III y Parallel IV
respectivamente.
El conector J5 está destinado a la conexión de un cable plano de 14 pines
suministrado con el cable Xilinx Parallel IV. Se debe conectar un jumper en las
posiciones 2-3 del JP6 para configurar la cadena de JTAG en modo standalone.
Figura 37: Conexión del cable Xilinx Parallel IV Figura 38: Cadena JTAG en modo standalone
2.6.2 Señal de reloj
La placa de desarrollo tiene diferentes fuentes de reloj disponibles. En la tabla
siguiente se muestran las entradas de reloj recomendadas para la señal de reloj del
sistema:
Frecuencia #pin FPGA Observaciones
66MHz AD13
100MHz A13
40MHz typ. AE14 Socket
- AE13 SMA
- AF14 Header
125MHz typ. C14, B14 No instalado por defecto
2.6.3 Memoria SRAM
La memoria SRAM es uno de los tipos de memoria disponibles en la placa de
desarrollo (además de memoria Flash y DDR SDRAM). Se trata de un dispositivo de
SRAM asíncrona conectado al bus compartido de datos con anchura de 32 bits.
Proporciona 2MB de memoria SRAM en un IC único organizada como 512KB x 32.
2.6.4 Conexión y grabación
El establecimiento de la conexión del equipo de desarrollo (ordenador personal)
con el dispositivo destino se realiza mediante la herramienta Impact del entorno de
desarrollo ISE de Xilinx pero esta herramienta no se ejecutar directamente sino que se
hace desde XPS (Xilinx Platform Studio), es decir, que Impact queda en segundo plano.
En cuanto a la conexión física, esta se consigue mediante la conexión física desde un
extremo del programador al puerto USB del ordenador y desde el otro hasta al conector
J5 de la placa de desarrollo.
La cadena detectada automáticamente por el Boundary Scan está compuesta por
dos puertos JTAG diferentes. El primero corresponde con la memoria PROM de la placa
de desarrollo utilizada para almacenar de forma permanente la configuración del
dispositivo FPGA, normalmente utilizada para diseños definitivos. El segundo puerto es
utilizado para la configuración del dispositivo programable desde el equipo de
desarrollo. Se especifica el fichero bitstream para el puerto.
La generación del fichero bitstream se lleva a cabo pulsando el botón “Generate
bitstream” del menú principal de XPS (Xilinx Platform Studio). Este proceso puede
llegar a tardar algunos minutos dependiendo de la envergadura del diseño. Una vez
generado el fichero bitstream, este se actualiza con el programa que se ejecutará en la
FPGA pulsando el botón “Update bitstream with software program information”.
Finalmente, el fichero resultante se descarga en la FPGA directamente utilizando el
puerto indicado en el fichero. En este proyecto la descarga será directa a la FPGA. En la
figura 39 se puede ver el menú principal de XPS (Xilinx Platform Studio) con los
botones principales utilizados para la sinterización del diseño y descarga a la FPGA.
Un aspecto a tener en cuenta es los pasos que se tienen que repetir si se modifica
algún fichero ya sea del diseño, del programa o los dos. En caso de que se modifique el
programa a ejecutar solo es necesario compilar dicho programa y añadirlo al fichero
bitstream y luego descargar el fichero resultante a la FPGA, mientras que, si se modifica
algún fichero del diseño, se tienen que repetir todos los tres pasos anteriores.
Figura 39: Pantalla principal de la herramienta de desarrollo XPS
2.6.5 Comunicación R232
En este proyecto se utiliza el puerto de comunicación serie que proporciona la
placa de desarrollo el cual se utiliza para enviar los resultados de algunas operaciones
realizadas por los dos procesadores además de todas aquellas en que los resultados de las
operaciones sean diferentes. Las señales de transmisión/recepción están conectadas a un
banco de entrada/salida de la FPGA (banco 6). Los puertos de entrada/salida utilizados
para las líneas RX y TX son los pines P4 y P5 respectivamente.
Figura 40: Conector serie DB9
2.7 Resultados experimentales
A diferencia de los apartados donde se averigua el comportamiento de Microblaze
con la ayuda del entorno de simulación ModelSim, los resultados experimentales se
obtienen mediante la ejecución de varios programas en la placa de desarrollo AVnet
Spartan-3.
2.7.1 Comprobación funcional
Para la comprobación funcional se ha utilizado el programa del anexo 3.1.1. Este
programa tiene dos entradas que son los operandos A y B y consiste en la ejecución de
las intrucciones suma, resta, multiplicacion, división y entero a flotante analizadas en
este proyecto. Estas ejecuciones se hacen con los dos procesadores y los resultados de
las mismas operaciones realizadas con cada procesador se comparan entre ellos. Con la
ayuda de un bucle y variando los operandos, estas ejecuciones y comparaciones de
resultados se prolongarán hasta que se decida parar o hasta que se llegue a un límite de
ejecuciones predefinido. Cuando se encuentra una diferencia se guarda el valor de los
operandos y el resultado de la operación en un fichero de texto via puerto serie.
Ejecutando el programa con los dos procesadores se han encontrado diferencias
entre los resultados de las operaciones suma, resta, multiplicación y división cuando los
dos operandos tienen un valor muy pequeño. Si el operando a y b tienen el valor
0x00002030 el resultado que obtiene Microblaze al hacer la operación suma es un
NAN(0xFFC00000) y el caso de Copro el resultado es 0x00802030. A continuación se
va hacer el análisis númerico para aseguarar que el valor dado por Copro es correcto.
En este caso particular donde el operando A= 0x00002030 (0000 0000 0000 0000
0010 0000 0011 0000b) y el operando B= 0x00002030 (0000 0000 0000 0000 0010
0000 0011 0000b) se puede ver que |a|>=|b| con lo cual se obtienen los siguientes
valores:
Signo Exponente Mantisa Numero representado en
hexadecimal
0 0000 0000 1000 0000 0010 0000 0011 0000 0x00002030
0 0000 0000 1000 0000 0010 0000 0011 0000 0x00002030
Exp_absdif= exp_a = 0x00h=0d como que 0 no es > 25 Exp_absdif=0d
Sig_res= sig_a = 0
Exp_res = exp_a = 0x0h
Man_high= ‘0’& Man_high & ”000”= 0x4010180
Man_low= 0x802030
En esta caso, como que Exp_absdif=0 no se hace ningún desplazamiento de la
man_low, por lo tanto, sticky bit=0.
Man_low_desplazada = Man_low &”000”
Man_low =’0’& 26 bits de más peso de la mantisa desplazada & sticky bit
Man_low = 0100 0000 0001 0000 0001 1000 0000 = 0x4010180
Man=Man_high + Man_low = 0x4010180+ 0x4010180= 0x8020300
Finalmente el número desnormalizado es: ‘0’&0x00&0x8020300.
Ahora, se tiene que normalizar el número pero esta vez se va a utilizar el método
de redondeo de Microblaze.
Mantisa desnormaliza=1000 0000 0010 0000 0011 0000 0000
Mantisa normalizada=1000 0000 0010 0000 0011 0000 0000no desplazamiento
El siguiente paso es coger los 24 bits de mayor peso de la mantisa y redondear con
los bits n+3, n+2 y n+1 (bits coloreados en magenta) del mismo modo que lo hace
Microblaze, es decir, no habra redondeo porque el conjunto de los bits R, G y SB es
inferior a 100.
Por tanto, la mantisa normalizada es 1000 0000 0010 0000 0011 0000 y el
exponente normalizado es el exponente desnormalizado + 1 – el numero de ceros
introducciones por la derecha de la mantisa desnormalizada ExpN=ExpD+1-0=0x1.
Una vez la mantisa y el exponente están normalizados se genera el resultado
normalizado de la operación concatenando el bit de signo, exponente normalizado y los
23 bits de menos peso de la mantisa normalizada ya que el bit de mayor peso llamado bit
oculto (bit coloreado en rojo) nunca se utiliza en la representación. Este bit se sabe que
siempre vale 1 a excepción del número 0.
Signo Exponente Mantisa Número representado en
hexadecimal
0 00000001 1000 0000 0010 0000 0011 0000 0x00802030
Para asegurar que el resultado de la suma realizada por Copro es correcto, se
recurrirá a una página web en la cual se pueden convertir de un modo muy sencillo un
número de representación decimal a otro de representación en coma flotante definido por
el estandar IEEE-754( http://www.h-schmidt.net/FloatApplet/IEEE754.html).
Utilizando el conversor de este sitio web se obtiene que el valor del operando A,
que es el mismo que el de B, es igual a 1.1547E-41 si se representa en decimal.
Seguidamente, si se hace la misma operación suma realizada por Copro con los
operandos A y B pero en formato decimal y utilizando un calculadora convencional, el
resultado es 2.3148E-41. Finalmente, si se vuelve a convertir el resultado decimal de la
suma a representación en coma flotante se obtiene el valor 0x00004087. Ahora,
comparando los dos resultados se ve a simple vista que el resultado de la suma realizada
por Copro es completamente diferente del resultado de la suma realizada con una
calculadora convencional y convirtiendo el resultado utilizando el sitio web mencionado
anteriormente, por lo tanto, se puede asegurar que para números cerecanos a cero Copro
no funciona correctamente y Microblaze devuelve un NAN. Volviendo al estándar IEEE
754, el conjunto de valores posibles a representar el cual llamaremos clases se dividen
en:
ceros
números normalizados
números desnormalizados
infinitos
NaN (not a number)
Las clases se distinguen principalmente por el valor del campo Exp, siendo
modificada ésta por el campo mantisa. Se considera Exp y Mantisa como campos de
números binarios sin signo (Exp se encuentra en el rango 0–255):
Clase Exp Mantisa
Ceros 0 0
Números desnormalizados 0 distinto de 0
Números normalizados 1-254 cualquiera
Infinitos 255 0
NaN (Not a Number) 255 distinto de 0
Se puede ver que los operandos A y B pertenecen a la clase de números
desnormalizados ya que todos los bits del campo exponente valen 0 y la mantisa es
diferente de 0. Esto hace pensar que Microblaze no trabaja con valores tan pequeños, por
lo tanto, se deduce que si alguno de los operandos tiene el campo exponente a 0 y el
campo mantisa diferente de 0, Microblaze devuelve una NAN como resultado de la
operación. Esta hipótesis se va comprobar y demostras seguidamente con 3 graficas,
realizas con el simulador ModelSim, las cuales van a mostrar la operación suma
realizada por Microblaze cuando cualquiera de los operandos esta formado por un
número desnomalizado. En caso de que la hipótesis sea cierta, Copro se va a modicar
para que su comportamiento sea el mismo que el de Microblaze ya que se ha
comprobado que de momento no es asi y además, la operación relalizada por Copro es
incorrecta.
Figura 41: Señales de Microblaze cuando realiza la suma de 0x00002030 y 0x00002030.
Figura 42: Señales de Microblaze cuando realiza la suma de 0x00002030 y 0x40000000.
Figura 43: Señales de Microblaze cuando realiza la suma de 0x40000000 y 0x00002030.
Las figuras 41, 42 y 43 confirman la hipótesis propuesta anteriormente. Se puede
ver que si el operando A y/o el B contienen un valor desnormalizado definido por el
estándar IEEE 754, el resultado de la operación es un NAN. También se ha comprobado
del mismo modo que este comportamiento es igual si se realiza cualquier operación la
cual precise dos operandos. En cuanto a la operación entero-flotante, este problema no
existe porque el valor a convertir es un decimal y no un flotante. Esto significa no nunca
se podrá guardar un número de dimensiones tant pequeñas en un operando del tipo
entero, por lo tanto, no será necesaria tal comprobación en el caso de la operación
entero-flotante.
2.7.2 Comprobación de la acceleración
Para la comprobación de la acceleración se ha utilizado el programa del anexo
3.1.2. Este programa tiene dos entradas que son los operandos A y B y consiste en la
ejecución de las intrucciones suma, resta, multiplicacion, división y entero a flotante
analizadas en este proyecto y también la raiz cuadrada la cual no se ha visto en este
proyecto porque la FPU de Microblaze no la tiene implementada si no que se emula por
software. Estas ejecuciones se hacen con los dos procesadores y con la ayuda de un
bucle se ejecuta desde 1 operación hasta un total de 1000 operaciones por cada una de la
intrucciones ya que, con este número, es suficiente para ver el valor asintótico al que
tiende la aceleración en cada caso. El tiempo que tarda en hacer las ejecuciones se
guarda en un fichero de texto via puerto serie. Este tiempo esta representado en ciclos de
reloj que, en caso de la placa de desarrollo Avnet spartan-3 la cual trabaja a una
frecuencia de 50MHz, es de ns 2050
16 por ciclo. Los resultados obtenidos son los
siguientes:
Entero a flontante, Suma, Resta y Multiplicación
Int2f, Suma, Resta y Multiplicación
0
5000
10000
15000
20000
25000
30000
35000
40000
1
48
95
14
2
18
9
23
6
28
3
33
0
37
7
42
4
47
1
51
8
56
5
61
2
65
9
70
6
75
3
80
0
84
7
89
4
94
1
98
8
Operaciones
Tie
mp
o
Microblaze
Copro
Figura 44: Operaciones vs tiempo de ejecución de 1 hasta 1000
Int2f, Suma, Resta y Multiplicación
0
50
100
150
200
250
300
350
400
450
1 2 3 4 5 6 7 8 9
Operaciones
Tie
mp
o
Microblaze
Copro
Figura 45: Operaciones vs tiempo de ejecución de 1 hasta 10
Acceleración Int2f, Suma, Resta y Multiplicación
0
1
2
3
4
5
6
7
8
9
10
1 48 95 142 189 236 283 330 377 424 471 518 565 612 659 706 753 800 847 894 941 988
Operaciones
Acele
ració
n
Acceleración
Figura 46: Aceleración de Copro respecto Microblaze
En las figuras 44 y 45 se puede ver que ejecutando una única operación
Microblaze es algo más rápido pero a medida que se incrementa el número de
operaciones ejecutadas, Copro se hace vuelve mucho más rápido. Básicamente, esto se
debe a que por cada operación el microprocesador softcore Microblaze tiene que leer los
operandos desde memoria, lanzar los comandos a la FPU, y escribir el resultado a
memoria decodificando en cada caso intrucciones de lenguaje máquina. Sin embargo, en
el caso de Copro esto no es asi ya que la circuiteria que tiene permite hacer operaciones
con los datos que hay en la memoria y escribir el resultado alli mismo sin la ayuda del
microprocesador, es decir, no es necesario decodificar intrucciones de lenguaje máquina
en cada operación realizada.
En la figura 46 se puede ver que la aceleración de Copro en caso de operaciones
entero a flotante, suma, resta y multiplicación es exponecial tendiendo a 9 a partir de
1000 operaciones, es decir, Copro es 9 veces más rápido que Microblaze a partir de 1000
operaciones.
División
División
0
10000
20000
30000
40000
50000
60000
70000
80000
90000
1
46
91
136
181
226
271
316
361
406
451
496
541
586
631
676
721
766
811
856
901
946
991
Operaciones
Tie
mp
o
Microblaze
Copro
Figura 47: Operaciones vs tiempo de ejecución de 1 hasta 1000
División
0
100
200
300
400
500
600
700
800
900
1 2 3 4 5 6 7 8 9
Operaciones
Tie
mp
o
Microblaze
Copro
Figura 48: Operaciones vs tiempo de ejecución de 1 hasta 10
Acceleración División
0
0,5
1
1,5
2
2,5
3
1 48 95 142 189 236 283 330 377 424 471 518 565 612 659 706 753 800 847 894 941 988
Operaciones
Accele
ració
n
Acceleración
Figura 49: Aceleración de Copro respecto Microblaze
En las figuras 47 y 48 se puede ver en primer lugar que Copro es igual de rápido
que Microblaze al ejecutar una única operación, y en segundo lugar, que Copro no es tan
rápido como lo es en el caso de las operaciones conversión entero a flotatante, suma,
resta y multiplicación. Esto se debe a que las dos FPUs necesitan muchos ciclos de reloj
para realizar la división haciendo que el tiempo que necesita la FPU de Microblaze para
obtener los datos, lanzar comandos y escribir resultados en memoria no sea tan grande
respecto al tiempo de ejecución de la división
En la figura 49 se puede ver que la aceleración es de 2.7 aproximadamente en caso
de ejecutar 1000 o más operaciones.
Raiz cuadrada
Raiz
0
500000
1000000
1500000
2000000
2500000
1
47
93
139
185
231
277
323
369
415
461
507
553
599
645
691
737
783
829
875
921
967
Operaciones
Tie
mp
o
Microblaze
Copro
Figura 50: Operaciones vs tiempo de ejecución de 1 hasta 1000
Raiz
0
2000
4000
6000
8000
10000
12000
14000
16000
18000
20000
1 2 3 4 5 6 7 8 9
Operaciones
Tie
mp
o
Microblaze
Copro
Figura 51: Operaciones vs tiempo de ejecución de 1 hasta 10
Acceleración Raiz
0
10
20
30
40
50
60
70
80
1 48 95 142 189 236 283 330 377 424 471 518 565 612 659 706 753 800 847 894 941 988
Operaciones
Accele
ració
n
Acceleración
Figura 52: Aceleración de Copro respecto Microblaze
En las figuras 50, 51, y 52 se puede ver claramente que Copro es muchísimo más
rápido que Microblaze y la aceleración esta por encima de 70 al ejecutar 1000
operaciones o más. Esto se debe a que Microblaze emula la operación raiz cuadrada en
todo momento.
2.8 Conclusiones
En los sistemas digitales con microprocesador un factor muy importante, y muchas
veces determinante, es la velocidad del sistema. En este trabajo se ha comprobado que la
VFPU llamada Copro es mucho más rápida la FPU de Microblaze. Esto básicamente se
debe a que las FPUs necesitan del microprocesador para leer y ejecutar las instrucciones
que permiten leer los operandos desde memoria, lanzar los comandos a la FPU, y
escribir el resultado a memoria. En cambio, las VFPUs son más autonomas ya que, una
vez el microprocesador configura el tipo de operación, las direcciones de los vectores y
su tamaño, estas acceden directamente a memoria leyendo los operandos y escribiendo
los resultados. Tambien se ha comprobado que cuando una operación se emula por
software en el microprocesador, como es el caso de la raiz cuadrada, el rendimiento de
este cae considerablemente.
2.9 Propuestas
Una mejora para un coprocesador matemático es ampliar el abanico de
operaciones que se pueden realizar. En este proyecto se han visto las operaciones de
conversión entero-flotante de un número, suma, resta, multiplicación y división pero no
se ha visto otras operaciones que Copro es capaz de realizar como son las operaciones de
comparación mayor que, menor que o igual que, valor absoluto y negación porque es
estos casos no es necesario redondear el número resultante ni hacer el cálculo del sticky
bit. Otra operación compleja que se puede realizar con Copro es la raíz cuadrada de un
número pero no se ha estudiado en detalle como las otras porque Microblaze no puede
realizar dicha operación con la FPU sino que lo hace por emulación de software. Las
operaciones con las cuales se podría ampliar el abanico de instrucciones de Copro son
operaciones exponenciales y también de senos y cosenos.
Otra mejora sería poder configurar la unidad de división y raiz cuadrada para
disminuir el número de ciclos de reloj requeridos. En la actual implementación se usa un
circuito de radix-2, al calcular un bit del resultado por ciclo de reloj. Se podría modificar
para calcular dos o más bits por ciclo de reloj (radix-4, radix-8, etc.), acelerando el
cómputo. Sin embargo sería necesario estudiar con mayor detalle el incremento de área
requerida, y que el retardo del critical-path no supere el periodo del reloj del sistema (20
ns para 50 MHz).
Otra mejora es mejorar el throughput de los cálculos vectoriales con flotantes,
mediante una arquitectura de pipeline. La FPU que se encuentra insertada en la VFPU
está segmentada en 4 etapas para las operaciones de suma, resta, multiplicación y
conversión de entero a flotante, lo que permite ejecutar 4 cálculos diferentes
concurrentente por ciclo de reloj, y obtener obtener un nuevo resultado por cada ciclo de
reloj. Sin embargo, el circuito de control de la VFPU para el acceso a memoria y control
de la FPU, no permite en la actualidad aprovechar esta posibilidad que permitiría
multiplicar por 4 el throughput para las operaciones descritas anteriormente.
3 Anexos
3.1 Anexos de Software
3.1.1 Programa C utilizado en la comprobación funcional.
void test_5()
{
float *operando_A=(float*)0x2E0000;
float *operando_B=(float*)0x2D0000;
float *resultado_Copro=(float*)0x2C0000;
float *resultado_Microblaze;
int Operando_A_int,Operando_B_int;
operando_A[0]=tofloat(0x02000001);//valor inicial OpA en flotante
operando_B[0]=tofloat(0x01200001); //valor inicial OpB en flotante
xil_printf("Start\n\r");
xil_printf("\n\r");
do{
CoproFPU_VectorAddr((float*)p1,p3,p4);
CoproFPU_VectorOP(CoproFPU_INT2F,2,true);
xil_printf("OpeA: %x,OpeB: %x\n\r",(int&)a,(int&)b);
CoproFPU_VectorAddr(operando_A,operando_B,resultado_Copro);
//Operación SUMA Microblaze
*resultado_Microblaze=(*operando_A)+(*operando_B);
//Operación SUMA Copro
CoproFPU_VectorOP(CoproFPU_ADD,1,true);
if ((*resultado_Microblaze)!=(*resultado_Copro))
{
xil_printf("Diferencias entre Copro y Microblaze
al realizar la operación SUMA\n\r");
xil_printf("OperandoA: %x, ",*(int*)operando_A);
xil_printf("OperandoB: %x\n\r",*(int*)operando_B);
xil_printf("Result Micro: %x\n\r",*(int*) resultado_Microblaze);
xil_printf("Result Copro: %x\n\r",*(int*) resultado_Copro);
xil_printf("\n\r");
}
//Operación RESTA Microblaze
*resultado_Microblaze=(*operando_A)-(*operando_B);
//Operación RESTA Copro
CoproFPU_VectorOP(CoproFPU_SUB,1,true);
if ((*resultado_Microblaze)!=(*resultado_Copro))
{
xil_printf("Diferencias entre Copro y Microblaze
al realizar la operación RESTA\n\r");
xil_printf("OperandoA: %x, ",*(int*)operando_A);
xil_printf("OperandoB: %x\n\r",*(int*)operando_B);
xil_printf("Result Micro: %x\n\r",*(int*) resultado_Microblaze);
xil_printf("Result Copro: %x\n\r",*(int*) resultado_Copro);
xil_printf("\n\r");
}
//Operación MULTIPLICACION Microblaze
*resultado_Microblaze=(*operando_A)*(*operando_B);
//Operación MULTIPLICACION Copro
CoproFPU_VectorOP(CoproFPU_MUL,1,true);
if ((*resultado_Microblaze)!=(*resultado_Copro))
{
xil_printf("Diferencias entre Copro y Microblaze
al realizar la operación MULTIPLICACIÓN\n\r");
xil_printf("OperandoA: %x, ",*(int*)operando_A);
xil_printf("OperandoB: %x\n\r",*(int*)operando_B);
xil_printf("Result Micro: %x\n\r",*(int*) resultado_Microblaze);
xil_printf("Result Copro: %x\n\r",*(int*) resultado_Copro);
xil_printf("\n\r");
}
//Operacion DIVISION Microblaze
*resultado_Microblaze=(*operando_A)/(*operando_B);
//Operación DIVISION Copro
CoproFPU_VectorOP(CoproFPU_DIV,1,true);
if ((*resultado_Microblaze)!=(*resultado_Copro))
{
xil_printf("Diferencias entre Copro y Microblaze
al realizar la operación DIVISION\n\r");
xil_printf("OperandoA: %x, ",*(int*)operando_A);
xil_printf("OperandoB: %x\n\r",*(int*)operando_B);
xil_printf("Result Micro: %x\n\r",*(int*) resultado_Microblaze);
xil_printf("Result Copro: %x\n\r",*(int*) resultado_Copro);
xil_printf("\n\r");
}
//Incremento de los operandos
Operando_A_int=(*(int*)operando_A)+1;
Operando_B_int=(*(int*)operando_B)+1;
operando_A[0]=tofloat(Operando_A_int);
operando_B[0]=tofloat(Operando_A_int);
}while (1);//Bucle infinito
xil_printf("End\n\r");
xil_printf("\n\r");
}
3.1.2 Programa C utilizado en la comprobación de la aceleración.
void test_6()
{
int timer,repetitions;
int *p1=(int*)0x2F0000;
float *p2=(float*)0x2E0000;
float *p3=(float*)0x2D0000;
float *p4=(float*)0x2C0000;
CoproFPU_VectorAddr(p2,p3,p4);
//llenar espacio reservado con los mimos valores
for (int j=0; j<1000; j++)
{
p2[j]=2.0;
p3[j]=1.3;
}
xil_printf("Start\n\r");
xil_printf("\n\r");
for (repetitions=1; repetitions<1000; repetitions++)
{
xil_printf("Nº Operations: %d",repetitions);
xil_printf("\n\r");
//Addition Microblaze
timer=0;
timer_clear_run(0);
timer_run(0);
for(int j=0; j<repetitions; j++)
{
p4[9]=p2[9]+p3[9];
}
timer=timer_stop_read(0);
xil_printf("Time for Microblaze Add operations: %d",timer);
xil_printf("\n\r");
//Addition Copro
timer=0;
timer_clear_run(0);
timer_run(0);
CoproFPU_VectorOP(CoproFPU_ADD,repetitions,true);
timer=timer_stop_read(0);
xil_printf("Time for Copro Add operations: %d",timer);
xil_printf("\n\r");
//Substraccion Microblaze
timer=0;
timer_clear_run(0);
timer_run(0);
for(int j=0; j<repetitions; j++)
{
p4[9]=p2[9]-p3[9];
}
timer=timer_stop_read(0);
xil_printf("Time for Microblaze Sub operations: %d",timer);
xil_printf("\n\r");
//Substraccion Copro
timer=0;
timer_clear_run(0);
timer_run(0);
CoproFPU_VectorOP(CoproFPU_SUB,repetitions,true);
timer=timer_stop_read(0);
xil_printf("Time for Copro Sub operations: %d",timer);
xil_printf("\n\r");
//Multiplication Microblaze
timer=0;
timer_clear_run(0);
timer_run(0);
for(int j=0; j<repetitions; j++)
{
p4[9]=p2[9]*p3[9];
}
timer=timer_stop_read(0);
xil_printf("Time for Microblaze Mul operations: %d",timer);
xil_printf("\n\r");
//Multiplication Copro
timer=0;
timer_clear_run(0);
timer_run(0);
CoproFPU_VectorOP(CoproFPU_MUL,repetitions,true);
timer=timer_stop_read(0);
xil_printf("Time for Copro Mul operations: %d",timer);
xil_printf("\n\r");
//Division Microblaze
timer=0;
timer_clear_run(0);
timer_run(0);
for(int j=0; j<repetitions; j++)
{
p4[j]=p2[j]/p3[j];
}
timer=timer_stop_read(0);
xil_printf("Time for Microblaze Div operations: %d",timer);
xil_printf("\n\r");
//Division Copro
timer=0;
timer_clear_run(0);
timer_run(0);
CoproFPU_VectorOP(CoproFPU_DIV,repetitions,true);
timer=timer_stop_read(0);
xil_printf("Time for Copro Div operations: %d",timer);
xil_printf("\n\r");
//Root Square Microblaze
timer=0;
timer_clear_run(0);
timer_run(0);
for(int j=0; j<repetitions; j++)
{
p4[j]=sqrt(p2[j]);
}
timer=timer_stop_read(0);
xil_printf("Time for Microblaze Sqrt operations: %d",timer);
xil_printf("\n\r");
//Root Square Copro
timer=0;
timer_clear_run(0);
timer_run(0);
CoproFPU_VectorOP(CoproFPU_SQRT,repetitions,true);
timer=timer_stop_read(0);
xil_printf("Time for Copro Sqrt operations: %d",timer);
xil_printf("\n\r");
}
xil_printf("Stop\n\r");
xil_printf("\n\r");
}
3.1.3 Programa C utilizado en el desarrollo de las operaciones de int2f, suma, resta,
multiplicación y división. .
void test_7()
{
//Operando A para la conversion entero-flontate en Copro
const int a[2]={0x1AAAAFEF,0x01AAAFEF};
//Operandos A y B para las otras operaciones expresados en real
const float ax[10]={3,2,1000000.0625,-
3,7563.576,1177.356,16.8567,1027.3367,135.2587,0.7};
const float b[10]={2,3,10.0625,2,7.456,3.376,4.356,3.356,2.356,7.2156};
int *p1=(int*)0x2F0000;
float *p2=(float*)0x2E0000;
float *p3=(float*)0x2D0000;
float *p4=(float*)0x2C0000;
for(int j=0; j<2; j++)
{p1[j]=a[j];}
for(int j=0; j<10; j++)
{p2[j]=ax[j];
p3[j]=b[j];}
//Operandos A y B para las otras operaciones expresados en flotante
/*p2[0]=tofloat(0x42F80000);
p3[0]=tofloat(0x40000000);
p2[1]=tofloat(0x4FFFFFF0);
p3[1]=tofloat(0x3F900002);
p2[2]=tofloat(0x42EEE000);
p3[2]=tofloat(0x40000003);
p2[3]=tofloat(0x42DDD000);
p3[3]=tofloat(0x40000004);
p2[4]=tofloat(0x42CCC000);
p3[4]=tofloat(0x40000002);
p2[5]=tofloat(0x42BBB000);
p3[5]=tofloat(0x40000004);
p2[6]=tofloat(0x42AAA000);
p3[6]=tofloat(0x40000002);
p2[7]=tofloat(0x42999000);
p3[7]=tofloat(0x4F800654);
p2[8]=tofloat(0x42888000);
p3[8]=tofloat(0x40800652);
p2[9]=tofloat(0x42777000);
p3[9]=tofloat(0x40800654);
for(int j=0; j<10; j++)
{p4[j]=p2[j]+p3[j];
}
/*for(int j=0; j<10; j++)
{p4[j]=p2[j]-p3[j];
}
for(int j=0; j<10; j++)
{p4[j]=p2[j]*p3[j];
}
for(int j=0; j<10; j++)
{p4[j]=p2[j]/p3[j];
}*/
CoproFPU_VectorAddr((float*)p1,p3,p4);
//CoproFPU_VectorOP(CoproFPU_INT2F,2,true);
CoproFPU_VectorAddr(p2,p3,p4);
CoproFPU_VectorOP(CoproFPU_ADD,10,true);
//CoproFPU_VectorOP(CoproFPU_SUB,10,true);
//CoproFPU_VectorOP(CoproFPU_MUL,10,true);
//CoproFPU_VectorOP(CoproFPU_DIV,10,true);
}
3.2 Anexos de Hardware
3.2.1 Adaptación del código VHDL
Este anexo no contendrá el código VHDL completo sino el código que se ha
modificado de los ficheros hardware del siguiente modo. Si hay algún cambio en una
línea de código existente se podrá justo debajo la misma línea de código con el cambio
aplicado. Esta línea de código nueva será resaltada en rojo y entre paréntesis. Si lo que
se ha hecho es introducir alguna línea de código nueva, esta se resaltará en azul. En
cualquier caso anterior se añadirá el número de línea. Finalmente, si hay alguna línea de
código que ha sido eliminada completamente sin ser reemplazada, esta se resaltará en
púrpura.
3.2.1.1 Modificaciones en el fichero FPU_int2f.vhd
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.STD_LOGIC_ARITH.conv_std_logic_vector;
use IEEE.STD_LOGIC_MISC.ALL;línea 5
architecture beh2b of FPU_int2f is
subtype T_INDEX is integer range 0 to 3;
(subtype T_INDEX is integer range 0 to 4;línea 34)
constant NBITS_INDEX: integer:=2;
(constant NBITS_INDEX: integer:=3;línea 35)
signal strt_s1,fin_s1: std_logic;
variable man: std_logic_vector(27 downto 0);
variable int,shfint: std_logic_vector(31 downto 0);
variable bitsperdidos: std_logic_vector(3 downto 0);línea 58
variable rshift: T_INDEX;
begin
man:=shfint(man'range);
De la línea 75 hasta la línea 84
if rshift>0 then --Si rshift>0 hacer el calculo del Sticky bit
bitsperdidos:=int(int'right+3 downto int'right);
case rshift is
when 1 => bitsperdidos:=bitsperdidos and "0001";
when 2 => bitsperdidos:=bitsperdidos and "0011";
when 3 => bitsperdidos:=bitsperdidos and "0111";
when others => null; --NO tocamos nada
end case;
man(0):=or_reduce(man(0)&bitsperdidos);
end if;
3.2.1.2 Modificaciones en el fichero FPU_addsubcmp.vhd
if not (rshift<3 or rshift>31) then
(if not (rshift<3) thenLínea 72)
for i in 0 to mant'left loop
if i+3<=rshift and mant(i)='1' then
(if i+3<rshift and mant(i)='1' then Línea 74)
if exp_absdif>=32 then
(if exp_absdif>=24 thenLínea 192)
exp_absdif:=conv_std_logic_vector(32,exp_absdif'length);
(exp_absdif:=conv_std_logic_vector(24,exp_absdif'length); Línea 193)
end if;
De la línea 308 hasta la línea 310
if man (man’left=’1’) then
man(1):=man(1) or man(0);
end if;
3.2.1.3 Modificaciones en el fichero FPU_mul.vhd
variable r: std_logic_vector(47 downto 0);
variable man: std_logic_vector(27 downto 0);
variable sticky_bit_vector: std_logic_vector(20 downto 0); línea 140
begin
fin_s2<=strt_s2;--fin_s2<=fin_s12;
r3:=r2(13 downto 12)+ab11_s12(23 downto 12);
r:=r3 & r2(11 downto 0) & r1(11 downto 0) & r0;
man:=r(47 downto 20);
De la línea 156 hasta la línea 162
sticky_bit_vector:=r(20 downto 0);
if man(man'left)='1' then
man(1):=or_reduce(sticky_bit_vector);
else
sticky_bit_vector(20):='0';
man(0):=or_reduce(sticky_bit_vector);
end if;
3.2.1.4 Modificaciones en el fichero FPU_mul.vhd
signal stickybit_divsqrt: std_logic;
signal dec_exp: std_logic_vector(0 downto 0);Línea 49
begin
ST1B: process(op_a,op_b,mode,mode_divsqrt)
begin
stickybit_divsqrt<='0';
De la línea 124 hasta la línea 126
if mode_divsqrt='1' then
stickybit_divsqrt<='1';
end if;
end process;
ST1C:
variable exp: std_logic_vector(7 downto 0);
variable exp_aux: std_logic_vector(7 downto 0);Línea 132
begin
strt_divsqrt<=strt_s1 and not fin_excep;
rst_divsqrt<=rst;
fin_s1<=qrdy_divsqrt;
exp:=exp_aux-dec_exp; Línea 137
res_s1<=sig & exp & ('0'&q_divsqrt);
tag_s1<=strt_tag;
if rising_edge(clk) and strt_divsqrt='1' then
sig:=sig_s1;
exp_aux:=exp_s1; Línea 146
end if;
end process;
ST2_0: if FPU_DIVISION_ZEROSHIFT=0 generate ST2: process
variable cmp: std_logic_vector(r'left+1 downto r'right);
variable r_first_24_bits: std_logic_vector(26 downto 3); Línea 159
variable rdy,busy,qready,mode,stickybit: std_logic;
begin
case estado is
when ESPERA =>
if strt_divsqrt='1' and busy='0' then
stickybit:=stickybit_divsqrt;
estado:=COMPARA;
if mode='0' then -- division
one:=(one'left=>'1', others=>'0');
b:="00" & b_divsqrt;
dec_exp<=(others=>'0'); Línea 179
end if;
end if;
when COMPARA =>
cmp:=('0'&r)-('0'&b);
if mode='0' then -- division
if cmp(cmp'left)='1' then
--q:=q;
else
q:=q or one;
r:=cmp(r'range);
end if;
De la línea 199 hasta la línea 210
r_first_24_bits:=r(r_first_24_bits'range);
if r_first_24_bits=X"000000" then
stickybit:='0';
else
stickybit:='1';
end if;
if index=T_INDEX'high and cmp(cmp'left)='1' then
--NO shr one
else
one:=shr(one,"1");
end if;
r:=shl(r,"1");
end if;
if index=T_INDEX'low or cmp=0 then
busy:='1'; qready:='1'; estado:=ESPERA;
else
De la línea 229 hasta la línea 233
if index=T_INDEX'high and cmp(cmp'left)='1' then
dec_exp<=(others=>'1');
else
index:=index-1;
end if;
end if;
end case;
3.2.1.5 Modificaciones en el fichero FPU_norm.vhd
function RoundBit(a: std_logic_vector; constant n: integer) return std_logic is
variable slc,cmp: std_logic_vector(n-1 downto 0);
variable round: std_logic:='0';
begin
if ROUND_METHOD=1 then
cmp(cmp'left):='1'; cmp(cmp'left-1 downto cmp'right):=(others=>'0');
slc:=a(a'right+n-1 downto a'right);
slc:=a(a'right+n downto a'right+1);Línea 90
if slc>cmp then
round:='1';
end if;
De la línea 94 hasta la línea 96
if slc=cmp and a(4)='1' then
round:='1';
end if;
end if;
return round;
end function;
begin
ST2: process(strt_s2,exp_res_s12,man_val_s12,index_s12,sig_s12,tag_s12)
variable round_bit,sig: std_logic;
begin
man_ext:=BarrelLShift(man_val_s12,index_s12);
round_bit:=RoundBit(man_ext,4);
round_bit:=RoundBit(man_ext,3); Línea 183
man:=man_ext(man'range)+round_bit;
end process;
3.2.1.6 Modificaciones en el fichero FPU_alu.vhd
scan_a: process(op_a)
begin
op_a_status<=FOK;
if op_a(30 downto 0)=0 then
op_a_status<=FZERO;
end if;
if op_a(30 downto 23)=255 then
if op_a(22 downto 0)=0 then
op_a_status<=FINF;
else
op_a_status<=FNAN;
end if;
end if;
De la línea 269 hasta la línea 273
if op_a(30 downto 23)=0 and op_a(22 downto 0)>0 then
op_a_status<=FNAN;
end if;
end process;
scan_b: process(op_b)
begin
op_b_status<=FOK;
if op_b(30 downto 0)=0 then
op_b_status<=FZERO;
end if;
if op_b(30 downto 23)=255 then
if op_b(22 downto 0)=0 then
op_b_status<=FINF;
else
op_b_status<=FNAN;
end if;
end if;
De la línea 288 hasta la línea 290
if op_b(30 downto 23)=0 and op_b(22 downto 0)>0 then
op_b_status<=FNAN;
end if;
end process;
3.2.2 Código VHDL completo
Este anexo contendrá el código VHDL completo de los ficheros hardware.
3.2.2.1 Fichero FPU_int2f.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.STD_LOGIC_ARITH.conv_std_logic_vector;
use IEEE.STD_LOGIC_MISC.ALL;
library copro_fpu_v2_00_b;
use copro_fpu_v2_00_b.FPU_pack_configuration.all;
use copro_fpu_v2_00_b.FPU_pack_defines.all;
entity FPU_int2f is
generic(
NBITS_TAG: integer:=0);
port(
clk : in std_logic;
rst : in std_logic;
strt_tag: in std_logic_vector(NBITS_TAG-1 downto 0);
strt_req : in std_logic;
strt_ack: out std_logic;
op_a_rdy: in std_logic;
op_a_ack: out std_logic;
op_a: in std_logic_vector(31 downto 0);
res_tag: out std_logic_vector(NBITS_TAG-1 downto 0);
res_status: out T_FLOAT;
res: out std_logic_vector(36 downto 0);
res_rdy: out std_logic;
res_ack: in std_logic);
end entity;
architecture beh2b of FPU_int2f is
subtype T_INDEX is integer range 0 to 4;
constant NBITS_INDEX: integer:=3;
signal strt_s1,fin_s1: std_logic;
signal res_s1: std_logic_vector(res'range);
signal res_status_s1: T_FLOAT;
function RSHIFT_IDX(a: std_logic_vector(31 downto 0)) return T_INDEX is
variable idx: T_INDEX:=0;
begin
for i in 1 to T_INDEX'high loop
if a(a'left-1-T_INDEX'high+i)='1' then
idx:=i;
end if;
end loop;
return idx;
end function;
begin
ST1: process(strt_s1,op_a,fin_s1)
variable sig: std_logic;
variable exp: std_logic_vector(7 downto 0);
variable man: std_logic_vector(27 downto 0);
variable int,shfint: std_logic_vector(31 downto 0);
variable bitsperdidos: std_logic_vector(3 downto 0);
variable rshift: T_INDEX;
begin
fin_s1<=strt_s1;
if fin_s1='1' then
sig:=op_a(31);
if sig='1' then
int:=0-op_a;
else
int:=op_a;
end if;
rshift:=RSHIFT_IDX(int);
exp:=conv_std_logic_vector(127+26+rshift,exp'length);
shfint:=shr(int,conv_std_logic_vector(rshift,NBITS_INDEX));
man:=shfint(man'range);
if rshift>0 then --Si rshift>0 hacer el calculo del Sticky bit
bitsperdidos:=int(int'right+3 downto int'right);
case rshift is
when 1 => bitsperdidos:=bitsperdidos and "0001";
when 2 => bitsperdidos:=bitsperdidos and "0011";
when 3 => bitsperdidos:=bitsperdidos and "0111";
when others => null; --NO tocamos nada
end case;
man(0):=or_reduce(man(0)&bitsperdidos);
end if;
else
sig:='X';
exp:=(others=>'X');
man:=(others=>'X');
end if;
res_status_s1<=FOK;
res_s1<=sig & exp & man;
if fin_s1='1' and Is_X(op_a)=false then assert int(31)='0'
report "Error in int2f" severity failure; end if;
end process;
handshake: block
begin
res<=res_s1;
res_status<=res_status_s1;
strt_s1<=strt_req and op_a_rdy;
strt_ack<=res_ack;
op_a_ack<=res_ack;
res_tag<=strt_tag;
res_rdy<=fin_s1;
end block;
end architecture;
3.2.2.2 Fichero FPU_addsubcmp.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.STD_LOGIC_ARITH.conv_std_logic_vector;
library copro_fpu_v2_00_b;
use copro_fpu_v2_00_b.FPU_pack_configuration.all;
use copro_fpu_v2_00_b.FPU_pack_defines.all;
entity FPU_addsubcmp is
generic(
NBITS_TAG: integer:=0);
port(
clk : in std_logic;
rst : in std_logic;
mode: T_MODE_ADDSUBCMP;
strt_tag: in std_logic_vector(NBITS_TAG-1 downto 0);
strt_req : in std_logic;
strt_ack: out std_logic;
op_a_rdy: in std_logic;
op_a_ack: out std_logic;
op_a_status : in T_FLOAT;
op_a : in std_logic_vector(31 downto 0);
op_b_rdy: in std_logic;
op_b_ack: out std_logic;
op_b_status : in T_FLOAT;
op_b : in std_logic_vector(31 downto 0);
cmp_tag: out std_logic_vector(NBITS_TAG-1 downto 0);
cmp: out std_logic_vector(31 downto 0);
cmp_rdy: out std_logic;
cmp_ack: in std_logic;
res_tag: out std_logic_vector(NBITS_TAG-1 downto 0);
res_status: out T_FLOAT;
res : out std_logic_vector(36 downto 0);
res_rdy: out std_logic;
res_ack: in std_logic);
end entity;
architecture beh2b of FPU_addsubcmp is
constant STAGES: integer:=FPU_ADDSUB_STAGES;
signal res_addsub_s2,res_addsub_excep: std_logic_vector(36 downto 0);
signal res_cmp: std_logic_vector(31 downto 0);
signal man_high_s1,man_high_s12: std_logic_vector(22+1 downto 0);
signal man_low_s1,man_low_s12: std_logic_vector(22+1 downto 0);
signal sig_res_s1,sig_res_s12: std_logic;
signal exp_res_s1,exp_res_s12: std_logic_vector(7 downto 0);
signal exp_dif_s1,exp_dif_s12: std_logic_vector(5 downto 0);
signal mode_subNadd,mode_cmp,mode_subNadd_s1,mode_subNadd_s12: std_logic;
signal cmp_agtb_excep,cmp_aeqb_excep,cmp_agtb_s1,cmp_aeqb_s1: std_logic;
signal fin_addsub_s1,fin_addsub_s12,fin_addsub_s2: std_logic;
signal fin_cmp_s1,fin_excep: std_logic;
signal status_addsub_excep,status_addsub_s2: T_FLOAT;
signal strt_s1,strt_s2: std_logic;
signal tag_cmp,tag_addsub_excep,tag_s2,tag_s1,tag_s12:
std_logic_vector(res_tag'range);
function STICKYBIT(mant,rshift: std_logic_vector) return std_logic is
variable sticky: std_logic:='0';
begin
if FPU_ADDSUB_ENABLE_STICKYBIT=1 then
if not (rshift<3) then
for i in 0 to mant'left loop
if i+3<rshift and mant(i)='1' then
sticky:='1';
end if;
end loop;
end if;
end if;
return sticky;
end function;
begin
ST1D: process(mode)
begin
case mode is
when FADD=> mode_subNadd<='0'; mode_cmp<='0';
when FSUB=> mode_subNadd<='1'; mode_cmp<='0';
when others=> mode_subNadd<='X'; mode_cmp<='1';
end case;
end process;
ST1A: process(op_a_status,op_b_status,op_a,op_b,strt_s1,tag_s1)
variable man_excep: std_logic_vector(27 downto 0);
variable exp_excep: std_logic_vector(7 downto 0);
variable sig_a,sig_b,sig_excep: std_logic;
begin
sig_a:=op_a(31);
sig_b:=op_b(31);
status_addsub_excep<=FOK;
cmp_agtb_excep<='X'; cmp_aeqb_excep<='X';
sig_excep:='X';
exp_excep:=(others=>'X');
man_excep:=(others=>'X');
fin_excep<='0';
if op_a_status=FINF then
status_addsub_excep<=FINF; --si alguno es INF resultado=INF
cmp_agtb_excep<=not sig_a; cmp_aeqb_excep<='0';
sig_excep:=sig_a;
fin_excep<=strt_s1;
end if;
if op_b_status=FINF then
status_addsub_excep<=FINF; --si alguno es INF resultado=INF
cmp_agtb_excep<=sig_b; cmp_aeqb_excep<='0';
sig_excep:=sig_b;
fin_excep<=strt_s1;
end if;
if op_a_status=FINF and op_b_status=FINF then
if sig_a=sig_b then
status_addsub_excep<=FINF;--si ambos son INF con mismo signo
cmp_agtb_excep<='0'; cmp_aeqb_excep<='1';
sig_excep:=sig_a;
fin_excep<=strt_s1;
else
status_addsub_excep<=FNAN;--si ambos son INF con diferente signo
cmp_agtb_excep<=sig_b; cmp_aeqb_excep<='0';
sig_excep:='0';
fin_excep<=strt_s1;
end if;
end if;
if op_a_status=FNAN or op_b_status=FNAN then
status_addsub_excep<=FNAN;--si alguno es NaN el resultado es NaN
cmp_agtb_excep<='0'; cmp_aeqb_excep<='0';
sig_excep:='0';
fin_excep<=strt_s1;
end if;
tag_addsub_excep<=tag_s1;
res_addsub_excep<=sig_excep & exp_excep & man_excep;
end process;
ST1B:
process(mode_cmp,strt_tag,strt_s1,fin_excep,op_a,op_b,mode_subNadd,op_a_status,op
_b_status)
variable sig_a,sig_b,sig_dif,sig_res,agtb,aeqb: std_logic;
variable exp_a,exp_b,exp_high: std_logic_vector(7 downto 0);
variable exp_dif,exp_absdif,testa,testb: std_logic_vector(7+1 downto 0);
variable man_a,man_b: std_logic_vector(22+1 downto 0);
variable man_high,man_low: std_logic_vector(22+1 downto 0);
variable man_dif: std_logic_vector(22+2 downto 0);
variable hidebit_a,hidebit_b: std_logic;
begin
fin_addsub_s1<=not mode_cmp and strt_s1 and not fin_excep;
fin_cmp_s1<=mode_cmp and strt_s1;-- and not fin_excep;
if op_a_status=FZERO then hidebit_a:='0'; else hidebit_a:='1'; end if;
if op_b_status=FZERO then hidebit_b:='0'; else hidebit_b:='1'; end if;
sig_a:=op_a(31);
sig_b:=op_b(31);
sig_dif:=sig_a xor sig_b;
exp_a:=op_a(30 downto 23);
exp_b:=op_b(30 downto 23);
testa:='0'&exp_a;
testb:='0'&exp_b;
exp_dif:=('0'&exp_a)-('0'&exp_b);
man_a:=hidebit_a&op_a(22 downto 0);
man_b:=hidebit_b&op_b(22 downto 0);
man_dif:=('0'&man_a)-('0'&man_b);
if exp_dif(exp_dif'left)='1' or (exp_dif=0 and man_dif(man_dif'left)='1') then
exp_absdif:=0-exp_dif;
man_high:=man_b;
--status_high:=status_b;
man_low:=man_a;
--status_low:=status_a;
--man_low_notzero:=hide_bit_b;
exp_high:=exp_b;
sig_res:=(not mode_subNadd and sig_b) or (mode_subNadd and not
sig_b);
else
exp_absdif:=exp_dif;
man_high:=man_a;
--status_high:=status_a;
man_low:=man_b;
--status_low:=status_b;
--man_low_notzero:=hide_bit_a;
exp_high:=exp_a;
sig_res:=sig_a;
end if;
if exp_absdif>=24 then
exp_absdif:=conv_std_logic_vector(24,exp_absdif'length);
end if;
cmp_aeqb_s1<='0';
cmp_agtb_s1<='0';
if sig_dif='1' then
cmp_agtb_s1<=sig_b;
elsif exp_dif(exp_dif'left)='1' then
cmp_agtb_s1<=sig_b;
elsif exp_dif/=0 then
cmp_agtb_s1<=not sig_b;
elsif man_dif(man_dif'left)='1' then
cmp_agtb_s1<=sig_b;
elsif man_dif/=0 then
cmp_agtb_s1<=not sig_b;
else
cmp_agtb_s1<='0';
cmp_aeqb_s1<='1';
end if;
man_high_s1<=man_high;
man_low_s1<=man_low;
exp_dif_s1<=exp_absdif(exp_dif_s1'range);
exp_res_s1<=exp_high;
sig_res_s1<=sig_res;
mode_subNadd_s1<=(not mode_subNadd and sig_dif) or (mode_subNadd and
not sig_dif);
tag_s1<=strt_tag;
end process;
ST1C:
process(cmp_agtb_s1,cmp_aeqb_s1,cmp_agtb_excep,cmp_aeqb_excep,fin_excep,mode,
op_a,op_b,tag_s1)
variable agtb,aeqb: std_logic;
variable bitres: std_logic;
begin
agtb:=cmp_agtb_s1; aeqb:=cmp_aeqb_s1;
if fin_excep='1' then
agtb:=cmp_agtb_excep; aeqb:=cmp_aeqb_excep;
end if;
case mode is
when FEQU=> bitres:=aeqb;
when FHIG=> bitres:=agtb;
when FHEQ=> bitres:=aeqb or agtb;
when FNEQ=> bitres:=not aeqb;
when FLWR=> bitres:=not(aeqb or agtb);
when FLEQ=> bitres:=not agtb;
when others=> bitres:='X';
end case;
case mode is
when FMAX|FMIN=>
res_cmp<=op_a;
if (mode=FMAX and agtb='0')or(mode=FMIN and agtb='1') then
res_cmp<=op_b;
end if;
when others=>
res_cmp<=(24=>bitres, 16=>bitres, 8=>bitres, 0=>bitres, others=>'0');
end case;
tag_cmp<=tag_s1;
end process;
ST1toST2_1: if STAGES=1 generate
process(man_high_s1,man_low_s1,exp_dif_s1,exp_res_s1,sig_res_s1,mode_subNadd_s
1,tag_s1)
begin
man_high_s12<=man_high_s1;
man_low_s12<=man_low_s1;
exp_dif_s12<=exp_dif_s1;--man_low_stickybit_s12<=man_low_stickybit_s1;
exp_res_s12<=exp_res_s1;
sig_res_s12<=sig_res_s1;
mode_subNadd_s12<=mode_subNadd_s1;
tag_s12<=tag_s1;
end process; end generate;
ST1toST2_2: if STAGES=2 generate process
begin
wait until rising_edge(clk);
if fin_addsub_s1='1' then
man_high_s12<=man_high_s1;
man_low_s12<=man_low_s1;
exp_dif_s12<=exp_dif_s1;--man_low_stickybit_s12<=man_low_stickybit_s1;
exp_res_s12<=exp_res_s1;
sig_res_s12<=sig_res_s1;
mode_subNadd_s12<=mode_subNadd_s1;
tag_s12<=tag_s1;
end if;
end process; end generate;
ST2:
process(strt_s2,exp_res_s12,exp_dif_s12,man_high_s12,man_low_s12,sig_res_s12,mod
e_subNadd_s12,tag_s12)
variable man_low_ext: std_logic_vector(26 downto 0);
variable man,man_high,man_low: std_logic_vector(27 downto 0);
variable man_low_stickybit: std_logic;
begin
fin_addsub_s2<=strt_s2;--fin_addsub_s12;
man_high:=("0"&man_high_s12&"000");
man_low_stickybit:=STICKYBIT(man_low_s12,exp_dif_s12);
man_low_ext:=shr(man_low_s12&"000",exp_dif_s12
man_low:=('0'&man_low_ext(26 downto 1)&man_low_stickybit);
if mode_subNadd_s12='0' then
man:=man_high+man_low;
else
man:=man_high-man_low;
end if;
if man (man’left=’1’) then
man(1):=man(1) or man(0);
end if;
res_addsub_s2<=sig_res_s12 & exp_res_s12 & man;
status_addsub_s2<=FOK;
tag_s2<=tag_s12;
end process;
handshake: block
signal state_busy_s1,strt_req_s1,strt_ack_s1,res_rdy_s1,res_ack_s1: std_logic;
signal state_busy_s2,strt_req_s2,strt_ack_s2,res_rdy_s2,res_ack_s2: std_logic;
begin
strt_ack<=res_ack when fin_excep='1' else strt_ack_s1;
op_a_ack<=res_ack when fin_excep='1' else strt_ack_s1;
op_b_ack<=res_ack when fin_excep='1' else strt_ack_s1;
res_tag<=tag_addsub_excep when fin_excep='1' else tag_s2;
res_rdy<=fin_excep when fin_excep='1' else res_rdy_s2;
res_status<=status_addsub_excep when fin_excep='1' else status_addsub_s2;
res<=res_addsub_excep when fin_excep='1' else res_addsub_s2;
cmp_tag<=tag_cmp;
cmp_rdy<=fin_cmp_s1;
cmp<=res_cmp;
strt_req_s1<=strt_req;
strt_s1<=strt_req_s1 and op_a_rdy and op_b_rdy and not state_busy_s1;
strt_ack_s1<=strt_s1;
res_rdy_s1<=fin_addsub_s1 or fin_cmp_s1 or fin_excep;
res_ack_s1<=(res_ack_s2 and not mode_cmp) or (cmp_ack and mode_cmp
strt_req_s2<=state_busy_s1;
strt_s2<=strt_req_s2 and not state_busy_s2;
strt_ack_s2<=strt_s2 ;
res_rdy_s2<=fin_addsub_s2;
res_ack_s2<=res_ack;
state_busy_s2<='0';
handshake_1: if STAGES=1 generate
state_busy_s1<='0';
end generate;
handshake_2: if STAGES=2 generate process
begin
wait until rising_edge(clk);
if rst='1' then
state_busy_s1<='0';
elsif fin_addsub_s1='1' then
state_busy_s1<='1';
elsif res_ack_s1='1' then
state_busy_s1<='0';
end if;
end process; end generate;
end block;
end architecture;
3.2.2.3 Fichero FPU_mul.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
library copro_fpu_v2_00_b;
use copro_fpu_v2_00_b.FPU_pack_configuration.all;
use copro_fpu_v2_00_b.FPU_pack_defines.all;
use IEEE.STD_LOGIC_MISC.ALL;
entity FPU_mul is
generic(
NBITS_TAG: integer:=0);
port(
clk : in std_logic;
rst : in std_logic;
strt_tag: in std_logic_vector(NBITS_TAG-1 downto 0);
strt_req: in std_logic;
strt_ack: out std_logic;
op_a_rdy: in std_logic;
op_a_ack: out std_logic;
op_a_status: in T_FLOAT;
op_a: in std_logic_vector(31 downto 0);
op_b_rdy: in std_logic;
op_b_ack: out std_logic;
op_b_status : in T_FLOAT;
op_b: in std_logic_vector(31 downto 0);
res_tag: out std_logic_vector(NBITS_TAG-1 downto 0);
res_status: out T_FLOAT;
res: out std_logic_vector(36 downto 0);
res_rdy: out std_logic;
res_ack: in std_logic);
end entity;
architecture beh2b of FPU_mul is
constant STAGES: integer:=FPU_MULTIPLIER_STAGES;
signal fin_s1,fin_s12,fin_s2,fin_s,fin_excep: std_logic;
signal signo_s1,signo_s12: std_logic;
signal ab00_s1,ab01_s1,ab10_s1,ab11_s1,ab00_s12,ab01_s12,ab10_s12,ab11_s12:
std_logic_vector(23 downto 0);
signal exp_s1,exp_s12: std_logic_vector(7 downto 0);
signal res_s2,res_excep: std_logic_vector(36 downto 0);
signal status_s2,status_excep: T_FLOAT;
signal strt_s1,strt_s2: std_logic;
signal tag_s1,tag_s12,tag_s2,tag_excep: std_logic_vector(res_tag'range);
begin
ST1A: process(op_a_status,op_b_status,signo_s1,strt_s1,tag_s1)
variable man_excep: std_logic_vector(27 downto 0);
variable exp_excep: std_logic_vector(7 downto 0);
variable sig_excep: std_logic;
begin
status_excep<=FOK;
sig_excep:=signo_s1;
exp_excep:=(others=>'X');
man_excep:=(others=>'X');
fin_excep<='0';
if op_a_status=FINF or op_b_status=FINF then
status_excep<=FINF; --si uno o los dos son INF hay que mirar los signos
fin_excep<=strt_s1;
end if;
if op_a_status=FZERO or op_b_status=FZERO then
status_excep<=FZERO;-- si alguno es cero el resultado es cero
fin_excep<=strt_s1;
end if;
if (op_a_status=FINF and op_b_status=FZERO) or (op_a_status=FZERO and
op_b_status=FINF) then
status_excep<=FNAN;--si uno es cero y el otro INF el resultado es NaN
fin_excep<=strt_s1;
end if;
if op_a_status=FNAN or op_b_status=FNAN then
status_excep<=FNAN; --si uno es NaN el resultado es NaN
fin_excep<=strt_s1;
end if;
res_excep<=sig_excep & exp_excep & man_excep;
tag_excep<=tag_s1;
end process;
ST1B: process(op_a,op_b,strt_s1,fin_excep,strt_tag)
variable a,b: std_logic_vector(11 downto 0);
variable exp_a,exp_b: std_logic_vector(7 downto 0);
begin
fin_s1<=strt_s1 and not fin_excep;
a:=op_a(11 downto 0);
b:=op_b(11 downto 0);
ab00_s1<=a*b;
a:='1'&op_a(22 downto 12);
b:=op_b(11 downto 0);
ab10_s1<=a*b;
a:=op_a(11 downto 0);
b:='1'&op_b(22 downto 12);
ab01_s1<=a*b;
a:='1'&op_a(22 downto 12);
b:='1'&op_b(22 downto 12);
ab11_s1<=a*b;
exp_a:=op_a(30 downto 23);
exp_b:=op_b(30 downto 23);
exp_s1<=exp_a+exp_b-127;--ojo puede dar overflow
signo_s1<=op_a(31) xor op_b(31);
tag_s1<=strt_tag;
end process;
ST1toST2_1: if STAGES=1 generate
process(ab00_s1,ab10_s1,ab01_s1,ab11_s1,exp_s1,signo_s1,tag_s1)
begin
ab00_s12<=ab00_s1;
ab10_s12<=ab10_s1;
ab01_s12<=ab01_s1;
ab11_s12<=ab11_s1;
exp_s12<=exp_s1;
signo_s12<=signo_s1;
tag_s12<=tag_s1;
end process; end generate;
ST1toST2_2: if STAGES=2 generate process
begin
wait until rising_edge(clk);
if fin_s1='1' then
ab00_s12<=ab00_s1;
ab10_s12<=ab10_s1;
ab01_s12<=ab01_s1;
ab11_s12<=ab11_s1;
exp_s12<=exp_s1;
signo_s12<=signo_s1;
tag_s12<=tag_s1;
end if;
end process; end generate;
ST2:
process(strt_s2,ab00_s12,ab01_s12,ab10_s12,ab11_s12,exp_s12,signo_s12,tag_s12)
variable r0,r3: std_logic_vector(11 downto 0);
variable r1,r2: std_logic_vector(13 downto 0);
variable r: std_logic_vector(47 downto 0);
variable man: std_logic_vector(27 downto 0);
variable sticky_bit_vector: std_logic_vector(20 downto 0);
begin
fin_s2<=strt_s2;--fin_s2<=fin_s12;
r0:=ab00_s12(11 downto 0);
r1:=("00"&ab00_s12(23 downto 12))+("00"&ab01_s12(11 downto
0))+("00"&ab10_s12(11 downto 0));
r2:=r1(13 downto 12)+("00"&ab01_s12(23 downto 12))+("00"&ab10_s12(23
downto 12))+("00"&ab11_s12(11 downto 0));
r3:=r2(13 downto 12)+ab11_s12(23 downto 12);
r:=r3 & r2(11 downto 0) & r1(11 downto 0) & r0;
man:=r(47 downto 20);
sticky_bit_vector:=r(20 downto 0);
if man(man'left)='1' then
man(1):=or_reduce(sticky_bit_vector);
else
sticky_bit_vector(20):='0';
man(0):=or_reduce(sticky_bit_vector);
end if;
res_s2<=signo_s12 & exp_s12 & man;
status_s2<=FOK;
tag_s2<=tag_s12;
end process;
handshake: block
signal state_busy_s1,strt_req_s1,strt_ack_s1,res_rdy_s1,res_ack_s1: std_logic;
signal state_busy_s2,strt_req_s2,strt_ack_s2,res_rdy_s2,res_ack_s2: std_logic;
begin
strt_ack<=strt_ack_s1;
op_a_ack<=strt_ack_s1;
op_b_ack<=strt_ack_s1;
res_tag<=tag_excep when fin_excep='1' else tag_s2;
res_status<=status_excep when fin_excep='1' else status_s2;
res_rdy<=fin_excep when fin_excep='1' else res_rdy_s2;
res<=res_excep when fin_excep='1' else res_s2;
strt_req_s1<=strt_req;
strt_s1<=strt_req_s1 and op_a_rdy and op_b_rdy and not state_busy_s1;
strt_ack_s1<=strt_s1;
res_rdy_s1<=fin_s1 or fin_excep;
res_ack_s1<=res_ack_s2;
strt_req_s2<=state_busy_s1;
strt_s2<=strt_req_s2 and not state_busy_s2;
strt_ack_s2<=strt_s2 ;
res_rdy_s2<=fin_s2;
res_ack_s2<=res_ack;
state_busy_s2<='0';
handshake_1: if STAGES=1 generate
state_busy_s1<='0';
end generate;
handshake_2: if STAGES=2 generate process
begin
wait until rising_edge(clk);
if rst='1' then
state_busy_s1<='0';
elsif fin_s1='1' then
state_busy_s1<='1';
elsif res_ack_s1='1' then
state_busy_s1<='0';
end if;
end process; end generate;
end block;
end architecture;
3.2.2.4 Fichero FPU_divsqrt.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
library copro_fpu_v2_00_b;
use copro_fpu_v2_00_b.FPU_pack_configuration.all;
use copro_fpu_v2_00_b.FPU_pack_defines.all;
entity FPU_divsqrt is
generic(
NBITS_TAG: integer:=0);
port(
clk : in std_logic;
rst : in std_logic;
mode: in T_MODE_DIVSQRT;
strt_tag: in std_logic_vector(NBITS_TAG-1 downto 0);
strt_req: in std_logic;
strt_ack: out std_logic;
op_a_rdy: in std_logic;
op_a_ack: out std_logic;
op_a_status: in T_FLOAT;
op_a: in std_logic_vector(31 downto 0);
op_b_rdy: in std_logic;
op_b_ack: out std_logic;
op_b_status : in T_FLOAT;
op_b: in std_logic_vector(31 downto 0);
res_tag: out std_logic_vector(NBITS_TAG-1 downto 0);
res_status: out T_FLOAT;
res: out std_logic_vector(36 downto 0);
res_rdy: out std_logic;
res_ack: in std_logic);
end entity;
architecture beh2b of FPU_divsqrt is
signal status_excep,status_s1: T_FLOAT;
signal exp_s1: STD_LOGIC_VECTOR (7 downto 0);
signal res_excep,res_s1: std_logic_vector(36 downto 0);
signal strt_s1,fin_excep,fin_s1,sig_s1,res_ack_s1,busy_s1: std_logic;
signal rst_divsqrt, strt_divsqrt, rdy_divsqrt, qrdy_divsqrt, qack_divsqrt, mode_divsqrt,
a_oddexp_divsqrt: std_logic;
signal a_divsqrt,b_divsqrt: std_logic_vector(23 downto 0);
signal q_divsqrt: std_logic_vector(23+3 downto 0);--2 bits adicionales + sticky_bit
signal stickybit_divsqrt: std_logic;
signal dec_exp: std_logic_vector(0 downto 0);--Decrementar exponente si la primera
resta de r-b da negativa.
signal tag_s1,tag_excep: std_logic_vector(res_tag'range);
begin
ST1A: process(op_a_status,op_b_status,strt_s1,mode,op_a(31),op_b(31),tag_s1)
variable man_excep: std_logic_vector(27 downto 0);
variable exp_excep: std_logic_vector(7 downto 0);
variable sig_excep: std_logic;
begin
status_excep<=FOK;
exp_excep:=(others=>'X');
man_excep:=(others=>'X');
fin_excep<='0';
if mode=FDIV then
sig_excep:=op_a(31) xor op_b(31);
if op_a_status=FZERO or op_b_status=FINF then
status_excep<=FZERO;
fin_excep<=strt_s1;
end if;
if op_a_status=FINF or op_b_status=FZERO then
status_excep<=FINF;
fin_excep<=strt_s1;
end if;
if op_a_status=FZERO and op_b_status=FZERO then
status_excep<=FNAN;
fin_excep<=strt_s1;
end if;
if op_a_status=FINF and op_b_status=FINF then
status_excep<=FNAN;
fin_excep<=strt_s1;
end if;
if op_a_status=FNAN or op_b_status=FNAN then
status_excep<=FNAN;
fin_excep<=strt_s1;
end if;
end if;
if mode=FSQRT then
sig_excep:='0';
if op_a_status=FZERO then
status_excep<=FZERO;
fin_excep<=strt_s1;
end if;
if op_a_status=FINF then
status_excep<=FINF;
fin_excep<=strt_s1;
end if;
if op_a_status=FNAN or op_a(31)='1' then
status_excep<=FNAN;
fin_excep<=strt_s1;
end if;
end if;
res_excep<=sig_excep & exp_excep & man_excep;
tag_excep<=tag_s1;
end process;
ST1B: process(op_a,op_b,mode,mode_divsqrt)
variable div_exp,sqrt_exp: std_logic_vector(7 downto 0);
begin
if mode=FSQRT then mode_divsqrt<='1'; else mode_divsqrt<='0'; end if;
div_exp:=op_a(30 downto 23)-op_b(30 downto 23);
sqrt_exp:=shr(op_a(30 downto 23)+1,"1");
a_divsqrt<='1'&op_a(22 downto 0);
b_divsqrt<='1'&op_b(22 downto 0);
a_oddexp_divsqrt<=not op_a(23);
if mode_divsqrt='0' then
sig_s1<=op_a(31) xor op_b(31);
exp_s1<=div_exp+127;--ojo que puede dar overflow
else
sig_s1<='0';
exp_s1<=sqrt_exp+63;
end if;
stickybit_divsqrt<='0';
if mode_divsqrt='1' then
stickybit_divsqrt<='1';
end if;
end process;
ST1C:
process(clk,rst,strt_s1,fin_excep,qrdy_divsqrt,sig_s1,exp_s1,q_divsqrt,strt_divsqrt,rdy_d
ivsqrt,res_ack_s1,strt_tag,dec_exp)
variable sig: std_logic;
variable exp: std_logic_vector(7 downto 0);
variable exp_aux: std_logic_vector(7 downto 0);
begin
strt_divsqrt<=strt_s1 and not fin_excep;
rst_divsqrt<=rst;
fin_s1<=qrdy_divsqrt;
exp:=exp_aux-dec_exp;
res_s1<=sig & exp & ('0'&q_divsqrt);
status_s1<=FOK;
qack_divsqrt<=res_ack_s1;
busy_s1<=not rdy_divsqrt;
tag_s1<=strt_tag;
if rising_edge(clk) and strt_divsqrt='1' then
sig:=sig_s1;
exp_aux:=exp_s1;
end if;
end process;
ST2_0: if FPU_DIVISION_ZEROSHIFT=0 generate ST2: process
type T_ESTADO is (ESPERA,COMPARA);
variable estado: T_ESTADO;
subtype T_INDEX is integer range q_divsqrt'left downto q_divsqrt'right+1;
variable index: T_INDEX;
variable q,one: std_logic_vector(T_INDEX'left downto T_INDEX'right);
variable r: std_logic_vector(a_divsqrt'left+5 downto a_divsqrt'right+3);
variable b: std_logic_vector(a_divsqrt'left+5 downto a_divsqrt'right+3);
variable cmp: std_logic_vector(r'left+1 downto r'right);
variable r_first_24_bits: std_logic_vector(26 downto 3);
variable rdy,busy,qready,mode,stickybit: std_logic;
begin
wait until rising_edge(clk);
case estado is
when ESPERA =>
if strt_divsqrt='1' and busy='0' then
mode:=mode_divsqrt;
busy:='1'; qready:='0';
index:=T_INDEX'high;
r:="00" & a_divsqrt;
q:=(others=>'0');
stickybit:=stickybit_divsqrt;
estado:=COMPARA;
if mode='0' then -- division
one:=(one'left=>'1', others=>'0');
b:="00" & b_divsqrt;
dec_exp<=(others=>'0');
else -- sqrt
one:=(one'left=>'1', others=>'0');
b:="00" & one(b'left-2 downto b'right);
if a_oddexp_divsqrt='1' then
r:=shl(r,"1");
end if;
end if;
end if;
when COMPARA =>
cmp:=('0'&r)-('0'&b);
if mode='0' then -- division
if cmp(cmp'left)='1' then
--q:=q;
else
q:=q or one;
r:=cmp(r'range);
end if;
r_first_24_bits:=r(r_first_24_bits'range);
if r_first_24_bits=X"000000" then
stickybit:='0';
else
stickybit:='1';
end if;
if index=T_INDEX'high and cmp(cmp'left)='1' then
--NO shr one
else
one:=shr(one,"1");
end if;
r:=shl(r,"1");
else -- sqrt
if cmp(cmp'left)='1' then
--q:=q;
else
q:=q or one;
r:=cmp(r'range);
end if;
one:=shr(one,"1");
r:=shl(r,"1");
b:=('0'&q(b'left-2 downto b'right)&'0') or ("00"&one(b'left-
2 downto b'right));
end if;
if index=T_INDEX'low or cmp=0 then
busy:='1'; qready:='1'; estado:=ESPERA;
else
if index=T_INDEX'high and cmp(cmp'left)='1' then
dec_exp<=(others=>'1');
else
index:=index-1;
end if;
end if;
end case;
if qack_divsqrt='1' or rst_divsqrt='1' then
busy:='0'; qready:='0';
q:=(others=>'X');
estado:=ESPERA;
end if;
rdy_divsqrt<=not busy;
qrdy_divsqrt<=qready;
q_divsqrt<=q&stickybit;
end process;
end generate;
handshake: block
signal state_busy_s1: std_logic;
begin
strt_ack<=res_ack when fin_excep='1' else strt_s1;
op_a_ack<=res_ack when fin_excep='1' else strt_s1;
op_b_ack<=(res_ack and mode_divsqrt) when fin_excep='1' else (strt_s1 and
mode_divsqrt);
strt_s1<=strt_req and op_a_rdy and (op_b_rdy or mode_divsqrt) and not
state_busy_s1;
res_tag<=tag_excep when fin_excep='1' else tag_s1;
res_rdy<=fin_excep when fin_excep='1' else fin_s1;
res<=res_excep when fin_excep='1' else res_s1;
res_status<=status_excep when fin_excep='1' else status_s1;
res_ack_s1<=res_ack;
state_busy_s1<=busy_s1;
end block;
end architecture;
3.2.2.5 Fichero FPU_norm.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.STD_LOGIC_ARITH.conv_std_logic_vector;
use IEEE.STD_LOGIC_MISC.ALL;
library copro_fpu_v2_00_b;
use copro_fpu_v2_00_b.FPU_pack_configuration.all;
use copro_fpu_v2_00_b.FPU_pack_defines.all;
entity FPU_norm is
generic(
NBITS_TAG: integer:=0);
port(
clk : in std_logic;
rst : in std_logic;
strt_tag: in std_logic_vector(NBITS_TAG-1 downto 0);
strt_req: in std_logic;
strt_ack: out std_logic;
op_a_status: in T_FLOAT;
op_a: in std_logic_vector(36 downto 0);
res_tag: out std_logic_vector(NBITS_TAG-1 downto 0);
res: out std_logic_vector(31 downto 0);
res_rdy: out std_logic;
res_ack: in std_logic);
end entity;
architecture beh2b of FPU_norm is
constant STAGES: integer:=FPU_NORMALIZER_STAGES;
constant ROUND_METHOD: integer:=FPU_NORMALIZER_ROUND;
subtype T_INDEX is integer range 0 to 28;
constant T_INDEX_NBITS: integer:=5;
signal index_s1,index_s12: T_INDEX; --integer range 30 downto 0;
signal exp_res_s1,exp_res_s12: STD_LOGIC_VECTOR (7 downto 0);
signal man_val_s1,man_val_s12: STD_LOGIC_VECTOR (27 downto 0);
signal res_excep,res_s2: STD_LOGIC_VECTOR (31 downto 0);
signal fin_excep,fin_s1,fin_s2: std_logic;--signal fin_excep,fin_s1,fin_s12,fin_s2:
std_logic;
signal sig_s1,sig_s12: STD_LOGIC;
signal strt_s1,strt_s2: std_logic;
signal tag_s1,tag_s12,tag_s2,tag_excep: std_logic_vector(res_tag'range);
function Index1(a: std_logic_vector(27 downto 0)) return T_INDEX is
variable o: T_INDEX:=T_INDEX'high;
variable f: std_logic:='0';
begin
for idx in a'high downto a'low loop
if f='0' and a(idx)='1' then
f:='1';
o:=a'high-idx;
end if;
end loop;
return o;
end function;
function BarrelLShift(a: std_logic_vector; idx: T_INDEX) return std_logic_vector is
begin
return shl(a,conv_std_logic_vector(idx,T_INDEX_NBITS));
end function;
function RoundBit(a: std_logic_vector; constant n: integer) return std_logic is
variable slc,cmp: std_logic_vector(n-1 downto 0);
variable round: std_logic:='0';
begin
if ROUND_METHOD=1 then
cmp(cmp'left):='1'; cmp(cmp'left-1 downto cmp'right):=(others=>'0');
slc:=a(a'right+n downto a'right+1);
if slc>cmp then
round:='1';
end if;
if slc=cmp and a(4)='1' then
round:='1';
end if;
end if;
return round;
end function;
begin
ST1A: process(op_a_status,sig_s1,strt_s1,tag_s1)
variable man_excep: std_logic_vector(22 downto 0):=conv_std_logic_vector(0,23);
variable exp_excep: std_logic_vector(7 downto 0):=conv_std_logic_vector(0,8);
variable sig_excep: std_logic;
begin
sig_excep:='X';
exp_excep:=(others=>'X');
man_excep:=(others=>'X');
fin_excep<='0';
if op_a_status=FZERO then
sig_excep:=sig_s1;
exp_excep:=(others=>'0');
man_excep:=(others=>'0');
fin_excep<=strt_s1;
end if;
if op_a_status=FINF then
sig_excep:=sig_s1;
exp_excep:=(others=>'1');
man_excep:=(others=>'0');
fin_excep<=strt_s1;
end if;
if op_a_status=FNAN then
sig_excep:='1';
exp_excep:=(others=>'1');
man_excep:=(22=>'1', others=>'0');
fin_excep<=strt_s1;
end if;
res_excep<=sig_excep & exp_excep & man_excep;
tag_excep<=tag_s1;
end process;
ST1B: process(strt_s1,fin_excep,op_a,strt_tag)
variable idx: T_INDEX;
begin
fin_s1<=strt_s1 and not fin_excep;
exp_res_s1<=op_a(35 downto 28);
idx:=Index1(op_a(27 downto 0));
man_val_s1<=op_a(27 downto 0);--BarrelLShift(op_a(27 downto 0),idx);
index_s1<=idx;
sig_s1<=op_a(36);
tag_s1<=strt_tag;
end process;
ST1toST2_1: if STAGES=1 generate
process(exp_res_s1,man_val_s1,index_s1,sig_s1,tag_s1)
begin
--fin_s12<=fin_s1 and not rst;
exp_res_s12<=exp_res_s1;
man_val_s12<=man_val_s1;
index_s12<=index_s1;
sig_s12<=sig_s1;
tag_s12<=tag_s1;
end process; end generate;
ST1toST2_2: if STAGES=2 generate process
begin
wait until rising_edge(clk);
if fin_s1='1' then
--fin_s12<=fin_s1 and not rst;
exp_res_s12<=exp_res_s1;
man_val_s12<=man_val_s1;
index_s12<=index_s1;
sig_s12<=sig_s1;
tag_s12<=tag_s1;
end if;
end process; end generate;
ST2: process(strt_s2,exp_res_s12,man_val_s12,index_s12,sig_s12,tag_s12)
variable man_ext: std_logic_vector(27 downto 0);
variable man: std_logic_vector(27 downto 4);--:=conv_std_logic_vector(0,23);
variable exp: std_logic_vector(7 downto 0);--:=conv_std_logic_vector(0,8);
variable round_bit,sig: std_logic;
begin
fin_s2<=strt_s2;--fin_s12;
exp:=exp_res_s12+(1-index_s12);--exp_res_s1<=redondeo_v2(30 downto 23);
man_ext:=BarrelLShift(man_val_s12,index_s12);
round_bit:=RoundBit(man_ext,3);
man:=man_ext(man'range)+round_bit;
sig:=sig_s12;
if exp_res_s12=254 and index_s12=0 then
exp:=(others=>'1');--miramos overflow, si el exponente es 255 implica
infinito pero manteniento el signo
man:=(others=>'0');
sig:=sig_s12;
end if;
if index_s12=T_INDEX'high then--index_s12=28 then
exp:=(others=>'0');--miramos underflow si la mantisa es cero el resultado
es cero
man:=(others=>'0');
sig:=sig_s12;
end if;
res_s2<=sig & exp & man(26 downto 4);
tag_s2<=tag_s12;
end process;
handshake: block
signal state_busy_s1,strt_req_s1,strt_ack_s1,res_rdy_s1,res_ack_s1: std_logic;
signal state_busy_s2,strt_req_s2,strt_ack_s2,res_rdy_s2,res_ack_s2: std_logic;
begin
strt_ack<=res_ack when fin_excep='1' else strt_ack_s1;
res_tag<=tag_excep when fin_excep='1' else tag_s2;
res_rdy<=fin_excep when fin_excep='1' else res_rdy_s2;
res<=res_excep when fin_excep='1' else res_s2;
strt_req_s1<=strt_req;
strt_s1<=strt_req_s1 and not state_busy_s1;
strt_ack_s1<=strt_s1;
res_rdy_s1<=fin_s1 or fin_excep;
res_ack_s1<=res_ack_s2;
strt_req_s2<=state_busy_s1;
strt_s2<=strt_req_s2 and not state_busy_s2;
strt_ack_s2<=strt_s2 ;
res_rdy_s2<=fin_s2;
res_ack_s2<=res_ack;
state_busy_s2<='0';
handshake_1: if STAGES=1 generate
state_busy_s1<='0';
end generate;
handshake_2: if STAGES=2 generate process
begin
wait until rising_edge(clk);
if rst='1' then
state_busy_s1<='0';
elsif fin_s1='1' then
state_busy_s1<='1';
elsif res_ack_s1='1' then
state_busy_s1<='0';
end if;
end process; end generate;
end block;
end architecture;
3.2.2.6 Fichero FPU_alu.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
library copro_fpu_v2_00_b;
use copro_fpu_v2_00_b.FPU_pack_configuration.all;
use copro_fpu_v2_00_b.FPU_pack_defines.all;
entity FPU_alu is
generic(
NBITS_TAG: integer:=0);
port (
clk: in std_logic;
rst: in std_logic;
mode : in T_MODE_ALU;
strt_tag: in std_logic_vector(NBITS_TAG-1 downto 0):=(others=>'X');
strt_req : in std_logic;
strt_ack: out std_logic;
op_a_rdy: in std_logic;
op_a_ack: out std_logic;
op_a : in STD_LOGIC_VECTOR (31 downto 0);
op_b_rdy: in std_logic;
op_b_ack: out std_logic;
op_b : in STD_LOGIC_VECTOR (31 downto 0);
res_tag: out std_logic_vector(NBITS_TAG-1 downto 0);
res : out STD_LOGIC_VECTOR (31 downto 0);
res_rdy: out std_logic;
res_ack: in std_logic);
end entity;
architecture beh2b of FPU_alu is
component FPU_sign is
generic(
NBITS_TAG: integer:=0);
port(
clk : in std_logic;
rst : in std_logic;
mode: in T_MODE_SIGN;
strt_tag: in std_logic_vector(NBITS_TAG-1 downto 0);
strt_req : in std_logic;
strt_ack: out std_logic;
op_a_rdy: in std_logic;
op_a_ack: out std_logic;
op_a_status: in T_FLOAT;
op_a: in std_logic_vector(31 downto 0);
res_tag: out std_logic_vector(NBITS_TAG-1 downto 0);
res: out std_logic_vector(31 downto 0);
res_rdy: out std_logic;
res_ack: in std_logic);
end component;
component FPU_int2f is
generic(
NBITS_TAG: integer:=0);
port(
clk : in std_logic;
rst : in std_logic;
strt_tag: in std_logic_vector(NBITS_TAG-1 downto 0);
strt_req : in std_logic;
strt_ack: out std_logic;
op_a_rdy: in std_logic;
op_a_ack: out std_logic;
op_a: in std_logic_vector(31 downto 0);
res_tag: out std_logic_vector(NBITS_TAG-1 downto 0);
res_status: out T_FLOAT;
res: out std_logic_vector(36 downto 0);
res_rdy: out std_logic;
res_ack: in std_logic);
end component;
component FPU_addsubcmp is
generic(
NBITS_TAG: integer:=0);
port(
clk : in std_logic;
rst : in std_logic;
mode: T_MODE_ADDSUBCMP;
strt_tag: in std_logic_vector(NBITS_TAG-1 downto 0);
strt_req : in std_logic;
strt_ack: out std_logic;
op_a_rdy: in std_logic;
op_a_ack: out std_logic;
op_a_status : in T_FLOAT;
op_a : in std_logic_vector(31 downto 0);
op_b_rdy: in std_logic;
op_b_ack: out std_logic;
op_b_status : in T_FLOAT;
op_b : in std_logic_vector(31 downto 0);
cmp_tag: out std_logic_vector(NBITS_TAG-1 downto 0);
cmp: out std_logic_vector(31 downto 0);
cmp_rdy: out std_logic;
cmp_ack: in std_logic;
res_tag: out std_logic_vector(NBITS_TAG-1 downto 0);
res_status: out T_FLOAT;
res : out std_logic_vector(36 downto 0);
res_rdy: out std_logic;
res_ack: in std_logic);
end component;
component FPU_mul is
generic(
NBITS_TAG: integer:=0);
port(
clk : in std_logic;
rst : in std_logic;
strt_tag: in std_logic_vector(NBITS_TAG-1 downto 0);
strt_req: in std_logic;
strt_ack: out std_logic;
op_a_rdy: in std_logic;
op_a_ack: out std_logic;
op_a_status: in T_FLOAT;
op_a: in std_logic_vector(31 downto 0);
op_b_rdy: in std_logic;
op_b_ack: out std_logic;
op_b_status : in T_FLOAT;
op_b: in std_logic_vector(31 downto 0);
res_tag: out std_logic_vector(NBITS_TAG-1 downto 0);
res_status: out T_FLOAT;
res: out std_logic_vector(36 downto 0);
res_rdy: out std_logic;
res_ack: in std_logic);
end component;
component FPU_divsqrt is
generic(
NBITS_TAG: integer:=0);
port(
clk : in std_logic;
rst : in std_logic;
mode: in T_MODE_DIVSQRT;
strt_tag: in std_logic_vector(NBITS_TAG-1 downto 0);
strt_req: in std_logic;
strt_ack: out std_logic;
op_a_rdy: in std_logic;
op_a_ack: out std_logic;
op_a_status: in T_FLOAT;
op_a: in std_logic_vector(31 downto 0);
op_b_rdy: in std_logic;
op_b_ack: out std_logic;
op_b_status : in T_FLOAT;
op_b: in std_logic_vector(31 downto 0);
res_tag: out std_logic_vector(NBITS_TAG-1 downto 0);
res_status: out T_FLOAT;
res: out std_logic_vector(36 downto 0);
res_rdy: out std_logic;
res_ack: in std_logic);
end component;
component FPU_norm is
generic(
NBITS_TAG: integer:=0);
port(
clk : in std_logic;
rst : in std_logic;
strt_tag: in std_logic_vector(NBITS_TAG-1 downto 0);
strt_req: in std_logic;
strt_ack: out std_logic;
op_a_status: in T_FLOAT;
op_a: in std_logic_vector(36 downto 0);
res_tag: out std_logic_vector(NBITS_TAG-1 downto 0);
res: out std_logic_vector(31 downto 0);
res_rdy: out std_logic;
res_ack: in std_logic);
end component;
signal op_a_status,op_b_status: T_FLOAT;
signal mode_addsubcmp: T_MODE_ADDSUBCMP;
signal strt_req_addsubcmp,strt_ack_addsubcmp,cres_rdy_addsub,cres_ack_addsub:
std_logic;
signal op_a_ack_addsubcmp,op_b_ack_addsubcmp: std_logic;
signal cres_rdy_cmp,cres_ack_cmp: std_logic;
signal cres_cmp: std_logic_vector(31 downto 0);
signal cres_addsub: std_logic_vector(36 downto 0);
signal cres_status_addsub: T_FLOAT;
signal cres_tag_addsub,cres_tag_cmp: std_logic_vector(NBITS_TAG-1 downto 0);
signal strt_req_mul,strt_ack_mul,cres_rdy_mul,cres_ack_mul: std_logic;
signal op_a_ack_mul,op_b_ack_mul: std_logic;
signal cres_mul: std_logic_vector(36 downto 0);
signal cres_status_mul: T_FLOAT;
signal cres_tag_mul: std_logic_vector(NBITS_TAG-1 downto 0);
signal mode_divsqrt: T_MODE_DIVSQRT;
signal strt_req_divsqrt,strt_ack_divsqrt,cres_rdy_divsqrt,cres_ack_divsqrt: std_logic;
signal op_a_ack_divsqrt,op_b_ack_divsqrt: std_logic;
signal cres_divsqrt: std_logic_vector(36 downto 0);
signal cres_status_divsqrt: T_FLOAT;
signal cres_tag_divsqrt: std_logic_vector(NBITS_TAG-1 downto 0);
signal strt_req_int2f,strt_ack_int2f,cres_rdy_int2f,cres_ack_int2f: std_logic;
signal op_a_ack_int2f: std_logic;
signal cres_int2f: std_logic_vector(36 downto 0);
signal cres_status_int2f: T_FLOAT;
signal cres_tag_int2f: std_logic_vector(NBITS_TAG-1 downto 0);
signal strt_req_norm,strt_ack_norm,cres_rdy_norm,cres_ack_norm: std_logic;
signal op_a_status_norm: T_FLOAT;
signal op_a_norm: std_logic_vector(36 downto 0);
signal cres_norm: std_logic_vector(31 downto 0);
signal strt_tag_norm,cres_tag_norm: std_logic_vector(NBITS_TAG-1 downto 0);
signal mode_sign: T_MODE_SIGN;
signal strt_req_sign,strt_ack_sign,cres_rdy_sign,cres_ack_sign: std_logic;
signal op_a_ack_sign: std_logic;
signal cres_sign: std_logic_vector(31 downto 0);
signal cres_tag_sign: std_logic_vector(NBITS_TAG-1 downto 0);
signal cres_mux_addsub, cres_mux_mul, cres_mux_divsqrt, cres_mux_int2f,
op_mux_norm: std_logic_vector(NBITS_TAG+op_a_norm'length-1 downto 0);
signal cres_mux_norm, cres_mux_cmp, cres_mux_sign, res_mux:
std_logic_vector(NBITS_TAG+res'length-1 downto 0);
begin
------------------------------------------------------
-- ports to/from Stage#1
------------------------------------------------------
scan_cmd:
process(mode,strt_req,strt_ack_addsubcmp,strt_ack_mul,strt_ack_divsqrt,strt_ack_int2f,
strt_ack_sign,op_a_ack_addsubcmp,op_b_ack_addsubcmp,op_a_ack_mul,op_b_ack_m
ul,op_a_ack_divsqrt,op_b_ack_divsqrt,op_a_ack_int2f,op_a_ack_sign)
begin
strt_req_sign<='0'; strt_req_addsubcmp<='0'; strt_req_mul<='0';
strt_req_divsqrt<='0'; strt_req_int2f<='0'; --evita inferencia latches
strt_ack<='0';
op_a_ack<='0'; op_b_ack<='0';
mode_sign<=FPOS; mode_addsubcmp<=FADD; mode_divsqrt<=FDIV; --evita
inferencia latches
case mode is
when FADD to FLWR =>
strt_ack<=strt_ack_addsubcmp;
strt_req_addsubcmp<=strt_req;
op_a_ack<=op_a_ack_addsubcmp;
op_b_ack<=op_b_ack_addsubcmp;
mode_addsubcmp<=mode;
when FMUL=>
strt_ack<=strt_ack_mul;
strt_req_mul<=strt_req;
op_a_ack<=op_a_ack_mul;
op_b_ack<=op_b_ack_mul;
when FDIV|FSQRT=>
strt_ack<=strt_ack_divsqrt;
strt_req_divsqrt<=strt_req;
op_a_ack<=op_a_ack_divsqrt;
op_b_ack<=op_b_ack_divsqrt;
mode_divsqrt<=mode;
when FINT2F=>
strt_ack<=strt_ack_int2f;
strt_req_int2f<=strt_req;
op_a_ack<=op_a_ack_int2f;
when FPOS to FABS=>
strt_ack<=strt_ack_sign;
strt_req_sign<=strt_req;
op_a_ack<=op_a_ack_sign;
mode_sign<=mode;
end case;
end process;
scan_a: process(op_a)
begin
op_a_status<=FOK;
if op_a(30 downto 0)=0 then
op_a_status<=FZERO;
end if;
if op_a(30 downto 23)=255 then
if op_a(22 downto 0)=0 then
op_a_status<=FINF;
else
op_a_status<=FNAN;
end if;
end if;
if op_a(30 downto 23)=0 and op_a(22 downto 0)>0 then
op_a_status<=FNAN;
end if;
end process;
scan_b: process(op_b)
begin
op_b_status<=FOK;
if op_b(30 downto 0)=0 then
op_b_status<=FZERO;
end if;
if op_b(30 downto 23)=255 then
if op_b(22 downto 0)=0 then
op_b_status<=FINF;
else
op_b_status<=FNAN;
end if;
end if;
if op_b(30 downto 23)=0 and op_b(22 downto 0)>0 then
op_b_status<=FNAN;
end if;
end process;
------------------------------------------------------
-- Stage#1
------------------------------------------------------
int2f: FPU_int2f generic map(NBITS_TAG) port map(
clk=> clk,
rst=> rst,
strt_tag=> strt_tag,
strt_req=> strt_req_int2f,
strt_ack=> strt_ack_int2f,
op_a_rdy=> op_a_rdy,
op_a_ack=> op_a_ack_int2f,
op_a=> op_a,
res_tag=> cres_tag_int2f,
res_status=> cres_status_int2f,
res=> cres_int2f,
res_rdy=> cres_rdy_int2f,
res_ack=> cres_ack_int2f);
addsubcmp: FPU_addsubcmp generic map(NBITS_TAG) port map(
clk=> clk,
rst=> rst,
mode=> mode_addsubcmp,
strt_tag=> strt_tag,
strt_req=> strt_req_addsubcmp,
strt_ack=> strt_ack_addsubcmp,
op_a_rdy=> op_a_rdy,
op_a_ack=> op_a_ack_addsubcmp,
op_a_status=> op_a_status,
op_a=> op_a,
op_b_rdy=> op_b_rdy,
op_b_ack=> op_b_ack_addsubcmp,
op_b_status=> op_b_status,
op_b=> op_b,
cmp_tag=> cres_tag_cmp,
cmp=> cres_cmp,
cmp_rdy=> cres_rdy_cmp,
cmp_ack=> cres_ack_cmp,
res_tag=> cres_tag_addsub,
res_status=> cres_status_addsub,
res=> cres_addsub,
res_rdy=> cres_rdy_addsub,
res_ack=> cres_ack_addsub);
mul: FPU_mul generic map(NBITS_TAG) port map(
clk=> clk,
rst=> rst,
strt_tag=> strt_tag,
strt_req=> strt_req_mul,
strt_ack=> strt_ack_mul,
op_a_rdy=> op_a_rdy,
op_a_ack=> op_a_ack_mul,
op_a_status=> op_a_status,
op_a=> op_a,
op_b_rdy=> op_b_rdy,
op_b_ack=> op_b_ack_mul,
op_b_status=> op_b_status,
op_b=> op_b,
res_tag=> cres_tag_mul,
res_status=> cres_status_mul,
res=> cres_mul,
res_rdy=> cres_rdy_mul,
res_ack=> cres_ack_mul);
divsqrt: FPU_divsqrt generic map(NBITS_TAG) port map(
clk=> clk,
rst=> rst,
mode=> mode_divsqrt,
strt_tag=> strt_tag,
strt_req=> strt_req_divsqrt,
strt_ack=> strt_ack_divsqrt,
op_a_rdy=> op_a_rdy,
op_a_ack=> op_a_ack_divsqrt,
op_a_status=> op_a_status,
op_a=> op_a,
op_b_rdy=> op_b_rdy,
op_b_ack=> op_b_ack_divsqrt,
op_b_status=> op_b_status,
op_b=> op_b,
res_tag=> cres_tag_divsqrt,
res_status=> cres_status_divsqrt,
res=> cres_divsqrt,
res_rdy=> cres_rdy_divsqrt,
res_ack=> cres_ack_divsqrt);
------------------------------------------------------
-- Stage#1 to Stage#2
------------------------------------------------------
cres_mux_addsub<=cres_tag_addsub & cres_addsub;
cres_mux_mul<=cres_tag_mul & cres_mul;
cres_mux_divsqrt<=cres_tag_divsqrt & cres_divsqrt;
cres_mux_int2f<=cres_tag_int2f & cres_int2f;
muxreg: FPU_muxreg generic map(VOLATILE=>1,
REG_BEFORE_MUX=>FPU_MUXREG_REG_BEFORE_MUX,
NBITS=>op_mux_norm'length) port map(
clk=> clk,
rst=> rst,
a_strt_req=> cres_rdy_addsub,
a_strt_ack=> cres_ack_addsub,
a_status=> cres_status_addsub,
a=> cres_mux_addsub,
b_strt_req=> cres_rdy_mul,
b_strt_ack=> cres_ack_mul,
b_status=> cres_status_mul,
b=> cres_mux_mul,
c_strt_req=> cres_rdy_divsqrt,
c_strt_ack=> cres_ack_divsqrt,
c_status=> cres_status_divsqrt,
c=> cres_mux_divsqrt,
d_strt_req=> cres_rdy_int2f,
d_strt_ack=> cres_ack_int2f,
d_status=> cres_status_int2f,
d=> cres_mux_int2f,
o_status=> op_a_status_norm,
o=> op_mux_norm,
o_rdy=> strt_req_norm,
o_ack=> strt_ack_norm);
strt_tag_norm<=op_mux_norm(op_mux_norm'left downto op_mux_norm'left-
(NBITS_TAG-1));
op_a_norm<=op_mux_norm(op_a_norm'range);
------------------------------------------------------
-- Stage#2
------------------------------------------------------
norm: FPU_norm generic map(NBITS_TAG) port map(
clk=> clk,
rst=> rst,
strt_tag=> strt_tag_norm,
strt_req=> strt_req_norm,
strt_ack=> strt_ack_norm,
op_a_status=> op_a_status_norm,
op_a=> op_a_norm,
res_tag=> cres_tag_norm,
res=> cres_norm,
res_rdy=> cres_rdy_norm,
res_ack=> cres_ack_norm);
------------------------------------------------------
-- Stage#2 to/from ports
------------------------------------------------------
sign: FPU_sign generic map(NBITS_TAG) port map(
clk=> clk,
rst=> rst,
mode=> mode_sign,
strt_tag=> strt_tag,
strt_req=> strt_req_sign,
strt_ack=> strt_ack_sign,
op_a_rdy=> op_a_rdy,
op_a_ack=> op_a_ack_sign,
op_a_status => op_a_status,
op_a => op_a,
res_tag=> cres_tag_sign,
res=> cres_sign,
res_rdy=> cres_rdy_sign,
res_ack=> cres_ack_sign);
cres_mux_norm<=cres_tag_norm & cres_norm;
cres_mux_cmp<=cres_tag_cmp & cres_cmp;
cres_mux_sign<=cres_tag_sign & cres_sign;
muxo: FPU_mux generic map(NBITS=>res_mux'length) port map(
a_strt_req=> cres_rdy_norm,
a_strt_ack=> cres_ack_norm,
a=> cres_mux_norm,--cres_norm,
b_strt_req=> cres_rdy_cmp,
b_strt_ack=> cres_ack_cmp,
b=> cres_mux_cmp,--cres_cmp,
c_strt_req=> cres_rdy_sign,
c_strt_ack=> cres_ack_sign,
c=> cres_mux_sign,--cres_sign,
o=> res_mux,--res,
o_rdy=> res_rdy,
o_ack=> res_ack);
res_tag<=res_mux(res_mux'left downto res_mux'left-(NBITS_TAG-1));
res<=res_mux(res'range);
end architecture;
4 Bibliografia
http://www.h-schmidt.net/FloatApplet/IEEE754.html
http://babbage.cs.qc.edu/IEEE-754/
http://www.eetimes.com/design/programmable-logic/4014815/All-
about-FPGAs
http://www.worldlingo.com/ma/enwiki/es/Application-
specific_integrated_circuit
http://www.tkt.cs.tut.fi/tools/public/tutorials/mentor/modelsim/g
etting_started/gsms.html
http://atc2.aut.uah.es/~rico/docencia/asignaturas/informatica/lab
_org_comp/archivos/Documentacion/ModelSim/Tutorial%20ModelSIM.pdf
http://en.wikipedia.org/wiki/IEEE_754-2008
http://steve.hollasch.net/cgindex/coding/ieeefloat.html
http://www.xilinx.com/ise/embedded/edk6_2docs/platform_studio_ug.
http://en.wikipedia.org/wiki/MicroBlaze
http://opencores.org
Recommended