Chapter 7. Improved I/O

Previous versions of VHDL provided fairly basic forms of binary and text I/O. In this chapter, we describe additions to the I/O and string conversion features provided by VHDL-2008.

The To_string Functions

In previous versions of VHDL, converting a value to a string required the use of the 'image attribute or the write procedures from the textio package. These options were very limiting. The 'image attribute was only defined for scalar types, and the write procedures were only defined for the predefined scalar types and for bit_vector. VHDL-2008 adds predefined to_string operations as a flexible alternative for string conversion. As a function, to_string is overloadable, supports scalar and composite types, and can have multiple parameters. In addition, for all bit-based array types, octal and hexadecimal string conversion functions are defined: to_ostring and to_hstring, respectively (see Section 7.1.3).

Example 7.1. Writing messages using to_string

One use of these functions is in assert statements, for example:

assert expected_val = read_val
   report "Expected Val /= Actual Val." &
             "Expected = " & to_string(expected_va1) &
             "Actual = "   & to_string(read_val)
  severity error;

Another use is with VHDL’s built-in write procedure (not std.textio.write) as follows:

if expected_val =  read_val then
   err_cnt := err_cnt + 1;
   write(output,
     "%%%Error: Expected Val /= Actual Val. " &
     "   Expected = " & to_string(expected_val) &
     "   Actual = "   & to_string(read_val) &
     "  at time: "  & to_string(now));
end if;

This call to the write procedure has a similar effect to a sequence of calls to the write procedures defined in the textio package, followed by a call to the writeline procedure. However, it is clearly much more concise.

Predefined To_string Functions

A basic form of to_string is predefined with the following signature.

to_string [AType return string]

