Verilog · Chapter 4.5 · Lexical Conventions
String Handling in Verilog
Verilog-2001 has no string type. What looks like a string is characters packed into an ordinary vector, eight bits per character — and once you know that, everything else about Verilog strings follows from it. This lesson shows you how a quoted literal is stored, how to write one and what the escape sequences do, how to size the vector that holds it, and what happens when the vector and the literal are different widths. You will also see why comparing two strings is really comparing two bit vectors, and where strings genuinely turn up in the kind of code you will write.
Foundation14 min readVerilogStringsASCIISyntax
Chapter 4 · Page 4.5 · Lexical Conventions
1. Verilog Has No String Type
That sentence sounds like a problem. It is actually the shortcut, because it means there is only one thing to learn:
A Verilog string literal is characters packed into an ordinary vector — eight bits per character.
There is no special type, no length field, no terminating byte. It is a vector like any other, and everything you already know about vectors from Number Representation applies unchanged.
So when you meet a quoted string in Verilog, do not read it as text. Read it as a value with a width.
2. A String Is Characters Packed Into a Vector
Take five characters:
reg [8*5-1:0] greet; // 40 bits — room for five characters
initial greet = "Hello";"Hello" is five characters, and each one occupies eight bits, so the literal is forty bits wide. Those forty bits are the ASCII codes laid end to end, first character in the most significant position:
'H' 'e' 'l' 'l' 'o'
0x48 0x65 0x6C 0x6C 0x6F
greet = 40'h48_65_6C_6C_6FThat is the whole storage model. Two consequences worth stating now, because every other rule on this page comes from them:
- The first character sits at the top of the vector. The last character sits at the bottom.
- The width is fixed by the declaration, not by the text. A vector does not grow or shrink to fit a string.
The [8*5-1:0] form is worth copying. Written that way, the character count is visible in the declaration itself:
reg [8*5 - 1:0] mode = "READY"; // five characters -> 40 bits
reg [8*7 - 1:0] label = "VLSI-MR"; // seven characters -> 56 bits
reg [8*64 - 1:0] line_buf; // a 64-character buffer3. Writing a String Literal
Three rules cover the syntax.
- A string literal goes in double quotes. Single quotes belong to number literals (Chapter 4.4) and are not string delimiters.
- A literal must sit on one source line. Verilog-2001 has no multi-line string.
- Anything you cannot type directly — a quote, a backslash, a newline — goes in as an escape sequence.
"Hello, world" // ordinary
"He said \"go\"" // an embedded double quote
"C:\\design\\rtl" // embedded backslashes
"line one\nline two" // an embedded newline
// "line one
// line two" // not legal — a literal cannot span lines4. Escape Sequences
Verilog-2001 defines a small set:
| Escape | Byte | Meaning |
|---|---|---|
\n | 0x0A | Newline |
\t | 0x09 | Tab |
\\ | 0x5C | A literal backslash |
\" | 0x22 | A literal double quote |
\ddd | varies | The byte whose value is the octal digits ddd |
%% | 0x25 | A literal % — inside a format string only |
\ddd takes one to three octal digits and lets you write any byte directly:
"\012" // 0x0A — the same byte as \n
"\033" // 0x1B — ESC
"\377" // 0xFF — the largest byte valueTwo things worth knowing about this table. First, it is short on purpose — escapes borrowed from C such as \r, \f and \a are accepted by many simulators but are not in the Verilog-2001 set, so a portable file should not rely on them. Second, there is no hexadecimal escape: octal is the only numeric form.
And % is not an escape at all. Inside an ordinary string literal it is simply the byte 0x25. It only becomes special inside a format string, where %% produces one literal % — see §7.
5. When the Vector and the Literal Are Different Widths
The literal has a width. The vector has a width. They do not have to agree, and what happens when they disagree is exactly the rule from Chapter 4.4 §11 — read here in characters instead of bits.
The vector is too narrow. The value is truncated, and truncation keeps the least-significant bits. For a string, the least-significant bits are the last characters — so it is the beginning of the string that goes:
reg [8*4-1:0] short; // 32 bits — four characters
initial short = "Hello"; // the literal is 40 bits
// the top eight bits — the 'H' — do not fit
// short holds "ello"The vector is too wide. The value is zero-extended on the left, so the characters sit at the bottom of the vector with zero bytes above them:
reg [8*8-1:0] buf; // 64 bits — eight characters
initial buf = "Hi"; // the literal is 16 bits
// buf = 64'h0000_0000_0000_4869
// six zero bytes, then 'H' 'i'Neither case is an error, and both are easy to miss when you are reading the source line rather than counting characters. The habit that avoids the whole category is the one from the declarations above:
Count the characters, and write the count into the declaration.
reg [8*5-1:0]for a five-character string says what you meant, and a reader can check it without counting bits.
6. Where Strings Actually Turn Up
Strings are not hardware. No chip stores your text as text. They appear in three places in ordinary Verilog work:
// 1. Simulation output — by far the most common
$display("state = %s, count = %0d", state_name, count);
// 2. Naming a file for a system task
$readmemh("init.hex", mem);
// 3. As a parameter value
parameter MODE_NAME = "BYPASS";The first is the one you will meet constantly. The point made in Chapter 3 applies here: the message is for the person watching the simulation, not data inside your design.
The third is worth a brief note, because it is the exception to "strings are testbench-only". A string-valued parameter is a legitimate way to configure a module, and synthesis tools generally accept one used that way — comparing it against a constant to pick a structure, for example. What a synthesis tool will not do is build you text-processing hardware.
7. Printing a String
%s is the format code that prints a value as characters:
reg [8*5-1:0] mode = "READY";
initial $display("mode = %s", mode); // prints: mode = READY%s reads its argument eight bits at a time from the top down and prints each group as one character. That is why the zero-padding in §5 matters in practice: printing a 64-bit vector holding "Hi" prints six NUL bytes before the H, which most terminals render as blanks.
The rest of the format codes — %d, %h, %b, %c, %t, %m and the %0 variants — belong to the display tasks themselves, and are covered in $display vs $monitor vs $strobe vs $write, Chapter 8.1.
8. Comparing Strings
Because a string is a vector, == is a bit-vector comparison. Both sides must be the same width for it to mean what you expect.
reg [8*5-1:0] a = "READY"; // 40 bits
reg [8*5-1:0] b = "READY"; // 40 bits
if (a == b) ... // true — same width, same bits
reg [8*6-1:0] c = "READY "; // 48 bits — note the trailing space
if (a == c) ... // false — a is zero-extended to 48 bits,
// so its top byte is 0x00 while c's is 'R'Nothing surprising is happening — it is ordinary vector equality on values that happen to hold characters. The practical answer is to pick one width for a given purpose and use it everywhere:
// one six-character slot, everything padded to it
reg [8*6-1:0] state_name;
initial state_name = "IDLE "; // padded to six characters9. Joining Strings
There is no + for strings in Verilog-2001. You join them the way you join any two vectors — with the concatenation operator {}:
reg [8*3-1:0] part_a = "Hi "; // 24 bits
reg [8*5-1:0] part_b = "World"; // 40 bits
reg [8*8-1:0] msg; // 64 bits
initial msg = {part_a, part_b}; // 24 + 40 = 64 -> "Hi World"The widths have to add up to the destination, or the result is truncated or extended by the rules in §5. The {} operator itself is Concatenation Operator, Chapter 10.10.
10. A Note on SystemVerilog
You will hear about a string type with a length, a + operator and methods like .len(). That is SystemVerilog, a different language standard, and none of it exists in the Verilog-2001 this chapter teaches.
It is worth knowing the distinction exists so you are not confused when you meet SystemVerilog testbench code — but nothing on this page changes because of it. In a .v file, a string is a packed vector.
11. Common Reading Mistakes
Reading a string as text instead of as a width. "Hello" is forty bits. If you do not know that, none of the truncation or comparison behaviour will make sense.
Using single quotes. 'Hello' is not a Verilog string. Double quotes for strings, single quote for a number's base.
Forgetting that escapes are one character. "say \"hi\"" is eight characters, not ten — the two \" pairs are one byte each. Miscount it and your vector width is wrong.
Expecting truncation to drop the end. It drops the beginning, because the first character lives in the most significant bits. A five-character string in a four-character vector keeps the last four.
Comparing different widths. "READY" and "READY " are not equal, and not because of the space alone — they are different widths, so the shorter one is zero-extended before the comparison.
12. Exercises
Work each one out before reading the answers.
Exercise 1 — Size the vector
How many bits are needed to hold "READY"? Write the declaration.
Exercise 2 — Read the result
reg [8*4-1:0] s;
initial s = "Hello";What does s end up holding, and why?
Exercise 3 — Count the characters
"say \"hi\""How many characters is this, and how wide is the literal?
Exercise 4 — Explain the comparison
reg [8*5-1:0] a = "READY";
reg [8*6-1:0] c = "READY ";Why is a == c false?
Exercise 5 — Join two strings
reg [8*3-1:0] part_a = "Hi ";
reg [8*5-1:0] part_b = "World";What width must the destination be to hold {part_a, part_b}, and what does it contain?
Answers
Exercise 1. Five characters at eight bits each — 40 bits:
reg [8*5-1:0] mode = "READY";Written [8*5-1:0] rather than [39:0], the character count stays visible to the next reader.
Exercise 2. s holds "ello". The literal is 40 bits and the vector is 32, so the value is truncated — and truncation keeps the least-significant bits, which for a string are the last characters. The 'H' sits in the top eight bits and is the part that does not fit.
Exercise 3. Eight characters, so 64 bits. The characters are s, a, y, space, ", h, i, " — each \" escape is a single byte, not two.
Exercise 4. They are different widths: a is 40 bits and c is 48. == is a bit-vector comparison, so a is zero-extended to 48 bits first, putting 0x00 in its top byte where c has 'R'. The values differ from the very first byte. Fix a single slot width and pad everything to it.
Exercise 5. 24 + 40 = 64 bits, so reg [8*8-1:0]. It contains "Hi World" — eight characters, because the widths add up exactly with nothing truncated or padded.
13. Summary
Everything on this page comes from one fact:
A Verilog string is characters packed into a vector, eight bits each, first character in the most significant bits.
From that:
- A literal has a width —
"Hello"is 40 bits. Count the characters and write[8*N-1:0]. - Escapes are one character each, and Verilog-2001's set is small:
\n,\t,\\,\"and\ddd. - Width mismatch behaves like any other vector. Too narrow truncates and loses the beginning of the string; too wide zero-pads above it.
==is vector equality, so widths must match. Pick one slot width and pad to it.{}joins strings, because joining vectors is what{}does. There is no+.- Strings are not hardware. They are for simulation output, file names and the occasional parameter.
Next, Identifier Declaration covers the other kind of name in your source — the ones you invent for signals, modules and instances.
Related Tutorials
- Lexical Conventions — Chapter 4 overview; the parent layer.
- Number Representation — Chapter 4.4; the width and truncation rules strings inherit.
- Identifier Declaration — Chapter 4.6; the next sub-topic.
- Variables & Data Types — Chapter 5; the
regvectors that hold these values. - $display vs $monitor vs $strobe vs $write — Chapter 8.1; the format codes in full.
- Concatenation Operator — Chapter 10.10; the
{}operator itself. - RTL Designing — Chapter 3; where
$displaydebug fits into the RTL workflow.
Standards & specifications
- Governing standard
- IEEE Std 1364 (Verilog)(opens IEEE in a new tab)
Defines the Verilog language and its simulation semantics, including the event scheduling model. Synthesis support is defined by tools, not by this standard.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the Verilog HDL curriculum.
