document

AVR用スレッドの実装
B3 suno
親 ide syu-he-
目次





概要
スレッドの実装の問題点・解決策
実装
デモ
まとめ
背景・目的

貧弱なプロセッサでも使い場所はまだまだある



簡単な処理にはスペック的に十分
安い
複数の機能を実装すると機能がまざる

それぞれの機能をスレッドとして実装すれば
スレッド単位で再利用できる

計算量が多い処理は割り込みで処理できない

貧弱なプロセッサで動作するスレッドを実装する
スレッドとは

プログラムの実行の単位
(ASCII24デジタル用語辞典)



CPUは複数の処理を同時には行えない


並行して実行できる
メモリ空間を共有している
頻繁に処理内容を切り換えて実行している
スレッド固有の情報



プログラムカウンタ – 実行中のプログラム
スタックレジスタ – 現在のスタックの位置
汎用レジスタ
使ったチップ

Atmel AT90s2313




速さ 4MHz
プログラム領域 2KB
メモリ 128byte SDRAM
汎用レジスタ 8bit * 32
開発環境

WinAVR(avr-gcc)


AVR用のCコンパイラ
AVRStudio

AVR用アセンブラ+シミュレータ
スレッドの実装の問題点

とにかくメモリが小さい

メモリ128 byte、汎用レジスタが32本




1スレッドごとに汎用レジスタ保存用に32 byte
1スレッドごとにスタックレジスタを保存
1スレッドごとにスタック領域1つ
動的なメモリ確保はできない

そんな余裕なし
解決策


コンパイル時にスレッド数を決める
静的にメモリを確保


スタック領域
スレッド情報保存用構造体
実装




実装方針
スレッドの初期化
コンテキストスイッチ
動かしてみたコード
実装方針

スタックレジスタ


汎用レジスタ


静的に予約したメモリ空間に退避
プログラムカウンタ



静的に予約したメモリ空間に退避
スタック上に保存
割り込みハンドラからのreturnで元のコードに戻る
スレッド切り換えのタイミング

内部のカウンタによる割り込み時
スレッドの初期化

char createThread
(void (*entrypoint)(char),char arg)



第一引数に指定した関数が第二引数のchar
を引数に呼び出される
すぐ実行されるわけではなく、スレッドの切り
換えはタイマーで行われる
成功で1,失敗で0
スレッドの初期化の手順


スレッド情報の初期化
実行中のスレッドに追加
スレッド情報の初期化

Threadinfo構造体の初期化



スタックの末尾にエントリーポイントのアドレ
スをセット

Threadinfo
汎用レジスタの24番にエントリーポイントの関数
の引数をセット
スタックレジスタをスタックの後ろから5バイト目に
セット
エンディアンが逆なのに注意
汎用レジスタ
関数への引数(r24)
stackarea
スタック
次のスレッドの構造体
スタックレジスタ
エントリーポイント
実行中のスレッドに追加

新しいThreadinfo構造体を実行中のスレッ
ドのリストに加える
汎用レジスタ
汎用レジスタ
汎用レジスタ
汎用レジスタ
汎用レジスタ
スレッド切り換え

SIGNAL (SIG_OVERFLOW1)

内部のタイマーのオーバーフローでスレッド切
り換え
スレッド切り替えの手順





r26-r31の値を静的領域に退避
currentinfoをcurrentinfo->nextに
スタックレジスタの入れ替え
r0-r27を入れ替え
r28-r31を入れ替え
r26-r31の値を静的領域に退避


レジスタの入れ替えの作業用レジスタとし
てr26-31を利用する
上書きしてはいけないのでメモリに退避す
る
レジスタ
char tmpregisterarea[6]
r26-31
r26-31
currentinfoをcurrentinfo->nextに

currentinfoは現在実行中のスレッド構造
体を指す

nextは次に実行するスレッドの構造体を指す
汎用レジスタ
Threadinfo *currentinfo
汎用レジスタ
スタックレジスタの入れ替え


現在のスタックレジスタの値と次のスレッド
のスタックレジスタの値を入れ替える
r26,27はこれ以降入れ替え作業用には使
わないのでレジスタに戻す
スタックレジスタ
レジスタ
(*currentinfo)
char tmpregisterarea[6]
r26-31
threads[n]->stack
r0-r27を入れ替え



次のスレッドのレジスタ保存領域からr0の
値をr28にロード
現在のr0の値をレジスタ保存領域にストア
r28にロードされている次のスレッドのr0の
値をr0にコピー
レジスタ
*currentinfo
r28-r31を入れ替え




次のスレッドのr28をレジスタ保存領域から
ロードしてpushする
退避していた前のスレッドのr28をロードし、
レジスタ保存領域にストア
繰り返す
まとめてpop レジスタ
スタック
(*currentinfo)
char tmpregisterarea[6]
r28-31
作ったもの


LEDを点滅させるプログラム
プライマリスレッドと別のスレッド2つがLED
を点滅させる

計3つのLEDが点滅
実際動かしたコード


LEDを点滅させる
引数で操作する
PORTBのピンを
指定する
void ticklight(char pin){
int loop1=0;
for(;;){
loop1++
if(!loop){
if(bit_is_clear(PORTB, pin)) {
sbi(PORTB, port);//off
} else {
cbi(PORTB, pin);//on
}
}
}
}
}
スレッド生成コード

引数を変えることでそれぞれの担当する
LEDを決めている
int main (void){
ioinit ();
sei ();
createThread((void (*)(char))tickLight,0);
createThread((void (*)(char))tickLight,1);
tickLight(2);
}
デモ

実機


LEDが点滅します
シミュレータ
まとめ


at90s2313上でスレッドの実装を行った
LEDを点滅させるプログラムを並行させて
動かした
感想

電気回路は難しい


未だによくわからない
低スペックだとスレッドをいっぱい作るのは
厳しい



メモリが超ギリギリ
プログラムサイズもちょっと大きい
割り込みでできるところは割り込みですべき