Skip to content

Instantly share code, notes, and snippets.

@Ttl
Created August 1, 2024 12:53
Show Gist options
  • Save Ttl/57fc505eaa0f4afe7a17fd3739dd85b5 to your computer and use it in GitHub Desktop.
Save Ttl/57fc505eaa0f4afe7a17fd3739dd85b5 to your computer and use it in GitHub Desktop.
#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