软核开发SG-Cyclic模式AxiDma Tx通道循环传输。将Tx通道与Rx通道修改成并发进行,在Tx通道循环传输过程中如果Rx通道接收到特定数据,则(使用复位函数XAxiDma_Reset()、将Tx控制寄存器RS位写为0、将Tx控制寄存器cyclic使能位写为0,三种方式中的一种)停止循环传输。但是在停止循环传输后无法再次进行循环传输,控制寄存器与状态寄存器截图如下:
_
红框:Tx Ctrl Reg 0x41e00000; Tx Stat Reg 0x41e00004;
蓝框:Rx Ctrl Reg 0x41e00030; Rx Stat Reg 0x41e00034.
_
1)复位函数停止循环传输(禁用Cyclic停止循环传输,双通道寄存器值相同)

2)RS位停止循环传输

cyclic传输会跳过CPU,由硬件自动进行传输,在循环传输过程中只有Tx、Rx通道的中断系统部分的代码会运行,所以我在Tx中断服务函数中加入重启DMA引擎,没有作用,无法再次进行循环传输。我不知道重启流程是否正确,这方面的参考资料太少。
1)DMA中断系统
//Tx中断回调函数
static void TxCallBack(XAxiDma_BdRing * TxRingPtr)
{
int BdCount;
XAxiDma_Bd *BdPtr;
//获取经过硬件处理的Bd
BdCount = XAxiDma_BdRingFromHw(TxRingPtr, XAXIDMA_ALL_BDS, &BdPtr);
if (!start_transfer_img) { //单帧和返回数据时,需要释放Bd环
u32 BdSts;
XAxiDma_Bd *BdCurPtr;
int Status;
int Index;
BdCurPtr = BdPtr;
//检查Bd环中每个Bd是否都传输完成
for (Index = 0; Index < BdCount; Index++) {
BdSts = XAxiDma_BdGetSts(BdCurPtr);
if ((BdSts & XAXIDMA_BD_STS_ALL_ERR_MASK) ||
(!(BdSts & XAXIDMA_BD_STS_COMPLETE_MASK))) {
Error = 1;
xil_printf("tx Bd error\r\n");
break;
}
BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(TxRingPtr, BdCurPtr);
}
//释放Bd环
Status = XAxiDma_BdRingFree(TxRingPtr, BdCount, BdPtr);
if (Status != XST_SUCCESS) {
Error = 1;
}
} else { //连续传输需要rx通道中断信号停止
if(stop_img == 0x0400){
// XAxiDma_Reset(&AxiDma);
// set_register_value(TX_CTRL_REG, 0, 0, 0); //停止Tx通道运行位
set_register_value(TX_CTRL_REG, 4, 4, 0); //禁用cyclic
}
else {
//刷新内存
Xil_DCacheFlush();
//写入帧序号,实时更新图像数据
write_img_num(TX_BUFFER_BASE);
set_img_para();
img_num += 1;
}
}
if(!Error){
TxDone += BdCount; //传输完成,中断计数变化
Tx_Intr_Flag = 1; //传输完成,中断信号变化
}
}
//mm2s通道中断服务函数
static void TxIntrHandler(void *Callback)
{
XAxiDma_BdRing *TxRingPtr = (XAxiDma_BdRing *) Callback;
u32 IrqStatus;
//获取中断状态
IrqStatus = XAxiDma_BdRingGetIrq(TxRingPtr);
//挂起中断
XAxiDma_BdRingAckIrq(TxRingPtr, IrqStatus);
if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
xil_printf("error tx intr\r\n");
return;
}
if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
XAxiDma_BdRingDumpRegs(TxRingPtr);
Error = 1;
xil_printf("irq intr error\r\n");
XAxiDma_Reset(&AxiDma);
while (!XAxiDma_ResetIsDone(&AxiDma));
return;
}
if ((IrqStatus & (XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK))) {
TxCallBack(TxRingPtr);
if (start_transfer_img && (!(Xil_In32(TX_CTRL_REG) & XAXIDMA_CR_RUNSTOP_MASK))) {
//重新设置dma
restart_dma();
usleep(1000);
}
}
Tx_Intr_Flag = 0;
}
//Rx中断回调函数
static void RxCallBack(XAxiDma_BdRing * RxRingPtr)
{
int BdCount;
XAxiDma_Bd *BdPtr;
Rx_Intr_Flag = 0;
BdCount = XAxiDma_BdRingFromHw(RxRingPtr, XAXIDMA_ALL_BDS, &BdPtr);
RxDone += BdCount;
Rx_Intr_Flag = 1;
}
//s2mm通道中断服务函数
static void RxIntrHandler(void *Callback)
{
XAxiDma_BdRing *RxRingPtr = (XAxiDma_BdRing *) Callback;
u32 IrqStatus = 0;
stop_img = 0;
IrqStatus = XAxiDma_BdRingGetIrq(RxRingPtr);
XAxiDma_BdRingAckIrq(RxRingPtr, IrqStatus);
if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
xil_printf("rx assert error, error stat %x\r\n", IrqStatus);
return ;
}
if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
XAxiDma_BdRingDumpRegs(RxRingPtr);
Error = 1;
xil_printf("rx intr error, code %x\r\n", (IrqStatus & XAXIDMA_IRQ_ERROR_MASK));
XAxiDma_Reset(&AxiDma);
while (!XAxiDma_ResetIsDone(&AxiDma));
return ;
}
if ((IrqStatus & (XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK))) {
RxCallBack(RxRingPtr);
stop_img = Xil_In32(RX_BUFFER_BASE + 4); //获取停止传输命令码
}
}
//设置dma中断系统
int SetupAxiDmaIntrSystem(INTC * IntcInstancePtr, XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId)
{
XAxiDma_BdRing *TxRingPtr = XAxiDma_GetTxRing(AxiDmaPtr);
XAxiDma_BdRing *RxRingPtr = XAxiDma_GetRxRing(AxiDmaPtr);
int Status;
//连接Tx中断源
Status = XIntc_Connect(IntcInstancePtr, TxIntrId, (XInterruptHandler) TxIntrHandler, TxRingPtr);
if (Status != XST_SUCCESS) {
xil_printf("Failed tx connect intc\r\n");
return XST_FAILURE;
}
//连接Rx中断源
Status = XIntc_Connect(IntcInstancePtr, RxIntrId, (XInterruptHandler) RxIntrHandler, RxRingPtr);
if (Status != XST_SUCCESS) {
xil_printf("Failed rx connect intc\r\n");
return XST_FAILURE;
}
//开启中断控制器
Status = XIntc_Start(IntcInstancePtr, XIN_REAL_MODE);
if (Status != XST_SUCCESS) {
xil_printf("Failed to start intc\r\n");
return XST_FAILURE;
}
XIntc_Disable(IntcInstancePtr, TxIntrId);
XIntc_Disable(IntcInstancePtr, RxIntrId);
XIntc_Enable(IntcInstancePtr, TxIntrId);
XIntc_Enable(IntcInstancePtr, RxIntrId);
//中断异常处理
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)INTC_HANDLER, (void *)IntcInstancePtr);
Xil_ExceptionEnable();
return XST_SUCCESS;
}
2)重启dma
void restart_dma()
{
XAxiDma_BdRing *TxRingPtr = XAxiDma_GetTxRing(&AxiDma);
XAxiDma_BdRing *RxRingPtr = XAxiDma_GetRxRing(&AxiDma);
//等待通道关闭
while((!(Xil_In32(TX_STAT_REG) & XAXIDMA_HALTED_MASK)) ||
(!(Xil_In32(RX_STAT_REG) & XAXIDMA_HALTED_MASK)));
XAxiDma_BdRingIntDisable(TxRingPtr, XAXIDMA_IRQ_ALL_MASK);
XAxiDma_BdRingIntDisable(RxRingPtr, XAXIDMA_IRQ_ALL_MASK);
//清除双向通道DMA、INTC核中断位
Xil_Out32(TX_CTRL_REG, Xil_In32(TX_CTRL_REG) & (~XAXIDMA_IRQ_ALL_MASK));
Xil_Out32(TX_STAT_REG, Xil_In32(TX_STAT_REG) | XAXIDMA_IRQ_ALL_MASK);
ClearIntrStatus(TX_MASK);
Xil_Out32(RX_CTRL_REG, Xil_In32(RX_CTRL_REG) & (~XAXIDMA_IRQ_ALL_MASK));
Xil_Out32(RX_STAT_REG, Xil_In32(RX_STAT_REG) | XAXIDMA_IRQ_ALL_MASK);
ClearIntrStatus(RX_MASK);
//使能dma工作模式
set_register_value(TX_CTRL_REG, 4, 4, 0);
set_register_value(RX_CTRL_REG, 4, 4, 1);
XAxiDma_BdRingIntEnable(TxRingPtr, XAXIDMA_IRQ_ALL_MASK);
XAxiDma_BdRingIntEnable(RxRingPtr, XAXIDMA_IRQ_ALL_MASK);
//设置双通道首尾描述符
Xil_Out32((UINTPTR)TX_CTRL_REG + XAXIDMA_CDESC_OFFSET, TX_RETURN_DATA_BUF);
Xil_Out32((UINTPTR)TX_CTRL_REG + XAXIDMA_TDESC_OFFSET, TX_RETURN_DATA_BUF);
Xil_Out32((UINTPTR)RX_CTRL_REG + XAXIDMA_CDESC_OFFSET, RX_BUFFER_BASE);
Xil_Out32((UINTPTR)RX_CTRL_REG + XAXIDMA_TDESC_OFFSET, RX_BUFFER_BASE);
//打开传输通道
set_register_value(TX_CTRL_REG, 0, 0, 1);
set_register_value(RX_CTRL_REG, 0, 0, 1);
}
我的问题是如何主动停止Tx循环传输后,再次进行循环传输?
另外我发现在Tx中断回调函数中加入内存刷新Xil_DCacheFlushRange()、内存无效化Xil_DCacheInvalidateRange()函数后带宽8.7GBps,但是会出现每帧图像会出现一个错误包(一帧图像6000个包),错误统一表现为丢失开头8字节数据,错误包(在一帧图像中)出现位置随机。回调函数中去掉内存无效化函数,并使用Xil_DCacheFlush()刷新内存则不会出现丢失字节的错误包,但是带宽会降低到4.8GBps。这个丢字节原因是因为内存不一致吗?