Codesys#マルチコアを使ってみよう

今回の記事では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
Task
B: Counter := Counter + 1

一見すると2増えそうですが、実際には内部はこうです:

1. Counterを読む
2. +1する
3. Counterへ書く

たとえば両方が同時に Counter = 10 を読んだら、

Task A: 10 → 11
Task B: 10 → 11

結果は 12 ではなく 11 になってしまいます。

これが競合です。

アトミック操作なら、

Task A: 10 → 11

Task B: 11 → 12

のように、他のCPUが途中に割り込まない形で更新されます。

Memory Reorderingとは

CPUやコンパイラが、見かけ上の命令順序を入れ替えることです。例えばプログラム上は、

Data := 100;
Ready := TRUE;

なので人間からすると当然この順番に見えます。

① Data = 100
② Ready = TRUE

ところがマルチコアCPUでは性能向上のために、CPU内部で書き込みが遅延したり、別のCoreから見える順番が変わったりしています。

Core 1から見るということが起こり得ます。

Ready = TRUE

少し遅れて
Data = 100

すると別TaskがCore 1で、Data = 古い値を読む可能性があります。

IF Ready THEN
x := Data;
END_IF
Ready
= TRUE

これが Memory Reordering の問題です。プログラマの期待では、

Data = 100

Ready = TRUE

でも実際には、Core1がData=100を読むときこの可能性があります。

Ready = TRUE ─────────────→ Core1から先に見える
Data = 100 ──────遅延───→

Core1はこういうことがあり得ます:

Core1:
Ready = TRUE
Data = 古い値

Codesysの取説には__MemoryBarrier()関数を使用することです。__MemoryBarrier()は、ここより前のメモリアクセスをきちんと完了・可視化してから、ここより後へ進むような境界を作ります。例えば概念的にはこうです。

Data := 100;
__MemoryBarrier();
Ready := TRUE;

手順

テストを追加するには、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
3036 3036 0 TS – 19 codesyscontrol

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’
3036 3036 2 TS – 19 codesyscontrol
3036 3038 0 TS – 19 EPADRunner
3036 3042 0 TS – 19 QThread
3036 3112 0 TS – 19 CMHooksTask
3036 3150 0 TS – 19 retain
3036 3164 0 TS – 19 SysWindowSingle
3036 3170 2 TS – 19 QXcbEventQueue
3036 3178 0 FF 58 98 CAAEventTask
3036 3179 0 TS – 19 OPCUATask
3036 3180 0 TS – 19 SchedProcessorL
3036 3181 1 TS – 19 SchedProcessorL
3036 3182 2 TS – 19 SchedProcessorL
3036 3183 3 TS – 19 SchedProcessorL
3036 3184 0 FF 78 118 SchedException
3036 3185 0 FF 83 123 Schedule
3036 3186 2 FF 73 113 TaskGapTask
3036 3193 0 TS – 19 IoMgrDiagTask
3036 3194 3 FF 56 96 ENIPScannerIOTa
3036 3195 1 TS – 20 ENIPScannerServ
3036 3196 1 FF 55 95 MainTask
3036 3197 1 FF 42 82 Profinet_Commun
3036 3198 1 FF 55 95 Profinet_IOTask
3036 3199 0 TS – 19 OPCUAServerWork
3036 3200 0 TS – 19 OPCUAServerWork
3036 3201 0 TS – 19 OPCUAServerSche
3036 3202 2 TS – 19 WebServerCloseC
3036 3203 0 TS – 19 MiscCommandServ
3036 3204 2 TS – 19 MiscCommandFBSe
3036 3205 0 TS – 19 BlkDrvTcp
3036 3206 0 TS – 19 BlkDrvUdp
3036 3207 0 TS – 19 OPCUAServer
3036 3208 0 TS – 19 CMCommCycleTask
3036 3235 0 TS – 19 Async_SrvServic
3036 4324 0 TS – 19 Async_SrvServic

特にこの4行で、Core1とCore3で実行してるタスクがあるとわかります。

3036 3194 3 FF 56 96 ENIPScannerIOTa
3036 3196 1 FF 55 95 MainTask
3036 3197 1 FF 42 82 Profinet_Commun
3036 3198 1 FF 55 95 Profinet_IOTask

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

シェアする

  • このエントリーをはてなブックマークに追加

フォローする