Read file line of unknown size as string in VHDL - testing

I'm trying to make a Test Bench where a file of one single line, where posible characters are "1" and "0". I've to read them all, and use one by one as input in my DUT.
So, in my TB, I've defined a process like the following, in order to read the file an pass it values to my DUT.
stim_proc: process
file input_file: TEXT is in "DatosEntrada.dat";
variable rdline : LINE;
variable line_content : string ( 1 to 4);
variable readed_char : character;
variable j : integer := 0;
begin
while not endfile(input_file) loop
readline(input_file, rdline);
--read(rdline, line_content);
for j in 1 to rdline'length-1 loop
readed_char := line_content(j);
if (readed_char = '1') then
input <= '1';
else
input <= '0';
end if;
wait for clk_period;
end loop;
end loop;
end process;
I'm reading the first (and only) line of my file with the first readline execution. After this, this loop shouldn't execute again.
Then, data from file should be inside rdline. So I've to process it. In order to do it, I've tried to loop over rdline length, but this loop doesn't execute.
for j in 1 to rdline'length-1 loop
So I thought I need to read this line in order to loop over it, and tried to move its data to a string var. The problem is that vector var like string need to have a defined size, and I don't know the file line size.
I've tried reading each time 4 chars from rdline into a string, process it, then repeat. However, I couldn't make it work.
I've found quite lot examples about reading files which have defined line formats, like columns or expected integers.
But how can I read an unknown text of one line?

