Sei sulla pagina 1di 7

--==========================================================

Johns Hopkins University ECE Dept. - Robert E. Jenkins


*** The modules in this archive package may be modified and used freely as long as
the Credits
are retained ***
If you intend to use this package, and are not expert in USB, please get this book:
"USB Complete", 3rd Edition, by Jan Axelson, which is heavily referenced in our
code.
Axelson's Visual Basic project (USBHIDIO2) was the starting point for our PC based
USB-FPGA
program, and the information in his book was essential to our development.
--==========================================================
Please be aware that this whole package is not a polished product, but was
developed as a senior
design project by several of our graduating EE's. Hence, no guarantees or support
are offered
for its use. However, we feel it is a good start toward a university open source
FPGA USB
interface, that requires no micro-controller or USB chip. Everything needed is self
contained in
the FPGA interface component.
--============================================================

This package contains all the elements needed to test and demonstrate a USB HID
communication
interface on an XESS XSA-3S1000 board mounted on an XST-3 extend board. In the
FPGA, the USB
interface component contains a tranceiver component to drive the usb differential
data lines
directly through FPGA 3.3V IO pins. The interface has not been tested with any
other boards,
however it should be portable to other development boards or custom built boards
with minor
changes to the VHDL.

We started this project for practical reasons of use in our FPGA Synthesis Lab
classes. But it
is, we hope, a start toward a university open source USB interface for XILINX
chips, requiring
no controller chip. The FPGA is connected directly to properly conditioned USB
lines on a host
board, and acts as both the USB controller as well as the device tranceiver. These
codes are a
good starting point for anyone wishing to continue the developement for their own
applications.

The package contains 4 VHDL modules along with a Visual Basic Program that allows
communication
with the FPGA to test and demonstrate the USB interface. The various modules are
described
below. We rely mainly on the comments in the source code as detailed documentation
and
explanation for anyone interested in using and modifying the code. In this document
we describe
the architecture and the overall approach.

The USB spec calls for the differential data lines to be 3.3 Volt logic levels,
with a 1.5K
pullup to 3.3V on either the D+ or the D- line depending on whether we operate at
full speed or
low speed. Hence, for a 3.3V Spartan-3 the USB data lines in a cable can be
directly attached to
two FPGA pins, the USB ground connected to the XSA board ground, and the red USB
power line left
disconnected since the interface is self poweredby the FPGA. The interface operates
at USB full
speed with the pullup on the D+ line, but can also work with some minor changes at
low speed
with the pullup on the D- line. The 12 MHZ full speed worked OK on all 3 computers
we tested it
on, however we are not sure it will work in all environments for the following
reason.

The XESS XSA board has a 100 MHz oscillator. With a 100 MHz master, it is difficult
to
synthesize and phase lock a USB full-speed 12 Mhz bit clock and still satisfy well
the USB spec
on jitter. We use a 32-bit DDS accumulator for clock division and phase locking,
and can't avoid
a 10 nsec jitter in the 12 Mhz, since 100 MHz is not an integer multiple of 12 MHz.
A 10 ns
jitter is not comfortably within the USB spec on the full speed bit clock. For the
usb low speed
of 1.5 MHz, a 10 nsec jitter is well within spec. Nevertheless, the interface
worked fine at
12MHz on all the computers we tested. With a different host board and a crystal
controlled
master clock that is a multiple of 12 Mhz (e.g. 96 Mhz), this isn't even an issue.

Although the USB HID standard calls for an Interrupt IN endpoint, things work OK
without it. The
FPGA interface and tranceiver codes allow for an interrupt EP1, and it has been
tested with a 1
msec interrupt polling rate. Interrupt reads at 1 msec were tested for dumping the
DRAM memory
on the XESS XSA board. However, it had poorer performance than control reads, and
also didn't
solve the read packet error problem described below. Since most of the time in our
application,
interrupt read requests were getting NAK'ed, wasting Hub bandwidth, we just left
EP1 out of the
Configuration Descriptor for this version of the package. This means you cannot use
the API
function 'ReadFile' for read reports in the Visual Basic Program; EP0 IN and EP0
OUT are the
only endpoints supported.
If your application needed interrupt EP1 support it is easy enough to add by
changing the number
of endpoints in the Descriptor array in the VHDL package.

--------Basic Architecture---------
The USBF_IFC.vhd is the interface component, serving a similar function to what is
frequently
implemented in firmware with a microprocessor. It has a state machine that waits
for interrupts
from the tranceiver to load data needed to service IN requests from the host, and
latch data
received as OUT packets. It has an advantage over firmware because of its speed.
The state
machine operates at 50 MHz, so even at USB full speed, it takes well less than one
bit time to
analyze a setup request, load the output buffer, and have the data ready to send.
For this
reason it doesn't need more than one Endpoint input or output buffer. It is fast
enough to use
the same buffers for EP0 or EP1, since only one is ever getting used in a
transaction at a time.
Consequently, a lot of the logic found in typical firmware about managing requests
and
pre-loading buffers isn't needed. The interface state machine just responds to the
current
interrupt in less than 1 bit-clock and then waits for the interrupt to be cleared
by the
tranceiver.

