Verilog

A clocked module, a testbench and an Icarus makefile — the same model your software target runs, in gates.

Deploy writes
  • icore_defs.vh
  • <name>.v
  • <name>_tb.v
  • Makefile (Icarus)
One call, one sample

one clk edge = one sample

Verified with

Icarus Verilog (iverilog, vvp)

Numbers

Q16.16 fixed point

Library blocks

306 of 308

The diagram becomes a module driven by clk: one edge, one sample. The generated icore_defs.vh carries the shared widths and fixed-point definitions.

Signals are Q16.16 fixed point, so the residual against the simulation is bounded by the 1.5e-5 quantum rather than by the generator. The verifier builds the testbench with Icarus Verilog and runs it with vvp.

Because the same diagram also exports to C or Rust, the usual FPGA workflow — prove the algorithm in software, then move it to fabric — stays one model rather than two implementations to keep in step.

What the export looks like

Every target page shows the same model, so the ten are directly comparable: an input, an error junction, a gain of 1.8, a discrete plant 0.4z⁻¹ / (1 − 0.6z⁻¹), an output — and the plant's output fed back into the junction. Five blocks, in a diagram the export-verification suite calls DPT_Feedback_Discrete — which is where the names in the file come from. Below is what Deploy writes for this target, with only the file's header banner removed.

DPT_Feedback_Discrete.v

`include "icore_defs.vh"

module DPT_Feedback_Discrete (
    input clk,
    input rst,
    input [1*`ICORE_WIDTH-1:0] blk2_gain,
    input [1*`ICORE_WIDTH-1:0] in_sig0,
    output [1*`ICORE_WIDTH-1:0] sig0,
    output [1*`ICORE_WIDTH-1:0] sig1,
    output [1*`ICORE_WIDTH-1:0] sig2,
    output [1*`ICORE_WIDTH-1:0] sig3,
    output [1*`ICORE_WIDTH-1:0] sig4
);

    function real to_real;
        input signed [`ICORE_WIDTH-1:0] v;
        begin to_real = $itor(v) / (2.0 ** `ICORE_FRAC_BITS); end
    endfunction
    function signed [`ICORE_WIDTH-1:0] to_fx;
        input real v;
        begin to_fx = $rtoi(v * (2.0 ** `ICORE_FRAC_BITS) + (v >= 0.0 ? 0.5 : -0.5)); end
    endfunction
    function integer fx_to_int;
        input signed [`ICORE_WIDTH-1:0] v;
        begin fx_to_int = v >>> `ICORE_FRAC_BITS; end
    endfunction

    reg [1*`ICORE_WIDTH-1:0] r_sig0 = 0;
    reg [1*`ICORE_WIDTH-1:0] r_sig1 = 0;
    reg [1*`ICORE_WIDTH-1:0] r_sig2 = 0;
    reg [1*`ICORE_WIDTH-1:0] r_sig3 = 0;
    reg [1*`ICORE_WIDTH-1:0] r_sig4 = 0;

    assign sig0 = r_sig0;
    assign sig1 = r_sig1;
    assign sig2 = r_sig2;
    assign sig3 = r_sig3;
    assign sig4 = r_sig4;

    reg signed [`ICORE_WIDTH-1:0] uh_blk3 [0:0][0:0];
    reg signed [`ICORE_WIDTH-1:0] yh_blk3 [0:0][0:0];

    always @(posedge clk) begin : exec
        reg [1*`ICORE_WIDTH-1:0] w_sig0;
        reg [1*`ICORE_WIDTH-1:0] w_sig1;
        reg [1*`ICORE_WIDTH-1:0] w_sig2;
        reg [1*`ICORE_WIDTH-1:0] w_sig3;
        reg [1*`ICORE_WIDTH-1:0] w_sig4;
        reg signed [2*`ICORE_WIDTH-1:0] acc;
        if (rst) begin
            r_sig0 <= 0;
            r_sig1 <= 0;
            r_sig2 <= 0;
            r_sig3 <= 0;
            r_sig4 <= 0;
            uh_blk3[0][0] <= 0;
            yh_blk3[0][0] <= 0;
        end else begin
            w_sig0 = r_sig0;
            w_sig1 = r_sig1;
            w_sig2 = r_sig2;
            w_sig3 = r_sig3;
            w_sig4 = r_sig4;
            acc = 0;

            // blk0: ICore Blocks/Home/DPT_Feedback_Discrete/In1
            w_sig0 = in_sig0;
            // blk1: ICore Blocks/Home/DPT_Feedback_Discrete/Error
            w_sig1[0*`ICORE_WIDTH +: `ICORE_WIDTH] = $signed(w_sig0[0*`ICORE_WIDTH +: `ICORE_WIDTH]) - $signed(w_sig3[0*`ICORE_WIDTH +: `ICORE_WIDTH]);
            // blk2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain
            acc = $signed(w_sig1[0*`ICORE_WIDTH +: `ICORE_WIDTH]) * $signed(blk2_gain[0*`ICORE_WIDTH +: `ICORE_WIDTH]);
            w_sig2[0*`ICORE_WIDTH +: `ICORE_WIDTH] = acc >>> `ICORE_FRAC_BITS;
            // blk3: ICore Blocks/Home/DPT_Feedback_Discrete/Plant
            acc = 0;
            acc = acc + $signed(to_fx(0)) * $signed(w_sig2[0*`ICORE_WIDTH +: `ICORE_WIDTH]);
            acc = acc + $signed(to_fx(0.40000000000000002)) * $signed(uh_blk3[0][0]);
            acc = acc - $signed(to_fx(-0.59999999999999998)) * $signed(yh_blk3[0][0]);
            w_sig3[0*`ICORE_WIDTH +: `ICORE_WIDTH] = acc >>> `ICORE_FRAC_BITS;
            uh_blk3[0][0] <= w_sig2[0*`ICORE_WIDTH +: `ICORE_WIDTH];
            yh_blk3[0][0] <= acc >>> `ICORE_FRAC_BITS;
            // blk4: ICore Blocks/Home/DPT_Feedback_Discrete/Out1
            w_sig4 = w_sig3;
            r_sig0 <= w_sig0;
            r_sig1 <= w_sig1;
            r_sig2 <= w_sig2;
            r_sig3 <= w_sig3;
            r_sig4 <= w_sig4;
        end
    end

endmodule

The same clocked pass in Verilog: each signal is a packed bus, the blocking working copies (w_sig*) run the ordered pass, and the non-blocking commits at the end make one edge one sample. The plant multiplies into a double-width acc and shifts by ICORE_FRAC_BITS — that shift is where Q16.16 rounding actually happens, and where an HDL residual against the simulation comes from.

How it is checked

Every one of the ten targets is verifiable, and this one is no exception: the export is compiled with the toolchain above, run across the simulation window, and compared against the solver sample by sample. Software targets pass at around 1e-11 % against a 0.1 % tolerance; the HDL targets are bounded by their fixed-point quantum instead. See verification.

See also: Code export  ·  Multi-target, multi-rate deploy

Get started

See it run on your own model.

Download the application from the customer portal, or read the documentation first — the manual, every block with its measured response, and the full command reference are public.