啟動 86Duino Zero 的 PCIe 功能

在 86Duino Zero 的右邊可以看到一排金手指,這排金手指使得 86Duino Zero 可以當作 PCIe Host/Target 使用,不過要當作 PCIe Host/Target 使用前必須要先對 86Duino Zero 做一些更動,這份文件將會分別說明如何改造 86Duino Zero 成為 PCIe Host 或是 PCIe Target。

注意:要把 86Duino Zero 改造成可以使用 PCIe 的功能需要焊上和解焊零件,有可能對 86Duino Zero 造成損害,導致無法正常使用,請讀者小心操作,如造成損壞本公司將不負保固責任

一、86Duino Zero PCIe 簡介

當我們俯視 86Duino Zero 時可以看到在右邊有一排的金手指,這排金手指的規格是 PCIe X1:

zero_size

這排 PCIe 金手指讓 86Duino Zero 可以作為 PCIe Host/Target 使用,由於 PCIe X1 相容於 PCIe X4, PCIe X8, PCIe X16,因此只要確定插槽是 PCIe 的規格,不管長短都可以使用:

zero_size

二、改造 86Duino Zero 成為 PCIe Host

當我們要把 86Duino Zero 作為 PCIe Host 使用時,必須改造 86Duino Zero,我們必須在 86Duino Zero 底板焊上一個電阻。在我們開始動手前,我們先來看 86Duino Zero 與 PCIe 相關的電路。

zero_size

在上圖中的藍框是 86Duino Zero 底板上的 PCIe 金手指,在虛線框框中的是 PCIe 相關的電路,86Duino Zero 出廠時虛線框框中的零件都是沒有焊上的。為了要讓 86Duino Zero 作為 PCIe Host 端時可以重啟外部的 PCIe Target 端,必須把 R27 的電阻焊上,讓 PCIRST- 連接到 HOSTRST 。而虛線框框中其他的沒被焊上的零件是要把 86Duino Zero 改造為 PCIe Target 時需要焊上的,這部份會在第三章說明。

知道怎麼改造以後,我們開始來動手改造吧!

zero_size

我們從電路圖上知道我們必須在 R27 的位置焊上一個 0 歐姆的電阻,由於 R27 在電池底下,要先把電池其中一個固定的腳位解焊,掀開電池後在 R27 焊上 0 歐姆的電阻,改裝完成應該和下圖一樣。

zero_size

這樣就成功的把 86Duino Zero 改造成 PCIe Host 了!

三、改造 86Duino Zero 成為 PCIe Target

當我們要把 86Duino Zero 作為 PCIe Target 使用時,必須改造 86Duino Zero,這裡我們把改造分成兩個部份,分別是 86Duino Module 和 86Duino Zero 底板,當我們俯視 86Duino Zero 時正中央有著 86Duino 標誌的金屬物是可以和 86Duino Zero 底板分開的,不曉得你是否因為好奇已經把 86Duino Module 和 86Duino Zero 底板分開過了呢?要把 86Duino Module 和 86Duino Zero 底板分開,只需要一手抓著 86Duino Module 一手抓著 86Duino Zero 底板,並且往相反方向出力即可。

zero_size

zero_size

在分離完 86Duino Module 和 86Duino Zero 底板後,我們還要把 86Duino Module 的散熱片移除,86Duino Module 的散熱片就是有著 86Duino 標誌的藍色金屬片,散熱片與 86Duino Module 利用卡榫固定,在移除時需要鬆開卡榫。

zero_size

以下我們將分別介紹 86Duino Module 和 86Duino Zero 底板要如何改造。

改造 86Duino Module

在動手之前我們先來看 86Duino Module 的電路圖。

zero_size

86Duino Module 電路圖上的 PCIE_MSEL 是控制 PCIe 模式的腳位,當 PCIE_MSEL 的電位是 HIGH 時為 Host,反之則為 Target,86Duino Zero 出廠時 R25 有焊上而 R26 是沒有沒有焊上的,因此原本是 Host 模式,當我們要從 Host 模式改成 Target 模式時,要把 R25 解焊並且焊上 R26。

看完電路圖知道該怎麼做以後,我們現在可以動手來改造 86Duino Module 了!

首先我們先來看原本的 86Duino Module,在下圖紅框中的電阻為 R25,我們要把 R25 解焊。

zero_size

解焊完 R25 後要在 R26 的位置焊上一個 0 歐姆的電阻,改造完成後應該和下圖一樣。

zero_size

改造 86Duino Zero 底板

改造好 86Duino Module 後我們要來改造 86Duino Zero 底板,一樣在開始動手前我們先來看 86Duino Zero 底板的電路圖。

zero_size

在上圖中的藍框是 86Duino Zero 底板上的 PCIe 金手指,在虛線框框中的是 PCIe 相關的電路,86Duino Zero 出廠時虛線框框中的零件都是沒有焊上的,因此當我們要切換成 Target 模式時,在虛線框框中的零件都必須焊上,一共有 6 個零件,分別是 Q6, Q7, R27, R187, R188, R204, Q6 和 Q7 是 3904 的電晶體,R27 是 0 歐姆的電阻,R187, R188, R204 是 4.7K 歐姆的電阻。