This readed_char := line_content(j); doesn't work when line_content isn't loaded. Otherwise your attempt to read values is basically sound.
The end of line is not contained in a read LINE buffer, there's no reason to not read the last character of rdline. An end of line is signaled by one or more format effectors other than horizontal tab, and just the line contents are present.
There's also this inference that you have some relationship to a clock edge and not just a clock period. The following example shows that. Note you can also supply an offset from an edge using a wait for time_value.
A loop constant is declared in the loop statement. The variable j you declared is not the same j the loop uses. The loop statement hides the j in the outer declarative region (the variable declaration in the process statement).
Your code treats any other character in the string buffer than '1' as a '0'. I didn't change that, do demonstrate it. You should be aware of the impact.
A LINE is an allocated string of some length dependent on the length of a line in your read file. Every time you call readline the string rdline points to is updated. It doesn't leak memory, the previous buffer rdline pointed to released. You can read the length by using the 'RIGHT attribute or as in this case simply consume all the characters.
There may be line length limits in a VHDL tool implementation. There are none defined in the standard other than the maximum length of a string (POSITIVE'RIGHT).
An MCVE:
library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
entity foo is
end entity;
architecture fum of foo is
signal input: std_logic ;
signal clk: std_logic := '0';
constant clk_period: time := 10 ns;
begin
stim_proc:
process
file input_file: TEXT is in "DatosEntrada.dat";
variable rdline: LINE;
-- variable line_content : string ( 1 to 4);
-- variable readed_char : character;
-- variable j: integer := 0;
begin
while not endfile(input_file) loop
readline(input_file, rdline);
--read(rdline, line_content);
-- for j in 1 to rdline'length - 1 loop -- EOL not in rdline
for j in rdline'range loop
-- readed_char := line_content(j);
-- if readed_char = '1' then
if rdline(j) = '1' then -- changed
input <= '1';
else
input <= '0';
end if;
-- wait for clk_period; -- sync to edge instead
wait until falling_edge(clk); -- input related to clk edge
end loop;
end loop;
wait; -- added prevents needless loops
end process;
CLOCK:
process
begin
wait for clk_period/2;
clk <= not clk;
if now > 32 * clk_period then
wait;
end if;
end process;
end architecture;
And for DatosEntrada.dat containing:
11011110001HELLO11230000
That produces:
Where you can see all non '1' characters are interpreted as '0'.

Related

Maple Sequence Length

I'm trying to create a basic program in Maple that runs the Collatz sequence when given a number (n) from the user. For those that don't know, the Collatz sequence is basically "If the number given is odd, do 3n + 1, if it is even, divide by 2 and continue to do so for every answer. Eventually, the answer will reach 1"
I'm trying to grab the number of iterations that the sequence is performed, say if the sequence is run through 10 times, it prints that out. Here is my current code:
Collatz := proc (n::posint)
if type(n, even) then (1/2)*n
else 3*n+1
end if
end proc
CollSeq := proc (n::posint)
local i;
i := n;
while 1 < i do
lprint(i);
i := Collatz(i)
end do
end proc
This so far works, and if the proc CollSeq(50) is entered, it will perform the Collatz sequence on 50 until it reaches 1. The bit I am stuck on is the length of the sequence. I have read around and learned that I might be able to use the nops([]) function of Maple to get the length of the sequence. Here is what I have tried:
CollLen := proc (n::posint)
local c;
c := CollSeq(n);
print(nops([c]))
end proc
I have a feeling this is horribly wrong. Any help would be much appreciated.
Many Thanks
Your function fails to return the actual sequence of values. You need to accumulate it as you go through the loop.
CollSeq := proc (n::posint)
local i, s;
i := n;
s := i;
while 1 < i do
lprint(i);
i := Collatz(i);
s := s, i;
end do;
s;
end proc
The lprint() command just prints its argument to the terminal (showing it on screen), it DOES not save it in a list. And nops() or a better command numelems() counts the number of elements in a list! So putting nops around something that has lprint will not count the number of things. Instead of using lprint in your second function (procedure), define a list, or better than list, an array and in the lprint-line, use a command to append the new number to your growing collection. If you want to see these numbers, just print this collection. Now this time, your third function can have a meaning and it will work as you expected.
Here is the closest fix to your codes.
Collatz := proc( n :: posint )
if type(n, even) then
return( n/2 ):
else
return( 3*n+1 ):
end if:
end proc:
CollSeq := proc ( n :: posint )
local
i :: posint,
c :: 'Array'( posint ):
i := n:
c := Array([]):
while 1 < i do
ArrayTools:-Append( c, i ):
i := Collatz( i ):
end do:
return( c ):
end proc:
CollLen := proc ( n :: posint )
local c :: posint:
c := CollSeq( n ):
return( numelems( c ) ):
end proc:
Here is a screenshot of using them in a Maple worksheet.
Why do I use an array and not a list? Because if you use a list which is immutable, each time you want to add an element to it, in fact it is defining a new list. It is not a memory efficient way, while array is mutable and your edits modifies the array itself. See the help pages on these in Maple.
And looking at your codes, it seems you have the same problem that some of my students in their first programming course usually have, return and print are not the same thing. If you really want a "print" action, that is fine, but you should not expect that the printed value be the output of the function unless you are using a return line inside the function that returns the same value of the print as well. For example you can have print(c): before return(c): in the second function above. So it both prints the sequence on the terminal and returns it to be used by another function or line of code.

The value of variables in a process after few executions

Let say I have some process in which i have initialized some variable.
process (clk) is
variable integer := 0;
begin
if (clk'event and clk='1') and (integer<32) then
integer := integer +1;
end if;
if (integer = 32) then
BusyOUT <= '1'; -- This is some outside signal
end if;
end proces;
Will this code set integer to 0 each time clk is on rising edge (since it will execute the entire code), or will it initialize integer to 0 only once (on first read rising edge of the clock, since it is in sensitivity list) and than increment it every time the clk is on rising edge until it reaches 32 and than it will activate the control BusyOUT signal (some outer signal) with which it will stop raising integer?
Thanks in advance,
Bojan
Yes, in meanwhile I figured that the part between the process and begin is executed only once.

Is it possible to declare variables in VHDL with an asterisk?

Quite new to VHDL here, so I'm not entirely sure if this is feasible at all, but here goes:
In my test code for some RAM, I have 2 8-bit std_logic_vector variables wdata_a_v and wdata_b_v. This is all I need for the current setup, but if the ratio of read to write data length changes, I will need more variables of the name wdata_*_v. I'm trying to write the code generically so that it will function for any amount of these variables, but I don't want to declare 26 of them in the code when I will likely only need a few.
It would be nice if there was a way to declare a variable like so:
variable wdata_*_v : std_logic_vector (7 downto 0);
that would, behind the scenes, declare all of the variables that fit this framework so that I could write a loop without worrying about running out of variables.
If there's a way to write a function or procedure etc. to make this work, that would be excellent.
Yes, you can go with a 2d array, recipe:
entity TestHelper is
generic (n: natural range 2 to 255 := 8);
end TestHelper;
architecture behavioral of TestHelper is
type array2d is array (n-1 downto 0) of std_logic_vector(7 downto 0);
begin
process
variable a : array2d;
begin
a(0)(0) := '0';
end process;
end architecture behavioral;
EDIT: Now to use it and create similar code for each of wdata_*_v:
process
variable wdata_v : array2d;
begin
someLabel: for i in 0 to n-1 generate
wdata_v(i)(0) := '0';
x <= y and z;
...
end generate;
x <= '1';
...
anotherLabel: for i in 1 to n generate
...
end generate;
...
end process;

VHDL shift operators?

I'm still trying to get used to some of the quirks of VHDL and I'm having a bit of an issue. First off, I understand that shift operators like rol, ror, ssl, srl, etc. are not synthesizeable. The purpose of this lab is to use a golden model to check against a synthesizeable version of the same thing in a testbench.
Now, the purpose of this program is to convert thermometer code into a 3-bit binary number. So, in other words, thermometer code "00000001" = "001", "00000011" = "010", "00000111" = "011", etc. I'm basically trying to count the number of 1's in the string from right to left. There will be no case where a '0' is placed between the string of 1's, so the vector "00011101" is invalid and will never occur.
I've devised a non-synthesizeable (and so far, non-compile-able) algorithm that I can't figure out how to get working. Basically, the idea is to read the thermometer code, shift it right and increment a counter until the thermometer code equals zero, and then assign the counter value to the 3-bit std_logic_vector. Below is the code I've done so-far.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity therm2bin_g is
port(therm : inout std_logic_vector(6 downto 0); -- thermometer code
bin : out std_logic_vector(2 downto 0); -- binary code
i : integer range 0 to 7);
end therm2bin_g;
architecture behavioral_g of therm2bin_g is
begin
golden : process(therm)
begin
while(therm /= "00000000") loop
therm <= therm srl 1;
i = i + 1;
end loop;
bin <= std_logic'(to_unsigned(i,3));
end process golden;
behavioral_g;
here's a version that is synthesisable. the while loop is replaced by a for loop. srl is implemented explicitly:
entity therm2bin_g is
port(therm : inout std_logic_vector(6 downto 0); -- thermometer code
bin : out std_logic_vector(2 downto 0); -- binary code
i : out integer range 0 to 7);
end therm2bin_g;
architecture behavioral_g of therm2bin_g is
begin
golden : process(therm)
variable i_internal: integer range 0 to 7;
begin
i_internal:=0;
for idx in 0 to therm'length loop
if therm/="0000000" then
therm<='0' & therm(therm'left downto 1);
i_internal := i_internal + 1;
end if;
end loop;
bin<=std_logic_vector(to_unsigned(i_internal,bin'length));
i<=i_internal;
end process golden;
end behavioral_g;
"... operators like rol, ror, ssl, srl, etc. are not synthesizeable..."
Who says that on who's authority? Have you checked? On which synthesis tool? Was it a recent version, or a version from the early 1990s?
Note that the argument that some tools might not support it is just silly. The fact that some kitchens might not have an oven does not stop people from writing recipes for cake.

Line intersection code in pascal

I'm trying to write line intersection code to detect if two lines intersect.
the form i have stuff in is there are O objects that can have Lo(l subscript O) lines, each line has 2 points and each point has a x and a y.
this is the record format.
TPoint = record
x,y:integer;
end;
TLine = record
Point : array[0..1] of TPoint;
Color : Tcolor;
end;
TFill = record
Point : TPoint;
Color : Tcolor;
end;
TDObject = record
Lines : array of TLine;
Fills : array of TFill;
Rotation : integer;
Position : Tpoint;
BoundTop,Boundleft,Boundbottom,Boundright:integer;
end;
I call Code to iterate through each line combination of the two objects I wish to test for collision.
Function DoCollide(obj1,obj2:Tdobject):boolean;
var i,j:integer;
coll:boolean;
begin
coll:=false;
for i:=0 to length(obj1.lines) do
begin
for j:=0 to length(obj2.lines) do
begin
coll:=DoesIntersect(obj2.lines[i],obj2.lines[j])or coll;
end;
end;
result:=coll;
end;
each line test is done like so
Function DoesIntersect(Line1,Line2:Tline):boolean;
var
m1,m2,c1,c2,intersect:real;
v1,v2:Boolean;
begin
//return true if lines cross
// if line if verticle do not workout gradient
if ((line1.point[1].x)-(line1.point[0].x))=0 then
v1:=true // remember line 1 is verticle
else
begin
m1 := ((line1.point[1].y)-(line1.point[0].y))/((line1.point[1].x)-(line1.point[0].x));
c1 := line1.point[0].y - (line1.point[0].x*m1);
end;
if ((line2.point[1].x)-(line2.point[0].x))=0 then
v2:=true // remember line 2 is verticle
else
begin
m2 := ((line2.point[1].y)-(line2.point[0].y))/((line2.point[1].x)-(line2.point[0].x));
c2 := line2.point[0].y - (line2.point[0].x*m2);
end;
if ((NOT(m1=m2)) and (NOT(v1 or v2))) then // non parrellel and non verticle
begin
//lines cross find where
intersect := (c2-c1)/(m1-m2); //line intersect solved for x
if ((round(intersect)>= Min(line1.point[0].x,line1.point[1].x))
and(round(intersect)<=max(line1.point[0].x,line1.point[1].x))
and(round(intersect)>=min(line2.point[0].x,line2.point[1].x))
and(round(intersect)<=max(line2.point[0].x,line2.point[1].x))) then
result := true
else
result := false
end
else if (v1 and v2) then // both lines are parralel
begin
// double verticle parallel exeption
if (((line1.Point[0].y>=min(line2.Point[0].y,line2.Point[1].y))
and(line1.Point[0].y<=max(line2.Point[0].y,line2.Point[1].y)))
or ((line1.Point[1].y>=min(line2.Point[0].y,line2.Point[1].y))
and(line1.Point[1].y<=max(line2.Point[0].y,line2.Point[1].y)))
or ((line2.Point[0].y>=min(line1.Point[0].y,line1.Point[1].y))
and(line2.Point[0].y<=max(line1.Point[0].y,line1.Point[1].y)))
or ((line2.Point[1].y>=min(line1.Point[0].y,line1.Point[1].y))
and(line2.Point[1].y<=max(line1.Point[0].y,line1.Point[1].y)))) then
result := true
else
result := false;
end
else if (v1 and not v2) then // line 1 is verticle and line 2 is not
begin
if ((((line1.Point[0].x*m2+c2)>=min(line1.Point[0].y,line1.Point[1].y))
and ((line1.Point[0].x*m2+c2)<=max(line1.Point[0].y,line1.Point[1].y)))) then
result := true
else
result := false
end
else if (v2 and not v1) then // line 2 is verticle and line 1 is not
begin
if (((line2.Point[0].x*m1+c1)>min(line2.Point[0].y,line2.Point[1].y))
and ((line2.Point[0].x*m1+c1)<max(line2.Point[0].y,line2.Point[1].y))) then
result := true
else
result := false
end
else if (m1=m2) then // parrellel non verticle lines
begin
if (((line1.Point[0].x>=min(line2.Point[0].x,line2.Point[1].x))
and(line1.Point[0].x<=max(line2.Point[0].x,line2.Point[1].x)))
or ((line1.Point[1].x>=min(line2.Point[0].x,line2.Point[1].x))
and(line1.Point[1].x<=max(line2.Point[0].x,line2.Point[1].x)))
or ((line2.Point[0].x>=min(line1.Point[0].x,line1.Point[1].x))
and(line2.Point[0].x<=max(line1.Point[0].x,line1.Point[1].x)))
or ((line2.Point[1].x>=min(line1.Point[0].x,line1.Point[1].x))
and(line2.Point[1].x<=max(line1.Point[0].x,line1.Point[1].x)))) then
result := true
else
result := false;
end;
end;
but according to my code all lines always intersect..... thus I have made a mistake... am I doing this in a silly way any ideas what I have done wrong?
There are better ways of detecting whether two sets of lines intersect, but don't worry about that for now.
I'm concerned that your program even ran long enough for you to detect that everything intersects; you iterate beyond the bounds of your arrays, so your program should have crashed. Always leave range checking enabled.
If you're going to be doing geometry, you should make sure to differentiate between lines and line segments. In two dimensions, non-parallel lines always intersect. Even parallel lines can intersect if they are coincident. It would also behoove you to get the spellings of parallel and vertical correct.
Your calculation of intersect is wrong. You need to divide the difference in slopes by the difference in y-intercepts:
if c1 = c2 then
intersect := c1
else
intersect := (m1 - m2) / (c2 - c1);
If both lines are vertical, then it's not enough to check whether they overlap in their y coordinates. You also need to check that their x coordinates are equal. Likewise, with parallel non-vertical lines, you need to check whether the y-intercepts are equal.
If you fix all those problems and still get wrong results, then it's time to dust off the debugger. Find a pair of line segments that your function returns true for and yet do not really intersect. Call the function on those values and step through your function with the debugger. To make debugging easier, you'll want to split those many-line-long conditional expressions into several intermediate variables so you can check each one separately. Determine which calculation is wrong, and then fix it. Make sure your test dataset contains elements that will exercise each possible conditional path in your function.