VHDL: la comparación de direcciones produce un resultado incorrecto

3

Estoy desarrollando el emulador TS-CAN1 en ATF1508AS de Atmel. Una parte de una aplicación es un decodificador de dirección implementado de la siguiente manera (solo quedan partes interesantes):

library ieee;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
use ieee.numeric_std.all;

entity can_decoder is
port (
      clk: in std_logic;
      reset: in std_logic;
      isa_addr: in std_logic_vector(9 downto 0);
      isa_data: inout std_logic_vector(7 downto 0);
      isa_ior: in std_logic;
      isa_iow: in std_logic;
      isa_irq: out std_logic;
      isa_iochrdy: out std_logic;
      sja_bus: inout std_logic_vector(7 downto 0);
      sja_ior: out std_logic;
      sja_iow: out std_logic;
      sja_ale: out std_logic;
      sja_cs:  out std_logic;
      sja_irq: in std_logic;
      debug: out std_logic);
end can_decoder;

architecture behavioral of can_decoder is
    constant SJA_ADDRESS_LOW: integer := 256;     -- 0x100
    constant SJA_ADDRESS_HIGH: integer := 287;    -- 0x11F
    constant TSCAN1_ADDRESS_LOW: integer := 336;  -- 0x150
    constant TSCAN1_ADDRESS_HIGH: integer := 343; -- 0x157

    type state_type is (wait_for_isa,
                        handle_tscan1,
                        hold_address_and_set_ale,
                        hold_address_and_clear_ale,
                        set_read_data,
                        hold_read_data,
                        set_write_data,
                        hold_write_data);

    signal current_state: state_type := wait_for_isa;
    signal next_state: state_type := wait_for_isa;
    signal address : integer;
    signal next_page: std_logic_vector(1 downto 0) := "00";
    signal page: std_logic_vector(1 downto 0) := "00";
    signal mode_control: std_logic_vector(6 downto 0) := "0000000";
    signal next_mode_control: std_logic_vector(6 downto 0) := "0000000";
    signal sja_enabled: std_logic;
begin
    process (clk)
    begin
        if (rising_edge(clk)) then
            if (reset = '0') then
                current_state <= wait_for_isa;
                page <= "00";
                mode_control <= "0000000";
            else
                current_state <= next_state;
                page <= next_page;
                mode_control <= next_mode_control;
            end if;
        end if;
    end process;

    process (current_state, isa_ior, isa_iow, isa_addr, sja_bus, sja_irq, isa_data, page, mode_control, address, sja_enabled, next_state)
    begin
        isa_irq <= (not sja_irq) and sja_enabled;
        address <= conv_integer('0' & isa_addr);
        sja_enabled <= mode_control(6);

        -- this is how I know wrong state is selected
        if (current_state = handle_tscan1) then
            debug <= '1';
        else
            debug <= '0';
        end if;

        case current_state is
            when wait_for_isa =>
                if (isa_ior = '0' or isa_iow = '0') and ((address >= SJA_ADDRESS_LOW) and (SJA_ADDRESS_HIGH >= address)) and sja_enabled = '1' then
                    -- Address in SJA1000 range
                    next_state <= hold_address_and_set_ale;
                elsif (isa_ior = '0' or isa_iow = '0') and ((address >= TSCAN1_ADDRESS_LOW) and (TSCAN1_ADDRESS_HIGH >= address)) then
                    -- Address is in TSCAN1 range
                    next_state <= handle_tscan1;
                else
                    -- Address outside of interesting range (or no triggering signals)
                    next_state <= wait_for_isa;
                end if;

            when handle_tscan1 =>
                if (isa_ior = '0') then
                    next_state <= handle_tscan1;
                elsif (isa_iow = '0') then
                    -- next_mode_control and next_page are set here
                    next_state <= handle_tscan1;
                else
                    next_state <= wait_for_isa;
                end if;


            when hold_address_and_set_ale =>
                next_state <= hold_address_and_clear_ale;

            when hold_address_and_clear_ale =>
                if (isa_ior = '0') then
                    next_state <= set_read_data;
                elsif (isa_iow = '0') then
                    next_state <= set_write_data;
                else
                    next_state <= wait_for_isa;
                end if;

            when set_read_data =>
                next_state <= hold_read_data;


            when hold_read_data =>
                if (isa_ior = '0' and next_state = hold_read_data) then
                    next_state <= hold_read_data;
                else
                    next_state <= wait_for_isa;
                end if;

            when set_write_data =>
                next_state <= hold_write_data;

            when hold_write_data =>
                if (isa_iow = '0' and next_state = hold_write_data) then
                    next_state <= hold_write_data;
                else
                    next_state <= wait_for_isa;
                end if;
        end case;
    end process;
