MKV30F128代码执行速度问题

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

MKV30F128代码执行速度问题

4,871 Views
yaoXin
Contributor II

      我在使用型号为MKV30F128的芯片做程序开发,在使用MCUxpresso Config工具做配置时,发现其Flash的时钟只有主频的1/4,即当主频为80MHZ时,Flash的时钟为20MHZ,那么当程序在Flash中执行时,指令执行速度是不是以20MHZ的频率执行的?

    外设Flash Memory Controller(FMC)的寄存器PFB0CR低5位默认为1,即开启了指令预取和数据预取功能,它对加速程序运行帮助大吗?

pastedImage_1.png

   另外我在使用UART的中断发送功能时,当UART的S1寄存器标志位TDRE(Transmit Data Register Empty Flag)为1时触发中断,发送中断的开启是在串口接收中断中启动的,UART的TX和RX中断优先级是最高,可以打断任意中断。做了这样的配置后,实际执行时发现打开发送中断使能1.8us后(使能时TDRE位为1),才进入到发送中断执行代码中,是因为这款芯片进入中断时间比较慢吗,还是配置有问题?中断服务程序如下所示:

void UART0_RX_TX_IRQHandler(void)
{
    //发送完成中断,必须要使用TX发送在前,因为RX接收中断完成后就马上执行开启发送中断
    if((UART_S1_TDRE_MASK & UART0->S1) && (UART0->C2 & UART_C2_TIE_MASK))
    {
        COMM_TransferDeal();
    }

    //接收寄存器满时,需要通过读取数据寄存器来清除故障
    //读取数据寄存器可以清除中断标志位
    if ((UART_S1_RDRF_MASK & UART0->S1) && ((UART_C2_RIE_MASK & UART0->C2))) //接收中断
    {
        Comm_ReceiveDeal();
    }

    __DSB();
}

0 Kudos
Reply
9 Replies

4,666 Views
jingpan
NXP TechSupport
NXP TechSupport

Hi,

我在frdmkv31f_uart_interrupt上测了你的程序。我的波特率是921600。

void UART0_RX_TX_IRQHandler(void)
{
    //发送中断标志
    if((UART_S1_TDRE_MASK & UART0->S1) && (UART0->C2 & UART_C2_TIE_MASK))
    {
        GPIO_PortClear(GPIOE,1<<24);
        UART0->D = data;
        UART_DisableInterrupts(UART0, kUART_TxDataRegEmptyInterruptEnable);

    }

    if ((UART_S1_RDRF_MASK & UART0->S1) && ((UART_C2_RIE_MASK & UART0->C2)))

    {
//        GPIO_PortSet(GPIOE,1<<24);

        data = UART0->D;
        UART_EnableTx(UART0, true);
        UART_EnableInterrupts(UART0, kUART_TxDataRegEmptyInterruptEnable);
        GPIO_PortSet(GPIOE,1<<24);
    }

__DSB();
}

图1是按照这个程序跑的结果,图二是把GPIOE24拉高这句提前到读数据之前。紫色线是RX,黄色线是TX,蓝色是GPIOE24脚。优化-O0。

NewFile1.png

NewFile0.png

从这两个图中可以看出,大约在RX的stop bit后一个UART clock时间,中断响应。执行过程大约需要2个clock。最后从数据写入write buffer到起始位发出需要一个clock。

下图是优化选项-O2。程序同图一。

新建文件1.png

可见,程序执行时间大部分省下来。去掉结束位后需要约2.6ms~3ms。这些时间部分是程序执行时间。部分是system clock到uart clock的同步时间。这是合理的。

Regards,

Jing

0 Kudos
Reply

4,666 Views
yaoXin
Contributor II

感谢回复!

    比较图1和图2,图1的stop bit开始到PE24的上升沿间隔为4.4us左右,图2中stop bit开始到PE24的上升沿时间为2.4us,和图1相比减小了2us时间,那么就是相当于如下几条语句在-o0优化时执行时间为2us。

        data = UART0->D;
        UART_EnableTx(UART0, true);
        UART_EnableInterrupts(UART0, kUART_TxDataRegEmptyInterruptEnable);

    观察这两个库函数内部操作,只有几条语句,那么这个2us执行时间就很长了,不知道测试的时候system clock是多少,如果是80M,那么相当于这些语句执行时间要160个system clock,时间就太长了,我之前使用ST的基于cortex-m4内核的芯片基本是一个system clock一条指令。

    如果图1和图3程序相同,只是优化等级不同的话,有如下结果:

    比较图1和图3,从PE24管脚即蓝色线的上升沿可以看出,图1的stop bit开始到PE24的上升沿间隔为4.4us左右,图3的stop bit开始到PE24上升沿间隔为2.4us左右,那么由优化等级带来节省下来的时间大约为2us。

