前段時間項目遇到瓶頸,片內RAM空間不足,想要解決這個問題要麼換更大RAM的MCU,要麼就外擴PSRAM。可惜所用的K21最大就128Kb,沒有更大的了,所以只有選擇外擴。方案選擇主要有兩個問題需要考慮:1.PSRAM的數據位數;2.是否使用鎖存器。由於IO口資源有限,同時爲保證片外PSRAM的速度。最終的方案選擇:16位數據的PSRAM,地址數據複用,兩個鎖存器切換數據地址方案。由於在網上沒有找到先例,所以硬件接法有很多不確定。現在分享給罈子裏的小夥伴們,歡迎指教。
相信看這篇文章的或多或少也知道Flexbus和PSRAM是什麼了吧,所以在這兒就不囉嗦講Flexbus和PSRAM的基本概念。實在有不懂的小夥伴百度一下,你就知道!
硬件部分
下面仔細來看看硬件框圖吧,下面框圖主要是Flexbus與PSRAM的接法。
根據上面框圖做如下說明:
1. PSRAM的16位數據線接FB_AD[15:0],地址線接FB_AD[20:1]。其中地址線的FB_AD[16:1]與數據複用。採用雙鎖存器鎖存地址。可能有的小夥伴就會產生疑惑了,爲什麼Flexbus的FB_AD[16:1]接PSRAM的A[15:0]呢?稍後解答!
2. 總線的FB_ALE接鎖存器的鎖存信號LE,用過鎖存器的小夥伴都知道鎖存器還有一個片選信號(讓鎖存器工作的開關),這個信號就隨便接MCU的IO口即可。
3. Flexbus的FB_CE,FB_OE,FB_WE分別要與PSRAM的CE,OE,WE連接。
4. Flexbus的FB_BE31_24,FB_BE23_16分別與UB,LB連接,爲什麼呢?
5. PSRAM還有一個ZZ引腳,控制PSRAM是否休眠的,如果需要接MCU的IO口即可
因爲不同的MCU的Flexbus所映射的IO不一樣,所以就整理了一下用框圖來說明。有什麼疑問歡迎留言討論。下面解答剛剛提出的兩個爲什麼。
1. 爲什麼Flexbus的FB_AD[16:1]接PSRAM的A[15:0]?
關於這個問題,只要理解MCU處理器的尋址和PSRAM芯片的尋址就明白了。
目前最流行的32位嵌入式處理器ARM處理器,當然我所用的Cortex-M4處理器也是RAM家族的。從處理器的角度看,系統中每個地址對應的是一個BYTE的數據單元。這和很多別的處理器是一樣的。
對16位的PSRAM芯片或者工作在16-BIT模式的芯片來說,一個地址對應16-BIT的數據。說到這裏想必小夥伴們都懂了吧。也就不囉嗦了。
2. 爲什麼Flexbus的FB_BE31_24,FB_BE23_16分別與UB,LB連接?
其實這個沒有什麼好說的,看MCU flexbus的datasheet就能找到。在此順便提一下。
我們這裏是右對齊模式,從上圖可知,軟件CSCRn[BLS]應設爲1 ,PSRAM的UB需要接FB_BE31_24,LU需要接FB_BE23_16。
軟件部分
硬件就已經講述完畢,接下來是軟件部分。我這裏使用的MQX4.0操作系統,MCU爲K21。配置函數如下,不同的平臺和MCU程序的表現形式不一樣,配置的寄存器都是一樣的,這裏需要小夥伴們舉一反三喔。
#define GPIO_PIN_MASK 0x1Fu #define GPIO_PINX(x) (((1)<<(x & GPIO_PIN_MASK))) /*FUNCTION*--------------------------------------------------------------------- * Function Name : flexbus_setup * Returned Value : none * Comments : * Setup FlexBus pins before PSRAM operation *END*-------------------------------------------------------------------------*/ void flexbus_setup (void) { #define ALT5 0x05 #define OFF_CHIP_ACCESS_ALLOW 3 PORT_MemMapPtr pctl; SIM_MemMapPtr sim = SIM_BASE_PTR; /* Enable clock to FlexBus module */ sim->SCGC7 |= SIM_SCGC7_FLEXBUS_MASK; sim->CLKDIV1 |= SIM_CLKDIV1_OUTDIV3(0x0);//flexbus not divided #ifdef SIM_SOPT2_FBSL sim->SOPT2 |= SIM_SOPT2_FBSL(OFF_CHIP_ACCESS_ALLOW); #endif //把對應的接口配置成總線模式 pctl = (PORT_MemMapPtr)PORTB_BASE_PTR; pctl->PCR[9] = PORT_PCR_MUX(ALT5); /* FB_AD20 */ pctl->PCR[10] = PORT_PCR_MUX(ALT5); /* FB_AD19 */ pctl->PCR[11] = PORT_PCR_MUX(ALT5); /* FB_AD18 */ pctl->PCR[16] = PORT_PCR_MUX(ALT5); /* FB_AD17 */ pctl->PCR[17] = PORT_PCR_MUX(ALT5); /* FB_AD16 */ pctl->PCR[18] = PORT_PCR_MUX(ALT5); /* FB_AD15 */ pctl->PCR[19] = PORT_PCR_MUX(ALT5); /* FB_OE_B */ // pctl->PCR[20] = PORT_PCR_MUX(ALT5); /* FB_AD31 */ //pctl->PCR[21] = PORT_PCR_MUX(ALT5); /* FB_AD30 */ // pctl->PCR[22] = PORT_PCR_MUX(ALT5); /* FB_AD29 */ // pctl->PCR[23] = PORT_PCR_MUX(ALT5); /* FB_AD28 */ pctl = (PORT_MemMapPtr)PORTC_BASE_PTR; pctl->PCR[0] = PORT_PCR_MUX(ALT5); /* FB_AD14 */ pctl->PCR[1] = PORT_PCR_MUX(ALT5); /* FB_AD13 */ pctl->PCR[2] = PORT_PCR_MUX(ALT5); /* FB_AD12 */ pctl->PCR[4] = PORT_PCR_MUX(ALT5); /* FB_AD11 */ pctl->PCR[5] = PORT_PCR_MUX(ALT5); /* FB_AD10 */ pctl->PCR[6] = PORT_PCR_MUX(ALT5); /* FB_AD9 */ pctl->PCR[7] = PORT_PCR_MUX(ALT5); /* FB_AD8 */ pctl->PCR[8] = PORT_PCR_MUX(ALT5); /* FB_AD7 */ pctl->PCR[9] = PORT_PCR_MUX(ALT5); /* FB_AD6 */ pctl->PCR[10] = PORT_PCR_MUX(ALT5); /* FB_AD5 */ pctl->PCR[11] = PORT_PCR_MUX(ALT5); /* FB_RW_B */ pctl->PCR[16] = PORT_PCR_MUX(ALT5); /* FB_BE23_16 */ pctl->PCR[17] = PORT_PCR_MUX(ALT5); /* FB_BE31_24 */ pctl->PCR[3] = PORT_PCR_MUX(ALT5); /* FB_CLKOUT */ pctl = (PORT_MemMapPtr)PORTD_BASE_PTR; pctl->PCR[0] = PORT_PCR_MUX(ALT5); /* FB_ALE */ pctl->PCR[1] = PORT_PCR_MUX(ALT5); /* FB_CS0_b */ pctl->PCR[2] = PORT_PCR_MUX(ALT5); /* FB_AD4 */ pctl->PCR[3] = PORT_PCR_MUX(ALT5); /* FB_AD3 */ pctl->PCR[4] = PORT_PCR_MUX(ALT5); /* FB_AD2 */ pctl->PCR[5] = PORT_PCR_MUX(ALT5); /* FB_AD1 */ pctl->PCR[6] = PORT_PCR_MUX(ALT5); /* FB_AD0 */ //如果FB_AD口被其他功能佔用,接口僅做地址線,可用flexbus的專用地址線FB_Ax代替,只需硬件上連接軟件把對應IO配成總線模式即可 //pctl->PCR[10] = PORT_PCR_MUX(ALT5); /* FB_A18 */ //pctl->PCR[11] = PORT_PCR_MUX(ALT5); /* FB_A19 */ //pctl->PCR[12] = PORT_PCR_MUX(ALT5); /* FB_A20 */ //Set PIN for GPIO functionality PORTC_PCR12 = (0|PORT_PCR_MUX(1));//PSRAM_ZZ腳 PORTB_PCR6 = (0|PORT_PCR_MUX(1));//573 OE腳 // GPIOB_PDDR=GPIO_PDDR_PDD(GPIO_PINX(16)); //Change PINto outputs GPIOC_PDDR=GPIO_PDDR_PDD(GPIO_PINX(12)); GPIOB_PDDR=GPIO_PDDR_PDD(GPIO_PINX(6)); GPIOC_PDOR=GPIO_PDOR_PDO(GPIO_PINX(12)); } /*FUNCTION*--------------------------------------------------------------------- * * Function Name : _bsp_flexbus_psram_setup * Returned Value : * Comments : * Setup FlexBus for PSRAM operation * *END*-------------------------------------------------------------------------*/ void flexbus_psram_setup () { FB_MemMapPtr fb_ptr = FB_BASE_PTR; /* Enable external MRAM mapped on CS0 */ /* CS0 base address */ fb_ptr->CS[0].CSAR = 0x60000000; /* CS0 control (16bit data, 2 wait state) */ fb_ptr->CS[0].CSCR = FB_CSCR_BLS_MASK |//低16位 FB_CSCR_AA_MASK | FB_CSCR_WS(0X2) | FB_CSCR_ASET(0X1) | FB_CSCR_PS(2) | FB_CSCR_WRAH(1) | FB_CSCR_RDAH(1) | FB_CSCR_BEM_MASK; /* CS0 address mask and enable */ fb_ptr->CS[0].CSMR = FB_CSMR_BAM(0x1F) | FB_CSMR_V_MASK;//2M fb_ptr->CSPMCR = FB_CSPMCR_GROUP3(2) | FB_CSPMCR_GROUP2(2); }
關於Flexbus每個寄存器的含義,詳參MCU的datasheet。
可靠性測試方案
外擴PSRAM一般用做內存使用,我們的應用程序的數據就是映射到片外的。所以PSRAM的可靠性必須要保證,必須是零容忍。不然就會出現異常死機的情況。這對於產品來說是絕對不允許的。所以雖然硬件軟件已經通了,但對PSRAM的壓力測試還是很有必要的。
方案:對PSRAM循環遍歷讀寫不同數據並判斷所寫數據是否正確。源碼如下:
void test_psram(void) { uint16 wdata16 = 0x5050; uint16 rdata16=0; uint32 n,i; int add; time++; wdata16 += time*16; for(n=0x0000;n<0x1ffff0;n+=2) { wdata16 += 1; add = n; *(vuint16*)(0x60000000+n) = wdata16; rdata16 = 0; rdata16 = (*(vuint16*)(0x60000000+n)); if((wdata16) != (rdata16)) { error++; printf("%d,%d,W=0x%4x,R=0x%4x,add=0x%4x\n",time,error,wdata16,rdata16,add); } } if(time%2 == 0) { printf("time = %d\n",time); } }
關於PSRAM的穩定性有幾點需要分享給大家。
1.關於PSRAM(IS66WVE1M16BLL)供電電壓,當初是採用3.3V供電,供應商也說3.3V供電沒問題。但是不能通過我們的壓力測試,遍歷一次會有一些錯誤出現。芯片手冊上是3.0V供電。
2. 壓力測試,剛上電第一次遍歷會有幾個錯誤,其實比例很小,萬分之一的樣子。正如前面所說,對PSRAM的要求是零錯誤。所以這樣的問題仍然會影響我們做出來的產品的質量,這個原因很難找,後面我們硬件上在鎖存器和PSRAM的片選處加了上拉電阻問題得以解決。可能是上電時片選狀態未知,導致PSRAM處於一個未知狀態,修改了PSRAM什麼配置吧,具體什麼原因我實在說不上來,在此分享給大家。如果有不同意見的,歡迎指教!
原創文章,歡迎評論,轉載標明出處,謝謝配合!