In Ada, how do I get the current time in seconds as a value I can print out and perform operations with? - time

I am doing a project for an operating systems class. I need to write a program that prints out the current time every ten seconds but also accounts for the delay of the overhead so that it does not drift when it has been running for a long time. I need it to be up to at least 1 decimal place as well.
I am stuck on step 1 as I can't figure out how to get the current time in seconds as a value. I have searched but could only find out how to get the current time in the HH:MM:SS format.
Thanks

Here's what I came up with:
writing_test.ads
package Writing_Test is
protected Writer is
entry write( Text : String; New_Line : Boolean:= True );
end Writer;
task Timer is
entry Start;
entry Pause;
entry Stop;
end Timer;
private
Timer_Frequency : constant Duration:= 10.0;
end Writing_Test;
writing_test.adb
with
Ada.Calendar,
Ada.Text_IO;
package body Writing_Test is
protected body Writer is
entry write( Text : String; New_Line : Boolean:= True ) when True is
begin
Ada.Text_IO.Put( Text );
if New_Line then
Ada.Text_IO.New_Line;
end if;
end;
end Writer;
task body Timer is
Active,
Stop_Task : Boolean:= False;
Next_Time : Ada.Calendar.Time;
use type Ada.Calendar.Time;
begin
MAIN:
loop
if not Active then
select
accept Start do
Active:= True;
Next_Time:= Ada.Calendar.Clock + Timer_Frequency;
end Start;
or
terminate;
end select;
else
select
accept Pause do
Active:= False;
end Pause;
or
accept Stop do
Stop_Task:= True;
end Stop;
or
delay until Next_Time;
end select;
exit MAIN when Stop_Task;
if Active then
declare
Use Ada.Calendar;
Now : Time renames Clock;
Str : String renames
Day_Duration'Image( Ada.Calendar.Seconds(Now) );
--' Formatter-correction trick
begin
Writer.write(Text => Str);
Next_Time:= Next_Time + Timer_Frequency;
end;
end if;
end if;
end loop MAIN;
end Timer;
end Writing_Test;

Here is a simple program that does the "print every 10s" part of the job. You could easily use this code in a separate package.
with Ada.Text_IO;
with Ada.Calendar;
procedure Periodic_Printer is
task type My_Printer_Task is
end My_Printer_Task;
task body My_Printer_Task is
use Ada.Calendar; -- for the "-" and "+" operations on Time
Start_Time : Ada.Calendar.Time;
Next_Cycle : Ada.Calendar.Time;
Period : constant Duration := 10.0;
begin
Start_Time := Ada.Calendar.Clock;
Next_Cycle := Start_Time;
loop
Ada.Text_IO.Put_Line(Duration'Image(Ada.Calendar.Clock - Start_Time)); --'
-- You could use Next_Cycle instead of Ada.Calendar.Clock - Start_Time
-- so the printing does not depend of the time needed to do the elapsed
-- time calculation
delay 3.0; -- like a long operation, takes time......
-- This pattern assumes the each cycle last less than Period
-- If you cannot ensure that, you should consider improving
-- the pattern or reduce the computation load of each cycle
Next_Cycle := Next_Cycle + Period;
delay until Next_Cycle;
end loop;
end My_Printer_Task;
Printer : My_Printer_Task;
begin
delay 90.0; -- You can do your 'real work' here.
-- Unclean way to terminate a task, you should consider improve it for a
-- real world scenario
abort Printer;
Ada.Text_IO.Put_Line("End of program");
end Periodic_Printer;

Related

If-else statement with same conditions as other if-else statement do not produce same output

So I have to do an assigment where I have to write a counter which hase upcount mode = 1 -> increment = 5 and downcount mode = 0 -> decrement = -9. There is an invalid value of -69 which the counter should jump over.
There are also upper and lower bounds: -250 to 248.
To test our counter, a testbench was given.
I used the following if-else statement inside a process with the clk signal as sensitivity list entry.
if((cnt_intern + 5) <= 248) then
cnt_intern <= cnt_intern + 5;
end if;
if(cnt_intern = -69) then
cnt_intern <= cnt_intern + 5;
end if;
this did not work as it set the cnt_intern to -69, which the second if statement should prevent. I rewrote the if statement to the following:
if(cnt_intern <= 243) then#
if(cnt_intern = -73) then
cnt_intern <= cnt_intern + 10;
else
cnt_intern <= cnt_intern + 5;
end if;
end if;
This time it did work and it jumped over the vlaue -69 directly to -64.
Anyone knows why? what is wrong with the first way?
best regards
The reason for the observed behavior is that a signal assignment inside a process does not immediately change the signal value. Instead, a transaction is scheduled on the signal, which will take effect when the process suspends (i.e. when the end of the process or a wait statement is reached).
In your first example, if cnt_intern is -74 at the beginning of the process, the first if statement schedules as transaction, that means a change of the signal's value to -69 will take place at the end of the process if no other assignment schedules a transaction on cnt_intern. However, the actual value of cnt_intern stays -74 until the end of the process. So the second if statement will evaluate to false and do nothing. At the end of the process, the value of -69 is assigned to cnt_intern.
This concept seems confusing if you start working with hardware description languages, but it is essential.
You could use a helper variable to circumvent that issue and keep the code readable:
process(clk)
variable v_cnt_intern : integer;
begin
if rising_edge(clk) then
v_cnt_intern := cnt_intern;
if((v_cnt_intern + 5) <= 248) then
v_cnt_intern := v_cnt_intern + 5;
end if;
if (v_cnt_intern = -69) then
v_cnt_intern := v_cnt_intern + 5;
end if;
cnt_intern <= v_cnt_intern;
end if;
end process;
Other solutions are something like the code in your second example, which looks fine.

Random number generator in VHDL

I'm designing a test bench and I need to create a random sequence of bits for one of the system's inputs which is normally controlled by the user.
I also want this sequence of bits not being in the same order every time I run the simulation.
I cannot use a PRNG since its initial state will be predefined meaning it while produce the same numbers every time. I also used the uniform function but I had the same issue.
RAND_GEN : process(clk) is
variable seed1, seed2 : positive := 1;
variable re : real;
begin
if rising_edge(clk) then
uniform(seed1, seed2, re);
if (re < 0.5) then
rand_bit <= '0';
else
rand_bit <= '1';
end if;
end if;
end process;
Is there any alternatives for this problem?
Testing with randomly generated inputs is a powerful tehnique and is the technique commonly used to verify ICs these days. Normally, you would run a test with a known, per-determined seed, whereas you want to be able to generate a varying seed. So, you absolutely MUST record this seed when you run the test and provide a mechanism to run a test using this seed. Otherwise, when you find a bug, you will not be able to test whether you've fixed it. You might find it more useful to a a fixed set of tests with a smaller number of manually-generated seeds.
You could use the linux date command with the %s format specifier, which outputs the number of seconds since 1/1/1970 and redirect that to a file.
date +%s >! seed.txt
Then read the file, eg:
RAND_GEN : process(clk) is
variable seed1, seed2 : positive := 1;
variable re : real;
file F: TEXT;
variable L: LINE;
variable seed_RNG : boolean := false;
begin
if not seed_RNG then
file_open(F, "seed.txt", READ_MODE);
readline (F, L);
read (L, seed1); -- or seed2
file_close(F);
report "seed1 = " & integer'image(seed1);
seed_RNG := true;
end if;
if rising_edge(clk) then
uniform(seed1, seed2, re);
if (re < 0.5) then
rand_bit <= '0';
else
rand_bit <= '1';
end if;
end if;
end process;
I don't know anything of VHDL, but in general I try to avoid randomness in tests. Flaky unit tests, for example, are bad. What's the value of a test that fails only sometimes?
Anyway, supposed you really want to do it, do you have access to a timer? You can initialize the PRNG with the current time as seed. Not cryptographically safe, but probably for this use case good enough.
Just for the record, in case anyone needs something similar, I used the above ideas by creating a do file which first writes the date in a file and then runs the do file of the actual test bench which reads this number as suggested before.
set t [clock seconds]
set outputFile [open date.txt w]
puts $outputFile $t
close $outputFile
do testbench.do

Ada - Skipping Whitespace using look_ahead

I have a procedure, that in theory, should be skipping whitespace using a look_ahead loop. Problem is, it's not working, if there's any whitespace in the input file, it is adding it to the array of records. I think my logic is correct, but could use another pair of eyes to let me know what I'm missing, and why it's not working.
PROCEDURE Read(Calc: OUT Calculation) IS
EOL: Boolean;
C: Character;
I: Integer := 1;
BEGIN
LOOP
LOOP
Look_Ahead(C, EOL);
EXIT WHEN EOL or C /= ' ';
Get(C);
END LOOP;
EXIT WHEN ADA.Text_IO.END_OF_FILE;
Look_Ahead(C, EOL);
IF Is_Digit(C) THEN
Calc.Element(I).Kind := Number;
Get(Calc.Element(I).Int_Value);
ELSE
Calc.Element(I).Kind := Symbol;
Get(Calc.Element(I).Char_Value);
END IF;
Calc.Len := Calc.Len+1;
IF Calc.Element(I).Char_Value = '=' THEN
EXIT;
END IF;
I := I+1;
END LOOP;
END Read;
EDIT: If any of the other procedures, the code for the record etc is needed for an answer, let me know and I will post it.
For Ada.Text_IO.Look_Ahead, ARM A.10.7(8) says
Sets End_Of_Line to True if at end of line, including if at end of page or at end of file; in each of these cases the value of Item is not specified. Otherwise, End_Of_Line is set to False and Item is set to the next character (without consuming it) from the file.
(my emphasis) and I think the "without consuming it" is key. Once Look_Ahead has found an interesting character, you need to call Get to retrieve that character.
I hacked this little demo together: I left end-of-file to exception handling, and I called Skip_Line once end-of-line’s been seen because just Get wasn’t right (sorry not to be more precise!).
with Ada.Text_IO;
with Ada.IO_Exceptions;
procedure Justiciar is
procedure Read is
Eol: Boolean;
C: Character;
begin
-- Instead of useful processing, echo the input to the output
-- replacing spaces with periods.
Outer:
loop
Inner:
loop
Ada.Text_IO.Look_Ahead (C, Eol);
exit Outer when Eol; -- C is undefined
exit Inner when C /= ' ';
Ada.Text_IO.Get (C); -- consume the space
Ada.Text_IO.Put ('.'); -- instead of the space for visibility
end loop Inner;
Ada.Text_IO.Get (C); -- consume the character which isnt a space
Ada.Text_IO.Put (C); -- print it (or other processing!)
end loop Outer;
Ada.Text_IO.Skip_Line; -- consume the newline
Ada.Text_IO.New_Line; -- clear for next call
end Read;
begin
loop
Ada.Text_IO.Put ("reading: ");
Read;
end loop;
exception
when Ada.IO_Exceptions.End_Error =>
null;
end Justiciar;
Usually it's better to read an entire line and parse it than to try to parse character by character. The latter is usually more complex, harder to understand, and more error prone. So I'd suggest something like
function De_Space (Source : String) return String is
Line : Unbounded_String := To_Unbounded_String (Source);
begin -- De_Space
Remove : for I in reverse 1 .. Length (Line) loop
if Element (Line, I) = ' ' then
Delete (Source => Line, From => I, Through => I);
end if;
end loop Remove;
return To_String (Line);
end De_Space;
Line : constant String := De_Space (Get_Line);
You can then loop over Line'range and parse it. Since I'm not clear if
Get(C);
Get(Calc.Element(I).Int_Value);
Get(Calc.Element(I).Char_Value);
represent 1, 2, or 3 different procedures, I can't really help with that part.

VHDL syn_looplimit and synthesis

I have a problem in synthesis with my VHDL code : I am trying to get the logarithm value of an input signal S_ink:
My code :
entity ....
....
architecture rtl of myEntity is
attribute syn_looplimit : integer;
attribute syn_looplimit of loopabc : label is 16384;
logcalc:process(I_clk)
variable temp : integer;
variable log : integer;
begin
if(I_clk'event and I_clk='1') then
if (IN_rst='0') then
S_klog<=0;
temp:=0;
log:=0;
else
temp := S_ink+1; --S_ink is an input of my entity (integer)
log:=0;
loopabc:while (temp/=0) loop
temp:=temp/2;
log :=log+1;
end loop loopabc;
S_klog<=3*log;
end if;
end if;
end process;
It works very well in simulation but doesn't synthesize.
The error message is : "While loop is not terminating. You can set the maximum of loop iterations with the syn_looplimit attribute"
However, this code synthesize (but that is not what I want)
entity ....
....
architecture rtl of myEntity is
attribute syn_looplimit : integer;
attribute syn_looplimit of loopabc : label is 16384;
logcalc:process(I_clk)
variable temp : integer;
variable log : integer;
begin
if(I_clk'event and I_clk='1') then
if (IN_rst='0') then
S_klog<=0;
temp:=0;
log:=0;
else
temp := 3000; -- a random constant value
log:=0;
loopabc:while (temp/=0) loop
temp:=temp/2;
log :=log+1;
end loop loopabc;
S_klog<=3*log;
end if;
end if;
end process;
When the synthesis tool translates the design, it will make a circuit with a topology that does not depend on the data values, but where the wires carries the data values. The circuit must have a fixed calculation latency between each level of flip-flops, so timing analysis can determine if the amount of logic between flip-flops can fit for the specified frequency. In this process any loops are unrolled, and you can think of this as converting the loop to a long sequence of ordinary (non-loop) statements. To do this unrolling, the synthesis tool must be able to determine the number of iterations in the loops, so it can repeated the loop body this number of times when doing loop unrolling.
In the first code example the number of iterations in the loop depends on the S_ink value, so the synthesis tool can't unroll the loop to a fixed circuit, since the circuit depends on the data value.
In the second code example the synthesis tool can determine the number of iterations in the loop, thus do the unrolling to a fixed circuit.
One way to address this is make the algorithm with a fixed number of iteration, where this number of iterations can handle the worst case input data, and where any superfluous iteration on other input data will not change the result.
Solution :
process(I_clk)
variable temp : integer;
variable log : integer;
begin
if(I_clk'event and I_clk='1') then
if (IN_rst='0') then
S_klog<=0;
temp:=0;
log:=0;
else
temp := S_ink+1;
log:=0;
for I in 1 to 14 loop
temp := temp/2;
if (temp /=0) then
log :=log+1;
end if;
end loop;
S_klog<=3*log; -- 3*log because of my application
end if;
end if;
end process;

Best way to modify strings in VHDL

I'm currently writing a test bench for a VHDL design I made and I need to write a message to a text file. The message is of the format
[instance_name];[simulation_time]
(i.e. U0;700 ns) and the filename must be [instance_name].log. Getting the instance name and simulation time is no problem, but writing to a custom filename has been problematic. Under simulation, the instance name will be given in the format:
"U0\ComponentX\test\"
and I would like to replace the slashes with underscores. Is there an easy way to do this?
Our PoC Library has quite a big collection on string operations/functions. There is a str_replace function in PoC.strings that should solve your question. There is also the PoC.utils package with non string related functions, that could also be helpful in handling strings and file I/O.
A simple implementation:
function replace(str : STRING) return STRING
variable Result : STRING(str'range) := str;
begin
for i in str'range loop
if (Result(i) = '\') then
Result(i) := '_';
end if;
loop;
return Result;
end function;
Usage:
constant original : STRING := "U0\ComponentX\test\";
constant replaced : STRING := replace(original);
Simple replace character function that is a bit more versatile and does the same job would be (nothing wrong with #Paebbels's answer)
function fReplaceChar(
a : character;
x : character;
s : string) return string
is
variable ret : string(s'range) := s;
begin
for i in ret'range loop
if(ret(i) = a) then
ret(i) := x;
end if;
end loop;
return ret;
end function fReplaceChar;
If there are more than one character to replace, one can always stack the function:
function fReplaceChar(
a : character;
b : character;
x : character;
s : string) return string
is
begin
return fReplaceChar(b, x, fReplaceChar(a, x, s));
end function fReplaceChar;
or function call:
fReplaceChar(')','_',fReplaceChar(':','(','_',tb'instance_name));
So for example:
process
begin
report lf & tb'instance_name & lf &
fReplaceChar(')','_',fReplaceChar(':','(','_',tb'instance_name));
wait;
end process;
gives:
# ** Note:
# :tb(sim):
# _tb_sim__

Resources