Inside this interface, a USB_DRVR.vhd component is instantiated to act as the


tranceiver driving
the USB differential data lines. This component serves a similar function to USB
interface
chips. It sits and waits for tokens and packets to arrive from the host, signals to
the
interface that setup requests or data are ready by setting interrupt signals, and
sends whatever
data is loaded or requested by the interface. This component handles the following
things:
1. It synthesizes the USB bit clock from the 100 MHz master and phase locks it to
the NRZI
signal changes in the incoming USB data.
2. It receives all transmissions from the host, determines whether it is a bus
reset, a
start-of-frame, or a token/data/status packet, stores the content, and tells the
interface about
content via interrupt signals.
3. It sends data packets or status replies (ACK, NAK or STALL) as requested by the
interface.
4. It handles the NRZI data encoding/decoding, CRC bit checking/generation, PID
checking, and
screens packets for the correct USB address.

In the USB_DRVR there are two state machine running at the USB bit rate, one for
receiving and
one for sending data. The state machines tri-state the D+/D- line drivers as
appropriate when
sending and receiving. The two state machine work together, but in a sense the
"receive" state
machine is the master, and the "send" state machine is a slave. This is because all
USB
transactions are initated by the host. The "receive" state machine detects the
start of a
transaction, receives the packets, signals the interface to prepare a response,
then launches
the "send" state machine to send the response, and waits for it to finish sending
the packet.

----------Known Problem---------------
For reasons not yet understood, there is a problem with rapid, sustained read
requests. The
problem is the same for both interrupt reads on EP1, and control reads on EP0.
After a certain
number of successful read reports, the host API software gets repeated packet
errors, and
doesn't recover till the program does a "Write". This generally isn't a problem
except for
dumping a large block of DRAM memory from the XSA board. (Each read packet being
four 16-bit
words). To make this work in the program, we had to make a write request for every
read request
when dumping the DRAM. Nothing like this occurs with the sustained packet writes
for Loading the
DRAM. We suspect this may have to do with buffers in the host hubs for handling
split
transactions with USB 2.0. All the machines we tested on had USB 2.0 hardware.

-----------USBF_Declares.vhd-----------
This is a package that declares constants, arrays, and signal types used in the USB
interface
and tranceiver. This package must be added to any FPGA project that includes the
USB interface.
All of the usb descriptor arrays for Device Descriptor, Config Descriptor, etc. are
for the HID
as described in Axelson's book. If you wanted to apply these codes to another class
device,
these descriptors would have to be changed, as well as some constants defined in
the Visual
Basic host program, FPGA-USB.exe.

-----------USBF_IFC.vhd-------------
This is the USB interface component, which acts as a USB HID class peripheral. The
USBF_IFC
entity instantiates the tranceiver component, which reads and drives the USB data
lines.
Operating as an HID at low speed limits the USB transactions to 8 bytes per packet,
and to 64
byte packets for full speed. In this version even though we run at full speed, we
have stuck
with 8 byte packets so we could easily test at both bit rates. Hence, the main port
input and
output of the interface are arrays of type "EIGHT_BYTES", which is "array(1 to 8)
of
std_logic_vector(7 downto 0)". The use of 8 byte IN and OUT transactions is kind of
wired into
the VHDL because of the history of how we got to this point. Introducing 64 byte
packets and
making this an easy switchover from 8 bytes requires more than trivial changes, so
we have put
this modification off while we tested what we have.

The input array is PCIn, which holds the values to be written up to the PC host
when the host
executes a get_report transaction. The port output signal "rd_req" goes high while
the PCIn
array is being sent by the tranceiver. The out array is PCOut, which contains the
packet just
written to the FPGA by the PC host, via either a control transfer on EP0_OUT or an
interrupt
transfer on EP1_OUT. Both methods work, but the control transfers are faster when
the USB hub is
not busy. A port output signal "new_dat" pulses high when a new PCOut array is
latched by the
interface.

--------USB_DRVR.vhd------------------
This component (included in the above file) is the transceiver, and is instantiated
inside the
USBF_IFC, to provides communication with the USB hub over the D+/D- data lines.
There are 2
state machines inside the USB_DRVR - one to read the USB data lines and recover
packets from the
host, the other to drive the usb data lines and send packet to the host. The
tranceiver signals
the interface via interrupt signals about packets received, and clears the
interrupts when the
resulting action is completed.

--------USB_Demo.vhd------------------
This is the top level of an ISE project designed to test the USB interface, and
serve as an
example of how to use it in conjunction with the FPGA-USB.exe Visual Basic program
on the host
computer. This module shows how to instantiate the USBF_IFC, how to use its input
and ouput
arrays, and also how to use the "mode" byte sent as a feature report.

