You will be able to:
Implement SystemVerilog data types and declarations such as logic, type definitions and enumerated types
You will also be able to Implement SystemVerilog procedural blocks
procedural statements including SystemVerilog enhanced case statements
state machines using SystemVerilog coding styles.
And you’ll also be able to take advantage of the enhanced port connections in SystemVerilog
In 2009, SystemVerilog merged with the base Verilog standard, becoming one standard, the IEEE standard 1800-2009
SystemVerilog provides a higher level of abstraction for modeling and verification than verilog. The language enhancements in SystemVerilog provide more concise hardware descriptions, while still providing an easy route with existing tools into current hardware implementation flows. The enhancements also provide extensive support for directed and constrained-random testbench development, coverage driven verification, and assertion based verification
Data declarations and data types
Procedural blocks
Procedural statements
State machine design and
Enhanced port connections
Now let's take a look at Data declarations and date types supported in SV.
SV provides more flexibility in variable and net types by introducing additional data types. To the Verilog reg and integer types, SV adds bit, byte, int, logic and time.
Bit, byte along with 3 forms of int; shortint, int and longint are 2 state data types. They can take on the values of 0 or 1. The default value is 0.
2-state date types simulate faster and consume less memory than 4 state data types. However, 2-state data types in simulation may not accurately model hardware implementation.
The new SV logic and time types are 4 state data types; they can take on the values 0, 1, x, or z. The default value is x.
2-state and 4 state integer types use integer arithmetic and can be signed or unsigned values. Bit, logic reg and time default to unsigned values. All others default to signed..
For this reason the keyword logic is recommended to be used in place of the keyword reg and wire in most cases.
As a general rule for synthesis, in SV, use net or wire where multiple drivers are required. For everything else use logic or bit.
In this example 2 variables data_wire and data_reg are defined as the data type logic. In the assignment of data_wire, a hardware register is not inferred. In the assignment of data_reg, a hardware register is inferred. The SV procedural block statements always_comb and always_ff will be covered later in this training module.
Systemverilog has more complex data types so it needs to be stricter about type conversions. So system verilog provides the cast operator, the apostrophe. Casting allows assignment of values to variables that may not be otherwise valid.
The four examples here list various uses of the casting operator. The first example cast the expression x-2 to change its size to 10 bits. Here no truncation warning will be given.
The second expression casts the result of the multiplication of two real numbers to the type int.
The third expression converts the variable x to be a signed variable.
Finally the last expression converts the number 0 to the corresponding value in the enum fruit.
User defined types allow the user to define their own data type using the typedef syntax. In the top example 2 new data types are defined by the user. The first uint is defined as unsigned integer values. The second user type defined is main_bus. This type is defined as a logic type of 16 elements.
Enumerated data types limit the variable assignments to a specific set of values. In the bottom example a new data type is defined as boolean. This is an enumerated data type with the possible values of true and false.
A type can be used before it is defined, provided it is first identified as a type by an empty typedef:
A structure is a collection of variables and/or constants defined as a single name. In this example a structure consisting of a logic data type called even and a logic data type consisting of 8 elements called parity is defined. The name assigned to the structure is par_struct.
The next line of code shows two structures, par_in, and par_out which are defined as the type par_struct.
Assignments to elements in the structure can be made by position as shown in the assignment to par_in where the bit even of the structure is assigned a value of 0 and the byte parity is assigned a hex value of 80.
Also individual assignments to elements in the structure can be made as shown in the par_out assignment statements.
Arrays can be passed through module ports.
Arrays can be either packed or unpacked, packed arrays dimention appear to the left of the array name in the declaration. It can be multidimentional.
Unpacked array dimentions appear to the right of the array name.
In the example code shown an array called data_mem is defined as a 2 dimensional array of bytes. The byte being a packed array of logic. The definition of the array describes 4 instances of a memory 256 entries deep; perhaps a 4 way cache memory.
After the array is defined in the example code, two assignment statements are made.
In the first assignment statement a single byte entry is updated in the array. Instance number 1 at line 100 is updated to the value of hex 55.
In the second assignment statement 2 adjacent bytes in the second instance of the memory array are updated at entries 80 and 81.
Animated Click
In this assignment the cast operator is used to distinguish the operation from a concatenation. That is the data values are not being concatenated to fit into the 8 bit entry.
Like Verilog memories, the dimensions following the data type, set the size of the packed array. The dimensions following the instance, set the unpacked size. As in Verilog-2001, a comma-separated list of array declarations can be made. All arrays in the list shall have the same data type and the same packed array dimensions.
SystemVerilog uses the term part select to refer to a selection of one or more contiguous bits of a single dimension packed array. This is consistent with the usage of the term part select in Verilog. SystemVerilog uses the term slice to refer to a selection of one or more contiguous elements of an array. Verilog only permits a single element of an array to be selected, and does not have a term for this selection.
SV provides new system functions that can be used to return information on arrays such as $size, $dimension… Please refer to the LRM for more details on these.
The supported methods are sum, product, and, or, or xor.
In the example given here, I have an array with three elements in it, the first assignment operator assigns the sum of all elements to the results.
The second operation sets the result to the product of the three elemetents
While the third assign operation here will perform a bitwise and of all the elements.
without a base specifier. All bits of the un-sized value are set to the value of the specified bit.
In this example code a parameter is used to define the width of the data bus. At the set condition, all bits of data_reg regardless of the size of the bus are set to 1 using the un-sized integer literal. The variable could also be assigned to all 0's x's or z's. Verilog did not provide a convenient method for filling an un-sized vector with all ones.
Packages allow user defined types, parameters, tasks and functions to be shared across multiple design modules.
In this example a package called global_defs has been defined. This package defines an enumerated data type and 2 other user defined types.
On the next slide we'll see how we can import and use the content from this package in other modules.
At the top, in the module definition of main_ctl there is an input port in_bus that is of a type main_bus that is defined in the package.
Using the double colon scope resolution operator allows the type defined in the global_defs packaged to be referenced..
In the middle example, we import everything in the package global_defs, after which we can reference main_bus directly.
Because we used the wild card to import, all the defines in the package can be available.
In the third example, we import in the module header. When importing this way, the global_defs package is locally imported in to the module only.
EDA tool such as synthesizers, simulators, LINT checkers and verification checkers can now perform their task with more accuracy and provide consistency between tools when understanding the designers intent.
A sensitivity list is required for the always_ff block. Each signal in the sensitivity list must be qualified by either a posedge or negedge keyword. This informs the software tools of the polarity of the clocking signal as well as the signal edge on which an asynchronous clear or set is performed.
Outputs from a SV always_ff procedural block, can not also be assigned in another procedural block.
The sensitivity list is automatically inferred and includes all signals that are read by the procedural block. See the LRM for complete details on what signals are inferred.
Because of the standard way to infer the sensitivity list, all sw tools will infer the same sensitivity list. This eliminates any ambiguity left by a designer by listing an incorrect or incomplete sensitivity list. In this example pckt_state, pkt_rdy, end_of_data and crc_done will be inferred in the sensitivity list. Any signals used in functions called by the always_comb block will also be included in the sensitivity list.
Any variables on the left hand side of any equations inside the always_comb procedural block can not be assigned a value in some other procedural block. This prevents usage of the logic in a non combinatorial way such as the creation of an unintentional latch. This also ensures that all sw tools enforce the same modeling rules.
The always_comb block will automatically trigger at time zero in simulation after all initial blocks and general purpose always blocks have been activated . This ensures that the output logic is consistent with the input signals at time zero.
like the always_comb procedural block the sensitivity is inferred. In this example the sensitivity list includes the data enable signals as well as the data input signals. And also like the always_comb block the left hand assignments can not be assigned in another procedural block.
The always_latch block is also evaluated in simulation at time zero.
These operators behave as blocking assignments. If used in sequential logic it is possible to create a race condition where a second sequential block reading the value of the counter may read the value of counter before or after the value is updated. Therefore to avoid race conditions, these operators should only be used in combinatorial blocks.
For example; the += assignment operator is equivalent to the output signal equals the output signal plus some input signal. Assignment operators support addition, subtraction, multiplication, division, remainder from division, bit wise operations such as and, or, xor and logical and arithmetic shifts right and left.
System verilog addes support for Wild equality and inequality operators that handles comparisons but treats X and Z values in the right operand as wildcards that matches 0,1,Z, or X.
X and Z values in the left operand are NOT treated as wildcards.
These operators compare operands bit for bit and returns a 1 bit result. 0 if false, 1 if true
Continue jumps to the end of a loop and executes the loop control, it is not necessary to add begin and end statements as required by the verilog disable statement.
Return can be executed at anytime in a task or function, the task or function will exit immediately.
In this example, data labels are used to identify the begin and end of the cntr_reg block of code and the ram_val_reg block of code.
Animation Click
Also shown in the example, a label write_registers is used to identify the total block of code in the always_comb procedural block.
Any non matching block labels will be detected by the Quartus II SW and cause a synthesis error.
SV provides special modifiers unique and priority to case, casex and casez statements.
The unique modifier allows designers to tell synthesis tools that no priority is implied in the case statement. Statements can be evaluated in parallel. This allows synthesis and simulation tools to optimize out any inferred priority.
The priority modifier indicates that the designer considers that is ok for more than one case selection expressions to be true at the same time and the first statement that is true should have priority.
In the top example the unique modifier is used. In this case only one case select will match and no priority should be inferred by simulation or synthesis tools.
In the second example it is possible for more than 1 case selects to be true. The designer by using the priority modifier indicates that the order of the statements indicates which statement has priority.
Assign default values to outputs derived from the state machines.
Separate the state machine logic from arithmetic functions, datapaths, and output values
If your design contains an operation that is used by more than one state, define the operation outside the state machine then use the value in the output logic of the state machine.
As shown when the active low async reset signal rst_async_ n goes low, this will cause the synchronizing flip flops to clear which will then cause System flip flops connected to the rst_sync_n to go to the clear state. Then when the async reset deasserts or goes high, the synchronizing flips flops will clock through the VCC tied to the 1st sync flip flop and remove the reset signal synchronously. This allows the Quartus II SW TimeQuest timing analysis tool to accurately measure the recovery and removal timing to the system flops.
Click
In the first example the default data type int is used. This results in defining a 32 bit signed int data type.
Click
The second example refines this to an unsigned data type. Remember the int data type is a 2 state variable taking only the values of '0' and '1'. While the 2-state data types save on simulation memory and may help simulations to run faster, they may not reflect the hardware in simulation accurately and hide design problems.
Click
In the third example a vectored data type of logic is defined to the state variables.
Click
The fourth example illustrates defining the state machine variables encodings. However the Quartus II software defaults to auto encoding and will ignore these assignments unless the State Machine Processing setting is changed from Auto to User encoded or one of the other options.
Click
Scroll down to State Machine Processing. The settings for State Machine processing default to auto, the tool will decide the state encodings for the state machine variables. The other options are Gray, Johnson, Minimal bits, One-hot, Sequential and User encoding.
Click.
The enumerated data type is used to define the state machine variables. The variables pckt_state and nxt_pckt_state may take on the values of Idle, SOP, DATA_PYLD, CRC and EOP. The enumerated data type uses the data type of logic.
Click
The clocked portion of the state machine uses the specialized SV procedural block always_ff indicating to synthesis and simulation tools that the intent of the designer is to implement sequential logic.
Click
The next state logic of the state machine uses the specialized procedural block always_comb to indicate the intent to infer combinatorial logic.
Also note the unique keyword is used to enhance the case statement. Again this tells the synthesis and simulation tools that no priority of the case statement is inferred.
Click
And finally the output assignments are made outside the clocked state and the combinatorial next state blocks.
Ordered port connections use the position of the port in the module declaration. The name is not required to be known, the connection is simply made by port position. The requirement to know the port position is a great disadvantage in making port connections. This method is prone to errors and also requires constant editing if changes to the port declaration are made. It is also difficult to understand the intent of the design.
Named port connections required the named port of the module declaration along with the net name that connects to the port. Using this method is less prone to connection errors. The position of the port is not required to be known, only the port name. The disadvantage of named port connections is that it is verbose. Both the port name and the port connection must be listed for each port.
SV introduces three enhancements that simplify netlists: the dot-name convention, the dot-star convention and interfaces.
The dot-name conventions takes advantage that many port connections use the same name of the net as the port connection. The SV syntax is to list the port name and than a wire of the same name is inferred to make the connection. The port name and size must match for a proper connection. When the port name does not match the net name, use the port naming convention of verilog.
In this example the port names clk,rst,addr wr,rd and data_in and data_out match nets in name and size. Therefore the connections are inferred. the ports mem_data1 and mem_data2 the connections are to nets that are named ram_data and flash_data therefore the verilog port naming convention need to be used for those ports.
As you can see from this example, making port connections is much simpler and less prone to error. All ports on the module mem_port that have nets that match the name and size are automatically connected. In this case the ports mem_data1 and memdata2 are explicitly connected to the ram_data and flash_data using the traditional verilog port naming convention.
SystemVerilog extends support for additional data types to be passed through module ports. SystemVerilog allows an array of any dimension to be passed through a module port or to a task/function as an argument.
In this example a structure is created and named tlb. Then an output and input structure of type tlb is defined as tlb_out and tlb_in.
With SV the structure can be passed through the module port connections.
CLICK
Using the standard verilog port naming convention the top level looks like this code snippet. This interface connection makes redundant connections on the other bus master as well all the slave interfaces. Using the SystemVerilog dot-name port convention makes this a little less tedious and less prone to error.
However SystemVerilog introduces a new port type: interfaces that will make these connections much easier and less prone to error.
CLICK
The connection between modules is defined as an interface and then using the SV dot-name port naming convention, the connections are simply made between modules.
With interfaces, although you can use any signal type you wish, it is highly recommended to use the type logic because it’s compatible with both procedural assignments and continuous assignments. For bidirectional signals and signals with multiple drivers, use the data type wire.
When specifying interfaces as ports of a module you have the option of declaring it as an array which further saves typing.
Lastly, you may wish to include tasks and functions associated with interface, this feature maybe useful for testbenches.
In this interface declaration two different views of the interface are defined; a master view and a slave view. Using the modport keyword in the interface declaration the direction of ports is defined in regards to whether the module connection will be a master or a slave.
In this example the master will drive the control signals such as read, write, select and address. It will also drive the data_input bus. The slave interface will drive the data output bus as well as the bus error signal.
Click
The fabric has 2 bus connections cpu_bus and usb_bus that use the slave view of my_bus.
Click
The fabric also has 4 bus connections sram, dram, flash and the usb controller that use the master view of My_bus.
Click
When referencing signals in the interface, use the interface name(dot) syntax shown here.
This code shows the sram_mbus(dot)sel signal is an output of a master view of an interface. When the input select signal of the slave view of the cpu bus interface is active along with the corresponding address bits on this bus interface, the select signal is active on the master port to the SRAM.
In my top level module body here, I first declare and instantiate 6 instances of the my_bus interface. That corresponds to the four slave and two master connections that go through the fabric.
Then when I instantiate the module fabric, for each of the instances, I would specify the port interface name along with the interface instance with the correct view..
Of course this example is not complete, we’d have to connect the other end of these 6 interfaces to the appropriate module for those components.
If you have more questions or would like more detail on SV, please see these reference documents:
Please consult Quartus II help for a complete list of supported and unsupported systemverilog constructs.
Please also consult references from Mentor, Synopsys, or Cadence to refer to verification aspects of System Verilog
We appreciate any comments you may have as we constantly look to improve our future trainings based on the feedback we get.