AType includes all scalar types and single dimensional array types whose element types contain only character literals. The string return value for various types is formed as follows:

  • For a value of an enumeration type other than character, if the value is a character literal, to_string returns the value as a single-element string; otherwise, the function returns the name of the identifier in lower case letters in a string. For example,

    to_string(bit'('0'))

    returns the string "0", and

    to_string(file_open_status '(OPEN_OK))

    returns the string "open_ok"

  • For a character value, to_string returns the character in a single-element string. Note that this may be different from the result for non-character types. In the case of control-character values, the result is a single-element string containing the control character, not a string containing the name of the control character.

  • For a one-dimensional array value containing only character literals, to_string returns a string of the same length as the array containing the element values converted to type character. For example, to_string(bit_vector'("0110")) returns the string of characters "0110".

  • For a value of an integer type, to_string returns the decimal literal. There is no exponent and no insignificant leading zeros. If the result is negative, the decimal literal is preceded immediately by a minus sign without any intervening space.

  • For a value of a floating point type, including real, to_string returns the decimal literal in standard form consisting of a normalized fraction and an exponent in which the sign is present and the “e” is in lowercase. There are no insignificant leading or trailing zeros. (Note that the floating point types referred to here are the abstract numeric types, not the types defined in the new floating-point packages described in Section 8.5.)

  • For a value of a physical type, to_string returns the decimal literal as an integer, a space, and the unit name. There is no exponent and no insignificant leading zeros. If the result is negative, the decimal literal is preceded immediately by a minus sign without any intervening space.

Overloaded To_string Functions

The basic to_string operations handle most of the required cases. However, there are additional predefined forms of to_string for values of types time and real. First, the following predefined forms provide textio-style formatting:

function to_string ( value: time; unit : time )
                          return string;

function to_string ( value: real; digits: natural )
                           return string;

The version for type time formats the value as an integer or real literal in multiples of unit. In the version for type real, digits specifies the number of digits that are to appear to the right of the decimal point.

Second, for type real, the following predefined form of to_string provides C-style formatting of the value:

function to_string ( value: real;  format: string )
                           return string;

The format parameter should contain a C-style format specification, such as is used in the C printf command. For example, assuming the variable x has the value 52.5:

• to_string (value => x, format => "%f") returns "52.5"
• to_string (value => x, format => "%5.2f") returns "52.50"
• to_string (value => x, format => "%E") returns "5.250000E+01"
• to_string (value => x, format => "%6.2e")returns "5.25e+0lW
• to_string (value => (x*10.0), format => "%g")returns "525"

The basic to_string operations are predefined for the vector types defined in packages std_logic-1 1164, numeric_bit and numeric_std, since the types just contain character elements ('0', 'I', 'L', 'II', and so on). However, the fixed-point and floating-point packages described in Chapter 8 overload the basic operations to provide more useful results. In particular, the overloaded versions defined for the fixed-point types ufixed and sfixed return a string with a radix point (a '.' character) at the appropriate position, for example, "100 1.00 10". The overloaded version for the floating-point type float returns a string with colon characters between the sign, exponent, and fraction bits, for example, "0:111011:00010001110".

The to_ostring and To_hstring Functions

In addition to binary string conversion, VHDL-2008 also adds octal and hexadecimal string conversion functions, to_ostring and to_hstring, respectively, for all bit-based array types. The signatures for these functions are:

to_ostring [BitArrayType return string]

to_hstring [BitArrayType return string]

BitArrayType includes bit_vector defined in the package standard, std_ulogic_vector defined in std_logic_1164, unsigned and signed defined in numeric_std, and unsigned and signed defined in numeric_bit. For these types, characters are implicitly added to the left of the array value to make the length a multiple of 3 (for to_ostring) or 4 (for to_hstring), so that complete octal or hexadecimal digits can be formed. The characters are added as follows:

  • For an array of type bit_vector, ‘0’ characters are added.

  • For an array of type std_ulogic_vector, std_logic_vector, or unsigned, if the leftmost element of the array is ‘Z’ or ‘XI, then ‘Z’ or ‘XI characters, respectively, are added; otherwise, ‘0’ characters are added.

  • For an array of type signed, the characters added are the same as the leftmost element of the array.

For array types based on either std_ulogic or std_logic, if all of the elements corresponding to an octal or hexadecimal digit contain ‘Z’ then the resulting character is ‘Z’. Otherwise, to_X01 is applied to the group of elements. If the result contains an ‘XI, the octal or hexadecimal digit is ‘X’. If the result contains only ‘0’ and ‘1’ values, they are converted to a normal octal or hexadecimal digit in upper case.

BitArrayType is supported for the fixed-point types ufixed and sfixed defined in the fixed-point package (see Section 8.4). For these types, characters before the radix point are handled as for unsigned and signed, respectively. Then the radix point is included in the string. For characters following the radix point, ‘0’ characters are added to the right of the array value to make the length of the fractional part (after the radix point) a multiple of 3 (for to_ostring) or 4 (for to_hstring). The fractional part is then converted as for unsigned values. For values in which the radix-point position lies outside the index range, to_ostring and to_hstring extend the value to include the radix point in the result. For example, a to_hstring operation for the value “10100” with index range 7 down to 3 would result in the string "A0.0", corresponding to the binary number 10100000.0. Similarly, a to_hstring operation for the value “10100” with index range -3 down to -7 would result in “0.28” (0.0010100 in binary).

BitArrayType is also supported for type float in the floating-point package (see Section 8.5). For this type, characters are added to the left of the array value string to make the length a multiple of 3 (for to_ostring) or 4 (for to_hstring). If the left most element of the array is ‘Z’ then ‘Z’ characters are added; otherwise, ‘0’ characters are added.

Note that for consistency, there is also a to_bstring operation defined for each of the types. However, it is simply an alias for the to_string function. There are further aliases defined for the operations: to_binary_string, to_octal_string, and to_hex_string. Some designers may consider these to be more readable than the shorter function names.

The Justify Function

One facility provided by the write operations in the textio package but not provided by the to_string functions is the ability to justify a string representation in a field of a given width. This is useful for tabular formatting of output. VHDL-2008 adds a justify function to the textio package to provide fixed-width formatting for string values. The function is defined as follows:

function justify ( value   : string;
                 justified : side  := right;
                 field     : width := 0 ) return string;

The value parameter contains the string value to be formatted, and the justified and field parameters are used in the same way as in the write procedures in textio. When the field parameter is greater than the length of value, the value is justified within the string by adding spaces to the left or right of the result for right, depending on the justified parameter. If the field parameter is less than or equal to the length of value, the value is returned unchanged.

Example 7.2. Tabular formatting of trace output

Suppose we wish to write trace output from a model and have it formatted in fixed-width columns. Each line of output consists of the current simulation time, a 16-bit unsigned value in hexadecimal format, and an integer counter value. We can write the values using the following write procedure call:

write( output, justify(to_string(now, ns),  width => 10),
                justify(to_hstring(out_vec), width =>  6),
                justify(to_string(count),    width => 10) );

Successive calls might yield the following output:

    20  ns XXXX      0
   120  ns ZZOO      1
   220  ns FFCO     10
   320  ns 0000     31

Newline Formatting

In some applications, we need to create a message string that would be too long to fit on a single line of output. If we are using write or writeline operations, we could split the message and write each line with a separate operation. However, VHDL does not guarantee that lines written by different processes during the same simulation cycle will not be interleaved. If we are generating a message using an assert or report statement, we would not want the additional text associated with an assertion violation to be included with each line of the message.

VHDL-2008 allows us to create multiline messages in these cases by using the linefeed character (LF) as a newline character. This interpretation applies to a report string in an assert or report statement and to a string written to a file of type text using a write, writeline, or tee procedure (see Section 7.5). The host operating system translates the LF character to whatever convention is used to represent a new line. For example, a UNIX based system would represent the new line using just the LF character, whereas a Windows system would represent it using a carriage return (CR) followed by a LF.

Example 7.3. Multiline output to a text file

We can use LF characters in a multiline message written to the standard output text file using a write procedure call:

write(output,  "%%%ERROR data value miscompare." & LF &
               " Actual value = "  & to_hstring(data) & LF &
               " Expected value = "& to_hstring(expdata) & LF &
               " at time: "        & to_string(now) );

Read and Write Operations

In earlier versions of VHDL, textual I/O operations were limited to values of predefined types, for which read and write operations were defined in the standard textio package. VHDL-2008 broadens the support for textual I/O by adding operations for all of the standard types in their respective packages. It also adds octal and hexadecimal I/O and enhances the string I/O capability.

The basic I/O operations added in each package are:

procedure write ( L : inout line; value : in AType ;
                     justified : in side := right;
                     field : in width := 0 );

procedure read  ( L : inout line; value : out AType ;
                     good : out boolean );

procedure read  ( L : inout line; value : out AType );

These operations behave in the same way as the corresponding operations for predefined types in the textio package. The write operation executes as if the following call to the textio package write operation were executed with the to_string operation (see Section 7.1) applied to the parameter:

write (L, to_string(value), justified, field);

The new write operations are defined for std_ulogic_vector and std_logic_vector in the std_logic_1164 package, for unsigned and signed in numeric_std, for unsigned and signed in numeric_bit, for ufixed and sfixed in the fixed-point packages (see Section 8.4), and for float in the floating-point packages (see Section 8.5).

Each read procedure skips white space, and then reads std_logic values until it encounters white space or a non_std_ulogic value, or until it has read value'length characters. Underscore characters ("-") embedded within the value are skipped, though it is an error if two underscores appear consecutively. The procedure must read enough characters to fill all of the elements of the value array, so it is an error if a space or an invalid character is encountered before value'length characters are read. The read procedures for ufixed and sfixed also accept a radix point (".") in the input, though it is an error if the radix point is not at the appropriate position. Specifically, the characters before the radix point must fill elements of the value parameter with non-negative indices, and the characters after the radix point must fill elements with negative indices. An error occurs if the radix point is encountered at a position other than between the characters corresponding to indices 0 and -1. Similarly, the read procedures for float accept ":" and "." delimiters between the sign, exponent, and fraction parts of the input, though it is an error if they are not at the appropriate positions.

The support for octal and hexadecimal I/O takes the form of the following procedures:

procedure owrite ( L : inout  line; value : in AType ;
                      justified : in side := right;
                      field : in width := 0 );

procedure hwrite ( L : inout line; value : in AType ;
                      justified : in side :=  right;
                      field : in width := 0 );

procedure oread ( L : inout line;  value : out AType;
                     good : out boolean );

procedure oread ( L : inout line;  value : out AType );

procedure hread ( L : inout line;  value : out  AType;
                     good : out boolean );

procedure hread ( L:  inout line;  value : out AType);

These operations also behave in the same way as the corresponding operations for predefined types in the textio package, but with octal or hexadecimal conversion applied. The owrite and hwrite operations execute as if the following calls to the textio package write operation were executed with the to_ostring and to_hstring operations (see Section 7.1) applied to the parameters:

write  (L, to_ostring (value), justified,  field);  -- owrite

write  (L, to_hstring (value), justified,  field);  -- hwrite

The operations are defined for the predefined type bit_vector, for std_ulogic_vector and std_logic_vector defined in the std_logic_1164 package, for unsigned and signed in numeric_std, and for unsigned and signed in numeric_bit. The behavior of the oread and hread operations in these cases is as follows. Each operation must read sufficient characters to fill the value argument, or an error occurs. Since value need not be a multiple of 3 (for oread) or 4 (for hread) in length, the length is rounded up to the nearest multiple of 3 or 4 to determine how many characters to read. Oread (hread) starts by skipping white space. It then reads octal (hexadecimal) digits until it encounters white space or a non-octal (non-hexadecimal) character other than "-", or until it has read sufficient characters to fill the value argument. Underscore characters embedded within the octal (hexadecimal) value are skipped. Oread converts each octal digit (0-7) to its 3-bit representation, and hread converts each hexadecimal digit (0-9, a-f, or A-F) to its 4-bit representation. For array types based on std_ulogic or std_logic, the characters ‘XI and ‘Z’ are also permitted. For octal, these characters are repeated 3 times in the result; hence, a ‘Z’ input is expanded to “ZZZ”. Similarly, for hexadecimal, these characters are repeated 4 times in the result; hence, a ‘Z’ input is expanded to “ZZZZ”. If conversion of characters to groups of 3 or 4 elements result in more elements than the length of the value argument, only the rightmost elements are used. Depending on the values of the discarded elements, an error may occur. If the type of the value argument is bit_vector, std_ulogic_vector, std_logic_vector, or unsigned, an error occurs if any of the discarded elements are ‘1’. For example, an hread that reads the characters “82” (”10000010’’ in binary) into a 6-bit unsigned value produces an error, since the two discarded bits are “10”. If the type of the value argument is signed, an error occurs if the discarded elements are not all the same as the leftmost element used for the value argument. For example, an hread that read the characters “7F” into a 6-bit signed value produces an error, since the two discarded bits are “01”, and the leftmost bit used for value is ‘1’.

The octal and hexadecimal write and read operations are also defined for types ufixed and sfixed in the fixed-point packages (see Section 8.4). The behavior of the oread and hread operations in these packages is as follows. Oread and hread each reads the value prior to the radix point as described above for unsigned or signed (depending on whether the value parameter is ufixed or sfixed, respectively). For the characters following the radix point, oread and hread each reads the value as described above for std_ulogic_vector; however, instead of discarding elements on the left, the operations discard elements on the right. An error occurs if an element discarded on the right is a ‘1’. The radix point may be explicitly included in the input, but an error occurs if it is not at the appropriate position (that is, between the characters corresponding to indices 0 and -1 of the value parameter). The radix point may also be omitted, in which case it is assumed at the appropriate position.

Finally, the octal and hexadecimal write and read operations are defined for the type float in the floating-point packages (see Section 8.5). The behavior of the oread and hread operations depends on whether ":" or "." delimiters are used in the input to separate the sign, exponent, and fraction parts of a floating-point number. When ":" delimiters are used (with the input formatted as “S:EEEE:FFFFFFFF”), the sign bit, the exponent, and the fraction are each read as separate octal or hexadecimal values using the same rules as described above for std_ulogic_vector values. When a delimiter is used (with the input formatted as “SEEEE.FFFFFFFF”), the rules described above for reading ufixed values are used. The value read before the radix point forms the part of the result comprising the sign and exponent elements, and the value read after the radix point forms the fraction part of the result. When no delimiters are used in the input, the entire float value is read as a single hexadecimal value as described above for std_ulogic_vector values.

Note that, for consistency, there are also definitions of binary I/O operations in each of the packages. However, they are simply aliases for the basic operations, defined as follows:

alias bwrite is  write [line, AType, side, width];

alias bread is read [line, AType, boolean];

alias bread is read [line, AType ];

In addition to the enhanced I/O operations for binary, octal, and hexadecimal values, VHDL-2008 adds enhanced I/O operations for character strings. The new swrite procedure in package textio writes a string value in the same way as the write procedure overloaded for a string value parameter. The difference is that there are no other overloaded versions of swrite, so we do not have to use type qualification when writing a string literal. We can write a call such as:

swri te(L, "The answer is: ");

Compare this with a call to the write operation:

write(L, string' ("The answer is: "));

We need to use the type qualification to distinguish the type of the string from other character array types (such as bit_vector) for which write is defined. Use of swrite to write string literals makes the model much clearer.

The new sread procedure in package textio reads string-based tokens. It is defined as follows:

procedure sread ( L : inout line;
                  value : out string; strlen : out natural );

The procedure skips leading white space and then reads consecutive non-white space characters, up to as many as will fit in the value parameter. The number of characters read is returned in the strlen parameter. If a white-space character stops reading before value is filled, the result in strlen will be less than the length of value. The remaining unfilled characters in value are not specified and should not be used. If no valid characters are read (for example, if the input is blank up to the end of the line), the strlen result value is 0.

The Tee Procedure

The textio package in previous versions of VHDL provided the file output for displaying messages to a user-interface. If we also wanted to log the messages in a separate file for subsequent analysis, we had to duplicate the write operations: once to output and once to the separate file. VHDL-2008 adds a tee procedure to the textio package that writes a line both to the file output and to a separate named file. This allows us to avoid replicated write operations. The definition of tee is:

procedure tee (file F : text; L : inout line);

The effect of TEE is the same as the statements:

write     (output, L.all & LF);
writeline (F, L) ;

Example 7.4. Logging output messages

Suppose we wish to write trace messages to a simulator’s user interface and to log the messages to a file named trace.log. We can do this using calls to tee in place of writeline, as follows:

use std.textio.al1 ;
file tracefile : text open write_mode is "trace.log";
variable L : line;
. . .

swrite(L, justify(to_string(now, ns), field => 10) &
          " starting operation ");
tee(tracefile, L);
. . .

The Flush Procedure

VHDL-2008 adds a predefined file flush procedure that requests that the effect of all previous calls to the write procedure for a file be completed. The procedure flush is predefined for all file types as follows:

procedure flush ( file F : FT );

When the flush procedure is called, the file must be opened in write or append mode; otherwise, an error occurs.

One use of flush is to ensure that all outstanding write operations to an external file are completed before read operations are performed on a separate file object associated with the same external file. Another use is to ensure that prompt messages written to a user interface appear before read operations take input from the user interface. It is important to note, however, that the flush operation simply requests that the host operating system complete the outstanding writes. It does not guarantee that the request will be met. In particular, host systems that use distributed network file systems may find it difficult to reliably honor flush requests.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.191.169