Skip to content

Latest commit

 

History

History
executable file
·
416 lines (311 loc) · 12.8 KB

content.asc

File metadata and controls

executable file
·
416 lines (311 loc) · 12.8 KB

A high speed external interface

This chapter is only applicable to Basys2 board - The Papilio board only has a serial port. It also assumes that you are using the Windows OS - but I’m sure that only minor changes are needed for it all to work under Linux too.

The Digilent Parallel Interface

Digilent FPGA boards have a port of the USB interface wired to the FPGA. I’ve used this to transfer data at up to 11 megabytes per second (but only on a Nexys2 - the interface Basys2 is much slower!). The supplied documentation is pretty terse, so here is a quick start guide.

The interface implements the long obsolete EPP protocol that was traditionally used to talk to parallel port scanners. It allows the connected device to address up to 256 8-bit registers that can be implemented within the FPGA.

These registers can either be read by the host PC one byte at a time, or a "Repeat" function can be called to read multiple bytes from the same register.

The "make or break" shortcoming of this interface is that there is no interrupt signal going back to the host which would allow the FPGA get its attention. Unlike when using RS-232 this forces the host software to poll the FPGA at regular intervals - which is not ideal for responsiveness or CPU usage.

The FPGA side of the interface

The following signals make up the interface:

Name

Type

Description

DB(7 downto 0)

INOUT

Data bus

WRITE

IN

Write enable (active low) - data will be written from the host during this cycle

ASTB

IN

Address strobe (active low) - data bus will be captured into the address register

DSTB

IN

Data strobe (active low) - the bus will be captured into the currently selected data register

WAIT

OUT

Asserted when FPGA is ready to accept data

INT

OUT

Interrupt request - not used

RESET

IN

Reset - not used

Read Transaction

The steps in a read transaction are:

  • Host lowers ASTB or DSTB to commence read of either the address register or the selected data register

  • FPGA presents data on data bus

  • FPGA raises WAIT indicating that the data is valid

  • Host captures the data

  • Host raises ASTB or DSTB

  • FPGA removes the data from the data bus

  • FPGA lowers WAIT to finish transaction

Write Transaction

The steps in a write transaction are:

  • Host presents data on the data bus

  • Host lowers write wnable to 0

  • Host lowers either ASTB or DSTB to commence write of either the address register or the selected data register

  • FPGA raises WAIT once data is captured

  • Host raises ASTB or DSTB, removes data from bus and raises write enable

  • FPGA lowers WAIT to finish transaction

FSM diagram

epp fsm

Constraints for the BASYS2 board

The constraints required to implement the interface are:

 NET "EppAstb" LOC = "F2"; # Bank = 3
 NET "EppDstb" LOC = "F1"; # Bank = 3
 NET "EppWR"   LOC = "C2"; # Bank = 3

 NET "EppWait" LOC = "D2"; # Bank = 3

 NET "EppDB<0>" LOC = "N2"; # Bank = 2
 NET "EppDB<1>" LOC = "M2"; # Bank = 2
 NET "EppDB<2>" LOC = "M1"; # Bank = 3
 NET "EppDB<3>" LOC = "L1"; # Bank = 3
 NET "EppDB<4>" LOC = "L2"; # Bank = 3
 NET "EppDB<5>" LOC = "H2"; # Bank = 3
 NET "EppDB<6>" LOC = "H1"; # Bank = 3
 NET "EppDB<7>" LOC = "H3"; # Bank = 3

VHDL for the FPGA interface

This source allows you to set the LEDs and read the switches from the PC. It has a few VHDL features that you won’t have seen up to now:

  • The EppDB (EPP Data Bus) is INOUT - a tri-state bidirectional bus. When you assign "ZZZZZZZZ" (high impedance) to the signal it will then \'read' as the input from the outside world. This is only really useful on I/O pins - within the FPGA all tri-state logic is implemented using multiplexers.

  • It uses an enumerated type to hold the FSM 'state'. This is only really useful if you don’t want to use individual bits within the state value to drive logic (which is usually a good way to get glitch free outputs)

 library IEEE;
 use IEEE.STD_LOGIC_1164.ALL;
 use IEEE.STD_LOGIC_UNSIGNED.ALL;

 entity epp_interface is
   port (Clk     : in    std_logic;
         -- EPP interface
         EppAstb : in    std_logic;
         EppDstb : in    std_logic;
         EppWR   : in    std_logic;
         EppWait : out   std_logic;
         EppDB   : inout std_logic_vector(7 downto 0);

         -- Feedback
         switches: in    std_logic_vector(7 downto 0);
           leds    : out   std_logic_vector(7 downto 0)
   );
 end epp_interface;

 architecture Behavioral of epp_interface is
   type   epp_state is (idle, data_read, data_write, addr_read, addr_write);
   signal state      : epp_state := idle;
   signal address    : std_logic_vector(7 downto 0) := (others => '0');
   signal port0data  : std_logic_vector(7 downto 0) := (others => '0');
 begin
   process(clk)
   begin

      if rising_edge(clk) then
         case state is
            when data_read  =>
               EppWait <= '1';
               case address is
                  when "00000000" =>
                     EppDB <= not port0data;
                  when "00000001" =>
                     EppDB <= switches;
                  when others =>
               end case;

               if EppDstb = '1' then
                  state <= idle;
               end if;
            when data_write =>
               EppWait <= '1';
               case address is
                  when "00000000" =>
                     port0data <= EppDB;
                  when "00000001" =>
                     leds <= EppDB;
                  when others =>
               end case;

               if EppDstb = '1' then
                  state <= idle;
               end if;

            when addr_read  =>
               EppWait <= '1';
               EppDB   <= address;
               if EppAstb = '1' then
                  state <= idle;
               end if;

            when addr_write =>
               EppWait <= '1';
               address <= eppDB;
               if EppAstb = '1' then
                  state <= idle;
               end if;

            when others =>
               EppWait  <= '0';
               EppDB <= "ZZZZZZZZ";
               if EppWr = '0' then
                  if  EppAstb = '0' then
                     state <= addr_write;
                  elsif EppDstb = '0' then
                     state <= data_write;
                  end if;
               else
                  if EppDstb = '0' then
                     state <= data_read;
                  elsif EppAstb = '0' then
                     state <= addr_read;
                  end if;
               end if;
         end case;
      end if;
   end process;
 end Behavioral;

