How can I compute a random value with multimodal distribution? - random

I would like to compute a random value with a multimodal distribution composed of N normal distributions.
I have an array with N elements of normal distribution parameters (std deviation, mean).
My language (VHDL) and my library allow me to calculate the following basic distributions:
- uniform distribution [-1.0; 1.0]
- normal distribution (Box Muller transformation)
- Poisson distribution
How can I calculate random values so that the histogram looks like N overlapping normal distributions?
Types and helpers:
type T_NORMAL_DIST_PARAM is record
StdDev : REAL;
Mean : REAL;
end record;
type T_JITTER_DIST is array(NATURAL range <>) of T_NORMAL_DIST_PARAM;
constant JitterDistribution : T_JITTER_DIST := (
0 => (0.2, -0.4),
1 => (0.2, 0.4)
);
The problems core:
procedure getMultiModalDistributedRandomNumber(Seed : T_SIM_SEED; Value : REAL; JitterDistribution : T_JITTER_DIST) is
variable rand : REAL;
variable Result : REAL;
begin
-- ...
for i in JitterDistribution'range loop
getNormalDistributedRandomValue(Seed, rand, JitterDistribution(i).StdDev, JitterDistribution(i).Mean);
-- how to accumulate rand?
end loop;
Value := Result;
end procedure;
It's used in:
procedure genClock(signal Clock : out STD_LOGIC; Period : TIME) is
constant TimeHigh : TIME := Period / 2;
constant TimeLow : TIME := Period - TimeHigh;
variable rand : REAL;
begin
initializeSeed(Seed);
while (not isStopped) loop
getMultiModalDistributedRandomNumber(Seed, rand, JitterDistribution);
Clock <= '1';
wait for TimeHigh + (Period * rand);
Clock <= '0';
wait for TimeLow;
end loop;
end procedure;

Related

VHDL: Generate a generic case statement with adjustable amount of cases

I want an approximation of the Tanh function by saving the values in a LUT (by this I am doing a quantization). I want to choose the Number of entries in the LUT.
As an not-correct example, I imagine a code like
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use ieee.fixed_pkg.all;
entity tanh_lut is
generic (
MIN_RANGE: real := 0.0; -- Minimum value of x
MAX_RANGE: real := 5.0; -- Maximum value of x
DATA_RANGE_int: positive:= 8;
DATA_RANGE_frac: positive:= 8;
);
Port ( DIN : in sfixed(DATA_RANGE_int-1 downto -(DATA_RANGE_frac-1));
DOUT : out sfixed(DATA_RANGE_int-1 downto -(DATA_RANGE_frac-1))
end tanh_lut;
architecture Behavioral of tanh_lut is
begin
lut_gen: for i in 0 to LUT_SIZE-1 generate
constant x_val : real := MIN_RANGE + (MAX_RANGE - MIN_RANGE) * i / (LUT_SIZE-1);
constant x_val_next : real := MIN_RANGE + (MAX_RANGE - MIN_RANGE) * (i+1) / (LUT_SIZE-1);
constant y_val : real := tanh(x_val);
if DIN>=x_val_previous AND DIN<x_val then
DOUT <= to_sfixed(tanh(y_val),DOUT ) ;
END IF
end generate;
end Behavioral;
Per example, if I want 4 entries in the range 0 to 3, I want that it is synthesizing a code like:
if DIN>0 AND DIN<=1 then
DOUT <= to_sfixed(0, DOUT);
else DIN>1 AND DIN<=2 then
DOUT <= to_sfixed(0.76159415595, DOUT);
else DIN>2 AND DIN<=3 then
DOUT <= to_sfixed(0.96402758007, DOUT);
else DIN>3 AND DIN<=4 then
DOUT <= to_sfixed(0.99505475368, DOUT);
End if
Is there any way that a code like this or a code which implements the idea behind this is possible?
A simple LUT with addresses is not possible because the addresses are always integer and DIN is fixed point, e.g., 1.5
The other possibility would be two LUTs, one for mapping the Input to an address, another for mapping the address to the LUT entry, e.g., LUT1: 1.5=> address 5, LUT2: address 5 => 0.90. But by this I would double the amount of resources what I dont want
My requirements: things like the tanh(x) should not be synthesized, only the final value of tanh(x). It shoudl also be hardware efficient
It does not matter if you use a nested „if-elsif“ construct or if you use a new „if“ construct for each check.
So you can create a loop like this:
for i in 0 to c_number_of_checks-1 loop
if c_boundaries(i)<DIN and DIN<=c_boundaries(i+1) then
DOUT <= c_output_values(i);
end if;
end loop;
Of course you must provide the constants c_number_of_checks and c_boundaries, c_output_values. This can be done by:
constant c_number_of_checks : natural := 4;
type array_of_your_data_type is array (natural range <>) of your_data_type;
constant c_boundaries : array_of_your_data_type(c_number_of_checks downto 0) := init_c_boundaries(c_number_of_checks);
constant c_output_values : array_of_your_data_type(c_number_of_checks-1 downto 0) := init_c_output_values(c_number_of_checks);
This means you will need the functions init_c_boundaries, init_c_output_values, which create arrays of values, which can initialize the constant c_boundaries and c_output_values.
But this is not complicated (you can use from ieee.math_real the function TANH), as the functions need not to be synthesizable, as they are called only during compile time.
As you see, you will have some effort. So perhaps it is easier to follow the other suggestions. If you do so (value as address of a LUT) you should think about automatic ROM inference, which is provided by several tool chains and will give you a very efficient (small) hardware.

AWGN channel in VHDL testbench

I want to create AWGN channel in VHDL testbench. Is it possible? and how can I proceed? I searched online but not many related posts.
Regards
Yes, you can.
How to generate a uniform distributed value in VHDL is described here. Quote (original by "Vipin Lal"):
library ieee;
use ieee.math_real.all;
entity rand_gen is end rand_gen;
architecture behavior of rand_gen is
signal rand_num : integer := 0;
begin
process
variable seed1, seed2: positive; -- seed values for random generator
variable rand: real; -- random real-number value in range 0 to 1.0
variable range_of_rand : real := 1000.0; -- the range of random values created will be 0 to +1000.
begin
uniform(seed1, seed2, rand); -- generate random number
rand_num <= integer(rand*range_of_rand); -- rescale to 0..1000, convert integer part
wait for 10 ns;
end process;
end behavior;
Then you have to scale the output of the uniform function to a AWGN distribution. An example in C is given here. Quote(summarized from original by "Dr Cagri Tanriover")
temp2 = ( rand() / ( (double)RAND_MAX ) );
temp1 = cos( ( 2.0 * (double)PI ) * rand() / ( (double)RAND_MAX ) );
result = sqrt( -2.0 * log( temp2 ) ) * temp1;
Just correctly convert this to VHDL. The math_real library contains all functions and constants required.

VHDL pass range to procedure

I'm writing my own package to deal with generic matrix-like objects due to unavailability of VHDL-2008 (I'm only concerned with compilation and simulation for the time being).
My aim is getting a matrix M_out from a matrix M_in such that:
M_out(i downto 0, j downto 0) <= M_in(k+i downto k, l+j downto l);
using a subroutine of sort. For, let's say, semantic convenience and analogy with software programming languages my subroutine prototype should ideally look something like this:
type matrix is array(natural range <>, natural range <>) of std_logic;
...
procedure slice_matrix(signal m_out: out matrix;
constant rows: natural range<>;
constant cols: natural range<>;
signal m_in: in matrix);
The compiler does however regard this as an error:
** Error: custom_types.vhd(9): near "<>": syntax error
** Error: custom_types.vhd(9): near "<>": syntax error
Is it possible to pass a range as an argument in some way or shall I surrender and pass 4 separate indexes to calculate it locally?
An unconstrained index range natural range <> is not a VHDL object of class signal, variable, constant, or file. Thus it can not be passed into a subprogram. I wouldn't implement a slice operations as a procedure, because it's a function like behavior.
An implementation for working with matrices and slices thereof is provided by the PoC-Library. The implementation is provided in the vectors package.
function slm_slice(slm : T_SLM; RowIndex : natural; ColIndex : natural; Height : natural; Width : natural) return T_SLM is
variable Result : T_SLM(Height - 1 downto 0, Width - 1 downto 0) := (others => (others => '0'));
begin
for i in 0 to Height - 1 loop
for j in 0 to Width - 1 loop
Result(i, j) := slm(RowIndex + i, ColIndex + j);
end loop;
end loop;
return Result;
end function;
More specialized functions to slice off a row or column can be found in that file too. It also provides procedures to assign parts of a matrix.
This package works in simulation and synthesis.
Unfortunately, slicing multi dimensional arrays will not be part of VHDL-2017. I'll make sure it's discuss for VHDL-202x again.
Passing ranges into a subprogram will be allowed in VHDL-2017. The language change LCS 2016-099 adds this capability.

Random Generator using UNIFORM

I am trying to generate a random number for a little game and I tried to use the UNIFORM function from the package MATH_REAL.
I get a value when I test my code, but it never changes. I tried to make the code happen at every rising_edge but it didn't change anything. I couldn't find or think of an effective fix.
I think I know that problem is simply that my random number is generated only once, but I couldn't figure out a way to change this.
Here is what I implemented (my variation with the clock) :
rand: process (count1)
variable seed1, seed2 : positive;
variable rand : real;
variable int_rand : integer range 1 to 5;
begin
if rst_i='1' then
count1 <= 1;
elsif rising_edge(clk_i) then
UNIFORM(seed1, seed2, rand);
int_rand := INTEGER(TRUNC(rand*5.0));
count1 <= int_rand;
end if;
end process;
The value of count1 is then used in another process. The above code gives the same simulation results as simply :
rand: process (count1)
variable seed1, seed2 : positive;
variable rand : real;
variable int_rand : integer range 1 to 5;
begin
UNIFORM(seed1, seed2, rand);
int_rand := INTEGER(TRUNC(rand*5.0));
count1 <= int_rand;
end process;
Is there a simple way to ensure more that a random number is generated more than once?
When the purpose is to generate a random number on count1 for every rising edge of clk_i, then update the sensitivity list for the process to (assuming async reset by rst_i):
rand: process (rst_i, clk_i)
This will make the process reexecute at every change of rst_i or clk_i, where the sensitivity list of count1 reexecutes at change of count1 only.
Note that variable int_rand : integer range 1 to 5; should be range 0 to 4, since uniform result is > 0.0 and < 1.0.
An interesting behavior is, that a sensitivity list of rand: process (count1) as for the second process, without the if guarding generation on count1, will actually make the process reexecute several times start of simulation, until the generated count1 value is identical to the previous count1 value, in which case the is no change on count1 to trigger reexecution of the process, and then process is then not executed any more.

Can Vivado handle user defined physical types?

I wrote some cross platform VHDL libraries for Xilinx XST, iSim, Altera Quartus II, Mentor Graphics QuestaSim and GHDL. Now I wanted to port my ISE 14.7 project, which uses these libraries to Vivado 2014.4, but one library seems to have fatal problems.
My library physical defines several new user defined physical types like: FREQUENCY and BAUD; conversion functions and report functions.
One main use case is the calculation of delay or counter cycles for a given delay and system frequency. So for example a delay of 125 ns requires 12 or 13 delay cycles at 100 MHz (it depends on the rounding mode at .5).
I get several infos and warnings from Vivado Synth (some are results of an assert statement, see minimal example below):
[Synth 8-638] synthesizing module 'Top_PhysicalTest' ["D:/Temp/PhysicalTest_Vivado2014.4/vhdl/Top_PhysicalTest.vhd":410]
[Synth 8-63] RTL assertion: "to_time: f= 2147483647.1000 THz return 2147483647.1000 sec" ["D:/Temp/PhysicalTest_Vivado2014.4/vhdl/Top_PhysicalTest.vhd":277]
[Synth 8-63] RTL assertion: "res_real: 0.000000" ["D:/Temp/PhysicalTest_Vivado2014.4/vhdl/Top_PhysicalTest.vhd":321]
[Synth 8-63] RTL assertion: "TimingToCycles:
Timing: 2147483647.1000 sec
Clock_Period: 2147483647.1000 sec
RoundingStyle: TO_NEAREST
res_real = 2147483647.1000
=> 0" ["D:/Temp/PhysicalTest_Vivado2014.4/vhdl/Top_PhysicalTest.vhd":323]
[Synth 8-26] 'image of non-integer, non-enum type not implemented ["D:/Temp/PhysicalTest_Vivado2014.4/vhdl/Top_PhysicalTest.vhd":422]
[Synth 8-63] RTL assertion: "CLOCK_FREQ: <complex-type>" ["D:/Temp/PhysicalTest_Vivado2014.4/vhdl/Top_PhysicalTest.vhd":422]
[Synth 8-63] RTL assertion: "CLOCK_FREQ: 2147483647.1000 THz" ["D:/Temp/PhysicalTest_Vivado2014.4/vhdl/Top_PhysicalTest.vhd":423]
[Synth 8-26] 'image of non-integer, non-enum type not implemented ["D:/Temp/PhysicalTest_Vivado2014.4/vhdl/Top_PhysicalTest.vhd":424]
[Synth 8-63] RTL assertion: "DELAY: <complex-type>" ["D:/Temp/PhysicalTest_Vivado2014.4/vhdl/Top_PhysicalTest.vhd":424]
[Synth 8-63] RTL assertion: "DELAY: 2147483647.1000 sec" ["D:/Temp/PhysicalTest_Vivado2014.4/vhdl/Top_PhysicalTest.vhd":425]
[Synth 8-63] RTL assertion: "CYCLES: 0" ["D:/Temp/PhysicalTest_Vivado2014.4/vhdl/Top_PhysicalTest.vhd":426]
[Synth 8-256] done synthesizing module 'Top_PhysicalTest' (1#1) ["D:/Temp/PhysicalTest_Vivado2014.4/vhdl/Top_PhysicalTest.vhd":410]
[Synth 8-3330] design Top_PhysicalTest has an empty top module
[Synth 8-3331] design Top_PhysicalTest has unconnected port Clock
[Synth 8-3330] design Top_PhysicalTest has an empty top module
[Synth 8-3331] design Top_PhysicalTest has unconnected port Clock
[Project 1-571] Translating synthesized netlist
My scenario is a bit complex, so the minimal example look not as minimal as it should look. I did not inline every function to prevent copy/replace errors and I did not remove the debug and assert/report routines.
Overview:
Package utils: common types, enums and functions
Package strings: string manipulation and conversion functions
Package physical: the new type and it's functions
Entity: a single top-level entity implementing a simple delay element / shift-register
Minimal example:
-- EMACS settings: -*- tab-width: 2; indent-tabs-mode: t -*-
-- vim: tabstop=2:shiftwidth=2:noexpandtab
-- kate: tab-width 2; replace-tabs off; indent-width 2;
--
-- ============================================================================
-- Package: Common functions and types
--
-- Authors: Thomas B. Preusser
-- Martin Zabel
-- Patrick Lehmann
--
-- License:
-- ============================================================================
-- Copyright 2007-2015 Technische Universitaet Dresden - Germany
-- Chair for VLSI-Design, Diagnostics and Architecture
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-- ============================================================================
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;
package utils is
-- rounding style
type T_ROUNDING_STYLE is (ROUND_TO_NEAREST, ROUND_TO_ZERO, ROUND_TO_INF, ROUND_UP, ROUND_DOWN);
function ite(cond : BOOLEAN; value1 : STRING; value2 : STRING) return STRING;
function imin(arg1 : integer; arg2 : integer) return integer;
function imax(arg1 : integer; arg2 : integer) return integer;
function log2ceil(arg : positive) return natural;
function log2ceilnz(arg : positive) return positive;
end package utils;
package body utils is
function ite(cond : BOOLEAN; value1 : STRING; value2 : STRING) return STRING is
begin
if cond then
return value1;
else
return value2;
end if;
end function;
function imin(arg1 : integer; arg2 : integer) return integer is
begin
if arg1 < arg2 then return arg1; end if;
return arg2;
end function;
function imax(arg1 : integer; arg2 : integer) return integer is
begin
if arg1 > arg2 then return arg1; end if;
return arg2;
end function;
function log2ceil(arg : positive) return natural is
variable tmp : positive := 1;
variable log : natural := 0;
begin
if arg = 1 then return 0; end if;
while arg > tmp loop
tmp := tmp * 2;
log := log + 1;
end loop;
return log;
end function;
function log2ceilnz(arg : positive) return positive is
begin
return imax(1, log2ceil(arg));
end function;
end utils;
-- ============================================================================
-- Package: String related functions and types
--
-- Authors: Thomas B. Preusser
-- Martin Zabel
-- Patrick Lehmann
--
-- =============================================================================
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;
use IEEE.MATH_REAL.all;
use work.utils.all;
package strings is
function raw_format_nat_dec(value : NATURAL) return STRING;
function str_format(value : REAL; precision : NATURAL := 3) return STRING;
FUNCTION resize(str : STRING; size : POSITIVE; FillChar : CHARACTER := NUL) RETURN STRING;
function str_length(str : STRING) return NATURAL;
function str_trim(str : STRING) return STRING;
function str_substr(str : STRING; start : INTEGER := 0; length : INTEGER := 0) return STRING;
end package strings;
package body strings is
-- raw_format_* functions
function raw_format_nat_dec(value : NATURAL) return STRING is
begin
return INTEGER'image(value);
end function;
-- str_format_* functions
function str_format(value : REAL; precision : NATURAL := 3) return STRING is
constant s : REAL := sign(value);
constant int : INTEGER := integer((value * s) - 0.5); -- force ROUND_DOWN
constant frac : INTEGER := integer((((value * s) - real(int)) * 10.0**precision) - 0.5); -- force ROUND_DOWN
constant res : STRING := raw_format_nat_dec(int) & "." & raw_format_nat_dec(frac);
begin
-- assert (not MY_VERBOSE)
-- report "str_format:" & CR &
-- " value:" & REAL'image(value) & CR &
-- " int = " & INTEGER'image(int) & CR &
-- " frac = " & INTEGER'image(frac)
-- severity note;
return ite((s < 0.0), "-" & res, res);
end function;
-- resize
FUNCTION resize(str : STRING; size : POSITIVE; FillChar : CHARACTER := NUL) RETURN STRING IS
CONSTANT MaxLength : NATURAL := imin(size, str'length);
VARIABLE Result : STRING(1 TO size) := (OTHERS => FillChar);
BEGIN
if (MaxLength > 0) then
Result(1 TO MaxLength) := str(str'low TO str'low + MaxLength - 1);
end if;
RETURN Result;
END FUNCTION;
-- String functions
FUNCTION str_length(str : STRING) RETURN NATURAL IS
VARIABLE l : NATURAL := 0;
BEGIN
FOR I IN str'range LOOP
IF (str(I) = NUL) THEN
RETURN l;
ELSE
l := l + 1;
END IF;
END LOOP;
RETURN str'length;
END FUNCTION;
function str_trim(str : STRING) return STRING is
constant len : NATURAL := str_length(str);
begin
if (len = 0) then
return "";
else
return resize(str, len);
end if;
end function;
function str_substr(str : STRING; start : INTEGER := 0; length : INTEGER := 0) return STRING is
variable StartOfString : positive;
variable EndOfString : positive;
begin
if (start < 0) then -- start is negative -> start substring at right string boundary
StartOfString := str'high + start + 1;
elsif (start = 0) then -- start is zero -> start substring at left string boundary
StartOfString := str'low;
else -- start is positive -> start substring at left string boundary + offset
StartOfString := start;
end if;
if (length < 0) then -- length is negative -> end substring at length'th character before right string boundary
EndOfString := str'high + length;
elsif (length = 0) then -- length is zero -> end substring at right string boundary
EndOfString := str'high;
else -- length is positive -> end substring at StartOfString + length
EndOfString := StartOfString + length - 1;
end if;
if (StartOfString < str'low) then report "StartOfString is out of str's range. (str=" & str & ")" severity error; end if;
if (EndOfString < str'high) then report "EndOfString is out of str's range. (str=" & str & ")" severity error; end if;
return str(StartOfString to EndOfString);
end function;
end strings;
-- ============================================================================
-- Package: This VHDL package declares new physical types and their
-- conversion functions.
--
-- Authors: Patrick Lehmann
--
-- ============================================================================
library IEEE;
use IEEE.MATH_REAL.all;
use work.utils.all;
use work.strings.all;
package physical is
type FREQ is range 0 to INTEGER'high units
Hz;
kHz = 1000 Hz;
MHz = 1000 kHz;
GHz = 1000 MHz;
THz = 1000 GHz;
end units;
constant C_PHYSICAL_REPORT_TIMING_DEVIATION : BOOLEAN := TRUE;
function to_time(f : FREQ) return TIME;
function to_real(t : TIME; scale : TIME) return REAL;
function to_real(f : FREQ; scale : FREQ) return REAL;
function TimingToCycles(Timing : TIME; Clock_Period : TIME; RoundingStyle : T_ROUNDING_STYLE := ROUND_TO_NEAREST) return NATURAL;
function TimingToCycles(Timing : TIME; Clock_Frequency : FREQ; RoundingStyle : T_ROUNDING_STYLE := ROUND_TO_NEAREST) return NATURAL;
function to_string(t : TIME; precision : NATURAL := 3) return STRING;
function to_string(f : FREQ; precision : NATURAL := 3) return STRING;
end physical;
package body physical is
-- iSim 14.7 does not support fs in simulation by default (fs values are converted to 0 ps)
-- activate fs support by overriding the time precision
-- fuse[.exe] [...] -timeprecision_vhdl 1fs [...]
function MinimalTimeResolutionInSimulation return TIME is
begin
if (1 fs > 0 sec) then return 1 fs;
elsif (1 ps > 0 sec) then return 1 ps;
elsif (1 ns > 0 sec) then return 1 ns;
elsif (1 us > 0 sec) then return 1 us;
elsif (1 ms > 0 sec) then return 1 ms;
else return 1 sec;
end if;
end function;
-- real division for physical types
function div(a : TIME; b : TIME) return REAL is
constant MTRIS : TIME := MinimalTimeResolutionInSimulation;
begin
if (a < 1 us) then
return real(a / MTRIS) / real(b / MTRIS);
elsif (a < 1 ms) then
return real(a / (1000 * MTRIS)) / real(b / MTRIS) * 1000.0;
elsif (a < 1 sec) then
return real(a / (1000000 * MTRIS)) / real(b / MTRIS) * 1000000.0;
else
return real(a / (1000000000 * MTRIS)) / real(b / MTRIS) * 1000000000.0;
end if;
end function;
function div(a : FREQ; b : FREQ) return REAL is
begin
return real(a / 1 Hz) / real(b / 1 Hz);
end function;
-- conversion functions
function to_time(f : FREQ) return TIME is
variable res : TIME;
begin
if (f < 1.0 kHz) then res := div(1.0 Hz, f) * 1.0 sec;
elsif (f < 1.0 MHz) then res := div(1.0 kHz, f) * 1.0 ms;
elsif (f < 1.0 GHz) then res := div(1.0 MHz, f) * 1.0 us;
elsif (f < 1.0 THz) then res := div(1.0 GHz, f) * 1.0 ns;
else res := div(1.0 THz, f) * 1.0 ps;
end if;
assert FALSE report "to_time: f= " & to_string(f) & " return " & to_string(res) severity note;
return res;
end function;
-- convert physical types (TIME, FREQ) to standard type (REAL)
function to_real(t : TIME; scale : TIME) return REAL is
begin
if (scale = 1.0 fs) then return div(t, 1.0 fs);
elsif (scale = 1.0 ps) then return div(t, 1.0 ps);
elsif (scale = 1.0 ns) then return div(t, 1.0 ns);
elsif (scale = 1.0 us) then return div(t, 1.0 us);
elsif (scale = 1.0 ms) then return div(t, 1.0 ms);
elsif (scale = 1.0 sec) then return div(t, 1.0 sec);
else report "to_real: scale must have a value of '1.0 <unit>'" severity failure;
end if;
end;
function to_real(f : FREQ; scale : FREQ) return REAL is
begin
if (scale = 1.0 Hz) then return div(f, 1.0 Hz);
elsif (scale = 1.0 kHz) then return div(f, 1.0 kHz);
elsif (scale = 1.0 MHz) then return div(f, 1.0 MHz);
elsif (scale = 1.0 GHz) then return div(f, 1.0 GHz);
elsif (scale = 1.0 THz) then return div(f, 1.0 THz);
else report "to_real: scale must have a value of '1.0 <unit>'" severity failure;
end if;
end;
-- calculate needed counter cycles to achieve a given 1. timing/delay and 2. frequency/period
-- ===========================================================================
-- #param Timing A given timing or delay, which should be achived
-- #param Clock_Period The period of the circuits clock
-- #RoundingStyle Default = round to nearest; other choises: ROUND_UP, ROUND_DOWN
function TimingToCycles(Timing : TIME; Clock_Period : TIME; RoundingStyle : T_ROUNDING_STYLE := ROUND_TO_NEAREST) return NATURAL is
variable res_real : REAL;
variable res_nat : NATURAL;
begin
res_real := div(Timing, Clock_Period);
case RoundingStyle is
when ROUND_TO_NEAREST => res_nat := natural(round(res_real));
when ROUND_UP => res_nat := natural(res_real + 0.5);
when ROUND_DOWN => res_nat := natural(res_real);
when others => report "RoundingStyle '" & T_ROUNDING_STYLE'image(RoundingStyle) & "' not supported." severity failure;
end case;
report "res_real: " & REAL'image(res_real) severity note;
assert FALSE
report "TimingToCycles: " & CR &
" Timing: " & to_string(Timing) & CR &
" Clock_Period: " & to_string(Clock_Period) & CR &
" RoundingStyle: " & str_substr(T_ROUNDING_STYLE'image(RoundingStyle), 7) & CR &
" res_real = " & str_format(res_real, 3) & CR &
" => " & INTEGER'image(res_nat)
severity note;
return res_nat;
end;
function TimingToCycles(Timing : TIME; Clock_Frequency : FREQ; RoundingStyle : T_ROUNDING_STYLE := ROUND_TO_NEAREST) return NATURAL is
begin
return TimingToCycles(Timing, to_time(Clock_Frequency), RoundingStyle);
end function;
-- convert and format physical types to STRING
function to_string(t : TIME; precision : NATURAL := 3) return STRING is
variable unit : STRING(1 to 3) := (others => NUL);
variable value : REAL;
begin
if (t < 1.0 ps) then
unit(1 to 2) := "fs";
value := to_real(t, 1.0 fs);
elsif (t < 1.0 ns) then
unit(1 to 2) := "ps";
value := to_real(t, 1.0 ps);
elsif (t < 1.0 us) then
unit(1 to 2) := "ns";
value := to_real(t, 1.0 ns);
elsif (t < 1.0 ms) then
unit(1 to 2) := "us";
value := to_real(t, 1.0 us);
elsif (t < 1.0 sec) then
unit(1 to 2) := "ms";
value := to_real(t, 1.0 ms);
else
unit := "sec";
value := to_real(t, 1.0 sec);
end if;
return str_format(value, precision) & " " & str_trim(unit);
end function;
function to_string(f : FREQ; precision : NATURAL := 3) return STRING is
variable unit : STRING(1 to 3) := (others => NUL);
variable value : REAL;
begin
if (f < 1.0 kHz) then
unit(1 to 2) := "Hz";
value := to_real(f, 1.0 Hz);
elsif (f < 1.0 MHz) then
unit := "kHz";
value := to_real(f, 1.0 kHz);
elsif (f < 1.0 GHz) then
unit := "MHz";
value := to_real(f, 1.0 MHz);
elsif (f < 1.0 THz) then
unit := "GHz";
value := to_real(f, 1.0 GHz);
else
unit := "THz";
value := to_real(f, 1.0 THz);
end if;
return str_format(value, precision) & " " & str_trim(unit);
end function;
end package body;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use work.utils.all;
use work.strings.all;
use work.physical.all;
entity Top_PhysicalTest is
Port (
Clock : in STD_LOGIC;
Input : in STD_LOGIC;
Output : out STD_LOGIC
);
end;
architecture rtl of Top_PhysicalTest is
-- configuration
constant CLOCK_FREQ : FREQ := 100 MHz;
constant SHIFTER_DELAY : TIME := 125 ns;
-- calculations
constant SHIFTER_DELAY_CYCLES : NATURAL := TimingToCycles(SHIFTER_DELAY, CLOCK_FREQ);
constant SHIFTER_BITS : NATURAL := SHIFTER_DELAY_CYCLES + 2; -- to prevent an underrun, while Vivado has a bug
signal Shifter_nxt : STD_LOGIC_VECTOR(SHIFTER_BITS - 1 downto 0);
signal Shifter_d : STD_LOGIC_VECTOR(SHIFTER_BITS - 2 downto 0) := (others => '0');
begin
assert false report "CLOCK_FREQ: " & FREQ'image(CLOCK_FREQ) severity note;
assert false report "CLOCK_FREQ: " & to_string(CLOCK_FREQ) severity note;
assert false report "DELAY: " & TIME'image(SHIFTER_DELAY) severity note;
assert false report "DELAY: " & to_string(SHIFTER_DELAY) severity note;
assert false report "CYCLES: " & INTEGER'image(SHIFTER_DELAY_CYCLES) severity note;
Shifter_nxt <= Shifter_d & Input;
Shifter_d <= Shifter_nxt(Shifter_d'range) when rising_edge(Clock);
Output <= Shifter_nxt(SHIFTER_DELAY_CYCLES);
end;
UDPT ::= user defined physical type
My observations:
synthesis runs through without any error but causes false calculations
'image is not implemented -> How should one debug this?
seems to hold a value of INTEGER'high
Questions:
How can I fix this for Vivado?
Can anyone confirm this behavior?
Which VHDL standard (87, 93, ...) introduced physical types?
Notice: Move my question from CR to SO by hand.
Edit 1:
I stripped the errors down to:
decimal literals in physical types are false handled -> result is 0 in every case
attribute 'image(..) on physical types is not implemented any more
operations with physical types, incl. comparisons results in false values
value ranges of physical types are not monotone
The issues are reported in the Xilinx forum.
THz is out of range for the minimum range of type Time (see IEEE Std 1076-2008, 16.3 Package standard, "type INTEGER is range implementation_defined;", 5.2.3 Integer types "...An implementation may restrict the bounds of the range constraint of integer types other than type universal_integer. However, an implementation shall allow the declaration of any integer type whose range is wholly contained within the bounds –2147483647 and +2147483647 inclusive.", type FREQ).
Counting from the top, line:pos 272:65, 298:55, 384:51.
I'm not surprised at your results.
How can I fix this for Vivado?
Prescalers. Use two integers, one for a fraction part of some integer unit. Alternatively use a bit array type, which shifts the problem to one of string conversion.
It seems unlikely you'll get Xilinx to support 64 bit integers for ranges as in the type declaration:
type FREQ is range 0 to INTEGER'high units
Hz;
kHz = 1000 Hz;
MHz = 1000 kHz;
GHz = 1000 MHz;
THz = 1000 GHz;
end units;
Can anyone confirm this behavior?
The arithmetic works out, no Vivaldo. It says 32 bit integers are supported.
Which VHDL standard (87, 93, ...) introduced physical types?
As Bill Lynch notes physical types are found in IEEE Std 1076-1987, 3.1.3 Physical types, Page 3-5.
This demonstrates the advantages of using a VHDL analyzer/simulator to validate a design specification before synthesis:
ghdl -a top_physicaltest.vhdl
top_physicaltest.vhdl:272:65: static constant violates bounds
top_physicaltest.vhdl:298:55: static constant violates bounds
top_physicaltest.vhdl:384:51: static constant violates bounds
Using ghdl-0.31 I increased the HIGH bound of FREQ:
type FREQ is range 0 to 2**61 units
This works because ghdl's universal integer is 64 bit and physical types can have a universal integer range.
(There's a bug in ghdl, should work with the integer equivalent of 2**63 -1, Tristan's fixed it I think for ghdl-0.33. I happended to know 2**61 is a safe bound).
That analyzes. You could note that the Real Range limits the accuracy of any scaling your perform. Without checking I couldn't tell you if ghdl has a 64 bit universal Real to match it's 64 bit universal integer.
So then analyze, elaborate and run Top_PhysicalTest (the test bench).
ghdl -r top_physicaltest
./top_physicaltest:error: bound check failure at physical.vhdl:117
ghdl: compilation error
-- raw_format_* functions
function raw_format_nat_dec(value : NATURAL) return STRING is
begin
return INTEGER'image(value);
end function;
-- str_format_* functions
function str_format(value : REAL; precision : NATURAL := 3) return STRING is
constant s : REAL := sign(value);
constant int : INTEGER := integer((value * s) - 0.5); -- force ROUND_DOWN
constant frac : INTEGER := integer((((value * s) - real(int)) * 10.0**precision) - 0.5); -- force ROUND_DOWN
constant res : STRING := raw_format_nat_dec(int) & "." & raw_format_nat_dec(frac); -- LINE 117
begin
-- assert (not MY_VERBOSE)
-- report "str_format:" & CR &
-- " value:" & REAL'image(value) & CR &
-- " int = " & INTEGER'image(int) & CR &
-- " frac = " & INTEGER'image(frac)
-- severity note;
return ite((s < 0.0), "-" & res, res);
end function;
Where Line 117 is the declaration for constant res in the package body for package strings.
And from std.standard:
subtype NATURAL is INTEGER range 0 to INTEGER'HIGH;
Type integer is 32 bit precision in ghdl (and universally in synthesis tools).
And that looks like a 'IMAGE error I may or may not have notified Tristan of (Yup!, see #31 Type conversion subtype constraint check not performed, it's fixed for 0.33, too (gee I'm going to skip 0.32 I think)).
So I tried with nvc, which also has 64 bit universal integers, and I had previously submitted some errors to do with expressions for:
nvc -a physical.vhdl
nvc -e Top_PhysicalTest
** Fatal: expression cannot be folded to an integer constant
File physical.vhdl, Line 412
And Line 412 is in Top_PhysicalTest:
constant CLOCK_FREQ : FREQ := 100 MHz;
constant SHIFTER_DELAY : TIME := 125 ns;
-- calculations
constant SHIFTER_DELAY_CYCLES : NATURAL := TimingToCycles(SHIFTER_DELAY, CLOCK_FREQ);
constant SHIFTER_BITS : NATURAL := SHIFTER_DELAY_CYCLES + 2; -- to prevent an underrun, while Vivado has a bug
constant SHIFTER_BITS : NATURAL := SHIFTER_DELAY_CYCLES + 2; -- to prevent an underrun, while Vivado has a bug
signal Shifter_nxt : STD_LOGIC_VECTOR(SHIFTER_BITS - 1 downto 0);
The declaration for signal Shifter_nxt.
(And which looks like a different type of error).
I seems like Xilinx Vivado 2019.2 is going to fix physical types and especially TIME.
From Vivado 2019.2 release notes:

Resources