---------------------------------------------------------------------------

-- Package TEXTIO as defined in Chapter 14 of the IEEE Standard VHDL

--   Language Reference Manual (IEEE Std. 1076-1987), as modified

--   by the Issues Screening and Analysis Committee (ISAC), a subcommittee

--   of the VHDL Analysis and Standardization Group (VASG) on

--   10 November, 1988.  See "The Sense of the VASG", October, 1989.

---------------------------------------------------------------------------

-- Version information: %W% %G%

---------------------------------------------------------------------------

 

package TEXTIO is

    type LINE is access string;

    type TEXT is file of string;

    type SIDE is (right, left);

    subtype WIDTH is natural;

 

       -- changed for vhdl92 syntax:

    file input : TEXT open read_mode is "STD_INPUT";

    file output : TEXT open write_mode is "STD_OUTPUT";

 

       -- changed for vhdl92 syntax (and now a built-in):

    procedure READLINE(file f: TEXT; L: out LINE);

 

    procedure READ(L:inout LINE; VALUE: out bit; GOOD : out BOOLEAN);

    procedure READ(L:inout LINE; VALUE: out bit);

 

    procedure READ(L:inout LINE; VALUE: out bit_vector; GOOD : out BOOLEAN);

    procedure READ(L:inout LINE; VALUE: out bit_vector);

 

    procedure READ(L:inout LINE; VALUE: out BOOLEAN; GOOD : out BOOLEAN);

    procedure READ(L:inout LINE; VALUE: out BOOLEAN);

 

    procedure READ(L:inout LINE; VALUE: out character; GOOD : out BOOLEAN);

    procedure READ(L:inout LINE; VALUE: out character);

 

    procedure READ(L:inout LINE; VALUE: out integer; GOOD : out BOOLEAN);

    procedure READ(L:inout LINE; VALUE: out integer);

 

    procedure READ(L:inout LINE; VALUE: out real; GOOD : out BOOLEAN);

    procedure READ(L:inout LINE; VALUE: out real);

 

    procedure READ(L:inout LINE; VALUE: out string; GOOD : out BOOLEAN);

    procedure READ(L:inout LINE; VALUE: out string);

 

    procedure READ(L:inout LINE; VALUE: out time; GOOD : out BOOLEAN);

    procedure READ(L:inout LINE; VALUE: out time);

 

       -- changed for vhdl92 syntax (and now a built-in):

    procedure WRITELINE(file f : TEXT; L : inout LINE);

 

    procedure WRITE(L : inout LINE; VALUE : in bit;

             JUSTIFIED: in SIDE := right;

             FIELD: in WIDTH := 0);

 

    procedure WRITE(L : inout LINE; VALUE : in bit_vector;

             JUSTIFIED: in SIDE := right;

             FIELD: in WIDTH := 0);

 

    procedure WRITE(L : inout LINE; VALUE : in BOOLEAN;

             JUSTIFIED: in SIDE := right;

             FIELD: in WIDTH := 0);

 

    procedure WRITE(L : inout LINE; VALUE : in character;

             JUSTIFIED: in SIDE := right;

             FIELD: in WIDTH := 0);

 

    procedure WRITE(L : inout LINE; VALUE : in integer;

             JUSTIFIED: in SIDE := right;

             FIELD: in WIDTH := 0);

 

    procedure WRITE(L : inout LINE; VALUE : in real;

             JUSTIFIED: in SIDE := right;

             FIELD: in WIDTH := 0;

             DIGITS: in NATURAL := 0);

 

    procedure WRITE(L : inout LINE; VALUE : in string;

             JUSTIFIED: in SIDE := right;

             FIELD: in WIDTH := 0);

 

    procedure WRITE(L : inout LINE; VALUE : in time;

             JUSTIFIED: in SIDE := right;

             FIELD: in WIDTH := 0;

             UNIT: in TIME := ns);

 

       -- is implicit built-in:

       -- function ENDFILE(file F : TEXT) return boolean;

 

    -- function ENDLINE(variable L : in LINE) return BOOLEAN;

    --

    -- Function ENDLINE as declared cannot be legal VHDL, and

    --   the entire function was deleted from the definition

    --   by the Issues Screening and Analysis Committee (ISAC),

    --   a subcommittee of the VHDL Analysis and Standardization

    --   Group (VASG) on 10 November, 1988.  See "The Sense of

    --   the VASG", October, 1989, VHDL Issue Number 0032.

end;

 

--*******************************************************

--**                                                   **

--** Copyright (c) Model Technology Incorporated 1991  **

--**               All Rights Reserved                 **

--**                                                   **

--*******************************************************

 

