Writing your own PCIe logic - a roadmap from someone who did it the slow way

Oct 12, 2026 Last reply: 3 days ago 3 Replies
Jump to replies (3)

I did basically this same project a few years back, solo, and it took me closer to four months than the two I'd budgeted. The good news is the hard block really does swallow the ugly parts, the physical layer and the link bring-up handshake are handled for you, so what's left on your plate is the transaction layer and whatever data mover you build on top. That's very much the right level to work at. Don't try to go lower, there's no upside and a world of pain. Start by running the vendor's example design as-is and capturing traffic with a protocol analyzer or the built-in sniffer in your lab tool. Look at actual completions coming back, the tag handling, the ordering. Half of this job is understanding what a completer owes you and when. Then build in this order: a completer first that just answers memory reads and writes, then a requester, then the DMA. Get each one passing loopback tests in simulation before you touch real hardware, because debug on the bench is miserable when you can't tell if your state machine or the link is at fault. Keep the descriptor format dead simple at first, one buffer, then scatter-gather later if you need it. Last thing, budget way more time for the software side than you think. A basic driver that maps your buffers and kicks off transfers is its own chunk of work, and you can't prove your bandwidth numbers without it. If you're hitting 1.5 months I'd say cut scope, do the simple path end to end and polish later.


I'm a few months into my first PCIe project right now and the ordering advice matches what I learned the hard way. I skipped the completer-first approach and went straight for a DMA engine because the PM wanted numbers on a slide. Lost two weeks chasing a bug that turned out to be my TLP handling, not the link, and I had no way to tell which one it was. Rebuilt with a simple memory write responder first and the rest went much faster. My setup is an Artix-7 with the x4 Gen2 core, and the example design plus the host-side test app from the vendor got me a working read path in about a week. The thing that slowed me down was the driver. I budgeted three days and it ate three weeks, mostly around getting consistent DMA buffers and figuring out why my interrupts were misfiring on some machines but not others. One thing I'd add is to log everything early. I added a tiny capture block that dumps transactions to block RAM, and it paid for itself the first time a transfer came back short on real hardware. Simulation showed nothing wrong, so having my own record of what the link actually did was the only reason I found it.

On timeline, I'm at four months and counting for what I thought was a three month job, so the estimate in the post above rings true to me.

One thing I'd check before blaming your own logic is the max payload size the host negotiates. On my last board we assumed 256 byte payloads and wrote the DMA around that, then a BIOS update on a test machine started negotiating 128 and our throughput fell off a cliff. Took a day to spot becasue everythign still worked, just slower. Now I print the negotiated values to a status register the moment the link comes up, saves a lot of head scratching later.

Not sure I buy the bit about your sim showing nothing wrong. If a transfer comes back short and the sim was clean, that ussually means the testbench never stressed the corner cases in the first place, like backpressure or the host cutting a transfer mid-stream. Before bolting a capture block onto your design I'd spend a day making the sim actually hostile. Hardware logging is fine as a last resort but it's slow to iterate on and you still have to decode it by hand.

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required