7.7. Trabajo de prácticas: Decodificador Morse

7.7.1. Objetivos

El objetivo general de esta práctica es la realización de un decodificador de lenguaje Morse.

El trabajo se realizará en un grupo formado por dos estudiantes.

7.7.2. Introducción

El código Morse es un sistema de representación de letras y números mediante señales, o pulsos, emitidos de forma intermitente. Cada carácter se representa en este código mediante una combinación de pulsos cortos (puntos) y pulsos largos (rayas). A continuación se muestra la representación de cada carácter según el código Morse internacional:

../../_images/morsecode-2.jpg

Figura 7.29 Código Morse para las letras y números

Según las especificaciones de este código, las normas para crear un texto correcto es la siguiente:

  1. la duración de una raya debe ser 3 veces la de un punto.

  2. El tiempo entre pulsos dentro de un mismo carácter es de una duración equivalente a un punto.

  3. La separación entre caracteres equivale a una raya.

  4. El espacio entre palabras es de 3 rayas.

Si tomamos como referencia una duración de 0,5 segundos para el punto, las duraciones de los símbolos del código Morse serían las que se muestran en la Tabla 7.4.

Tabla 7.4 Codificación de pulsos en Morse

Símbolo

Valor de entrada

Duración

Punto

1

0.5 s

Raya

1

1.5 s

Espacio in-char

0

0.5 s

Espacio de letra

0

1.5 s

Espacio de palabra

0

4.5 s

La propuesta de este trabajo consiste en diseñar e impementar un decodificador de código Morse reducido. El diseño tendrá una única entrada para los pulsos en código Morse, que asignaremos a uno de los pulsadores de la tarjeta DE10-Lite. La salida del decodificador consistirá en la representación de los caracteres para los displays de 7 segmentos de la tarjeta DE10-Lite, junto con una señal de validación. Los caracteres validados se almacenarán en un buffer, cuyo contenido se mostrará por los displays de 7 segmentos de la tarjeta DE10-Lite. La Figura 7.30 muestra un esquema general del sistema a implementar.

../../_images/Morse_decoder_overview.png

Figura 7.30 Diagrama de bloques del decodificador Morse

Del diagrama de bloques mostrado en Figura 7.30 los estudiantes deben realizar las dos máquinas de estado correspondientes al Pulse decoder ( Sección 7.7.4.3) y al Char decoder ( Sección 7.7.4.4).

7.7.3. Requisitos del diseño

Debe tenerse en cuenta que no todos los caracteres se pueden representar en un display de 7 segmentos. Además, implementar la decodificación de todos los posibles caracteres es un trabajo tedioso y de escaso interés. Por ello, se propone implementar tan sólo la decodificación de un subconjunto de caracteres del código Morse, atendiendo a los siguientes requisitos:

  • El diseño permitirá decodificar un mínimo de 10 caracteres diferentes del código Morse.

  • Los caracteres escogidos para decodificar deberán poder mostrarse en un display de 7 segmentos.

  • Sólo se decodificarán letras (A-Z), no números (0-9).

  • El diseño debe decodificar la letra E obligatoriamente.

  • Se deben decodificar al menos 2 letras a elegir entre: A, I y N.

  • Se deben decodificar al menos 3 letras a elegir entre: D, G, O, R, S y U.

  • Se deben decodificar al menos 4 letras a elegir entre: B, C, F, H, J, L, P, Q, Y y Z.

  • Para todos aquellos códigos que no hayan sido seleccionado en el diseño (caracteres no decodificables y códigos no válidos) se generará un código de error común, que se podrá mostrar en un display de 7 segmentos.

7.7.4. Descripción de los componentes del diseño

En las siguientes secciones se detalla el funcionamiento de los distintos bloques mostrados en la Figura 7.30.

7.7.4.1. Detector de flancos (flank detector)

