2007年7月26日 星期四

FPPA 多執行緒?

這兩天看到這篇文章 Coroutine in FPPA,寫有關於用同一顆 CPU 來跑不同 TASK 的問題。
突然也靈機一動,想來試試這樣子的機制可以用在哪邊~

答案是~多執行緒~Time Sharing

把一個CPU當成四個來用,靠TIMER中斷來控制PC頁的切換~
可惜由於中斷只有FPP0能產生,所以只能作用在FPP0~
不過,要切成幾個分時的TASK基本上都沒有問題~嘿~有趣了。

測試的中間也發現一個有趣的問題,delay 這個指令由於不是一個指令周期就能完成(同時 acc 遞減),導致有可能delay到一半被 swap out,再回來時 PC 又重回到了delay上(acc又重置),結果就是 delay 不完。
解決的方法目前想到的就是暫時把中斷關掉嘍。(安全起見)

另一個問題,萬一 time slice 小於一個指令周期的話,會造成類似上面的情形,指令尚未完成,PC還沒來的及往後加,就被 swap out,再回來又回到了原點,結果就是一個指令永遠也跑不完。

這樣子的狀況代表的意義很值得深入去探討,也就是關係到 fppa 核心硬體設計上的特性~
於是會不會有一種可能,太快的 timer interrupt 或 time slice 的大小~造成「不正常的」swap out?

解決的方式可能還有待進行各種不同的試驗才會知道,應當是可以調整到切出固定的 time slice 出來(正確來講是分配到同樣多的指令周期),當然如果你的要求不是很嚴苛的話,那只要確保能往下跑也就ok了。

另外一種解決方式,就是用 internal interrupt 的方式來由各 task 控制把自己的執行權讓出來,這也是可行,不過好像就少了那種味道了。

至於是不是還有其它更多問題? 我想要實際拿來用才知道了。

不過話說回來,也蠻懷疑這樣大費周張的效益倒底有多大,因為畢竟八核己經很夠用了,而且除非要寫的程式要控制的事項非常的煩多,才會突顯出它的便利性吧?


// ======== START RAM Define ========
.ramadr 0x00
word nousePC;
word task0PC;
word task1PC;
word task2PC;
word task3PC;
word t16index;
int taskno;
// ======== END RAM Define ========

// ======== START FPPA ========
.romadr 0x0000
goto fpp0Boot;
goto fpp1Boot;
goto fpp2Boot;
goto fpp3Boot;
goto fpp4Boot;
goto fpp5Boot;
goto fpp6Boot;
goto fpp7Boot;
// ======== END FPPA ========

// ======== START Interrpt Service Routine ========
.romadr 0x0010
mov a, 0xFF; /// 第一次產生中斷,stack裡的pc是無用的
ceqsn taskno, a;
goto switch01;
popw nousePC;
pushw task0PC; /// 準備切到task0 pc
goto switchOk;
switch01:
mov a, 0;
ceqsn taskno, a;
goto switch12;
popw task0PC; /// 保存好task0 PC
pushw task1PC; /// 準備切到task1
goto switchOk;
switch12:
mov a, 1;
ceqsn taskno, a;
goto switch23;
popw task1PC; /// 保存好task1 PC
pushw task2PC; /// 準備切到task2
goto switchOk;
switch23:
mov a, 2;
ceqsn taskno, a;
goto switch30;
popw task2PC; /// 保存好task2 PC
pushw task3PC; /// 準備切到task3
goto switchOk;
switch30:
mov a, 3;
ceqsn taskno, a;
goto switchOk;
popw task3PC; /// 保存好task3 PC
pushw task0PC; // /準備切到task0
switchOk:
inc taskno; /// 記錄目前task編號-遞增循環
mov a, 0b00000011;
and taskno, a;
stt16 t16index ; /// 重置 Time Slice
set0 intrq.2;
reti;
// ======== END Interrpt Service Routine ========
fpp0boot:
// -------- START FPPA Boot Setting --------
///...略
// -------- END FPPA Boot Setting --------
// -------- START Interrupt initial setup --------
mov a, 0x00;
mov intrq, a; /// clear interrpt reqest register
mov a, 0b00000100;
mov inten, a; /// enable Timer16 interrupt
// -------- END Interrupt initial setup --------

// -------- START Timer16 initial setup --------
mov a, 0x00; /// timer 16 is up-count
mov lb@t16index, a ; ///Time Slice
mov a, 0x00;
mov hb@t16index, a ;

mov a, 0b10010000 ;
mov t16m, a ; /// Set OSC/Prescale
// -------- END Timer16 initial setup --------

// -------- START variables initialize --------
mov a, la@Task0;
mov lb@task0PC, a;
mov a, ha@Task0;
mov hb@task0PC, a;

