A technical knowledge portal by eVision Systems GmbH

EDA Praxis / Application notes

From AI-generated VHDL to a simulation test: a complete example

A counter with fixed requirements, complete testbench and actual GHDL tests, plus an Aldec procedure with explicit test status.

This application note takes FPGA and verification engineers from a fixed counter specification to a reproducible simulation test. The example was developed for EDA Praxis with Codex; its faulty variants are deliberately constructed test cases. They are not a retrospective story about an accidentally observed model error. You receive complete original VHDL files, a self-checking testbench, a local runner and the actual GHDL transcripts. The Aldec procedure is documentation-based; vendor-tool testing is pending.

Prerequisites and test scope

You should understand synchronous logic, VHDL processes and basic arithmetic. The project uses VHDL-2008, ieee.std_logic_1164, ieee.numeric_std and std.env.finish. Reproducing the executed test requires an existing GHDL installation and Python 3 without additional packages. The described Aldec path requires an authorised Active-HDL or Riviera-PRO installation with suitable VHDL support. The download includes no vendor software, licences or model-service credentials.

Testing was performed locally on Windows x64 with GHDL 6.0.0 and the mcode backend. The result report includes the complete version banner. Neither Aldec nor Sigasi was executed for this example. The design has one clock, reset, enable and an unsigned counter output. Its value before the first reset is unspecified. Control inputs are assumed to be binary and synchronous; unknown logic values and crossings between clock domains are outside this specification.

Step 1: Fix requirements before correcting the code

The unchanged requirements for this example
IDRequirementAcceptance
R1Active-high reset at the rising edge, with priority over enable.Reset with enable 0 and 1, including from a nonzero value.
R2Increment by one when reset is 0 and enable is 1.A directed sequence of enabled clock cycles.
R3Hold when reset is 0 and enable is 0.Repeated disabled cycles before and after reset.
R4Wrap modulo 2 to the power of WIDTH.Two complete wraps, including maximum to zero.
R5Reset is synchronous.Asserting reset between edges must not yet change the output.

WIDTH is a positive parameter. The testbench exercises widths 1, 4 and 8; its reference arithmetic is explicitly limited to 1 through 8. The smallest width is useful because wraparound occurs after only two increments. A larger width checks that the implementation is genuinely parameterised. These selected values do not establish every possible configuration. Requirements and the derived testbench remain unchanged during the correction loop, so a patch cannot silently redefine what counts as success.

Step 2: Prepare the files and agent instruction