Este bloque se proporciona ya hecho a los estudiantes. El código VHDL de este componente se muestra en el Listado 7.22. El propósito de este bloque consiste en detectar los flancos, o variaciones, en la señal de entrada pulse_in, tanto de “0” a “1” como de “1” a “0”. Para ello, el bloque implementa un registro de desplazamiento de 2 bits, alimentado con la señal pulse_in y que se actualiza en cada ciclo de reloj. Se detecta un flanco cada vez que el valor de los 2 biestables del registro de desplazamiento tienen un valor distinto, tal y como se muestra en la Figura 7.31. La salida del bloque, flank_out se alimenta tanto al contador de ticks como al decodificador de pulsos.

../../_images/Flank_detector.png

Figura 7.31 Esquema del circuito detector de flancos

7.7.4.2. Contador de ticks (tick counter)

Este bloque se proporciona ya diseñado a los estudiantes. El código VHDL de este componente se muestra en el Listado 7.23. Este módulo se encarga de llevar la cuenta del número de ticks transcurridos desde el último flanco detectado en la señal de entrada pulse_in. Por «tick» entendemos aquí el intervalo de tiempo equivalente a la duración de un punto del código Morse, que hemos establecido en 0.5 segundos. La duración efectiva de cada tick dependerá de dos factores: por un lado, la frecuencia de reloj con que alimentemos al circuito, y por otro el límite de cuenta establecido para el temporizador. En el diseño que nos ocupa, este límite de cuenta es un parámetro configurable (es decir, un genérico) denominado CYCLES_PER_TICK. La ventaja de esta solución radica en que nos permite reutilizar el mismo diseño para distintas señales de reloj, así como establecer la duración de tick que más convenga en función de cada aplicación. En nuestro caso alimentaremos el circuito con un reloj de 50 MHz, por lo que para obtener ticks de una duración de 0.5 segundos habrá que establecer el valor del genérico CYCLES_PER_TICK a 25000000.

Internamente, el bloque cuenta con un temporizador, un biestable tipo T (que nos permite reconstruir la señal de entrada pulse_in a partir de los flancos detectados) y dos contadores, count0 y count1, que llevan la cuenta del número de ticks consecutivos con la señal pulse_in al valor “0” y “1”, respectivamente. Se emplean 2 contadores en vez de sólo uno con el fin de poder recuperar el valor anterior de cuenta ante pulsos de duración inferior a un tick, a modo de mecanismo de seguridad frente a rebotes en el pulsador de entrada.

El temporizador es un contador ascendente, que cuenta desde 1 al valor CYCLES_PER_TICK y que se actualiza en cada ciclo de reloj. El temporizador se reinicia en dos posibles situaciones: se detecta un flanco en la señal pulse_in (en cuyo caso también cambia la polaridad del biestable T) o se produce un desbordamiento del temporizador. Un tick se completa cada vez que el temporizador desborda, y en consecuencia uno de los contadores incrementa su valor en 1, en función del valor de la señal pulse_in reconstruida. Los contadores no se reinician de forma automática con el flanco de pulse_in, sino cuando se completa el primer tick después de un flanco; y además no desbordan, sino que conservan su valor una vez que llegan al valor máximo de cuenta. La salida del bloque, tick_count_out, muestra el valor de uno de los contadores en función de la polaridad del biestable T. El cronograma de la Figura 7.32 ilustra el funcionamiento de este bloque.

../../_images/Tick_chronogram.png

Figura 7.32 Sincronismo del contador de ticks.

7.7.4.3. Decodificador de pulsos (pulse decoder)

El propósito de este módulo es extraer los símbolos que conforman el código Morse (punto, raya y los distintos tipos de espacios) a partir del valor y la duración de los pulsos de entrada. Debe diseñarse como una Máquina de Estados Finitos, con las siguientes especificaciones:

  • Entradas:

    • clock: reloj del sistema.

    • reset_n: reset, activo por nivel bajo.

    • flank_in: flanco de entrada, proporcionado por el detector de flancos.

    • tick_count_in: número de ticks transcurridos desde el último flanco detectado, proporcionado por el contador de ticks.

  • Salidas:

    • led_dot: indica que el pulso actual corresponde a un punto.

    • led_dash: indica que el pulso actual corresponde a una raya.

    • dot_out: validación de punto.

    • dash_out: validación de raya.

    • lspace_out: validación de espacio de letra.

    • wspace_out: validación de espacio de palabra.

  • Funcionalidad: ver Tabla 7.5