The PC side of the interface

Header files and libraries

These are in the Adept SDK, which can be downloaded from http://www.digilentinc.com/Products/Detail.cfm?Prod=ADEPT2

The zip file includes all the files you need, including documentation, libraries and examples.

The following header files are needed in your C code:

  • gendefs.h

  • dpcdefs.h

  • dpcutil.h

You will also need to add the path to the libraries into your project’s linking settings.

Connecting to a device

Connecting isn’t that simple, but it’s not that hard either. Three functions are needed:

  • DpcInit()

  • DvmgGetDefaultDev()

  • DvmgGetDevName()

	if (!DpcInit(&erc)) {
		printf("Unable to initialise\n");
		return 0;
	}

	id = DvmgGetDefaultDev(&erc);
	if (id == -1) {
		printf("No default device\n");
		goto error;
	}

	if(!DvmgGetDevName(id, device, &erc)) {
		printf("No device name\n");
		goto error;
	}

The first time you make use of the interface you may need to call one more

function only once to present a dialogue box allowing you to select which FPGA board will be your default device:

  • DvmgStartConfigureDevices()

Once used, the settings will be saved in the registry and will persist.

Connecting to the EPP port of that device

One function is used to connect to the device (vs connecting to the JTAG port):

  • DpcOpenData()

	if (!DpcOpenData(&hif, device, &erc, NULL)) {
		goto fail;
	}

Reading a port

Reading a port is achieved with either of these functions:

  • DpcGetReg() - Read a single byte from a register

  • DpcGetRegRepeat() - Read multiple bytes from a register

Here’s an example function that opens the EPP port and reads a single register:

  static int GetReg(unsigned char r) {
	unsigned char b;
	ERC		erc;
	HANDLE	hif;

	if (!DpcOpenData(&hif, device, &erc, NULL)) {
		goto fail;
	}

	if (!DpcGetReg(hif, r, &b, &erc, NULL)) {
		DpcCloseData(hif,&erc);
		goto fail;
	}

	erc = DpcGetFirstError(hif);
	DpcCloseData(hif, &erc);

	if (erc == ercNoError)
		return b;
   fail:
	return -1;
  }

Writing to a register

Writing to a port is achieved with either of these functions:

  • DpcPutReg() - Read a single byte from a register

  • DpcPutRegRepeat() - Read multiple bytes from a register

Here’s an example function that opens the EPP port and writes to a single register

  static int PutReg(unsigned char r, unsigned char b) {
        ERC		erc;
	HANDLE	hif;
	printf("Put %i %i\n",r,b);
	if (!DpcOpenData(&hif, device, &erc, NULL)) {
		goto fail;
	}

		if(!DpcPutReg(hif, r, b, &erc, NULL)) {
			DpcCloseData(hif,&erc);
			goto fail;
		}

	erc = DpcGetFirstError(hif);
	DpcCloseData(hif, &erc);

	if (erc == ercNoError)
		return 0;

  fail:
	return -1;
  }

Closing the EPP port

One function is used to close the EPP port:

  • DpcCloseData()

	DpcCloseData(hif, &erc);

	if (erc == ercNoError)
		return b;

Closing the interface

It is always good to clean up after yourself. Use the following function to do so:

  • DpcTerm()

	DpcTerm();

Project - Using the PC end of the interface

m18s1

  • Create a C program that opens the interface and reads a single byte from registers 5 and 6 and displays the value to the screen

  • Close off Adept and check that your C program also shows the state of the switches on the Basys2

  • Expand your C program to write to the value of the switches to register 1 - this is the LEDs

You now have the host side of bidirectional communication sorted!

Project - Implementing the FPGA end of the interface

  • Create a new FPGA project

  • Create a module that implements the EPP protocol - or use the one of Digilent’s reference designs if you want

  • Connect writes of register 1 to the LEDs

  • Connect reads of register 5 or 6 to the switches

  • Test that your design works just as well with your program as Digilent’s reference design