mov a, la@Task1;
mov lb@task1PC, a;
mov a, ha@Task1;
mov hb@task1PC, a;

mov a, la@Task2;
mov lb@task2PC, a;
mov a, ha@Task2;
mov hb@task2PC, a;

mov a, la@Task3;
mov lb@task3PC, a;
mov a, ha@Task3;
mov hb@task3PC, a;

mov a, 0xFF;
mov taskno, a;
// -------- END variables initialize --------

/// Enable ALL FPP !!
mov a, 0b11111111;
mov fppen, a ;

/// Stack Pointer Setting for fpp0
mov a, 0x60;
mov sp, a;

stt16 t16index ;
engint;
goto $; /// Idle... waitting first interrupt
Task0:
tog LED0;
goto Task0;
Task1:
tog LED1;
goto Task1;
Task2:
tog LED2;
goto Task2;
Task3:
tog LED3;
goto Task3;

不同速度各 CPU 同步的問題

在不同時脈下工作的 CPU,例如在 pmode=0 的模式 fpp0(/2) fpp1(/8)
假設 fpp0 的 1T=1us、那麼 fpp1 的 1T=4us…

程式碼A:
fpp0:
delay unknown_cycles
set0 signal
goto fpp0
fpp1:
wait0 signal
set1 signal
goto fpp1

問題:fpp1 在 wait 的時候是每 4us 去檢查一次 signal 或者是每 1us 就會檢查一次(或更即時?) 並且往下跑 ? (這影響到 fpp1 頻率固定的問題)

實際測試結果==>

使用 ICE 單步追蹤的結果是 wiat 指令是按當時 CPU 的指令周期來算的,fppa1每 4us 才會檢查一次signal ~(真正的晶片應該也是?)

於是底下程式碼就有可能變成 fpp1 always waitting。(因為每次去檢查都不巧被設為1)

程式碼B:
fpp0Loop:
set0 signal;
set0 signal;
set1 signal;
goto fpp0Loop;

fpp1Loop:
wait0 signal;
// do something
goto fpp1Loop;

再回頭看原來的程式碼A…
程式碼A的 fpp1 有可能在檢查 signal 之前剛好 signal 被設為 0
也可能在檢查 signal 之後的 3us 之內 signal 才被設為 0,於是有了誤差。

相反的,若是快的 cpu 等慢的 cpu 也會有搶到拍子的情況發生。

結論==>
 這應該是屬於 fppa 硬體上的特性,程式設計師自己要小心留意到的一個問題。

2007年7月22日 星期日

簡單的按鈕防彈跳程式碼

 wait1 BTN0;
call Delay;
t1sn BTN0;
goto $-3;
// TODO: button down
wait0 BTN0;
call Delay;
t0sn BTN0;
goto $-3;
// TODO: button release

2007年7月21日 星期六

FPPA 副程式區城變數的問題(結語)

這樣子的結果,感覺還不錯

雖然還不確定在實際應用面能起多大作用

但是,至少在多了一種控制多 cpu 的方式

可以預見,在一些場合,會須要有類似這樣子的(先後)機制

譬如,一個專司 LCM 模組的 cpu,就不能隨機的接受輸出,不然輸出會不成樣…

當然,這些是必須要花一些 cpu 成本的,(大約7-8個指令周期)

而且,可能會有不確定性~(的等待時間)

再者,這也只是自己試的一個方法,還沒有完整的驗證過(包括硬體層面)

說不一定在怎樣的情況下會造成打死結的情況~!?

2007年7月20日 星期五

FPPA 副程式區城變數的問題(正解…?)

延續上一次的架構…
多加了一個鎖,來讓等待中的cpu同步進入要求 share 變數的階段…

現在手邊 ICE 正在跑 8 個 CPU 同步在執行同一段 delay 副程式,執行次數可以說完全一樣。
也試過 8 個 CPU 不同步( 幾乎隨機 ),跑同一段 delay 程式,沒有明顯互相鎖死的情況發生,也確實都有在跑。
再來, 8 個 cpu 不同步,也不同速( mode 0 ),跑同一段 delay 程式,沒有明顯互相鎖死的情況發生,也確實都有在跑。

嘿~看來真的可行~
一個 share 變數( 或一段程式 ) exclusive 執行。
意即~一段程式可以( 安全 )共享執行。


重點在這兩行
inc share_token;
dzsn share_token;
8核同時執行,但是share_token狀態唯一的特性使得只有一個核心得到0的結果。

WaitToken macro // min cost 8 i-cycle
wait0 wait_look;
inc share_token;
dzsn share_token;
goto $-3;
inc share_token;
set1 wait_look;
endm

FreeToken macro
dec share_token;
set0 wait_look;
endm

2007年7月19日 星期四

FPPA 副程式區城變數的問題(意想不到的問題)

// 取得 share 變數使用權:
inc share_token; // 要求取得 share 變數使用權
dzsn share_token; // 由於保證 ram 內容一次只有一個 fppa 能動~所以能保證只有一個 fppa 能過這關
goto $-2; // 其它 fppa 進入等待的循環
inc share_token; // fppa 取得 share 變數使用權

// 釋放 share 變數使用權 :
dec share_token; // fppa 釋放 share 變數使用權

當cpu都同步同速時~幾乎沒有問題~
反之,各cpu就有可能在1-2間打結,造成 share_token 永不為零的狀況,死當。

這個問題很有可能不被發現~要看你這段程式的使用頻率狀況而定~在隨機的情況下,發生的機率應該很大。

2007年7月17日 星期二

FPPA 副程式區城變數的問題(解法)

這個方法~看起來好像很不錯~嘿
簡單好用,稍加修改還可以用 share 作雙迴圈
但是萬萬沒想到有個嚴重的缺點~是什麼?


.ramadr 0x00
word share;
int share_token;

WaitToken macro
inc share_token;
dzsn share_token;
goto $-2;
inc share_token;
endm

FreeToken macro
dec share_token;
endm

Delay:
WaitToken;
mov a, 50;
mov lb@share, a;
DelayLoop:
pushw share;
FreeToken;

delay 200; // do some thing here

WaitToken;
popw share;
dzsn lb@share;
goto DelayLoop;

FreeToken;

ret;

2007年7月15日 星期日

FPPA 副程式區城變數的問題

FPPA 由於各 CPU 沒有各自獨立的 RAM SPACE,8-CPU RAM 都是共用的,所以在呼叫副程式時有可能會造成變數干擾的情況發生,例如一個DELAY的副程式…


Delay:
mov a, 50;
mov count, a;
DelayLoop:
delay 200;
dzsn count;
goto DelayLoop;
ret;
外迴圈要用到一個變數 count,如果多 cpu 同時來呼叫這個副程式,那必定會大亂。
當然,同樣的狀況也可能發生在主程式,我想這也是多核心程式撰上要很小心的一個問題。(最好 complier 有辦法預先檢驗出來? )
何解?
首先想到的是要規畫一段記憶體給這個副程式用,而且當不同的 CPU 進來的時候,副程式要能「知道」
最先想到的是 stack space,因為每個 cpu sp 不一樣,所以可以拿來利用。
問題是 FPPA 沒有類似 idzsn [index] 的指令,不可以直接操作 sp 指到的記憶體內容。
pushw [sp]?
POPW [SP]?
idzsn [index]?

或者如果能在硬體上多一個可以拿來作 ram index 的 暫存器 問題就都解決了。

於是要多一個變數來暫存,問題又回到原點嘍~同變數名一定會被不同 cpu 干擾(或說 thread 比較恰當)
再想想?
嗯~沒錯,要 lock,用 lock、unlock 的概念來控制這個變數的存取試試…
wait0 SIGNAL; // or any other signal.
set1 SIGNAL; // lock signal
…使用變數…

set0 SIGNAL; // unlock signal
結果,失敗,8-CPU同時等在 wait0 SIGNAL 時,又會同時跑到 set1 SIGNAL。
所以除非 CPU 有錯開,否則一樣會錯亂。
但程式在隨機呼叫的情況下又不可能保證不會同時呼叫到。
還有什麼好方法?
下回分解…

2007年7月14日 星期六

FPPA 指令速查表


set0 bit 位元設0
set1 bit 位元設1
tog bit 位元0/1切換
swapc bit 位元與c切換
===================
call addr 呼叫副程式
goto addr 跳躍到標籤
icall [index] 呼叫指標指到的副程式
igoto [index] 跳躍到指標指到的標籤
delay x 延時 x+1 周期
ret / reti /ret x 副程式返回主程式
nop 空指令
pcadd a 程式指標+a往下跳(case用法?)
engint 致能中斷
disgint 除能中斷
stopsys 系統中止
reset 系統重置
wdreset 看門狗重置
pmode n fppa分時模式設定
===================
ceqsn x,y if x=y skip
t0sn bit if b=0 skip
t1sn bit if b=1 skip
izsn x x+=1,if x=0 skip
dzsn x x-=1, if x=0 skip
wait0 bit 等待直到 b=0
wait1 bit 等待直到 b=1
===================
sr x 位元右移,補0
src x 位元右移,補c
sl x 位元左移,補0
slc x 位元左移,補c
swap x 交換高低 4bits
===================
add x,y x=x+y
addc x,y x=x+y+c
nadd x,y x=-x+y
sub x,y x=x-y
subc x,y x=x-y-c
inc x x+=1
dec x x-=1
clear x x=0
mul (mulhr,a)=mulop*a
===================
mov x,y x=y
pushw [index] / pushw pcN push index的值或 pc 到 stack
popw [index] / popw pcN 從 stack pop 值到 index 或 pc
ldtabh [index] 從 index (word計算)讀取高 byte(到a) ==>rom
ldtabl [index] 從 index (word計算)讀取低 byte(到a) ==>rom
ldt16 [index] 將 timer16 值存到 index 中 (??index是2byte計算?) ==>ram
stt16 [index] 將 index 指到的值存回 timer16 (??index是2byte計算?) ==>ram
idxm a,[index] / idxm [index],a 從 ram 中讀/存 值
xch x 將值跟 a 交換
===================
and x,y x=x and y
or x,y x=x or y
xor x,y x=x xor y
not x 1的補數(反相) x=xnot x
neg x 2的補數(負) x=-x
comp x,y x-y?
===================
hb@xxx xxx 的高 byte
lb@xxx xxx 的低 byte
ha@xx xxx 的位址高 byte
la@xxx xxx 的位址高 byte
===================
.define xxx
.ifdef xxx
.else
.endif
.include "xxx"
x equ y
xxx macro
endm
.ramadr xxxx
.romadr xxxx
word xxx
int xxx
dc xxxx,oooo

2007年7月13日 星期五

奇怪的問題

mov a, 0b01010100;
mov clkmd, a; // Internal High RC

ldtabl tablePtr; // pc 會亂掉~亂指

但是在 Internal High RC/2 以下的模式就不會有問題。

2007年7月12日 星期四

FPPA 版的跑馬燈!!! 感想

寫完讓人體驗到「即時」「多工」的威力。
很想多想點有趣的點子來試試。

FPPA 版的跑馬燈!!! PART II

再來一個~這是它的變化型~(按住就會變成一長串喔~)




FPPA0Loop:
wait0 Btn0;
set1 Led0;
wait1 Btn0;
set0 Led0;
goto FPPA0Loop;

FPPA1Loop:
wait1 Led0;
delay Delay;
set1 Led1;
wait0 Led0;
delay Delay;
set0 Led1;
goto FPPA1Loop;

FPPA2Loop:
wait1 Led1;
delay Delay;
set1 Led2;
wait0 Led1;
delay Delay;
set0 Led2;
goto FPPA2Loop;

FPPA3Loop:
wait1 Led2;
delay Delay;
set1 Led3;
wait0 Led2;
delay Delay;
set0 Led3;
goto FPPA3Loop;

FPPA4Loop:
wait1 Led3;
delay Delay;
set1 Led4;
wait0 Led3;
delay Delay;
set0 Led4;
goto FPPA4Loop;

FPPA5Loop:
wait1 Led4;
delay Delay;
set1 Led5;
wait0 Led4;
delay Delay;
set0 Led5;
goto FPPA5Loop;

FPPA6Loop:
wait1 Led5;
delay Delay;
set1 Led6;
wait0 Led5;
delay Delay;
set0 Led6;
goto FPPA6Loop;

FPPA7Loop:
wait1 Led6;
delay Delay;
set1 Led7;
wait0 Led6;
delay Delay;
set0 Led7;
goto FPPA7Loop;

FPPA 版的跑馬燈!!!

哈-很有在跑的感覺~(用手跑)



FPPA0Loop:
wait1 Btn0;
wait0 Btn0;
set1 Led0;
delay Delay;
set0 Led0;
goto FPPA0Loop;

FPPA1Loop:
wait1 Led0;
wait0 Led0;
set1 Led1;
delay Delay;
set0 Led1;
goto FPPA1Loop;

FPPA2Loop:
wait1 Led1;
wait0 Led1;
set1 Led2;
delay Delay;
set0 Led2;
goto FPPA2Loop;

FPPA3Loop:
wait1 Led2;
wait0 Led2;
set1 Led3;
delay Delay;
set0 Led3;
goto FPPA3Loop;

FPPA4Loop:
wait1 Led3;
wait0 Led3;
set1 Led4;
delay Delay;
set0 Led4;
goto FPPA4Loop;

FPPA5Loop:
wait1 Led4;
wait0 Led4;
set1 Led5;
delay Delay;
set0 Led5;
goto FPPA5Loop;

FPPA6Loop:
wait1 Led5;
wait0 Led5;
set1 Led6;
delay Delay;
set0 Led6;
goto FPPA6Loop;

FPPA7Loop:
wait1 Led6;
wait0 Led6;
set1 Led7;
delay Delay;
set0 Led7;
goto FPPA7Loop;

更高效處理 micro second 的方式

更高效處理 micro second 的方式…  以 STM32 為例… __IO unsigned long sys_tick = 0; void SysTick_Handler(void) {     HAL_IncTick();     sys_tick += (SysTi...