ai-counter/
  rtl/ai_counter.vhd
  tb/tb_ai_counter.vhd
  run.py
  README.md
  LICENSE
  results/report.json
  results/*.log

Download the complete example and actual GHDL results (ZIP). Extract it into a separate local working folder. Source files and runner use the MIT licence; no vendor files are included. Write your own results into a different output folder to retain the supplied reference results.

A suitable agent instruction names the five requirements, both source files, the RTL file as the only permitted edit target, and the checking procedure. For example: “Check the implementation against R1 through R5. Modify only rtl/ai_counter.vhd. Run the existing runner and report the source change, actual commands, version and exit code. Do not change expected values or assertions. If a tool is missing, a licence fails or the run is incomplete, stop and report the check as failed or not executed, as appropriate.”

Use a separate copy for experiments. The supplied runner constructs its deliberately faulty variants in temporary directories without modifying the supplied RTL file. This keeps the fault demonstration separate from the usable reference implementation. For your own AI-assisted change, also use version control and inspect the diff before execution, particularly any change to test scripts or expected results. The agent’s explanation is useful context, but the files and execution records remain the evidence.

Step 3: Understand the hardware description

The significant order inside the clocked process is reset first, followed by enable. The output receives the internal register value. ieee.numeric_std provides typed unsigned addition; the fixed width implements modular wraparound. Omitting an assignment in the disabled branch preserves the register value in this clocked process. The complete entity and architecture are supplied in rtl/ai_counter.vhd; the excerpt below shows the part that determines the behaviour being checked.

if rising_edge(clk) then
  if rst = '1' then
    count <= (others => '0');
  elsif en = '1' then
    count <= count + 1;
  end if;
end if;

Project analysis can first expose incorrect identifiers, type errors or missing dependencies. The Sigasi application note describes the documented agent path and its licensing boundaries. For this small project, that is an additional stage to evaluate separately. An accepted analysis report would not automatically confirm R4: a correctly written saturating counter would also be linguistically plausible while violating the required behaviour.

Step 4: Understand the testbench

Testbench and DUT: Derive the expectation independently
Two paths into the assertionRequirements R1 through R5 define expected values. Stimuli drive the counter inputs clk, rst and en. The assertion compares output q against the independent expected value one nanosecond after the rising edge. A watchdog stops an incomplete test.Requirements R1–R5Define expected values firstStimuli / tickclk · rst · enDUT: ai_counterRTL under testAssertionq = expected value?Independent expected valueqCheck: ↑ clk + 1 nsCompletion: PASS only after all assertions · Watchdog: 20 µs simulation time

The testbench checks specified behaviour; it does not use the implementation as its reference. The 1 ns wait belongs to the simulation sequence and is not a physical timing requirement.

The tick procedure drives reset and enable at a falling edge and checks the output one nanosecond after the next rising edge. That wait allows the model’s signal assignments to settle. It is not a physical timing requirement for the eventual circuit. The testbench compares each expected value with q and identifies the associated requirement on failure. Reset between edges has an additional check explicitly performed before the next rising edge.

tick('0', '1', 1, "R2 enabled increment");
tick('0', '0', 1, "R3 disabled hold");
tick('1', '1', 0, "R1 reset priority with enable");
-- The complete file also checks two wraps and synchronous reset.
-- PASS is emitted only after every assertion has succeeded.

Assertions report errors with severity failure. Only after every check does EDA_AI_COUNTER_PASS appear with the current width, followed by finish(0). A watchdog fails after 20 microseconds of simulation time if the test does not complete. The Python runner additionally limits each analysis, elaboration and simulation process to 30 seconds of real execution time. An unfinished simulation and a successful test therefore remain distinct states. GHDL runtime controls are described in the official simulation manual.

Expected timing of the first six checks

Derived from the testbench, WIDTH = 4. Inputs change at the falling edge; each check occurs 1 ns after the next rising edge.

Synchronous reset, increment and holdRising edges at 15, 25, 35, 45, 55 and 65 nanoseconds. Expected checked q values: 0, 1, 1, 1, 0, 1. At 55 nanoseconds, reset clears the counter despite active enable. q is unspecified before the first reset. The data table lists every checkpoint.15 ns25 ns35 ns45 ns55 ns65 nsclkrstenq?011101Check162636465666? = unspecified before first reset · q shown after signal updates55 ns: rst = 1 has priority over en = 1
Expected values; rst and en apply at the immediately preceding rising edge
Check time (ns)rstenExpected qBehaviour checked
16100Reset without enable
26011Increment
36001Hold
46001Hold again
56110Reset has priority
66011Increment again

Original schematic, not a new simulator capture or an Aldec test. The complete testbench contains the additional wraparound and reset checks.

Step 5: Simulate with Active-HDL

Open an authorised Active-HDL installation and create a new workspace and design in an empty evaluation folder. Add the two original VHDL files. Select VHDL-2008 in the compiler settings and compile the RTL first, then the testbench, into work. Select tb_ai_counter as the simulation top and start with WIDTH equal to 4. Initialise the simulation and enter set breakassertlevel 2 in the console.

Use Run All. Expect the complete PASS marker for WIDTH=4 and normal completion without assertion errors. Reinitialise and repeat for WIDTH=1 and WIDTH=8. For the negative check, change elsif en = '1' then to else in a scratch copy only. After recompilation and initialisation, R3 must fail and no PASS marker may appear. Restore the original file afterwards. Source for the control setting: Aldec assertion thresholds.

Aldec also documents the shell and batch sequence of library creation, compilation, initialisation and simulation. Confirm the compiler standard, options and process termination against the help shipped with your installed version before building a pipeline from it. The GUI procedure described here is actionable but was not tested locally with Aldec. Source: Active-HDL Batch Mode. Do not continue after errors merely to turn a failed run into an apparently successful completion.

Step 6: Riviera-PRO and automated failure handling

For Riviera-PRO, use the same functional procedure with the original files: a new design, VHDL-2008, RTL before testbench, tb_ai_counter as top, initialisation and a complete run. Its documented assertion control is vhdlassert.break error, a different command from Active-HDL’s setting. Stopping at an assertion does not by itself establish a nonzero operating-system exit code. Before connecting CI, verify that the deliberately faulty version also produces a failed job.

This vendor-tool run remains pending. During your evaluation, record the build number, edition, operating system, commands and behaviour of finish(0). If simulation only pauses, a licence is unavailable or the completion marker is absent, acceptance remains open. The published GHDL runner is not an untested Aldec adapter. It demonstrates a result-checking principle whose implementation must be tested separately for each product. Preserve both the positive and negative transcripts when you establish that integration.

Step 7: The local tests that were actually executed

python run.py --ghdl ghdl --output test-results

Run the runner from the extracted project directory. For one manual run, create the new manual-run subdirectory there and enter it; ../rtl and ../tb then refer to the supplied sources. Use a directory name that does not already exist so that no old work library is reused.

mkdir manual-run
cd manual-run
ghdl -a --std=08 ../rtl/ai_counter.vhd ../tb/tb_ai_counter.vhd
ghdl -e --std=08 tb_ai_counter
ghdl -r --std=08 tb_ai_counter -gWIDTH=4 --assert-level=error

On 19 October 2026, the three positive widths completed with exit code 0 and the matching marker. Four intentional variants compiled successfully and were then rejected by assertions: reset gated by enable, saturation instead of wraparound, ignored enable and asynchronous reset. All four returned exit code 1 without PASS. The runner explicitly refuses to count a compilation failure as successful detection of a behavioural fault. Individual transcripts and report.json are included in the download.

Interpretation, limits and the next step

When a check fails, read the named requirement, the last input combination and the expected value first. An R3 failure, for example, points to unwanted counting while enable is disabled. Inspect the smallest responsible RTL section and repeat the complete unchanged test. Adding a case can strengthen the suite; removing an assertion changes the meaning of the previous result. Keep the fault demonstration distinct from changes you intend to retain.

The result establishes behaviour for the specified stimuli and parameters. It does not establish synthesis support in a particular FPGA tool version, a clock-frequency target, CDC/RDC properties or complete correctness of every possible extension. Source checking, the actual GHDL run and human technical approval are separate states; no human technical sign-off is claimed. For an Aldec/Sigasi evaluation for your project, extend this small example with a representative case from your own design.

Would you like to apply this approach to your project?

Discuss suitable development tools

EDA PRAXIS / NEWSLETTER

Fresh perspectives.
Straight to your inbox.

Practical knowledge about HDL, simulation and verification. New articles, application notes and insights into useful tools.