今回の記事ではEXORのex707M上でCodesys実行し、なおかつマルチコアの設定手順とコンセプトを説明します。
さ、FAを楽しもう!

マルチコア?
現在のコントローラでは、複数のCPUコアを持つプロセッサがますます一般的になっています。タスクとCPUコアの割り当ては、Task Configuration オブジェクトの [Task Groups] タブに表示されます。
マルチコア機能を使用せずに、マルチコアコントローラ上で CODESYS を実行した場合、タスクの分配はオペレーティングシステムのスケジューラによって処理されます。
これは、アプリケーションが複数のタスクで実行されている場合に当てはまります。この場合、タスクの分配に直接影響を与えることはできません。負荷分散の状況によっては、タスクが異なるCPUコア上で実行されることもあります。
CODESYS Multicore 機能を使用すると、取得済みのCPUコア数を考慮しながら、IECタスク自体を専用のCPUコアに割り当てることができます。これにより、性能が向上する場合があります。この目的のために、IECプログラムを複数のタスクに分割する必要があります。
注意!
IECタスクをCPUコアに分散させる場合、IECプログラムの動作にいくつかの変化が生じるため、それらを考慮する必要があります。
-
優先度によるIECタスクの処理順序は保証されなくなります。
タスクが1つのCPUコアにまとめられている場合にのみ、優先度に従って処理されます。 - 最も高い優先度を持つIECタスク内のデータについて、サイクル中の一貫性は保証されなくなります。そのため、サイクル中に値が変化してはいけない場合は、IECタスクサイクルの開始時にデータをローカルへコピーする必要があります。
- すべてのタスクには、各タスクが動作するための共有プロセスイメージがあります。
- 各タスクの開始時に ReadInputs() 関数が呼び出され、終了時に WriteOutputs() 関数が呼び出されます。
- その結果、I/Oモデルは各タスクに個別に適用されます。これらの関数は共有プロセスイメージに対して読み書きを行います。
- プロセスイメージは、バスサイクルタスクが呼び出された場合にのみ、パケットとして物理的に書き込まれたり、送信されたりします。
- ただし、バスサイクルタスクは各タスクの WriteOutputs および ReadInputs の呼び出しを待機します。
バスサイクルタスク設定
こちらはBusのCycle 足す設定です。

IOアクセス設定
こちらはTask Deployment タブで、IOはどのTaskで更新するかを確認・変更できます。

注意!データの一貫性
- IECプログラムにおいて、マルチコアCPU上ではビットアクセス(データ型 BIT)は一貫して(アトミックに)処理されません。そのため、外部ライブラリ関数 SysCpuTestAndSetBit() を使用することを推奨します。
- 32ビット幅までの単純なデータ型(BOOL、BYTE、WORD / INT、DWORD / DINT など)は、マルチコアCPU上のIECプログラムでも一貫して(アトミックに)処理されます。
- 64ビットのデータ型(LINT、LWORD、LREAL)は、64ビットシステムかつマルチコアシステム上のIECプログラムでのみ、一貫して(アトミックに)処理されます。
- 複雑なデータ型(STRING、FB、STRUCT、ARRAY)へアクセスする場合は、同期/データ一貫性を確保するための対策を自分で行う必要があります。Task Configuration の Variable Usage タブでは、IECタスク内の変数について、読み取りアクセスまたは書き込みアクセスがあるかどうかを定義できます。
アトミックとは?
アトミックとは「途中で他の処理に割り込まれず、ひとかたまりとして完了する操作」です。たとえば共有変数 Counter を2つのタスクを同時に触るとします。
Task A: Counter := Counter + 1 |
|---|
一見すると2増えそうですが、実際には内部はこうです:
1. Counterを読む
|
|---|
たとえば両方が同時に Counter = 10 を読んだら、
Task A: 10 → 11
|
|---|
結果は 12 ではなく 11 になってしまいます。
これが競合です。
アトミック操作なら、
Task A: 10 → 11
Task B: 11 → 12
のように、他のCPUが途中に割り込まない形で更新されます。
Memory Reorderingとは
CPUやコンパイラが、見かけ上の命令順序を入れ替えることです。例えばプログラム上は、
Data := 100;
|
|---|
なので人間からすると当然この順番に見えます。
① Data = 100
|
|---|
ところがマルチコアCPUでは性能向上のために、CPU内部で書き込みが遅延したり、別のCoreから見える順番が変わったりしています。
Core 1から見るということが起こり得ます。
Ready = TRUE
|
|---|
すると別TaskがCore 1で、Data = 古い値を読む可能性があります。
IF Ready THEN
|
|---|
これが Memory Reordering の問題です。プログラマの期待では、
Data = 100
↓
Ready = TRUE
↓
でも実際には、Core1がData=100を読むときこの可能性があります。
Ready = TRUE ─────────────→ Core1から先に見える
|
|---|
Core1はこういうことがあり得ます:
Core1:
|
|---|
Codesysの取説には__MemoryBarrier()関数を使用することです。__MemoryBarrier()は、ここより前のメモリアクセスをきちんと完了・可視化してから、ここより後へ進むような境界を作ります。例えば概念的にはこうです。
Data := 100;
|
|---|
手順
テストを追加するには、CodesysのプロジェクトにあるTask Configuration→右クリック→Add Object→Taskします。

Task名を設定します。

Taskが追加されました。

先程追加したTaskを開き、Add Callします。

プロジェクトにあるPOUをTaskに割り付けましょう。

今回は下図のように2つのEthernet/IPタスクを追加します。

結果
最初にCODESYSのProcess IDを確認します。
UN78XXXXM03001268 admin@HMI-11eb:~$ ps -eLo pid,tid,psr,cls,rtprio,pri,comm | grep codesys
|
|---|
Process3036を確認すると、CODESYSのTask Group設定どおり、IEC Taskが Core 1 / Core 3 で実際に動いています。
UN78XXXXM03001268 admin@HMI-11eb:~$ ps -eLo pid,tid,psr,cls,rtprio,pri,comm | awk ‘$1 == 3036’
|
|---|
特にこの4行で、Core1とCore3で実行してるタスクがあるとわかります。
3036 3194 3 FF 56 96 ENIPScannerIOTa
|
|---|
CODESYS Task | CODESYS Core | Linux PSR | Linux Scheduler | RT Priority |
|---|---|---|---|---|
ENIPScannerIOTask | 3 | 3 | FF | 56 |
MainTask | 1 | 1 | FF | 55 |
Profinet_CommunicationTask | 1 | 1 | FF | 42 |
Profinet_IOTask | 1 | 1 | FF | 55 |
ENIPScannerServiceTask | 1 | 1 | TS | – |
ライセンス
また、Codesysでマルチコアを使用するには別ライセンスの可能性があり、メーカーに確認する必要があります。

Codesysマルチコアを使うには、最低限下図のどちらかのライセンスが必要です。
- CODESYS Control Performance M
- CODESYS Control Performance L
