Created
August 1, 2024 12:53
-
-
Save Ttl/57fc505eaa0f4afe7a17fd3739dd85b5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "sdspi/emmcdrv.h" | |
#include "xparameters.h" | |
#include "xstatus.h" | |
#include "xpm_counter.h" | |
#include <stdio.h> | |
#include "xil_cache.h" | |
#include "pl_dma.h" | |
#define SDIO_BASE_ADDR XPAR_SDIO_TOP_0_BASEADDR | |
#define SDIO_DMA | |
#define TCOUNTS (64.0f / 666.666687e6f) | |
struct EMMCDRV_S *pl_emmc = 0; | |
static XAxiDma dma_device_emmc; | |
#define RESET_TIMEOUT_COUNTER 10000 | |
volatile int DmaRxDone_emmc; | |
volatile int DmaTxDone_emmc; | |
volatile int DmaError_emmc; | |
volatile int dma_rx_busy_emmc; | |
volatile int dma_tx_busy_emmc; | |
volatile int dma_rx_len_emmc; | |
volatile int dma_rx_trig_len_emmc; | |
volatile int emmc_rx_flag = 0; | |
volatile int emmc_tx_flag = 0; | |
volatile UINTPTR emmc_dma_rx_ptr = 0; | |
volatile int emmc_dma_rx_size = 0; | |
volatile int emmc_dma_rx_end = 0; | |
void pl_emmc_set_debug(int en) { | |
emmc_set_debug(en); | |
} | |
static void EmmcDmaRxIntrHandler(void *Callback) { | |
u32 IrqStatus; | |
int TimeOut; | |
XAxiDma *AxiDmaInst = (XAxiDma *)Callback; | |
/* Read pending interrupts */ | |
IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA); | |
/* Acknowledge pending interrupts */ | |
XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA); | |
/* | |
* If no interrupt is asserted, we do not do anything | |
*/ | |
if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) { | |
return; | |
} | |
dma_rx_busy_emmc = 0; | |
dma_rx_len_emmc = Xil_In32((AxiDmaInst->RegBase) + (0x58)); | |
/* | |
* If error interrupt is asserted, raise error flag, reset the | |
* hardware to recover from the error, and return with no further | |
* processing. | |
*/ | |
if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) { | |
DmaError_emmc = 1; | |
/* Reset could fail and hang | |
* NEED a way to handle this or do not call it?? | |
*/ | |
XAxiDma_Reset(AxiDmaInst); | |
TimeOut = RESET_TIMEOUT_COUNTER; | |
while (TimeOut) { | |
if (XAxiDma_ResetIsDone(AxiDmaInst)) { | |
break; | |
} | |
TimeOut -= 1; | |
} | |
return; | |
} | |
if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) { | |
emmc_dma_rx_size += dma_rx_len_emmc; | |
emmc_dma_rx_ptr += dma_rx_len_emmc; | |
if (emmc_dma_rx_size < emmc_dma_rx_end) { | |
dma_rx_busy_emmc = 1; | |
int status = XAxiDma_SimpleTransfer(&dma_device_emmc, emmc_dma_rx_ptr, emmc_dma_rx_end - emmc_dma_rx_size, XAXIDMA_DEVICE_TO_DMA); | |
if (status != XST_SUCCESS) { | |
printf("dma rx err: %d\r\n", status); | |
} | |
} else { | |
DmaRxDone_emmc = 1; | |
emmc_rx_flag = 1; | |
} | |
} | |
} | |
static void EmmcDmaTxIntrHandler(void *Callback) { | |
u32 IrqStatus; | |
int TimeOut; | |
XAxiDma *AxiDmaInst = (XAxiDma *)Callback; | |
/* Read pending interrupts */ | |
IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE); | |
/* Acknowledge pending interrupts */ | |
XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE); | |
/* | |
* If no interrupt is asserted, we do not do anything | |
*/ | |
if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) { | |
return; | |
} | |
dma_tx_busy_emmc = 0; | |
/* | |
* If error interrupt is asserted, raise error flag, reset the | |
* hardware to recover from the error, and return with no further | |
* processing. | |
*/ | |
if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) { | |
DmaError_emmc = 1; | |
/* | |
* Reset should never fail for transmit channel | |
*/ | |
XAxiDma_Reset(AxiDmaInst); | |
TimeOut = RESET_TIMEOUT_COUNTER; | |
while (TimeOut--) { | |
if (XAxiDma_ResetIsDone(AxiDmaInst)) { | |
break; | |
} | |
} | |
return; | |
} | |
if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) { | |
DmaTxDone_emmc = 1; | |
emmc_tx_flag = 1; | |
} | |
} | |
static int init_pl_emmc_dma(void) { | |
return init_pl_dma(2, &dma_device_emmc, (Xil_InterruptHandler)EmmcDmaTxIntrHandler, (Xil_InterruptHandler)EmmcDmaRxIntrHandler); | |
} | |
int emmc_dma_mm2s(UINTPTR ptr, u32 length) { | |
if (dma_tx_busy_emmc) { | |
printf("emmc_dma_mm2s already busy\r\n"); | |
return XST_FAILURE; | |
} | |
dma_tx_busy_emmc = 1; | |
DmaTxDone_emmc = 0; | |
Xil_DCacheFlushRange(ptr, length); | |
int status = XAxiDma_SimpleTransfer(&dma_device_emmc, ptr, length, XAXIDMA_DMA_TO_DEVICE); | |
if (status != XST_SUCCESS) { | |
dma_tx_busy_emmc = 0; | |
} | |
return status; | |
} | |
int emmc_dma_s2mm(UINTPTR ptr, u32 length) { | |
if (dma_rx_busy_emmc) { | |
printf("emmc_dma_s2mm already busy\r\n"); | |
return XST_FAILURE; | |
} | |
dma_rx_busy_emmc = 1; | |
DmaRxDone_emmc = 0; | |
Xil_DCacheFlushRange(ptr, length); | |
dma_rx_trig_len_emmc = length; | |
emmc_dma_rx_size = 0; | |
emmc_dma_rx_end = length; | |
emmc_dma_rx_ptr = ptr; | |
int status = XAxiDma_SimpleTransfer(&dma_device_emmc, ptr, length, XAXIDMA_DEVICE_TO_DMA); | |
if (status != XST_SUCCESS) { | |
dma_rx_busy_emmc = 0; | |
} | |
return status; | |
} | |
int pl_emmc_init(void) { | |
#ifdef SDIO_DMA | |
if (init_pl_emmc_dma() != XST_SUCCESS) { | |
return XST_FAILURE; | |
} | |
#endif | |
pl_emmc = emmc_init((EMMC*)SDIO_BASE_ADDR); | |
if (pl_emmc == 0) { | |
return XST_FAILURE; | |
} | |
//emmc_set_cache(pl_emmc, 1); | |
if (emmc_set_speed(pl_emmc, EMMC_BUS_HS200, 1) != 0) { | |
return XST_FAILURE; | |
} | |
return XST_SUCCESS; | |
} | |
int pl_emmc_write(u32 sector, u32 count, char *buf) { | |
if (!pl_emmc) { | |
return XST_FAILURE; | |
} | |
if (count == 0) { | |
return XST_SUCCESS; | |
} | |
int status; | |
status = emmc_write(pl_emmc, sector, count, buf); | |
#ifdef SDIO_DMA | |
if ((status = emmc_dma_mm2s((UINTPTR)buf, count * 512)) != XST_SUCCESS) { | |
printf("emmc_dma_mm2s failed: %d\r\n", status); | |
return status; | |
} | |
#endif | |
return status; | |
} | |
int pl_emmc_read(u32 sector, u32 count, char *buf) { | |
if (!pl_emmc) { | |
return XST_FAILURE; | |
} | |
if (count == 0) { | |
return XST_SUCCESS; | |
} | |
int status; | |
#ifdef SDIO_DMA | |
if ((status = emmc_dma_s2mm((UINTPTR)buf, count * 512)) != XST_SUCCESS) { | |
printf("emmc_dma_s2mm failed: %d\r\n", status); | |
return status; | |
} | |
#endif | |
status = emmc_read(pl_emmc, sector, count, buf); | |
return status; | |
} | |
int pl_emmc_benchmark(u32 start_sector, u32 sectors, float *tx_speed, float *rx_speed) { | |
u32 *buf = (u32*)DMA_BUFFER_BASE; | |
u32 i; | |
for (i = 0; i < sectors * 512 / sizeof(u32); i++) { | |
buf[i] = (i + i / 512) & 0xffffffff; | |
} | |
u32 write_start = Xpm_ReadCycleCounterVal(); | |
int status = pl_emmc_write(start_sector, sectors, (char*)buf); | |
if (status != XST_SUCCESS) { | |
return status; | |
} | |
#ifdef SDIO_DMA | |
while(!DmaTxDone_emmc); | |
#endif | |
while(emmc_busy(pl_emmc)); | |
DmaTxDone_emmc = 0; | |
u32 write_end = Xpm_ReadCycleCounterVal(); | |
float t_write = (write_end - write_start) * TCOUNTS; | |
float data_mbs = sectors * 512.0f / (1024.0f * 1024.0f); | |
*tx_speed = data_mbs / t_write; | |
printf("pl_emmc_write %d, %f MB/s\r\n", status, *tx_speed); | |
u32 *buf_rx = (u32*)(DMA_BUFFER_BASE + sectors * 512); | |
u32 read_start = Xpm_ReadCycleCounterVal(); | |
status = pl_emmc_read(start_sector, sectors, (char*)buf_rx); | |
if (status != XST_SUCCESS) { | |
return status; | |
} | |
#ifdef SDIO_DMA | |
while(!DmaRxDone_emmc); | |
#endif | |
DmaRxDone_emmc = 0; | |
while(emmc_busy(pl_emmc)); | |
u32 read_end = Xpm_ReadCycleCounterVal(); | |
float t_read = (read_end - read_start) * TCOUNTS; | |
*rx_speed = data_mbs / t_read; | |
printf("pl_emmc_read %d, %f MB/s\r\n", status, *rx_speed); | |
int ok = 1; | |
for (i = 0; i < sectors * 512 / sizeof(u32); i++) { | |
if (buf[i] != buf_rx[i] && ok) { | |
printf("\r\n%lu: %08lx != %08lx\r\n", i * sizeof(u32), buf[i], buf_rx[i]); | |
ok = 0; | |
} | |
} | |
if (!ok) { | |
printf("Verification failed at %lu\r\n", i); | |
} else { | |
printf("Verification ok\r\n"); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment