VHDL.vhd
A synthesizable entity where one rising clock edge is one sample - plus a testbench and a GHDL makefile.
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.fixed_pkg.all; use ieee.fixed_float_types.all; use ieee.math_real.all; -- sin/cos for simulation-only source blocks library work; use work.icore_pkg.all; entity DPT_Feedback_Discrete is port ( clk : in std_logic; -- one tick == one sample period rst : in std_logic; blk2_gain : in Fx_matrix(0 to 0, 0 to 0); in_sig0 : in Fx_matrix(0 to 0, 0 to 0); sig0 : out Fx_matrix(0 to 0, 0 to 0); sig1 : out Fx_matrix(0 to 0, 0 to 0); sig2 : out Fx_matrix(0 to 0, 0 to 0); sig3 : out Fx_matrix(0 to 0, 0 to 0); sig4 : out Fx_matrix(0 to 0, 0 to 0) ); end entity DPT_Feedback_Discrete; architecture rtl of DPT_Feedback_Discrete is type Signals_t is record sig0 : Fx_matrix(0 to 0, 0 to 0); sig1 : Fx_matrix(0 to 0, 0 to 0); sig2 : Fx_matrix(0 to 0, 0 to 0); sig3 : Fx_matrix(0 to 0, 0 to 0); sig4 : Fx_matrix(0 to 0, 0 to 0); end record; constant SIGNALS_ZERO : Signals_t := ( sig0 => (others => (others => to_fx(0.0))), sig1 => (others => (others => to_fx(0.0))), sig2 => (others => (others => to_fx(0.0))), sig3 => (others => (others => to_fx(0.0))), sig4 => (others => (others => to_fx(0.0))) ); signal sig : Signals_t := SIGNALS_ZERO; signal uh_blk3 : Fx_matrix(0 to 0, 0 to 0) := (others => (others => to_fx(0.0))); signal yh_blk3 : Fx_matrix(0 to 0, 0 to 0) := (others => (others => to_fx(0.0))); begin -- Observable signal outputs sig0 <= sig.sig0; sig1 <= sig.sig1; sig2 <= sig.sig2; sig3 <= sig.sig3; sig4 <= sig.sig4; exec : process(clk) variable s : Signals_t; variable acc : Fx; variable acc2 : Fx; variable iacc : integer; begin if rising_edge(clk) then if rst = '1' then s := SIGNALS_ZERO; uh_blk3 <= (others => (others => to_fx(0.0))); yh_blk3 <= (others => (others => to_fx(0.0))); else s := sig; -- seed from last committed state acc := to_fx(0.0); acc2 := to_fx(0.0); iacc := 0; -- blk0: ICore Blocks/Home/DPT_Feedback_Discrete/In1 s.sig0 := in_sig0; -- blk1: ICore Blocks/Home/DPT_Feedback_Discrete/Error for i in 0 to 0 loop for j in 0 to 0 loop s.sig1(i, j) := resize(s.sig0(i, j) - s.sig3(i, j), s.sig1(i, j)); end loop; end loop; -- blk2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain for i in 0 to 0 loop for j in 0 to 0 loop s.sig2(i, j) := resize(s.sig1(i, j) * blk2_gain(0, 0), s.sig2(i, j)); end loop; end loop; -- blk3: ICore Blocks/Home/DPT_Feedback_Discrete/Plant acc := to_fx(0.0); acc := resize(acc + to_fx(0.0) * s.sig2(0, 0), acc); acc := resize(acc + to_fx(0.40000000000000002) * uh_blk3(0, 0), acc); acc := resize(acc - to_fx(-0.59999999999999998) * yh_blk3(0, 0), acc); s.sig3(0, 0) := acc; uh_blk3(0, 0) <= s.sig2(0, 0); yh_blk3(0, 0) <= acc; -- blk4: ICore Blocks/Home/DPT_Feedback_Discrete/Out1 s.sig4 := s.sig3; end if; sig <= s; -- commit end if; end process; end architecture rtl;
What Deploy writes for the reference model below, with only the file's
header banner removed. The tunable parameters and the external input are input ports, the signals are output
ports, and the entire ordered pass sits in one clocked process: it seeds a variable from the
last committed state, runs the five blocks, and commits once at the end - so one rising edge is
exactly one sample, and the feedback path reads the previous commit. Values are Fx,
the Q16.16 subtype from the generated icore_pkg.vhd, and every arithmetic result
is resized back into its signal.
From the diagram to your VHDL build.
Deploy writes the folder, ICore checks it against the simulation, and your code calls it once per sample.
Deploy writes
icore_pkg.vhd<name>.vhd<name>_tb.vhdMakefile (GHDL)
Built, run and compared
Built with GHDL, 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 VHDL.
one rising clk edge = one sampleWhere VHDL fits.
The model becomes an entity clocked at your sample rate: raise clk, and
the whole diagram has advanced one step. The generated icore_pkg.vhd holds the
shared types and the fixed-point helpers the entity uses.
Fixed point, and what it costs
Every signal is carried in Q16.16 - sixteen integer bits including sign, sixteen fractional. That is a range of about ±32768 and a step of 1.5e-5. Unlike the software targets, whose residual against the simulation sits around 1e-11 %, an HDL residual is set by that quantum: the arithmetic is not wrong, it is coarser. Coefficients matter more than you might expect - rounding a transfer function's coefficients can move its DC gain by more than the quantum alone suggests, which is worth checking before you blame the generator.
The verifier builds and runs the testbench under GHDL and compares it against the simulation exactly as it does for the software targets.
| 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.