[Out of Step(Z)変数] レポート
変数は設定されてから読み取られるのが、タスク内における通常の変数の使用順序です。[Out of Step(Z)] 変数レポートは、これとは逆に、読み取られてから設定される計算順序のグローバル変数および静的変数を特定します。タスクベースのソフトウェアでは、これによって計算に使用される変数の値が古くなる場合があるのです。
例として次のコードを取りあげます。変数 globalA が初期化されることにより、 初めて taskA を呼び出す場合でも、globalA の値を取得します。ただし、taskA は globalA の値を更新してから終了します。次回 taskA が呼び出されると、taskA が読み取る globalA の値は、前回の呼び出しの最後に代入された値となります。これが望ましい動作であることもありますが、最初に globalA に代入する必要がある(センサーの読み取りなどによって)プログラミング・エラーを示唆する可能性もあるのです。
int globalA = 1;
int subX() {
int localX;
localX = globalA;
return localX;
}
void subY(int paramY) {
globalA = paramY;
}
void taskA() {
int localA, localB;
localA = subX();
localB = localA;
subY(localB);
}
|
[Out of Step 変数] レポートでは、タスク内でこのような読み取られた後に設定される、クローバル変数あるいは静的変数の使用を検出します。
Out-of-Step (Z) Variables (Read Before Being Set)
Key:
r or R (letter): variable is read
s or S (letter): variable is set
S or R (upper case): variable is used in current function
s or r (lower case): variable is only used in called functions
rs (order): variable is read in at least one path before being set
sr (order): variable is set in all paths before being read
r* (order): variable is read first in at least one path;
it is set and read in various orders
s* (order): variable is set first in all paths where it is read;
it is set and read in various orders thereafter
RO (PTN): pattern of variable use across all tasks is read only
Z1 (PTN): pattern is status variable (only set to constants)
Z2 (PTN): pattern is aggregation variable
(assignments involve its own value)
Z3 (PTN): pattern is external data variable (array set to constants
and likely written to externally like DMA)
Settings:
Global Variables: displayed
Static Variables: displayed
Unset Variables: displayed
Struct Container Summary: omitted
Union/Bitfield Members: separate
Show Z-var Design Pattern: displayed
Task Definitions
Tasks are from User Defined Tasks
Name Members Graph Root
Task A 3 [+] taskA
Variable PTN Func1 Func2 Func3 (order of calls)
Task: Task A
taskA
. subX
. . subY
-------------------------------------------------- = = =
globalA .......................................... rs R S
|
デザインパターン
ソフトウェア内で検出された周期遅れ変数の使用の一部は、基準を満たした使用と判断される、標準的なデザインパターンに適合することがあります。レポートにある PTN カラムには、このようなパターンに一致する変数の使用が表示されます。これを活用することで、そのような使用を迅速に確認でき、レポートされたパターンに一致しない変数の使用に対して、レビューを重点的に行うことが可能です。
Z1 - ステータス変数
Z1 パターンは、前回のシステムのステータスをタスクに通知するために使用される、グローバル変数を参照します。タスクは最初に if 文内の変数を読み取り、次にステータスを別の値に設定します。ステータス変数は、他のどのような変数の計算においても決して使用されることはありません。
int Status = 0;
void task_Z1a1() {
if (Status) {
/* まず "if" 文もしくは "switch" 文で
読み取られるが演算では使用されない */
SomeAction();
Status = 0; /* Status を定数に設定 */
}
}
void task_Z1a2() {
int thisHappened;
thisHappened = SomeValue();
if (thisHappened) {
Status = 1;
}
}
|
Z1パターンの2つめの動作は、タスクのリクエストに関係します。タスクあるいは外部プロセスは2つめのタスク内で、ある処理を行うための変数を設定します。同タスクは、何かしら処理する対象が存在するか否かを確認するため、その変数をチェックするのです。処理対象が存在する場合は、それを処理したのち、既出の変数(action request variable)をリセットします。
int Action = 0;
int ActionParam;
void task_Z1b1() {
Action = 1;/* タスクが処理をリクエスト */
ActionParam = SomeValue();
}
void task_Z1b2() {
switch (Action) {
/* タスクが処理をチェック */
case 1:
SomeAction1(ActionParam);
Action = 0; /* 処理コードをリセット */
break;
case 2:
SomeAction2(ActionParam);
Action = 0;
break;
}
}
|
変数の型 | Single(配列、構造体メンバを除く): 整数型または列挙型 |
変数を使用するタスク | 1つもしくは複数 |
使用パターン | RS もしくは S のいずれか |
読み取り時の条件 | 条件分岐文 if または switch 内に限定 |
設定時の条件 | 代入は定数に限定 |
Z2 - 集約変数
Z2 パターンでは数回のサイクルにわたり、1つのタスクが、ある変数内に値を集約するような状態を特定します。例えばタスクは、各サイクルで同じ変数に値を追加することや、配列に値を挿入することでサイクルごとにインデックスを増加させたり、変数内で追跡される値が、最大値を超過しているか否かをチェックしたりすることがあります。このようなケースではそれぞれにおいて、タスクはまず値を集約するために使用される変数を読み込んでから設定を行います。2つめのタスクは最終的に、この集約された変数を読み取り、計算処理でその変数を使用します。また同タスクは、その後、新しい集約サイクルを開始できるよう、その変数をリセットすることがあります。
以下の参考例 Z2a は、各サイクルで集約用に追加される値を持つ、周期遅れの Single 変数(スカラ型)を示します。
int AggregateCounter = 0;
double Aggregate = 0.0;
void task_Z2a1()
{
Aggregate += SomeFloatValue();
AggregateCounter++;
}
void task_Z2a2() {
if (AggregateCounter < 10) {
/* タスクは場合により
集約された値を評価 */
double Calc;
Calc = SomeCalc(Aggregate);
/* 集約変数をリセット */
AggregateCounter = 0;
Aggregate = 0.0;
}
}
|
次の参考例 Z2b では、最大値が追跡される周期遅れの構造体メンバを示します。
#define AGGRB 100
struct Aggr {
int AC;
double Max;
} GlobAggr = {0, 0.0};
void task_Z2b1() {
double val = SomeFloatValue();
if (val > GlobAggr.Max) {
GlobAggr.Max = val;
}
GlobAggr.AC++;
}
void task_Z2b2() {
if (GlobAggr.AC >= AGGRB) {
/* タスクは最大値を表示 */
Display(GlobAggr.Max);
/* 集約変数をリセット */
GlobAggr.AC = 0;
GlobAggr.Max = 0.0;
}
}
|
参考例 Z2c は、単一のタスク内の各サイクルで挿入される値を持つ、周期遅れの配列を示します。
#define AGGRC 100
int AC = 0;
double AggregateC[AGGRC]; /* Z-var */
double SomeCalc(double[]);
void task_Z2c() {
if (AC >= AGGRC) {
/* タスクは集約された値を評価 */
double Calc;
Calc = SomeCalc(AggregateC);
/* 集約変数をリセット */
AC = 0;
}else {
if (AC > 0) {
/* 2点の移動平均を追跡 */
AggregateC[AC] = (AggregateC[AC-1] +
SomeFloatValue()) / 2;
}else {
AggregateC[AC] = SomeFloatValue();
}
AC++;
}
}
|
変数の型 | Single、配列、構造体メンバ(型は問わない) |
変数を使用するタスク | 1つもしくは複数 |
使用パターン | RS または S |
読み取り時の条件 | それ自身の値をともなう演算(++、--、+=、-=、&=、比較および設定)もしくは、if 条件文や計算の後に定数値にリセットされる演算のいずれか |
設定時の条件 | それ自身の値をともなう演算(++、--、+=、-=、&=)もしくは、定数値にリセットされる演算のいずれか
|
その他の留意点 | 集約の計算およびリセットは、同じタスクないし別々のタスクで実行することが可能 |
Z3 - 外部データ変数
Z3 パターンは、センサーハードウェアのメモリに対する直接的な書き込みや、タスクがメモリを読み取るような状況を特定します。これは通常、アクセスが読み取り専用であることを理由に、レポート内の周期遅れ変数としてフラグが立てられることはありません。ただし、一部の設計においては、タスクが完了時にメモリ(の一部)をリセットすることもあります。そのような場合、変数の使用は周期遅れとしてリストされ、Z3 オプションが示されます。
/* DMA 領域の変数 */
volatile int DMAVar[128];
void task_Z3()
{
if (DMAVar[0]) {
/* DMAがレディ・インディケータを置く場所 */
SomeAction(DMAVar);
DMAVar[0] = 0;
}
}
|
変数の型 | 配列 |
変数を使用するタスク | 1つ |
使用パターン | RS |
読み取り時の条件 | 最初の読み取りは if 条件文で行われ、続けて演算内におけるその他の読み取りが実行 |
設定時の条件 | 定数値へのリセット |
その他の留意点 | この設計パターンは Z1 -ステータス変数に、非常によく類似しているが、これは配列であり、その後の演算において複数の読み取りが許可されていることが相違点 |
|