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’swriteline
procedure produce a newline after the printed text
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?
Hello, and thanks for commenting!
It’s entirely up to you whether to use the
report
statement or thetextio
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:
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.
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.