0 Kudos
Reply

4,666 Views
jingpan
NXP TechSupport
NXP TechSupport

Hi,

由于flash本身结构的原因,最大只能跑到24M。但是flash控制器有cache,它可以缓存一些指令,具体请看flash的27章。另外flash的宽度是64bit。意味着一次可取多条指令。所以平均的执行速度并不会比在RAM里执行慢太多。

PFB0CR低5位对执行速度影响是明显的。关掉表示cache功能没有了。那就只能靠flash的宽度来提升速度了。但是顺序执行时没有了预取,循环跳转时没有了循环体缓存。

慢的原因得具体分析。UART的RX和TX是同一个中断。所以总得RX退出中断后重新进入。1.8us的时间在100M下大概能执行100多条指令。你可以用工具看看大概是些什么指令。从CORTEX-M4的结构来说是不会有什么特别的地方导致中断特别慢的。

Regards,

Jing

0 Kudos
Reply

4,666 Views
yaoXin
Contributor II

多谢回复!

    如果我保持外设FMC的寄存器全都为默认值,Flash内部执行速度会是最快的吗,如果还有可以提升运行速度的方式的话,应该去配置什么寄存器,能提供相关例程吗?

   UART的RX中断最后一条指令是开启发送中断,如下所示:并没有其它的指令了,测得的再次进入发送中断消耗的时间1.8us,是使用内部定时器测出来的,如果不是内核的问题导致中断进入慢,有没有可能是因为要执行Cache内部缓存的指令,执行完后再跳转到TX中断导致的?

   UART0->C2 |= 0x80;

0 Kudos
Reply

4,666 Views
yaoXin
Contributor II

时间准确测试时1.2us左右,是在RX中断服务函数的开启TX中断后,最后一条语句读定时器T1,然后在TX发送中断服务函数第一条语句读T2,然后计算T2-T1的值。定时器使用FTM0,定时器时钟为40MHZ

0 Kudos
Reply

4,666 Views
jingpan
NXP TechSupport
NXP TechSupport

Hi,

默认的flash设置已经是最佳设置,不需要修改。KV30有SWO 接口。你可以通过SWO来看看在这期间执行了哪些代码。这需要一个有SWO接口的仿真器,比如jlink,mulitlink,ulinkpro。在MCUxpresso上的操作方法请见安装目录下的MCUXpresso_IDE_SWO_Trace.pdf。

Regards,

Jing

0 Kudos
Reply

4,666 Views
yaoXin
Contributor II

      如果使用默认配置就已经时最优的话,还是有疑问,我使用McuXpresso编译程序,预编译宏定义如下所示,已经开启了三级优化,芯片的SDK的版本为2.60,当调用执行下面的库函数时,执行完成消耗的时间居然达到了7us,芯片主频为80MHZ,如果屏蔽库函数内部的assert函数,去掉内部的除法运算等,执行时间能够降低到4.5us。为了能够继续降低代码执行时间,将库函数内部对寄存器的操作单独拉出来,能够继续将时间减小到1.5us,但查看它的汇编代码,只有54条指令,也就是差不多要两个时钟周期才能执行一条指令。

      第一个问题:库函数UART_SendEDMA()执行时间贵公司有在demo板上测试过吗,测试的时间跟我测试的时间一致吗,如果不一致,是我编程环境或寄存器配置哪里有问题吗?(UART的edma传输没有使用tcd)

       如果直接使用UART_SendEDMA()库函数,即将要发送的数据通过DMA传输到UART的发送寄存器,当代码执行完(7us)后,UART发送管脚Tx上马上就会有要发送的数据;如果直接使用寄存器操作替换库函数,当代码执行完(1.5us)后,还要等待接近4us,UART发送管脚TX上才会有数据,

      第二个问题:以下寄存器操作替换库函数(黑体部分)是少了那些配置吗?导致Tx管脚上数据要代码执行完4us后才有数据。(寄存器操作是把库函数内部的代码单独拿出来配置的)

