Before bsc parses anything, it splits the source characters into tokens:
identifiers, keywords, literals, operators, and punctuation. Parsing, type checking,
and elaboration all work on those tokens, so knowing the token rules tells you
which stage an error message comes from. This chapter follows §2 of the BSV
Language Reference Guide. You only need to read the module, rule, Reg, and
$display code from A module and its interface
and Registers and one rule; most examples
here display a few values from a single rule and stop. One lexical element, the
don't-care value ?, is taught where it is used, together with case, in
Selection, case, and the don't-care value.
1. Identifiers and keywords
1.1 Identifier rules
An identifier names something: a variable, a module, a type. It consists of
letters, digits, _, and $, and it cannot start with a digit. Case matters, so
count and Count are different names.
Syntax
first character (letter, _, $) followed by letters, digits, _, $Bit#(8) data_in; // a valid identifier
Bit#(8) count2; // digits are fine after the first character
Bit#(8) 2count; // error: starts with a digitNames beginning with $ are reserved for system tasks and functions such as
$display and $finish. An instance name beginning with _ is left out of the
hierarchical names in the generated Verilog.
1.2 What the first letter means
BSV uses the case of the first letter to tell kinds of names apart. This is grammar, not style: breaking it is a compile error.
| First letter | Used for | Examples |
|---|---|---|
| Uppercase | Packages, types, interface types, enum labels, tagged-union members, typeclasses | Tally, Bit, Reg, True |
| Lowercase | Variables, modules, interface instances, rules, methods, other values | count, mkTop, step |
Bit#(4) Count = 4'd1; // error: a variable starting with an uppercase letter
Bit#(4) count = 4'd1; // correctThe first line fails with P0005, and the message says that an
identifier starting with lowercase was expected.
1.3 Keywords
A keyword is a word the language reserves, so it can never be an identifier.
module, rule, method, function, if, and case are keywords. BSV
includes every SystemVerilog keyword. Keywords are lowercase except valueOf. The
special type names Action and ActionValue cannot be redefined either.
Bit#(4) rule = 4'b1010; // error: rule is a keyword
Bit#(4) ruleValue = 4'b1010; // correctNo context turns a keyword back into a name. When rule appears where a variable
name belongs, bsc reads it as the start of a new rule and fails.
1.4 Example: a keyword used as a variable name
This example fails to compile on purpose. Inside rule show, a local variable
is named rule. Compilation stops in bsc, so no simulation runs.
Predict before you run. Does the failure come from type checking or from
parsing? Does bsc ever reach the $display line?
Check
Where does KeywordAsIdentifier.bsv stop?
package KeywordAsIdentifier;
(* synthesize *)
module mkTop(Empty);
rule show;
// rule is a keyword, so it cannot also name a variable.
Bit#(4) rule = 4'b1010;
$display("%b", rule);
$finish(0);
endrule
endmodule
endpackageCheck the result.
| Item | Detail |
|---|---|
| Faulty code | The keyword rule used as a local variable name |
| Failing stage | Parsing in bsc; type checking and simulation never run |
| Message | P0005 with Unexpected BSV 3.8 keyword `rule' |
| Fix | Rename it to a lowercase non-keyword such as ruleValue |
Try it yourself. Rename both occurrences to ruleValue. The file compiles and
displays 1010.
2. Whitespace and comments
2.1 Whitespace
Spaces, tabs, newlines, form feeds, and carriage returns may appear freely between tokens. Whitespace only separates tokens; it never joins two pieces into one.
2.2 Line and block comments
Syntax
// a comment to the end of the line
/* a comment that may span lines */Bit#(8) limit = 8'd5; // upper bound
/* the rule below
counts up to limit */| Comment | Ends at | Other comment markers inside it |
|---|---|---|
// | the newline | /* and */ are ordinary text |
/* ... */ | the first */ | // and a second /* are ordinary text |
A comment behaves exactly like whitespace: it separates tokens and never splices them.
Note. Block comments do not nest. In /* a /* b */ c */, the first */ closes
the comment, so c */ is left as source code and parsing fails with P0005. A
block comment that is never closed fails with P0003.
2.3 Example: an identifier split by a comment
This example fails to compile on purpose. It puts a block comment in the middle
of the name value: val/* gap */ue.
Predict before you run. Once the comment is gone, which tokens does bsc see:
one identifier value, or two?
Check
Once the comment in val/* gap */ue is removed, what does bsc see?
package TokenCommentSplit;
(* synthesize *)
module mkTop(Empty);
Bit#(4) val/* gap */ue = 4'b1010;
endmodule
endpackageCheck the result.
| Item | Detail |
|---|---|
| Faulty code | A block comment inside an identifier |
| Failing stage | Parsing in bsc |
| Message | P0005 with Unexpected identifier `ue' |
| Fix | Move the comment between complete tokens, as in value /* gap */ = ... |
The comment turns into whitespace, leaving two identifiers, val and ue. A
declaration cannot have a second identifier there, so the error points at ue.
There is no special "comment inside a token" diagnostic. If P0005 names an
unexpected identifier that looks like half a name, check for a comment or a space
right before it.
3. Integer literals
3.1 Sized literals
A sized literal writes a width, an apostrophe, a base letter, and digits.
Syntax
<width>'<base><digits>| Base letter | Base | Example | Value |
|---|---|---|---|
d or D | decimal | 8'd42 | 42 |
b or B | binary | 8'b1010_1010 | 170 |
o or O | octal | 6'o52 | 42 |
h or H | hexadecimal | 16'hBEEF | 48879 |
Underscores between digits only help reading; they do not change the value. They
are also allowed right after the base letter, so 16'h_BEEF equals 16'hBEEF.
3.2 Unsized literals
Leave out the width and you have an unsized literal: 125, -16, and
'h48454a are all unsized. The surrounding context decides the width.
Bit#(8) a = 125; // context is Bit#(8), so the value is 8 bits
Int#(4) b = -8; // the sign belongs to the literal: the minimum Int#(4)
Int#(4) c = 8; // error: Int#(4) ranges from -8 to 7| Kind | Sign | Initial type | Converted to the context type by |
|---|---|---|---|
| Unsized | part of the literal | Integer | fromInteger from typeclass Literal |
| Sized | not part of the syntax; in -8'd1 the - is unary minus | a bit vector of the stated width | fromSizedInteger from typeclass SizedLiteral |
Int#(4) c = 8; fails with T0051, Literal 8 is not a valid Int#(4). The
literal -8 includes its sign, so it is in range.
3.3 Fill literals
'0 and '1 are fill literals. They set every bit of whatever width the
context needs to 0 or to 1.
Bit#(12) zeros = '0; // all 12 bits zero
Bit#(5) ones = '1; // all 5 bits one: 5'b11111A fill literal has no width of its own. The same '1 produces a different number
of bits under a different declared type.
3.4 Values too large for the width
When a literal's value does not fit, BSV does not drop the upper bits. Compilation fails instead.
| Code | Result |
|---|---|
Bit#(8) x = 8'd300; | T0132, The value 300 does not fit in 8 bits. |
Bit#(8) y = 300; | T0051, Literal 300 is not a valid Bit#(8). |
3.5 Example: printing integer literals
IntegerLiterals.bsv stores several literals in variables and displays them. Each
output line begins with the literal's original spelling, so you can compare
spelling and value side by side. Look for:
decimalandoctal, written in two different bases;hexandhexLeadingUnderscore, which differ only by an underscore;zerosandones, fill literals in aBit#(12)context.
Predict before you run.
- Do
8'd42and6'o52print the same number? - What is
16'h_BEEFin decimal? - How many hexadecimal digits does the
'1 in Bit#(12)line show?
Check
How does the '1 in Bit#(12) line print?
package IntegerLiterals;
(* synthesize *)
module mkTop(Empty);
// Sized literals carry their width and base in the literal itself.
Bit#(8) decimal = 8'd42;
// Underscores between digits do not change the value.
Bit#(8) binary = 8'b1010_1010;
Bit#(16) hex = 16'hBEEF;
// An underscore immediately after a base marker is also ignored.
Bit#(16) hexLeadingUnderscore = 16'h_BEEF;
Bit#(6) octal = 6'o52;
// An unsized literal starts as Integer and converts to the required type.
Bit#(8) unsized = 125;
// Fill literals set every bit of whatever width the context requires.
Bit#(12) zeros = '0;
Bit#(12) ones = '1;
rule show;
$display("8'd42 = %0d", decimal);
$display("8'b1010_1010 = %b", binary);
$display("16'hBEEF = %h", hex);
$display("16'h_BEEF = %0d", hexLeadingUnderscore);
$display("6'o52 = %0d", octal);
$display("unsized 125 = %0d", unsized);
$display("'0 in Bit#(12) = %h", zeros);
$display("'1 in Bit#(12) = %h", ones);
$finish(0);
endrule
endmodule
endpackagePractice this concept
Use this concept in a short BSV problem as your next step.
Check the result.
| Output line | What it shows |
|---|---|
8'd42, 6'o52 | Both are 42; octal 52 is 5×8 + 2 |
8'b1010_1010 | Printed as 10101010, without the underscore |
16'hBEEF, 16'h_BEEF | The same value; the second line prints it in decimal, 48879 |
unsized 125 | The unsized literal became 125 in a Bit#(8) context |
'0 and '1 lines | Twelve bits are three hex digits: 000 and fff |
Try it yourself.
| Change | Expected result |
|---|---|
Bit#(12) ones to Bit#(5) ones | The same '1 fills five bits and prints 1f |
8'd42 to 8'd300 | The value exceeds 8 bits and compilation fails with T0132 |
4. Real literals
4.1 Spelling
A Real literal needs either digits after a decimal point or an exponent.
Syntax
<integer part>.<fraction digits>
<number>e<exponent> // E also works; the exponent may have + or -Real a = 1.2; // decimal point form
Real b = 5e-3; // exponent form, 0.005
Real c = 1_000.5; // underscores may separate digitsA leading + or - is a unary operator, not part of the literal, and a literal
cannot start with an underscore.
4.2 Rounding to binary64
Real literals are rounded to IEEE 754 binary64 (64-bit floating point). A value
that is short in decimal may still have no exact binary form.
| Value | In binary | binary64 encoding |
|---|---|---|
3.125 | exact (2 + 1 + 1/8) | 4009000000000000 |
0.005 | approximate | 3f747ae147ae147b |
A Real literal has type Real by default. Types that provide fromReal through
the RealLiteral typeclass, such as the library's FixedPoint, can accept it too.
Note. Real storage and general floating-point datapaths are not synthesized.
Only Real constant expressions whose values are known at compile time are folded
into bit values.
4.3 Example: two spellings of one value
RealLiterals.bsv writes each value once with a decimal point and once with an
exponent, then compares them. $realtobits returns a Real value's binary64
encoding as a Bit#(64).
Predict before you run.
- Is
3.125 == 3125e-3true? 0.005has no exact binary form. Is0.005 == 5e-3still true?
Check
0.005 has no exact binary64 form. What does the comparison 0.005 == 5e-3 print?
package RealLiterals;
(* synthesize *)
module mkTop(Empty);
// Compile-time Real constants can fold to IEEE 64-bit encodings without synthesizing general floating-point storage or datapaths.
Real decimalA = 3.125;
Real exponentA = 3125e-3;
Real decimalB = 0.005;
Real exponentB = 5e-3;
rule show;
$display("3.125 == 3125e-3: %0d; bits = %h (expected 4009000000000000)",
decimalA == exponentA, $realtobits(decimalA));
$display("0.005 == 5e-3 after IEEE rounding: %0d; bits = %h (expected 3f747ae147ae147b)",
decimalB == exponentB, $realtobits(decimalB));
$finish(0);
endrule
endmodule
endpackageCheck the result.
| Output line | What it shows |
|---|---|
| first line | Comparison 1 and encoding 4009000000000000: an exactly representable value |
| second line | Comparison 1 and encoding 3f747ae147ae147b: both spellings round to the same approximation |
Both encodings are settled at compile time; the module contains no floating-point hardware.
Try it yourself. Replace the second pair with 0.5 and 5e-1. The comparison
still prints 1, and the encoding becomes 3fe0000000000000. One half is a power of
two, so it needs no rounding at all.
5. String literals
5.1 Spelling and escapes
A string literal is enclosed in double quotes and must end on the line where it starts. Characters you cannot type directly, such as a double quote or a newline, are written as escapes.
Syntax
"characters"$display("plain");
$display("say \"hi\"\n"); // escaped quotes and a newline| Escape | Character |
|---|---|
\n | newline |
\t | horizontal tab |
\\ | backslash |
\" | double quote |
\v, \f, \a | vertical tab, form feed, bell |
\OOO | exactly three octal digits, 000 to 377 |
\xHH | exactly two hexadecimal digits, 00 to FF |
Any other escape is an error. Even the familiar \r is not supported in BSV; it
fails with P0091.
A string literal has type String by default, and types that provide
fromString can accept one. Strings are for messages and compile-time work; a
String is not a synthesizable byte array.
5.2 Example: decoding escapes
StringLiterals.bsv displays one kind of escape per line. Count characters after
decoding, not characters in the source.
Predict before you run.
- What do
"\102\123\126"and"\x42\x53\x56"spell? (Octal 102 is decimal 66.) - How many characters does
"\1024"contain?
Check
The octal then digit line holds an octal escape for the letter B followed by the digit 4. How many characters does it decode to?
package StringLiterals;
(* synthesize *)
module mkTop(Empty);
rule show;
// A string literal ends on the source line it starts on.
$display("plain = %s", "BSV");
// Escapes put a quote or a backslash into the string.
$display("quoted = \"BSV\" \\");
// Tab and newline are written as escapes, not as literal characters.
$display("tabbed = a\tb");
// An octal escape takes three digits, a hexadecimal escape takes two.
$display("octal escape = %s", "\102\123\126");
// An octal escape consumes exactly three digits; the trailing 4 is literal.
$display("octal then digit = %s", "\1024");
$display("hex escape = %s", "\x42\x53\x56");
$finish(0);
endrule
endmodule
endpackageCheck the result.
| Output line | What it shows |
|---|---|
quoted | \" and \\ print as a double quote and a backslash |
tabbed | A tab separates a and b |
octal escape, hex escape | Both spell BSV |
octal then digit | B4: only \102 is an escape, and 4 is an ordinary character |
An octal escape consumes exactly three digits, so a digit after it is never ambiguous.
Try it yourself. Change "\1024" to "\1025". The B from the escape stays,
and only the final character becomes 5.
5.3 Example: a short octal escape
This example fails to compile on purpose. The octal escape \10 has only two
digits.
Predict before you run. Does bsc read \10 as the character with code 8, or
reject it?
Check
The string in ShortOctalEscape.bsv has an octal escape with only two digits. What happens?
package ShortOctalEscape;
(* synthesize *)
module mkTop(Empty);
rule show;
$display("%s", "\10");
$finish(0);
endrule
endmodule
endpackageCheck the result.
| Item | Detail |
|---|---|
| Faulty code | The octal escape \10 has two digits |
| Failing stage | Lexical analysis in bsc |
| Message | P0091 with Bad string escape char: `1' |
| Fix | Pad to three digits, \010, which is the character with code 8 |
5.4 Example: a short hexadecimal escape
This example also fails to compile on purpose. Only one hexadecimal digit
follows \x.
package ShortHexEscape;
(* synthesize *)
module mkTop(Empty);
rule show;
$display("\x4");
endrule
endmodule
endpackageCheck the result.
| Item | Detail |
|---|---|
| Faulty code | The hexadecimal escape \x4 has one digit |
| Failing stage | Lexical analysis in bsc |
| Message | P0091 with Bad string escape char: `x' |
| Fix | Write two digits, \x04 |
Case does not matter in hexadecimal digits: \x4a and \x4A are the same
character.
6. Compiler directives
A compiler directive starts with a backtick (`) and rewrites source text
before normal parsing. It need not start in the first column or sit on a line by
itself, but it is not recognized inside strings or comments.
6.1 Including files and adjusting locations
| Directive | Effect |
|---|---|
`include "file" | Inserts the file's contents at this point |
`include <file> | Inserts the file the same way as the quoted form |
`line number "file" level | Sets the file name and line number used by later diagnostics |
`include paths are absolute or relative to the current directory, and the
included file may contain directives too. The level of `line is 1 when
entering an included file, 2 when returning, and 0 otherwise. `line is
mostly used by external preprocessors that want diagnostics to point at the
original source.
6.2 Defining and removing macros
Syntax
`define NAME replacement text
`define NAME(arg1, arg2) replacement text
`undef NAME`define WIDTH 8
`define DOUBLE(v) ((v) + (v))
Bit#(`WIDTH) x = `DOUBLE(8'd6); // Bit#(8) x = ((8'd6) + (8'd6));| Element | Meaning |
|---|---|
`define NAME text | Stores the rest of the line as NAME; a trailing backslash continues it on the next line |
`NAME | Replaced by the stored text, verbatim |
| Formal arguments | Listed in parentheses right after the name and replaced by the actual argument text |
`undef NAME | Removes the definition for the rest of the source |
`resetall | Removes every current macro definition |
Macros are untyped text substitution. A formal used twice copies the argument text twice, and names in the replacement can collide with names at the use site. Parenthesize the whole replacement and each formal in expression-like macros. For reusing arithmetic, a function with real types and scoping is safer.
Note. In a macro body, `" produces a double quote, `\`" a backslash
and double quote, and `` joins the text on both sides with no space.
`bluespec and `BLUESPEC are predefined.
Note. The BSV Language Reference Guide says commas inside strings, comments,
and parentheses do not split actual arguments. With bsc 2026.01, though, commas
inside strings and comments did split arguments and produced P0146; only commas
inside parentheses stayed within one argument. Wrap an argument containing commas
in parentheses, and check the behavior on the bsc version you use.
6.3 Conditional compilation
Syntax
`ifdef NAME
// kept when NAME is defined
`elsif OTHER
// kept when OTHER is defined
`else
// kept when neither is defined
`endif`ifndef keeps its first branch when the name is not defined. You may use
any number of `elsif branches and at most one `else, and groups may
nest. Only the first branch whose condition holds reaches the parser.
Note. Directives choose source text before compilation. They cannot read a register or switch behavior from cycle to cycle.
6.4 Example: macro substitution and a conditional branch
Directives.bsv defines a macro with a formal argument, DOUBLE, and a feature
switch, USE_FEATURE. The declaration of selected depends on which
`ifdef branch survives.
Predict before you run.
- What text does
`DOUBLE(8'd6)become, and what is its value? - Is
selecteda5or5a?
Check
With the define of USE_FEATURE in place, which value does selected get?
package Directives;
// A macro with a formal argument. The invocation substitutes the actual text.
`define DOUBLE(value) ((value) + (value))
`define USE_FEATURE
(* synthesize *)
module mkTop(Empty);
Bit#(8) doubled = `DOUBLE(8'd6);
// Only the branch whose condition holds reaches the compiler.
`ifdef USE_FEATURE
Bit#(8) selected = 8'hA5;
`else
Bit#(8) selected = 8'h5A;
`endif
rule show;
$display("DOUBLE(8'd6) = %0d", doubled);
$display("selected = %h", selected);
$finish(0);
endrule
endmodule
endpackageCheck the result.
| Output line | What it shows |
|---|---|
DOUBLE(8'd6) = 12 | The macro became ((8'd6) + (8'd6)); the argument text appears twice |
selected = a5 | USE_FEATURE is defined, so only the first `ifdef branch survived |
Try it yourself.
| Change | Expected result |
|---|---|
Delete the `define USE_FEATURE line | The `else branch survives and selected becomes 5a |
Replace DOUBLE with function Bit#(8) double(Bit#(8) v) = v + v; and call it | Still 12, but now the argument type is checked and the body has its own scope |
7. Common mistakes
7.1 Uppercase or keyword variable names
// Wrong
Bit#(4) Count = 4'd1; // variable starting with an uppercase letter
Bit#(4) rule = 4'd1; // keyword
// Right
Bit#(4) count = 4'd1;
Bit#(4) ruleValue = 4'd1;Both wrong lines fail to parse with P0005. Variable names start with a lowercase
letter and must not be keywords.
7.2 Nesting block comments
// Wrong: the first */ closes the comment
/* outer /* inner */ still outer */
// Right: use a line comment, or a single block comment
// outer /* inner */ still outerBlock comments do not nest, so the leftover text is read as code and parsing fails
with P0005. To switch off a block of code temporarily, `ifdef is another
option.
7.3 A literal too large for its width
// Wrong
Bit#(8) x = 8'd300; // T0132
// Right: widen the type or fix the value
Bit#(9) x = 9'd300;BSV never silently drops a literal's upper bits. Compare the value and width in the message and decide which one is wrong.
7.4 A macro without parentheses
// Wrong: `TWICE(8'd3) * 2 expands to 8'd3 + 8'd3 * 2, which is 9
`define TWICE(v) v + v
// Right: the same use expands to ((8'd3) + (8'd3)) * 2, which is 12
`define TWICE(v) ((v) + (v))A macro knows nothing about operator precedence. Without parentheses, the expansion binds to the surrounding operators in unintended ways.
8. Summary
| Element | Key point |
|---|---|
| Identifiers | Letters, digits, _, $, not starting with a digit; types start uppercase, values lowercase |
| Keywords | Never usable as identifiers, in any context |
| Comments | Separate tokens like whitespace; block comments do not nest |
| Integer literals | <width>'<base><digits>; unsized literals and '0/'1 take their width from context; an oversized value is a compile error |
Real literals | Rounded to binary64; enter hardware only as compile-time constants |
| Strings | End on their line; octal escapes take three digits, hexadecimal escapes two |
? | The don't-care value, usable only where its value is never observed; see Selection, case, and the don't-care value |
| Directives | Text substitution before parsing; parenthesize expression macros |
9. Check your understanding
-
Why is
val/* gap */uenot the identifiervalue?Show answer
A comment acts as whitespace and separates tokens. Two identifiers remain,
valandue, and a declaration cannot have a second identifier there, so parsing fails withP0005. -
Are
16'o42and16'd42the same value?Show answer
No.
16'o42is octal, 4×8 + 2 = 34, while16'd42is 42. Both are 16 bits wide. -
Int#(4) b = -8;compiles butInt#(4) c = 8;does not. Why?Show answer
Int#(4)holds -8 to 7. In an unsized literal the sign is part of the literal, so-8is in range, while8is not and fails withT0051. -
Explain how
"\1024"becomesB4.Show answer
An octal escape consumes exactly three digits, so
\102is decimal 66, the letterB. The remaining4is an ordinary character. -
With
`define TWICE(v) v + v, the expression`TWICE(8'd3) * 2is 9. How do you make it 12?Show answer
Parenthesize the replacement and each formal:
`define TWICE(v) ((v) + (v)). The use then expands to((8'd3) + (8'd3)) * 2, which is 12. If you also want type checking, a function is the safer choice.
The Practice problems of this chapter follow the integer-literal example. In the first, four 16-bit constants all come out wrong; use the printed values to work out whether the base, the digits, or a fill literal is at fault. In the second, a mask meant to keep only the low four bits lets the whole input through.