Multiline strings in VHDL

This article shows how to work with strings containing line breaks in VHDL. Read on to learn how to split long lines in your VHDL code, print text containing newline characters, and how VHDL differs from regular programming languages regarding non-printing control characters in strings.

Splitting string literals in the VHDL code

Sometimes you want to define a long line of text as a string constant or variable while keeping your VHDL code lines short, and that’s easy to do. You can split long and unruly text lines by using the ampersand & concatenation operator:

constant quote : string := "VHDLwhiz helps you understand " &
  "advanced concepts within FPGA design without being overly technical.";

When printing the quote constant during simulation, the two string literals appear as one long line:

# VHDLwhiz helps you understand advanced concepts within FPGA design without being overly technical.

Adding a newline character to a string

This section shows how to add a line feed character in your string to produce a line break once you print the string during simulation.

Line breaks are made up of control characters that differ between systems. Fortunately, VHDL makes it easy to do with its dedicated LF line feed character:

constant banner : string :=
  "__      ___    _ _____  _             _     _     " & LF &
  "\ \    / / |  | |  __ \| |           | |   (_)    " & LF &
  " \ \  / /| |__| | |  | | |  __      _| |__  _ ____" & LF &
  "  \ \/ / |  __  | |  | | |  \ \ /\ / / '_ \| |_  /" & LF &
  "   \  /  | |  | | |__| | |___\ V  V /| | | | |/ / " & LF &
  "    \/   |_|  |_|_____/|______\_/\_/ |_| |_|_/___|";

When printed to the simulator console, the LF characters translate into line breaks:

# __      ___    _ _____  _             _     _     
# \ \    / / |  | |  __ \| |           | |   (_)    
#  \ \  / /| |__| | |  | | |  __      _| |__  _ ____
#   \ \/ / |  __  | |  | | |  \ \ /\ / / '_ \| |_  /
#    \  /  | |  | | |__| | |___\ V  V /| | | | |/ / 
#     \/   |_|  |_|_____/|______\_/\_/ |_| |_|_/___|

According to the VHDL Language Reference Manual, the LF character shall translate into the representation of the end of a line on whatever system you are running. Thus, the LF character may produce slightly different log files between operating systems.

For completeness, let’s see what happens if we use the "\r\n" sequence instead, which means carriage return + line feed (CR+LF) in many programming languages:

constant banner_not_working : string :=
  "__      ___    _ _____  _             _     _     \r\n" &
  "\ \    / / |  | |  __ \| |           | |   (_)    \r\n" &
  " \ \  / /| |__| | |  | | |  __      _| |__  _ ____\r\n" &
  "  \ \/ / |  __  | |  | | |  \ \ /\ / / '_ \| |_  /\r\n" &
  "   \  /  | |  | | |__| | |___\ V  V /| | | | |/ / \r\n" &
  "    \/   |_|  |_|_____/|______\_/\_/ |_| |_|_/___|";

It doesn’t work because VHDL doesn’t treat the backslash as an escape sequence. Instead, they are printed literally to the simulator console:

# __      ___    _ _____  _             _     _     \r\n\ \    / / |  | |  __ \| |           | |   (_)    \r\n \ \  / /| |__| | |  | | |  __      _| |__  _ ____\r\n  \ \/ / |  __  | |  | | |  \ \ /\ / / '_ \| |_  /\r\n   \  /  | |  | | |__| | |___\ V  V /| | | | |/ / \r\n    \/   |_|  |_|_____/|______\_/\_/ |_| |_|_/___|

Printing a single line in VHDL

And finally, when printing using the report statement or the writeline procedure, a newline always follows the printed text. For example, after importing use std.textio.all; at the top of the VHDL file, we can define this procedure that prints a string to the simulator console:

procedure print(msg : string) is
  variable l : line;
begin
  write(l, msg);
  writeline(output, l);
end procedure;

Then we could call the new print procedure once for each string we want to print on an individual line:

print("__      ___    _ _____  _             _     _     ");
print("\ \    / / |  | |  __ \| |           | |   (_)    ");
print(" \ \  / /| |__| | |  | | |  __      _| |__  _ ____");
print("  \ \/ / |  __  | |  | | |  \ \ /\ / / '_ \| |_  /");
print("   \  /  | |  | | |__| | |___\ V  V /| | | | |/ / ");
print("    \/   |_|  |_|_____/|______\_/\_/ |_| |_|_/___|");

That would have the same effect as calling print on the banner constant we defined earlier, containing the LF line breaks:

print(banner);

The output to the simulator console in both cases is:

# __      ___    _ _____  _             _     _     
# \ \    / / |  | |  __ \| |           | |   (_)    
#  \ \  / /| |__| | |  | | |  __      _| |__  _ ____
#   \ \/ / |  __  | |  | | |  \ \ /\ / / '_ \| |_  /
#    \  /  | |  | | |__| | |___\ V  V /| | | | |/ / 
#     \/   |_|  |_|_____/|______\_/\_/ |_| |_|_/___|

Summary

  • Ampersand & is the string concatenation operator in VHDL
  • LF is VHDL’s line feed/newline/end of line/EOL character
  • The backslash “\” character has no special meaning in string literals
  • The report statement and TEXTIO’s writeline procedure produce a newline after the printed text

Similar Posts

3 Comments

  1. Thanks for the article! Very useful

    I’ve always seen printing using both write line and report in testbenches also noticed that report gets registered in the msg field in the waveform window view, while writeline doesn’t….

    Sometimes also seen that it is preferred declaring a print procedure with write and writeline instead using the report function which this last I think it would be quicker and will require less coding.
    just wondering, is there any advantage of using one method or the other (report vs writeline)? Or both would be equivalent?

    1. Hello, and thanks for commenting!

      It’s entirely up to you whether to use the report statement or the textio package to print text. However, this is how I use them:

      I use report only for temporary debugging printouts. Because, as you say, they appear as tick marks above the waveform when simulating.

      However, report clutters the transcript window with all the simulation time and iteration (delta cycle) information.

      If you need to know the time value, it’s better to define a procedure that prints it before your message like this:

      procedure print(msg : string) is
          variable l : line;
        begin
          write(l, to_string(now) & " - " & msg);
          writeline(output, l);
        end procedure;
      

      At least in Questa/ModelSim, you can click the time values in the transcript window, and the cursor will automatically jump to that point in the waveform. That’s what I usually do.

      1. Hello Jonas, thanks for taking the time to answer.
        Good point, I’ve never thought that it might “clutter” the transcript, definitely is a good thing to be aware of.
        Despite (as I mentioned), I’ve seen using different approaches personally I most of the time used a similar approach as you described but mainly because most of the time I needed to generate a tb output file log result to add to the reports, so I’ve used an additional procedure to print out into a file and the report for debugging.

Leave a Reply

Your email address will not be published. Required fields are marked *