git-svn-id: http://moon:8086/svn/vhdl/trunk@1073 cc03376c-175c-47c8-b038-4cd826a8556b
1327 lines
34 KiB
VHDL
1327 lines
34 KiB
VHDL
-- Std.Textio package body. This file is part of GHDL.
|
|
-- Copyright (C) 2002, 2003, 2004, 2005 Tristan Gingold
|
|
--
|
|
-- GHDL is free software; you can redistribute it and/or modify it under
|
|
-- the terms of the GNU General Public License as published by the Free
|
|
-- Software Foundation; either version 2, or (at your option) any later
|
|
-- version.
|
|
--
|
|
-- GHDL is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
-- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
-- for more details.
|
|
--
|
|
-- You should have received a copy of the GNU General Public License
|
|
-- along with GCC; see the file COPYING. If not, write to the Free
|
|
-- Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
-- 02111-1307, USA.
|
|
|
|
package body textio is
|
|
-- output routines for standard types
|
|
|
|
-- TIME_NAMES associates time units with textual names.
|
|
-- Textual names are in lower cases, since according to LRM93 14.3:
|
|
-- when written, the identifier is expressed in lowercase characters.
|
|
-- The length of the names are 3 characters, the last one may be a space
|
|
-- for 2 characters long names.
|
|
type time_unit is
|
|
record
|
|
val : time;
|
|
name : string (1 to 3);
|
|
end record;
|
|
type time_names_type is array (1 to 8) of time_unit;
|
|
constant time_names : time_names_type :=
|
|
((fs, "fs "), (ps, "ps "), (ns, "ns "), (us, "us "),
|
|
(ms, "ms "), (sec, "sec"), (min, "min"), (hr, "hr "));
|
|
|
|
-- Non breaking space character. --V93
|
|
constant nbsp : character := character'val (160); --V93
|
|
|
|
procedure writeline (f: out text; l: inout line) is --V87
|
|
procedure writeline (file f: text; l: inout line) is --V93
|
|
begin
|
|
if l = null then
|
|
-- LRM93 14.3
|
|
-- If parameter L contains a null access value at the start of the call,
|
|
-- the a null string is written to the file.
|
|
write (f, "");
|
|
else
|
|
-- LRM93 14.3
|
|
-- Procedure WRITELINE causes the current line designated by parameter L
|
|
-- to be written to the file and returns with the value of parameter L
|
|
-- designating a null string.
|
|
write (f, l.all);
|
|
deallocate (l);
|
|
l := new string'("");
|
|
end if;
|
|
end writeline;
|
|
|
|
procedure write
|
|
(l: inout line; value: in string;
|
|
justified: in side := right; field: in width := 0)
|
|
is
|
|
variable length: natural;
|
|
variable nl: line;
|
|
begin
|
|
-- l can be null.
|
|
if l = null then
|
|
length := 0;
|
|
else
|
|
length := l.all'length;
|
|
end if;
|
|
if value'length < field then
|
|
nl := new string (1 to length + field);
|
|
if length /= 0 then
|
|
nl (1 to length) := l.all;
|
|
end if;
|
|
if justified = right then
|
|
nl (length + 1 to length + field - value'length) := (others => ' ');
|
|
nl (nl.all'high - value'length + 1 to nl.all'high) := value;
|
|
else
|
|
nl (length + 1 to length + value'length) := value;
|
|
nl (length + value'length + 1 to nl.all'high) := (others => ' ');
|
|
end if;
|
|
else
|
|
nl := new string (1 to length + value'length);
|
|
if length /= 0 then
|
|
nl (1 to length) := l.all;
|
|
end if;
|
|
nl (length + 1 to nl.all'high) := value;
|
|
end if;
|
|
deallocate (l);
|
|
l := nl;
|
|
end write;
|
|
|
|
procedure write
|
|
(l: inout line; value: in integer;
|
|
justified: in side := right; field: in width := 0)
|
|
is
|
|
variable str: string (11 downto 1);
|
|
variable val: integer := value;
|
|
variable digit: natural;
|
|
variable index: natural := 0;
|
|
begin
|
|
-- Note: the absolute value of VAL cannot be directly taken, since
|
|
-- it may be greather that the maximum value of an INTEGER.
|
|
loop
|
|
-- LRM93 7.2.6
|
|
-- (A rem B) has the sign of A and an absolute value less then
|
|
-- the absoulte value of B.
|
|
digit := abs (val rem 10);
|
|
val := val / 10;
|
|
index := index + 1;
|
|
str (index) := character'val(48 + digit);
|
|
exit when val = 0;
|
|
end loop;
|
|
if value < 0 then
|
|
index := index + 1;
|
|
str(index) := '-';
|
|
end if;
|
|
write (l, str (index downto 1), justified, field);
|
|
end write;
|
|
|
|
procedure write
|
|
(l: inout line; value: in boolean;
|
|
justified: in side := right; field: in width := 0)
|
|
is
|
|
begin
|
|
if value then
|
|
write (l, string'("TRUE"), justified, field);
|
|
else
|
|
write (l, string'("FALSE"), justified, field);
|
|
end if;
|
|
end write;
|
|
|
|
procedure write
|
|
(l: inout line; value: in character;
|
|
justified: in side := right; field: in width := 0)
|
|
is
|
|
variable str: string (1 to 1);
|
|
begin
|
|
str (1) := value;
|
|
write (l, str, justified, field);
|
|
end write;
|
|
|
|
function bit_to_char (value : in bit) return character is
|
|
begin
|
|
case value is
|
|
when '0' =>
|
|
return '0';
|
|
when '1' =>
|
|
return '1';
|
|
end case;
|
|
end bit_to_char;
|
|
|
|
procedure write
|
|
(l: inout line; value: in bit;
|
|
justified: in side := right; field: in width := 0)
|
|
is
|
|
variable str : string (1 to 1);
|
|
begin
|
|
str (1) := bit_to_char (value);
|
|
write (l, str, justified, field);
|
|
end write;
|
|
|
|
procedure write
|
|
(l: inout line; value: in bit_vector;
|
|
justified: in side := right; field: in width := 0)
|
|
is
|
|
constant length : natural := value'length;
|
|
alias n_value : bit_vector (1 to value'length) is value;
|
|
variable str : string (1 to length);
|
|
begin
|
|
for i in str'range loop
|
|
str (i) := bit_to_char (n_value (i));
|
|
end loop;
|
|
write (l, str, justified, field);
|
|
end write;
|
|
|
|
procedure write
|
|
(l: inout line; value : in time;
|
|
justified: in side := right; field: in width := 0; unit : in TIME := ns)
|
|
is
|
|
-- Copy of VALUE on which we are working.
|
|
variable val : time := value;
|
|
|
|
-- Copy of UNIT on which we are working.
|
|
variable un : time := unit;
|
|
|
|
-- Digit extract from VAL/UN.
|
|
variable d : integer; -- natural range 0 to 9;
|
|
|
|
-- Index for unit name.
|
|
variable n : integer;
|
|
|
|
-- Result.
|
|
variable str : string (1 to 28);
|
|
|
|
-- Current character in RES.
|
|
variable pos : natural := 1;
|
|
|
|
-- Add a character to STR.
|
|
procedure add_char (c : character) is
|
|
begin
|
|
str (pos) := c;
|
|
pos := pos + 1;
|
|
end add_char;
|
|
begin
|
|
-- Note:
|
|
-- Care is taken to avoid overflow. Time may be 64 bits while integer
|
|
-- may be only 32 bits.
|
|
|
|
-- Handle sign.
|
|
-- Note: VAL cannot be negated since its range may be not symetric
|
|
-- around 0.
|
|
if val < 0 ns then
|
|
add_char ('-');
|
|
end if;
|
|
|
|
-- Search for the first digit.
|
|
-- Note: we must start from unit, since all units are not a power of 10.
|
|
-- Note: UN can be multiplied only after we know it is possible. This
|
|
-- is a to avoid overflow.
|
|
if un <= 0 fs then
|
|
assert false report "UNIT argument is not positive" severity error;
|
|
un := 1 ns;
|
|
end if;
|
|
while val / 10 >= un or val / 10 <= -un loop
|
|
un := un * 10;
|
|
end loop;
|
|
|
|
-- Extract digits one per one.
|
|
loop
|
|
d := val / un;
|
|
add_char (character'val (abs d + character'pos ('0')));
|
|
val := val - d * un;
|
|
exit when val = 0 ns and un <= unit;
|
|
if un = unit then
|
|
add_char ('.');
|
|
end if;
|
|
-- Stop as soon as precision will be lost.
|
|
-- This can happen only for hr and min.
|
|
-- FIXME: change the algorithm to display all the digits.
|
|
exit when (un / 10) * 10 /= un;
|
|
un := un / 10;
|
|
end loop;
|
|
|
|
add_char (' ');
|
|
|
|
-- Search the time unit name in the time table.
|
|
n := 0;
|
|
for i in time_names'range loop
|
|
if time_names (i).val = unit then
|
|
n := i;
|
|
exit;
|
|
end if;
|
|
end loop;
|
|
assert n /= 0 report "UNIT argument is not a unit name" severity error;
|
|
if n = 0 then
|
|
add_char ('?');
|
|
else
|
|
add_char (time_names (n).name (1));
|
|
add_char (time_names (n).name (2));
|
|
if time_names (n).name (3) /= ' ' then
|
|
add_char (time_names (n).name (3));
|
|
end if;
|
|
end if;
|
|
|
|
-- Write the result.
|
|
write (l, str (1 to pos - 1), justified, field);
|
|
end write;
|
|
|
|
-- Parameter DIGITS specifies how many digits to the right of the decimal
|
|
-- point are to be output when writing a real number; the default value 0
|
|
-- indicates that the number should be output in standard form, consisting
|
|
-- of a normalized mantissa plus exponent (e.g., 1.079236E23). If DIGITS is
|
|
-- nonzero, then the real number is output as an integer part followed by
|
|
-- '.' followed by the fractional part, using the specified number of digits
|
|
-- (e.g., 3.14159).
|
|
-- Note: Nan, +Inf, -Inf are not to be considered, since these numbers are
|
|
-- not in the bounds defined by any real range.
|
|
procedure write (L: inout line; value: in real;
|
|
justified: in side := right; field: in width := 0;
|
|
digits: in natural := 0)
|
|
is
|
|
-- STR contains the result of the conversion.
|
|
variable str : string (1 to 320);
|
|
|
|
-- POS is the index of the next character to be put in STR.
|
|
variable pos : positive := str'left;
|
|
|
|
-- VAL contains the value to be converted.
|
|
variable val : real;
|
|
|
|
-- The exponent or mantissa computed is stored in MANTISSA. This is
|
|
-- a signed number.
|
|
variable mantissa : integer;
|
|
|
|
variable b : boolean;
|
|
variable d : natural;
|
|
|
|
-- Append character C in STR.
|
|
procedure add_char (c : character) is
|
|
begin
|
|
str (pos) := c;
|
|
pos := pos + 1;
|
|
end add_char;
|
|
|
|
-- Add digit V in STR.
|
|
procedure add_digit (v : natural) is
|
|
begin
|
|
add_char (character'val (character'pos ('0') + v));
|
|
end add_digit;
|
|
|
|
-- Add leading digit and substract it.
|
|
procedure extract_leading_digit is
|
|
variable d : natural range 0 to 10;
|
|
begin
|
|
-- Note: We need truncation but type conversion does rounding.
|
|
-- FIXME: should consider precision.
|
|
d := natural (val);
|
|
if real (d) > val then
|
|
d := d - 1;
|
|
end if;
|
|
|
|
val := (val - real (d)) * 10.0;
|
|
|
|
add_digit (d);
|
|
end extract_leading_digit;
|
|
begin
|
|
-- Handle sign.
|
|
-- There is no overflow here, since with IEEE implementations, sign is
|
|
-- independant of the mantissa.
|
|
-- LRM93 14.3
|
|
-- The sign is never written if the value is non-negative.
|
|
if value < 0.0 then
|
|
add_char ('-');
|
|
val := -value;
|
|
else
|
|
val := value;
|
|
end if;
|
|
|
|
-- Compute the mantissa.
|
|
-- FIXME: should do a dichotomy.
|
|
if val = 0.0 then
|
|
mantissa := 0;
|
|
elsif val < 1.0 then
|
|
mantissa := -1;
|
|
while val * (10.0 ** (-mantissa)) < 1.0 loop
|
|
mantissa := mantissa - 1;
|
|
end loop;
|
|
else
|
|
mantissa := 0;
|
|
while val / (10.0 ** mantissa) >= 10.0 loop
|
|
mantissa := mantissa + 1;
|
|
end loop;
|
|
end if;
|
|
|
|
-- Normalize VAL: in [0; 10[
|
|
if mantissa >= 0 then
|
|
val := val / (10.0 ** mantissa);
|
|
else
|
|
val := val * 10.0 ** (-mantissa);
|
|
end if;
|
|
|
|
if digits = 0 then
|
|
for i in 0 to 15 loop
|
|
extract_leading_digit;
|
|
|
|
if i = 0 then
|
|
add_char ('.');
|
|
end if;
|
|
exit when i > 0 and val < 10.0 ** (i + 1 - 15);
|
|
end loop;
|
|
|
|
-- LRM93 14.3
|
|
-- if the exponent is present, the `e' is written as a lower case
|
|
-- character.
|
|
add_char ('e');
|
|
|
|
if mantissa < 0 then
|
|
add_char ('-');
|
|
mantissa := -mantissa;
|
|
end if;
|
|
b := false;
|
|
for i in 4 downto 0 loop
|
|
d := (mantissa / 10000) mod 10;
|
|
if d /= 0 or b or i = 0 then
|
|
add_digit (d);
|
|
b := true;
|
|
end if;
|
|
mantissa := (mantissa - d * 10000) * 10;
|
|
end loop;
|
|
else
|
|
if mantissa < 0 then
|
|
add_char ('0');
|
|
mantissa := mantissa + 1;
|
|
else
|
|
loop
|
|
extract_leading_digit;
|
|
exit when mantissa = 0;
|
|
mantissa := mantissa - 1;
|
|
end loop;
|
|
end if;
|
|
add_char ('.');
|
|
for i in 1 to digits loop
|
|
if mantissa = 0 then
|
|
extract_leading_digit;
|
|
else
|
|
add_char ('0');
|
|
mantissa := mantissa + 1;
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
write (l, str (1 to pos - 1), justified, field);
|
|
end write;
|
|
|
|
procedure untruncated_text_read --V87
|
|
(variable f : text; str : out string; len : out natural); --V87
|
|
procedure untruncated_text_read --V93
|
|
(file f : text; str : out string; len : out natural); --V93
|
|
|
|
attribute foreign : string; --V87
|
|
attribute foreign of untruncated_text_read : procedure is "GHDL intrinsic";
|
|
|
|
procedure untruncated_text_read
|
|
(variable f : text; str : out string; len : out natural) is --V87
|
|
(file f : text; str : out string; len : out natural) is --V93
|
|
begin
|
|
assert false report "must not be called" severity failure;
|
|
end untruncated_text_read;
|
|
|
|
procedure readline (variable f: in text; l: inout line) --V87
|
|
procedure readline (file f: text; l: inout line) --V93
|
|
is
|
|
variable len, nlen, posn : natural;
|
|
variable nl, old_l : line;
|
|
variable str : string (1 to 128);
|
|
variable is_eol : boolean;
|
|
begin
|
|
-- LRM93 14.3
|
|
-- If parameter L contains a non-null access value at the start of the
|
|
-- call, the object designated by that value is deallocated before the
|
|
-- new object is created.
|
|
if l /= null then
|
|
deallocate (l);
|
|
end if;
|
|
|
|
-- We read the input in 128-byte chunks.
|
|
-- We keep reading until we reach a newline or there is no more input.
|
|
-- The loop invariant is that old_l is allocated and contains the
|
|
-- previous chunks read, and posn = old_l.all'length.
|
|
posn := 0;
|
|
loop
|
|
untruncated_text_read (f, str, len);
|
|
exit when len = 0;
|
|
if str (len) = LF or str (len) = CR then
|
|
-- LRM 14.3
|
|
-- The representation of the line does not contain the representation
|
|
-- of the end of the line.
|
|
is_eol := true;
|
|
len := len - 1;
|
|
-- End of line is any of LF/CR/CR+LF/LF+CR.
|
|
if len > 0 and (str (len) = LF or str (len) = CR) then
|
|
len := len - 1;
|
|
end if;
|
|
elsif endfile (f) then
|
|
is_eol := true;
|
|
else
|
|
is_eol := false;
|
|
end if;
|
|
l := new string (1 to posn + len);
|
|
if old_l /= null then
|
|
l (1 to posn) := old_l (1 to posn);
|
|
deallocate (old_l);
|
|
end if;
|
|
l (posn + 1 to posn + len) := str (1 to len);
|
|
exit when is_eol;
|
|
posn := posn + len;
|
|
old_l := l;
|
|
end loop;
|
|
end readline;
|
|
|
|
-- Replaces L with L (LEFT to/downto L'RIGHT)
|
|
procedure trim (l : inout line; left : natural)
|
|
is
|
|
variable nl : line;
|
|
begin
|
|
if l = null then
|
|
return;
|
|
end if;
|
|
if l'left < l'right then
|
|
-- Ascending.
|
|
if left > l'right then
|
|
nl := new string'("");
|
|
else
|
|
nl := new string (left to l'right);
|
|
-- nl := new string (1 to l'right + 1 - left);
|
|
nl.all := l (left to l'right);
|
|
end if;
|
|
else
|
|
-- Descending
|
|
if left < l'right then
|
|
nl := new string'("");
|
|
else
|
|
nl := new string (left downto l'right);
|
|
-- nl := new string (left - l'right + 1 downto 1);
|
|
nl.all := l (left downto l'right);
|
|
end if;
|
|
end if;
|
|
deallocate (l);
|
|
l := nl;
|
|
end trim;
|
|
|
|
-- Replaces L with L (LEFT + 1 to L'RIGHT or LEFT - 1 downto L'RIGHT)
|
|
procedure trim_next (l : inout line; left : natural)
|
|
is
|
|
variable nl : line;
|
|
begin
|
|
if l = null then
|
|
return;
|
|
end if;
|
|
if l'left < l'right then
|
|
-- Ascending.
|
|
trim (l, left + 1);
|
|
else
|
|
-- Descending
|
|
trim (l, left - 1);
|
|
end if;
|
|
end trim_next;
|
|
|
|
function to_lower (c : character) return character is
|
|
begin
|
|
if c >= 'A' and c <= 'Z' then
|
|
return character'val (character'pos (c) + 32);
|
|
else
|
|
return c;
|
|
end if;
|
|
end to_lower;
|
|
|
|
procedure read (l: inout line; value: out character; good: out boolean)
|
|
is
|
|
variable nl : line;
|
|
begin
|
|
if l = null or l'length = 0 then
|
|
good := false;
|
|
else
|
|
value := l (l'left);
|
|
trim_next (l, l'left);
|
|
good := true;
|
|
end if;
|
|
end read;
|
|
|
|
procedure read (l: inout line; value: out character)
|
|
is
|
|
variable res : boolean;
|
|
begin
|
|
read (l, value, res);
|
|
assert res = true
|
|
report "character read failure"
|
|
severity failure;
|
|
end read;
|
|
|
|
procedure read (l: inout line; value: out bit; good: out boolean)
|
|
is
|
|
begin
|
|
good := false;
|
|
for i in l'range loop
|
|
case l(i) is
|
|
when ' '
|
|
| NBSP --V93
|
|
| HT =>
|
|
null;
|
|
when '1' =>
|
|
value := '1';
|
|
good := true;
|
|
trim_next (l, i);
|
|
return;
|
|
when '0' =>
|
|
value := '0';
|
|
good := true;
|
|
trim_next (l, i);
|
|
return;
|
|
when others =>
|
|
return;
|
|
end case;
|
|
end loop;
|
|
return;
|
|
end read;
|
|
|
|
procedure read (l: inout line; value: out bit)
|
|
is
|
|
variable res : boolean;
|
|
begin
|
|
read (l, value, res);
|
|
assert res = true
|
|
report "bit read failure"
|
|
severity failure;
|
|
end read;
|
|
|
|
procedure read (l: inout line; value: out bit_vector; good: out boolean)
|
|
is
|
|
-- Number of bit to parse.
|
|
variable len : natural;
|
|
|
|
variable pos, last : natural;
|
|
variable res : bit_vector (1 to value'length);
|
|
|
|
-- State of the previous byte:
|
|
-- LEADING: blank before the bit vector.
|
|
-- FOUND: bit of the vector.
|
|
type state_type is (leading, found);
|
|
variable state : state_type;
|
|
begin
|
|
-- Initialization.
|
|
len := value'length;
|
|
if len = 0 then
|
|
-- If VALUE is a nul array, return now.
|
|
-- L stay unchanged.
|
|
-- FIXME: should blanks be removed ?
|
|
good := true;
|
|
return;
|
|
end if;
|
|
good := false;
|
|
state := leading;
|
|
pos := res'left;
|
|
for i in l'range loop
|
|
case l(i) is
|
|
when ' '
|
|
| NBSP --V93
|
|
| HT =>
|
|
case state is
|
|
when leading =>
|
|
null;
|
|
when found =>
|
|
return;
|
|
end case;
|
|
when '1' | '0' =>
|
|
case state is
|
|
when leading =>
|
|
state := found;
|
|
when found =>
|
|
null;
|
|
end case;
|
|
if l(i) = '0' then
|
|
res (pos) := '0';
|
|
else
|
|
res (pos) := '1';
|
|
end if;
|
|
pos := pos + 1;
|
|
len := len - 1;
|
|
last := i;
|
|
exit when len = 0;
|
|
when others =>
|
|
return;
|
|
end case;
|
|
end loop;
|
|
|
|
if len /= 0 then
|
|
-- Not enough bits.
|
|
return;
|
|
end if;
|
|
|
|
-- Note: if LEN = 0, then FIRST and LAST have been set.
|
|
good := true;
|
|
value := res;
|
|
trim_next (l, last);
|
|
return;
|
|
end read;
|
|
|
|
procedure read (l: inout line; value: out bit_vector)
|
|
is
|
|
variable res : boolean;
|
|
begin
|
|
read (l, value, res);
|
|
assert res = true
|
|
report "bit_vector read failure"
|
|
severity failure;
|
|
end read;
|
|
|
|
procedure read (l: inout line; value: out boolean; good: out boolean)
|
|
is
|
|
-- State:
|
|
-- BLANK: space are being scaned.
|
|
-- L_TF : T(rue) or F(alse) has been scanned.
|
|
-- L_RA : (t)R(ue) or (f)A(lse) has been scanned.
|
|
-- L_UL : (tr)U(e) or (fa)L(se) has been scanned.
|
|
-- L_ES : (tru)E or (fal)S(e) has been scanned.
|
|
type state_type is (blank, l_tf, l_ra, l_ul, l_es);
|
|
variable state : state_type;
|
|
|
|
-- Set to TRUE if T has been scanned, to FALSE if F has been scanned.
|
|
variable res : boolean;
|
|
begin
|
|
-- By default, it is a failure.
|
|
good := false;
|
|
state := blank;
|
|
for i in l'range loop
|
|
case state is
|
|
when blank =>
|
|
if l (i) = ' '
|
|
or l (i) = nbsp --V93
|
|
or l (i) = HT
|
|
then
|
|
null;
|
|
elsif to_lower (l (i)) = 't' then
|
|
res := true;
|
|
state := l_tf;
|
|
elsif to_lower (l (i)) = 'f' then
|
|
res := false;
|
|
state := l_tf;
|
|
else
|
|
return;
|
|
end if;
|
|
when l_tf =>
|
|
if res = true and to_lower (l (i)) = 'r' then
|
|
state := l_ra;
|
|
elsif res = false and to_lower (l (i)) = 'a' then
|
|
state := l_ra;
|
|
else
|
|
return;
|
|
end if;
|
|
when l_ra =>
|
|
if res = true and to_lower (l (i)) = 'u' then
|
|
state := l_ul;
|
|
elsif res = false and to_lower (l (i)) = 'l' then
|
|
state := l_ul;
|
|
else
|
|
return;
|
|
end if;
|
|
when l_ul =>
|
|
if res = true and to_lower (l (i)) = 'e' then
|
|
trim_next (l, i);
|
|
good := true;
|
|
value := true;
|
|
return;
|
|
elsif res = false and to_lower (l (i)) = 's' then
|
|
state := l_es;
|
|
else
|
|
return;
|
|
end if;
|
|
when l_es =>
|
|
if res = false and to_lower (l (i)) = 'e' then
|
|
trim_next (l, i);
|
|
good := true;
|
|
value := false;
|
|
return;
|
|
else
|
|
return;
|
|
end if;
|
|
end case;
|
|
end loop;
|
|
return;
|
|
end read;
|
|
|
|
procedure read (l: inout line; value: out boolean)
|
|
is
|
|
variable res : boolean;
|
|
begin
|
|
read (l, value, res);
|
|
assert res = true
|
|
report "boolean read failure"
|
|
severity failure;
|
|
end read;
|
|
|
|
function char_to_nat (c : character) return natural
|
|
is
|
|
begin
|
|
return character'pos (c) - character'pos ('0');
|
|
end char_to_nat;
|
|
|
|
procedure read (l: inout line; value: out integer; good: out boolean)
|
|
is
|
|
variable val : integer;
|
|
variable d : natural;
|
|
|
|
type state_t is (leading, sign, digits);
|
|
variable cur_state : state_t := leading;
|
|
begin
|
|
val := 1;
|
|
for i in l'range loop
|
|
case cur_state is
|
|
when leading =>
|
|
case l(i) is
|
|
when ' '
|
|
| NBSP --V93
|
|
| ht =>
|
|
null;
|
|
when '+' =>
|
|
cur_state := sign;
|
|
when '-' =>
|
|
val := -1;
|
|
cur_state := sign;
|
|
when '0' to '9' =>
|
|
val := char_to_nat (l(i));
|
|
cur_state := digits;
|
|
when others =>
|
|
good := false;
|
|
return;
|
|
end case;
|
|
when sign =>
|
|
case l(i) is
|
|
when '0' to '9' =>
|
|
val := val * char_to_nat (l(i));
|
|
cur_state := digits;
|
|
when others =>
|
|
good := false;
|
|
return;
|
|
end case;
|
|
when digits =>
|
|
case l(i) is
|
|
when '0' to '9' =>
|
|
d := char_to_nat (l(i));
|
|
val := val * 10;
|
|
if val < 0 then
|
|
val := val - d;
|
|
else
|
|
val := val + d;
|
|
end if;
|
|
when others =>
|
|
trim (l, i);
|
|
good := true;
|
|
value := val;
|
|
return;
|
|
end case;
|
|
end case;
|
|
end loop;
|
|
deallocate (l);
|
|
l := new string'("");
|
|
if cur_state /= leading then
|
|
good := true;
|
|
value := val;
|
|
else
|
|
good := false;
|
|
end if;
|
|
end read;
|
|
|
|
procedure read (l: inout line; value: out integer)
|
|
is
|
|
variable res : boolean;
|
|
begin
|
|
read (l, value, res);
|
|
assert res = true
|
|
report "integer read failure"
|
|
severity failure;
|
|
end read;
|
|
|
|
procedure read (l: inout line; value: out real; good: out boolean)
|
|
is
|
|
-- The result.
|
|
variable val : real;
|
|
-- True if the result is negative.
|
|
variable val_neg : boolean;
|
|
|
|
-- Number of digits after the dot.
|
|
variable nbr_dec : natural;
|
|
|
|
-- Value of the exponent.
|
|
variable exp : integer;
|
|
-- True if the exponent is negative.
|
|
variable exp_neg : boolean;
|
|
|
|
-- The parsing is done with a state machine.
|
|
-- LEADING: leading blank suppression.
|
|
-- SIGN: a sign has been found.
|
|
-- DIGITS: integer parts
|
|
-- DECIMALS: digits after the dot.
|
|
-- EXPONENT_SIGN: sign after "E"
|
|
-- EXPONENT_1: first digit of the exponent.
|
|
-- EXPONENT: digits of the exponent.
|
|
type state_t is (leading, sign, digits, decimals,
|
|
exponent_sign, exponent_1, exponent);
|
|
variable cur_state : state_t := leading;
|
|
|
|
-- Set VALUE to the result, and set GOOD to TRUE.
|
|
procedure set_value is
|
|
begin
|
|
good := true;
|
|
|
|
if exp_neg then
|
|
val := val * 10.0 ** (-exp);
|
|
else
|
|
val := val * 10.0 ** exp;
|
|
end if;
|
|
if val_neg then
|
|
value := -val;
|
|
else
|
|
value := val;
|
|
end if;
|
|
end set_value;
|
|
|
|
begin
|
|
-- Initialization.
|
|
val_neg := false;
|
|
nbr_dec := 1;
|
|
exp := 0;
|
|
exp_neg := false;
|
|
|
|
-- By default, parsing has failed.
|
|
good := false;
|
|
|
|
-- Iterate over all characters of the string.
|
|
-- Return immediatly in case of parse error.
|
|
-- Trim L and call SET_VALUE and return in case of success.
|
|
for i in l'range loop
|
|
case cur_state is
|
|
when leading =>
|
|
case l(i) is
|
|
when ' '
|
|
| NBSP --V93
|
|
| ht =>
|
|
null;
|
|
when '+' =>
|
|
cur_state := sign;
|
|
when '-' =>
|
|
val_neg := true;
|
|
cur_state := sign;
|
|
when '0' to '9' =>
|
|
val := real (char_to_nat (l(i)));
|
|
cur_state := digits;
|
|
when others =>
|
|
return;
|
|
end case;
|
|
when sign =>
|
|
case l(i) is
|
|
when '0' to '9' =>
|
|
val := real (char_to_nat (l(i)));
|
|
cur_state := digits;
|
|
when others =>
|
|
return;
|
|
end case;
|
|
when digits =>
|
|
case l(i) is
|
|
when '0' to '9' =>
|
|
val := val * 10.0 + real (char_to_nat (l(i)));
|
|
when '.' =>
|
|
cur_state := decimals;
|
|
when others =>
|
|
-- A "." (dot) is required in the string.
|
|
return;
|
|
end case;
|
|
when decimals =>
|
|
case l(i) is
|
|
when '0' to '9' =>
|
|
val := val + real (char_to_nat (l(i))) / (10.0 ** nbr_dec);
|
|
nbr_dec := nbr_dec + 1;
|
|
when 'e' | 'E' =>
|
|
-- "nnn.E" is erroneous.
|
|
if nbr_dec = 1 then
|
|
return;
|
|
end if;
|
|
cur_state := exponent_sign;
|
|
when others =>
|
|
-- "nnn.XX" is erroneous.
|
|
if nbr_dec = 1 then
|
|
return;
|
|
end if;
|
|
trim (l, i);
|
|
set_value;
|
|
return;
|
|
end case;
|
|
when exponent_sign =>
|
|
case l(i) is
|
|
when '+' =>
|
|
cur_state := exponent_1;
|
|
when '-' =>
|
|
exp_neg := true;
|
|
cur_state := exponent_1;
|
|
when '0' to '9' =>
|
|
exp := char_to_nat (l(i));
|
|
cur_state := exponent;
|
|
when others =>
|
|
-- Error.
|
|
return;
|
|
end case;
|
|
when exponent_1 | exponent =>
|
|
case l(i) is
|
|
when '0' to '9' =>
|
|
exp := exp * 10 + char_to_nat (l(i));
|
|
cur_state := exponent;
|
|
when others =>
|
|
trim (l, i);
|
|
set_value;
|
|
return;
|
|
end case;
|
|
end case;
|
|
end loop;
|
|
|
|
-- End of string.
|
|
case cur_state is
|
|
when leading | sign | digits =>
|
|
-- Erroneous.
|
|
return;
|
|
when decimals =>
|
|
-- "nnn.XX" is erroneous.
|
|
if nbr_dec = 1 then
|
|
return;
|
|
end if;
|
|
when exponent_sign =>
|
|
-- Erroneous ("NNN.NNNE")
|
|
return;
|
|
when exponent_1 =>
|
|
-- "NNN.NNNE-"
|
|
return;
|
|
when exponent =>
|
|
null;
|
|
end case;
|
|
|
|
deallocate (l);
|
|
l := new string'("");
|
|
set_value;
|
|
end read;
|
|
|
|
procedure read (l: inout line; value: out real)
|
|
is
|
|
variable res : boolean;
|
|
begin
|
|
read (l, value, res);
|
|
assert res = true
|
|
report "real read failure"
|
|
severity failure;
|
|
end read;
|
|
|
|
procedure read (l: inout line; value: out time; good: out boolean)
|
|
is
|
|
-- The result.
|
|
variable res : time;
|
|
|
|
-- UNIT is computed from the unit name, the exponent and the number of
|
|
-- digits before the dot. UNIT is the weight of the current digit.
|
|
variable unit : time;
|
|
|
|
-- Number of digits before the dot.
|
|
variable nbr_digits : integer;
|
|
|
|
-- True if a unit name has been found. Used temporaly to know the status
|
|
-- at the end of the search loop.
|
|
variable unit_found : boolean;
|
|
|
|
-- True if the number is negative.
|
|
variable is_neg : boolean;
|
|
|
|
-- Value of the exponent.
|
|
variable exp : integer;
|
|
|
|
-- True if the exponent is negative.
|
|
variable exp_neg : boolean;
|
|
|
|
-- Unit name extracted from the string.
|
|
variable unit_name : string (1 to 3);
|
|
|
|
-- state is the kind of the previous character parsed.
|
|
-- LEADING: leading blanks
|
|
-- SIGN: + or - as the first character of the number.
|
|
-- DIGITS: digit of the integer part of the number.
|
|
-- DOT: dot (.) after the integer part and before the decimal part.
|
|
-- DECIMALS: digit of the decimal part.
|
|
-- EXPONENT_MARK: e or E.
|
|
-- EXPONENT_SIGN: + or - just after the exponent mark (E).
|
|
-- EXPONENT: digit of the exponent.
|
|
-- UNIT_BLANK: blank after the exponent.
|
|
-- UNIT_1, UNIT_2, UNIT_3: first, second, third character of the unit.
|
|
type state_type is (leading, sign, digits, dot, decimals,
|
|
exponent_mark, exponent_sign, exponent,
|
|
unit_blank, unit_1, unit_2, unit_3);
|
|
variable state : state_type;
|
|
|
|
-- Used during the second scan of the string, TRUE is digits is being
|
|
-- scaned.
|
|
variable has_digits : boolean;
|
|
|
|
-- Position at the end of the string.
|
|
variable pos : integer;
|
|
|
|
-- Used to compute POS.
|
|
variable length : integer;
|
|
begin
|
|
-- Initialization.
|
|
-- Fail by default; therefore, in case of error, a return statement is
|
|
-- ok.
|
|
good := false;
|
|
|
|
nbr_digits := 0;
|
|
is_neg := false;
|
|
exp := 0;
|
|
exp_neg := false;
|
|
res := 0 fs;
|
|
|
|
-- Look for exponent and unit name.
|
|
-- Parse the string: this loop checks the correctness of the format, and
|
|
-- must return (GOOD has been set to FALSE) in case of error.
|
|
-- Set: NBR_DIGITS, IS_NEG, EXP, EXP_NEG.
|
|
state := leading;
|
|
for i in l'range loop
|
|
case l (i) is
|
|
when ' '
|
|
| NBSP --V93
|
|
| HT =>
|
|
case state is
|
|
when leading | unit_blank =>
|
|
null;
|
|
when sign | dot | exponent_mark | exponent_sign =>
|
|
return;
|
|
when digits | decimals | exponent =>
|
|
state := unit_blank;
|
|
when unit_1 | unit_2 =>
|
|
exit;
|
|
when unit_3 =>
|
|
-- Cannot happen, since an exit is performed at unit_3.
|
|
assert false report "internal error" severity failure;
|
|
end case;
|
|
when '+' | '-' =>
|
|
case state is
|
|
when leading =>
|
|
if l(i) = '-' then
|
|
is_neg := true;
|
|
end if;
|
|
state := sign;
|
|
when exponent_mark =>
|
|
if l(i) = '-' then
|
|
exp_neg := true;
|
|
end if;
|
|
state := exponent_sign;
|
|
when others =>
|
|
return;
|
|
end case;
|
|
when '0' to '9' =>
|
|
case state is
|
|
when exponent_mark | exponent_sign | exponent =>
|
|
exp := exp * 10 + char_to_nat (l (i));
|
|
state := exponent;
|
|
when leading | sign | digits =>
|
|
-- Leading "0" are not significant.
|
|
if nbr_digits > 0 or l (i) /= '0' then
|
|
nbr_digits := nbr_digits + 1;
|
|
end if;
|
|
state := digits;
|
|
when decimals =>
|
|
null;
|
|
when dot =>
|
|
state := decimals;
|
|
when others =>
|
|
return;
|
|
end case;
|
|
when 'a' to 'z' | 'A' to 'Z' =>
|
|
case state is
|
|
when digits | decimals =>
|
|
-- "E" has exponent mark.
|
|
if l (i) = 'e' or l(i) = 'E' then
|
|
state := exponent_mark;
|
|
else
|
|
return;
|
|
end if;
|
|
when unit_blank =>
|
|
unit_name (1) := to_lower (l(i));
|
|
state := unit_1;
|
|
when unit_1 =>
|
|
unit_name (2) := to_lower (l(i));
|
|
state := unit_2;
|
|
pos := i;
|
|
when unit_2 =>
|
|
unit_name (3) := to_lower (l(i));
|
|
state := unit_3;
|
|
exit;
|
|
when others =>
|
|
return;
|
|
end case;
|
|
when '.' =>
|
|
case state is
|
|
when digits =>
|
|
state := decimals;
|
|
when others =>
|
|
exit;
|
|
end case;
|
|
when others =>
|
|
exit;
|
|
end case;
|
|
end loop;
|
|
|
|
-- A unit name (2 or 3 letters) must have been found.
|
|
-- The string may end anywhere.
|
|
if state /= unit_2 and state /= unit_3 then
|
|
return;
|
|
end if;
|
|
|
|
-- Compute EXP with the sign.
|
|
if exp_neg then
|
|
exp := -exp;
|
|
end if;
|
|
|
|
-- Search the unit name in the list of time names.
|
|
unit_found := false;
|
|
for i in time_names'range loop
|
|
-- The first two characters must match (case insensitive).
|
|
-- The third character must match if:
|
|
-- * the unit name is a three characters identifier (ie, not a blank).
|
|
-- * there is a third character in STR.
|
|
if time_names (i).name (1) = unit_name (1)
|
|
and time_names (i).name (2) = unit_name (2)
|
|
and (time_names (i).name (3) = ' '
|
|
or time_names (i).name (3) = unit_name (3))
|
|
then
|
|
unit := time_names (i).val;
|
|
unit_found := true;
|
|
-- POS is set to the position of the first invalid character.
|
|
if time_names (i).name (3) = ' ' then
|
|
length := 1;
|
|
else
|
|
length := 2;
|
|
end if;
|
|
if l'left < l'right then
|
|
pos := pos + length;
|
|
else
|
|
pos := pos - length;
|
|
end if;
|
|
exit;
|
|
end if;
|
|
end loop;
|
|
if not unit_found then
|
|
return;
|
|
end if;
|
|
|
|
-- Compute UNIT, the weight of the first non-significant character.
|
|
nbr_digits := nbr_digits + exp - 1;
|
|
if nbr_digits < 0 then
|
|
unit := unit / 10 ** (-nbr_digits);
|
|
else
|
|
unit := unit * 10 ** nbr_digits;
|
|
end if;
|
|
|
|
-- HAS_DIGITS will be set as soon as a digit is found.
|
|
-- No error is expected here (this has been checked during the first
|
|
-- pass).
|
|
has_digits := false;
|
|
for i in l'range loop
|
|
case l (i) is
|
|
when ' '
|
|
| NBSP --V93
|
|
| HT =>
|
|
if has_digits then
|
|
exit;
|
|
end if;
|
|
when '+' | '-' =>
|
|
if not has_digits then
|
|
has_digits := true;
|
|
else
|
|
assert false report "internal error" severity failure;
|
|
return;
|
|
end if;
|
|
when '0' to '9' =>
|
|
-- Leading "0" are not significant.
|
|
if l (i) /= '0' or res /= 0 fs then
|
|
res := res + char_to_nat (l (i)) * unit;
|
|
unit := unit / 10;
|
|
end if;
|
|
has_digits := true;
|
|
when 'a' to 'z' | 'A' to 'Z' =>
|
|
if has_digits then
|
|
exit;
|
|
else
|
|
assert false report "internal error" severity failure;
|
|
return;
|
|
end if;
|
|
when '.' =>
|
|
if not has_digits then
|
|
assert false report "internal error" severity failure;
|
|
return;
|
|
end if;
|
|
when others =>
|
|
assert false report "internal error" severity failure;
|
|
return;
|
|
end case;
|
|
end loop;
|
|
|
|
-- Set VALUE.
|
|
if is_neg then
|
|
value := -res;
|
|
else
|
|
value := res;
|
|
end if;
|
|
good := true;
|
|
trim (l, pos);
|
|
return;
|
|
end read;
|
|
|
|
procedure read (l: inout line; value: out time)
|
|
is
|
|
variable res : boolean;
|
|
begin
|
|
read (l, value, res);
|
|
assert res = true
|
|
report "time read failure"
|
|
severity failure;
|
|
end read;
|
|
|
|
procedure read (l: inout line; value: out string; good: out boolean)
|
|
is
|
|
constant len : natural := value'length;
|
|
begin
|
|
if l'length < len then
|
|
good := false;
|
|
return;
|
|
end if;
|
|
good := true;
|
|
if len = 0 then
|
|
return;
|
|
end if;
|
|
if l'left < l'right then
|
|
value := l (l'left to l'left + len - 1);
|
|
trim (l, l'left + len);
|
|
else
|
|
value := l (l'left downto l'left - len + 1);
|
|
trim (l, l'left - len);
|
|
end if;
|
|
end read;
|
|
|
|
procedure read (l: inout line; value: out string)
|
|
is
|
|
variable res : boolean;
|
|
begin
|
|
read (l, value, res);
|
|
assert res = true
|
|
report "string read failure"
|
|
severity failure;
|
|
end read;
|
|
|
|
end textio;
|