VHDL - Adressvergleich liefert falsches Ergebnis

Ich entwickle einen TS-CAN1-Emulator auf Atmels ATF1508AS. Ein Teil einer Anwendung ist ein wie folgt implementierter Adressdecoder (nur interessante Teile bleiben übrig):

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;

Das Problem ist, dass manchmal eine Adresse zwischen 0x100und 0x11Fso dekodiert wird, als wäre sie zwischen 0x150und 0x157. Das Bild unten zeigt die Timing-Analyse von Bussen. Die erste Zeile ist eine Adresse auf dem ISA-Bus (dies ist die Adresse, die ich dekodieren muss). Die vierte Zeile ( IOW) ist ein Signal, dessen Übergang von hoch nach niedrig eine Zustandsänderung auslösen kann, wenn die Adresse übereinstimmt. DEBUGSignal ist hoch, wenn handle_tscan1der Zustand erreicht wird.

Zustandsanalyse

Wie Sie sehen können, löst die Adresse von 0x114und IOW = 0den Übergang korrekt aus. Aber falscher Übergang ausgewählt. Da sollte 0x100 < 0x114 < 0x11fder Übergang zu hold_address_and_aleausgewählt werden, ist aber handle_tscan1stattdessen ausgewählt.

Ich bin ziemlich neu in der PLD-Welt, daher kenne ich noch nicht alle Fallstricke. Vielleicht mache ich etwas falsch (Bus in Ganzzahl konvertieren, Ganzzahl mit Konstanten vergleichen?). Bitte beraten.

Wenn mehr Code benötigt wird, sagen Sie es mir bitte, ich werde es posten.

Registrieren Sie Ihre Eingaben? Haben Sie Ihre Eingabepfade eingeschränkt? Wenn dies nicht der Fall ist, kann dies zu einer langen kombinatorischen Einschwingzeit führen, und zwischenzeitliche Busübergänge können den falschen Zustand verursachen.
@FRob: Registrierst du deine Eingaben? Wenn Sie meinen, habe ich DFFs verwendet, um den Adresswert zu speichern, dann nein. Ich verlasse mich darauf, dass der ISA-Busmaster IOWbei konstanter Adresse strobe. Haben Sie Ihre Eingabepfade eingeschränkt? Es tut mir leid, aber ich weiß nicht, was Sie damit meinen. Habe ich Klimmzüge/Pulldowns verwendet? Unglücklicherweise nicht. Ich habe jedoch ein Oszilloskop verwendet, um die Form und die Anstiegszeit des Busses zu überprüfen, und es sah gut aus.
Sie sollten versuchen, die Signale mit einem internen Takt abzutasten. Stellen Sie zweitens sicher, dass Ihre Logik eingeschränkt ist, dh der Eingabe-zu-Ausgabe-Pfad hat eine maximale Latenz, die mit Ihrem System übereinstimmt. Außerdem ändert sich Ihr Adressbus unangenehm nahe an der steigenden Flanke von clk. Stellen Sie sicher, dass alle Eingaben ungefähr die gleiche Zeit benötigen, um Ihre Logik zu erreichen, wenn Sie nicht abtasten.

Antworten (1)

Ich habe die Adresse in der Sensitivitätsliste übersehen, sie hätte so funktionieren sollen, wie sie ist. Ich dachte, ich würde mir Ihren erweiterten Code ansehen und bemerkte es.

Ist isaddr (9) in Bezug auf Dinge, die die Adresserkennung stoppen können, niedrig (ist es in der Wellenformanzeige Ihres Logikanalysators enthalten, die ISA_ADDR anzeigt?)? Wenn es hoch ist, würden Sie das Symptom erwarten, denke ich. Nach dem Vorbild eines noch nicht verbundenen Netzes.

Sie brauchen eigentlich keine Komparatoren für Adressbereiche:

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 kann mit den oberen 5 Bits von isa_addr dekodiert werden:

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);

Dabei können Sie sja_address = '1' als Bedingungsausdruck verwenden.

Die Erkennung von TSCAN1_ADDRESS erfordert zwei zusätzliche Bits:

"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);

Dabei können Sie tscan1_address = '1' als Bedingungsausdruck verwenden.

Sie würden erwarten, dass sich ein gutes Synthese-Tool nicht darum kümmert und Ihnen das Äquivalent in Gates liefert.

Dieses Problem sieht so aus, als ob es eine Überprüfung der Syntheseberichte verdient, dann vielleicht eine Simulation.

Sie haben use-Klauseln in Ihrer Kontextklausel für die Pakete std_logic_arith und numeric_std beide. Die Verwendung von numeric_std für die Konvertierung in Integer würde wie folgt aussehen:

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

Sie verwenden außer conv_integer nichts anderes, was von std_logic_arith beobachtbar ist.

Ich habe mehr Code gepostet, wie er jetzt ist, ohne Ihre Vorschläge. Ich werde sie am Montag ausprobieren, wenn ich auf der Arbeit bin. Der Kürze halber habe ich nur Zuweisungen an gelassen next_state, damit die Flusskontrolle leicht nachverfolgt werden kann. Ich weiß nicht, ob es relevant ist, aber in jedem Flusssteuerungszweig weise ich jedes Signal zu, also gibt es keine Latches im Design. Ich habe keine Testbench bereitgestellt, weil ich keine schreiben konnte, die dieses Verhalten nachahmen würde. Die Zeitanalyse stammt von einem realen Gerät.
Sie sollten in der Lage sein, etwas Einfaches zu schreiben, um dies zu testen, Sie setzen auf den Zustand wait_for_isa zurück. Im schlimmsten Fall müssen Sie den Reset-Wert für mode_control(6) auf '1' umdrehen. Die Prüfstandsuhr muss nicht gedehnt werden, IOR '1', ISA_ADDR, IOW '0'.
Das Problem bei einem einfachen Test ist, dass er besteht. Wie ich in Frage sagte: Es passiert nur manchmal. Nicht sicher wann. Am Montag werde ich versuchen, die auf dem Logikanalysator registrierte Welle irgendwie in den Simulator zu importieren. Außerdem werde ich Ihren logischen Ausdruck versuchen, um eine Adresse zu erkennen.
Verbunden damit, dass die Uhr gedehnt wird?
Ich habe versucht, mit Eingaben (isa_addr, isa_data, iow, sja, clk) zu simulieren, die vom Logikanalysator gesammelt wurden. Das Ergebnis des Tests war positiv, was bedeutet, dass alles korrekt funktioniert hat.