Verilog has no dedicated text type - storing something like a test name or a log message means packing ASCII characters into a fixed-width reg vector and hoping it's wide enough. SystemVerilog fixes this with a real string data type: dynamically sized, easy to compare and slice, and built specifically for the kind of text handling a testbench needs constantly.
8 min read | Beginner Level
What You'll Learn
- What the
stringdata type is and how it differs from packing characters into areg - The operators and built-in methods available on
stringvariables - Why
stringis verification-only, unlike the fixed-width vector workaround - How to convert between strings and numeric types
What is a SystemVerilog string ?
The string data type is an ordered collection of characters. The length of a string variable is the number of characters it holds, which can change dynamically over the course of a simulation. A string variable doesn't behave the same way as a string literal, and no truncation occurs when you use the string type.
Syntax
string variable_name [= initial_value];
variable_name is a valid identifier, and the optional initial_value can be a string literal, the value "" for an empty string, or a string-typed expression. If no initial value is given at declaration, the variable defaults to "", an empty string.
SystemVerilog String Example
module tb;
// Declare a string variable called "dialog" to store string literals
// Initialize the variable to "Hello!"
string dialog = "Hello!";
initial begin
// Display the string using %s string format
$display ("%s", dialog);
// Iterate through the string variable to identify individual characters and print
foreach (dialog[i]) begin
$display ("%s", dialog[i]);
end
end
endmodule
ncsim> run Hello! H e l l o ! ncsim: *W,RNQUIE: Simulation is complete.
How are strings represented in Verilog ?
A single ASCII character requires 8 bits (1 byte), so storing a string in plain Verilog needs as many bytes as there are characters.
Click here to learn about Verilog Arrays
reg [16*8-1:0] my_string; // Can store 16 characters
my_string = "How are you"; // 5 zeros are padded from MSB, and 11 char are stored
my_string = "How are you doing?"; // 18 characters; my_string will get "w are you doing?"
Design vs. Verification: A fixed-width reg vector like my_string above is synthesizable - it maps directly to hardware bits. The string data type is not. It's a verification-only construct that exists purely for testbench code and can never appear in synthesizable RTL.
Quick Check
Q: Why does assigning "How are you doing?" (18 characters) to a 16-character-wide my_string result in "w are you doing?" instead of an error?
Show Answer
A fixed-width vector can't grow to fit the string, so the assignment silently keeps only the rightmost 16 characters and drops the first 2 ("H" and "o") from the front. This kind of silent truncation is exactly what the string type avoids.
String Operators
| Category | Operator | Semantics |
|---|---|---|
| Equality | Str1 == Str2 | Returns 1 if the two strings are equal and 0 if they are not |
| Inequality | Str1 != Str2 | Returns 1 if the two strings are not equal and 0 if they are |
| Comparison | Str1 < Str2, Str1 <= Str2, Str1 > Str2, Str1 >= Str2 | Returns 1 if the corresponding condition is true and 0 if false |
| Concatenation | {Str1, Str2, ..., StrN} | All strings are concatenated into one resultant string |
| Replication | {multiplier{Str}} | Replicates the string N times, where N is the multiplier |
| Indexing | Str[index] | Returns a byte - the ASCII code at the given index. Returns 0 if the index is out of range |
| Methods | Str.method([args]) | The dot (.) operator is used to call string functions |
Example
module tb;
string firstname = "Joey";
string lastname = "Tribbiani";
initial begin
// String Equality : Check if firstname equals or not equals lastname
if (firstname == lastname)
$display ("firstname=%s is EQUAL to lastname=%s", firstname, lastname);
if (firstname != lastname)
$display ("firstname=%s is NOT EQUAL to lastname=%s", firstname, lastname);
// String comparison : "<" compares lexicographically by ASCII value, not by length
if (firstname < lastname)
$display ("firstname=%s is LESS THAN lastname=%s", firstname, lastname);
// String comparison : ">" compares lexicographically by ASCII value, not by length
if (firstname > lastname)
$display ("firstname=%s is GREATER THAN lastname=%s", firstname, lastname);
// String concatenation : Join first and last names into a single string
$display ("Full Name = %s", {firstname, " ", lastname});
// String Replication
$display ("%s", {3{firstname}});
// String Indexing : Get the ASCII character at index number 2 of both first and last names
$display ("firstname[2]=%s lastname[2]=%s", firstname[2], lastname[2]);
end
endmodule
ncsim> run firstname=Joey is NOT EQUAL to lastname=Tribbiani firstname=Joey is LESS THAN lastname=Tribbiani Full Name = Joey Tribbiani JoeyJoeyJoey firstname[2]=e lastname[2]=i ncsim: *W,RNQUIE: Simulation is complete.
String comparison is lexicographic, based on ASCII values - "Joey" < "Tribbiani" because 'J' (ASCII 74) comes before 'T' (ASCII 84), the same way you'd compare words alphabetically.
Basic String Methods
SystemVerilog also includes a number of special methods to work with strings, using built-in method notation.
| Usage | Definition | Comments |
|---|---|---|
| str.len() | function int len() | Returns the number of characters in the string |
| str.putc() | function void putc (int i, byte c); | Replaces the ith character in the string with the given character |
| str.getc() | function byte getc (int i); | Returns the ASCII code of the ith character in str |
| str.tolower() | function string tolower(); | Returns a string with characters in str converted to lowercase |
| str.compare(s) | function int compare (string s); | Compares str and s, as in the ANSI C strcmp function |
| str.icompare(s) | function int icompare (string s); | Compares str and s, ignoring case |
| str.substr (i, j) | function string substr (int i, int j); | Returns a new string formed by the characters in position i through j of str |
Example
module tb;
string str = "Hello World!";
initial begin
string tmp;
// Print length of string "str"
$display ("str.len() = %0d", str.len());
// Assign to tmp variable and put char "d" at index 3
tmp = str;
tmp.putc (3,"d");
$display ("str.putc(3, d) = %s", tmp);
// Get the character at index 2
$display ("str.getc(2) = %s (%0d)", str.getc(2), str.getc(2));
// Convert all characters to lower case
$display ("str.tolower() = %s", str.tolower());
// Comparison
tmp = "Hello World!";
$display ("[tmp,str are same] str.compare(tmp) = %0d", str.compare(tmp));
tmp = "How are you ?";
$display ("[tmp,str are diff] str.compare(tmp) = %0d", str.compare(tmp));
// Ignore case comparison
tmp = "hello world!";
$display ("[tmp is in lowercase] str.icompare(tmp) = %0d", str.icompare(tmp));
tmp = "Hello World!";
$display ("[tmp,str are same] str.compare(tmp) = %0d", str.compare(tmp));
// Extract new string from i to j
$display ("str.substr(4,8) = %s", str.substr (4,8));
end
endmodule
str.len() = 12 str.putc(3, d) = Heldo World! str.getc(2) = l (108) str.tolower() = hello world! [tmp,str are same] str.compare(tmp) = 0 [tmp,str are diff] str.compare(tmp) = -1 [tmp is in lowercase] str.icompare(tmp) = 0 [tmp,str are same] str.compare(tmp) = 0 str.substr(4,8) = o Wor
Common Mistake: str.compare() mirrors ANSI C's strcmp - it returns 0 for equal strings and a nonzero value otherwise, not a boolean 1/0. Don't treat a nonzero result as "true" the way you might with ==.
String Conversion Methods
| Method | Definition | Description |
|---|---|---|
| str.atoi() | function integer atoi(); | Returns the integer corresponding to the ASCII decimal representation in str |
| str.atohex() | function integer atohex(); | Interprets the string as hexadecimal |
| str.atooct() | function integer atooct(); | Interprets the string as octal |
| str.atobin() | function integer atobin(); | Interprets the string as binary |
| str.atoreal() | function real atoreal(); | Returns the real number corresponding to the ASCII decimal representation in str |
| str.itoa(i) | function void itoa (integer i); | Stores the ASCII decimal representation of i into str |
| str.hextoa(i) | function void hextoa (integer i); | Stores the ASCII hexadecimal representation of i into str |
| str.octtoa(i) | function void octtoa (integer i); | Stores the ASCII octal representation of i into str |
| str.bintoa(i) | function void bintoa (integer i); | Stores the ASCII binary representation of i into str |
| str.realtoa(r) | function void realtoa (real r); | Stores the ASCII real representation of r into str |
Where Are Strings Used?
Testbenches use string constantly - test names passed via +UVM_TESTNAME, log and error messages built with $sformatf, config file paths, and comparing a DUT's ASCII output against an expected string in a reference model. None of this could be done cleanly with a fixed-width reg vector.
Common Beginner Mistakes
Mistake #1: Assuming string can be used in synthesizable RTL.
string is verification-only. If you need text-like storage in a real design, you're back to a fixed-width reg/logic vector, with the truncation behavior shown earlier.
Mistake #2: Off-by-one errors with string indexing.
Like arrays, string indexing is 0-based. firstname[2] is the third character, not the second - easy to miscount when a string is short.
Mistake #3: Treating str.compare()'s return value as a boolean.
It returns 0 for equality and a nonzero value otherwise, matching ANSI C's strcmp - not a straightforward 1-for-true result.
Practice Exercise
Challenge: Extract a Substring
Given string str = "SystemVerilog";, write a single line using str.substr() that extracts just the word "Verilog".
Hint
Index positions are 0-based. Count characters starting from 0 to find where "Verilog" begins and ends within "SystemVerilog".
Solution
str.substr(6,12)
"SystemVerilog" has "System" occupying indices 0-5, so "Verilog" starts at index 6 and ends at index 12 (the last character, "g").
Summary
In this tutorial, you learned:
- The
stringdata type is a dynamically-sized, verification-only text type - unlike a fixed-widthregvector, it never silently truncates - Strings support equality, comparison, concatenation, replication, and indexing operators
- Built-in methods like
len(),putc(),getc(),tolower(),compare(), andsubstr()cover most everyday string manipulation - Conversion methods like
atoi()anditoa()move data between string and numeric representations