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:
$ lspci
...
01:05.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] RS880 [Radeon HD 4200]
02:00.0 Network controller: Ralink corp. RT5390 Wireless 802.11n 1T/1R PCIe
03:00.0 RAM memory: Xilinx Corporation Default PCIe endpoint ID
04:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8101/2/6E PCI Express Fast/Gigabit Ethernet controller (rev 05)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 IngressSend 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 IngressSend 32-bit Read CommandPCIE IngressParses and notifiesPCIE ControlHostwait for Status UpdatePCIE Egress->Hostsend Status Update the Buffer Done ready will indicate where the data is locatedHostrepeats the above process until all of the data is read from the deviceHostwait for final Status Update with Done flag assertedPCIE Egress->Hostsend 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 IngressSend 32-bit Write CommandPCIE IngressParse and notifyPCIE ControlHostwaits for Status UpdatePCIE Egress->Hostsend Status Update Buffer Status indicates which buffer is ready to be read fromHostrepeats the above process until all of the data is sent to the deviceHostwaits for the Status Update with the Done flag assertedPCIE Egress->Hostsend 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:Hostsends an address to thePCIE Ingress- Expected Result:
PCIE Ingressparses Memory Write TLP and address is correctly written to the Address Register
- Test 0
- Expected Result:
PCIE Ingress: Receive Buffer Status:Hostsends a buffer status value to thePCIE Ingressindicating the status of the host side buffer- Expected Result:
PCIE Ingressparses Memory Write TLP and new data is correctly written to the Buffer StatusPCIE Ingress->Ingress Buffer Managerwithin a write context the Buffer Status Update Signal is strobed
- Test 0
- Expected Result:
PCIE Ingress: Receive Memory Write Command:Hostsends a memory write command to initiate a memory write transaction- Expected Result:
PCIE Ingressparses the Memory Write TLP and the data count is updatedPCIE Ingress->PCIE Controlstrobes Command Strobe
- Test 6 Visually Observed Using GTKWave
- Expected Result:
PCIE Ingress: Receive Completion Packet:Hostsends a packet of data in response to a memory read request from thePCIE Control- Expected Result:
PCIE Ingressparses the Memory Write TLP and the data count is updatedPCIE Ingress->PCIE Controlstrobes 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 Ingresssends a Memory Write Command toPCIE Controlusing a short packet, not needing to switch between buffers or stressingCredit Manager- Expected Result:
PCIE Ingress->PCIE Controlstrobe Command Write StrobePCIE Controlenters the Data Ingress Sub-State Machine, specifically Ingress Flow ControlPCIE Controldetermines there is more data to read and proceeds to Wait for Tag StatePCIE Controlwaits for Tag Ready signalIngress Buffer Manager->PCIE Control: assert Tag ReadyPCIE Controlproceed to Wait Flow ControlCredit Manager->PCIE Controlassert Flow Control ReadyPCIE Controlproceed to Send Memory RequestPCIE Control->PCIE Egresssend a request for a packet of data associated with a tagPCIE Controlupdate counts and registerPCIE Controlif full buffer has been read send a Status UpdatePCIE Controlwill proceed to Ingress Flow ControlPCIE Control->PCIE EgressIf 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 Ingresssends a Memory Write Command toPCIE Control.Ingress Buffer Managerwill need to manage a buffer that spans multple tags- Expected Result:
PCIE Ingress->PCIE Controlstrobe Command Write StrobePCIE Controlenters the Data Ingress Sub-State Machine, specifically Ingress Flow ControlPCIE Controldetermines there is more data to read and proceeds to Wait for Tag StatePCIE Controlwaits for Tag Ready signalIngress Buffer Manager->PCIE Control: assert Tag ReadyPCIE Controlproceed to Wait Flow ControlCredit Manager->PCIE Controlassert Flow Control ReadyPCIE Controlproceed to Send Memory RequestPCIE Control->PCIE Egresssend a request for a packet of data associated with a tagPCIE Controlupdate counts and registerPCIE Controlif full buffer has been read send a Status UpdatePCIE Controlwill proceed to Ingress Flow ControlPCIE Control->PCIE EgressIf 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 Ingresssends a Memory Write Command toPCIE Control.Ingress Buffer Managerwill need to manage a buffer that spans 2 buffers- Expected Result:
PCIE Ingress->PCIE Controlstrobe Command Write StrobePCIE Controlenters the Data Ingress Sub-State Machine, specifically Ingress Flow ControlPCIE Controldetermines there is more data to read and proceeds to Wait for Tag StatePCIE Controlwaits for Tag Ready signalIngress Buffer Manager->PCIE Control: assert Tag ReadyPCIE Controlproceed to Wait Flow ControlCredit Manager->PCIE Controlassert Flow Control ReadyPCIE Controlproceed to Send Memory RequestPCIE Control->PCIE Egresssend a request for a packet of data associated with a tagPCIE Controlupdate counts and registerPCIE Controlif full buffer has been read send a Status UpdatePCIE Controlwill proceed to Ingress Flow ControlPCIE Control->PCIE EgressIf 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 Ingresssends a Memory Write Command toPCIE Control.Ingress Buffer Managerwill need to manage a buffer that spans 3 buffers- Expected Result:
PCIE Ingress->PCIE Controlstrobe Command Write StrobePCIE Controlenters the Data Ingress Sub-State Machine, specifically Ingress Flow ControlPCIE Controldetermines there is more data to read and proceeds to Wait for Tag StatePCIE Controlwaits for Tag Ready signalIngress Buffer Manager->PCIE Control: assert Tag ReadyPCIE Controlproceed to Wait Flow ControlCredit Manager->PCIE Controlassert Flow Control ReadyPCIE Controlproceed to Send Memory RequestPCIE Control->PCIE Egresssend a request for a packet of data associated with a tagPCIE Controlupdate counts and registerPCIE Controlif full buffer has been read send a Status UpdatePCIE Controlwill proceed to Ingress Flow ControlPCIE Control->PCIE EgressIf 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 Controlinitiates a transaction thePCIE Egresswill transmit all the data:- Expected Result:
PCIE Control->PCIE EgressAssert Send PacketPCIE Egresssend TLP packet to hostPCIE Egress->PCIE ControlAssert Send Packet Finished `PCIE Control->PCIE EgressDe-assert Send PacketPCIE Egress->PCIE ControlDe-Assert Send Packet Finished
- Test 1
- Expected Result:
Ingress Buffer Manager Tests
Ingress Buffer ManagerEnable: Initialize whenPCIE Controlassert enable signal- Expected Result:
PCIE Control->Ingress Buffer Managerassert enableIngress Buffer ManagerExits reset state and waits for Buffer Status Update Strobe
- Test 6 Visually Observed Using GTKWave
- Expected Result:
Ingress Buffer ManagerPrepares Buffer: WhenIngress Buffer Managerreceives a Buffer Status Update Strobe fromPCIE Ingressit configures Tag State Machine and indicates Tag Ready toPCIE Control- Expected Result:
PCIE Control->Ingress Buffer Managerassert Buffer Status Update StrobeIngress Buffer Managerwill read in the Buffer Status register and initiate the appropriate Tag State MachineIngress Buffer Manager->PCIE Controlwill 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 Managerwill generate the appropaite address: WhenPCIE Ingressreceives 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 Managerwill update the appropriate address- Expected Result:
PCIE Ingress->Ingress Buffer Managertag value is updated when a new Completion Packet is detectedIngress Buffer Managergenerates an address associated with tagPCIE Ingress->Ingress Buffer Managerstrobes Completion Packet Finished theIngress Buffer Managerwill update the count and address for that tag.Ingress Buffer managerTag 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 ManagerInitiate FIFO Write: IfIngress Buffer Managerdetects the entire buffer has been written down to theBuffer BuildertheIngress Buffer Managerwill tell theBuffer Builderto populate the Ingress FIFO with the 4096 word data- Expected Result:
Ingress Buffer Managerwill determine the amount of data that has been read from the host buffer and when the buffer is completely read it will signal theBuffer Builderto populate the Ingress FIFO
- Test 6 Visually Observed Using GTKWave
- Expected Result:
Ingress Buffer ManagerUpdatePCIE Controlwhen data has been written to Ingress FIFO: After the Ingress FIFO has been populated the buffer is finished and theBuffer Builderwill indicate this status toIngress Buffer Manager. It should assert Buffer Finished toPCIE Control- Expected Result:
Ingress Buffer Manager->Buffer Builderassert appropriate Populate FIFO FlagBuffer Builder->Ingress Buffer Managerassert appropriate FIFO Finished Flag indicating it has successfully finished writing data from the buffer to the FIFOIngress Buffer Manager->PCIE Controlassert appropriate Buffer Finished Flag indicating that all data fromHosthas successfully been written to the DevicePCIE Control->Ingress Buffer Managerassert appropriate Buffer Finished Ack Flag indicatingPCIE Controlhas received flagIngress Buffer ManagerResets the tag state machine and resets the appropriate buffer’s status
- Test 6 Visually Observed Using GTKWave
- Expected Result:
Ingress Buffer ManagerFlow Control: Do not overflow the buffer- Expected Result:
Ingress Buffer Managerneither of the Buffer Enable channels are asserted. Enable associated Buffer EnableIngress Buffer Managera Buffer Enable channel is asserted. Wait for Buffer FinishBuffer Builder->Ingress Buffer Managerassert Buffer FinishIngress Buffer ManagerEnable associated Buffer Enable
- Expected Result:
Credit Manager Tests
Credit ManagerInitialize: WhenCredit Managerstarts it reads the size of the variousXilinx PCIE Corebuffers. At the initialization state theCredit Managerwill populate it’s local registers with the correct credits- Expected Result:
Credit Managerreceives a reset signal and populates the appropriate registers
- Expected Result:
Credit ManagerFlow Control Ready: Assert Flow Control Ready flag to indicate PCIE Core can handle the transaction- Expected Result:
- When
Credit ManagerIf space is available in the Xilinx PCIE Core assert Flow Control Ready
- When
- Expected Result:
Credit ManagerDecrement Count: Update PCIE Count whenPCIE Controlasserts Flow Control Commit- Expected Result:
Credit Manager->PCIE Controlassert Flow Control ReadyPCIE Control-> Strobe Flow Control CommitCredit ManagerDecrement Count
- Expected Result:
- ``Credit Manager` Increment Count: Increment Packet Count when new data is read
- Expected Result:
PCIE Ingress->Credit Managerassert Packet FinishedCredit ManagerIncrement the appropraite data available
- Expected Result:
Buffer Builder Tests
Buffer BuilderPopulate FIFO: Populate appropriate Ingress FIFO when the associated signal is asserted- Expected Result:
Ingress Buffer Manager->Buffer BuilderAssert PPFIFO Write EnableBuffer BuilderPopulate FIFOBuffer Builder->Ingress Buffer ManagerAssert PPFIFO Write FinishedIngress Buffer Manager->Buffer BuilderDe-Assert PPFIFO Write EnableBuffer BuilderDe-assert PPFIFO Write Finished
- Test 9 Visually Observed Using GTKWave
- Expected Result: