I have a program with tasks.
I want to generate two random numbers,
First: between 0.5 and 3.5
Second: between 4.5 and 6.5
I have to implement it in a protected unit:
with Ada.Text_IO, Ada.Numerics.Float_Random;
use Ada.Text_IO, Ada.Numerics.Float_Random;
procedure simulation is
.
.
.
protected Helper is
procedure generateFirstRandomNumber;
procedure generateSecondRandomNumber;
end Helper;
.
.
.
protected helper is
procedure generateFirstRandomNumber is
begin
...
end generateFirstRandomNumber;
procedure generateSecondRandomNumber is
begin
...
end generateSecondRandomNumber;
end helper;
.
.
.
begin
.
.
.
end simulation;
I have two entry points where I have to pass those values.
So how can I implement that two random number generators in the protected unit?
This is a sketch of part of a solution.
protected Helper is
procedure Generate_First_Random_Number (Result : out Float);
procedure Generate_Second_Random_Number (Result : out Float);
First, I hope I don't tread on your toes, but this is a more standard Ada style (Title_Case_With_Underscores).
Second, you need to get the results out somehow. You can't use a function, because functions aren't allowed to change the internal state of the PO, and (part of) that internal state is the random number generator.
Third, I'd have declared subtypes to represent the ranges you have to provide: subtype Lower_Range is Float range 0.5 .. 3.5;, for example.
private
Gen : Ada.Numerics.Float_Random.Generator;
Internal variables have to be declared in the private part of the protected spec. And, as a general rule, it's better to use fully-qualified names, so it's obvious where the object's coming from (personally, I make an exception for Ada.Text_IO).
end Helper;
protected body Helper is
You have to use the word body here.
procedure Generate_First_Random_Number (Result : out Float)
is
Rnd : constant Float := Ada.Numerics.Float_Random.Random (Gen);
Rnd is in the range 0.0 .. 1.0,
begin
Result := 3.0 * Rnd + 0.5;
but you want a result in 0.5 .. 3.5.
end Generate_First_Random_Number;
... and similarly for the other procedure.
Since it appears to be for your studies, I'll only point you in the right direction. The Ada Language Reference Manual includes a detailed description of the Ada standard libraries. These happen to include provisions for generating random floating point values with an equal distribution in a well-defined range.
Related
I am mainly interested in stable sorting large arrays of strings. With real-world data I succeed in beating Tarray.sort by a factor 10-100 (even though the latter is not stable). As an exercise I tried to repeat this for simple types. Works fine. And then to bundle it into a generic class as in Tarray. Consider my starting point:
procedure sortArray(var values : array of byte); overload;
var temp : array of byte;
function getSequence(from: integer) : integer;
procedure doThis(...);
procedure doThat(...);
begin
setLength(temp,high(values)+1);
// actual code calling one of the procedures
end;
procedure sortArray(var values : array of double); overload;
var temp : array of double;
... etc ...
All the local subroutines have access to values and temp.
It seemed to me like an ideal situation to try out how generics work. In a generic class you can't have local subroutines, so these are all changed into methods. I then have:
type
TsimpleArray<T> = class
private
class function getSequence(var values,temp : array of T; from: integer) : integer;
class procedure doThis(var values,temp : array of T; ...);
...
public
class procedure sort(var values : array of T);
end;
Class functions can not have access to global temp storage, so that has to be passed as a parameter to all these methods, as well as the source array. All comaprisons if v>values[k] and the like to be changed into if Tcomparer<T>.default.compare(v,values[k])>0.
Well, it works. But it is now around 10 times slower then my simple procedure approach. (Still a lot faster than Tarray.sort.) I guess this is the prize we pay for better maintainable generic code. Or are there things I could do better?
I have a record type(REC) which contains an element of type VECTOR which is an unconstrained 2d array of STD_LOGIC:
package TEST is
type VECTOR is array (NATURAL range <>, NATURAL range <>) of STD_LOGIC;
type REC is record
data : VECTOR;
end record REC;
function con(
a : REC;
b : REC)
return REC;
end package TEST;
When declaring the body of the con function I want to specify the size of the REC.data array like so:
package body TEST is
function con(
a : REC;
b : REC)
return REC is variable r : REC(
data(a.data'length(1) + b.data'length(1) - 1 downto 0, a.data'length(2) - 1 downto 0));
begin
-- . . . do things
end function con;
end package body TEST;
but in the line where I attempt to set the size of data it Vivado throws the following error:
Sliced name is allowed only on single-dimensional arrays
Does this mean I cannot have an unconstrained 2d arraign in a record or is there a different method for defining the size of it in the con function ?
---- EDIT ----
Since I now understand that it is not possible to use 2d arrays in this context what method should I use to create a function which:
Takes two 2d arrays (or arrays of arrays) of size [x][y] and [z][y] as inputs
Outputs an array of size [x+z][y]
With both input arrays being unconstrained and the output array being constrained to size [x+z][y]
And all arrays (input and return) be a record type
user1155120 I don't think I fully understand what you are trying to say. Are you saying my code is not
a minimal reproducible example, because except for me forgetting to include the STD_LOGIC library
the code reproduces the problem when I paste it into Vivado, and it is about as minimal as I can get it.
Or are you saying that the code you linked as this works works for you, because at least in my
Vivado it still throws the same error ? – Mercury 4 hours ago
The original comment was meant for Tricky. There's a missing return statement in the function con which would prevent use in an example.
It isn't clear if record constraints (a -2008 feature) are supported in Vivado Simulator. In Vivado Synthesis Guide UG901 2020.1 we see all sorts of wonderful uses of record types from everything to inferring ram to record elements of record types. Xilinx has been busy.
It'd seems odd if unconstrained elements in record type declarations were supported but not record constraints (they're pretty much a package deal, pun aside). Note the comment called it a -2008 feature.
A record constraint is limited to supply the constraint of an element or subelement that is of an unconstrained type (typically arrays). VHDL has always been capable of supplying multi-dimensional array constraints. The language of the standard is particular based on the flexibility of the syntax.
This code is VHDL -2008 compliant and provides your record constraint:
library ieee; -- ADDED for MCVe
use ieee.std_logic_1164.all; -- ADDED
package TEST is
type VECTOR is array (NATURAL range <>, NATURAL range <>) of STD_LOGIC;
type REC is record
data: VECTOR;
end record REC;
function con (a: REC; b: REC) return REC;
end package TEST;
package body TEST is
function con (a: REC; b: REC) return REC is
variable r:
REC ( -- record constraint:
data (
natural range a.data'length(1) + b.data'length(1) - 1 downto 0,
natural range a.data'length(2) - 1 downto 0
)
);
begin
-- . . . do things
return r; -- ADDED required return statement
end function con;
end package body TEST;
You'll note the changes to the record constraint from yours is the addition of natural range before each range of the element data constraint.
From IEEE Std 1076-2008 5.3.3 Record types:
record_constraint ::=
( record_element_constraint { , record_element_constraint } )
record_element_constraint ::= record_element_simple_name element_constraint
From 6.3 Subtype declarations:
element_constraint ::=
array_constraint
| record_constraint
Here the element data is an array so an array_constraint is appropriate.
From 5.3.2 Array types, 5.3.2.1:
array_constraint ::=
index_constraint [ array_element_constraint ]
| ( open ) [ array_element_constraint ]
Because element data array elements are scalar (enumeration type STD_LOGIC) we follow index_constraint.
index_constraint ::= ( discrete_range { , discrete_range } )
discrete_range ::= discrete_subtype_indication | range
The code shown above uses a discrete subtype indication for the index ranges of the element data dimensions and successfully analyzes (compiles).
From 5.2.2 Scalar types, 5.2.2.1:
range ::=
range_attribute_name
| simple_expression direction simple_expression
direction ::= to | downto
The constraint in the question uses a range with simple expressions and direction.
So why did it produce the error message about multi-dimensional slices?
In 9. Expressions, 9.1 the BNF, simple_expression -> term -> factor -> primary -> name.
In 8. Names, 8.1 name -> slice_name, only multi-dimensional slices are not allowed semantically in 8.5 Slice names who's BNF tells us it's syntactically valid:
slice_name ::= prefix ( discrete_range )
The prefix of a slice shall be appropriate for a one-dimensional array object. The base type of this array type is the type of the slice.
and semantically the prefix data is not appropriate for a one-dimensional array object.
These comments provide bit more context for the problem although it isn't clear which version the record constraint you used in the reported eventual success:
#Mercury when you add a (VHDL) file to Vivado, it's by default set to use VHDL 93, but not 2008. Go
to the property Window and change the file type from VHDL to VHDL 2008. I'm not sure why it
prints the wrong error message in your case. (Vivado likes to confuse users with wrong error messages
...). Anyhow, it should have reported your feature is only supported in 2008 mode. – Paebbels 3 hours
ago
#Paebbels Thank you, that fixed the problem, maybe add it as a sub note to your response so I can
mark it as accepted answer. You just saved me hours of frustration. And I'm already getting familiar with
Vivado's shenanigans, my Favorit one of which is "ERROR close to ..." which has to be one of the most
useless error messages I have experience in a long time :) – Mercury 3 hours ago
As far as detecting -2008 source, there are no -1993 syntax errors and no -2008 new reserved word, delimiters, separators or graphics characters in either the primary unit or the secondary.
That leaves you at the mercy of semantic analysis which failed. You could also note the unconstrained record element wasn't reported during analysis of the package declaration. It occurred during evaluation of the variable r. All declared objects are required to be constrained. VHDL doesn't have a recital of all features, semantics can be restrictive as well. It's legal in places to have unconstrained elements and objects.
Associating semantics rules found in the text of the standard with an textual element of the particular declaration or statement can be tough and the squeaky wheel gets the lubricant. Record constraints are relatively new to VHDL implementations.
The problem appears to have been one of tool familiarity.
This is the definition of row and column merges from PoC's vectors package, see lines starting at 359:
function slm_merge_rows(slm1 : T_SLM; slm2 : T_SLM) return T_SLM is
constant ROWS : positive := slm1'length(1) + slm2'length(1);
constant COLUMNS : positive := slm1'length(2);
variable slm : T_SLM(ROWS - 1 downto 0, COLUMNS - 1 downto 0);
begin
for i in slm1'range(1) loop
for j in slm1'low(2) to slm1'high(2) loop -- WORKAROUND: Xilinx iSIM work-around, because 'range(2) evaluates to 'range(1); see work-around notes at T_SLM type declaration
slm(i, j) := slm1(i, j);
end loop;
end loop;
for i in slm2'range(1) loop
for j in slm2'low(2) to slm2'high(2) loop -- WORKAROUND: Xilinx iSIM work-around, because 'range(2) evaluates to 'range(1); see work-around notes at T_SLM type declaration
slm(slm1'length(1) + i, j) := slm2(i, j);
end loop;
end loop;
return slm;
end function;
function slm_merge_cols(slm1 : T_SLM; slm2 : T_SLM) return T_SLM is
constant ROWS : positive := slm1'length(1);
constant COLUMNS : positive := slm1'length(2) + slm2'length(2);
variable slm : T_SLM(ROWS - 1 downto 0, COLUMNS - 1 downto 0);
begin
for i in slm1'range(1) loop
for j in slm1'low(2) to slm1'high(2) loop -- WORKAROUND: Xilinx iSIM work-around, because 'range(2) evaluates to 'range(1); see work-around notes at T_SLM type declaration
slm(i, j) := slm1(i, j);
end loop;
for j in slm2'low(2) to slm2'high(2) loop -- WORKAROUND: Xilinx iSIM work-around, because 'range(2) evaluates to 'range(1); see work-around notes at T_SLM type declaration
slm(i, slm1'length(2) + j) := slm2(i, j);
end loop;
end loop;
return slm;
end function;
The definition of T_SLM is:
type T_SLM is array(natural range <>, natural range <>) of std_logic;
Currently, I see no problem in nesting this code in another layer like your unconstrained record.
Edit:
Thie code above requires VHDL 2008 to be enabled in Vivado.
When VHDL files are added to Vivado, it's by default set to use VHDL 93, but not 2008. Go to the property Window and change the file type from VHDL to VHDL 2008``.
The printed error message is misleading and points to a false feature being used. The correct message should be it's a VHDL-2008 feature, please enable it.
I am currently doing the Ada tutorial from learn.adacore.com, and I am now at the second example: reading and outputting an integer. Since copy-pasting is for people who don't want to learn the syntax, I manually typed out most of the code (Some of it was generated by gnat-gps, but I'm now using vim).
I compiled and ran the program, and surprisingly, the second line of output is indented by roughly one tab. Why?
Here's the code:
With Ada.Text_IO;
Use Ada.Text_IO;
With Ada.Integer_Text_IO;
use Ada.Integer_Text_IO;
procedure Main is
N : Integer;
begin
-- Insert code here.
Put("Enter an integer value: ");
Get(N);
if N>0 then
Put (N);
Put_Line(" is a positive number");
end if;
end Main;
(how do I get syntax highlighting?)
Here is a sample of the output (the first 1 being input):
Enter an integer value: 1
1 is a positive number
The Put procedure from Ada.Integer_Text_IO uses a default field width padded with spaces.
The specification for that procedure is defined in the Ada Language Reference Manual as:
procedure Put(Item : in Num;
Width : in Field := Default_Width;
Base : in Number_Base := Default_Base);
The Width and Base parameters are given default values. Your call to Put only supplied a value for the formal parameter Item. To eliminate the left padding simply specify a desired width. I suggest you use Ada named notation for the call as in
Put(Item => N, Width => 1);
Found lots of questions on SO that refer to error messages or warnings about something that is not locally or globally static.
There are many questions that ask about getting rid of such error messages, but astonishly, nobody seems to have ever asked what static really means.
The IEEE standard talks about static expressions in §9.4, but the definition isn't easy to understand (as it is recursive and - funnily enough - thus appears to be static itself by definition).
Thought it would be valuable to have an SO question that answers all the implications of what static really means in VHDL in one single place.
as stated in the comments above, a static expression is basically something that can be fully evaluated at compile time (either during the compilation of the design unit it is defined in - than it is said to be locally static - or during the evaluation of the complete design which makes it globally static).
This basically makes a static expression a constant after evaluation ('compilation'). Very similar to constexpr in C++.
What are the implications of this for synthesis?
One interesting usage is - considering an innocent looking footnote in Chapter 9.4 of the standard:
The rules for globally static expressions imply that a declared constant or a generic may be initialized with an
expression that is not globally static, for example, with a call to an impure function. The resulting constant value may be
globally static, even though its initial value expression is not. Only interface constant, variable, and signal declarations
require that their initial value expressions be static expressions.
that you can actually use VHDL statements that are not synthesisable (like, e.g., pointers or real numbers) as long as they fulfill the static requirements, i.e. can be fully evaluated into synthesisable constructs at compile time:
architecture rtl of sintab is
type sinus_table_type is array(natural range <>) of signed(31 downto 0);
function sinus_table(start_value, end_value, step : real) return sinus_table_type is
constant table_size : natural :=
integer(ieee.math_real.ceil(
end_value - start_value) / step);
variable sintab : sinus_table_type(0 to table_size - 1);
begin
for i in sintab'low to sintab'high loop
sintab(i) := to_signed(integer(
ieee.math_real.sin(start_value + real(i) * step) *
32767.0), sintab(i)'length);
end loop;
return sintab;
end function sinus_table;
function isin(sintab : sinus_table_type; us : signed) return signed is
variable ret : signed(us'range);
begin
ret := sintab(to_integer(us mod sintab'length));
return ret;
end function isin;
constant PI : real := 3.1415926;
constant stab : sinus_table_type := sinus_table(0.0, 2.0 * PI, 0.01);
begin
calc : process
begin
wait until rising_edge(clk);
res <= isin(stab, arg);
end process calc;
end architecture rtl;
In the above example, non-synthesisable expressions (using ieee.math_real, generally not synthesisable), are used to construct a constant array of scaled signed values as a lookup table, fulfilling the requirement of static as these expressions can be fully evaluated at compile time.
I have following problem with my function which should return a random numer. When I want to generate a couple of numbers by calling that function, they are exactly the same. How can I fix the problem of returning the same number all the time when I call the function? I need that random to keep in function.
Here is the code:
with Ada.Numerics.discrete_Random
function generate_random_number ( n: in Positive) return Integer is
subtype Rand_Range is Integer range 0 .. n;
package Rand_Int is new Ada.Numerics.Discrete_Random(Rand_Range);
use Rand_Int;
gen : Rand_Int.Generator;
ret_val: Rand_Range;
begin
Rand_Int.Reset(gen);
ret_val := Random(gen);
return ret_val;
end;
The random generator gen should not be local to the function. Currently you are creating it anew on each generate_random_number call, and initialising it, so it's not surprising you always get the same result.
If you make gen - for instance - a global variable, initialised once, then each time you use it you'll get a new random number. (Yes, global variables are bad : but see below)
Unfortunately this does not play particularly well with the semantics of function generate_random_number ( n: in Positive) which can restrict the range of the random number differently each time. The simplest solution is to make gen return any valid integer and use modular arithmetic to return a number in the correct range for each call. This will work but you should be aware that it potentially introduces cryptographic weaknesses, beyond my skills to analyze.
If that is the case you will need a different approach, for example creating a different random generator for each range you need; taking care to seed (reset) them differently otherwise there may again be crypto weaknesses such as correlations between different generators.
Now global variables are poor structure for all the usual reasons in any language. So a much better way is to make it a resource by wrapping it in a package.
package RandGen is
function generate_random_number ( n: in Positive) return Positive;
end RandGen;
And that is all the client needs to see. The package is implemented as follows:
with Ada.Numerics.discrete_Random;
package body RandGen is
subtype Rand_Range is Positive;
package Rand_Int is new Ada.Numerics.Discrete_Random(Rand_Range);
gen : Rand_Int.Generator;
function generate_random_number ( n: in Positive) return Integer is
begin
return Rand_Int.Random(gen) mod n; -- or mod n+1 to include the end value
end generate_random_number;
-- package initialisation part
begin
Rand_Int.Reset(gen);
end RandGen;
EDIT : Jacob's suggestion of rejecting out-of-range values is better but inefficient if n is much smaller than the generator range. One solution might be to create several generators and let the generate_random_number function choose the one which covers 0 .. N with least waste.
The Random procedure with only one parameter is supposed to initiate the random number generator with a "time-dependent state". However, if it is called more than once rapidly in succession, it is very likely that the "time" that the program uses will be the same each time, since the program is likely to run faster than the clock resolution. (Try your test with something like delay 0.5 in between the two uses of generate_random_number; most likely you will get two different results.)
In any event, the intended use of a random number generator is to use Reset on it once to set the seed, and then generate a sequence of random numbers starting with the seed. By doing this, you can also use the same seed every time in order to get a duplicatable sequence of random numbers, for testing; or you can use a time-dependent seed. However, resetting the random number generator every time you want a random number is not the normal or recommended way of using RNG's. Therefore, you need to move the Reset call out of generate_random_number. Unfortunately, this also means that you have to use the same generator even if the n parameter will be different on every call, which means you may be forced to use Float_Random instead of Discrete_Random.
P.S. Looking into it further, I found this in G.2.5: "Two different calls to the time-dependent Reset procedure shall reset the generator to different states, provided that the calls are separated in time by at least one second and not more than fifty years." [Emphasis mine.] I'm sure that when you tried it, the calls were less than one second apart.
Assuming that the upper limit can be determined when you start the program:
Package Random (specification):
with Generic_Random;
with Upper_Limit_Function;
package Random is new Generic_Random (Upper_Limit => Upper_Limit_Function);
Generic package Generic_Random (specification):
generic
Upper_Limit : Positive;
package Generic_Random is
subtype Values is Natural range 0 .. Upper_Limit;
function Value return Values;
end Generic_Random;
Generic package Generic_Random (body):
with Ada.Numerics.Discrete_Random;
package body Generic_Random is
package Random is new Ada.Numerics.Discrete_Random (Values);
Source : Random.Generator;
function Value return Values is
begin
return Random.Random (Source);
end Value;
begin
Random.Reset (Source);
end Generic_Random;