package body TEXTIO is

    constant MAX_LINE : integer := 500;

       -- Maximum number of characters allowed in an input line

       --   by the READLINE routine.

 

    constant MAX_DIGITS : integer := 20;

       -- Number of decimal digits which can be processed by the

       --   integer input and output routines.  Includes leading

       --   minus sign, should be large enough for 64-bit integers.

 

    subtype int_string_buf is string(1 to MAX_DIGITS);

 

    -- V-System VHDL will round time values below the base simulation

    --   unit to 0 when the model is loaded (more precisely, the

    --   internal integer representation of a time value is divided

    --   by the integer number of femtoseconds in the base time unit,

    --   which results in values less than the base time unit

    --   becoming zero).  It is possible to determine the simulation

    --   time unit by scanning the following list for the first

    --   non-zero entry.  ns is used in the declaration of all times

    --   because textio is not a legal program unless the base time

    --   unit is less than or equal to ns (ns is used in the

    --   declaration of the version of WRITE which outputs time

    --   values!).

 

    type time_unit_enum is

                  (u_fs,  u_ps,  u_ns,  u_us,  u_ms,  u_sec, u_min, u_hr);

    type time_unit_name_array is array (time_unit_enum) of string(1 to 3);

    constant time_unit_names: time_unit_name_array

              := ("fs ", "ps ", "ns ", "us ", "ms ", "sec", "min", "hr ");

    type time_array is array (time_unit_enum) of time;

    constant find_base_unit: time_array

                := (1.0E-6 ns,    -- fs

                  1.0E-3 ns,       -- ps

                       1 ns,

                       1 us,

                      1 ms,

                      1 sec,

                      1 min,

                      1 hr);

 

    procedure Int_to_string(

       constant val : in integer;

       variable result:  out int_string_buf;

       variable last: out  integer)

    is

       variable buf : string(MAX_DIGITS downto 1);

       variable pos : integer := 1;

       variable tmp : integer := abs(val);

       variable digit : integer;

    begin

       loop

           digit := abs(tmp MOD 10);      -- MOD of integer'left returns neg number!

           tmp := tmp / 10;

           buf(pos) := character'val(character'pos('0') + digit);

           pos := pos + 1;

           exit when tmp = 0;

       end loop;

       if val < 0 then

           buf(pos) := '-';

           pos := pos + 1;

       end if;

       pos := pos - 1;

       result(1 to pos) := buf(pos downto 1);

       last := pos;

    end Int_to_string; -- procedure

 

    function Int_to_string(val : integer)

       return string

    is

       variable buf : int_string_buf;

       variable last : integer;

    begin

       Int_to_string(val, buf, last);

       return buf(1 to last);

    end Int_to_string; -- function

 

    procedure READLINE(file f: TEXT; L: out LINE)

    --procedure READLINE(variable f: in TEXT; L : inout LINE)

    is

       variable buf : string(1 to MAX_LINE);

       variable len : integer := 0;

       variable c : character;

    begin

       --if L /= NULL then

       --    Deallocate(L);

       --end if;

       if not Endfile(f) then

           READ(f, buf, len);

           assert len <= MAX_LINE

              report "Textio: Truncated input line greater than "

                     & Int_to_string(MAX_LINE)

                     & " characters."

              severity ERROR;

       end if;

       if (len > 0) and (buf(len) = LF) then

           len := len - 1;

       end if;

       L := new string'(buf(1 to len));

    end;

 

    procedure Skip_white(variable L : in LINE; pos : inout integer)

    is

    begin

       while pos <= L'high loop

           case L(pos) is

              when ' ' | HT  =>

                  pos := pos + 1;

              when others =>

                  exit;

           end case;

       end loop;

    end;

 

    procedure Shrink_line(L : inout LINE; pos : in integer)

    is

       variable old_L : LINE := L;

    begin

       if pos > 1 then

           L := new string'(old_L(pos to old_L'high));

           Deallocate(old_L);

       end if;

    end;

 

    procedure Grow_line(L : inout LINE; incr : in integer)

    is

       variable old_L : LINE := L;

       variable bfp: integer;     -- Blank fill pointer.

    begin

       assert incr > 0

           report "Textio: Grow_line called with zero increment."

           severity error;

 

       if L = null then

           bfp := 0;

           L := new string(1 to incr);

       else

           bfp := old_L'high;

           L := new string(old_L'low to old_L'high + incr);

           L(old_L'low to old_L'high) := old_L.all;

           Deallocate(old_L);

       end if;

       for i in 1 to incr loop

           L(bfp + i) := ' ';

       end loop;

    end;

 

 

    procedure Report_results(good : boolean; read_type : string)

    is

    begin

       assert good

           report "Could not read type " & read_type & " from line."

           severity error;

    end;

 

    function lower_case(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;

 

    -- compare two strings ignoring case

    function strcmp(a, b : string) return boolean

    is

       alias a_val : string(1 to a'length) is a;

       alias b_val : string(1 to b'length) is b;

       variable a_char, b_char : character;

    begin

       if a'length /= b'length then

           return false;

       elsif a = b then

           return true;

       end if;

       for i in 1 to a'length loop

           a_char := lower_case(a_val(i));

           b_char := lower_case(b_val(i));

           if a_char /= b_char then

              return false;

           end if;

       end loop;

       return true;

    end;

 

    procedure Extract_integer(

       variable L:          inout LINE;

       variable pos:        inout integer;

       variable value:        out integer;

       variable ok:           out boolean)

    is

       variable sign: integer := 1;

       variable rval: integer := 0;

    begin

       ok := FALSE;

       if pos < L'right and (L(pos) = '-' or L(pos) = '+') then

           if L(pos) = '-' then

              sign := -1;

           end if;

           pos := pos + 1;

       end if;

 

       -- Once the optional leading sign is removed, an integer can

       --   contain only the digits '0' through '9' and the '_'

       --   (underscore) character.  VHDL disallows two successive

       --   underscores, and leading or trailing underscores.

 

       if pos <= L'right and L(pos) >= '0' and L(pos) <= '9' then

           while pos <= L'right loop

              if L(pos) >= '0' and L(pos) <= '9' then

                  rval := rval * 10

                          + character'pos(L(pos)) - character'pos('0');

                  ok := TRUE;

              elsif L(pos) = '_' then

                  if pos = L'right

                  or L(pos + 1) < '0'

                  or L(pos + 1) > '9' then

                     ok := FALSE;

                     exit;

                  end if;

              else

                  exit;

              end if;

              pos := pos + 1;

           end loop;

       end if;

 

       value := sign * rval;

    end Extract_integer;

 

    procedure Extract_real(

       variable L:          inout LINE;

       variable pos:        inout integer;

       variable value:        out real;

       variable ok:         inout boolean)

    is

       variable sign:        real := 1.0;

       variable rval:        real := 0.0;

 

        procedure Apply_mantissa(

            variable L:          inout LINE;

            variable pos:        inout integer;

            variable rval:       inout real;

            variable ok:         inout boolean)

        is

        begin

            -- this procedure reads numeric characters and the '_' character until

            -- encountering a '.' character. It converts these characters into a

            -- real number and indicates any problems through the ok parameter.

 

            ok := FALSE;

            rval := 0.0;

            if pos <= L'right and L(pos) >= '0' and L(pos) <= '9' then

                while pos <= L'right and L(pos) /= '.' and L(pos) /= ' ' and L(pos) /= HT  loop

                    if L(pos) >= '0' and L(pos) <= '9' then

                        rval := rval*10.0 + real(character'pos(L(pos)) - character'pos('0'));

                        pos := pos+1;

                        ok := true;

                    elsif L(pos) = '_' then

                        if pos+1 <= L'right then

                            if L(pos+1) >= '0' and L(pos+1) <= '9' then

                                pos := pos+1;

                            else

                                ok := false;

                                exit;

                            end if;

                        else

                            ok := false;

                            exit;

                        end if;

                    else

                        ok := false;

                        exit;

                    end if;

                end loop;

            end if;

        end;

 

        procedure Apply_fraction(

            variable L:          inout LINE;

            variable pos:        inout integer;

            variable rval:       inout real;

            variable ok:         inout boolean)

        is

            variable powerten:     real := 0.1;

        begin

            -- this procedure reads numeric characters and the '_' character from a

            -- line variable and converts them into a fractional number.  It indicates

            -- the status of the conversion throught the ok parameter.

            ok := false;

            if pos <= L'right then

                while pos <= L'right and ((L(pos) >= '0' and L(pos) <= '9') or L(pos) = '_') loop

                    if L(pos) = '_' then

                        if pos+1 <= L'right then

                            if L(pos+1) >= '0' and L(pos+1) <= '9' then

                                pos := pos+1;

                            else

                                ok := false;

                                exit;

                            end if;

                        else

                          ok := false;

                          exit;

                        end if;

                    else

                        rval := rval + (real(character'pos(L(pos))-character'pos('0'))*powerten);

                        powerten := powerten*0.1;

                        pos := pos+1;

                        ok := true;

                    end if;

                end loop;

            end if;

        end;

 

 

        procedure Apply_exponent(

            variable L:         inout LINE;

            variable pos:       inout integer;

            variable rval:      inout real;

            variable ok:        inout boolean)

        is

            variable int_val:   integer:=0;

                     variable sign : integer := 1;

        begin

            -- this procedure reads in numeric characters and the '_' character and

            -- uses them as an exponent for the rval parameter.  It indicates the

            -- success of the operation through the ok parameter.

 

            ok := false;

            if pos <= L'right then

                           if (L(pos) = '+') then

                                  pos := pos + 1;

                           elsif (L(pos) = '-') then

                                  sign := -1;

                                  pos := pos + 1;

                           end if;

                while pos <= L'right and ((L(pos) >= '0' and L(pos) <= '9') or L(pos) = '_') loop

                    if L(pos) >= '_' then

                        if pos+1 <= L'right then

                            if L(pos+1) >= '0' and L(pos+1) <= '9' then

                                pos := pos+1;

                            else

                                ok := false;

                                exit;

                            end if;

                        else

                          ok := false;

                          exit;

                        end if;

                    else

                        if int_val <= integer'high/10 then

                            int_val := int_val*10 + (character'pos(L(pos)) - character'pos('0'));

                            pos := pos+1;

                            ok := true;

                        else

                            assert false report "Overflow in Exponent of real number!" severity failure;

                            ok := false;

                            exit;

                        end if;

                    end if;

                end loop;

                if ok then

                    rval := rval*(10.0**(int_val * sign));      

                end if;

            end if;

        end;

 

    begin

       ok:= FALSE;

       pos := L'left;

       Skip_white(L, pos);

       if (pos <= L'right) and (L(pos) = '-') then

           sign := -1.0;

           pos := pos + 1;

       end if;

        Apply_mantissa(L,pos,rval,ok);  -- get number before decimal point

       if ok and pos <= L'right and L(pos) = '.' then

           pos := pos + 1;

            Apply_fraction(L,pos,rval,ok);  -- get fraction after decimal (before exponent)

            if ok and pos <= L'right and (L(pos) = 'E' or L(pos) = 'e') then

              pos := pos + 1;

              Apply_exponent(L,pos,rval,ok);  -- get fraction

            end if;

        end if;

        if ok then

            value := rval * sign;

        end if;

    end;

 

    -----------------------------------------------------------------

    -- Bit reading

    -----------------------------------------------------------------

    procedure READ(L:inout LINE; VALUE: out bit; GOOD : out BOOLEAN)

    is

       variable pos : integer;

       variable ok  : boolean := FALSE;

    begin

       if L /= NULL then

           pos := L'left;

           Skip_white(L, pos);

           if pos <= L'right then

              if L(pos) = '0' then

                  VALUE := '0';

                  ok := TRUE;

              elsif L(pos) = '1' then

                  VALUE := '1';

                  ok := TRUE;

              end if;

           end if;

       end if;

 

       GOOD := ok;

       if ok then

           Shrink_line(L, pos + 1);

       end if;

    end;

 

    procedure READ(L:inout LINE; VALUE: out bit)

    is

       variable GOOD : BOOLEAN;

    begin

       READ(L, VALUE, GOOD);

       Report_results(GOOD, "BIT");

    end;

 

    -----------------------------------------------------------------

    -- Bit vector reading

    -----------------------------------------------------------------

 

    procedure READ(L:inout LINE; VALUE: out bit_vector; GOOD : out BOOLEAN)

    is

       alias    val  : bit_vector(1 to VALUE'length) is VALUE;

       variable vpos : integer := 0;     -- Index of last valid bit in val.

       variable lpos : integer;   -- Index of next unused char in L.

    begin

       if L /= NULL then

           lpos := L'left;

           Skip_white(L, lpos);

           while lpos <= L'right and vpos < VALUE'length loop

              if L(lpos) = '0' then

                  vpos := vpos + 1;

                  val(vpos) := '0';

              elsif L(lpos) = '1' then

                  vpos := vpos + 1;

                  val(vpos) := '1';

              else

                  exit;     -- Bit values must be '0' or '1'.

              end if;

              lpos := lpos + 1;

           end loop;

       end if;

 

       if vpos = VALUE'length then

           GOOD := TRUE;

           Shrink_line(L, lpos);

       else

           GOOD := FALSE;

       end if;

    end;

 

    procedure READ(L:inout LINE; VALUE: out bit_vector)

    is

       variable GOOD : BOOLEAN;

    begin

       READ(L, VALUE, GOOD);

       Report_results(GOOD, "BIT_VECTOR");

    end;

 

    -----------------------------------------------------------------

    -- BOOLEAN reading

    -----------------------------------------------------------------

 

    procedure READ(L:inout LINE; VALUE: out BOOLEAN; GOOD : out BOOLEAN)

    is

       variable pos : integer;

       variable len : integer;

       variable ok  : boolean := FALSE;

    begin

       if L /= NULL then

           pos := L'left;

           Skip_white(L, pos);

           len := L'right - pos + 1;

           if len >= 4 and strcmp(L(pos to pos + 3), "true") then

              ok := TRUE;

              VALUE := TRUE;

              pos := pos + 4;

           elsif len >= 5 and strcmp(L(pos to pos + 4), "false") then

              ok := TRUE;

              VALUE := FALSE;

              pos := pos + 5;

           end if;

       end if;

 

       GOOD := ok;

       if ok then

           Shrink_line(L, pos);

       end if;

    end;

 

    procedure READ(L:inout LINE; VALUE: out BOOLEAN)

    is

       variable GOOD : BOOLEAN;

    begin

       READ(L, VALUE, GOOD);

       Report_results(GOOD, "BOOLEAN");

    end;

 

    -----------------------------------------------------------------

    -- CHARACTER reading

    -----------------------------------------------------------------

 

    procedure READ(L:inout LINE; VALUE: out character; GOOD : out BOOLEAN)

    is

    begin

       if L /= NULL and L'length > 0 then

           GOOD := TRUE;

           VALUE := L(L'left);

           Shrink_line(L, L'left + 1);

       else

           GOOD := FALSE;

       end if;

    end;

 

    procedure READ(L:inout LINE; VALUE: out character)

    is

       variable GOOD : BOOLEAN;

    begin

       READ(L, VALUE, GOOD);

       Report_results(GOOD, "CHARACTER");

    end;

 

    -----------------------------------------------------------------

    -- INTEGER reading

    -----------------------------------------------------------------

 

    procedure READ(L: inout LINE; VALUE: out integer; GOOD: out BOOLEAN)

    is

       variable posinteger;

       variable rval: integer := 0;

       variable expinteger := 0;

       variable ok:   boolean := FALSE;

    begin

       if L /= NULL then

           pos := L'left;

           Skip_white(L, pos);

           Extract_integer(L, pos, rval, ok);

           if  ok

           and pos < L'right

           and (L(pos) = 'E' or L(pos) = 'e') then

               pos := pos + 1;

              Extract_integer(L, pos, exp, ok);

              if ok then

                  if exp > 0 then

                     rval := rval * 10 ** exp;

                  elsif exp < 0 then

                     ok := FALSE;

                  end if;

              end if;

           end if;

       end if;

 

       GOOD := ok;

       if ok then

           VALUE := rval;

           Shrink_line(L, pos);

       end if;

    end;

 

    procedure READ(L:inout LINE; VALUE: out integer)

    is

       variable GOOD : BOOLEAN;

    begin

       READ(L, VALUE, GOOD);

       Report_results(GOOD, "INTEGER");

    end;

 

    -----------------------------------------------------------------

    -- REAL reading

    -----------------------------------------------------------------

 

    procedure READ(L: inout LINE; VALUE: out real; GOOD : out BOOLEAN)

    is

       variable rval: real;

       variable ok  : boolean := FALSE;

       variable pos : integer;

    begin

       if L /= NULL then

           pos := L'left;

           Skip_white(L, pos);

           Extract_real(L, pos, rval, ok);

       end if;

 

       GOOD := ok;

       if ok then

           VALUE := rval;

           Shrink_line(L, pos);

       end if;

    end;

 

    procedure READ(L: inout LINE; VALUE: out real)

    is

       variable GOOD : BOOLEAN;

    begin

       READ(L, VALUE, GOOD);

       Report_results(GOOD, "REAL");

    end;

 

    -----------------------------------------------------------------

    -- STRING reading

    -----------------------------------------------------------------

 

    procedure READ(L:inout LINE; VALUE: out string; GOOD : out BOOLEAN)

    is

       alias    val  : string(1 to VALUE'length) is VALUE;

       variable vpos : integer := 0;     -- Index of last valid character in val.

       variable lpos : integer;   -- Index of next unused char in L.

    begin

       if L /= NULL then

           lpos := L'left;

           while lpos <= L'right and vpos < VALUE'length loop

              vpos := vpos + 1;

              val(vpos) := L(lpos);

              lpos := lpos + 1;

           end loop;

       end if;

 

       if vpos = VALUE'length then

           GOOD := TRUE;

           Shrink_line(L, lpos);

       else

           GOOD := FALSE;

       end if;

    end;

    procedure READ(L:inout LINE; VALUE: out string)

    is

       variable GOOD : BOOLEAN;

    begin

       READ(L, VALUE, GOOD);

       Report_results(GOOD, "STRING");

    end;

 

    -----------------------------------------------------------------

    -- TIME reading

    -----------------------------------------------------------------

 

    procedure READ(L:inout LINE; VALUE: out time; GOOD : out BOOLEAN)

    is

       variable rval: real;

       variable tval: real;

       variable ok  : boolean := FALSE;

       variable pos : integer;

       variable len : integer;

    begin

       if L /= NULL then

           pos := L'left;

           Skip_white(L, pos);

           Extract_real(L, pos, rval, ok);

           -- The numeric literal is optional. If it doesn't appear,

           --   assume 1.

           if not ok then

              rval := 1.0;

              pos := L'left;

              ok := TRUE;

           end if;

           Skip_white(L, pos);

           len := L'right - pos + 1;

           if len >= 2 then

              if    strcmp(L(pos to pos + 1), "fs") then

                  tval := 1.0e-6;

                  pos := pos + 2;

              elsif strcmp(L(pos to pos + 1), "ps") then

                  tval := 1.0e-3;

                  pos := pos + 2;

              elsif strcmp(L(pos to pos + 1), "ns") then

                  tval := 1.0;

                  pos := pos + 2;

              elsif strcmp(L(pos to pos + 1), "us") then

                  tval := 1.0e3;

                  pos := pos + 2;

              elsif strcmp(L(pos to pos + 1), "ms") then

                  tval := 1.0e6;

                  pos := pos + 2;

              elsif strcmp(L(pos to pos + 1), "hr") then

                  tval := 3600.0 * 1.0e9;

                  pos := pos + 2;

              elsif len >= 3 then

                  if strcmp(L(pos to pos + 2), "sec") then

                     tval := 1.0e9;

                     pos := pos + 3;

                  elsif strcmp(L(pos to pos + 2), "min") then

                     tval := 60.0 * 1.0e9;

                     pos := pos + 3;

                  else

                     ok := FALSE;

                  end if;

              else

                  ok := FALSE;

              end if;

           else

              ok := FALSE;

           end if;

       end if;

 

       GOOD := ok;

       if ok then

           VALUE := (rval * tval) * 1 ns;

           Shrink_line(l, pos);

       end if;

    end;

 

    procedure READ(L:inout LINE; VALUE: out time)

    is

       variable GOOD : BOOLEAN;

    begin

       READ(L, VALUE, GOOD);

       Report_results(GOOD, "TIME");

    end;

 

    procedure WRITELINE(file f : TEXT; L : inout LINE)

    --procedure WRITELINE(f : out TEXT; L : inout LINE)

    is

    begin

       if L /= null then

           write(f, L.all & LF);

           Deallocate(L);

       else

           -- Write a blank line

           write(f, (1 => LF));

       end if;

    end;

 

    procedure WRITE(L : inout LINE; VALUE : in bit;

             JUSTIFIED: in SIDE := right;

             FIELD: in WIDTH := 0)

    is

       variable fw: integer := 1;

       variable new_L: LINE;

       variable bp: integer;

    begin

       if L /= null then

           bp := L'high + 1;

       else bp := 1;

       end if;

       if FIELD < 1 then

           fw := 1;

       elsif FIELD > 1 then

           fw := FIELD;

           if JUSTIFIED = right then

              bp := bp + fw - 1;

           end if;

       end if;

       Grow_line(L, fw);

       L(bp) := character'val(bit'pos(VALUE) + character'pos('0'));

    end;

 

 

    procedure WRITE(L : inout LINE; VALUE : in bit_vector;

             JUSTIFIED: in SIDE := right;

             FIELD: in WIDTH := 0)

    is

       variable fw: integer := VALUE'length;

       variable bp: integer;

       variable offset: integer := 0;

       alias normal : bit_vector(0 to value'length - 1) is value;

    begin

       if L /= null then

           bp := L'high + 1;

       else bp := 1;

       end if;

       if FIELD > VALUE'length then

           fw := FIELD;

           if JUSTIFIED = right then

              offset := fw - VALUE'length;

           end if;

       end if;

       Grow_line(L, fw);

       for i in normal'range loop

           L(bp + i + offset) := character'val(

                  bit'pos(normal(i)) + character'pos('0'));

       end loop;

    end;

 

    procedure WRITE(

       variable L :        inout LINE;

       constant VALUE :    in    BOOLEAN;

       constant JUSTIFIED: in    SIDE := right;

       constant 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;

 

    procedure WRITE(L : inout LINE; VALUE : in character;

             JUSTIFIED: in SIDE := right;

             FIELD: in WIDTH := 0)

    is

       variable fw: integer := 1;

       variable new_L: LINE;

       variable bp: integer;

       variable highest : integer;

    begin

       if L = NULL then

           highest := 0;

       else highest := L'high;

       end if;

       bp := highest + 1;

       if FIELD < 1 then

           fw := 1;

       elsif FIELD > 1 then

           fw := FIELD;

           if JUSTIFIED = right then

              bp := highest + fw;

           end if;

       end if;

       Grow_line(L, fw);

       L(bp) := VALUE;

    end;

 

    procedure WRITE(L : inout LINE; VALUE : in integer;

             JUSTIFIED: in SIDE := right;

             FIELD: in WIDTH := 0)

    is

       variable buf: int_string_buf;

       variable last: integer;

    begin

       Int_to_string(VALUE, buf, last);

       WRITE(L, buf(1 to last), JUSTIFIED, FIELD);

    end;

 

    procedure WRITE(L : inout LINE; VALUE : in real;

             JUSTIFIED: in SIDE := right;

             FIELD: in WIDTH := 0;

             DIGITS: in NATURAL := 0)

    is

       constant max_useful_digits: integer := 7;

           -- Single precision floating point gives almost 7

           --   full digits of precision (not the same as the

           --   DIGITS parameter) on a 386/387, and the VHDL

           --   Language Reference Manual uses 7 in its example of

           --   floating point output, so that's what we'll have.

 

       constant scale_to: real := 10.0 ** max_useful_digits;

           -- The floating point equivalent to seven useful digits.

 

       constant max_digits_spec: integer := 40;

           -- Ignore a digits specification greater than this, since

           --   the decimal exponent range is approximately 10 ** 38

           --   to 10 ** -38.

 

       variable decimal_scale: integer := max_useful_digits - 1;

           -- After scaling, there will be one significant digit

           --   to the left of the decimal point, and the

           --   decimal_scale will be the correct value for

           --   "n.nnnE<exponent>" format printing.

 

       variable scale_factor: real := 1.0;

       variable pos_val: real := VALUE;

       variable int_buf: int_string_buf;

       variable last: integer;

       variable buf: string(1 to 2 * max_digits_spec + 2);

       variable bufp: integer := buf'low; -- Next available char in buf.

       variable cc: integer;

       variable i: integer;

       variable rh_digits: integer := 0;

       variable int_val: integer;

       variable dot_position: integer;

    begin

       if VALUE < 0.0 then

           pos_val := - VALUE;

           buf(bufp) := '-';

           bufp := bufp + 1;

       end if;

       if pos_val = 0.0 then

           int_val := 0;

           decimal_scale := 0;

       elsif pos_val < scale_to then

           while (pos_val * scale_factor * 10.0) < scale_to loop

              decimal_scale := decimal_scale - 1;

              scale_factor := scale_factor * 10.0;

           end loop;

           int_val := integer(pos_val * scale_factor);

       else

           while pos_val / scale_factor > scale_to loop

              decimal_scale := decimal_scale + 1;

              scale_factor := scale_factor * 10.0;

           end loop;

           int_val := integer(pos_val / scale_factor);

       end if;

       Int_to_string(int_val, int_buf, last);

       if last - int_buf'low + 1 > max_useful_digits then

           last := int_buf'low + max_useful_digits - 1;

       end if;

 

       if DIGITS = 0 or DIGITS > max_digits_spec then

           buf(bufp) := int_buf(int_buf'low);

           buf(bufp + 1) := '.';

           bufp := bufp + 2;

           cc := last - int_buf'low;     -- We've already taken the first one.

           if (cc = 0) then

              buf(bufp) := '0';

              bufp := bufp + 1;

           else

              buf(bufp to bufp + cc - 1)

                  := int_buf(int_buf'low + 1 to int_buf'low + cc);

              bufp := bufp + cc;

           end if;

 

           -- Remove trailing zeroes (except the just before the

           --   decimal point which makes this a real number).

           while buf(bufp - 1) = '0' loop bufp := bufp - 1; end loop;

           if    buf(bufp - 1) = '.' then bufp := bufp + 1; end if;

 

           if decimal_scale /= 0 then

              buf(bufp) := 'E';

              bufp := bufp + 1;

              Int_to_string(decimal_scale, int_buf, last);

              cc := last - int_buf'low + 1;

              buf(bufp to bufp + cc - 1) := int_buf(int_buf'low to last);

              bufp := bufp + cc;

           end if;

       else

           if decimal_scale >= 0 then

              -- Add zeroes on the right side.

              dot_position := bufp + decimal_scale + 1;

              buf(dot_position) := '.';

              for i in int_buf'low to last loop

                  if bufp = dot_position then

                     bufp := bufp + 1;    -- Skip the dot.

                  end if;

                  if bufp > dot_position then

                     if rh_digits < DIGITS then

                         rh_digits := rh_digits + 1;

                     else

                         exit;

                     end if;

                  end if;

                  buf(bufp) := int_buf(i);

                  bufp := bufp + 1;

              end loop;

              if bufp <= dot_position then

                  while bufp < dot_position loop

                     buf(bufp) := '0';

                     bufp := bufp + 1;

                  end loop;

                  bufp := bufp + 1;      -- Skip the dot.

              end if;

              for i in rh_digits to DIGITS - 1 loop

                  if rh_digits < DIGITS then

                     rh_digits := rh_digits + 1;

                  else

                     exit;

                  end if;

                  buf(bufp) := '0';

                  bufp := bufp + 1;

              end loop;

           else

              buf(bufp to bufp + 1) := "0.";

              bufp := bufp + 2;

              i := int_buf'low;

              while rh_digits < DIGITS loop

                  if decimal_scale < -1 then

                     buf(bufp) := '0';

                     decimal_scale := decimal_scale + 1;

                  elsif i <= last then

                     buf(bufp) := int_buf(i);

                     i := i + 1;

                  else

                     buf(bufp) := '0';

                  end if;

                  rh_digits := rh_digits + 1;

                  bufp := bufp + 1;

              end loop;

           end if;

       end if;

       WRITE(L, buf(buf'low to bufp - 1), JUSTIFIED, FIELD);

    end;

 

    procedure WRITE(

       variable L        : inout LINE;

       constant VALUE    : in    string;

       constant JUSTIFIED: in    SIDE := right;

       constant FIELD    : in    WIDTH := 0)

    is

       variable bp : integer;

       variable fw : WIDTH := VALUE'length;

    begin

       if VALUE'length > 0 then

           if L = null then

              bp := 1;

           else

              bp := L'high + 1;

           end if;

           if FIELD > VALUE'length then

              fw := FIELD;

              if JUSTIFIED = right then

                  bp := bp + fw - VALUE'length;

              end if;

           end if;

           Grow_line(L, fw);

           L(bp to bp + VALUE'length - 1) := VALUE;

       end if;

    end;

 

    procedure WRITE(

              L : inout LINE;

              VALUE : in time;

             JUSTIFIED: in SIDE := right;

             FIELD: in WIDTH := 0;

             UNIT: in TIME := ns)

    is

       variable base_time_index: time_unit_enum := u_hr;

       variable unit_time_index: time_unit_enum := u_ns;

       variable int_buf: int_string_buf;

       variable buf: string(1 to MAX_DIGITS + 6);

       variable last: integer;

       variable i: integer;

       variable decimal_shift: integer;

       variable is_neg : boolean := (value < 0 ns);

       variable val : time := abs(value);

    begin

       for i in time_unit_enum loop

           if find_base_unit(i) /= 0 hr then

              if base_time_index = u_hr then

                  base_time_index := i;

              end if;

              if UNIT = find_base_unit(i) then

                  unit_time_index := i;

              end if;

           end if;

       end loop;

       assert base_time_index /= u_hr

           report "WRITE: find_base_unit failed."

           severity error;

       if UNIT = 0 hr or unit_time_index < base_time_index then

           -- This may not be a strictly conforming IEEE VHDL

           --   program, since if a UNIT smaller than the base

           --   simulation unit is specified, the program is

           --   in error.  We'll handle the problem gracefully.

           unit_time_index := base_time_index;

       end if;

       Int_to_string(val / find_base_unit(base_time_index), int_buf, last);

       buf(int_buf'range) := int_buf;

       if unit_time_index /= base_time_index then

           decimal_shift := 3 * (time_unit_enum'pos(unit_time_index)

                            - time_unit_enum'pos(base_time_index));

           if last > decimal_shift then

              last := last + 1;

              for i in last downto (last - decimal_shift + 1) loop

                  buf(i) := buf(i - 1);

              end loop;

              buf(last - decimal_shift) := '.';

           else

              i := decimal_shift + 2;

              buf(i - last + buf'low to i) := buf(buf'low to last);

              for i in buf'low to i - last + buf'low - 1 loop

                  buf(i) := '0';

              end loop;

              buf(buf'low + 1) := '.';

              last := i;

           end if;

 

           -- Strip trailing zero's, perhaps even the decimal point!

           while buf(last) = '0' loop

              last := last - 1;

           end loop;

           if buf(last) = '.' then

              last := last - 1;

           end if;

       end if;

 

       -- Add the unit identifier and "print it".

       buf(last + 1) := ' ';

       if time_unit_names(unit_time_index)(3) = ' ' then

           buf(last + 2 to last + 3) :=

              time_unit_names(unit_time_index)(1 to 2);

           last := last + 3;

       else

           buf(last + 2 to last + 4) :=

              time_unit_names(unit_time_index);

           last := last + 4;

       end if;

       if (is_neg) then

           WRITE(L, '-' & buf(buf'low to last), JUSTIFIED, FIELD);

       else

           WRITE(L, buf(buf'low to last), JUSTIFIED, FIELD);

       end if;

    end;

 

end;