-----------------------------------------------------------------------------

-- Intel 8237A DMA controller
-- Roman Lysecky
--
-- 07/16/1999
-- Version 1.1(Small)
-- Notes: The port names follow the 8237A naming except for EOP.
--        Due to synthesis problems, EOP has been named eopp.
--        This design only provides a small subset of the 8237A.

-----------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;

-----------------------------------------------------------------------------

entity I8237A is
    port(clk   : in    STD_LOGIC;
         cs    : in    STD_LOGIC;
         reset : in    STD_LOGIC;
         ready : in    STD_LOGIC;
         hlda  : in    STD_LOGIC;
         dreq  : in    UNSIGNED(3 downto 0);
         db    : inout UNSIGNED(7 downto 0);
         ior   : inout STD_LOGIC;
         iow   : inout STD_LOGIC;
         eopp  : inout STD_LOGIC;
         a3_0  : inout UNSIGNED(3 downto 0);
         a7_4  : out   UNSIGNED(3 downto 0);
         hrq   : out   STD_LOGIC;
         dack  : out   UNSIGNED(3 downto 0);
         aen   : out   STD_LOGIC;
         adstb : out   STD_LOGIC;
         memr  : out   STD_LOGIC;
         memw  : out   STD_LOGIC);
end I8237A;

-----------------------------------------------------------------------------

architecture BHV_I8237A of I8237A is

    --
    -- type declarations
    --
    subtype INT2       is INTEGER range 0 to 3;
    subtype REG_16     is UNSIGNED(15 downto 0);
    subtype REG_8      is UNSIGNED(7 downto 0);
    subtype REG_6      is UNSIGNED(5 downto 0);
    subtype REG_4      is UNSIGNED(3 downto 0);
    type    STATE_TYPE is (IDLE, S0, SS1, SSB2, SS2, SS3, SS4);

    --
    -- constant declarations
    --
    constant Z_8   : UNSIGNED(7 downto 0)  := "ZZZZZZZZ";
    constant Z_4   : UNSIGNED(3 downto 0)  := "ZZZZ";
    constant C0_16 : UNSIGNED(15 downto 0) := "0000000000000000";    
    constant C0_8  : UNSIGNED(7 downto 0)  := "00000000";    
    constant CFF_8 : UNSIGNED(7 downto 0)  := "11111111";    
    constant C0_6  : UNSIGNED(5 downto 0)  := "000000";    
    constant C0_4  : UNSIGNED(3 downto 0)  := "0000";    
    constant C1_4  : UNSIGNED(3 downto 0)  := "1111";
    constant BT    : UNSIGNED(1 downto 0)  := "10";    

    --
    -- register declarations
    --
    signal curr_addr : REG_16;
    signal curr_word : REG_16;
    signal base_addr : REG_16;
    signal base_word : REG_16;
    signal command   : REG_8;
    signal mode      : REG_6;
    signal request   : REG_4;
    signal mask      : REG_4;
    
    --
    -- signal declarations
    --
    signal drequest     : UNSIGNED(3 downto 0);
    signal ff           : STD_LOGIC;
    signal mast_clr     : STD_LOGIC;
    signal state        : STATE_TYPE;
    signal channel      : INT2;
    signal adstb_needed : STD_LOGIC;
    
