Skip to content
VLSI Mentor

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:

Azvya Education Pvt. Ltd.VLSI Mentor
storage.v
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:

Azvya Education Pvt. Ltd.VLSI Mentor
what greet actually holds
  'H'   'e'   'l'   'l'   'o'
  0x48  0x65  0x6C  0x6C  0x6F

  greet = 40'h48_65_6C_6C_6F

That 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:

Azvya Education Pvt. Ltd.VLSI Mentor
sizing.v — say the character count in the declaration
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 buffer

3. 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.
Azvya Education Pvt. Ltd.VLSI Mentor
literals.v
"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 lines

4. Escape Sequences

Verilog-2001 defines a small set:

EscapeByteMeaning
\n0x0ANewline
\t0x09Tab
\\0x5CA literal backslash
\"0x22A literal double quote
\dddvariesThe byte whose value is the octal digits ddd
%%0x25A literal % — inside a format string only

\ddd takes one to three octal digits and lets you write any byte directly:

Azvya Education Pvt. Ltd.VLSI Mentor
octal-escape.v
"\012"     // 0x0A — the same byte as \n
"\033"     // 0x1B — ESC
"\377"     // 0xFF — the largest byte value

Two 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:

Azvya Education Pvt. Ltd.VLSI Mentor
too-narrow.v
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:

Azvya Education Pvt. Ltd.VLSI Mentor
too-wide.v
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:

Azvya Education Pvt. Ltd.VLSI Mentor
uses.v
// 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:

Azvya Education Pvt. Ltd.VLSI Mentor
print.v
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.

Azvya Education Pvt. Ltd.VLSI Mentor
compare.v
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:

Azvya Education Pvt. Ltd.VLSI Mentor
fixed-slot.v
// one six-character slot, everything padded to it
reg [8*6-1:0] state_name;

initial state_name = "IDLE  ";   // padded to six characters

9. Joining Strings

There is no + for strings in Verilog-2001. You join them the way you join any two vectors — with the concatenation operator {}:

Azvya Education Pvt. Ltd.VLSI Mentor
concat.v
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

Azvya Education Pvt. Ltd.VLSI Mentor
exercise-2.v
reg [8*4-1:0] s;

initial s = "Hello";

What does s end up holding, and why?

Exercise 3 — Count the characters

Azvya Education Pvt. Ltd.VLSI Mentor
exercise-3.v
"say \"hi\""

How many characters is this, and how wide is the literal?

Exercise 4 — Explain the comparison

Azvya Education Pvt. Ltd.VLSI Mentor
exercise-4.v
reg [8*5-1:0] a = "READY";
reg [8*6-1:0] c = "READY ";

Why is a == c false?

Exercise 5 — Join two strings

Azvya Education Pvt. Ltd.VLSI Mentor
exercise-5.v
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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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.

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.