Las señales led_dot y led_dash sirven de indicación al usuario (se mostrarán por los LEDs de la tarjeta DE10-Lite). Sin embargo, estos pulsos no se validarán hasta que cese el pulso correspondiente (dejando de pulsar el botón de la tarjeta DE10-Lite). Por su parte, las señales dot_out, dash_out, lspace_out y wspace_out validan los pulsos introducidos, y se transmiten al intérprete de caracteres para completar la decodificación del código Morse. Estas cuatro salidas deben estar activas un único ciclo de reloj por cada pulso validado, de lo contrario el intérprete de caracteres podría interpretar varios símbolos seguidos a partir de un mismo pulso.

La decodificación de los pulsos de entrada se hará en base a las especificaciones de la Tabla 7.5. Debe tenerse en cuenta que el decodificador de pulsos no recibe directamente el valor de pulse_in, sino sus flancos. Debe asumirse que cuando la máquina de estados está en el estado de reposo/reset, el valor de pulse_in es “0”.

Tabla 7.5 Funcionamiento del decodificador de pulsos

pulse_in

tick_count_in

led_dot

led_dash

Símbolo

Notas

1 (pulsación)

0 (< 0.5 s)

0

0

Sin efecto

1 - 2 (0.5 - 1.5 s)

1

0

Punto

Estos pulsos no se validan inmediatamente, sino después de in espacio in-char

3+ (> 1.5 s)

0

1

Raya

0 (no pulsación)

0 (< 0.5 s)

M

M

Sin efecto

Los leds conservan el valor anterior (“0” en caso de reset)

1 - 2 (0.5 - 1.5 s)

0

0

Espacio in-char

Se valida el pulso anterior (punto o raya) un único ciclo de reloj

3 - 8 (1.5 - 4.5 s)

0

0

Espacio de letra

El pulso se validará un único ciclo de reloj

9+ (> 4.5 s)

0

0

Espacio de palabra

Como puede observarse, los distintos pulsos se validan únicamente cuando pulse_in está a nivel bajo. Debe tenerse en cuenta que un mismo pulso de entrada a 0 puede validar varios símbolos en secuencia (punto o raya, más espacio de letra, más espacio de palabra) dependiendo de la duración del pulso.

7.7.4.4. Intérprete de caracteres (char decoder)

Este módulo toma los símbolos validados por el decodificador de pulsos (punto, raya, espacio de letra y espacio de palabra) y genera los códigos para representar diversos caracteres del código Morse en los displays de 7 segmentos de la tarjeta DE10-Lite. Debe diseñarse como una Máquina de Estados Finitos para el reconocimiento de múltiples patrones, con las siguientes especificaciones:

  • Entradas:

    • clock: reloj del sistema.

    • reset_n: reset, activo por nivel bajo.

    • dot_in: validación de punto.

    • dash_in: validación de raya.

    • lspace_in: validación de espacio de letra.

    • wspace_in: validación de espacio de palabra.

  • Salidas:

    • char_out: representación de un carácter para display de 7 segmentos (incluyendo punto decimal).

    • char_valid: validación del código emitido por la salida char_out.

  • Funcionalidad:

    • La secuencia de puntos y rayas recibidos (por las entradas dot_in y dash_in respectivamente) se va acumulando internamente.

    • Cuando se recibe un espacio de letra (input lspace_in) se evalúa la secuencia de símbolos recibida:

      • Si la secuencia de puntos y rayas coincide con la representación en código Morse de las letras elegidas pora el decodificador (ver Sección 7.7.3), se emite la codificación de la letra en formato de código de 7 segmentos por la salida char_out, junto con la validación del mismo (char_valid = “1”), durante un único ciclo de reloj. Luego se reinicia la secuencia interna de símbolos.

      • En caso contrario, se emite el código de error «01111111» (punto decimal) por la salida char_out, junto con la validación del mismo (char_valid = “1”), durante un único ciclo de reloj. Luego se reinicia la secuencia interna de símbolos.

    • Cuando se recibe un espacio de palabra (input wspace_in), se emite el código «11111111» (espacio en blanco) por la salida char_out, junto con la validación del mismo (*char_valid*=”1”), durante un único ciclo de reloj.