end behavioral;

El problema es que a veces se decodifica una dirección entre 0x100 y 0x11F , ya que estaría entre 0x150 y 0x157 . La imagen de abajo presenta el análisis de tiempo tomado de los autobuses. La primera fila es una dirección en el bus ISA (esta es la dirección que necesito para decodificar). La cuarta fila ( IOW ) es una señal que, de alta a baja, la transición puede provocar un cambio de estado, si la dirección coincide. La señal DEBUG es alta cuando se ingresa el estado handle_tscan1 .

Como puede ver, la dirección de 0x114 y IOW = 0 activa la transición. Pero se selecciona la transición incorrecta. Como 0x100 < 0x114 < 0x11f , se debe seleccionar la transición a hold_address_and_ale , pero en su lugar se selecciona handle_tscan1 .

Soy bastante nuevo en el mundo de PLD, así que todavía no conozco todos los escollos. Tal vez estoy haciendo algo mal (¿convertir bus a entero, comparando enteros con constantes?). Por favor avise.

Si se necesita más código, por favor, dígame, lo publicaré.

    
pregunta aadam

1 respuesta

1

Me perdí de ver la dirección en la lista de sensibilidad, debería haber funcionado como está. Pensé que echaría un vistazo a tu código ampliado y lo noté.

En cuanto a las cosas que pueden detener el reconocimiento de la dirección, ¿isaddr (9) tiene un nivel bajo (está incluido en la pantalla de forma de onda del analizador lógico que muestra ISA_ADDR?)? Si es alto esperarías el síntoma, creo. En la línea de una red aún no conectada.

Realmente no necesitas comparadores para los rangos de direcciones:

constant SJA_ADDRESS_LOW: integer := 256;     -- 0x100
constant SJA_ADDRESS_HIGH: integer := 287;    -- 0x11F
constant TSCAN1_ADDRESS_LOW: integer := 336;  -- 0x150
constant TSCAN1_ADDRESS_HIGH: integer := 343; -- 0x157

"01_0000_0000"  -- 0x100
"01_0001_1111"  -- 0x11F

"01_000"        -- 5 bit value to recognize SJA_ADDRESS range.

SJA_ADDRESS se puede decodificar con los 5 bits superiores de isa_addr:

signal sja_address:  std_logic;

sja_address <= not isa_addr(9) and     isa_addr(8) and 
               not isa_addr(7) and not isa_addr(6) and
               not isa_addr(5);

Donde puede usar sja_address = '1' como expresión de condición.

El reconocimiento

TSCAN1_ADDRESS requiere dos bits adicionales:

"01_0101_0000"  -- 0x150
"01_0101_0111"  -- 0x157
"01_0101_0"     -- 7 bit value to recognize TSCAN1_ADDRESS range

signal tscan1_address:  std_logic;

tscan1_address <= not isa_addr(9) and isa_addr(8) and 
                  not isa_addr(7) and isa_addr(6) and
                  not isa_addr(5) and isa_addr(4) and
                  not isa_addr(3);

Donde puedes usar tscan1_address = '1' como expresión de condición.

Usted esperaría que una buena herramienta de síntesis no le importaría y le daría el equivalente en puertas.

Este problema parece que merece una revisión de los informes de síntesis y quizás una simulación.

Tiene cláusulas de uso en su cláusula de contexto para los paquetes std_logic_arith y numeric_std ambos. El uso de numeric_std para la conversión a entero se vería así:

    address <= to_integer(unsigned(isa_addr)); -- conv_integer('0' & isa_addr);

No estás usando otra cosa observable de std_logic_arith que no sea conv_integer.

    
respondido por el user8352

Lea otras preguntas en las etiquetas