begin
    drequest(0) <= ((command(6) xor dreq(0)) and not mask(0)) or request(0);
    drequest(1) <= ((command(6) xor dreq(1)) and not mask(1)) or request(1);
    drequest(2) <= ((command(6) xor dreq(2)) and not mask(2)) or request(2);
    drequest(3) <= ((command(6) xor dreq(3)) and not mask(3)) or request(3);

    process(clk, reset, mast_clr, drequest)
    begin

        if ( reset = '1' ) then

            --
            -- steady state
            --
            state <= IDLE;
            db    <= Z_8;
            ior   <= 'Z';
            iow   <= 'Z';
            eopp  <= '1';
            a3_0  <= Z_4;
            a7_4  <= Z_4;
            hrq   <= '0';
            dack  <= "1111";
            aen   <= '0';
            adstb <= '0';
            memr  <= '0';
            memw  <= '0';

            --
            -- reset device
            --
            curr_addr    <= C0_16;
            curr_word    <= C0_16;
            base_addr    <= C0_16;
            base_word    <= C0_16;
            command      <= C0_8;
            mode         <= C0_6;
            request      <= C0_4;
            mask         <= C0_4;
            ff           <= '0';
            mast_clr     <= '0';
            channel      <= 0;
            adstb_needed <= '0';

        elsif ( mast_clr = '1' ) then

            --
            -- steady state
            --
            state <= IDLE;
            db    <= Z_8;
            ior   <= 'Z';
            iow   <= 'Z';
            eopp  <= 'Z';
            a3_0  <= Z_4;
            a7_4  <= Z_4;
            hrq   <= '0';
            dack  <= C1_4;
            aen   <= '0';
            adstb <= '0';
            memr  <= '0';
            memw  <= '0';

            --
            -- reset device
            --
            command      <= C0_8;
            request      <= C0_4;
            mask         <= C0_4;
            ff           <= '0';
            mast_clr     <= '0';
            channel      <= 0;
            adstb_needed <= '0';

        elsif ( clk'event and clk = '1' ) then

            --
            -- steady state
            --
            state        <= IDLE;
            db           <= Z_8;
            ior          <= 'Z';
            iow          <= 'Z';
            eopp         <= 'Z';
            a3_0         <= Z_4;
            a7_4         <= Z_4;
            hrq          <= '0';
            aen          <= '0';
            adstb        <= '0';
            memr         <= '0';
            memw         <= '0';
            adstb_needed <= '0';

            --
            -- output correct acknowledge
            -- 
            if ( command(7) = '1' ) then
                dack <= C0_4;
            elsif( command(7) = '0' ) then
                dack <= C1_4;
            else
                dack <= C1_4;
            end if;

            case ( state ) is

                when IDLE =>
                    state <= IDLE;
                    if ( hlda = '0' and drequest /= C0_4 ) then
                        state <= S0;
                        hrq <= '1';
                    elsif ( hlda = '0' and cs = '0' ) then

                        if( ior = '1' and iow = '0' ) then
                            case( a3_0 ) is
                            when "1000" =>
                                command <= db;

                            when "1011" =>
                                if ( db(3 downto 2) /= 3 ) then
                                    mode <= db(7 downto 2);
                                end if;

                            when "1101" =>
                                mast_clr <= '1';

                            when "0000" =>
                                if ( ff = '0' ) then
                                    curr_addr(7 downto 0) <= db;
                                    base_addr(7 downto 0) <= db;
                                    ff <= '1';
                                else
                                    curr_addr(15 downto 8) <= db;
                                    base_addr(15 downto 8) <= db;
                                    ff <= '0';
                                end if;

                            when "0001" =>
                                if ( ff = '0' ) then
                                    curr_word(7 downto 0) <= db;
                                    base_word(7 downto 0) <= db;
                                    ff <= '1';
                                else
                                    curr_word(15 downto 8) <= db;
                                    base_word(15 downto 8) <= db;
                                    ff <= '0';
                                end if;

                            when others =>
                                null;
                            end case;
                        elsif( ior = '0' and iow = '1' ) then
                            case( a3_0 ) is

                            when "0000" =>
                                if ( ff = '0' ) then
                                    db <= curr_addr(7 downto 0);
                                    ff <= '1';
                                else
                                    db <= curr_addr(15 downto 8);
                                    ff <= '0';
                                end if;
                                
                            when "0001" =>
                                if ( ff = '0' ) then
                                    db <= curr_word(7 downto 0);
                                    ff <= '1';
                                else
                                    db <= curr_word(15 downto 8);
                                    ff <= '0';
                                end if;

                            when others =>
                                null;
                            end case;
                        end if;
                    end if;

                when S0 =>
                    state <= S0;
                    hrq <= '1';
                    if ( drequest(channel) = '0' ) then
                        state <= IDLE;
                    elsif ( hlda  = '1' ) then
                        state <= SS1;
                    end if;

                    if ( hlda = '0' and cs = '0' ) then
                        state <= S0;
                        if( ior = '1' and iow = '0' ) then 
                            case( a3_0 ) is
                            when "1000" =>
                                command <= db;

                            when "1011" =>
                                if ( db(3 downto 2) /= 3 ) then
                                    mode <= db(7 downto 2);
                                end if;

                            when "1101" =>
                                mast_clr <= '1';

                            when others =>
                                null;
                            end case;
                        end if;
                    end if;

                when SS1 =>
                    state <= SS2;
                    hrq <= '1';
                    if ( drequest(channel) = '0' ) then
                        state <= IDLE;
                    else
                        aen <= '1';
                    end if;
                    
                when SS2 =>
                    state <= SS3;
                    hrq <= '1';
                    if ( drequest(channel) = '0' ) then
                        state <= IDLE;
                    else
                        aen <= '1';
                        adstb <= '1';
                        db <= curr_addr(15 downto 8);
                        a7_4 <= curr_addr(7 downto 4);
                        a3_0 <= curr_addr(3 downto 0);
                        dack(channel) <= command(7);
                        case ( mode(1 downto 0) ) is
                            when "00" =>
                                null;
                            when "01" =>
                                ior  <= '1';
                                memw <= '1';
                            when "10" =>
                                iow  <= '1';
                                memr <= '1';
                            when others =>
                                null;
                        end case;
                    end if;

                when SSB2 =>
                    state <= SS3;
                    hrq <= '1';
                    aen <= '1';
                    if( adstb_needed = '1' ) then
                        adstb <= '1';
                        db <= curr_addr(15 downto 8);
                    end if;
                    a7_4 <= curr_addr(7 downto 4);
                    a3_0 <= curr_addr(3 downto 0);
                    dack(channel) <= command(7);
                    case ( mode(1 downto 0) ) is
                        when "00" =>
                            null;
                        when "01" =>
                            ior  <= '1';
                            memw <= '1';
                        when "10" =>
                            iow  <= '1';
                            memr <= '1';
                        when others =>
                            null;
                    end case;

                when SS3 =>
                    state <= SS4;
                    hrq <= '1';
                    aen <= '1';
                    a7_4 <= curr_addr(7 downto 4);
                    a3_0 <= curr_addr(3 downto 0);
                    dack(channel) <= command(7);
                    case ( mode(1 downto 0) ) is
                        when "00" =>
                            null;
                        when "01" =>
                            ior  <= '0';
                            memw <= '0';
                        when "10" =>
                            iow  <= '0';
                            memr <= '0';
                        when others =>
                            null;
                    end case;

                when SS4 =>
                    state <= IDLE;
                    hrq <= '1';
                    aen <= '1';
                    a7_4 <= curr_addr(7 downto 4);
                    a3_0 <= curr_addr(3 downto 0);
                    dack(channel) <= command(7);
                    case ( mode(1 downto 0) ) is
                        when "00" =>
                            null;
                        when "01" =>
                            ior  <= '0';
                            memw <= '0';
                        when "10" =>
                            iow  <= '0';
                            memr <= '0';
                        when others =>
                            null;
                    end case;
                    if( curr_word = C0_16 ) then
                        if( mode(2) = '1' ) then
                            curr_word <= base_word;
                            curr_addr <= base_addr;
                        elsif( mode(2) = '0' ) then
                            curr_word <= curr_word - 1;
                            if( mode(3) = '0' ) then
                                curr_addr <= curr_addr + 1;
                            elsif( mode(3) = '1' ) then
                                curr_addr <= curr_addr - 1;
                            end if;
                        end if;
                    else
                        curr_word <= curr_word - 1;
                        if( mode(3) = '0' ) then
                            if( curr_addr(7 downto 0) = CFF_8) then
                                adstb_needed <= '1';
                            end if;
                            curr_addr <= curr_addr + 1;
                        elsif( mode(3) = '1' ) then
                            if( curr_addr(7 downto 0) = C0_8) then
                                adstb_needed <= '1';
                            end if;
                            curr_addr <= curr_addr - 1;
                        end if;

                        if( mode(5 downto 4) = BT ) then
                            state <= SSB2;
                        else
                            state <= IDLE;
                        end if;
                    end if;

                when others =>
                    null;
            end case;
        end if;
    end process;
end BHV_I8237A;

-----------------------------------------------------------------------------

<div align="center"><br /><script type="text/javascript"><!--
google_ad_client = "pub-7293844627074885";
//468x60, Created at 07. 11. 25
google_ad_slot = "8619794253";
google_ad_width = 468;
google_ad_height = 60;
//--></script>
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script><br />&nbsp;</div>