3
\$\begingroup\$

I am developing TS-CAN1 emulator on Atmel's ATF1508AS. One part of an application is an address decoder implemented as follows (only interesting parts are left):

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;

The problem is that sometimes an address between 0x100 and 0x11F is decoded as it would be between 0x150 and 0x157. Picture below presents timing analysis taken from buses. First row is an address on ISA bus (this is address I need to decode). Fourth row (IOW) is a signal thats high to low transition can trigger change of state, if address matches. DEBUG signal is high when handle_tscan1 state is entered.

State analysis

As you can see address of 0x114 and IOW = 0 correctly triggers transition. But wrong transition is selected. Since 0x100 < 0x114 < 0x11f, transition to hold_address_and_ale should be selected, but handle_tscan1 is selected instead.

I am quite new in PLD world, so I don't know all the pitfalls yet. Maybe I am doing something wrong (converting bus to integer, comparing integer with constants?). Please advise.

If more code is needed, please tell me, I will post it.

\$\endgroup\$
  • \$\begingroup\$ Do you register your inputs? Did you constrain your input paths? If not, you might end up with a long combinational settling time and intermediate bus transitions might cause the wrong state. \$\endgroup\$ – FRob Jan 31 '15 at 1:11
  • \$\begingroup\$ @FRob: Do you register your inputs? If you mean, did I use DFF's to latch address value, then no. I bank on the fact that ISA bus master will strobe IOW when address is steady. Did you constrain your input paths? I'm sorry, but I do not know what you mean by that. Did I use pullups/pulldowns? Unfortunately no. However I used oscilloscope to check shape and rising time of the bus and it looked fine. \$\endgroup\$ – aadam Jan 31 '15 at 1:27
  • \$\begingroup\$ You should try sampling the signals with an internal clock. Secondly, make sure your logic is constrained, i.e. input to output path has a maximum latency consistent with your system. Also, your address bus changes uncomfortably close to rising edge of clk. Make sure all inputs take about the same time to reach your logic if you don't sample. \$\endgroup\$ – FRob Jan 31 '15 at 1:32
1
\$\begingroup\$

I missed seeing address in the sensitivity list, it should have worked as is. I thought I'd take a look at your expanded code and noticed.

As far as things that can stop the address recognition, is isaddr(9) driven low (is it included in your logic analyzer waveform display showing ISA_ADDR?)? If it's high you'd expect the symptom I think. Along the lines of a not yet connected net.

You don't actually need comparators for address ranges:

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 can be decoded with the upper 5 bits of 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);

Where you can use sja_address = '1' as a condition expression.

TSCAN1_ADDRESS recognition requires two additional 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);

Where you can use tscan1_address = '1' as a condition expression.

You'd expect a good synthesis tool wouldn't care and would give you the equivalent in gates.

This problem looks like it deserves a review of synthesis reports then perhaps a simulation.

You have use clauses in your context clause for packages std_logic_arith and numeric_std both. Using numeric_std for the conversion to integer would look like:

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

You're not using anything else observable from std_logic_arith other than conv_integer.

\$\endgroup\$
  • \$\begingroup\$ I posted more code as it is now, without your suggestions. I will try them on Monday, when I am at work. For brevity I left only assignments to next_state, so flow control can be easily tracked. I don't know if it is relevant, but in every flow control branch I assign every signal, so there are no latches in design. I did not provide a testbench, because I couldn't write one, that would mimic that behavior. Time analysis is from a real world device. \$\endgroup\$ – aadam Jan 30 '15 at 21:47
  • \$\begingroup\$ You should be able to write something simple to test this, you reset to state wait_for_isa. at worst case you might need to flip the reset value to '1' for mode_control(6). The test bench clock doesn't have to stretch, IOR '1', ISA_ADDR, IOW '0'. \$\endgroup\$ – user8352 Jan 31 '15 at 1:02
  • \$\begingroup\$ The problem with simple test is that it passes. Like I said in question: it only happens sometimes. Not sure when. On Monday I will try somehow import wave registered on logic analyzer to simulator. Also I will try your logic expression to recognize an address. \$\endgroup\$ – aadam Jan 31 '15 at 1:37
  • \$\begingroup\$ Associated with the clock being stretched? \$\endgroup\$ – user8352 Jan 31 '15 at 1:43
  • \$\begingroup\$ I did try to simulate with inputs (isa_addr, isa_data, iow, sja, clk) collected from logic analyzer. Result of the test was positive, meaning that everything worked correctly. \$\endgroup\$ – aadam Feb 2 '15 at 14:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.