The Exerciser script language can already produce FC Symbol Sequences and Frames.
The Exerciser script enhancements described in this section allow generation of Commands and Application Layer sequences (as in the Exerciser), by processing received frames, making complex decisions, and generating the contents of frames in run-time, for both RX and TX. Variables can keep the run-time state of the bus. Low-level commands can manipulate variables and use variables to create patterns.
The features and commands include:
-
Variable Operations/Identifier
-
Functions
-
Commands
-
Wait
-
SendData
-
Generation Options
-
Orderset
-
Flow control
-
Procedures
The Exerciser can be programmed to act as FC Initiator, or FC Target.
Note: An Example Project, which includes scripts implementing UNH tests (Exerciser UNH example.gep), can be found under the Examples/ Projects folder in the installation.
FC Initiator
As an Initiator, Exerciser can send commands and interact with its peer to complete the command in normal conditions and some popular error conditions. Limitations are:
-
Uses only one command at a time.
-
Sends only limited Write data patterns.
-
Has tight flow control, due to limited RX frame processing.
FC Target
As a Target, Exerciser can receive commands in all protocols and respond to them in normal conditions or some popular error conditions. Limitations are:
-
Uses only one command at a time. Command queuing is not supported.
-
Has tight flow control, due to limited RX frame processing.
Variable Definition
Variable definition is similar to definitions in programming languages. You can define up to 250 32-bit or 125 64-bit variables. There is no constraint on variable names, except that you cannot use keywords.
Variable scopes are general and you should define them in the script header before the Generation block.
The syntax of variable definition is:
VAR32 @VariableName1, @VariableName2, ...
VAR64 @VariableName
VAR64 holds field values greater than 32 bits, such as FCAddress.
Note: Variable names should start with @.
Assigning Variable Values
You can set variable values in different ways:
Constant Value
@varName1 = 1234
Other Variable Value
@varName1 = @varName2
Result of Expression on Other Variables
@varName1 = @varName1 + @varName2
Fields of Last Received Frame
@varName1 = (FCFrame)LRF::FrameType, where LRF is Last Received Frame.
Note: Specifying packet type (FCFrame) before LRF causes last received frame to be this packet type, and field start-bit position is calculated according to the packet-type definition.
Part of Last Received Frame
@varName1 = LRF[startBitOffset:endBitOffset], where offsets are bit based
Example
@varName1 = LRF[32:39]
SendFcFrameCommand_Initiator
{
Data = LRF[startBitOffset:endBitOffset]
Tag = 0x101
}
The constraints are:
-
Length bigger than 64 bit is not supported
-
Offsets (StartBitOffset and EndBitOffset) should be in same DWORD, or adjacent DWORDS
Random Values
@varName1 = Random
Expressions on Variables
Mathematical expressions, such as sum, subtract, and shift:
@varName1 + @varName2
@varName1 - @ varName2
@varName1 & @varName2
@varName1 | @varName2
@varName1 ^ @varName2
@varName1 << 2
@varName1 >> 1
Logical expressions, such as compare, equal, not, and, and or:
@varName1 > @varName2
@varName1 < @ varName2
@varName1 == @varName2
@varName1 != @ varName2
!@varName1
(logical expression1) && (logical expression2)
(logical expression1) || (logical expression2)
Complex expressions (combination of different operators) with prioritizing supported:
(@varName1 + @varName2) > @varName3
Extended Variable Operations (Multiply, Divide, Remainder, none-numeric shift):
Multiplication Operations
var32 @a = @b * 3
@a *= 2
@c = @a * @b
Division Operations
var32 @a = @b / 3
@a /= 2
@c = @a / @b
Remainder Operations
var32 @a = @b % 3
@a %= 2
@c = @a % @b
Shift Operations
@a = @b >> @c
@a = @b << @c
Note: In order to use extended variable operations like *, /, % and shift (<<' >>) with variables, this include file must be added before Generation block: %include "Generation/Include/VariableOperations.inc"
Reserved Constants
Reserved constants are predefined values that can be assigned to variables and their values changes during program execution. The following reserved constants are defined in the software:
Training_ERROR_COUNT
LRT
Local_Tx_status_word
-B_B_Credi
Example
Var32 @a = B_B_Credit
Variable Domains
Variable domains can be either global (outside Generation block) or local (inside a Generation block or a procedure).
Local and Global Domains
Variables can be either defined outside the Generation block which makes them global variables or inside Generation block or procedures. Global variables are visible everywhere after they are defined.
Variables that are defined in the Generation block are valid after they are defined, and also inside inline procedures, or procedures with parameters (which are also inline).
Variables that are defined inside the procedures are valid only inside the procedure and all the inline procedures that are called after the variable definition (inside the same procedure).
If a variable is defined inside a domain that has already a variable with the same name in the higher domain, the variable defined in the same domain will override the previous variable and the previous variable would no longer be visible in the current domain but it value won't be updated by changing the newly defined variable.
Note: This Domain concept also applies to Constants, Identifiers, DataPatterns and Symbols.
Examples
var32 @a = 1 # Global::@a
Procedure Proc2
{
var32 @var1_proc2 = @a # on the first call, @a is Global::@a and equals to 1
# on the second call, @a is Proc1::@a and equals to 2 var32 @a = 3 # Proc2::@a
var32 @var2_proc2 = @a # @var2_proc2 == 3, here @a is overrided by
#local variable and its value is 3
@a = 4 #only update Proc2::@a to 4
}
Procedure Proc1
{
var32 @var1_proc1 = @a # valid operation: @var1_proc1 == 1,
# Here @a (Global::@a) is valid and equal to 1
Call Proc2
#Proc2::@a is no longer visible here, so @a here is Generation::@a
var32 @var2_proc1 = @a # valid operation: @var2_proc1 == 1,
#Here @a (Global::@a) is valid and equal to 1
var32 @a = 2 # proc1::@a
Call Proc2
#value of @a is still 2 here (Proc1::@a)
}
Generation
{
var32 @var1_gen = @a # valid operation: @var1_gen == 1 Call Proc1
var32 @var2_gen = @a # valid operation: @var2_gen == 1, here @a is still Global::@a
var32 @a = 5 # Generation::@a
var32 @var3_gen = @a # valid operation: @var3_gen == 5 as
# Generation::@a would override the Global::@a
exit(@a) # exits with the code 5
}
If/While in Logical Expressions
Like programming languages, scripts allow conditional statements. The if/while syntaxes are:
If (expression)
{
…
}
ElseIf (expression 2)
{
…
}
.
.
.
ElseIf (expression n)
{
…
}
else
{
…
}
While(expression)
{
…
If (condition 1) { BreakWhile }
…
If (condition 2) { ContinueWhile }
…
}
BreakWhile
If it’s called inside a While loop block, program execution point would jump to the next instruction after the While block.
ContinueWhile
If it's called inside a While loop block, program execution point would jump to the first instruction inside the While block.
Example for if, then, else
@HT_RxFISType = LRF[0:7]
#LRF_SATA_FIS_TYPE_START_BIT:LRF_SATA_FIS_TYPE_END_BIT
if (@HT_RxFISType == SATA_FIS_TYPE_DMA_ACTIVATE) then {…}
else { if (@HT_RxFISType == SATA_FIS_TYPE_DATA) then {…}
else { if (@HT_RxFISType == SATA_FIS_TYPE_RD2H) then {…} } }
Example for while
while (@NCQ_Temp0) {
@NCQ_Temp1= @NCQ_Temp1 >> 1
If (@NCQ_Temp1 != 0) then { … }
@NCQ_Temp0 = @NCQ_Temp1 & 0x00000001 }
Note: Nested while and if are supported.
-
The keyword then is optional.
Wait/When/Do in Logical Expressions
The wait/when/do syntaxes are:
wait (time)
{
When {exp} do
{
…
}
Elsewhen {exp}do
{
…
}
on_timeout
{
…
}
}
Example
wait { #no timeout use global WaitTimeout value default 1000 useconds (1 ms)
when {WF_R_RDY} do
{ … }
elsewhen {WF_VC_RDY} do
{ … } on_timeout
{ … }
}
Note: Nested wait should not exceed two deep. Use a procedure call to extend wait logic sequence.
Example
wait_for (100000) { WF_R_RDY WF_TIMEOUT} { … } # (100 ms)
"Wait_For (1000) { WF_SOFi3 WF_R_RDY WF_TIMEOUT }
"Wait_For (1000) { WF_SOFi3 WF_R_RDY }
"Wait_For { WF_SOFi3 WF_R_RDY WF_TIMEOUT }
Also these three are the same and just wait for 1ms
"Wait_For { WF_TIMEOUT }
"Wait_For (1000)
"Wait_For (1000) { WF_TIMEOUT }
Note: Using the WF_TIMEOUT condition is optional when a timeout value is defined. Wait + Wait_For is OK. So these three expressions are the same (default timeout value is 1ms).
Using Variable Values in Creating Patterns on Bus
In creating patterns to send on bus, the Exerciser script allows using variables. In these cases, because the created pattern is dynamic, it is not possible to do scrambling and calculating in the software code. These tasks are done in the hardware Bus Engine. To activate, set “Auto scramble mode” to “on”.
The following examples show uses of variables in creating patterns.
Use Variable for Field Value
SendELSReqPLOGI
{
OriginatorExchageId = 0x1
DestinationIdentifier = @variableName1
...
}
The constraint is that a Field Length bigger than 64 bit is not supported.
Use LRF Directly for Field Value
Send_FCPData
{
Data = LRF[startBitOffset:endBitOffset]
SourceIdentifier = 0x101
}
The constraints are:
-
Length bigger than 64 bit is not supported
-
Offsets (StartBitOffset and EndBitOffset) should be in same DWORD, or adjacent Dwords
Wait_For{WF_R_RDY} SendData
{
[01: Idle, SOFn3]
[10:@AA, @BB]
[10:0x08090008, @DD]
[10:@CC, @frame_count]
send(@IncInit, SEND_INCREMENTAL, last_size/8) #(fcp_dl-max_mtu)
[01:CRC ,EOFt]
}
When there are variable values in SendData, you must include SendCRC command instead of last DWORD. Otherwise, wrong CRC value will be sent.
The constraint is that SendCRC is only supported on last DWORD before end of frame.
Timer
Exerciser script syntax allows using some timers. You can start a timer anywhere. The timer current value is loadable on variable to be used in expressions and conditions on this expression. There are four timers, named A, B, C, and D.
Starting Timer (setting timer value to zero)
CLEAR_TIMER_A
CLEAR_TIMER_B
CLEAR_TIMER_C
CLEAR_TIMER_D
Loading Timer Current Value in Variables
@varName1 = TIMER_A
@varName1 = TIMER_B
@varName1 = TIMER_C
@varName1 = TIMER_D
Example
CLEAR_TIMER_A
While(@Counter < MaxPeriodCount) {
… @Counter = TIMER_A … }
Frame and Symbol Resources A-F
There are six Recording Resources as defined in Generation Options (Advance Wait Conditions). Details on their use is described in Wait Commands.
PATTERN Counter
Exerciser script syntax allows you to use counters on a number of defined events in generation settings.
Syntax for loading counters in variables is:
@varName1 = COUNT_FRAME_RESOURCE_OUTPUT_A
…
@varName1 = COUNT_FRAME_RESOURCE_OUTPUT_F
@varName1 = COUNT_SYMBOL_RESOURCE_OUTPUT_A
…
@varName1 = COUNT_SYMBOL_RESOURCE_OUTPUT_F
Syntax for clearing (resetting) counters is:
CLEAR_FRAME_RESOURCE_OUTPUT_A
…
CLEAR_FRAME_RESOURCE_OUTPUT_F
CLEAR_SYMBOL_RESOURCE_OUTPUT_A
…
CLEAR_SYMBOL_RESOURCE_OUTPUT_F
Example
CLEAR_FRAME_RESOURCE_OUTPUT_A
While(@Counter < MaxReceivedFrameCount)
{
…
@Counter = COUNT_FRAME_RESOURCE_OUTPUT_A
…
}
Procedure Definition
Procedures allow creating simple syntaxes for complex reusable parts in scripts. You can write such code once as a procedure and use everywhere required.
Inline procedures are the same as procedures by they increase the compiled assembly size but they can use local variables which are defined after them (before call instruction).
Procedure definition syntax is:
procedure procedureName
{
...
}
OR
Procedure_Inline procedureName
{
...
}
Calling procedure syntax is Call procedureName.
Note: Recursive calls are not allowed and are not flagged.
-
Users can define up to 256 non-inline procedures,/ti including those inside the include files. Defining each procedure uses one resource only if that procedure is referenced in the script more than once.
-
Procedures with parameters are inline. Defining a local variable inside a procedure makes it inline.
Hint: To pass parameters to procedures or return values from them, global variables or parameters can be used.
Procedure with Parameters
For passing a parameter to a procedure, you can define different of parameters:
PARAM_ID: Value parameters are defined with the keyword PARAM_ID and can be constants, identifiers, numbers and any numeric expression that is supported.
PARAM_VAR32 / PARAM_VAR64: For sending variables you can use either 32-bit variables using PARAM_VAR32 or 64-bit variables using PARAM_VAR64. Variable expressions also can be used to send the result to the procedure as a parameter.
PARAM_ID_REF: For returning a numeric value from a procedure, PARAM_ID_REF can be used and an identifier name should be passed as parameter during calling the procedure.
PARAM_VAR32_REF / PARAM_VAR64_REF: For returning a 32/64 bit Variable, PARAM_VAR32_REF / PARAM_VAR64_REF can be used and a variable name with the same size should be passed as parameter during calling the procedure.
Parameter Types
PARAM_ID: can be used for sending numeric values to procedures
PARAM_ID_REF: can be used for sending and receiving numeric values to/from procedures
PARAM_VAR32: can be used for sending 32-bit variables values to procedures
PARAM_VAR32_REF: can be used for sending and receiving 32-bit variables values to/from procedures
PARAM_VAR64: can be used for sending 64-bit variables values to procedures
PARAM_VAR64_REF: can be used for sending and receiving 32-bit variables values to/from procedures
Calling Functions with Parameters
For calling parameter with parameters, valid expressions must be used for each type of parameters which are as the following:
PARAM_ID: numeric expressions like values, constants or identifier expressions can be used (e.g. 12, i, i + 1)
PARAM_ID_REF: only identifiers can be used. For using an identifier it should be defined already by assigning a default value like 0 to it before passing it to a procedure. (e.g. i, j)
PARAM_VAR32: numeric and 32-bit variable expressions like values, constants or identifier expressions and 32-variables and 32-bit variable expressions can be used (e.g. 12, i, i + 1, @a, @a + i + 1, @a + @b)
PARAM_VAR32_REF: only 32-bit variables can be used. For using a variable it should be defined already before call instruction. (e.g. @a, @j)
PARAM_VAR64: numeric and 32/64-bit variable expressions like values, constants or identifier expressions and variables and variable expressions can be used (e.g. 12, i, i + 1, @a, @a + i + 1, @a + @b)
PARAM_VAR64_REF: only 64-bit variables can be used. For using a variable it should be defined already before call instruction. (e.g. @a_64, @j_64)
Procedure definition with parameters syntax is:
procedure procedureName(Param_Type1 Param1, Param_Type2 Param2, … , Param_Type_n Param_n)
{
...
}
# Parameter Types
# - PARAM_ID: for sending numeric values to procedures
# - PARAM_ID_REF: for sending and receiveing numeric values to/from procedures
# - PARAM_VAR32: for sending 32-bit variables values to procedures
# - PARAM_VAR32_REF: for sending and receiveing 32-bit variables values to/from procedures
# - PARAM_VAR64: for sending 64-bit variables values to procedures
# - PARAM_VAR64_REF: for sending and receiveing 32-bit variables values to/from procedures
Procedure ProcedureName3
(
PARAM_ID param1, #e.g. 1, 2 + i, i
PARAM_ID_REF param2, #e.g. i
PARAM_VAR32 @param3, #e.g. @a, @a + @b, 2
PARAM_VAR32_REF @param4, #e.g. @a
PARAM_VAR64 @param5, #e.g. @a, @a + @b, 2
PARAM_VAR64_REF @param6 #e.g. @a
)
{
#You can access procedure parameters here inside procedure block ...
}
Calling procedure syntax is:
Call procedureName(Param1, Param2, … , Param_n)
Flow Control Expressions
Return: Return is used for returning from Procedures and continues the program from the next instruction after the call. If return is used inside the Generation Block, it would work the same as the exit(0) instruction.