The entity also contains the XESS SDRAM interface module, and a state machine
designed to
communicate with the PC program for loading and reading the DRAM on the XST board.
This
demonstrates how to exchange DRAM data using the USB interface. We designed the
DRAM access
state machine to use the interface outputs, rd_req and new_dat, to coordinate with
the program
running on the PC. We rely on the FPGA being much faster than the PC program, so
the memory
access state machine has to pause and wait for the PC to either prepare and write
the next
packet for loading the DRAM, or read the last packet read from the DRAM.

We also note that the interface makes use of the "set_feature" capability of an HID
class
peripheral to send a "mode" byte to the FPGA project that can control the project
operation.
This serves a purpose similar to setting dip switches on the XST board, but is much
more
powerful and easy to use. We make use of the "mode" byte to activate the DRAM
access state
machine, when doing memory load or read operations, as an example of how the mode
byte can be
used to control the FPGA operation.

There is a debug vector passed up from the interface that we have connected to LED
elements on
the XESS board. If everything is working OK, in a different application this port
signal could
be left open, and rely on logic trimming by the synthesis process to eliminate the
debug vector
from the final circuit.
------------------------
SDRAMJ.vhd - This is a Hopkins modification of the XESS packages to support the
DRAM interface.
We have consolidated everything needed for the DRAM interface, including all
declarations of
FPGA-side signals, into a single package file that also includes the interface.
This was done to
make it a bit easier to use the XSA DRAM in our student projects.
-------------------------
USBF-3S.ucf - This is the ucf file for building the USB_Demo.vhd project for the
XSA-3S1000
board mounted on the XST-3. If one desired to re-build the project bit file for a
different
board combination, a new ucf file would have to be used. The pins used for the USB
data lines
were arbitrarily selected for easy plug-in to the XST-3 header.
------------------------
FPGA-USB.exe - This is the Visual Basic executable for FPGA communications. The
entire VB
Project is included with all the source code and the executable. The code is based
heavily on
Jan Axelson's HIDIO version 2 VB code, with references to his book in the comments.
We suggest
that anyone who wishes to understand and modify this code for their own
applications, purchase a
copy of Jan's book, "USB Complete". We started with Jan's HIDIO version 2 code for
Visual Basic
6.0. His latest version is for Visual Net, which we don't use at Hopkins for
reasons of heritage
and backward compatability.

When it starts up, the FPGA-USB program searches the USB devices connected to the
PC to find the
FPGA. So it expects the FPGA circuit to be active and successfully enumerated when
it starts.
There is a debug window in the program frame that tells the user what's going on
about finding
the FPGA device, and maintaining connectivity. If the PC FPGA-USB program is
running and the
FPGA project bit file is re-loaded, you have to first disconnect and then reconnect
the USB
cable, and then request the PC program to re-find the FPGA device after it re-
enumerates.

There are various command buttons and options widgets in the program window that
are more or
less self explanitory. Any DRAM operations expect a compatible state machine to be
active in the
FPGA.
----------------------

Additional Comments:
Note that the USB cable should be unattached when your project bit file is loaded,
and then
attached. At that point, the project is held in reset until (1)the DLL's in the
SDRAM interface
achieve lock, and then (2)until the USBF_IFC completes enumeration. An output
signal from the
interface (config_done) goes high when enumeration is complete, and the USB device
driver on the
PC has accepted the FPGA as an HID class peripheral. We tie this signal to bargraph
LED
segment-10 so we know when the FPGA is ready for usb communications. Sometimes,
depending on the
state of things, enumeration may not complete when the usb cable is attached. In
this case the
XST reset pushbutton needs to be pushed to reset the FPGA interface after the cable
is
disconnected, and then the usb cable needs to be re-inserted to restart
enumeration. This always
works for us; but you can always reload the bit file with the cable disconnected.

In the FPGA-USB.exe program, the SendBuffer and ReadBuffer used for USB reports are
allocated as
global static byte arrays. Thus, once they are filled, they will hold those values
for the next
report. This makes it easy to change, say a single byte in a report and just re-
send the whole
8-byte report, rather than change the report size. We opted for this approach for
simplicity at
both ends of a transaction. In the FPGA, the USB PCOut and PCIn arrays are
simliarly latched and
maintain their values until rewritten.

So basically, what is in the FPGA-USB SendBuffer appears in the FPGA PCOut array
after a
"set_report" or a "WriteFile", and what is in the FPGA PCIn array appears in the
ReadBuffer
after a "get_report". Thus in the demo program, the "Send hex Bytes" operation
sends 8 bytes
from the window to the PCOut array in the FPGA, and displays what was sent. The
"Read Byte
Packet" operation receives into the ReadBuffer, whatever std_logic hex values are
tied to the
PCIn array in the FPGA. In the FPGA demo project we have tied the first 5 PCIn
bytes to the
PCOut bytes, and bytes 6 and 7 are tied to a 16 hz counter. This lets you try out
the
"Continuous Read" mode, where the get_report is executed 5 times per second, to
observe
real-time changes in the FPGA values. This makes the 8 byte display act similar to
LED's.

Potrebbero piacerti anche