組込みシステムに適したコンポーネントシステムTECSの最新状況～普及期に入ったTECS～
安積 卓也
大阪大学大学院基礎工学研究科
takuya@sys.es.osaka-u.ac.jp1

目次
 TECSの基本
 ASP3のSyslog, シリアルドライバ、ログタスクの実装に採用
 TECSの利点
 mrubyプラットフォーム

2

TECS (TOPPERS Embedded Component System）
 組込みシステムでコンポーネントベース開発を実現– 再利用性の向上→生産性の向上– インタフェースの明確な定義

補足：
コンポーネント＝ソフトウェア部品

 C 言語のプロトタイプ宣言の曖昧さを TECS がカバー->さまざまなコードを自動生成可能：例は後で説明
 その他の特徴– マルチインスタンス化が容易（デフォルト）
ここを中心に説明
 同種のコンポーネントを複数生成– ダイナミックバインディング（相当）を実現– 関数テーブルを自動生成– カプセル化できる
 関数インタフェースのみで結合– 静的な生成と結合
 実行時オーバヘッド、メモリオーバヘッドの低減
3

TECS 開発の流れ
これだけ知れば、始められる！


TECS CDL (コンポーネント記述言語)の記述– コンポーネント間のインタフェースの記述
 シグニチャ (signature)記述– コンポーネントタイプの記述
 セルタイプ (celltype) 記述– コンポーネントの設置と結合
 組上げ記述 (cell の記述)
≒ コンポーネント図のテキスト表現

C 言語の記述– 振る舞いの記述
 セルタイプコード ＝ C 言語によるプログラム

4

TECS CDL：インタフェースの記述（シグニチャ）

AppFile
シグニチャ
sFile

補足：2重線はプログラムのスタート地点を示す（タスク等）

シグニチャ記述

アプリケーション
開発者
コンポーネント
仕様開発者

設計
仕 様 の規定

シグニチャ記述（インタフェースの定義） セルタイプ記述（コンポーネントの定義）
TECS CDL(コンポーネント記述言語)

TECS
ジェネレータコンポーネント図

組上げ記述（コンポーネントの構成の定義）

プラグイン

signature sFile {設計
ヘッダ
ER open( [in,string]char * fileName, テンプレート[in]int16_tmode);
インタフェースコードコード
ER close(void);
プラグイン
開発者
セルタイプコードＣコンパイラER read( [out,size_is(length),count_is(*count)]int8_t*buffer,（コンポーネントのソースコード）[in]int32_t length, [out]int32_t *count);
リンカ
設計
ER write( [in,size_is(length)]int8_t *buffer,アプリケーションモジュ
[in]int32_t length, [out]int32_t *wroteLength);ール
コンポーネント
開発者
ER seek( [in]int32_t offset);
エンドユーザー
製品利用};
・[ ] 部分を取り除くと、C のプロトタイプ宣言になる
・in, out は入出力方向の指定、size_is, count_is, string はポインタの指定子で、配列長さ、有効要素数、文字列を指定
5RPC
アクセス制御トレース
TECS CDL：コンポーネントの定義（セルタイプ）

App

呼び口cFile

受け口eFile

シグニチャsFile

File

アプリケーション
開発者
コンポーネント仕様開発者

設計
仕 様 の規定

セルタイプ記述

シグニチャ記述（インタフェースの定義） セルタイプ記述（コンポーネントの定義）

celltype tApp{// 呼び口(call) の設置
call sFile cFile;};

TECS CDL(コンポーネント記述言語)••

属性は、デフォルトの値を指定できる(未指定の場合、セル定義時に値指定が必須)(内部)変数は、属性を参照して初期化できる

6

コンポーネント図

組上げ記述（コンポーネントの構成の定義）

RPC
アクセス制御トレース

celltype tFile{
設計// 受け口(entry) の設置
ヘッダ
テンプレート
インタフェース
コード eFile;
コード
entry sFile
プラグイン
attr { セルタイプコード// 属性：コンポーネント（セル）ごとの定数
開発者
Ｃコンパイラ（コンポーネントのint16_t
buffer_len = 512;
ソースコード）リンカ};
設計var { // (内部)変数：セルごとの変数
アプリケーションモジュ
ール[size_is(buffer_len)]コンポーネント開発者
int8_t *buffer;
エンドユーザー製品
利用
int fd; // ファイル記述子};};
TECS
ジェネレータプラグイン

TECS CDL：コンポーネントの設置と結合

App

呼び口
cFile

受け口eFile

シグニチャ
sFile

組上げ記述

File

アプリケーション
開発者
コンポーネント
仕様開発者

設計
仕 様 の規定

コンポーネント図

シグニチャ記述（インタフェースの定義） セルタイプ記述（コンポーネントの定義）
TECS CDL(コンポーネント記述言語)// コンポーネント（セル）の静的な生成
cell tFile File{buffer_len = 64; // 属性
テンプレートコード};
セルタイプコード
cell tApp App{
（コンポーネントのソースコード）//呼び口を受け口に結合設計
cFile = File.eFile;};組上げ記述（コンポーネントの構成の定義）

プラグイン

TECS
ジェネレータ設計
ヘッダ

インタフェースコード
プラグイン開発者

Ｃコンパイラリンカ

アプリケーションモジュール

コンポーネント
開発者
製品

利用エンドユーザー

tecscde(GUIツール)
7

RPC
アクセス制御トレース
C言語 ：振る舞いの記述

App

呼び口cFile

受け口eFile

シグニチャ
sFile

File

テンプレート[tFile.c]#include “tFile_tecsgen.h”

アプリケーション開発者
コンポーネント
仕様開発者}
ER•

仕 様 の規定

TECS CDL(コンポーネント記述言語)

組上げ記述（コンポーネントの構成の定義）

プラグイン

TECS
ジェネレータeFile_open( … )コンポーネントの振舞いを記述
eFile_close():

TECS ジェネレータがテンプレートを生成するので、それを埋める形でセルタイプコードを作成できる

設計

テンプレートコード

セルタイプコード（コンポーネントのソースコード）

設計

ヘッダ

インタフェースコード
プラグイン
開発者

Ｃコンパイラリンカ

アプリケーションモジュール
コンポーネント
開発者
製品

8コンポーネント図

シグニチャ記述（インタフェースの定義） セルタイプ記述（コンポーネントの定義）// 受け口関数 (受け口名)_(関数名)

ER{

設計

利用

エンドユーザー

RPC
アクセス制御トレース
TECSジェネレータ：インタフェースコード生成

App

呼び口
cFile

受け口eFile

File

アプリケーション
開発者
コンポーネント
仕様開発者

シグニチャ
sFile

設計
仕 様 の規定

コンポーネント図

シグニチャ記述（インタフェースの定義） セルタイプ記述（コンポーネントの定義）
TECS CDL(コンポーネント記述言語)••

コンポーネント間をつなぐインタ
フェースコードを TECSジェネレータ
が自動生成
結合状況に応じて関数テーブルを生成したり、属性や変数等に応じてROM部(定数)、RAM 部（RAM を初期化するコード）を生成

組上げ記述（コンポーネントの構成の定義）

プラグイン

TECS
ジェネレータ設計

テンプレートコード

セルタイプコード（コンポーネントのソースコード）

設計

ヘッダ

インタフェースコード
プラグイン
開発者

Ｃコンパイラリンカ

アプリケーションモジュール
コンポーネント開発者
製品

9

利用

エンドユーザー

RPC
アクセス制御
トレース
再利用を上げる仕組み：App
インタフェース定義
signature sSIOPort {void open(void);void close(void);bool_t putChar([in] char c);char getChar(void);
…
};

App
int main(){…

cSIOPort_putChar(c);インタフェース名 関数名…
}

10

補足：SIO：Serial Input Output
TOPPERSでは、ターゲット依存部のシリアルドライバ※実際のアプリケーションはSerialPort（ターゲット非依存）を利用することが多い。
SIOPortB

SIOPortA…
bool_teSIOPort_putChar( CELLIDX idx, char c){…
if (uart_putready(p_cellcb)){
sil_wrw_mem((void*)(ATTR_uartBase + USART_THR),c);
return(true);}
return(false);}…

再利用を上げる仕組み：App
インタフェース定義
signature sSIOPort {void open(void);void close(void);bool_t putChar([in] char c);char getChar(void);
…
};

App
int main(){…

cSIOPort_putChar(c);インタフェース名 関数名…
}

11

SIOPortB

SIOPortA

TECSでは、SIOPortAとSIOPortBのどちらを利用する場合でも、App側の同じコード（C言語）を利用可能=>再利用性の向上

結合の実装構造の標準形
呼び側

受け側

ER

tSIOPort_eSIOPort_putChar_skel{SIOPortA

受け口
ディスクリプタtSIOPort_eSIOPort_putChar_skel( structtag_sSig1_VDES *epd, char c)
struct tag_tSIOPort_eSIOPort_DES *lepd= (struct tag_tSIOPort_eSIOPort_DES *)epd;
return tSIOPort_eSIOPort_putChar( lepd->idx, c );tSIOPort_eSIOPort_getChar_skel}&tSIOPort_eSIOPort_MT

受け口関数テーブルへのポインタ&tSIOPort_SIOPortA_CB

受け側のセルＣＢ
呼び側のセルＣＢtSIOPort_cSIOPort_putChar( p_cellcb, c)#define tApp_cSIOPort_putChar( p_that, c ) ¥(p_that)->cSIOPort->VMT->¥
putChar( (p_that)->cSIOPort, c )typedef struct tag_tApp_CB {/* call port */struct tag_sSIOPort_VDES *cSIOPort;} tApp_CB;

12Ａpp

受け口スケルトン関数

tSIOPort_eSIOPort_close_skel/* 呼び口関数マクロ（短縮形） */#define cSIOPort_putChar( c ) ¥tSIOPort

受け口関数テーブルtSIOPort_eSIOPort_open_skel

cSIOPort_putChar(c )

tApp

受け口関数
bool_t eSIOPort_putChar(CELLIDX idx, char c){CELLCB *p_cellcb;//エラーチェック省略
if (uart_putready(p_cellcb)){
sil_wrw_mem((void*)(ATTR_uartBase + USART_THR),c);
return(true);}return(false);}

結合の実装構造の標準形
呼び側

受け側Ａpp

SIOPortA

受け口スケルトン関数

tSIOPort_eSIOPort_close_skel

ERtSIOPort_eSIOPort_putChar_skel( structtag_sSig1_VDES *epd, char c)

コンポーネントの構成によってはtSIOPort_eSIOPort_putChar_skel{struct tag_tSIOPort_eSIOPort_DES *lepd= (struct tag_tSIOPort_eSIOPort_DES *)epd;
return tSIOPort_eSIOPort_putChar( lepd->idx, c );tSIOPort_eSIOPort_getChar_skel

受け口ディスクリプタ}&tSIOPort_eSIOPort_MT

受け口関数テーブルへのポインタ&tSIOPort_SIOPortA_CB

受け側のセルＣＢ

最適化で省略可/* 呼び口関数マクロ（短縮形） */#define cSIOPort_putChar( c ) ¥呼び側のセルＣＢtSIOPort_cSIOPort_putChar( p_cellcb, c)#define tApp_cSIOPort_putChar( p_that, c ) ¥(p_that)->cSIOPort->VMT->¥
putChar( (p_that)->cSIOPort, c )typedef struct tag_tApp_CB {/* call port */struct tag_sSIOPort_VDES *cSIOPort;} tApp_CB;

13tSIOPort

受け口関数テーブルtSIOPort_eSIOPort_open_skel

cSIOPort_putChar(c )

tApp

受け口関数
bool_t eSIOPort_putChar(CELLIDX idx, char c){
CELLCB *p_cellcb;//エラーチェック省略
if (uart_putready(p_cellcb)){
sil_wrw_mem((void*)(ATTR_uartBase + USART_THR),c);
return(true);}
return(false);}

TECSの適用イメージ①：プラットフォームへの適用アプリケーションモジュール（１）アプリケーションモジュール（２）アプリケーションは、既存と同様の実装（Ｃ言語）
TECSをライブラリとして利用
後述のASP3はこの構成

API

APIラッパ（オプション）

ファイル
システム
各種
ドライバ

その他のミドルウェア（オープン／商品）

カーネル（TOPPERS/ASP, FMP, HRP2, ATK, …）
TECSコンポーネント
14

プラットフォーム

TCP/IP
プロトコル
スタック（TINET, …）

TECSの適用イメージ②：システム全体への適用

アプリケーション アプリケーション
コンポーネント（１） コンポーネント（２）アプリケーションを含めすべての要素をTECS仕様に準拠（再利用性高）実装言語はＣ言語
例：ETロボコン（NXT）
認定プラットフォーム

APIラッパ（オプション）
ファイル
システム
各種
ドライバ

その他のミドルウェア（オープン／商品）

カーネル（TOPPERS/ASP, FMP, HRP2, ATK, …）
TECSコンポーネント
15

プラットフォーム

TCP/IP
プロトコル
スタック（TINET, …）

TECSの適用イメージ③：自動生成機構を利用
mruby：組込みシステム向けにRuby軽量化したスクリプト言語
プログラマはプラットフォーム側（Ｃ言語）の知識がなくても利用可能

アプリケーション
mrubyプログラム

アプリケーション
mrubyプログラム

連携用のコードを自動生成

mrubyブリッジコード
ファイルシステム
各種
ドライバ

その他のミドルウェア（オープン／商品）

カーネル（TOPPERS/ASP, HRP2）
TECSコンポーネント
16

プラットフォーム

TCP/IP
プロトコル
スタック（TINET, …）

TECSがASP3のSyslog, シリアルドライバ、ログタスクの実装に標準採用（予定）○ Syslogの出力先（シリアル、Bluetooth、LCD等）をプログラムを修正せずに変更可能△ ターゲット依存部のポーティングには、TECSの基本を覚える必要がある○TECSのすべての機能を使いこなすには、時間がかかるが、ポーティングに必要な最低限の知識であれば、それほど時間はかからない○ ポーティング初心者には、何を実装すればよいかが明

確になる

17

ASP3：ログタスク＆シリアルドライバの例（Mac）
tLogTask

LogTask

ログタスク
cSysLog

cPutLog

cnSerialPort

cSerialPort

eSerialPort

enSerialPorttSerialPort

SerialPort

eSysLog

SyslogtSyslogtSyslog

ePutLogcSIOPorteSIOCBReSIOPortcSIOCBRtSIOPortMacOSX

SIOPortTarget
tPutLogMacOSX

PutLogTarget
低レベル出力
18

ターゲット
依存部

Serial Input Output

ターゲット
非依存部

ASP3：ログタスク＆シリアルドライバの例（ARM）
tLogTask

LogTask

ログタスク
cSysLog

cPutLog

cnSerialPort

cSerialPort

eSerialPort

enSerialPort

tSerialPort

SerialPort

eSysLog

SyslogtSyslogtSyslog

ePutLogtPutLogCT11MPCore

PutLogTarget
低レベル出力19cSIOPorteSIOCBReSIOPortcSIOCBRtSIOPortCT11MPCorecSIOPort

ターゲット依存部

SIOPortTarget

Serial Input Output

ターゲット
非依存部

SIOPortのポーティング例tSIOPortCT11MPCore

SIOPortTarget

TECSジェネレータがテンプレートコードを生成

インタフェース定義
signature sSIOPort {void open(void);void close(void);bool_t putChar([in] char c);
int_t getChar(void);…
};
celltype tSIOPortCT11MPCoreMain {entry sSIOPort eSIOPort;
…
}

20

void
eSIOPort_open(CELLIDX idx)
{CELLCB*p_cellcb;
if (VALID_IDX(idx)) {p_cellcb = GET_CELLCB(idx);}

ここにドライバのコードを実装}
voideSIOPort_close(CELLIDX idx){…

TECSの利点：複合コンポーネント&cfg
 利用者はSIOPortが複数のコンポーネントで構成されていることを意識せずに利用可能
tSIOPortCT11MPCoretSIOPortCT11MPCore

SIOPorttISR

SIOPorttSIOPortCT11MPCoreMain

tInitializeRoutine

tTerminateRoutine

tInterruptRequest

tUartPL011

カーネルの設定ファイルの生成
CFG_INT(EB_IRQNO_UART0, { TA_NULL, -2 });CRE_ISR(ISRID_tISR_SIOPortTarget_base_ISRInstance, { TA_NULL, &tISR_CB_tab[0],EB_IRQNO_UART0, tISR_start, 1 });ATT_INI({ TA_NULL, NULL, tInitializeRoutine_start });ATT_TER({ TA_NULL, NULL, tTerminateRoutine_start });21TECSの利点：メモリ保護の設定（HRP2）
 コンポーネント記述からHRP2カーネルの設定ファイルを自動生成する– リージョン（グループ化）を保護ドメインに対応させる
 リージョンに所属するタスクおよび、コンポーネント（セル）固有のデータを、対応する保護ドメインに所属させる– 設定ファイルをTECSジェネレータにより出力
region rKernelDomain(trusted)region rDomain1(nontrusted)
TASK1

SWC

SWC1

SVC

パーティション間の通信処理を行うコンポーネント
22

region rDomain2(nontrusted)
TASK2

SWC2

SWC3

この機構の内部をユーザが意識する必要はなく、さらに、拡張サービスコール用の設定ファイルも自動生成される

無所属

SWC4

mrubyプラットフォーム

23

軽量RubyフォーラムとTOPPERSの協業

TECS WG

ライブラリ WG

• 相互に広めあう• 両技術を組み合わせて活用する

24

協業内容（2012年～）


TOPPERS開発者会議（2012/10/21-22）
ETスペシャルセッション（2012/11/15）– TECSの概要とmrubyとの連携（安積）TOPPERS TECS 合宿＠松江（2013/3/22-23）
ESEC（2013/5/8-10）– TOPPERSブース



25

TOPPERSカンファレンス（2013/6/21）
SWEST15：mrubyセッション（2013/8/22-23）
TOPPERS TECS 合宿＠福岡（2014/3/23-24）
TOPPERS TECS 合宿＠有馬（2015/3/22-23）
SWEST17：mrubyセッション（2015/8/27-28）
mruby向けEV3プラットフォームの構築
ETロボコン認定プラットフォーム

EV3用プラットフォーム
https://www.toppers.jp/tecs.html#mruby_ev3rt•
その他のOSS... •LMS2012

名古屋大学により開発
デバドラなどをLinuxなどから移植(LinuxベースのVM)

MINDSTORMS EV3用
TOPPERSプラットフォーム

leJOS(Java VM)

EV3RT

MonoBrick

今回のプラットフォーム(.Net framework)

3devev(Linux)TOPPERS/HRP2TOPPERS/ASP

26

TECS仕様••TECS WGにより開発
mrubyの動作環境

TECSの適用イメージ③：他言語（mruby）との連携
mruby：組込みシステム向けにRuby軽量化したスクリプト言語
プログラマはプラットフォーム側（Ｃ言語）の知識がなくても利用可能

アプリケーション
mrubyプログラム

アプリケーション
mrubyプログラム

連携用のコードを自動生成

mrubyブリッジコード
ファイル
システム
各種
ドライバ

その他のミドルウェア（オープン／商品）

カーネル（TOPPERS/ASP, HRP2）
TECSコンポーネント
27

プラットフォーム

TCP/IP
プロトコル
スタック（TINET, …）

mruby⇒TECSブリッジ 補足：mrubyプラットフォームでは、
setPowerをpower=にリネームして利用

mruby ⇒ C 言語のI/Fの自動生成– TECSのインタフェース定義を利用
下記はモータを利用する例

モータのインタフェース定義
signature sMotor{
ER setPower([in]int power);
ER stop([in] bool_t brake);…
};（呼び先=サーバー側）（呼び元=クライアント側）

mrubyのコード

mruby オブジェクト

振る舞いはmruby で記述
28

BridgeMotorAtMotorMotorA

ブリッジセルセル

tsMotormruby オブジェクト/
TECS コンポーネント
自動生成!!

ev3way_sample
 操作手順– 電源を入れる– 黒色のライン上にカラーセンサを移動– タッチセンサを押す：黒色の値を取得– 白色の上にカラーセンサを移動– タッチセンサを押す：白色の値を取得：しっぽを下ろす– ライン上移動– タッチセンサを押す：ライントレーススタート

29

ev3way_sample.rb：初期化
begin
LCD.puts "ev3way_sample.rb"
ひとつしかないもの（ポート番号指定不要）は、
LCD.puts "--- mruby version ---"
クラスメソッドとして直接呼び出す
Speaker.volume = 1
forward = turn = 0# initialize sensors$sonar = UltrasonicSensor.new(SONAR_SENSOR)$color = ColorSensor.new(COLOR_SENSOR)$color.reflect$touch = TouchSensor.new(TOUCH_SENSOR)$gyro = GyroSensor.new(GYRO_SENSOR)# initialize motors
ポート番号を指定して初期化$motor_l = Motor.new(LEFT_MOTOR)（インスタンス化）$motor_r = Motor.new(RIGHT_MOTOR)$motor_t = Motor.new(TAIL_MOTOR)$motor_t.reset_count# Signal calibration
LED.color = :orange…

30

ev3way_sample.rb：黒色、白色の取得# Calibration$black_value = color_calibration
LCD.puts "black::#{$black_value}"$white_value = color_calibration
LCD.puts "white::#{$white_value}"

ライントレースの基準値を計算

threshold = (($black_value + $white_value) / 2).round# wait startdef color_calibration(n=10)
LCD.puts "Ready to start"
loop {
break if $touch.pressed?
RTOS.delay(10)タッチセンサが押されるまで待つ}
col = 0n.times { col += $color.reflect}
col = (col / n).round
カラーセンサn回取得し、Speaker.tone(:a4, 200)平均値を取得
RTOS.delay(500)colend31

ev3way_sample.rb：スタート準備# wait start
LCD.puts "Ready to start"
loop {#initialize tailtail_control(TAIL_ANGLE_STAND_UP)
RTOS.delay(10)
しっぽの位置を指定された角度に保つ# Touch sensor start（フィードバック制御）
break if $touch.pressed?
}# reset motor encoder
def tail_control(angle)$motor_l.reset_count
目標値
現在の値$motor_r.reset_count# reset Gyro sensor
pwm = ((angle - $motor_t.count) * P_GAIN).to_i$gyro.reset
pwm = (pwm > PWM_ABS_MAX) ? PWM_ABS_MAX :(pwm < -PWM_ABS_MAX) ? -PWM_ABS_MAX : pwm# Signal start status$motor_t.power = pwm
LED.color = :green$motor_t.stop(true) if pwm == 0end32

ev3way_sample.rb：ライントレース
障害物まで一定の距離以下になると止まる

def sonar_alert$sonar_counter += 1
if $sonar_counter == 10
distance = $sonar.distance$sonar_alert = distance <=SONAR_ALERT_DISTANCE&& distance >= 0$sonar_counter = 0
end$sonar_alert
end# main loop
forward = turn = 0loop {start = RTOS.msec# up tailtail_control(TAIL_ANGLE_DRIVE)
if sonar_alert
forward = turn = 0
else# Line trace
turn = $color.reflect >= threshold ? 20 : -20
forward = 30
end
カラーセンサと閾値と比較し…
サンプルでは、30に固定
どちらかに曲がる}
ここを変更すると、自前のライントレースが可能33

ev3way_sample.rb：倒立制御# main loop
loop {start = RTOS.msec…
バランサの返り値が2つ# call balance_control API
pwm_l, pwm_r = Balancer.control(forward.to_f,turn.to_f,$gyro.rate.to_f,
C言語で実装されたバランサを呼び出す
GYRO_OFFSET,$motor_l.count.to_f,$motor_r.count.to_f,
Battery.mV.to_f)$motor_l.stop(true) if pwm_l == 0$motor_l.power = pwm_l$motor_r.stop(true) if pwm_r == 0$motor_r.power = pwm_r
4ミリ秒周期で実行
wait = 4 - (RTOS.msec - start)
現状1ミリ秒程度で処理完了
RTOS.delay(wait) if wait > 0
mubyでも十分制御可能}
34

わからないときは

TOPPERS 会員の皆さま– com-wg@toppers.jp– dev@toppers.jp

非会員の皆さま– users@toppers.jp

35… TECS WG の ML
… 開発者 ML… ユーザー ML