La funcionalidad de este módulo se resume en la Tabla 7.6.

Tabla 7.6 Funcionalidad del interprete de caracteres (char decoder)

dot_in

dash_in

lspace_in

wspace_in

char_valid

Efecto

“0”

“0”

“0”

“0”

“0”

Sin efecto.

“1”

“0”

“0”

“0”

“0”

Se agrega un punto a la secuencia de símbolos interna.

“0”

“1”

“0”

“0”

“0”

Se agrega una raya a la secuencia de símbolos interna.

“0”

“0”

“1”

“0”

“1”

Se emite la representación en display de 7 segmentos para una letra, o bien el código de error «01111111», según la secuencia de símbolos recibida. Se reinicia la secuencia de símbolos interna.

“0”

“0”

“0”

“1”

“1”

Se emite un espacio en blanco (código «11111111»).

7.7.5. Trabajo previo a realizar

En primer lugar, los estudiantes debe concebir y dibujar los grafos de estados de las Máquinas de Estados Finitos correspondientes a los bloques del decodificador de pulsos y del intérprete de caracteres, sobre la bade de las especificaciones de las Secciones 7.7.4.3 y 7.7.4.4. Para el diseño de estas MEFs se deben tener en cuenta todas las posibles situaciones, más allá de los casos descritos en las Tablas 7.5 y 7.6.

7.7.6. Trabajo práctico

7.7.6.1. Describir el decodificador de pulsos en VHDL y verificar

En primer lugar se deberá describir en VHDL (mediante un VPL) la máquina de estados finitos correspondiente al decodificador de pulsos, en base al grafo de estados correspondiente realizado por los estudiantes.

7.7.6.2. Describir el intérprete de caracteres en VHDL y verificar

En segundo lugar se deberá describir en VHDL (mediante un VPL) la MEF correspondiente al intérprete de caracteres, en base al grafo de estados correspondiente realizado por los estudiantes.

7.7.6.3. Describir el decodificador Morse en VHDL

Por último, los estudiantes completarán la descripción VHDL del top del decodificador Morse (disponible en el Listado 7.24) incorporando las dos máquinas de estado diseñadas. El sistema completo incluye los bloques correspondientes al detector de flancos y al contador de ticks, cuyas descripciones en VHDL se proporcionan en los Listados 7.22 y 7.23. La entidad del codificador Morse tiene un parámetro genérico, DOT_DURATION, que permite definir la duración del punto en el código Morse, y que se asigna al genérico CYCLES_PER_TICK del contador de ticks.

7.7.6.4. Implementar y validar el decodificador Morse en la tarjeta DE10-Lite

Para implementar el diseño en la tarjeta DE10-Lite, los alumnos tendrán que embeber su diseño en una entidad de nivel superior, TrabajoPR6.vhd, proporcionado en el Listado 7.25. Esta entidad además incluye un char_buffer que actúa como un registro de desplazamiento para los caracteres validados a la salida del decodificador Morse, y que permite mostrar los caracteres decodificados por los displays de 7 segmentos de la tarjeta DE10-Lite. El código VHDL de este buffer de salida se proporciona en el Listado 7.26.

Una vez programada la tarjeta DE10-Lite con el diseño del decodificador Morse, se podrán introducir los pulsos por uno de los botones (pushbuttons) de la tarjeta. El otro botón se reserva para la activación del reset del circuito. Cada vez que pulsemos el botón de pulse_in, los LEDs de la tarjeta nos indicarán si el pulso actual se corresponde con un punto o una raya del código Morse. Los caracteres validados por el decodificador se irán mostrando por los displays de 7 segmentos, entrando por el lado derecho y desplazándose hacia la izquierda a medida que se vayan generando nuevos caracteres. La conexión de los pines se muestra en la siguiente Figura:

