Digilent EPP Performance

From Hamsterworks Wiki!

Jump to: navigation, search

This FPGA project was completed November 2012.

Anthony Hernandez wrote::

I have a question concerning the Spartan-3E Xilinx transfer rate that you said was possible (in your Introducing the Spartan 3E FPGA and VHDL).

The ebook says that 11MB/s should be an attainable transfer rate from the xilinx board, but it is taking me 30 seconds to read 5MB from the board when I try it.

Do you have any idea as to why I might be experiencing such low transfer speeds? I used the DeppDemo in the Linux adept SDK.

This was odd, so I performed some testing. Oh gosh! How wrong could I be!

My original board was the Nexys2, and that is where I originally played with the EPP code (reading and writing the flash chip. I just verified that it worked the same on the Basys2. I didn't known that there is a 30x performance difference - the Basys2 doesn't have any Flash to program or read back, so I never tried it!

I ran the same code (except changing the board name) to the same project (except changing the UCF file). It transfers 10,000 byte blocks for 10 seconds.

Contents

Performance on Basys2 vs Nexys2

The application below send bytes out to EPP port 10, the LEDs, on both boards.

Basys 2

This is the Basys2, 170,000 bytes per second:

C:\Users\Hamster\Documents\Visual Studio 2010\Projects\throughput\Debug>throughput.exe
Opening
Enabling
Outputting
Stream to register completed 1700000 bytes!    

Nexys2

This is the Nexys2, 4,984,000 bytes per second

C:\Users\Hamster\Documents\Visual Studio 2010\Projects\throughput\Debug>throughput.exe
Opening
Enabling
Outputting
Stream to register completed 49840000 bytes!

Improvements

When I added a DCM to run the design at 160Mhz, slightly reorganized the code and then increased the buffer to 64kB I found that I ran out of CPU on my low-end AMD laptop. Moving to an old Core i3 M520 (2.4GHz) I managed to transfer 1024MB in 100 seconds - just over 10MB/sec, or fifty times what the Basys2 is capable of.

Performance was much the same with either GetReg() or PutReg().

App Source Code

Just change the board name passed to DmgrOpen() for the different boards

#include <StdAfx.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "dpcdecl.h"
#include "depp.h"
#include "dmgr.h"

static HIF hif = hifInvalid;

static void DoPutRegRepeat() {
    BYTE    data[10000];
    int i = 0;
    int start = time(NULL);
    memset(data,0xff,sizeof(data));
    while(time(NULL) - start < 10)
    {
      if(!DeppPutRegRepeat(hif, 10, data, sizeof(data), fFalse)){
        printf("DeppPutRegRepeat failed.\n");
        exit(3);
      }
      i++;
    }
    printf("Stream to register completed %i bytes!\n",(int)(sizeof(data)*i));
}

int main(int cszArg, char * rgszArg[]) {
    fprintf(stderr,"Opening\n");
    if(!DmgrOpen(&hif, "Nexys2")) {  // Change to Basys2 for the other board.
        printf("DmgrOpen failed (check the device name you provided)\n");
        return 0;
    }

    fprintf(stderr,"Enabling\n");
    if(!DeppEnable(hif)) {
        printf("DeppEnable failed\n");
        return 0;
    }

    fprintf(stderr,"Outputting\n");
    DoPutRegRepeat();

    if( hif != hifInvalid ) {
        DeppDisable(hif);
        DmgrClose(hif);
    }
    return 0;
}

The design for the FPGA

This is nearly all from the Adept SDK example - I've put it here for completeness.

----------------------------------------------------------------------------
--    DPIMREF.VHD -- Digilent Parallel Interface Module Reference Design
----------------------------------------------------------------------------
-- Author:  Gene Apperson
--          Copyright 2004 Digilent, Inc.
----------------------------------------------------------------------------
-- IMPORTANT NOTE ABOUT BUILDING THIS LOGIC IN ISE
--
-- Before building the Dpimref logic in ISE:
--     1.      In Project Navigator, right-click on "Synthesize-XST"
--        (in the Process View Tab) and select "Properties"
--    2.    Click the "HDL Options" tab
--    3.     Set the "FSM Encoding Algorithm" to "None"
----------------------------------------------------------------------------
--
----------------------------------------------------------------------------
--    This module contains an example implementation of Digilent Parallel
--    Interface Module logic. This interface is used in conjunction with the
--    DPCUTIL DLL and a Digilent Communications Module (USB, EtherNet, Serial)
--    to exchange data with an application running on a host PC and the logic
--    implemented in a gate array.
--
--    See the Digilent document, Digilent Parallel Interface Model Reference
--    Manual (doc # 560-000) for a description of the interface.
--
--    This design uses a state machine implementation to respond to transfer
--    cycles. It implements an address register, 8 internal data registers
--    that merely hold a value written, and interface registers to communicate
--    with a Digilent DIO4 board. There is an LED output register whose value
--    drives the 8 discrete leds on the DIO4. There are two input registers.
--    One reads the switches on the DIO4 and the other reads the buttons.
--
--    Interface signals used in top level entity port:
--        mclk        - master clock, generally 50Mhz osc on system board
--        pdb            - port data bus
--        astb        - address strobe
--        dstb        - data strobe
--        pwr            - data direction (described in reference manual as WRITE)
--        pwait        - transfer synchronization (described in reference manual
--                        as WAIT)
--        rgLed        - LED outputs to the DIO4
--        rgSwt        - switch inputs from the DIO4
--        ldb            - led gate signal for the DIO4
--        rgBtn        - button inputs from the DIO4
--        btn            - button on system board (D2SB or D2FT)
--        led            - led on the system board
--       
----------------------------------------------------------------------------
-- Revision History:
--  06/09/2004(GeneA): created
--    08/10/2004(GeneA): initial public release
--    04/25/2006(JoshP): comment addition 
----------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity dpimref is
    Port (
        mclk     : in std_logic;
        pdb        : inout std_logic_vector(7 downto 0);
        astb     : in std_logic;
        dstb     : in std_logic;
        pwr     : in std_logic;
        pwait     : out std_logic;
        rgLed    : out std_logic_vector(7 downto 0);
        rgSwt    : in std_logic_vector(7 downto 0);
        rgBtn    : in std_logic_vector(4 downto 0)
    );
end dpimref;

architecture Behavioral of dpimref is

------------------------------------------------------------------------
-- Component Declarations
------------------------------------------------------------------------

------------------------------------------------------------------------
-- Local Type Declarations
------------------------------------------------------------------------

------------------------------------------------------------------------
--  Constant Declarations
------------------------------------------------------------------------

    -- The following constants define state codes for the EPP port interface
    -- state machine. The high order bits of the state number give a unique
    -- state identifier. The low order bits are the state machine outputs for
    -- that state. This type of state machine implementation uses no
    -- combination logic to generate outputs which should produce glitch
    -- free outputs.
    constant    stEppReady    : std_logic_vector(7 downto 0) := "0000" & "0000";
    constant    stEppAwrA    : std_logic_vector(7 downto 0) := "0001" & "0100";
    constant    stEppAwrB    : std_logic_vector(7 downto 0) := "0010" & "0001";
    constant    stEppArdA    : std_logic_vector(7 downto 0) := "0011" & "0010";
    constant    stEppArdB    : std_logic_vector(7 downto 0) := "0100" & "0011";
    constant    stEppDwrA    : std_logic_vector(7 downto 0) := "0101" & "1000";
    constant    stEppDwrB    : std_logic_vector(7 downto 0) := "0110" & "0001";
    constant    stEppDrdA    : std_logic_vector(7 downto 0) := "0111" & "0010";
    constant    stEppDrdB    : std_logic_vector(7 downto 0) := "1000" & "0011";

------------------------------------------------------------------------
-- Signal Declarations
------------------------------------------------------------------------

    -- State machine current state register
    signal    stEppCur    : std_logic_vector(7 downto 0) := stEppReady;

    signal    stEppNext    : std_logic_vector(7 downto 0);

    signal    clkMain        : std_logic;

    -- Internal control signales
    signal    ctlEppWait    : std_logic;
    signal    ctlEppAstb    : std_logic;
    signal    ctlEppDstb    : std_logic;
    signal    ctlEppDir    : std_logic;
    signal    ctlEppWr    : std_logic;
    signal    ctlEppAwr    : std_logic;
    signal    ctlEppDwr    : std_logic;
    signal    busEppOut    : std_logic_vector(7 downto 0);
    signal    busEppIn    : std_logic_vector(7 downto 0);
    signal    busEppData    : std_logic_vector(7 downto 0);

    -- Registers
    signal    regEppAdr    : std_logic_vector(3 downto 0);
    signal    regData0    : std_logic_vector(7 downto 0);
    signal    regData1    : std_logic_vector(7 downto 0);
    signal  regData2    : std_logic_vector(7 downto 0);
    signal  regData3    : std_logic_vector(7 downto 0);
    signal  regData4    : std_logic_vector(7 downto 0);
    signal    regData5    : std_logic_vector(7 downto 0);
    signal    regData6    : std_logic_vector(7 downto 0);
    signal    regData7    : std_logic_vector(7 downto 0);
    signal    regLed        : std_logic_vector(7 downto 0);

    signal    cntr        : std_logic_vector(23 downto 0);

------------------------------------------------------------------------
-- Module Implementation
------------------------------------------------------------------------

begin

    ------------------------------------------------------------------------
    -- Map basic status and control signals
    ------------------------------------------------------------------------

    clkMain <= mclk;

    ctlEppAstb <= astb;
    ctlEppDstb <= dstb;
    ctlEppWr   <= pwr;
    pwait      <= ctlEppWait;    -- drive WAIT from state machine output

    -- Data bus direction control. The internal input data bus always
    -- gets the port data bus. The port data bus drives the internal
    -- output data bus onto the pins when the interface says we are doing
    -- a read cycle and we are in one of the read cycles states in the
    -- state machine.
    busEppIn <= pdb;
    pdb <= busEppOut when ctlEppWr = '1' and ctlEppDir = '1' else "ZZZZZZZZ";

    -- Select either address or data onto the internal output data bus.
    busEppOut <= "0000" & regEppAdr when ctlEppAstb = '0' else busEppData;

    rgLed <= regLed;

    -- Decode the address register and select the appropriate data register
    busEppData <=    regData0 when regEppAdr = "0000" else
                    regData1 when regEppAdr = "0001" else
                    regData2 when regEppAdr = "0010" else
                    regData3 when regEppAdr = "0011" else
                    regData4 when regEppAdr = "0100" else
                    regData5 when regEppAdr = "0101" else
                    regData6 when regEppAdr = "0110" else
                    regData7 when regEppAdr = "0111" else
                    rgSwt    when regEppAdr = "1000" else
                    "000" & rgBtn when regEppAdr = "1001" else
                    "00000000";

    ------------------------------------------------------------------------
    -- EPP Interface Control State Machine
    ------------------------------------------------------------------------

    -- Map control signals from the current state
    ctlEppWait <= stEppCur(0);
    ctlEppDir  <= stEppCur(1);
    ctlEppAwr  <= stEppCur(2);
    ctlEppDwr  <= stEppCur(3);

    -- This process moves the state machine to the next state
    -- on each clock cycle
    process (clkMain)
        begin
            if clkMain = '1' and clkMain'Event then
                stEppCur <= stEppNext;
            end if;
        end process;

    -- This process determines the next state machine state based
    -- on the current state and the state machine inputs.
    process (stEppCur, stEppNext, ctlEppAstb, ctlEppDstb, ctlEppWr)
        begin
            case stEppCur is
                -- Idle state waiting for the beginning of an EPP cycle
                when stEppReady =>
                    if ctlEppAstb = '0' then
                        -- Address read or write cycle
                        if ctlEppWr = '0' then
                            stEppNext <= stEppAwrA;
                        else
                            stEppNext <= stEppArdA;
                        end if;

                    elsif ctlEppDstb = '0' then
                        -- Data read or write cycle
                        if ctlEppWr = '0' then
                            stEppNext <= stEppDwrA;
                        else
                            stEppNext <= stEppDrdA;
                        end if;

                    else
                        -- Remain in ready state
                        stEppNext <= stEppReady;
                    end if;                                           

                -- Write address register
                when stEppAwrA =>
                    stEppNext <= stEppAwrB;

                when stEppAwrB =>
                    if ctlEppAstb = '0' then
                        stEppNext <= stEppAwrB;
                    else
                        stEppNext <= stEppReady;
                    end if;       

                -- Read address register
                when stEppArdA =>
                    stEppNext <= stEppArdB;

                when stEppArdB =>
                    if ctlEppAstb = '0' then
                        stEppNext <= stEppArdB;
                    else
                        stEppNext <= stEppReady;
                    end if;

                -- Write data register
                when stEppDwrA =>
                    stEppNext <= stEppDwrB;

                when stEppDwrB =>
                    if ctlEppDstb = '0' then
                        stEppNext <= stEppDwrB;
                    else
                         stEppNext <= stEppReady;
                    end if;

                -- Read data register
                when stEppDrdA =>
                    stEppNext <= stEppDrdB;
                                       
                when stEppDrdB =>
                    if ctlEppDstb = '0' then
                        stEppNext <= stEppDrdB;
                    else
                          stEppNext <= stEppReady;
                    end if;

                -- Some unknown state               
                when others =>
                    stEppNext <= stEppReady;

            end case;
        end process;
       
    ------------------------------------------------------------------------
    -- EPP Address register
    ------------------------------------------------------------------------

    process (clkMain, ctlEppAwr)
        begin
            if clkMain = '1' and clkMain'Event then
                if ctlEppAwr = '1' then
                    regEppAdr <= busEppIn(3 downto 0);
                end if;
            end if;
        end process;

    ------------------------------------------------------------------------
    -- EPP Data registers
    ------------------------------------------------------------------------
     -- The following processes implement the interface registers. These
    -- registers just hold the value written so that it can be read back.
    -- In a real design, the contents of these registers would drive additional
    -- logic.
    -- The ctlEppDwr signal is an output from the state machine that says
    -- we are in a 'write data register' state. This is combined with the
    -- address in the address register to determine which register to write.

    process (clkMain, regEppAdr, ctlEppDwr, busEppIn)
        begin
            if clkMain = '1' and clkMain'Event then
                if ctlEppDwr = '1' and regEppAdr = "0000" then
                    regData0 <= busEppIn;
                end if;
            end if;
        end process;

    process (clkMain, regEppAdr, ctlEppDwr, busEppIn)
        begin
            if clkMain = '1' and clkMain'Event then
                if ctlEppDwr = '1' and regEppAdr = "0001" then
                    regData1 <= busEppIn;
                end if;
            end if;
        end process;

    process (clkMain, regEppAdr, ctlEppDwr, busEppIn)
        begin
            if clkMain = '1' and clkMain'Event then
                if ctlEppDwr = '1' and regEppAdr = "0010" then
                    regData2 <= busEppIn;
                end if;
            end if;
        end process;

    process (clkMain, regEppAdr, ctlEppDwr, busEppIn)
        begin
            if clkMain = '1' and clkMain'Event then
                if ctlEppDwr = '1' and regEppAdr = "0011" then
                    regData3 <= busEppIn;
                end if;
            end if;
        end process;

    process (clkMain, regEppAdr, ctlEppDwr, busEppIn)
        begin
            if clkMain = '1' and clkMain'Event then
                if ctlEppDwr = '1' and regEppAdr = "0100" then
                    regData4 <= busEppIn;
                end if;
            end if;
        end process;

    process (clkMain, regEppAdr, ctlEppDwr, busEppIn)
        begin
            if clkMain = '1' and clkMain'Event then
                if ctlEppDwr = '1' and regEppAdr = "0101" then
                    regData5 <= busEppIn;
                end if;
            end if;
        end process;

    process (clkMain, regEppAdr, ctlEppDwr, busEppIn)
        begin
            if clkMain = '1' and clkMain'Event then
                if ctlEppDwr = '1' and regEppAdr = "0110" then
                    regData6 <= busEppIn;
                end if;
            end if;
        end process;

    process (clkMain, regEppAdr, ctlEppDwr, busEppIn)
        begin
            if clkMain = '1' and clkMain'Event then
                if ctlEppDwr = '1' and regEppAdr = "0111" then
                    regData7 <= busEppIn;
                end if;
            end if;
        end process;

    process (clkMain, regEppAdr, ctlEppDwr, busEppIn)
        begin
            if clkMain = '1' and clkMain'Event then
                if ctlEppDwr = '1' and regEppAdr = "1010" then
                    regLed <= busEppIn;
                end if;
            end if;
        end process;

    process (clkMain)
        begin
            if clkMain = '1' and clkMain'Event then
                cntr <= cntr + 1;
            end if;
        end process;
end Behavioral;

UCF file for NEXYS2

NET "mclk"   LOC = "B8";
NET "astb"   LOC = "V14";
NET "dstb"   LOC = "U14";
NET "pwait"   LOC = "N9";
NET "pwr"   LOC = "V16";
NET "pdb<0>"  LOC = "R14";
NET "pdb<1>"  LOC = "R13";
NET "pdb<2>"  LOC = "P13";
NET "pdb<3>"  LOC = "T12";
NET "pdb<4>"  LOC = "N11";
NET "pdb<5>"  LOC = "R11";
NET "pdb<6>"  LOC = "P10";
NET "pdb<7>"  LOC = "R10";
NET "rgLed<0>"  LOC = "J14";
NET "rgLed<1>"  LOC = "J15";
NET "rgLed<2>"  LOC = "K15";
NET "rgLed<3>"  LOC = "K14";
NET "rgLed<4>"  LOC = "E17";
NET "rgLed<5>"  LOC = "P15";
NET "rgLed<6>"  LOC = "F4";
NET "rgLed<7>"  LOC = "R4";
NET "rgSwt<0>" LOC = "G18";
NET "rgSwt<1>" LOC = "H18";
NET "rgSwt<2>" LOC = "K18";
NET "rgSwt<3>" LOC = "K17";
NET "rgSwt<4>" LOC = "L14";
NET "rgSwt<5>" LOC = "L13";
NET "rgSwt<6>" LOC = "N17";
NET "rgSwt<7>" LOC = "R17";
NET "rgbtn<0>" LOC = "B18";
NET "rgbtn<1>" LOC = "D18";
NET "rgbtn<2>" LOC = "E18";
NET "rgbtn<3>" LOC = "H13";

UCF file for BASYS2

NET "mclk" LOC = "B8";
NET "mclk" CLOCK_DEDICATED_ROUTE = FALSE;

NET "astb" LOC = "F2";
NET "dstb" LOC = "F1";
NET "pwr"   LOC = "C2";
NET "pwait" LOC = "D2";
NET "pdb(0)" LOC = "N2";
NET "pdb(1)" LOC = "M2";
NET "pdb(2)" LOC = "M1";
NET "pdb(3)" LOC = "L1";
NET "pdb(4)" LOC = "L2";
NET "pdb(5)" LOC = "H2";
NET "pdb(6)" LOC = "H1";
NET "pdb(7)" LOC = "H3";
NET "rgLed(7)" LOC = "G1" ;
NET "rgLed(6)" LOC = "P4" ;
NET "rgLed(5)" LOC = "N4" ;
NET "rgLed(4)" LOC = "N5" ;
NET "rgLed(3)" LOC = "P6" ;
NET "rgLed(2)" LOC = "P7" ;
NET "rgLed(1)" LOC = "M11" ;
NET "rgLed(0)" LOC = "M5" ;
NET "rgSwt(7)" LOC = "N3";
NET "rgSwt(6)" LOC = "E2";
NET "rgSwt(5)" LOC = "F3";
NET "rgSwt(4)" LOC = "G3";
NET "rgSwt(3)" LOC = "B4";
NET "rgSwt(2)" LOC = "K3";
NET "rgSwt(1)" LOC = "L3";
NET "rgSwt(0)" LOC = "P11";
NET "rgBtn(3)" LOC = "A7";
NET "rgBtn(2)" LOC = "M4";
NET "rgBtn(1)" LOC = "C11";
NET "rgBtn(0)" LOC = "G12"; # Bank = 0, Signal name = BTN0

Personal tools