2023-11-21 11:34 PM
I have integrated the ST25R3911B into an ARM64 Linux development board, using rfal_v1.3 for communication via SPI.
I am able to poll and read cards successfully, but I've observed higher CPU usage.
Upon inspecting the perf.svg, it appears that the st25r3911ReadMultipleRegisters function is consuming a significant amount of CPU.
Could you assist in analyzing this issue? here is my
/******************************************************************************
* @attention
*
* <h2><center>© COPYRIGHT 2018 STMicroelectronics</center></h2>
*
* Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.st.com/myliberty
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
* AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/*! \file pltf_spi.c
*
* \author Shikha Singh
*
* \brief Implementation for SPI communication.
*
*/
/*
******************************************************************************
* INCLUDES
******************************************************************************
*/
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include "pltf_spi.h"
#include "st_errno.h"
/*
******************************************************************************
* DEFINES
******************************************************************************
*/
#define SPI_MODE_CONFIG SPI_MODE_1
#define SPI_BITS_PER_WORD 8
#define SPI_MAX_FREQ 1000000
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
/*
******************************************************************************
* STATIC VARIABLES
******************************************************************************
*/
/* ST25R3911XX is connected with Linux host's SPI port /dev/spidev0.0 */
static const char *device = "/dev/spidev0.0";
static int fd = 0;
static int isSPIInit = 0;
/* Lock to serialize SPI communication */
static pthread_mutex_t lockCom;
/*
******************************************************************************
* GLOBAL AND HELPER FUNCTIONS
******************************************************************************
*/
ReturnCode spi_init(void)
{
int ret = 0;
uint32_t mode = SPI_MODE_CONFIG;
uint8_t bitsperword = SPI_BITS_PER_WORD;
uint32_t speed = SPI_MAX_FREQ;
fd = open(device, O_RDWR);
if (fd < 0) {
printf("Error: spi device open = %d\n", fd);
ret = fd;
goto error;
}
/* set spi mode */
ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
if (ret < 0) {
printf("Error: SPI mode setting\n");
goto error;
}
/* set spi bits per word */
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bitsperword);
if (ret < 0) {
printf("Error: setting spi bitsperword\n");
goto error;
}
/* set spi frequency */
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret < 0) {
printf("Error: setting SPI frequency\n");
goto error;
}
ret = pthread_mutex_init(&lockCom, NULL);
if (ret != 0)
{
printf("Error: mutex init to protect SPI communication is failed\n");
goto error;
}
isSPIInit = 1;
return ERR_NONE;
error:
return ERR_IO;
}
HAL_statusTypeDef spiTxRx(const uint8_t *txData, uint8_t *rxData, uint8_t length)
{
int ret = 0;
int i;
/* check if SPI init is done */
if (!isSPIInit) {
printf(" error: spi is used for communication before its initialization\n");
return HAL_ERROR;
}
/* lock for atomic SPI transaction */
//pthread_mutex_lock(&lock);
struct spi_ioc_transfer transfer;
memset(&transfer, 0, sizeof(struct spi_ioc_transfer));
if (txData)
transfer.tx_buf = (unsigned long) txData;
if (rxData)
transfer.rx_buf = (unsigned long) rxData;
transfer.len = (unsigned int) length;
transfer.speed_hz = SPI_MAX_FREQ;
transfer.bits_per_word = SPI_BITS_PER_WORD;
transfer.delay_usecs = 0;
transfer.cs_change = 0;
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &transfer);
if (ret < 0) {
printf("Error: SPI error in data transfer=%d\n",ret);
/* Unlock the mutex before returning from error case */
//pthread_mutex_unlock(&lock);
return HAL_ERROR;
}
/* Unlock the mutex */
//pthread_mutex_unlock(&lock);
return HAL_OK;
}
void pltf_protect_com(void)
{
pthread_mutex_lock(&lockCom);
}
void pltf_unprotect_com(void)
{
pthread_mutex_unlock(&lockCom);
}
void plat_spi_select(void)
{
usleep(150);
}
void plat_spi_deselect(void)
{
usleep(150);
}
Solved! Go to Solution.
2023-11-28 11:48 PM
Hi zhaocz,
I missed that you are on an old version. It has been updated quite some while ago. Please find it at https://www.st.com/en/embedded-software/stsw-st25r009.html
BR, Ulysses
2023-11-22 01:42 AM
Hi Zhaocz,
can you please share the results you are getting and also where you think the CPU consumption should be?
The function is issuing SPI transfers in a blocking manner, but expectation is that it would be sleeping while waiting for the SPI to be finished.
BR, Ulysses
2023-11-22 01:57 AM
My application runs on different boards, both using arm64 architecture with Debian 10, but from different manufacturers.
On Board A, the CPU usage is only 5%, while on Board B, it's 20%.
After analysis, I found that on Board B, a significant portion of the CPU is spent on the st25r3911ReadMultipleRegisters operation.
I suspect that this difference in CPU usage might be due to variations in the SPI between the two boards.
Could you please provide some guidance on this? Thank you.
2023-11-22 04:27 AM
Hi,
I would presume a difference in SPI driver implementation (different SPI IP?). I guess you have also an analysis of the function called by st25r3911ReadMultipleRegisters()? Please look into it as well and share. If this cannot be meaningfully analyzed, then please introduce a proxy function for SPI (for st25r3911ReadMultipleRegisters() only and analyze it. Basically look at the delta of the CPU usage.
BR, Ulysses
2023-11-22 06:32 PM
I apologize for overlooking one point—the CPU difference starts from interrupt_init()on pltf_gpio.c and then goes to st25r3911Isr() then goes tost25r3911ReadMultipleRegisters().
I'm not quite clear on why there's a need to call st25r3911ReadMultipleRegisters(ST25R3911_REG_IRQ_MAIN, iregs, sizeof(iregs)) to read the Main Interrupt Register.
In my understanding, polling should not require interrupts.
Below is the code. Could you please take a look? Thank you very much
void* pthread_func()
{
int ret = 0;
int c = 0;
uint16_t event;
struct pollfd poll_fd;
poll_fd.fd = fd_readGPIO;
poll_fd.events = POLLPRI;
/* First check if GPIOInit is done or not */
if (!isGPIOInit) {
printf("GPIO is not initialized\n");
return NULL;
}
/* poll interrupt line forever */
while(1)
{
ret = poll(&poll_fd, 1, -1);
if (ret < 1) {
printf("Error: in polling for interrupt line\n");
return NULL;
}
if (poll_fd.revents & (POLLPRI|POLLERR))
{
printf("this is interrupt?\n");
lseek(poll_fd.fd, 0, SEEK_SET);
read(poll_fd.fd, &c, 1);
/* Call RFAL Isr */
st25r3911Isr();
}
}
return NULL;
}
ReturnCode interrupt_init(void)
{
pthread_t intr_thread;
struct sched_param params;
int ret;
/* create a pthread to poll for interrupt */
ret = pthread_create(&intr_thread, NULL, pthread_func, NULL);
if (ret) {
printf("Error: poll thread creation %d\n", ret);
return ERR_IO;
}
/* Assign highest priority to polling thread */
params.sched_priority = sched_get_priority_max(SCHED_FIFO);
ret = pthread_setschedparam(intr_thread, SCHED_FIFO, ¶ms);
if (ret) {
printf("Error: assigning high priority to polling thread\n");
return ERR_IO;
}
return ERR_NONE;
}
void st25r3911Isr( void )
{
st25r3911CheckForReceivedInterrupts();
if (NULL != st25r3911interrupt.callback)
st25r3911interrupt.callback();
}
void st25r3911CheckForReceivedInterrupts( void )
{
uint8_t iregs[ST25R3911_INT_REGS_LEN];
uint32_t irqStatus;
ST_MEMSET( iregs, (uint8_t)ST25R3911_IRQ_MASK_ALL, ST25R3911_INT_REGS_LEN );
/* In case the IRQ is Edge (not Level) triggered read IRQs until done */
while( platformGpioIsHigh( ST25R391X_INT_PORT, ST25R391X_INT_PIN ) )
{
st25r3911ReadMultipleRegisters(ST25R3911_REG_IRQ_MAIN, iregs, sizeof(iregs));
#ifdef PLATFORM_LED_FIELD_PIN
if (iregs[0] & ST25R3911_IRQ_MASK_TXE)
{
platformLedOn( PLATFORM_LED_FIELD_PORT, PLATFORM_LED_FIELD_PIN );
}
#endif /* PLATFORM_LED_FIELD_PIN */
#ifdef PLATFORM_LED_RX_PIN
if (iregs[0] & ST25R3911_IRQ_MASK_RXS)
{
platformLedOn( PLATFORM_LED_RX_PORT, PLATFORM_LED_RX_PIN );
}
if ((iregs[0] & ST25R3911_IRQ_MASK_RXE) || (iregs[1] & (ST25R3911_IRQ_MASK_NRE >> 8))) /* In rare cases there is rxs but not rxe, then we have nre */
{
platformLedOff( PLATFORM_LED_RX_PORT, PLATFORM_LED_RX_PIN );
}
#endif /* PLATFORM_LED_RX_PIN */
irqStatus = (uint32_t)iregs[0];
irqStatus |= (uint32_t)iregs[1]<<8;
irqStatus |= (uint32_t)iregs[2]<<16;
/* forward all interrupts, even masked ones to application. */
st25r3911interrupt.status |= irqStatus;
}
}
2023-11-22 07:14 PM
I added logs in this loop while (platformGpioIsHigh(ST25R391X_INT_PORT, ST25R391X_INT_PIN)) and found that on the board with high CPU usage, it often gets stuck in this loop, occurring dozens of times per minute.
On another board, it only happens once or twice.
2023-11-23 01:24 AM
Hi,
please compare the resulting polling frequency and the achieved SPI speed to see if the differences are in this area. Interrupts a dozen of times per minute unusual low for a polling loop. Or did you enable the wake-up mode? Is it only polling without a card? What are the used discoverParams?
BR, ULysses
2023-11-28 12:18 AM
I've tried adjusting the SPI speed, and the higher the SPI speed, the higher the polling frequency triggered.
I'm sorry, I misspoke. It's several tens of times per second.
The wake-up mode is disabled.
It polls even when there is no card.
I'm sorry, I don't understand what discoverParam is.
2023-11-28 02:29 AM
Hi zhaocs,
when using our demos you will find a
static rfalNfcDiscoverParam discParam;
Likely in demo_pollling.c the code will set its totalDuration to a value. This value should result in the period of the polling: Does it match your observations from the outside? What is the polling period for both of your implementations?
If unsure please share logic analyzer traces of both for comparison (MISO,MOSI,SCLK,CS, INT).
Best Regards, Ulysses
2023-11-28 05:11 PM
I couldn't locate rfalNfcDiscoverParam in my code. Would it be possible for you to share your Linux demo code? Thank you