Paste "write me an AXI-Stream width converter" into a chat window and you get plausible SystemVerilog back. That is useful, and for many engineers it is where AI for FPGA work stops. The code arrives, you read it, and you find the bugs yourself.
There is a second mode, and it is the one worth learning. In it the AI does not only write the code. It runs the tools, reads what they say, and changes the code in response, again and again, until the checks pass or it hits something it should hand back to you. That is what "agentic" means in practice. This article explains it from the ground up.
A chatbot answers. An agent does the work.
In FPGA tooling, "AI" currently means one of two things.
A chatbot answers questions. AMD's Vivado chatbot, which we looked at in a practical guide, is one: a faster way to search the documentation. When it is wrong, you have to notice.
An agent operates tools. Agentic coding tools appeared in software first. They read a repository, edit files, run commands, and read what comes back, from an ordinary terminal.[1][2] Pointed at an FPGA project, the same machinery reads HDL instead of Python, and the commands it runs are lint, simulation, and synthesis instead of a unit test suite.
There are several to choose from, and the loop in this article works with any of them. Claude Code and OpenAI's Codex run in the terminal, as desktop apps, and as extensions for VS Code and similar editors.[1][2] Cursor builds the agent into an editor.[3] What they share is the part that counts here: they read files, run commands, and read what the commands print. What differs is the model behind them, the editor integration, and where the code goes.
An agent is a loop around a model:
- Plan. Break the goal into steps. Read the spec, sketch the interface, decide the structure.
- Act. Edit the RTL, write or extend the testbench, run a tool.
- Observe. Read what the tool returned. The compile error, the failing assertion, the timing path.
- Iterate. Decide the next action from that observation, and repeat until the stop condition is met.
A code generation response without tool execution collapses this to one pass. Plan, act, stop. It never sees that its always_comb inferred a latch or that the design misses timing by 1.8 ns, because it never ran anything. The difference is step three. What it observes comes from the tool, not from its own confidence.
This is not the same as autonomous. You set the goal, you set the limits, and you review the result. The agent owns the inner cycle of edit, run, read, which is the part that is tedious for a person and cheap for a machine.
Two loops, not one
In practice that plan, act, observe, iterate cycle runs through two tool loops, and the difference between them is run time.
The simulation loop is the fast one. The agent edits the RTL and the testbench, lints, simulates, and checks the results: the assertions, the reference model, and coverage. Its goal is a design that does what the specification says, shown by every assertion holding, every output word matching the reference model, and the coverage points confirming that the tests reached the cases that matter. A failing assertion or a coverage gap sends the agent straight back to the edit. This is where the iterations concentrate, because it is the quicker of the two loops.
The synthesis and implementation loop is the slower one, and the agent normally enters it only when simulation is green. An early synthesis pass to check that the design fits the part and to get a rough clock estimate is a reasonable exception. Synthesis proves the design builds and surfaces what lint missed: a multiplier that inferred more DSP blocks than expected, a memory that became distributed logic, a block that does not fit. A CDC report on the synthesised netlist classifies the clock crossings it can recognise.[4] Implementation places and routes the design, and timing analysis turns "looks fast" into a slack figure in nanoseconds. If timing is not met, the fix is a change to the RTL, or a constraint change that the agent proposes and you approve. An RTL change goes back through the simulation loop first. The loop can also extend onto the hardware, with the FPGA itself in the loop running the bitstream and reporting back.
Feedback does not come only from the last stage of each loop. Lint, synthesis, placement, routing, and timing each report their own failures, and the agent reacts to whichever fires first.
Every tool in both loops can be driven from the command line and reports in text: a log file, a slack figure, a pass or fail. That is the precondition. The agent runs the simulator and the synthesis and implementation tools of whichever vendor the project uses in batch mode through a script, reads the log and report files rather than the GUI, and everything it reads goes into its context. Nothing in the loop depends on the vendor, only on the tool having a command line and writing text. A flow that only lives in the GUI is the first thing to fix. Once that is in place the agent is not guessing whether its change helped. It is being told by the same tools you already trust.
A width converter, start to finish
Take a bounded task inside an existing project: implement an AXI-Stream data width converter, 32 bit in to 128 bit out, that packs four beats and handles a partial final word correctly.[5] The goal is checkable: the reference checker passes, every coverage point is hit, and the block meets the project clock on its part.
The prompt that starts a loop reads differently from one that asks for code. It names the goal, the tools, the stop condition, and the limits:
Implement axis_width_conv in rtl/axis_width_conv.sv: AXI-Stream, 32 bit in,
128 bit out, four beats per output word, tlast and tkeep correct on a
partial final word. Spec is in docs/axis_width_conv.md.
Run python run.py lint, then sim, then cov, then synth, then impl, and read
the logs after each. Fix what fails and run again. Done means: lint clean,
sim reports RESULT: PASS with zero mismatches and zero protocol errors, cov
reports full coverage, synth reports no latches, and impl passes timing.
You may edit rtl/axis_width_conv.sv and add stimulus in tb/stimulus.sv.
Do not edit tb/checker.sv, tb/ref_model.sv, the spec, or any .xdc file.
If the same check still fails after two attempts, stop and report what you
tried and what you think the problem is, so I can break it down with you.
Every line after the first is what turns one pass into a loop. The tools to run and the logs to read give it something to observe, the "done means" line is the stop condition, and the last block is the limits, including the rule that two failed attempts on the same check hand the problem back to you. The stop condition is also where the tokens go: every requirement folded into it means more runs and more logs read before the agent may stop, so a single condition that covers everything is the most thorough loop and also the most expensive one, and that is a trade off you set. If you want to study the block itself before running the loop, the appendix covers the AXI4-Stream handshake, the packing rules and one architecture, with waveforms. Note what this loop closes on: functionality. It does not prescribe an architecture, set a resource budget or push the clock, and the implementation run at the end is only a sanity check that the design is synthesisable and meets an easy constraint. Those targets could be added to the loop in the same way, as one more check with a log the agent can read, for example a maximum LUT count from the utilisation report or a tighter clock period in the constraints file. What follows is a representative run of that prompt, not a transcript of one, and the bugs in it are the ones this block tends to have.
- Plan. The agent restates the interface, notes the packing ratio, and flags
tlast,tkeepon a partial final word, and back pressure ontreadyas the risky parts. - Act. It writes the module and adds stimulus to the testbench: randomised packets of varying length. The checker and the reference model are yours, and it cannot edit them.
- Observe. Lint is clean. Simulation fails: when the packet length is not a multiple of four, the partially filled final output word is never flushed, and the checker reports a mismatch on the next word because it was still expecting the one that never came.
- Iterate. The agent reads the mismatch and the packet it occurred on, adds the flush with
tlastand the righttkeep, and reruns. Simulation passes. - Observe coverage. Coverage shows the back pressure path,
treadylow mid packet, was never exercised. The agent adds a stress test, finds that whentreadydrops on the fourth beat the converter still accepts the input word and has nowhere to put it, and fixes the handshake. - Close. Simulation is green, so the agent runs the generic synthesis check, which reports the cell count and no latches, and then the vendor implementation flow for the target part. Both are clean, the block fits, and timing meets the project clock. It stops and hands you the design, the added tests, the coverage report, and the timing report. In a full project the same loop would end with the bitstream.
No single step is beyond a competent engineer. The loop did the edit, run, read cycling that nobody enjoys, and the coverage gap, not the agent's judgement, is what found the second bug.
The checks have to be independent
The failure we see most often in first attempts is simple. The agent runs the tests, they pass, and the design is still wrong, because the agent also wrote the tests and it wrote them to pass. Feedback the agent can trivially satisfy is not feedback.
So the checks have to be independent of the agent. The assertions are written from the specification, not from the implementation, and the reference model is one it did not author. Coverage as well as pass or fail, so a green test that ran 40% of the branches reads as a warning. Synthesis, implementation, and timing run for real, because "it simulates" is not "it builds".
The agent also needs limits. It may change the module and add stimulus and tests, but the checker, the assertions, the reference model, and the constraints are files it cannot write, because an agent that can edit the goal will meet it. A constraint change it can only propose. It gets a small number of attempts per problem, so one it cannot solve comes back to you, broken down, instead of consuming the afternoon. And what it hands back is a candidate for review, not a finished design.
The loop helps least on the problems that were never about iteration. Choosing an architecture, reasoning about a clock domain crossing, and closing timing on a congested design stay with you. The loop does not remove the engineer. It moves the engineer to the parts that need one.
What it takes to build a loop
Four things, and the loop degrades without any one of them.
Tests that must pass, coverage to hit, timing to meet. A vague goal produces a vague loop.
Lint, simulation, synthesis and timing that the agent can run and read as logs and reports, not screenshots.
Done means the checks pass. Without a clear stop the loop wanders or declares victory early.
What it may edit, how many attempts it gets, and a review gate, so the agent improves the design rather than the metric.
In our experience getting these four right is most of the work, and it is what our FPGA and AI training is built around. The course sets up the tool interface, the independent checks, and the limits on a real design.
References
5 sources
- Anthropic, "Claude Code" documentation: an agentic coding tool that reads a codebase, edits files, and runs commands from the terminal.
- OpenAI, "Codex CLI" documentation: OpenAI's coding agent, run from the terminal and from scripts, that reads and edits a repository and runs commands.
- Cursor, "Agent" documentation: an agent inside the editor that edits code and runs terminal commands on its own.
- AMD UG906: Vivado Design Suite User Guide: Design Analysis and Closure Techniques: timing reports,
report_cdc, and closure methodology. - Arm IHI 0051, AMBA 4 AXI4-Stream Protocol Specification: the
tvalid/treadyhandshake,tlast, and thetkeepbyte qualifiers used to mark a partial final word.
Our FPGA and AI training sets this loop up on your own designs, with the checks and the limits described here.
Comments
Loading comments...
Leave a Comment