知道該做些什麼後我們來看原本的 86Duino Zero 底板,在下圖中用紅框標示的是需要焊上的零件的位置,在紅框的旁邊有該零件的編號。

zero_size

我們要在 Q6 和 Q7 焊上 3904 的電晶體,在 R187, R188, R204 焊上 4.7K 歐姆的電阻,由於 R27 在電池底下,要先把電池其中一個固定的腳位解焊,掀開電池後在 R27 焊上 0 歐姆的電阻,改裝完成應該和下圖一樣。

zero_size

這樣就成功的把 86Duino Zero 改造成 PCIe Target 了!

四、連接過程

在這個小節介紹連接 PCIe Host 端和 86Duino Zero 時的操作順序,依循這個操作順序可以減少錯誤的發生。

(1) 把 86Duino Zero 插在 PCIe Host 上的 PCIe 插槽上
由於不能確定 PCIe Host 端是否支援 PCIe 的熱插拔,所以在 PCIe Host 通電前先把 86Duino Zero 插好。

(2) 將 86Duino Zero 通電
86Duino Zero 無法使用 PCIe Host 端上 PCIe 插槽提供的電,因此需要額外連接電源。還有一個原因就是和第一步所敘述的一樣,在 PCIe Host 端辨識是否有 PCIe Target 端時 PCIe Target 必須是通電的狀態。

(3) 將 PCIe Host 端通電
前兩步做完後接著把 PCIe Host 端通電,開機後就可以看到 86Duino Zero 為 PCIe 上的一個裝置。

(4) 在 PCIe Host 端與 86Duino Zero 間傳遞資料
進入作業系統後,會發現作業系統多了一個 PCIe 的裝置,這個 PCIe 的裝置就是 86Duino Zero,該 PCIe 的暫存器和一般的 COM port 無異,因此可以使用一般操控 COM Port 的方法來進行資料的傳輸。

五、86Duino Zero 實作 PCIe Target 的方式

在這一章節會說明當 86Duino Zero 被改造成為 PCIe Target 後,在與 PCIe Host 端間傳遞資料時,內部的硬體是如何運作的,了解這個章節的說明,對於實作把 86Duino Zero 作為 PCIe Target 與 PCIe Host 端間的溝通相當有幫助。

當我們把 86Duino Module 改造成 PCIe Target 時,86Duino Module 會自動把內部 COM9 和 COM10 的 TX/RX 對接,使得 COM9 和 COM10 彼此可以互相傳輸資料。

zero_size

當 86Duino Zero 與 PCIe Host 端連接時,86Duino Zero COM10 的暫存器將可以被 PCIe Host 端存取,因此 PCIe Host 端可以自由的操控 COM10。

zero_size

在 86Duino Zero 與 PCIe Host 端接傳遞資料時,86Duino Zero 會操控 COM9,PCIe Host 端會操控 COM10,而 COM9 和 COM10 的 TX/RX 對接,因此可以想成 86Duino Zero 與 PCIe Host 端間是透過標準的 UART 傳遞資料。

六、以 86Duino IDE 撰寫 86Duino Zero PCIe Target 回聲(echo)程式

此章節將以 86Duino IDE 撰寫 86Duino Zero (PCIe Target) 的回聲程式範例,這個範例在接收到 PCIe Host 的傳遞的資料後,把該資料傳送到 86Duino IDE 的序列埠監視視窗並且把接收到的資料傳送回 PCIe Host。

char rt_val;

HardwareSerial Serial9(COM9, 6000000L, BYTESIZE8|NOPARITY|STOPBIT1, 0L, 500L);

void setup()
{
	PCIe_target_setting();

	Serial.begin(115200);
	Serial9.begin(6000000);
} 

void loop()
{
	if(Serial9.available() > 0)
	{
		rt_val = Serial9.read();
		Serial.print(rt_val);
		Serial9.print(rt_val);
	}
}

bool PCIe_target_setting(void)
{
	unsigned short uart_config;

	io_outpdw(CROSSBARBASE + 0x28, io_inpdw(CROSSBARBASE + 0x28) & 0xfffffcffL);    //在 CrossBar 裡開啟 COM9 和 COM10

	uart_config = sb_Read16(0x60) & 0xff80;    //0x60 為南橋中儲存 UART 位址的 offset
	io_outpdw(uart_config + 0x20, 0x00D703E0L);    //設定 COM9
	io_outpdw(uart_config + 0x24, 0x00700000L);    //設定 COM10
}

接著我們把上面這個範例程式細部分解來說明~

HardwareSerial Serial9(COM9, 6000000L, BYTESIZE8|NOPARITY|STOPBIT1, 0L, 500L);

創建一個名為 Serial9 的 HardwareSerial 物件實例,這裡使用的 HardwareSerial 就是我們平常使用的 Serial 的類別,Serial9 在初始化時被對應到 COM9 而使用方式和一般常見的 Serial、Serial1 相同,因此在接下來的程式我們都用 Serial9 來操控 COM9。

