2013年12月25日 星期三

Beaglebone Black I/O library 開發歷程(二) - mmap


How to access register using mamp in TI AM335x CPU

Base on Beaglebone Black


C Library development note

Author : Cai , Meng-Lun , (VegetableAvenger) , 2013/12/19

Introduction :


這份筆記紀錄了BBBio library的開發過程,提供了一個基本的簡介來使用mmap函數,同時這份筆記也提供了一些流程,透過mmap來存取Device memory map,這份筆記提供了一個簡約的方法,在user space來存取I/O,以下過程均以C語言開發。

This note contain :

Ÿ   mmap basic introduction.
Ÿ   Memory mapped I/O
Ÿ   Memory map of ARM Cortex-A8 (AM335x)
Ÿ   Access memory map register via C

mmap basic introduction :


mmap 的精要解釋如下:

map files or devices into memory .

在檔案的R/W中,在read/writefunction中都存在一個緩衝區,當使用這些指令時,資料均需透過這個緩衝區才能實際的R/W到對應的檔案之中,而透過mmap可以免去這個過程,把檔案當作記憶體般的存取,加快了檔案存取的速度,以下部落格提供了圖文並茂的解釋,歡迎各位閱讀:

http://stenlyho.blogspot.tw/2008/08/zero-copy.html

 以上是其中一個用法,而這份筆記記載的是mmap種多用法之一,:Memory mapped I/O

 Memory mapped I/O :


I/Omemory共用記憶體空間,不用特別的指令來存取I/O,以記憶體讀寫的方式來進行I/O port的存取,在本文中使用的是ARM的核心,TI AM335X,便提供這種方式來進行I/O port的存取。

透過 memory mapped I/O的方式,許多外接元件或是module,都可以透過memory access的方式來進行存取,從C語言的角度來看,則是只需要用指標就可以存取這些記憶體,只要知道mapped的範圍,便可以存取裝置,在使用上非常直覺。
1Memory mapped I/O



Memory map of ARM Cortex-A8 (AM335x)



這篇筆記使用的是TI AM335XSoC,其spec可以從網路上載到:


 AM335x Technical Reference Manual


http://www.ti.com/product/am3359

 這是一篇長達4千多頁的PDF,介紹了這一整個SoC的概要,而memory map則列於page .168~176頁,在這裡截錄一段USB sub system memory map的表格:


2Memory map sample (USBSS)

圖2的格中清楚標明了USB sub systemmemory map範圍,而以Block為名是因為每一個裝置均由多個register控制,而這些register為一個群組,即為一群Block,以USBSS (0x4740_0000~0x4740_0FFF)為例,此Block內含的register列在page 2283session 16.5.10table 16-29,內含約42個左右的register來表明各種狀態(由命名大概可推測為interrupt相關的操作),如圖3列出其中一部份 :


 

3Block registers(USBSS)

而這些register才是真正用來存取device的關鍵,以SYSCONFIG register為例,其詳細列在page.2288session 16.5.4之中,如圖4所示,可以看出其資料布局的方式,同時也解釋這個Register的詳細作用,像是bit0用來resetUSB modulebit 8~11用來控制clock等。


4SYSCONFIG register

上述一個簡單的memory map查表,用來尋找USB subsystem對應的memory map位址,同時瞭解其對應位址下的register的意義,而詳細的操作方法,則需要參閱相對應的章節,才可以正確的操作device

Access memory map register via C


在此章節將介紹如何透過C來進行memory map的方法,同時將上述所提到的SYSCONFIG register的資料read出來,詳細的code放在github上:


程式碼主要分為2個部份:
  1. 使用mmap 映射記憶體
  2. 存取register

1.      使用mmap映射記憶體

首先需要先定義要映射的記憶體空間與映射長度,以SUBSS為例,由圖2可知,其start address 0x4740_0000 (hex)end address0x4740_0FFF,故可知此blocklength0x1000,透過這些資訊,定義2define


#define USBSS_ADDR     0x47400000
#define USBSS_LEN         0x1000

 有了這些資訊後,接著必須使用mmap來進行記憶體的映射,程式碼如下:


int memh = 0;
volatile unsigned int *USBSS_p = NULL;
memh=open("/dev/mem", O_RDWR);
USBSS_p = mmap(0, USBSS_LEN, PROT_READ | PROT_WRITE, MAP_SHARED, memh, \
USBSS_ADDR); 

 透過open的方式,來開啟/dev/mem這個檔案,關於此檔案,我找到一段不錯的解釋:

Mr. Shickadance"mem is a character device file that is an image of the main memory of the computer. It may be used, for example, to examine (and even patch) the system. Byte addresses in mem are interpreted as physical memory addresses."

接著再透過mmap函數來進行memory mapped,將其mapR/W模式,同時使用MAP_SHARED,而非MAP_PRIVATE,因為存取的是device,是公用的裝置,若是同時有2個程式mmap,使用MAP_PRIVATE沒辦法得知對方的存取動作;剛剛定義的USBSS_LENUSBSS_ADDR則是做為map lengthoffset

注意在這裡使用了volatile修飾詞,因為存取的是實際上的裝置,為了避免在暫存器上產生的誤動作,因此使用此修飾詞,讓每次存取時都到記憶體中拿資料,而不要透過暫存器。

 2.      存取register

接著再透過指標存取的方式,到SYSCONFIG這個registerread資料:



volatile unsigned int *reg = NULL ;

reg=(void *)USBSS_p + SYSCONFIG;

printf("USBSS - SYSCONFIG register : %X (hex)\n", *reg);

在筆者平台顯示的值為0x14,在根據page.2288session 16.5.4的描述可以得知此值代表:打開usb0~1clockno-idleno-standby的模式。

 由這2個簡單的步驟,即可完成一個device memory map的讀取。



2 則留言:

  1. 挑個小筆誤:標題應該是 Beaglebone,不是 Beagleboen

    回覆刪除