eLua體系結構
下圖總體概述了eLua的體系結構python
![](http://static.javashuo.com/static/loading.gif)
eLua使用平臺的概念表示一組具備相同內核結構的CPU,儘管這些CPU針對不一樣的具體外設,內存和其餘一些屬性分別有特定的實現。一個eLua實現給定平臺的一個或多個CPU。好比,eLua的lm3s移植能夠運行LMSS8962,LM3S6965,LM3S6918處理器,它們都是lm3s平臺下的一部分。從上圖能夠看出,eLua試着儘量在不一樣平臺之間使用一些簡單的設計規則。shell
- 全部代碼跟具體平臺無關的是通用代碼,並且它應該儘量使用標準C語言書寫。這樣就可使它在不一樣的體系結構和編譯器間有高度的可移植性,就像Lua自己同樣。
- 全部那些不可能通用的代碼(幾乎都是外設和具體CPU相關)應該儘量的使之可移植,經過使用通用接口,這種通用接口要能讓eLua能在全部的平臺上都能運行。這種接口稱爲平臺接口。
- 全部平臺(和它們的外設)不能同等的建立,由於在功能上有巨大的變化。如前所述,平臺接口試着組合不一樣平臺的共同特性。若是想在一個給定平臺上實現具體的一個功能的話,能夠經過使用一個平臺模塊。這些平臺模塊是數據特定平臺的,它們的目的就是填補平臺接口和一個具體平臺所提供的全部具體特性之間的缺口。
通用代碼
下面給出了不完整的一些項能夠被歸類於通用代碼。編程
- Lua代碼(很明顯是)加上LTR補丁
- 全部在eLua的組件(好比ROM文件系統,XMODEM接收代碼,eLua shell,TCP/IP棧等等)
- 全部通用模塊,這些模塊是lua的模塊,展現了Lua平臺的不一樣功能。
- 通用外設支持代碼,好比ADC支持代碼(src/common/elua_adc.c),此代碼獨立於真實的ADC硬件。
- libc代碼(好比allocators和Newlib)
上述的描述應該會給你一個好的關於通用代碼的印象。注意通用代碼所佔據的層應該儘量的多,也就是說,應該儘量的有更多的通用代碼。好比以下例子:網絡
- 若是你想給eLua添加一個新的文件系統,這很清楚應該是通用代碼。這就好像代碼產生的文件系統與具體依賴相關的物理媒介的處理。若是你幸運的話,你可使用在平臺接口的一些函數解決這種依賴關係。好比經過SPI控制SD卡,由於這個平臺擁有SPI層。若是不是那樣的話,你必須在一個獨立的接口中組合與特定的平臺相關的函數,而後實現它讓其在全部平臺上都能使用這種文件系統。這給了代碼最大的移植性。
- 若是你想爲特定的ADC外設(此ADC經過SPI工做)添加驅動,那麼可使用如前所述的一樣理論,盡你可能的把它寫成通用代碼,而且使用平臺接口中的函數實現你須要的特定的SPI。
當設計和實現一個新的組件時,記住另一個eLua的設計目標:靈活性。用戶應該能夠選擇在二進制文件裏面包含哪些組件,實現的時候就要把這些考慮進去。一樣的理論也適用於通用模塊:用戶能根據須要選擇相應的模塊。
爲了移植性的最大化,儘量讓你的代碼工做在各類可能遇到的情景中。這樣的例子就如在stdio/stdout/stderr中代碼的處理。
代碼(src/Newlib/genstd.c),此中代碼就認可了這樣一個事實,終端能夠經過能夠多種我物理傳輸接口實現(RS232,SPI控制的液晶/鍵盤等等)。因此代碼使用指針來處理收和發的函數。這樣在速度和資源的消耗上是最小的,可是它關係到重要的移植部分。
平臺接口
運用得當的話,平臺接口能容許在不一樣的平臺上書寫具備可移植性的代碼,包括C和Lua.平臺接口的一個重要特性就是它試着組合不一樣平臺下共同特性。好比,一個eLua所支持的平臺的串口能工做在迴路模式,可是其它平臺不能,那麼關於迴路模式的支持就不能被包括進平臺接口裏面。
關於平臺接口使用的特別的一點是:記住它不但能夠用C,還能夠用Lua.平臺接口主要經過通用模塊容許Lua控制平臺的外設,但這不是它惟一的用法。若是可能的話也能夠用C語言實現一個通用模塊並且也能用C控制外設。這個例子就是上面所述的打造一個新的文件系統。
平臺接口的定義老是在inc/platform.h頭文件裏。
平臺和移植
全部運行eLua的平臺(和i平臺接口的實現)都是在它的概念層上實現的。移植就是eLua能在一個給定的平臺下運行。
移植應當包括特定的外設驅動,不少時候這都是來自CPU所在平臺的支持包。這些驅動用於實現平臺接口。注意下面幾點:
- 移植不要實現平臺下的全部功能,只是它須要的就夠了。也就是說,經過所構建的eLua二進制文件用戶可以取得相應的須要控制的部分。若是你不須要SPI模塊,你就不須要實現關於它的平臺接口。
- 平臺接口中的一部分能夠在一個文件中實現,讓其夠工做在全部平臺上(src/common.c),這簡化了一些模塊的設計(好比定時器模塊),這樣也實現了所綁定平臺的通用特性,但在全部平臺上也有共同的表現(好比虛擬定時器)。若是你正在寫特定平臺下代碼的話,儘量不要去修改它,可是最好記住它是幹什麼的。
一個平臺的也許包括一個或多個獨立平臺的模塊。正如已經解釋的那樣,這樣作的目的是讓Lua能發揮全部平臺相關外設的潛能,而不僅是被平臺接口覆蓋到的外設功能。固然外設具備特定的功能時也不會被歸類到平臺接口。按照慣例,全部平臺的獨立模塊應該被組合進一個單獨的模塊,此模塊的名字與它自己的平臺是同樣的。若是一個平臺的獨立模塊增長的功能是平臺接口所擁有的,它應該也是相同的名字。不然,應該給它一個不同的名字。好比:
- 若是在LM3S平臺實現了一個新的功能關於UART模塊,那麼響應的模塊應該被稱做lms3s.uart。
- 若是實現了一個特定外設驅動在LPC2888平臺下,好比雙通道DAC,給它一個特別的名字,好比lpc288x.audiodac.
移植相關結構
全部關於平臺名字的代碼(包括外設驅動)必須集中在一個叫作src/platform/<name>的文件裏(好比src/platform/lm3s就是針對lm3s平臺的)。每個這樣的特定於平臺的子目錄必須包含至少這些文件:
type.h:其中定義的特定的數據類型。下圖是關於i386平臺下一些數據類型。
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned long u32;
typedef signed long s32;
typedef unsigned long long u64;
typedef signed long long s64;
conf.py:這是特定平臺的構建文件。構建系統會使用此文件,此文件有如下目的:
得到特定平臺下將被編譯成eLua二進制文件的文件列表。它們經過specific_files字符串
加載,空格隔開。並且必須給出相關文件的路徑。下面的例子是關於i386平臺的:
specific_files = "boot.s common.c descriptor_tables.c gdt.s interrupt.s isr.c kb.c monitor.c timer.c platform.c"
# Prepend with path
specific_files = " ".join( [ "src/platform/%s/%s" % ( platform, f ) for f in specific_files.split() ] )
爲了得到不一樣工具鏈單元的全部須要的命令來編譯eLua。它們須要被聲明在tools變量裏。每一個獨立的字典裏的關鍵字是相應平臺的名字,linkcom代碼linker,ascom表明assember,好比下面就是關於i386平臺工具變量的設置:
# Toolset data
tools[ 'i386' ] = {}
tools[ 'i386' ][ 'cccom' ] = "%s %s %s -march=i386 -mfpmath=387 -m32 -ffunction-sections -fdata-sections -fno-builtin -fno-stack-protector %s -Wall -c $SOURCE -o $TARGET" % ( toolset[ 'compile' ], opt, local_include, cdefs )
tools[ 'i386' ][ 'linkcom' ] = "%s -nostartfiles -nostdlib -march=i386 -mfpmath=387 -m32 -T %s -Wl,--gc-sections -Wl,-e,start -Wl,--allow-multiple-definition -o $TARGET $SOURCES -lc -lgcc -lm %s" % ( toolset[ 'compile' ], ldscript, local_libs )
注意到tools的定義使用到了toolset的定義,toolset是一個包含當前所使用工具名的字典。這也是eLua構建系統的一部分。
爲了實現一個能夠接收eLua可執行文件的函數(構建步驟的結果),而且產生一個在相應平臺上合適的編程文件,這個函數的名字應該被設置進tools字典中,以下所示(關於str7平臺的例子代碼):
# Programming function for STR7
def progfunc_str7( target, source, env ):
outname = output + ".elf"
os.system( "%s %s" % ( toolset[ 'size' ], outname ) )
print "Generating binary image..."
os.system( "%s -O binary %s %s.bin" % ( toolset[ 'bin' ], outname, output ) )
tools[ 'str7' ][ 'progfunc' ] = progfunc_str7
再一次提醒這個函數使用相同的toolset變量跟上面的同樣。
stacks.h:按照慣例,系統所使用的棧空間被聲明在這裏。下面給出的例子是來自at91sam7x平臺下的一段代碼:
#define STACK_SIZE_USR 2048
#define STACK_SIZE_IRQ 64
#define STACK_SIZE_TOTAL ( STACK_SIZE_USR + STACK_SIZE_IRQ )
platform.c:按照慣例,平臺接口的實現就在這個文件裏。它也包含一些與平臺無關的初始化函數。
platform_conf.h:這是關於平臺配置的頭文件,其中給出了平臺自己和構建相關平臺的配置信息。從這個文件中,你將看到下列內容:
- 將要被構建的組件列表
- 將要被構建的模塊列表
- 靜態配置數據
- CPU的各類外設的數目,下面的例子代碼展現了來自同一平臺(lm3s)下不一樣CPU的外設狀況。宏FORXXX被定義在conf.py文件中
// Number of resources (0 if not available/not implemented)
#define NUM_PIO 7
#define NUM_SPI 1
#ifdef FORLM3S6965
#define NUM_UART 3
#else
#define NUM_UART 2
#endif
#define NUM_TIMER 4
#ifndef FORLM3S6918
#define NUM_PWM 6
#else
#define NUM_PWM 0
#endif
#define NUM_ADC 4
- 特定的外設配置:包括UART使能緩衝,使能和開啓虛擬定時器,設置PIO配置等等。
- 內存配置:描述了內存分佈區域。此區域將被標準系統分配。兩個宏MEM_START_ADDRESS 和MEM_END_ADDRESS定義了空閒RAM的起始地址和結束地址。若是你的板子有外部RAM空間,你也應該在這裏定義。若是沒有的話,你將只能使用內部內存,而且通常你須要使用連接定義標誌end找出你空閒內存從哪裏開始,下面的例子來自ATEVK1100(AVR32)開發板,它擁有內部和外部RAM
// Allocator data: define your free memory zones here in two arrays
// (start address and end address)
#define MEM_START_ADDRESS { ( void* )end, ( void* )SDRAM }
#define MEM_END_ADDRESS { ( void* )( 0x10000 - STACK_SIZE_TOTAL - 1 ), ( void* )( SDRAM + SDRAM_SIZE - 1 ) }
- 網絡配置:若是須要TCP/IP在你的開發板上,你須要向eLua添加網絡支持。你也須要另外一個叫作uip_conf.h文件用來配置uip以用於你特定的處理器架構。
除了上述那些被要求的文件外,常見的狀況是在移植中須要添加跟特定平臺相關的文件:
- 啓動文件:通常用匯編書寫,作一些低檔次的初始化,設置堆棧指針,歸零BSS區域,將ROM中DATA區域移動到RAM中,而後跳轉到main函數。
- 連接命令文件
- CPU支持包:通常來自設備製造商,包括的代碼有控制外設,配置內核,開啓中斷等等。
水平有限,若有錯誤,給出指正。