status_t UART_SendEDMA(UART_Type *base, uart_edma_handle_t *handle, uart_transfer_t *xfer)
{
// assert(handle);
// assert(handle->txEdmaHandle);
// assert(xfer);
// assert(xfer->data);
// assert(xfer->dataSize);

.....

Uart_Dma_Handle.txState = kUART_TxBusy; //状态设置为忙碌
Uart_Dma_Handle.txDataSizeAll = 10; //设置传输长度

/* Store the initially configured eDMA minor byte transfer count into the UART handle */
Uart_Dma_Handle.nbytes = 1;

/* source address */
Uart_Dma_Handle.txEdmaHandle->base->TCD[Uart_Dma_Handle.txEdmaHandle->channel].SADDR = (uint32_t) &TxData[0];
/* destination address */
Uart_Dma_Handle.txEdmaHandle->base->TCD[Uart_Dma_Handle.txEdmaHandle->channel].DADDR = (uint32_t) & (UART0->D);
/* Source data and destination data transfer size */
Uart_Dma_Handle.txEdmaHandle->base->TCD[Uart_Dma_Handle.txEdmaHandle->channel].ATTR = 0;
/* Source address signed offset */
Uart_Dma_Handle.txEdmaHandle->base->TCD[Uart_Dma_Handle.txEdmaHandle->channel].SOFF = 1;
/* Destination address signed offset */
Uart_Dma_Handle.txEdmaHandle->base->TCD[Uart_Dma_Handle.txEdmaHandle->channel].DOFF = 0;
/* Minor byte transfer count */
Uart_Dma_Handle.txEdmaHandle->base->TCD[Uart_Dma_Handle.txEdmaHandle->channel].NBYTES_MLNO = 1;
/* Current major iteration count */
Uart_Dma_Handle.txEdmaHandle->base->TCD[Uart_Dma_Handle.txEdmaHandle->channel].CITER_ELINKNO = 10;
/* Starting major iteration count */
Uart_Dma_Handle.txEdmaHandle->base->TCD[Uart_Dma_Handle.txEdmaHandle->channel].BITER_ELINKNO = 10;

/* Enable auto disable request feature */
Uart_Dma_Handle.txEdmaHandle->base->TCD[Uart_Dma_Handle.txEdmaHandle->channel].CSR |= DMA_CSR_DREQ_MASK;
/* Enable major interrupt */
Uart_Dma_Handle.txEdmaHandle->base->TCD[Uart_Dma_Handle.txEdmaHandle->channel].CSR |= DMA_CSR_INTMAJOR_MASK;

Uart_Dma_Handle.txEdmaHandle->base->SERQ = DMA_SERQ_SERQ(Uart_Dma_Handle.txEdmaHandle->channel);

/* Enable UART TX EDMA. */
UART0->C5 |= UART_C5_TDMAS_MASK;
UART0->C2 |= UART_C2_TIE_MASK;

pastedImage_1.png

pastedImage_2.png

0 Kudos
Reply

4,666 Views
jingpan
NXP TechSupport
NXP TechSupport

Hi,

1. 库函数的目标是用户通用性和安全性,不是为了追求最快速度。所以响应速度不是测试的目标。

2. 我测了速度,发现影响响应速度的最重要的不是UART_SendEDMA函数本身,而是波特率和UART初始化需要的时间。首先初始化程序运行结束后(UART_Init),模块内部还需要很长的时间才能准备好。这个时间同波特率有关。在115200bps下,需要50us;在57600下需要100多us。

      其次,从数据写入buffer到起始位发出的时间也由波特率决定。在57600bps时需要10~20us,在115200bps时需要5~12us,在230400bps时需要2~5us,在921600bps只需要不到1us。这个速度的差别主要源自于UART模块的接口部分是按照波特率时钟运行的。用DMA的方式和用polling的方式相差很小。

所以如果你对响应速度非常看重,你可以用更高的波特率。

Regards,

Jing

4,666 Views
yaoXin
Contributor II

多谢回复

1.我理解为保证通用性,库函数执行速度不是最快的,我是对芯片的代码执行效率有疑问,2.6版本的SDK,UART_SendEDMA()这个函数,在80MHZ的主频下执行时间是多少还是要再确认下(不考虑TX管脚发送数据的延时,仅考虑代码执行完成,用定时器或IO翻转都行)。

2.我使用的是2.5MHZ的波特率,而且使用了TX发送中断,和RX接收中断,如前文所述,在RX中断接收完成后,开启UART的TX发送中断,再发送中断中写入要发送的数据,发现有很大的延时(从写入寄存器到检测到Tx管脚上的数据,延时有4us),这个延时应该不是UART_Init()带来,因为此时初始化已经执行完很长时间,还是不明白这么高的波特率下这个延时如何产生,中断服务函数如下所示:

void UART0_RX_TX_IRQHandler(void)
{
    //发送中断标志
    if((UART_S1_TDRE_MASK & UART0->S1) && (UART0->C2 & UART_C2_TIE_MASK))
    {
        COMM_TransferDeal();   //往UART->D寄存器中写入发送数据
    }


//接收中断,中断的最后一条语句允许发送中断,执行如下两个库函数

//UART_EnableTx(LPSCI_1_PERIPHERAL, true);
//UART_EnableInterrupts(UART0, kUART_TxDataRegEmptyInterruptEnable);


    if ((UART_S1_RDRF_MASK & UART0->S1) && ((UART_C2_RIE_MASK & UART0->C2)))
    {
        Comm_ReceiveDeal();
    }

__DSB();
}