Writing testbenches involves writing a lot of code and also requires coding guidelines. These guidelines are designed to enhance code maintainability and readability, as well as to prevent common or obscure mistakes.
The specifics of a guideline may not be important. It is the fact that it is specified and that everyone does it the same way that is important.
Define guidelines as a group, then follow them
Coding guidelines have no functional benefits. Their primary contribution is toward creating a readable and maintainable design.Having common design guidelines makes code familiar to anyone familiar with the implied style, regardless of who wrote it. The primary obstacle to coding guidelines are personal preferences. It is
important that the obstacle be recognized for what it is: personal taste. There is no intrinsic value to a particular set of guidelines.The value is in the fact that these guidelines are shared by the entire group. If even one individual does not follow them, the entire group is diminished.Coding guidelines should be enforced by using a linting tool or code reviews.
1. Use an identical directory structure for every project
2. Use a single module, interface, program or package in a file.
3. Specify files required by the current file using the `include directive.
4. Surround source files with ‘ifndef, ‘define and ‘endif directives.
It is very likely that more than one file would depend on the same file. If each file includes all of the file it depends on, the file would included more than once, causing compilation errors. By surrounding source files with conditional compilation directives, it will be compiled only once, even if it is included multiple times.
‘ifndef DESIGN__SV
‘define DESIGN__SV
module design(...);
...
endmodule
‘endif
5. Include a mechanism to exclude all debug statements automatically.
Debug information should be excluded by default and should be
enabled automatically via a control file or command-line options.
Do not comment out debug statements and then uncomment them
when debugging. This approach requires significant editing. When
available, use a preprocessor to achieve better runtime perfor-
mance.
Example of poor debug statement exclusion:
// $write("Address = %h, Data = %d\n",
// address, data);
Example of proper debug statement exclusion:
‘ifdef DEBUG
$write("Address = %h, Data = %d\n",
address, data);
‘endif
6. Use this. when referring to data members.
Explicitly using this. documents that you are referring to a data members instead of a variable currently in scope. Variables in scope are usually declared nearby whereas data members can be inherited and their declarations located in different files. Furthermore, it avoid having to come up with artificially different names for the
same thing in method arguments.
class bfm;
virtual interf sigs;
function new(virtual interf sigs);
this.sigs = sigs;
endfunction: new
endclass: bfm
7.Name all begin blocks
Declarations inside an unnamed block cannot be accessed using hierarchical references. Naming a block makes it possible to be explicitly disabled. If a block is not named, some features in debugging tools may not be available. Labeling also provides for an additional opportunity to document the code.
foreach (data[i]) begin: scan_bits_lp
...
end
8. Label closing “end” keywords.
The start and end of a block may be separated by hundreds of lines. Labeling matching end keywords facilitates recognizing the end of a particular construct.
Example:
moduleFIFO(...);
...
endmodule: FIFO
9.Use symbolic constants instead of “magic” hard-coded numeric values.
Numeric values have no meaning in and of themselves. Symbolic constants add meaning and are easier to change globally. This result is especially true if several constants have an identical value but a different meaning. Parameters, enumerals and ‘define symbols.
Example of poor constant usage:
inttable[256];
for(i=0;i<=255;i++)...
Example of good constant usage:
parameterTABLE_LENGTH = 256;
inttable[TABLE_LENGTH];
for(i=0;i<TABLE_LENGTH;i++)...
10. Do not specify a bit range when referring to a complete vector.
If the range of a vector is modified, all references would need to be changed to reflect the new size of the vector. Using bit ranges implicitly means that you are referring to a subset of a vector. If you want to refer to the entire vector, do not specify a bit range.
Example of poor vector reference:
bit [15:0] count;
...
count[15:0] <= count[15:0] + 1;
carry <= count[15];
Example of proper vector reference:
bit [15:0] count;
...
count <= count + 1;
carry <= count[15];
11. Avoid using `define symbols.
`define symbols are global to the compilation and may interfere with other symbols defined in another source file. For constant values, use parameters. If `define symbols must be used, undefine them by using `undef when they are no longer needed.
Example of poor style using `define symbols:
`defineCYCLE100
`definens*1
always
begin
#(`CYCLE/2`ns);
clk=~clk;
end
Example of good style avoiding `define symbols:
parameterCYCLE=100;
`definens*1
always
begin
#(CYCLE/2`ns);
clk=~clk;
end
`undefns
12. Use prefixes to differentiate identifiers in shared space
When declaring an identifier in a shared name space, prefix it with a unique prefix that will ensure it will not collide with a similar identifier declared in another component. The suffix used has to be unique to the author or the authoring group or organization.
Example of poor shared identifier naming:
‘define DEBUG
Example of good shared identifier naming:
‘define MII_DEBUG
13. Use a nonblocking assignment for variables used outside the always or initial block where the variable was assigned.
Using nonblocking assignments prevents race conditions between blocks that read the current value of the variable and the block that updates the variable value. This assignment guarantees that simulation results will be the same across simulators or with different command-line options.
Example of coding creating race conditions:
always@(s)
begin
if(s)q=q+1;
end
always@(s)
begin
$write("Q=%b\n",q);
end
Example of good portable code:
always@(s)
begin
if(s)q<=q+1;
end
always@(s)
begin
$write("Q=%b\n",q);
end
14. Assign variables from a single always or initial block.
Assigning variables from a single block prevents race conditions between blocks that may be setting a variable to different values at the same time. This assignment convention guarantees that simulation results will be the same across simulators or with different command-line options.
Example of coding that creates race conditions:
always@(s)
begin
if(s)q<=1;
end
always@(r)
begin
if(r)q<=0;
end
Example of good portable code:
always@(sorr)
begin
if(s)q<=1;
elseif(r)q<=0;
end
15. Do not disable tasks with output or inout arguments.
The return value of output or inout arguments of a task that is disabled is not specified in the SystemVerilog standard. Use the return statement or disable an inner begin/end block instead. This technique guarantees that simulation results will be the same across simulators or with different command-line options.
Example of coding with unspecified behavior:
taskcpu_read(output[15:0]rdat);
...
if(data_rdy)begin
rdat=data;
disablecpu_read;
end
...
endtask
Example of good portable code:
taskcpu_read(output[15:0]rdat);
...
if(data_rdy)begin
rdat=data;
return;
end
...
endtask
16. Do not disable blocks containing nonblocking assignments with delays.
What happens to pending nonblocking assignments performed in a disabled block is not specified in the SystemVerilog standard. Not disabling this type of block guarantees that simulation results will be the same across simulators or with different command-line options.
Example of coding with unspecified behavior:
begin:drive
addr<=#1016'hZZZZ;
...
end
always @ (rst)
begin
if (rst) disable drive;
end
17. Do not read a wire after updating a register in the right-hang side of a continu-
ous assignment, after a delay equal to the delay of the continuous assign-
ment.
If you read the driven wire after a delay equal to the delay of the
continuous assignment, a race condition will occur. The wire may
or may not have been updated.
Example creating a race condition:
assign qb = ~q;
assign #5 qq = q;
initial
begin
Q = 1’b0;
$write(“Qb = %b\n”, qb);
#5;
$write(“QQ = %b\n”, qq);
end
18. Do not use the bitwise operators in a Boolean context.
Some code coverage tools cannot interpret a bitwise operator as a logical operator and will not provide coverage on the various com- ponents of the conditions that caused the execution to take a particular branch.
Example of poor use of bitwise operator:
reg [3:0] BYTE;
reg VALID
if (BYTE & VALID) begin
...
end
Example of good use of Boolean operator:
reg [3:0] BYTE;
reg VALID
if (BYTE != 4’b0000 && VALID) begin
...
end