void setup()
{
	PCIe_target_setting();

	Serial.begin(115200);
	Serial9.begin(6000000);
} 

在 setup 的一開始先呼叫 PCIe_target_setting 函式,這個函式會設定 Vrotex86EX (86Duino Module 中的 CPU) 的暫存器讓 PCIe Target 正常運作,在最後我們會介紹 PCIe_target_setting 做了什麼,這邊我們先想成呼叫完 PCIe_target_setting 函式 PCIe Target 就可以正常運作就好了,接著呼叫 Serial.begin(115200) 和 Serial9.begin(6000000) 分別設定 Serial 的 baudrate 為 115200 Hz 和設定 Serial9 的 baudrate 為 6000000 Hz。

void loop()
{
	if(Serial9.available() > 0)
	{
		rt_val = Serial9.read();
		Serial.print(rt_val);
		Serial9.print(rt_val);
	}
}

這段程式碼相信有在寫 86Duino 程式的使用者會覺得很熟悉,在每次進入 loop 後先判斷 Serial9 有沒有可以接收的資料,如果有的話把資料讀出來並且輸出到序列埠監視視窗,輸出到序列埠監視視窗後使用 Serial9.print(rt_val) 把讀到的資料傳送回 PCIe Host, Serial9 的使用方法是不是和我們平常使用 Serial 一樣呢,很簡單吧!

bool PCIe_target_setting(void)
{
	unsigned short uart_config;

	io_outpdw(CROSSBARBASE + 0x28, io_inpdw(CROSSBARBASE + 0x28) & 0xfffffcffL);    //在 CROSSBAR 裡開啟 COM9 和 COM10

	uart_config = sb_Read16(0x60) & 0xff80;    //0x60 為南橋中儲存 UART 位址的 offset
	io_outpdw(uart_config + 0x20, 0x00D703E0L);    //設定 COM9
	io_outpdw(uart_config + 0x24, 0x00700000L);    //設定 COM10
}

這裡大家可能就會比較陌生了,如果你有看過 86Duino 函式庫的原始碼,也會看到很多類似這樣的程式碼,會使用到 io_out* 這樣的函式是為了設定 Vortex86EX 的暫存器,為了知道在這裡設定給暫存器的值有什麼效果,我們得要看 Vortex86EX 的規格書,所以先讓我們來看 Vortex86EX 規格書,與 UART 相關的暫存器說明在 239~255 頁 ; 與 PCIe Target 相關的暫存器說明在 490~502 頁。

知道暫存器如何設定以後我們可以回過頭來看 PCIe_target_setting 函式做了什麼,第 5 行在 CrossBar 裡把 COM9 和 COM10 開啟,第 7 行從南橋裡讀出 UART 暫存器的位置,第 8、9 行分別設定 COM9、COM10,至於設定的值代表什麼請讀者參考 Vortex86EX 的規格書。

注意:如果讀者要將上面的範例程式上傳到 86Duino Zero 可能會有問題,那是因為我們把 86Duino Zero 改造成 PCIe Target 的關係,解決辦法在 “附錄:注意事項” 第二點。

附錄:注意事項

(1) 由於一個已知的 bug , C 版本以前的 EX CPU (包含 C 版本),BIOS 讀取 86Duino Zero 作為 PCIe Target 時使用的 IRQ 編號會有問題,導致需要使用中斷的功能無法運作,接著以下會說明如何分辨 EX CPU 的版本:

在下圖中被紅框標記的是 EX CPU

zero_size

在 EX CPU 上的文字與下圖相似,其中由上往下數的第三行 “1339-CTF”,後面三個英文字中的第一個即為版本編號,以下圖為例後三個英文字為 “CTF”,第一個英文字為 “C” ,因此該 EX CPU 為 C 版本
zero_size

(2) 當我們把 86Duino Zero 改造成 PCIe Target 後,如果要像改造前一樣使用可能會有問題(例如:利用 IDE 燒錄程式到 86Duino Zero 執行),以下我們來看 86Duino Zero 與 PCIe Target 相關的電路圖,來解說為什麼使用上會有問題:

zero_size

從電路圖我們可以看到 86Duino Zero 金手指上的 A11(HOSTRST) 連接到 PCIRST- 和 RESET-,而 RESET- 連接至 86Duino Module 的 RESET 晶片,這個 RESET 晶片如果察覺 RESET- 是低電位則會重新啟動 86Duino Module,當 86Duino Zero 沒有插在 PCIe 插槽上時, A11 的電位是浮動的,如果被晶片判斷這時的電位為低電位,則 86Duino Module 會一直被重新啟動,因此無法正常使用,如果要像改造前那樣使用,可以從 86Duino Zero 的底板上連接 3.3V 到 A11,可以使 86Duino Module 不要一直重新啟動。

下圖為連接的範例,用一個電線連接 86Duino Zero 底板上的 3.3V 到 A11,A11 在 86Duino Zero 底板的背面,不要接錯了喔

zero_size