私の組み込みシステムプラットフォームの定義は非常に単純です。電子製品の原因プログラムをスムーズに開発できる環境。
- システムソフトウェアとドライバー
- ハードウェアプラットフォーム
- 開発環境(コンパイラ、デバッグ、ダウンロードツール)
- エミュレータ
- プログラミング仕様
したがって、通常、組み込みソフトウェア開発チームには「システムプラットフォームグループ」があり、その主なタスクは次のとおりです。
- システムアーキテクチャの設計と実装
- 組み込みオペレーティングシステムの設計と実装
- APIの設計と実装
- メモリ使用量の構成(モジュールまたはプログラムが使用できるメモリアドレス範囲を標準化する)
- 開発環境設計
- シミュレータの設計と実装
-
システム統合(ドライバー、システムプログラム、サブシステム、ライブラリ関数、およびアプリケーションの統合)
- バージョン制作
1.システムアーキテクチャの設計
<img src = " https://cdn.jsdelivr.net/gh/Leon1023/leon_pics/img/20201115105212.png " alt = "埋め込みシステムプラットフォームアーキテクチャ図" style = "zoom:80%;" />
(1)システムアーキテクチャ設計前:製品仕様を明確にする必要があります
- ハードウェア仕様:
- CPU速度:アプリケーションまたはアルゴリズムの複雑さに応じてCPU速度を決定します。
- メモリ容量:プログラムのサイズ、静的スペース、および動的スペースに応じて。
- 周辺機器の性能:
- 製品特性:使用環境、販売エリア、ターゲットユーザーグループおよびアプリケーション特性、範囲など。
- ドライバー、システム、サブシステム、およびアプリケーションレイヤー間の通信インターフェース。
- オブジェクト指向の設計システムを使用するかどうか
- 人的資源と進歩
- 品質要件
- リアルタイムの要件
- マルチタスクの要件
- 電力、スケーラビリティ、移植性、およびその他の要件
(2)システムアーキテクチャの表現:ブロック図またはUML
ブロック図の例:
<img src = " https://cdn.jsdelivr.net/gh/Leon1023/leon_pics/img/20201115110613.png " alt = "システムアーキテクチャ図の例" style = "zoom:80%;" />
ブロックはワークパッケージに対応し、各ブロックをさらに小さなブロック図に分割して、ワークリストを生成することができます。システムアーキテクチャを設計するときは、次の原則に注意する必要があります。APIはシンプルで明確である必要があります。プログラムモジュール間の結合が少ない。設計範囲には、各モジュールのユニットテストとストレステストを含める必要があります。コールバックのアイデアを使用して、アプリケーションをシステムモジュールに埋め込むことができます。次に、これらの各ボックスをさらに紹介します。
- ハードウェア層
このレイヤーには、実際のハードウェアとシミュレーターの2つがあります。1つ目は、プログラムが実際に実行されるハードウェアをよく理解していることです。シミュレーターは、実際のハードウェアの動作を無差別にシミュレートできるPCベースのプログラムであり、一般的に高価です。
- ドライバーレイヤー
この層は完全にハードウェアに関連しています。周辺機器を駆動する機能を実現することに加えて、他のプログラムモジュールから呼び出すことができるAPIも提供する必要があります。ドライバーはすべてのハードウェア機能をシールドでき、上位アプリケーションはハードウェアデバイスを呼び出すために提供されるAPIを使用するだけでよいため、ドライバーレイヤーはハードウェア抽象化レベル(HAL)とも呼ばれます。
-
オペレーティングシステム層
組み込みシステムで使用されるOSは一般にRTOSと呼ばれ、その機能は一般的に単純です。一般的なものは、Linux、uclinux、ucOS、WinCEなどです。オペレーティングシステムを2つの部分に分割する理由は、シミュレータがハードウェア関連の機能を追加でシミュレートする必要があるのに対し、ハードウェアに依存しない部分は一連のプログラムのみを必要とするためです。
- ハードウェア関連部品
ハードウェア関連の機能には、マルチタスク、割り込み制御、リアルタイムクロックRTC、およびタイマーが含まれます。
- ハードウェアとは関係のない部品
ハードウェア関連の機能には、プロセス間通信、同期メカニズム、動的メモリ機能などがあります。
-
グラフィック関数ライブラリ、GUIサブシステム
製品に操作用の画面がある場合は、シンプルなウィンドウシステムを提供する必要があります。GUIは、グラフィックス関数ライブラリ(ポイント、ライン、サーフェス)を使用して画像表示を実現すると同時に、フォント表示を処理する機能を備えている必要があります。
-
その他のサブシステム
ネットワーク通信プロトコル、解凍ライブラリ機能、ファイルシステムおよびその他の機能モジュール。
- アプリケーション層
上記のすべてのレイヤーは、ハードウェアとは関係がないことを除いて、システム機能ですが、一般的な機能です。ただし、特定の分野で同様の機能を専用のモジュールライブラリとして使用できる場合は、ソフトウェアの再利用にも非常に役立ちます。
-
- 製品特性に基づく排他的関数ライブラリ関数:多くの共有可能なモジュールをライブラリ関数に抽出します。
- アプリケーション層:上記の層によって提供されるAPIを使用して製品機能を実装するプログラム
もちろん、UMLを使用してこのようなアーキテクチャを説明することもできます。UMLは、さまざまな観点からシステムを表現するためのさまざまな図を提供します。
見る | グラフィックス |
---|---|
ユーザーモデルビュー | ユーザーケース図 |
構造モデルビュー | クラス図 |
行動モデルビュー | シーケンス図(シーケンス図)およびコラボレーション図(コラボレーション図);状態図(状態図);アクティビティ図(アクティビティ図) |
実装モデルビュー | コンポーネント図 |
環境モデルビュー | 展開図 |
(3)データフロー
<img src = " https://cdn.jsdelivr.net/gh/Leon1023/leon_pics/img/20201115132618.png " style = "zoom:80%;" />
- 2つの入力ソース:周辺デバイスの状態が変化し、システムは「ポーリング」または「割り込み」の2つの方法で入力データを取得します。
- ドライバーのトリガー:システムがポーリング方式を採用している場合、ドライバーは監視プログラムによってトリガーされます。割り込み方式が採用されている場合、対応する割り込みハンドラー(ISR)が実行されます。
- ISRは、ハードウェア状態の変化を判断し、同時にシステム層にメッセージを送信します。グローバル変数、メッセージキューなどを設定できます。
- システムには無限のループがあります。ループの役割は、新しいハードウェアイベントの到着を確認または待機することです。存在する場合は処理され、存在しない場合はアイドルモードになります。
while(1)
{
os_MSG new_msg;
if(os_get_msg(&new_msg))
{
//在消息队列中有新的消息,则处理新的消息
os_process_msg(&new_msg);
}
else
{
//没有新消息到达,则进入待机模式(idle mode)
//当有新的消息到达时,系统会自动离开待机模式,并继续执行该循环语句
drv_enter_idle_mode();
}
}
- システム層は、メッセージをアプリケーションに送信するかどうかを決定します。通信方法は次のとおりです。アプリケーションは、処理するハードウェアイベントのコールバック関数を登録し、この時間内に発生すると、システムはアプリケーションのイベント処理関数を自動的に実行します。
(4)再利用性と移植性
プログラムを設計するときは、ハードウェア関連モジュールとハードウェア非依存モジュールを分離するように注意してください。ハードウェアに依存しないモジュールは他のプロジェクトで再利用できるため、ハードウェア関連のモジュールの移植性と再利用性は一般的に低くなります。組み込み分野では、ハードウェア関連モジュールとハードウェア非依存モジュールは設計段階で明確に区別され、モジュール間の通信に使用できるのはパブリックAPIのみです。例えば:
<img src = " https://cdn.jsdelivr.net/gh/Leon1023/leon_pics/img/20201115135222.png " alt = "2レベルのドライバー" style = "zoom:80%;" />
(5)スケーラビリティと調整可能性
組み込みシステムでは、通常、構成ファイルをsys_config.h
使用していくつかのマクロを定義し、次に条件付きコンパイルを使用して、システムに含める必要のあるモジュールを選択します。例えば:
/************************************************
File Name :sys_config.h
Function: 通过定义合适的宏,去编译包含特定模块的程序
************************************************/
//常数定义
//
#define _HW_CONFIG_FALSE 0
#define _HW_CONFIG_TRUE 1
#define KME_MODEL_A 1
#define KME_MODEL_B 2
#define KME_MODEL_C 3
//定义产品名称
//
#define PRODUCT_NAME KME_MODEL_B
//定义系统是否支持某些硬件的驱动程序
//
#define HW_MUSIC_MP3_SUPPORT _HW_CONFIG_FALSE
#define HW_MR_SENSOR_SUPPORT _HW_CONFIG_FALSE
#define HW_REMOTE_CONTROLLER_SUPPORT _HW_CONFIG_FALSE
#define HW_SD_CARD_SUPPORT _HW_CONFIG_FALSE
//定义LCD相关属性
//
#if (PRODUCT_NAME == KEM_MODEL_A)
//产品2的LCD分辨率为160x160
//
#define LCD_RESOLUTION_WIDTH 160
#define LCD_RESOLUTION_HEIGHT 160
#else
#define LCD_RESOLUTION_WIDTH 160
#define LCD_RESOLUTION_HEIGHT 240
#endif
#define LCD_LCD_COLOR_LEVEL 8
#define WITH_TOUCH_PANEL _HW_CONFIG_TRUE
//定义相同是否支持某些子系统
//
#define SYS_TCPIP_SUPPORT _HW_CONFIG_FALSE
#define SYS_FAT32_SUPPORT _HW_CONFIG_FALSE
/*******************************************
File Name :my_function.c
Function:条件式编译范例
******************************************/
#include <sys_config.h>
void my_function(void)
{
#ifdef(PRODUCT_NAME == KEM_MODEL_B)
//和产品B有关的程序段
//
product_B();
#else
//这段程序给产品B之外的产品使用
//
not_product_B();
#endif
#if(HW_MUSIC_MP3_SUPPORT == _HW_CONFIG_TRUE)
//和播放MP3有关的程序段
//
play_mp3();
#else
// 系统不支持MP3时采用这段程序
//
do_nothing();
#endif
}
これらの調整可能なシステム構成は、設計段階のできるだけ早い段階で策定する必要があり、統一された方法で管理する必要があります。また、システムアーキテクチャ設計者の責任者が従うように、各構成の意味を明確に定義する必要があります。各構成は完全に独立したモジュールである必要があります。システムに追加しても、他のプログラムモジュールには影響しません。
2.APIとプログラミングスタイルの設計
システムアーキテクチャの設計作業とモジュール計画が終了したら、各モジュールをプログラム開発者に渡して詳細設計を行います。このとき、誰かがシステムプログラムとアプリケーションプログラムの書き方を決めなければなりません!
(1)システムプログラミングスタイル
システムプログラムにも多くのスタイル制限がありますが、全体的にそれほど強力ではありません。たとえば、前述のデータフローアーキテクチャ図のメッセージディスパッチャは、製品関連のシステムモジュールです。製品ごとに要件が異なる場合がありますが、基本的なプログラムアーキテクチャは同じである必要があります。設計ファイルのサンプルコードからまたは、例を取得するための疑似コード。
// sample code of message dispatcher
// - forever loop
//
void os_message_dispatcher(void)
{
struct os_msg new_msg;
struct os_event new_event;
while(1)
{
//取得新的消息
//
if(os_get_msg(&new_msg) == TRUE)
{
//driver层送来新的消息
//
new_event = os_preprocess_message(&new_msg);
if(new_event == NULL)
continue; //事件已经处理,无须再网上传递
if(new_evnet.owner != NULL)
{
//将事件传递给指定的应用程序或对象
//
os_send_sys_event(&new_event);
}
else
{
//处理新事件,方法视具体产品而定
//1. 送给current/active AP
//2. 送给所有的AP或对象,由其自己决定
//
os_process_new_event(&new_event);
}
}
else
{
//暂时没有硬件信息,让系统进入待机模式
//
os_enter_idle_mode();
}
}
}
したがって、このシステムを使用して製品を開発する場合は、この例を直接使用して製品を変更できます。システム設計文書で例を提供することに加えて、システムアーキテクチャおよび設計概念に基づくシステムプログラミングスタイルを指定することもできます。たとえば、システムが構造指向の考え方を採用している場合、すべてのモジュールは「情報をメインとして処理する」という特性を示す必要があります。以下は、構造システムモジュールの一般的なプログラムスタイルです。
// 系统模块程序风格规范
//重要原则:
// 声明这个模块中处理各种信息的静态函数(类似对象的method)
// 1. 这些函数只有这个模块会调用
// 2.每种信息有其专用的处理函数
//
static int xxx_msg_1_processor(struct message * new_msg);
static int xxx_msg_2_processor(struct message * new_msg);
static int xxx_msg_3_processor(struct message * new_msg);
static int xxx_msg_default_processor(struct message * new_msg);
/****************************************************
foo模块信息处理程序:foo_module_basic_message_processor
****************************************************/
int foo_module_basic_message_processor(struct message * new_msg)
{
int msg_type = new_msg->message_type;
switch(msg_type)
{
case MSG_TYPE_001:
return xxx_msg_1_processor(new_msg);
case MSG_TYPE_002:
return xxx_msg_2_processor(new_msg);
case MSG_TYPE_003:
return xxx_msg_3_processor(new_msg);
default:
xxx_msg_default_processor(new_msg);
}
return MSG_PASS; //继续让其它模块处理这个消息
}
/*************************************************
foo模块信息1处理程序:foo_msg_1_processor
*************************************************/
static int foo_msg_1_processor(struct message * new_msg)
{
//处理第一种类信息的程序代码
//
...
//已处理完毕,系统无须再将此信息送给其它模块
return MSG_PROCESSED;
}
...
(2)アプリケーションスタイル
アプリケーションプログラミングスタイルの仕様の考え方は比較的単純です。ここで強調すべき2つのポイントがあります:
- アプリケーションを作成するときに注意を払う必要のある制限を策定し、アプリケーションプログラマーに準拠するように要求します。プログラムのライフサイクル、使用されるリソースなど。
- アプリケーションがオブジェクト指向のアプローチで設計されているが、コンパイラの制限またはパフォーマンスの考慮事項のために、開発に使用できるのはC言語のみである場合、いくつかの仕様または提案があります。専用の記事で詳しく説明します。
(3)API
優れたAPIドキュメントには次のものが含まれている必要があります。
- モジュールの機能説明と使用範囲
- データ構造と定数の説明
- 機能、パラメータ、各機能の戻り値の説明
- 十分な例
- 注意事項と制限事項
- 関連機能
例えば:
3.組み込みオペレーティングシステム
組み込みオペレーティングシステムには、uC / OS、組み込みLinux、uCLinux、FreeRTOS、Androidなどが含まれます。ほとんどの組み込みオペレーティングシステムはリアルタイムオペレーティングシステムであり、次の特性があります。
- 高い携帯性(ポータブル)
- ROM可能:通常、小さな組み込みシステムにはディスクデバイスやファイルシステムの概念がないため、システムはROMで直接実行可能である必要があります。
- 調整可能性(スケーラブル)と再編成(構成可能)
- マルチタスクとタスク管理
- 調整可能なタスクスケジューリングアルゴリズム(スケジューリングアルゴリズム)
- タスク(スレッド)同期メカニズム:セマフォ、ミューテックスなど。
- タスク間通信メカニズム(IPC):メッセージキュー、メールボックス
- 割り込みメカニズム
- メモリ管理
- 資源管理
オペレーティングシステムのコアタスクは依然としてタスクスケジューリングです。一部の組み込みシステムには、実行中にPCプログラムと通信したり、デバッグコマンドを実行したり、デバッグ情報を送信したりできるデバッグサブシステムが含まれています。
(1)組み込みシステムのタスクアーキテクチャ
メインタスクが1つしかない製品を例にとってみましょう。顧客は電力を節約する必要があるため、メインタスクが何もすることがない場合、システムは自動的にスリープし、アイドルタスクの責任である優先度の低いアイドルタスクを制御します。 CPUをスリープモードにします。
ハードウェアイベントが発生すると、ISRはハードウェアイベントをメインタスクのメッセージキューに渡します。このとき、CPUはスリープモードからウェイクアップし、アイドルタスクは実行を継続し、メインタスクがウェイクアップされます(ウェイクアップ)。メインタスクの優先度が高いため、システムはCPU使用権をメインタスクに転送します。メインタスクは新しいハードウェアイベントを処理できます。処理後、スリープ状態に戻り、アイドルタスクに制御を移してスタンバイ状態になります。モード。
次の図は、システム内のタスクとISRの相互作用図と実際の疑似コードを示しています。
(2)マルチタスクプログラミングに関する注意事項
A.マルチタスクシステムの実行プロセス
B.タスクスケジューリングのタイミング
マルチタスクシステムを使用する場合は、設計段階でスケジューリングアルゴリズムを決定する必要があり、実際のコーディングの前に、各プログラム開発者はシステムスケジューリングアルゴリズムを知っている必要があります。異なるアルゴリズムで使用されるプログラミング方法または同期メカニズムが異なるためです。
タスクのスケジューリングが必要な3つの一般的なタイミングは次のとおりです。
- ブートフェーズの最後に、システムは実行する最初のタスクを選択する必要があります
- スケジューリングに関連するシステム機能(sleep、delay、wakeup-task、wait_eventなど)
- ハードウェア割り込みが発生した後、ISRが実行されます。
C.RTOSマルチタスクシステムの機能
- マルチタスクシステム=複数のユーザーのタスク+スケジューラ
- タスク実行ポイント(入力ポイント):C言語関数
- アクティブなタスク:機能+コンテキスト。
- システムは、さまざまなタスクの情報制御ブロック(Task-Control-Block)をいくつかのリンクされたリスト(リスト)に格納し、スケジューラーはこれらの制御ブロックに従ってスケジューリングを実行します。以下に示すように:
D.RTOSマルチタスクシステムの一般的なスケジューリングアルゴリズム
選択するスケジューリングアルゴリズムを決定する前に、タスクがプリエンプティブであるかどうか、およびタスクの実行順序と実行時間が決定的であるかどうかを検討する必要があります。
E.RTOSマルチタスクシステムに関する注意事項
1.適切なスケジューリングアルゴリズムを選択し、タスク間の実行順序の不確実性を排除しようとします。
2.クリティカルセクション(クリティカルセクション)の保護に注意してください。
LinuxやWindowsとは異なり、ユーザーモードとカーネルモードを区別し、各アプリケーションには独自のアドレススペースがあり、相互に干渉しません。RTOSでは、すべてのプログラムがアドレススペースを共有しているため、注意しないと相互に影響を与える可能性があります。ランダムな割り込み効果と相まって、プログラムの実行シーケンスを正確に予測することはできません。このため、システムは共有変数または特定のプログラムセグメントの保護メカニズムを提供する必要があります。
その中で、割り込みの禁止は最も効果的な保護メカニズムの1つですが、割り込みを頻繁に禁止すると、システムのリアルタイムパフォーマンスも低下します。この目的のために、システムはMutexやSemaphoreなどのメカニズムを提供します。しかし、デッドロック(デッドロック)と飢餓(飢餓)を防ぐためにそれらを使用してください。モジュール化により、システムの複雑さを軽減し、変数共有の機会(重要なセクションの数)を減らすことが重要です。
最近の組み込みシステムの開発に伴い、Linuxは一般に複雑な機能を備えた組み込みシステムのプラットフォームとして使用されていますが、元のRTOSプログラムをLinuxに移植するにはどうすればよいでしょうか。1つの方法は、RTOS上の複数のタスクをLinuxのプロセスで実行させることです。製品のコア機能をLinuxで正常に実行できるようにし、拡張機能を追加する場合は、Linuxでそれを実現するプロセスを追加できます。
4.ソースツリーの設計とプログラムスタイルの仕様
ソースツリーは、システム全体のソースコードの構造を標準化し、どのコードファイルをどのディレクトリに配置するかを決定するために使用されます。一般的な原則は、システムプログラムのディレクトリ構造を他のプロジェクトで使いやすくすること、つまり移植性を満たすことです。基本的に、ハードウェア関連、ハードウェア関連、製品関連、および製品関連を区別する必要があります。次の図は例です。
-
プログラミングスタイルコンベンション(プログラミングスタイルコンベンション)
いわゆるプログラムスタイル仕様は、命名仕様を作成するだけでなく、少なくともプログラムコードに含める必要のある情報とプログラム作成の注意事項も指定します。
- ファイル説明
- 著者と日付
- 再開の編集
- 特別な目立つ記号を使用して段落を区別する
- 各関数は、目的、各パラメーターの意味、および戻り値の意味を指定する必要があります
- 各グローバル変数の目的
- データ構造の各要素の意味
- コメントをもっと書く
- きちんとインデントされています(スペースの代わりにタブを使用してください)
- for、while、およびnested ifステートメントが長すぎる場合は、中括弧の最後に、ループまたは判断の内容を説明するための注釈を付ける必要があります。
- 空白行でけちなことはしないでください
- プログラムファイルのサイズは1000〜2000行に制御され、関数の行数は約50行(1ページ)に制御されます。
次に例を示します。
// os_message_queue.c
//
/***********************************************************
程序名称:os_message_queue.c
所在目录:library/os
项目名称:Typhoon 2020
创建者 :S202001(工号)Leon George
程序用途:。。。。。。。。
版权声明:Copyright (C) KME S/W Co.Ltd. All Right Reserved
维护信息:2020/11/1 created by Leon George
2020/11/7 Add new API - emptyQueue() by Leon George
。。。
*************************************************************/
/************************************
INCLUDE FILE
*************************************/
#include <system_config.h>
#include <os\os_message_queue.h>
/************************************
CONSTANT Definition
*************************************/
//常数名称的所有字母大写
//只会在本文件中用到的常数不必定义在.h文件中
//常数的详细用途解释。。。
#define OS_MSGQ_MAX_ENTRY_NO 20
。。。
/************************************
数据结构与数据类型定义
*************************************/
//一般数据结构定义在.h文件中,除非它是静态的
//数据结构的详细用途
//
struct messageQueue
{
//详细解释各组成元素的用途与约束
struct message mQueue[OS_MSGO_MAX_ENTRY_NO];
//使用时的注意事项:initial Value must be 0
short mqFront;
short mqRear;
}
/************************************
Global Variable Definition
*************************************/
//“p"表示pointer
//全局变量:首字母大写
//全局变量的详细用途解释
//
struct messageQueue * pSystemMessageQueue = NULL;
/************************************
静态函数声明
*************************************/
//静态函数不会声明在.h文件中
//静态函数的用途、参数、返回值解释
//
static void os_msgq_internal_func(...);
//用特殊显眼的符号区分程序段落
/************************************
FUNCTION NAME:os_msgq_initMessageQueue
函数用途:。。。
参数描述:。。。
返回值描述:。。。
特殊算法:。。。
注意事项:。。。
*************************************/
short os_msgq_initMessageQueue(struct messageQueue *newMQueue)
{
//局部变量定义,尽量描述其用途
//“p”表示指针
//局部变量首字母小写
struct messageQueue *pnew_MQueue = NULL;
int i;
if(newMQueue != NULL)
{
//如果又用到任何特殊技巧,一定要写明
...
for(i=0; i < OS_MSGQ_MAX_ENTRY_NO; i++)
{
...
}//程序中的缩进必须整齐
}//如果内容太长,要在结尾说明该语句内容
}
...
//end of program - os_message_queue.c
-
ヘッダーファイル仕様(ヘッダーファイル)
優れたヘッダーファイルは、モジュールのヘッダーファイルを確認することによってのみモジュールをプログラムで使用できるという要件を満たす必要があります。
-
すべてのプログラムドキュメントの要件
-
インクルードの繰り返しによって引き起こされる定義の繰り返しは避けてください。
-
#ifndef XXX_OS_MSG_QUEUE_H #define XXX_OS_MSG_QUEUE_H //XXX_OS_MSG_QUEUE_H是一个在其它地方不会用到或定义 。。。实际内容 #endif
- 定数とマクロ定義は、それらの目的を明確に説明する必要があります
- データ構造(struct、union、enum)およびデータタイプ(typedef)を定義するときは、それらの意味を指定する必要があります
- ヘッダーファイルには、定義ではなく、関数または変数の宣言のみを含める必要があります。.hファイルで関数を実装したり変数を定義したりしないでください。
-
コーディングスタイル
コードのレビューに役立つ静的テストツールはたくさんあります。たとえば、C言語コンパイラには静的コードテストツールが付属しており、検出された問題はそれぞれ「警告」と「エラー」で示されます。さらに、埋め込みフィールドには、埋め込みCプログラミング専用の静的コードテストルールがあります。最も一般的なものはMISRA Cで、必須ルールに分割された21種類の141ルールを定義します(必須)および推奨ルール(アドバイザリ)。PC-Lint、LDRA Testbed、LogiScope / Rule-Checkなど、多くの組み込み静的テストツールにこのルールセットが含まれます。