C6455 CSL_EMIF詳解
原網址http://www.61ic.com/Article/C6000/C64X/201303/47507.html C6455CSL詳解
和DSP6455的EMIFA模塊http://blog.csdn.net/ruby97/article/details/7539151
將兩篇文章整理一下,一起來看就清楚多了。
看這篇文章之前,還以為C6000 CSL是可以給全系列的C6000DSP使用呢。下次文檔一定要找對,代碼和文檔對應不上的時候一定要看看是不是文檔找錯了。
ABSTRACT
開發C6455的應用程序,一定會使用TI 提供的CSL(Chip Support Library),CSL 提供的API已經將底層的實現細節給我包裝好了,我們只需要直接調用這些API就可以了。但是為了更好的使用這些API,我們很有必要仔細的分析這些API函數是怎樣實現的。
這篇文章中我以EMIFA接口為例子,分析CSL 的相應API函數和結構體定義。
在CCS中添加CSL的頭文件和庫文件
CCS(Code Composer Studio)是TI公司開發的用於其公司的DSP,MSP等芯片的綜合開發工具,CCS5.x的版本是基於Eclipse開發的,而CCS4.x以及其前面的版本的開發平台我不太清楚。
我使用的是CCS5.2,在CCS5.x中,CSL的庫並不是默認被包含的該CCS中的,在http://www.ti.com/tool/sprc234中可以下載到最新的CSL庫,下載下來的CSL庫如下,里面包含了這個庫實現的源代碼和我們需要的庫文件和頭文件,以及一個PDF格式的API Reference。
從上面的圖我們可以看到,這個版本的CSL已經把INTC(中斷管理)做出一個單獨的模塊了,而其他的外設放在了另外一個模塊中。
使用的時候,我們必須在工程中加入頭文件我庫文件所在的路徑以及庫文件的名字,這樣在編譯器編譯和鏈接的時候才能找到它們,不然會提示找不到的錯誤。
EMIF
C6455的EMIF模塊
EMIF 是 External Memory Interface 的簡稱。個人認為它是 DSP 比較強大
的地方之一。通過 EMIF 接口,使得 DSP 可以和 FPGA 很方便地進行大數據量的數據傳輸。
C6455 的 EMIFA 可以訪問多種外部存儲器,比如:SRAM,ROM,FLASH 等等。當然,也包括 FPGA。本文的重點就是介紹使用 EMIFA 接口與 FPGA 建立無縫連接。
這是官方文檔給出的 EMIFA 模塊的接口示意圖,乍一看,復雜的很。好多
引腳而且還有好多復用。沒關系,我們再貼一張,你就會感覺輕松很多了。 這一張圖首先是把 EMIFA 模塊的接口分了類,然和呢,我把在與 FPGA 通信場合下所需要使用的管腳使用紅色框框標注了出來。是不是少了很多呢。歸納一下標注的管腳,如下:
AED[63:0] 64 位數據總線
AEA[19:0] 20 位地址總線(Optional)
ACE2 片選信號(低有效)
AECLKOUT 時鍾信號
ASWE 寫使能(低有效)
ASRE 讀使能(低有效)
(注:應用場合是 DSP 讀 FPGA 內部 RAM 中的圖像數據,其他場合續根據情況調整)
由於 FPGA 的可編程性,使得一切從 DSP 看來簡單了許多。因為 DSP 面對的"存儲器"顯得格外智能。甚至連地址線都可以不需要。
下面,我們來一一分析上述的信號。
首先,應該是片選信號 CE。這里不得不提到 DSP 的地址空間。下圖是
DSP6455 的 EMIFA 映射情況
從圖中可以看出
EMIFA 共支持 4 個外部存儲器,比如可以把 CE2 分配給 FPGA,CE3 分
配給 SRAM,CE4 分配給 FLASH 等。
每個外部存儲器的尋址空間大小是 8MB。20 根地址線即 2 的 20 次方,
也就是 1MB,此外由於數據總線是 64 位的,故對應的尋址空間是 8MB
由於FPGA內部時序邏輯可以產生地址,所以我們可以不使用地址線。這樣,下面的事情就簡單了。只要把 CE2 管腳和 FPGA 的某一個通用 IO 口連上即可。在讀取 FPGA 內部 RAM 數據時告訴 EDMA 要讀取的數據的基地址是
0xA0000000,以及讀取的數據的長度即可。
第二個信號,ECLKOUT,即時鍾信號的。因為 FPGA 工作是需要時鍾激勵的,沒有時鍾信號怎么產生地址邏輯呢?此外,時鍾頻率不能過高,要考慮到 FPGA 芯片的能力。OK,因為有了同步時鍾,所以 EMIFA 模塊的工作模式
也就確定了,即同步工作模式。
第三個信號,包括 2 個,即 ASRE,ASWE。更熟悉的叫法是 RE,WE。讀使能
和寫使能。這個就不贅述了。
第四個信號,數據總線&地址總線。也不贅述了。
經過上面的分析,我們可以簡要的畫出 FPGA 與 DSP 的連接圖:
配置EMIFA寄存器
在連接的思路清晰之后,我們可以開始配置 EMIFA 的寄存器了。
其實也就 1 個比較重要的寄存器,即 CEnCFG。該寄存器有兩套完全不同的配置。分別對應於同步存儲器模式和異步存儲器模式。由於 FPGA 內部 RAM 工作於同步模式,故我們來看一下同步模式下該寄存器的配置。
SSEL 設置為 1 時,表示該 CE 對應同步模式的外部存儲器。在與 FPGA 連接時,主要考慮以下四個參數:
R_ENABLE :設置 SRE/SADS 管腳的功能 為 1 時:SRE 為 0 時:SADS
W_LTNCY 寫延時(0,1,2,3)
00 :0 周期延時
01 :1 周期延時
02 :2 周期延時
03 :3 周期延時
R_LTNCY 讀延時(1,2,3)
(讀延時即:當 CE 和 RE 同時為低電平后,表示 DSP 開始讀 FPGA 的 RAM,經過 R_LTNCY 個 ECLKOUT 周期后第一個數據出現在數據總線上)
01 :1 周期延時
02 :2 周期延時
03 :3 周期延時
SBSIZE 數據位寬(8,16,32,64)
00 :8 位數據總線
01 :16 位數據總線
02 :32 位數據總線
03 :64 位數據總線
使用 CSL 配置 EMIFA 模塊時,主要的步驟如下:
-
1. 使能設備 EMIFA 模塊
-
2. 配置 CEnCFG 寄存器
-
3. 初始化 EMIFA 模塊
-
4. 打開 EMIFA 模塊
-
5. 把 2 中配置的參數設置到打開的 EMIFA 模塊中
完整配置代碼:(把 EMIFA 的 CE2 配置為以 FPGA 作為外部存儲器,64 位數據線,2 個周期的讀延時)
/*-----------------------------------------------------------------------------------
*
* 初始化EMIFA
*
-----------------------------------------------------------------------------------*/
#define EMIFA_MEMTYPE_ASYNC 0 //都是用宏定義
#define EMIFA_MEMTYPE_SYNC 1
#define EMIFA_CE2_BASE_ADDR (0xA0000000)//地址空間基地址
#define CSL_EMIFA_SYNCCFG_RLTNCY_PARAMETER 2//讀延時2周期
#define CSL_EMIFA_SYNCCFG_SBSIZE_PARAMETER 3//64位數據總線
#define CSL_EMIFA_SYNCCFG_READEN_PARAMETER 1//SRE
//CEnCFG寄存器參數宏
#define CSL_EMIFA_SYNCCFG_PARAMETER {\
(Uint8)CSL_EMIFA_SYNCCFG_READBYTEEN_DEFAULT, \
(Uint8)CSL_EMIFA_SYNCCFG_CHIPENEXT_DEFAULT, \
(Uint8)CSL_EMIFA_SYNCCFG_READEN_PARAMETER,\ (Uint8)CSL_EMIFA_SYNCCFG_WLTNCY_DEFAULT, \
(Uint8)CSL_EMIFA_SYNCCFG_RLTNCY_PARAMETER, \
(Uint8)CSL_EMIFA_SYNCCFG_SBSIZE_PARAMETER \
}
void Init_EMIF()
{
CSL_EmifaObj emifaObj;
CSL_Status status;
CSL_EmifaHwSetup hwSetup;
CSL_EmifaHandle hEmifa; //EMIF的句柄
CSL_EmifaMemType syncVal; //不同的類型對應相應的CE空間
CSL_EmifaSync syncMem =CSL_EMIFA_SYNCCFG_PARAMETER;
/* Clear local data structures */
memset(&emifaObj, 0, sizeof(CSL_EmifaObj));
memset(&hwSetup, 0, sizeof(CSL_EmifaHwSetup));
//步驟1: 使能設備的EMIFA功能(不用先解鎖外設寄存器)
CSL_FINST(((CSL_DevRegs*)CSL_DEV_REGS)->PERCFG1,
DEV_PERCFG1_EMIFACTL, ENABLE);
//步驟2:配置CE2CFG寄存器
syncVal.ssel = EMIFA_MEMTYPE_SYNC;
syncVal.async = NULL;
syncVal.sync = &syncMem;
hwSetup.ceCfg[0] = &syncVal; //CE2
hwSetup.ceCfg[1] = NULL;
hwSetup.ceCfg[2] = NULL;
hwSetup.ceCfg[3] = NULL;
//步驟3:初始化EMIFA模塊
status = CSL_emifaInit(NULL);
#ifdef SHOW_PRINTF
if (status != CSL_SOK)
{
printf("EMIFA: Initialization error.\n");
printf("\tReason: CSL_emifaInit [status = 0x%x].\n", status);
return;
}
else
{printf("EMIFA: Module Initialized.\n"); }
#endif
//步驟4:打開EMIFA模塊
hEmifa = CSL_emifaOpen(&emifaObj,CSL_EMIFA,NULL,&status);
#ifdef SHOW_PRINTF
if ((status != CSL_SOK) || (hEmifa == NULL))
{printf("EMIFA: Error opening the instance. [status = 0x%x, hEmifa \
= 0x%x]\n", status, hEmifa);
return;
}
Else
{printf("EMIFA: Module instance opened.\n"); }
#endif
//步驟5:把步驟2中配置的參數設置到打開的EMIFA模塊中
status = CSL_emifaHwSetup(hEmifa,&hwSetup);
#ifdef SHOW_PRINTF
if (status != CSL_SOK) {
printf("EMIFA: Error in HW Setup.\n");
printf("Read write operation fails\n");
return;
}
else
{printf("EMIFA: Module Hardware setup is successful.\n"); }
#endif
}
EMIFA的CSL API
EMIFA的CSL API做得相當的規整,主要有以下的函數,
而這些函都是CSL中每一個外設都會實現的,它們做得事情的相似的。
上面的圖是從API文檔中截取下來的,其大概的描述了每一個函數所完成的事情。下面我以CSL中提供的example為例,說明一下其用法。
CSL中EMFIA的example
CSL中對每一個外設至少包含了一個example以幫助開發者快速的掌握其用法。
初始化並打開EMFIA
在Emifa_ReadWrite_example.c文件中有兩個函數,在main函數中只有一句代碼
CSL_FINST(((CSL_DevRegs*)CSL_DEV_REGS)->PERCFG1, DEV_PERCFG1_EMIFACTL, \
ENABLE);
要看懂這句話要一定的時間,但是我們知道其做的事情就是配置PERCFG1打開EMIFA外設(PERCFG1(Peripheral Configuration Register 1)The Peripheral Configuration Register (PERCFG1) is used to enable the EMIFA and DDR2 Memory Controller.對於一般的外設,我們需要先往PERLOCK中寫入一個特定的UNLOCK值后才能打開,但是對於DDR2和EMIFA是可以直接打開的,詳細的解釋可以看相應的文檔)。打開EMIFA后,main函數直接調用emifaReadWrite()函數了。
在emifaReadWrite()中,
第一個被調用的函數就是CSL_emifaInit(NULL),從上面的解釋中可以看出,這個函數是optional的,查看了其源代碼以后,會發現其什么也沒有做,只是單純的返回了一個CSL_OK。
第二個被調用的函數是
hEmifa = CSL_emifaOpen(&emifaObj, CSL_EMIFA, NULL, &status);
其函數原型為
CSL_EmifaHandle CSL_emifaOpen (
CSL_EmifaObj *hEmifaObj,
CSL_InstNum emifaNum,
CSL_EmifaParam *pEmifaParam,
CSL_Status *status
);
這個函數必須詳細得看一看,其第一個參數是CSL_EmifaObj*類型的,這個類型里面其實有兩個域
typedef struct CSL_EmifaObj {
/* This is a pointer to the registers of the instance of EMIFA
* referred to by this object
*/
CSL_EmifaRegsOvly regs;
/** This is the instance of EMIFA being referred to by this object */
CSL_InstNum perNum;
} CSL_EmifaObj;
CSL_EmifaRegsOvly中包含EMIF這個模塊的所有寄存器,CSL_InstNum表示這是第幾個EMIF模塊(C6455中只有一個EMIF模塊,所以這個值只能是0)。
CSL_EMIFA是一個宏,其真實的值為
/** @brief Peripheral Instance for EMIFA */
#define CSL_EMIFA (0)
也就是表示只有一個EMIF模塊。
第三個參數是留着以后用的
typedef struct {
/** Bit mask to be used for module specific parameters. The below
* declaration is just a place-holder for future implementation. Passed as
* an argument to CSL_emifaOpen().
*/
CSL_BitMask16 flags;
} CSL_EmifaParam;
所以總是傳入NULL。
第四個參數是CSL_Status*,這個參數實際上是同時作為輸入和輸出參數的。
這個函數在時間運行時,首先檢查status是不是NULL,如果是NULL的話,它就返回一個NULL給hEmifa ,所以我們一定要保證傳入的值不是NULL,如果我們總是用CSL_emifaInit(NULL)返回的值傳入的話,這個條件就總是滿足了,所以我們先調用CSL_emifaInit(NULL)函數,然后再調用CSL_emifaOpen。
該函數返回值為一個handler,其實際類型是一個指針
/** @brief This is a pointer to @a CSL_EmifaObj and is passed as the first
* parameter to all EMIFA CSL APIs
*/
typedef struct CSL_EmifaObj *CSL_EmifaHandle;
如果不太能夠理解的話也沒有關系,其他的API函數都會以這個handler作為其第一個參數。
配置EMIFA的硬件
成功的初始化並打開硬件后,就需要安裝自己的要求配置硬件了
status = CSL_emifaHwSetup(hEmifa, &hwSetup);
主要開看二個參數,hwSetup是CSL_emifaHwSetup的一個變量,這兒要特別注意,這個函數的名字叫CSL_emifaHwSetup,而其參數的類型也是CSL_emifaHwSetup的指針,CSL_emifaHwSetup的定義如下
typedefstruct {
/** Pointer to structure for configuring the Asynchronous Wait Cycle
* Configuration register
*/
CSL_EmifaAsyncWait *asyncWait;
/** Array of CSL_EmifaMemType* for configuring the Chip enables
* as Async or Sync memory type.
*/
CSL_EmifaMemType *ceCfg[NUMCHIPENABLE];
} CSL_EmifaHwSetup;
要注意的是,不同的外設,他們的HwSetup類型的定義是不一樣的,因為這個結構體是與硬件(Hw->Hardward) 相關的,其實在調用這個函數之前,hwSetup各個域已經被設置為我們想要的值了,相應的設置代碼我就不copy下來了。