FPGA PCIE Host Interface
Intro
I worked on Nysa to allow users to interact with FPGA using their computer. Originally I started working with a UART interface but quickly I found the bottleneck of a 115K bitrate presented. I worked on a USB2 FIFO interface using the FTDI FT2232H. With this higher throughput I was able to demonstrate playing videos on a remote LCD screen and reading video from a camera but this interface still suffers from throughput issues.
I’ve been working on other solutions including USB 3.0 and SDIO and they are promising but the approach that is the most attractive is PCIE. I’m writing this to talk about the challenges that I am working on and working through.
PCIE and FPGAs
PCIE is a high throughput protocol available on most modern motherboards as well as some embedded boards including the Intel Galileo and NVIDIA TK1 and TX1.
In order to bring up PCIE on an FPGA you need to generate a PCIE core. All the large FPGA vendors provide PCIE cores in some fashion. My experience only lies in the Xilinx tools. I used coregen to generate a PCIE core with an AXI interface. I’ll go into more of my PCIE coregen choices later on.
Xilinx PCIE-AXI Interface
The core has a relatively simple AXI stream interface. If you haven’t had a chance to use AXI it’s very powerful. The AXI streaming interface is different from the full AXI interface because it only contains the data in and data out bus instead of all the command, address write, address read and acknowledgement bus. I was able to write and verify a simple AXI to BRAM converter pretty quickly.
The rest of the interface is essentially flags and registers you can read and write to/from the Xilinx PCIE core.
First Demo
The goals of my first demo were:
- Observe PCIE linkup with the host computer
- Observe raw reads and writes from the host
When starting this endevore I knew there was a lot of oportunities to paint myself into a corner or fall into debug pergatory where I would spend more time thorizing what could be wrong with the design. Instead I spent most of my time in simulation.
Instead of desiging the PCIE core to behave like the interface between the host computer and the FPGA I designed it to be a slave for a known good Nysa interface (USB 2.0 8-bit FIFO) This way I can observe and manipulate all the flags and registers of the PCIE core without having to rebuild it everytime I want to test a feature.
During simulation I used cocotb extensively. I interacted with my core in the same way the Xilinx generated core does (through AXI stream interface). I wrote an AXI bus interface using Cocotb. Now, In order to write data to my core in simulation I send a data array to an AXI bus Python object and it will behave like the data portion of the Xilinx PCIE core.
I used the Xilinx 1052 Application Note as the starting point for my design. The application note provided a kernel source which allowed users to use mmap to read and write blocks of data to the FPGA.
Finally time to bring up my core. Fortunatley I see this immediately:
Wow, immediately I see:
03:00.0 RAM memory: Xilinx Corporation Default PCIe endpoint ID
It took me a while to adapt there kernel module to my device but I finally start observing a write transaction with the host computer
Write Path
Different Modules
Ingress buffer manager.
- Tells the PCIE controller that it is waiting for a buffer from the host computer.
- Receives buffer status updates from the host computer when new data is available. Keep track of which one has come in first.
- Supply the PCIE controller with a tag to use.
- Listen for a commit from the PCIE controller that it has requested data from the host.
- The IBM should listen for the PCIE ingress to receive a ‘completion’ tlp. The tag that is associated with it will determine the appropriate address. If multiple completion headed are required to finish a transaction the IBM updates the appropriate address and tag count. When it outputs the max count it is finished with a tag.
- When a tag is finish it marks it as complete. The IBM keeps track of all the buffers being read from and the tags used to read that buffer. When all tags associated with a buffer is finished or all data is read the IBM notifies the controller that a buffer is done. The controller will then issue a status update to the host which will tell the host that the appropriate buffer is ready for new data.
Buffer builder:
In the end I need to populate the ping pong FIFO. Verify data flows from the large 8k buffer to FIFOs in the correct order. The buffer builder us not responsible for the order, in fact the only responsibility it has is to populate the FIFOs correctly.
PCIE ingress
command context
completion context
In this context the PCIE ingress needs to recognize the complete TCP. The entire process should recognize the TCP and respond by writing the data to the buffer builder at the correct address. Actually the PCIE ingress doesn’t manage the addresses. The ingress buffer manager does this.
register update context
Waits for the status buffer from the device to say buffers are available. Then populates either buffer and notifies the device that a buffer now has data. Read in the buffer status from the host computer
Supply the correct tags to the PCIE egress
Tests
Host Tests
Host
: Write Register: Transmit a 32-bit Register value toPCIE Ingress
- Expected Result:
Host
->PCIE Ingress
Send 32-bit value to a register
- Demonstrated using kernel driver and user application
- Expected Result:
Host
: Send Read Command: Transmit a Read Command- Expected Result:
Host
->PCIE Ingress
Send 32-bit Read CommandPCIE Ingress
Parses and notifiesPCIE Control
Host
wait for Status UpdatePCIE Egress
->Host
send Status Update the Buffer Done ready will indicate where the data is locatedHost
repeats the above process until all of the data is read from the deviceHost
wait for final Status Update with Done flag assertedPCIE Egress
->Host
send Status Update the Done Flag will indicate the transaction is finished
- Expected Result:
Host
: Send Write Command: Transmit a Write Command toPCIE Ingress
- Expected Result:
Host
->PCIE Ingress
Send 32-bit Write CommandPCIE Ingress
Parse and notifyPCIE Control
Host
waits for Status UpdatePCIE Egress
->Host
send Status Update Buffer Status indicates which buffer is ready to be read fromHost
repeats the above process until all of the data is sent to the deviceHost
waits for the Status Update with the Done flag assertedPCIE Egress
->Host
send Status Update the Done Flag will indicate the transaction is finished
- Demonstrated using kernel driver and user application
- Expected Result:
PCIE Ingress Tests
PCIE Ingress
: Receive Address Register:Host
sends an address to thePCIE Ingress
- Expected Result:
PCIE Ingress
parses Memory Write TLP and address is correctly written to the Address Register
- Test 0
- Expected Result:
PCIE Ingress
: Receive Buffer Status:Host
sends a buffer status value to thePCIE Ingress
indicating the status of the host side buffer- Expected Result:
PCIE Ingress
parses Memory Write TLP and new data is correctly written to the Buffer StatusPCIE Ingress
->Ingress Buffer Manager
within a write context the Buffer Status Update Signal is strobed
- Test 0
- Expected Result:
PCIE Ingress
: Receive Memory Write Command:Host
sends a memory write command to initiate a memory write transaction- Expected Result:
PCIE Ingress
parses the Memory Write TLP and the data count is updatedPCIE Ingress
->PCIE Control
strobes Command Strobe
- Test 6 Visually Observed Using GTKWave
- Expected Result:
PCIE Ingress
: Receive Completion Packet:Host
sends a packet of data in response to a memory read request from thePCIE Control
- Expected Result:
PCIE Ingress
parses the Memory Write TLP and the data count is updatedPCIE Ingress
->PCIE Control
strobes Command Strobe
- Test 6 Visually Observed Using GTKWave
- Expected Result:
PCIE Control (Write Context) Tests
PCIE Control
: Response to Memory Write Command (Short Packet):PCIE Ingress
sends a Memory Write Command toPCIE Control
using a short packet, not needing to switch between buffers or stressingCredit Manager
- Expected Result:
PCIE Ingress
->PCIE Control
strobe Command Write StrobePCIE Control
enters the Data Ingress Sub-State Machine, specifically Ingress Flow ControlPCIE Control
determines there is more data to read and proceeds to Wait for Tag StatePCIE Control
waits for Tag Ready signalIngress Buffer Manager
->PCIE Control
: assert Tag ReadyPCIE Control
proceed to Wait Flow ControlCredit Manager
->PCIE Control
assert Flow Control ReadyPCIE Control
proceed to Send Memory RequestPCIE Control
->PCIE Egress
send a request for a packet of data associated with a tagPCIE Control
update counts and registerPCIE Control
if full buffer has been read send a Status UpdatePCIE Control
will proceed to Ingress Flow ControlPCIE Control
->PCIE Egress
If all of the data has been read from the host assert Done Signal and initiate a Status Update
- Test 6 Visually Observed Using GTKWave
- Expected Result:
PCIE Control
: Response to Memory Write Command (Medium Packet):PCIE Ingress
sends a Memory Write Command toPCIE Control
.Ingress Buffer Manager
will need to manage a buffer that spans multple tags- Expected Result:
PCIE Ingress
->PCIE Control
strobe Command Write StrobePCIE Control
enters the Data Ingress Sub-State Machine, specifically Ingress Flow ControlPCIE Control
determines there is more data to read and proceeds to Wait for Tag StatePCIE Control
waits for Tag Ready signalIngress Buffer Manager
->PCIE Control
: assert Tag ReadyPCIE Control
proceed to Wait Flow ControlCredit Manager
->PCIE Control
assert Flow Control ReadyPCIE Control
proceed to Send Memory RequestPCIE Control
->PCIE Egress
send a request for a packet of data associated with a tagPCIE Control
update counts and registerPCIE Control
if full buffer has been read send a Status UpdatePCIE Control
will proceed to Ingress Flow ControlPCIE Control
->PCIE Egress
If all of the data has been read from the host assert Done Signal and initiate a Status Update
- Test 7 Visually Observed Using GTKWave
- Expected Result:
PCIE Control
: Response to Memory Write Command (Long Packet):PCIE Ingress
sends a Memory Write Command toPCIE Control
.Ingress Buffer Manager
will need to manage a buffer that spans 2 buffers- Expected Result:
PCIE Ingress
->PCIE Control
strobe Command Write StrobePCIE Control
enters the Data Ingress Sub-State Machine, specifically Ingress Flow ControlPCIE Control
determines there is more data to read and proceeds to Wait for Tag StatePCIE Control
waits for Tag Ready signalIngress Buffer Manager
->PCIE Control
: assert Tag ReadyPCIE Control
proceed to Wait Flow ControlCredit Manager
->PCIE Control
assert Flow Control ReadyPCIE Control
proceed to Send Memory RequestPCIE Control
->PCIE Egress
send a request for a packet of data associated with a tagPCIE Control
update counts and registerPCIE Control
if full buffer has been read send a Status UpdatePCIE Control
will proceed to Ingress Flow ControlPCIE Control
->PCIE Egress
If all of the data has been read from the host assert Done Signal and initiate a Status Update
- Test 8 Visually Observed Using GTKWave
- Expected Result:
PCIE Control
: Response to Memory Write Command (Long Packet):PCIE Ingress
sends a Memory Write Command toPCIE Control
.Ingress Buffer Manager
will need to manage a buffer that spans 3 buffers- Expected Result:
PCIE Ingress
->PCIE Control
strobe Command Write StrobePCIE Control
enters the Data Ingress Sub-State Machine, specifically Ingress Flow ControlPCIE Control
determines there is more data to read and proceeds to Wait for Tag StatePCIE Control
waits for Tag Ready signalIngress Buffer Manager
->PCIE Control
: assert Tag ReadyPCIE Control
proceed to Wait Flow ControlCredit Manager
->PCIE Control
assert Flow Control ReadyPCIE Control
proceed to Send Memory RequestPCIE Control
->PCIE Egress
send a request for a packet of data associated with a tagPCIE Control
update counts and registerPCIE Control
if full buffer has been read send a Status UpdatePCIE Control
will proceed to Ingress Flow ControlPCIE Control
->PCIE Egress
If all of the data has been read from the host assert Done Signal and initiate a Status Update
- Test 9 Visually Observed Using GTKWave
- Expected Result:
PCIE Egress Tests
PCIE Egress
: Transmit Packet: WhenPCIE Control
initiates a transaction thePCIE Egress
will transmit all the data:- Expected Result:
PCIE Control
->PCIE Egress
Assert Send PacketPCIE Egress
send TLP packet to hostPCIE Egress
->PCIE Control
Assert Send Packet Finished `PCIE Control
->PCIE Egress
De-assert Send PacketPCIE Egress
->PCIE Control
De-Assert Send Packet Finished
- Test 1
- Expected Result:
Ingress Buffer Manager Tests
Ingress Buffer Manager
Enable: Initialize whenPCIE Control
assert enable signal- Expected Result:
PCIE Control
->Ingress Buffer Manager
assert enableIngress Buffer Manager
Exits reset state and waits for Buffer Status Update Strobe
- Test 6 Visually Observed Using GTKWave
- Expected Result:
Ingress Buffer Manager
Prepares Buffer: WhenIngress Buffer Manager
receives a Buffer Status Update Strobe fromPCIE Ingress
it configures Tag State Machine and indicates Tag Ready toPCIE Control
- Expected Result:
PCIE Control
->Ingress Buffer Manager
assert Buffer Status Update StrobeIngress Buffer Manager
will read in the Buffer Status register and initiate the appropriate Tag State MachineIngress Buffer Manager
->PCIE Control
will assert Tag Ready when a single tag within the Tag State Machine is in the ready state
- Test 6 Visually Observed Using GTKWave
- Expected Result:
Ingress Buffer Manager
will generate the appropaite address: WhenPCIE Ingress
receives a Completion Packet it generates the address that conforms to the tag. If the response to a tag is longer than the Max Read PacketIngress Buffer Manager
will update the appropriate address- Expected Result:
PCIE Ingress
->Ingress Buffer Manager
tag value is updated when a new Completion Packet is detectedIngress Buffer Manager
generates an address associated with tagPCIE Ingress
->Ingress Buffer Manager
strobes Completion Packet Finished theIngress Buffer Manager
will update the count and address for that tag.Ingress Buffer manager
Tag State Machine Moves to finished status when all the data for a buffer is written toBuffer Builder
- Test 6 Visually Observed Using GTKWave
- Expected Result:
Ingress Buffer Manager
Initiate FIFO Write: IfIngress Buffer Manager
detects the entire buffer has been written down to theBuffer Builder
theIngress Buffer Manager
will tell theBuffer Builder
to populate the Ingress FIFO with the 4096 word data- Expected Result:
Ingress Buffer Manager
will determine the amount of data that has been read from the host buffer and when the buffer is completely read it will signal theBuffer Builder
to populate the Ingress FIFO
- Test 6 Visually Observed Using GTKWave
- Expected Result:
Ingress Buffer Manager
UpdatePCIE Control
when data has been written to Ingress FIFO: After the Ingress FIFO has been populated the buffer is finished and theBuffer Builder
will indicate this status toIngress Buffer Manager
. It should assert Buffer Finished toPCIE Control
- Expected Result:
Ingress Buffer Manager
->Buffer Builder
assert appropriate Populate FIFO FlagBuffer Builder
->Ingress Buffer Manager
assert appropriate FIFO Finished Flag indicating it has successfully finished writing data from the buffer to the FIFOIngress Buffer Manager
->PCIE Control
assert appropriate Buffer Finished Flag indicating that all data fromHost
has successfully been written to the DevicePCIE Control
->Ingress Buffer Manager
assert appropriate Buffer Finished Ack Flag indicatingPCIE Control
has received flagIngress Buffer Manager
Resets the tag state machine and resets the appropriate buffer’s status
- Test 6 Visually Observed Using GTKWave
- Expected Result:
Ingress Buffer Manager
Flow Control: Do not overflow the buffer- Expected Result:
Ingress Buffer Manager
neither of the Buffer Enable channels are asserted. Enable associated Buffer EnableIngress Buffer Manager
a Buffer Enable channel is asserted. Wait for Buffer FinishBuffer Builder
->Ingress Buffer Manager
assert Buffer FinishIngress Buffer Manager
Enable associated Buffer Enable
- Expected Result:
Credit Manager Tests
Credit Manager
Initialize: WhenCredit Manager
starts it reads the size of the variousXilinx PCIE Core
buffers. At the initialization state theCredit Manager
will populate it’s local registers with the correct credits- Expected Result:
Credit Manager
receives a reset signal and populates the appropriate registers
- Expected Result:
Credit Manager
Flow Control Ready: Assert Flow Control Ready flag to indicate PCIE Core can handle the transaction- Expected Result:
- When
Credit Manager
If space is available in the Xilinx PCIE Core assert Flow Control Ready
- When
- Expected Result:
Credit Manager
Decrement Count: Update PCIE Count whenPCIE Control
asserts Flow Control Commit- Expected Result:
Credit Manager
->PCIE Control
assert Flow Control ReadyPCIE Control
-> Strobe Flow Control CommitCredit Manager
Decrement Count
- Expected Result:
- ``Credit Manager` Increment Count: Increment Packet Count when new data is read
- Expected Result:
PCIE Ingress
->Credit Manager
assert Packet FinishedCredit Manager
Increment the appropraite data available
- Expected Result:
Buffer Builder Tests
Buffer Builder
Populate FIFO: Populate appropriate Ingress FIFO when the associated signal is asserted- Expected Result:
Ingress Buffer Manager
->Buffer Builder
Assert PPFIFO Write EnableBuffer Builder
Populate FIFOBuffer Builder
->Ingress Buffer Manager
Assert PPFIFO Write FinishedIngress Buffer Manager
->Buffer Builder
De-Assert PPFIFO Write EnableBuffer Builder
De-assert PPFIFO Write Finished
- Test 9 Visually Observed Using GTKWave
- Expected Result: