2013-04-27

Learn ARM Assembly in Raspberry Pi

拿到Raspberry Pi之後到處找一些有趣的教學想試試看,無意間找到一個利用Raspberry Pi來學習寫一個作業系統的課程,是用組合語言來實作,以前沒學過組語,趁這個機會學一點組語的皮毛。Baking Pi – Operating Systems Development

在開始之前要先在Mac上建立編譯組合語言的環境,參考這篇Setting Up an ARM EABI Toolchain on Mac OS X,接著到OS Template下載編譯教學課程檔案用的Script Template。

一開始的概念很簡單,利用Raspberry Pi上的GPIO控制器送訊號到LED燈,藉此控制燈的明暗,這個教學中要控制的是ACT燈號,屬於16th GPIO pin。

在template/source中新增一個main.s檔,檔案內容如下:

/* <#1> */
.section .init
.globl _start
_start:

/* <#2> */
ldr r0,=0x20200000

/* <#3> */
mov r1,#1
lsl r1,#18
str r1,[r0,#4]

/* <#4> */
mov r1,#1
lsl r1,#16
str r1,[r0,#40]

/* <#5> */
loop$:
b loop$


以下為程式碼的說明:
<#1>
檔案一開始要指定程式的進入點,在Template的設定中以.init為進入點,而_start為第一個執行的程式區段,利用.globl宣告為global function。

<#2>
用指令ldr將記憶體位址0x20200000寫入暫存器r0中,0x20200000是GPIO控制器的記憶體位址,透過改變這個位址的值送訊號給GPIO,所以目前暫存器r0中的值代表GPIO的位址。

<#3>
用指令mov設定暫存器r1的值為1,在這裡ldr與mov看起來像是類似功能的指令,但是mov只能設定8bit的值,而ldr能設定32bit的值,因此要用ldr才能指定記憶體位址,而處理小數字用mov就夠了。接著用指令lsl將r1的值,向左移位18 bits,也就是說從0b1變成0b1000000000000000000。
Why?

跟據SoC-Peripherals.pdf第六章,Raspberry Pi的GPIO控制器記憶體位址規劃如下:
0~24 bytes: Function Select
28~36 bytes: Pin Output Set
40~48 bytes: Pin Output Clear


前24 bytes設定為每4 bytes選擇10 pins,GPIO總共控制54 pins,所以會用到24 bytes。而4 bytes共有32 btis,因此每3 bits可設定一個pin的控制方式,排列如xx999888777666555444333222111000,假設這是第一組4 bytes,最高2 bits不用,接下來3 bits控制9th pin,再來是8th pin,一直到0th pin。第二組4 bytes的話,依序為19th pin、18th pin、17th pin、16th pin直到10th pin。而ACT燈號為16th pin,所以是第二組4 bytes的xx999888777666555444333222111000,由右向左數為19~21st bits,在此將16th pin這3 bits設定為001,代表要將16th pin當作output,即r1為0b1000000000000000000。

最後用指令 str把r1的值寫入[r0,#4]的位址中,前面提到r0為GPIO控制器的起點,[r0,#4]代表r0的記憶體位址加4 bytes的位址,也就是第二組4 bytes的起點,如此一來即可將GPIO16th pin設定為output。

<#4>
設定好16th pin為output後,接下來要點亮ACT燈,方法為利用Pin Output Clear給予訊號。(注意要讓pin通電是使用Pin Output Clear而不是Pin Output Set)

Pin Output Set/Clear的控制範圍為前4 bytes對應0~31st pins,1 bit對應1 pin,後4 bytes對應32~54th pins,後4 bytes只使用低位的22 bits,剩餘的不使用。將該位置的bit值設定為1即可指定對應位置的pin為Set/Clear。

因此將r1的值設定為1,用lrl將r1的值向左位移16 bits,最後用str將r1的值寫入r0加40 bytes的位址,即設定GPIO 16th pin為Output Clear。

<#5>
最後為了不讓程式結束跳離,使用空的無限迴圈,指定一段程式區間 loop$,再用指令b執行loop$這個沒有任何指令區間。

回到Template目錄,執行make,即可編譯,將編譯產生的kernel.img複製到SD卡上取代原本Linux的kernel.img。插入SD卡開機後,發現ACT燈號恆亮,代表成功!
Post a Comment