Verilog.v
A clocked module, a testbench and an Icarus makefile - the same model your software target runs, in gates.
`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
What Deploy writes for the reference model below, with only the file's
header banner removed. 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.
From the diagram to your Verilog build.
Deploy writes the folder, ICore checks it against the simulation, and your code calls it once per sample.
Deploy writes
icore_defs.vh<name>.v<name>_tb.vMakefile (Icarus)
Built, run and compared
Built with Icarus Verilog (iverilog, vvp), run across the simulation window and compared with the solver sample by sample, against a 1 % tolerance.
One call, one sample
Numbers are Q16.16 fixed point, and 749 of 806 library blocks export to Verilog.
one clk edge = one sampleWhere Verilog fits.
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.
| Target class | Tolerance | Observed |
|---|---|---|
| Software · 7 languages | 0.1 % | ≈1e-11 % |
| HDL · Q16.16 | 1 % | ≈1e-3 % |
One model, ten targets.
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 and an output,
with the plant's output fed back into the junction. The export-verification suite calls it
DPT_Feedback_Discrete - which is where the names in the file come
from.
See it run on your own model.
Download the application from the customer portal, or read the documentation first - the manual, a page for every block, and the full command reference are public.