../../_images/Morse_decoder_FPGA_pins.png

Figura 7.33 Pin-out del decodificador Morse.

7.7.7. Entregables

7.7.7.1. Trabajo teórico

Por hacer

ET6.1. Memoria de trabajo.

ET6.2. Grafo de estados del decodificador de pulsos.

ET6.3. Grafo de estados del intérprete de caracteres.

7.7.7.2. Trabajo práctico

Por hacer

ET6.3. laboratorio virtual (VPL) con la descripción VHDL funcional del decodificador de pulsos del apartado Decodificador de pulsos (pulse decoder).

ET6.4. laboratorio virtual (VPL) con la descripción VHDL funcional del intérprete de caracteres del apartado Intérprete de caracteres (char decoder).

ET6.5. Implementación del decodificador Morse en la tarjeta DE10-Lite.

7.7.8. Listados VHDL

Lista 7.22 Código VHDL del detector de flancos(flank_detector.vhd)
-- Ver en el campus Virtual de la Asignatura
Lista 7.23 Código VHDL del contador de ticks (tick_counter).
-- Ver en el campus Virtual de la Asignatura
Lista 7.24 Esqueleto del top del decodificador Morse morse_decoder.vhd.
--Electronica Digital 2º curso EITE/ULPGC
--Para usar únicamente en los Trabajos de asignatura
-- Entidad de más alto nivel del decodificador Morse (a completar por los alumnos)
LIBRARY ieee;
USE ieee.std_logic_1164.all;

ENTITY morse_decoder IS
    GENERIC (DOT_DURATION: positive := 5);
    PORT (
        clock : IN STD_LOGIC;
        reset_n : IN STD_LOGIC;
        pulse_in:  IN STD_LOGIC;                        -- Entrada de Pushbutton
        -- Status LEDs
        led_dot: OUT STD_LOGIC;                         -- Indicación de pulso actual: punto
        led_dash: OUT STD_LOGIC;                        -- Indicación de pulso actual: raya
        -- Output characters
        char_out: OUT STD_LOGIC_VECTOR(7 DOWNTO 0);     -- Representación de letra para display de 7 segmentos
        char_valid: OUT STD_LOGIC                       -- Validación de la salida char_out
    );
END morse_decoder;

ARCHITECTURE beh of morse_decoder IS
    COMPONENT flank_detector IS
        PORT (
            clock : IN STD_LOGIC ;
            reset_n : IN STD_LOGIC;
            input1 : IN STD_LOGIC;
            flank_out : OUT STD_LOGIC
        );
    END COMPONENT;
    
    COMPONENT tick_counter IS
        GENERIC (CYCLES_PER_TICK: positive := 5);
        PORT (
            clock : IN STD_LOGIC;
            reset_n : IN STD_LOGIC;
            flank_in : IN STD_LOGIC;
            tick_count_out : OUT STD_LOGIC_VECTOR(3 DOWNTO 0)
        );
    END COMPONENT;
    
    -- DECLARAR LOS COMPONENTES NECESARIOS
    
    -- DECLARAR LAS SEÑALES NECESARIAS
BEGIN
    flank_inst: COMPONENT flank_detector PORT MAP(
        clock => clock,
        reset_n => reset_n,
        input1 => pulse_in,
        flank_out => flank
    );
    tick_inst: COMPONENT tick_counter GENERIC MAP (DOT_DURATION) PORT MAP(
        clock => clock,
        reset_n => reset_n,
        flank_in => flank,
        tick_count_out => tick_count
    );
    
    -- INSTANCIAR LOS COMPONENTES DECLARADOS
    
END beh;
Lista 7.25 Fichero VHDL TrabajoPR6_decodificadorMorse.vhd.
-- Ver en el campus Virtual de la Asignatura
Lista 7.26 Código VHDL del buffer de salida (char_buffer).
-- Ver en el campus Virtual de la Asignatura