diff --git a/_targets/Makefile.riscv64-gr765 b/_targets/Makefile.riscv64-gr765 index cadf57e3..675fa084 100644 --- a/_targets/Makefile.riscv64-gr765 +++ b/_targets/Makefile.riscv64-gr765 @@ -6,4 +6,4 @@ # Copyright 2025 Phoenix Systems # -DEFAULT_COMPONENTS := grlib-uart +DEFAULT_COMPONENTS := grlib-uart grlib-can-core grlib-can-driver can-sender-stress can-receiver-stress can-test-app diff --git a/can/grlib-can/Makefile b/can/grlib-can/Makefile new file mode 100644 index 00000000..8994d4d6 --- /dev/null +++ b/can/grlib-can/Makefile @@ -0,0 +1,43 @@ +# +# Makefile for Phoenix-RTOS grlib-can +# +# Copyright 2023 Phoenix Systems +# +# %LICENSE% +# +NAME := grlib-can-core +LOCAL_SRCS := grlib-can-core.c +LOCAL_HEADERS := grlib-can-core.h grlib-can-shared.h + +include $(static-lib.mk) + + +NAME := grlib-can-driver +LOCAL_SRCS := grlib-can-driver.c +DEP_LIBS := grlib-can-core + +include $(binary.mk) + +NAME := grlib-can-if +LOCAL_SRCS := grlib-can-if.c +LOCAL_HEADERS := grlib-can-if.h + +include $(static-lib.mk) + +NAME := can-test-app +LOCAL_SRCS := can-test-app.c +DEP_LIBS := grlib-can-if + +include $(binary.mk) + +NAME := can-sender-stress +LOCAL_SRCS := grlib-can-sender-stress-test.c +DEP_LIBS := grlib-can-core + +include $(binary.mk) + +NAME := can-receiver-stress +LOCAL_SRCS := grlib-can-receiver-stress-test.c +DEP_LIBS := grlib-can-core + +include $(binary.mk) \ No newline at end of file diff --git a/can/grlib-can/can-test-app.c b/can/grlib-can/can-test-app.c new file mode 100644 index 00000000..15273445 --- /dev/null +++ b/can/grlib-can/can-test-app.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "grlib-can-if.h" + +int main(int argc, char **argv) +{ + char *canDevice = "/dev/can0"; + oid_t canDev; + + if (lookup(canDevice, NULL, &canDev) < 0) { + printf("Device does not exist\n"); + return EXIT_FAILURE; + } + + printf("Successfully checked CAN device oid\n"); + + printf("Can device open: %d\n", grlibCan_open(canDev)); + + uint32_t status; + grlibCan_getStatus(canDev, &status); + printf("Can device status: %x\n", status); + + grlibCan_config_t config; + + grlibCan_getConfig(canDev, &config); + printf("Can device baud rate - nom: %d, data: %d\n", config.nomBdRate, config.dataBdRate); + + grlibCan_msg_t frames[10]; + + frames->frame.head = (1 << 18); + frames->frame.stat = 0x10u << 24; + frames->frame.payload[0] = 0x0F; + + for (int i = 1; i < 10; i++) { + memcpy((void *)&frames[i], (void *)frames, sizeof(grlibCan_msg_t)); + frames[i].frame.payload[0] = 0x0F + i; + } + + int ret = grlibCan_Send(canDev, frames, 10, true); + printf("Can device managed to send %d frames : block\n", ret); + + grlibCan_msg_t buf[10]; + + ret = grlibCan_Recv(canDev, buf, 10, true); + printf("Can device managed to receive %d frames : block\n", ret); + + ret = grlibCan_Send(canDev, frames, 10, false); + printf("Can device managed to send %d frames : non-block\n", ret); + + ret = grlibCan_Recv(canDev, buf, 10, false); + printf("Can device managed to receive %d frames : non-block\n", ret); + + printf("Sending frame of extended length\n"); + frames->frame.stat = 0xF0 << 24 | (1 << 2); + ret = grlibCan_Send(canDev, (void *)frames, 5, true); + printf("Ret: %d\n", ret); + + printf("Trying to read in SYNC mode to buffer which is to small\n"); + ret = grlibCan_Recv(canDev, buf, 1, true); + printf("Ret: %d\n", ret); + + printf("Trying to read in SYNC mode to buffer of appropriate size\n"); + ret = grlibCan_Recv(canDev, buf, 5, true); + printf("Ret: %d\n", ret); + + printf("Sending frame of extended length\n"); + frames->frame.stat = 0xF0 << 24 | (1 << 2); + ret = grlibCan_Send(canDev, (void *)frames, 5, true); + printf("Ret: %d\n", ret); + + printf("Trying to read in ASYNC mode to buffer which is to small\n"); + ret = grlibCan_Recv(canDev, buf, 1, false); + printf("Ret: %d\n", ret); + + printf("Trying to read in ASYNC mode to buffer of appropriate size\n"); + ret = grlibCan_Recv(canDev, buf, 5, false); + printf("Ret: %d\n", ret); + + printf("Closing device\n"); + grlibCan_close(canDev); + + ret = grlibCan_Send(canDev, frames, 10, true); + printf("Trying to send with device closed: %d\n", ret); + + return EXIT_SUCCESS; +} diff --git a/can/grlib-can/grlib-can-core.c b/can/grlib-can/grlib-can-core.c new file mode 100644 index 00000000..4278aa44 --- /dev/null +++ b/can/grlib-can/grlib-can-core.c @@ -0,0 +1,746 @@ +/* + * Phoenix-RTOS + * + * GRCANFD driver + * + * GRLIB CANFD driver file + * + * Copyright 2025 Phoenix Systems + * Author: Mikolaj Matalowski + * + * %LICENSE% + */ + +/* System includes */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Platform specific includes */ +#include +#include +#include +#include + +/* Local includes */ +#include "grlib-can-core.h" + +/* Interrupt handler */ +int __attribute__((section(".interrupt"), aligned(0x1000))) grlibCan_irqHandler(unsigned int irqNum, void *arg) +{ + grlibCan_dev_t *dev = (grlibCan_dev_t *)arg; + grlibCan_hwDev_t *device = dev->device; + + int ret = 0; + + /* Handlers for SYNC TX/RX */ + if ((device->penIsrMsk & GRLIB_CAN_RX_IRQ) != 0) { + dev->rxBufStatus = rxBufReady; + dev->device->irqMskSet &= ~(GRLIB_CAN_RX_IRQ); + ret = 1; + } + + if ((device->penIsrMsk & GRLIB_CAN_TX_IRQ) != 0) { + dev->txBufStatus = txBufReady; + dev->device->irqMskSet &= ~GRLIB_CAN_TX_IRQ; + ret = 1; + } + + if ((device->penIsrMsk & (GRLIB_CAN_BMRDERR_IRQ | GRLIB_CAN_BMWRERR_IRQ | GRLIB_CAN_OR_IRQ | GRLIB_CAN_BUSSOFF_IRQ | GRLIB_CAN_PASS_IRQ)) != 0) { + /* Error on the bus occurred */ + /* Turn off codec and set device state to error */ + dev->device->ctrlReg &= ~1; + dev->deviceStatus = deviceError; + } + + /* Clear interrupt register */ + device->irqClrReg = ~(0); + + return ret; +} + +/* Pop one frame from RX channel */ +int grlibCan_popFrame(grlibCan_dev_t *dev, grlibCan_msg_t *msg) +{ + uint32_t rdPtr = (dev->device->rxRdPtr) >> 4; + (void)memcpy((void *)msg, (void *)(&dev->rxBufAdd[rdPtr]), sizeof(grlibCan_msg_t)); + dev->device->rxRdPtr = (((rdPtr + 1) % dev->rxBufSz) << 4); + return 0; +} + +/* Push one frame into TX channel */ +int grlibCan_pushFrame(grlibCan_dev_t *dev, const grlibCan_msg_t *msg) +{ + uint32_t wrPtr = (dev->device->txWrtPtr) >> 4; + (void)memcpy((void *)(&dev->txBufAdd[wrPtr]), (void *)msg, sizeof(grlibCan_msg_t)); + dev->device->txWrtPtr = (((wrPtr + 1) % dev->txBufSz) << 4); + return 0; +} + +static inline double getBd_nom(uint32_t reg) +{ + reg = reg >> 5; + uint32_t PS2 = reg & ((~(uint32_t)0) >> 27); + reg = reg >> 5; + uint32_t PS1 = reg & ((~(uint32_t)0) >> 26); + reg = reg >> 6; + uint32_t SC = reg & ((~(uint32_t)0) >> 24); + uint32_t a = (SC + 1) * (PS1 + PS2 + 1); + return (double)(SYSCLK_FREQ) / ((double)a); +} + +static inline double getBd_data(uint32_t reg) +{ + reg = reg >> 5; + uint32_t PS2 = reg & ((~(uint32_t)0) >> 28); + reg = reg >> 5; + uint32_t PS1 = reg & ((~(uint32_t)0) >> 28); + reg = reg >> 6; + uint32_t SC = reg & ((~(uint32_t)0) >> 24); + uint32_t a = (SC + 1) * (PS1 + PS2 + 1); + return (double)(SYSCLK_FREQ) / ((double)a); +} + +void grlibCan_copyConfig(grlibCan_dev_t *device, grlibCan_config_t *config) +{ + config->conf = device->device->confReg; + config->syncMask = device->device->syncMask; + config->syncCode = device->device->syncCode; + + config->nomBdRate = (uint32_t)getBd_nom(device->device->nomTimConf); + config->dataBdRate = (uint32_t)getBd_data(device->device->dataTimConf); + + config->txCtrlReg = device->device->txCtrlReg; + config->rxCtrlReg = device->device->rxCtrlReg; + config->rxAccMask = device->device->rxAccMask; + config->rxAccCode = device->device->rxAccCode; +} + +int grlibCan_resetDevice(grlibCan_dev_t *dev) +{ + dev->device->ctrlReg |= 1 << 1; + while ((dev->device->ctrlReg & (1 << 1)) != 0) { } + dev->device->ctrlReg |= 1; + grlibCan_applyDefConf(dev); + dev->deviceStatus = deviceReady; + return 0; +} + +/* Apply default config */ +int grlibCan_applyDefConf(grlibCan_dev_t *dev) +{ + volatile grlibCan_hwDev_t *device = dev->device; + + mutexLock(dev->ctrlLock); + + /* Reset device and turn off codec */ + device->ctrlReg &= ~1; + device->ctrlReg = (1 << 1); + + /* Sleeping until reset finished */ + while ((device->ctrlReg & (1u << 1)) != 0) { } + + /* Configure ids to listen to */ + device->confReg = 0; + device->confReg |= (1 << 2) | (1 << 1) | (1 << 6); + + /* Do no compare on any bits - listen to all */ + device->syncMask = (0u); + device->syncCode = ~(0u); + + /* Set baud-rate for both nominal and data */ + device->nomTimConf = grlibCan_setBdRateNom(125000.0, device->nomTimConf); + device->dataTimConf = grlibCan_setBdRateData(125000.0, device->dataTimConf); + device->transDelCompReg = 0; + + /* Turn off CANopen */ + device->copReg &= ~1; + + /* Configure interrupts */ + volatile uint32_t pending = device->penIrq; + (void)pending; + + /* Enable error interrupts */ + device->irqMskSet = GRLIB_CAN_BMRDERR_IRQ | GRLIB_CAN_BMWRERR_IRQ | + GRLIB_CAN_OR_IRQ | GRLIB_CAN_BUSSOFF_IRQ | GRLIB_CAN_PASS_IRQ; + + /* Configure TX channel */ + device->txCtrlReg &= ~1; + while ((device->txCtrlReg & (1 << 1)) != 0) { } + + + device->txBufAdd = (uint32_t)va2pa((void *)dev->txBufAdd); + device->txBufSz = (uint32_t)((dev->txBufSz / 4) << 6); + device->txWrtPtr = 0; + device->txRdPtr = 0; + + device->txCtrlReg &= ~(1 << 2); + device->txCtrlReg |= 1; + + /* Configure RX channel */ + device->rxCtrlReg &= ~1; + while ((device->rxCtrlReg & (1 << 1)) != 0) { } + + device->rxBufAdd = (uint32_t)va2pa((void *)dev->rxBufAdd); + device->rxBufSz = (dev->rxBufSz / 4) << 6; + device->rxWrtPtr = 0; + device->rxRdPtr = 0; + /* Put all frames that we got from the bus into the buffer */ + device->rxAccCode = (0u); + device->rxAccMask = (0u); + + device->rxCtrlReg &= ~(1 << 3); + device->rxCtrlReg |= 1; + + /* Turn codec back on */ + device->ctrlReg |= 1; + + mutexUnlock(dev->ctrlLock); + return 0; +} + +/* Apply user provided config */ +int grlibCan_applyConfig(grlibCan_dev_t *dev, grlibCan_config_t *config) +{ + grlibCan_hwDev_t *device = dev->device; + + mutexLock(dev->ctrlLock); + mutexLock(dev->txLock); + mutexLock(dev->rxLock); + + /* Turn off codec */ + device->ctrlReg &= ~(1); + while ((device->ctrlReg & (1u << 1)) != 0) { } + + /* Apply user defined configuration */ + device->confReg = config->conf; + + device->syncMask = config->syncMask; + device->syncCode = config->syncCode; + + /* Set timing configuration */ + device->nomTimConf = grlibCan_setBdRateNom((double)config->nomBdRate, device->nomTimConf); + device->dataTimConf = grlibCan_setBdRateData((double)config->dataBdRate, device->dataTimConf); + device->transDelCompReg = config->transDelComp; + + /* Set TX and RX config */ + device->txCtrlReg = config->txCtrlReg; + device->rxCtrlReg = config->rxCtrlReg; + device->rxAccCode = config->rxAccCode; + device->rxAccMask = config->rxAccMask; + + /* Turn codec back on */ + device->ctrlReg |= 1; + + mutexUnlock(dev->ctrlLock); + mutexUnlock(dev->txLock); + mutexUnlock(dev->rxLock); + return 0; +} + +/* Transmit buffer of frames in blocking mode */ +int grlibCan_transmitSync(grlibCan_dev_t *dev, const grlibCan_msg_t *buffer, + const uint32_t length) +{ + if (dev->deviceStatus == deviceError) { + return 0; + } + uint32_t i = 0; + + (void)mutexLock(dev->txLock); + + dev->txStatus = txOngoing; + + while (i < length) { + /* Check for bus status, if error return how many frames sent */ + if (dev->deviceStatus == deviceError) { + (void)mutexUnlock(dev->txLock); + return -i; + } + + uint32_t wrPtr = dev->device->txWrtPtr >> 4; + uint32_t rdPtr = dev->device->txRdPtr >> 4; + + /* While there is space in transmit buffer push frames */ + while (wrPtr != (rdPtr > 0 ? (rdPtr - 1) : (dev->txBufSz - 1))) { + if (i == length) { + (void)mutexUnlock(dev->txLock); + return length; + } + + (void)grlibCan_pushFrame(dev, &buffer[i]); + i++; + + wrPtr = dev->device->txWrtPtr >> 4; + rdPtr = dev->device->txRdPtr >> 4; + } + + /* TX buffer has been filled */ + dev->txBufStatus = txBufFull; + dev->device->irqMskSet |= GRLIB_CAN_TX_IRQ; + + while (dev->txBufStatus == txBufFull) { + if (i == length) { + break; + } + /* Wait for interrupt to clear TX_FULL */ + (void)condWait(dev->cond, dev->txLock, 1000000); + } + } + + dev->txStatus = txDone; + + (void)mutexUnlock(dev->txLock); + return i; +} + +/* Transmit buffer of frames in non-blocking mode */ +int grlibCan_transmitAsync(grlibCan_dev_t *dev, const grlibCan_msg_t *buffer, + const uint32_t length) +{ + if (dev->deviceStatus == deviceError) { + return 0; + } + uint32_t i = 0; + + (void)mutexLock(dev->txLock); + + uint32_t wrPtr = dev->device->txWrtPtr >> 4; + uint32_t rdPtr = dev->device->txRdPtr >> 4; + + /* While there is space in transmit buffer push frames */ + while (wrPtr != (rdPtr > 0 ? (rdPtr - 1) : (dev->txBufSz - 1))) { + if (i == length) { + break; + } + (void)grlibCan_pushFrame(dev, &buffer[i]); + i++; + + wrPtr = dev->device->txWrtPtr >> 4; + rdPtr = dev->device->txRdPtr >> 4; + } + + (void)mutexUnlock(dev->txLock); + return i; +} + +/* Receive length frames in blocking mode */ +int grlibCan_recvSync(grlibCan_dev_t *dev, grlibCan_msg_t *buffer, + uint32_t length, uint32_t *pending) +{ + uint32_t i = 0; + *pending = 0; + + (void)mutexLock(dev->rxLock); + + while (i < length) { + + /* Check for bus status, if error return how many frames read */ + if (dev->deviceStatus == deviceError) { + (void)mutexUnlock(dev->rxLock); + return -i; + } + + /* While there are frames to read, process them */ + while (dev->device->rxRdPtr != dev->device->rxWrtPtr) { + if (i == length) { + (void)mutexUnlock(dev->rxLock); + return length; + } + /* Check if whole CAN frame can be processed */ + uint32_t frame_len = ((dev->rxBufAdd[dev->device->rxRdPtr >> 4]).frame.stat >> 28); + if (frame_len > 8) { + /* Whole frame cannot be consumed */ + if ((frame_len - 8) / sizeof(grlibCan_msg_t) + 1 >= length - i) { + (void)mutexUnlock(dev->rxLock); + *pending = (frame_len - 8) / sizeof(grlibCan_msg_t) + 1; + return i; + } + else { + /* Read whole packet and continue*/ + for (int j = 0; j < (frame_len - 8) / sizeof(grlibCan_msg_t) + 1; j++) { + (void)grlibCan_popFrame(dev, &buffer[i]); + i++; + continue; + } + } + } + /* Read one packet */ + (void)grlibCan_popFrame(dev, &buffer[i]); + i++; + } + + /* RX buffer is empty */ + dev->rxBufStatus = rxBufEmpty; + dev->device->irqMskSet |= GRLIB_CAN_RX_IRQ; + + while (dev->rxBufStatus == rxBufEmpty) { + if (i == length) { + break; + } + (void)condWait(dev->cond, dev->rxLock, 100000); + } + } + + (void)mutexUnlock(dev->rxLock); + return i; +} + +/* Receive at most length frames in non-blocking mode */ +int grlibCan_recvAsync(grlibCan_dev_t *dev, grlibCan_msg_t *buffer, + const uint32_t length) +{ + if (dev->deviceStatus == deviceError) { + return 0; + } + uint32_t i = 0; + + (void)mutexLock(dev->rxLock); + + /* While there are frames to read, process them */ + while (dev->device->rxRdPtr != dev->device->rxWrtPtr && i < length) { + /* Check if whole CAN frame can be processed */ + uint32_t frame_len = ((dev->rxBufAdd[dev->device->rxRdPtr >> 4]).frame.stat >> 28); + if (frame_len > 8) { + /* Whole frame cannot be consumed */ + if ((frame_len - 8) / sizeof(grlibCan_msg_t) + 1 >= length - i) { + (void)mutexUnlock(dev->rxLock); + return i; + } + else { + /* Read whole packet and continue*/ + for (int j = 0; j < (frame_len - 8) / sizeof(grlibCan_msg_t) + 1; j++) { + (void)grlibCan_popFrame(dev, &buffer[i]); + i++; + continue; + } + } + } + /* Read one packet */ + (void)grlibCan_popFrame(dev, &buffer[i]); + i++; + } + + (void)mutexUnlock(dev->rxLock); + return i; +} + +/* Allocate TX/RX circular buffers */ +int grlibCan_allocateBuffers(grlibCan_dev_t *dev) +{ + dev->txBufAdd = mmap(NULL, _PAGE_SIZE, + PROT_WRITE | PROT_READ, MAP_UNCACHED | MAP_ANONYMOUS, -1, 0); + if (dev->txBufAdd == MAP_FAILED) { + return -ENOMEM; + } + + dev->txBufSz = (_PAGE_SIZE / (sizeof(grlibCan_msg_t) * 4)) * 4; + + dev->rxBufAdd = mmap(NULL, _PAGE_SIZE, + PROT_WRITE | PROT_READ, MAP_UNCACHED | MAP_ANONYMOUS, -1, 0); + if (dev->rxBufAdd == MAP_FAILED) { + (void)munmap((void *)dev->txBufAdd, _PAGE_SIZE); + dev->txBufSz = 0; + return -ENOMEM; + } + + dev->rxBufSz = (_PAGE_SIZE / (sizeof(grlibCan_msg_t) * 4)) * 4; + return 0; +} + +/* Allocate resources for the devices */ +int grlibCan_allocateResources(grlibCan_dev_t *dev, uintptr_t base, int id) +{ + dev->ownerPid = 0; + dev->ctrlLock = (handle_t)-1; + dev->cond = (handle_t)-1; + dev->txLock = (handle_t)-1; + dev->rxLock = (handle_t)-1; + dev->txBufAdd = MAP_FAILED; + dev->rxBufAdd = MAP_FAILED; + dev->txBufSz = 0; + dev->rxBufSz = 0; + + uintptr_t offset = (base & ~(_PAGE_SIZE - 1)); + dev->device = mmap(NULL, _PAGE_SIZE, PROT_WRITE | PROT_READ, + MAP_DEVICE | MAP_PHYSMEM | MAP_ANONYMOUS, -1, (off_t)offset); + + if (dev->device == MAP_FAILED) { + debug("grlib-can: Failed to map device physical address\n"); + return -1; + } + + if (grlibCan_allocateBuffers(dev) < 0) { + debug("grlib-can: Failed to allocate circular buffers\n"); + return -1; + } + + if (mutexCreate(&dev->ctrlLock) < 0) { + debug("grlib-can: Failed to create lock for config\n"); + return -1; + } + + if (mutexCreate(&dev->txLock) < 0) { + debug("grlib-can: Failed to create lock for config\n"); + return -1; + } + + if (mutexCreate(&dev->rxLock) < 0) { + debug("grlib-can: Failed to create lock for config\n"); + return -1; + } + + if (condCreate(&dev->cond) < 0) { + debug("grlib-can: Failed to create conditional\n"); + return -1; + } + +#ifdef VERBOSE + debug("grlib-can: Managed to allocate resources\n"); +#endif + + return 0; +} + +/* Cleanup resources */ +void grlibCan_cleanupResources(grlibCan_dev_t *dev) +{ + if (dev->device != MAP_FAILED) { + (void)munmap((void *)dev->device, _PAGE_SIZE); + } + + if (dev->txBufAdd != MAP_FAILED) { + (void)munmap((void *)dev->txBufAdd, _PAGE_SIZE); + } + + if (dev->rxBufAdd != MAP_FAILED) { + (void)munmap((void *)dev->rxBufAdd, _PAGE_SIZE); + } + + if (dev->ctrlLock != (handle_t)-1) { + resourceDestroy(dev->ctrlLock); + } + + if (dev->rxLock != (handle_t)-1) { + resourceDestroy(dev->rxLock); + } + + if (dev->txLock != (handle_t)-1) { + resourceDestroy(dev->txLock); + } + + if (dev->cond != (handle_t)-1) { + resourceDestroy(dev->cond); + } +} + +/* Initialise devices */ +int grlibCan_initDevices(grlibCan_dev_t *devices, int num) +{ + for (int i = 0; i < num; i++) { + if (devices[i].canId == -1) + continue; + + if (grlibCan_allocateResources(&devices[i], (uintptr_t)devices[i].device, 0) < 0) { + grlibCan_cleanupResources(&devices[i]); + return -1; + } + + if (interrupt(devices[i].irqNum, grlibCan_irqHandler, + (void *)&devices[i], devices[i].cond, NULL) < 0) { + debug("grlib-can: Failed to set up the interrupt\n"); + } + +#ifdef VERBOSE + debug("grlib-can: Managed to register callback to device interrupt\n"); +#endif + + /* Now having memory for all needed operations we can configure the device */ + grlibCan_applyDefConf(&devices[i]); + +#ifdef VERBOSE + debug("grlib-can: Applied default config to the device\n"); +#endif + } + return 0; +} + +/* Register devices in the file system */ +int grlibCan_registerDevices(oid_t *oid, grlibCan_dev_t *devices, uint32_t length) +{ + oid_t dir; + + if (lookup("/dev", &dir, NULL) < 0) { + debug("grlib-can: Failed to create devices\n"); + return -1; + } + + for (int i = 0; i < length; i++) { + char buf[24]; + if (snprintf(buf, sizeof(buf), "can%d", i) >= sizeof(buf)) { + debug("grlib-can: Failed to create devices\n"); + return -1; + } + oid->id = i; + + if (create_dev(oid, buf) < 0) { + debug("grlib-can: Failed to create devices\n"); + return -1; + } + } + + return 0; +} + +/* Unregister devices from file system */ +int grlibCan_unregisterDevices(oid_t *port, grlibCan_dev_t *devices, uint32_t length) +{ + oid_t dir; + + while (lookup("/dev", NULL, &dir) < 0) { + usleep(100000); + } + + for (int i = 0; i < length; i++) { + if (devices[i].canId == -1) + continue; + char buf[24]; + if (snprintf(buf, sizeof(buf), "/dev/can%d", i) >= sizeof(buf)) { + printf("grlib-can: Failed to remove can%d\n", i); + continue; + } + port->id = i; + if (unlink(buf) < 0) { + printf("grlib-can: Failed to remove can%d\n", i); + } + } + + return 0; +} + +static inline double getBd(uint16_t SC, uint16_t PS1, uint16_t PS2) +{ + uint32_t a = (SC + 1) * (PS1 + PS2 + 1); + return (double)(SYSCLK_FREQ) / ((double)a); +} +/* Search for most optimal configuration to set given baud rate */ +uint32_t grlibCan_setBdRateData(double dataBdRate, uint32_t prevSetting) +{ + uint16_t optSC = 0; + uint16_t optPS1 = 0; + uint16_t optPS2 = 0; + double optDiff = DBL_MAX; + + for (uint16_t SC = 0; SC <= 255; SC++) { + for (uint16_t PS1 = 1; PS1 <= 15; PS1++) { + for (uint16_t PS2 = 2; PS2 <= 8; PS2++) { + double diff = dataBdRate - getBd(SC, PS1, PS2); + if (diff < 0) { + diff = -diff; + } + if (diff < optDiff) { + optSC = SC; + optPS1 = PS1; + optPS2 = PS2; + optDiff = diff; + } + if (diff <= DBL_EPSILON) { + break; + } + } + } + } + + if (optDiff >= 0.2 * dataBdRate) { + return prevSetting; + } + + return (optSC << 16) | (optPS1 << 10) | (optPS2 << 5) | (optPS1 < optPS2 ? optPS1 : optPS2); +} + +uint32_t grlibCan_setBdRateNom(double nomBdRate, uint32_t prevSetting) +{ + uint16_t optSC = 0; + uint16_t optPS1 = 0; + uint16_t optPS2 = 0; + double optDiff = DBL_MAX; + + for (int SC = 0; SC <= 255; SC++) { + for (int PS1 = 2; PS1 <= 63; PS1++) { + for (int PS2 = 2; PS2 <= 16; PS2++) { + double diff = nomBdRate - getBd(SC, PS1, PS2); + if (diff < 0) { + diff = -diff; + } + if (diff < optDiff) { + optSC = SC; + optPS1 = PS1; + optPS2 = PS2; + optDiff = diff; + } + if (diff <= DBL_EPSILON) { + break; + } + } + } + } + + if (optDiff >= 0.2 * nomBdRate) { + return prevSetting; + } + + return (optSC << 16) | (optPS1 << 10) | (optPS2 << 5) | (optPS1 < optPS2 ? optPS1 : optPS2 - 1); +} + +int grlibCan_queryForDevices(grlibCan_dev_t *dev) +{ + int detectedDevices = 0; + /* System query for number of GRCAN controllers and their properties */ + for (unsigned int i = 0; i < GRLIB_MAX_CAN_DEVICES; i++) { + unsigned int id = i; + + ambapp_dev_t device = { .devId = CORE_ID_GRCANFD }; + platformctl_t ctl = { + .action = pctl_get, + .type = pctl_ambapp, + .task.ambapp.dev = &device, + .task.ambapp.instance = &id + }; + + if (platformctl(&ctl) < 0) { + break; + } + + detectedDevices++; + + if (device.bus == BUS_AMBA_AHB) { + dev[i].canId = -1; + dev[i].irqNum = 0; + dev[i].device = 0; + /* GRCANFD should be on APB bus */ + continue; + } + else if (device.bus == BUS_AMBA_APB) { + dev[i].canId = device.devId; + dev[i].irqNum = device.irqn; + dev[i].device = (grlibCan_hwDev_t *)device.info.apb.base; + } + } + return detectedDevices; +} diff --git a/can/grlib-can/grlib-can-core.h b/can/grlib-can/grlib-can-core.h new file mode 100644 index 00000000..a581aa08 --- /dev/null +++ b/can/grlib-can/grlib-can-core.h @@ -0,0 +1,255 @@ +/* + * Phoenix-RTOS + * + * GRCANFD driver + * + * GRLIB CANFD driver file + * + * Copyright 2025 Phoenix Systems + * Author: Mikolaj Matalowski + * + * %LICENSE% + */ + +#ifndef GRLIB_CAN_CORE_H +#define GRLIB_CAN_CORE_H + +#include +#include +#include + +#include "grlib-can-shared.h" + +extern uint64_t curr_rx_irq_handler; +extern uint64_t prev_rx_irq_handler; +extern uint32_t sleep_counter; + +#define VERBOSE 1 +#undef VERBOSE + +/* Memory used by DMA has to be aligned to 1kbyte segments (here used 4kbyte) */ +#define PAGE_ALIGN(addr) (((addr_t)(addr) + _PAGE_SIZE - 1) & ~(_PAGE_SIZE - 1)) + +#define GRLIB_CAN_DEF_CIRCBUFSZ 16 +#define GRLIB_MAX_CAN_DEVICES 8 + +/* IRQ definitions */ +#define GRLIB_CAN_TXLOSS_IRQ (1u << 16) /* Loss of arbitration during transmission */ +#define GRLIB_CAN_RXMISS_IRQ (1u << 15) /* Message filtered away */ +#define GRLIB_CAN_TXERRCNT_IRQ (1u << 14) /* TX error counter incremented */ +#define GRLIB_CAN_RXERRCNT_IRQ (1u << 13) /* RX error counter incremented */ +#define GRLIB_CAN_TXSYNC_IRQ (1u << 12) /* Sync message send by the TX line */ +#define GRLIB_CAN_RXSYNC_IRQ (1u << 11) /* Sync message send on RX line */ +#define GRLIB_CAN_TX_IRQ (1u << 10) /* Successful transmission of the message */ +#define GRLIB_CAN_RX_IRQ (1u << 9) /* Successful reception of the message */ +#define GRLIB_CAN_TXEMPTY_IRQ (1u << 8) /* Successful transmission of all messages */ +#define GRLIB_CAN_RXFULL_IRQ (1u << 7) /* All stored frames read */ +#define GRLIB_CAN_TXPTR_IRQ (1u << 6) /* TX buffer read pointer equal to txIrq pointer*/ +#define GRLIB_CAN_RXPTR_IRQ (1u << 5) /* RX buffer write pointer equal to rxIrq pointer*/ +#define GRLIB_CAN_BMRDERR_IRQ (1u << 4) /* Error during AMBA read */ +#define GRLIB_CAN_BMWRERR_IRQ (1u << 3) /* Error during AMBA write */ +#define GRLIB_CAN_OR_IRQ (1u << 2) /* Over-run during reception */ +#define GRLIB_CAN_BUSSOFF_IRQ (1u << 1) /* Bus-off condition */ +#define GRLIB_CAN_PASS_IRQ 1u /* Error-passive condition */ + +/* Structure that follows GRCANFD register structure */ +typedef struct { + volatile uint32_t confReg; /* Configuration register, only last byte not reserved */ + volatile uint32_t statReg; /* Status register */ + volatile uint32_t ctrlReg; /* Select between CAN/CANFD and reset */ + volatile const uint32_t capReg; /* Capability register */ + + volatile const uint32_t rsvrd0[2]; + + volatile uint32_t syncMask; /* Sync mask filter register */ + volatile uint32_t syncCode; /* Sync code filter register */ + + volatile const uint32_t rsvrd1[8]; + + /* Timing configuration*/ + volatile uint32_t nomTimConf; /* Nominal timing configuration */ + volatile uint32_t dataTimConf; /* Data timing configuration */ + volatile uint32_t transDelCompReg; /* Transmission delay compensation */ + + volatile const uint32_t rsvrd2[13]; + + /* CANopen configuration */ + volatile uint32_t copReg; /* CANopen heartbeat configuration register */ + volatile uint32_t ohtTimReg; /* CANopen heartbeat timeout register */ + volatile const uint32_t ohtCntReg; /* CANopen heartbeat counter register */ + volatile const uint32_t ohtStatusReg; /* CANopen heartbeat status register */ + + volatile const uint32_t rsvrd3[28]; + + /* IRQ configuration */ + volatile uint32_t penIsrMsk; /* Pending interrupt masked status register */ + volatile uint32_t penIrqMsk; /* Pending interrupt masked register */ + volatile uint32_t penIsr; /* Pending interrupt status register */ + volatile uint32_t penIrq; /* Pending interrupt register */ + volatile uint32_t irqMskSet; /* Interrupt mask register */ + volatile uint32_t irqClrReg; /* Pending interrupt clear register */ + + volatile const uint32_t rsrvd4[58]; + + /* TX channel configuration */ + volatile uint32_t txCtrlReg; /* TX channel control register */ + volatile uint32_t txBufAdd; /* TX channel circular buffer base address */ + volatile uint32_t txBufSz; /* TX channel circular buffer size */ + volatile uint32_t txWrtPtr; /* TX channel circular buffer write pointer */ + volatile uint32_t txRdPtr; /* TX channel circular buffer read pointer*/ + volatile uint32_t txIrqReg; /* TX channel interrupt configuration register */ + + volatile const uint32_t rsrvd5[58]; + + /* RX channel configuration */ + volatile uint32_t rxCtrlReg; /* RX channel control register */ + volatile uint32_t rxBufAdd; /* RX channel circular buffer base address */ + volatile uint32_t rxBufSz; /* RX channel circular buffer size */ + volatile uint32_t rxWrtPtr; /* RX channel circular buffer write pointer */ + volatile uint32_t rxRdPtr; /* RX channel circular buffer read pointer */ + volatile uint32_t rxIrqReg; /* RX channel interrupt configuration register */ + volatile uint32_t rxAccMask; /* RX channel acceptance mask */ + volatile uint32_t rxAccCode; /* RX channel acceptance code */ +} grlibCan_hwDev_t; + +typedef enum { + deviceIdle = 0, + deviceReady = 1, + deviceError = -1 +} deviceStatus; + +typedef enum { + txOngoing = 0, + txDone = 1 +} txStatus; + +typedef enum { + txBufReady = 0, + txBufFull = 1 +} txBufStatus; + +typedef enum { + rxBufReady = 0, + rxBufEmpty = 1 +} rxBufStatus; + +typedef struct { + int canId; + msg_rid_t ownerPid; + unsigned int irqNum; + grlibCan_hwDev_t *device; + + deviceStatus deviceStatus; + txStatus txStatus; + + handle_t ctrlLock; /* Lock for configuration and control of the device */ + handle_t txLock; + handle_t rxLock; + handle_t cond; /* Conditional passed to the interrupt */ + + uint32_t rxIrqCounter; + uint32_t txIrqCounter; + + /* RX buffer */ + grlibCan_msg_t *rxBufAdd; + rxBufStatus rxBufStatus; + uint32_t rxBufSz; + + /* TX buffer */ + grlibCan_msg_t *txBufAdd; + txBufStatus txBufStatus; + uint32_t txBufSz; +} grlibCan_dev_t; + +typedef struct { + uint32_t port; + grlibCan_dev_t *devices; + int num; +} grlibCan_driver_t; + +/* Core driver functions */ + + +/* Create devices */ +int grlibCan_initDevices(grlibCan_dev_t *dev, int num); + + +/* Reset codec */ +int grlibCan_resetDevice(grlibCan_dev_t *dev); + + +/* Allocate resources */ +int grlibCan_allocateResources(grlibCan_dev_t *dev, uintptr_t base, int id); + + +/* Allocate TX/RX buffers */ +int grlibCan_allocateBuffers(grlibCan_dev_t *dev); + + +/* Register devices on the system */ +int grlibCan_registerDevices(oid_t *port, grlibCan_dev_t *devices, uint32_t length); + + +/* Cleanup resources */ +void grlibCan_cleanupResources(grlibCan_dev_t *dev); + + +/* Copy current device config */ +void grlibCan_copyConfig(grlibCan_dev_t *device, grlibCan_config_t *config); + + +/* Apply default config */ +int grlibCan_applyDefConf(grlibCan_dev_t *dev); + + +/* Apply user provided config */ +int grlibCan_applyConfig(grlibCan_dev_t *dev, grlibCan_config_t *config); + + +/* Driver interrupt handler */ +int grlibCan_irqHandler(unsigned int irqNum, void *arg); +/* Transmit buffer of frames in blocking mode */ +int grlibCan_transmitSync(grlibCan_dev_t *dev, const grlibCan_msg_t *buffer, + const uint32_t length); + + +/* Transmit buffer of frames in non-blocking mode */ +int grlibCan_transmitAsync(grlibCan_dev_t *dev, const grlibCan_msg_t *buffer, + const uint32_t length); + + +/* Receive at most length frames in blocking mode */ +int grlibCan_recvSync(grlibCan_dev_t *dev, grlibCan_msg_t *buffer, + const uint32_t length, uint32_t *pending); + + +/* Receive at most length frame in non-blocking mode */ +int grlibCan_recvAsync(grlibCan_dev_t *dev, grlibCan_msg_t *buffer, + const uint32_t length); + + +/* Calculate register configuration for given baud rate */ +uint32_t grlibCan_setBdRateData(double dataBdRate, uint32_t prevSetting); + + +/* Calculate register configuration for given baud rate */ +uint32_t grlibCan_setBdRateNom(double nomBdRate, uint32_t prevSetting); + + +/* Remove device files */ +int grlibCan_unregisterDevices(oid_t *port, grlibCan_dev_t *devices, uint32_t length); + + +/* Pop one CAN frame from circular buffer */ +int grlibCan_popFrame(grlibCan_dev_t *dev, grlibCan_msg_t *msg); + + +/* Push one CAN frame to circular buffer */ +int grlibCan_pushFrame(grlibCan_dev_t *dev, const grlibCan_msg_t *msg); + + +/* Use platformctl syscall to query system for GRCANFD controllers*/ +int grlibCan_queryForDevices(grlibCan_dev_t *dev); + + +#endif diff --git a/can/grlib-can/grlib-can-driver.c b/can/grlib-can/grlib-can-driver.c new file mode 100644 index 00000000..292008c5 --- /dev/null +++ b/can/grlib-can/grlib-can-driver.c @@ -0,0 +1,211 @@ +/* + * Phoenix-RTOS + * + * GRCANFD driver + * + * GRLIB CANFD driver file + * + * Copyright 2025 Phoenix Systems + * Author: Mikolaj Matalowski + * + * %LICENSE% + */ + +/* System includes */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Platform specific includes */ +#include "grlib-can-core.h" + +static int grlibCan_handleDevCtl(msg_t *msg, grlibCan_dev_t *device) +{ + grlibCan_devCtrl_t *idevctl = (grlibCan_devCtrl_t *)(msg->i.raw); + int ret; + uint32_t pending; + + switch (idevctl->type) { + case can_setConfig: + if (msg->i.size < sizeof(grlibCan_config_t)) { + msg->o.err = -1; + return -1; + } + return grlibCan_applyConfig(device, (grlibCan_config_t *)msg->i.data); + case can_getConfig: + if (msg->o.size != sizeof(grlibCan_config_t)) { + msg->o.err = -1; + return -1; + } + grlibCan_copyConfig(device, msg->o.data); + msg->o.err = 0; + return 0; + case can_getStatus: + if (msg->i.size < sizeof(uint32_t)) { + msg->o.err = -EINVAL; + return -1; + } + msg->o.err = 0; + *(uint32_t *)(msg->o.data) = device->device->statReg; + return 0; + case can_reset: + grlibCan_resetDevice(device); + msg->o.err = EOK; + return 0; + case can_writeSync: + ret = grlibCan_transmitSync(device, (grlibCan_msg_t *)msg->i.data, (uint32_t)msg->i.size / sizeof(grlibCan_msg_t)); + msg->o.err = ret; + return ret; + case can_readSync: + /* Places number of pending frames in RX circular buffer, if buffer cannot fit whole CAN frame */ + ret = grlibCan_recvSync(device, (grlibCan_msg_t *)msg->o.data, (uint32_t)msg->o.size / sizeof(grlibCan_msg_t), &pending); + *(uint32_t *)((grlibCan_msg_t *)msg->o.data + ret) = pending; + msg->o.err = ret; + return ret; + case can_writeAsync: + ret = grlibCan_transmitAsync(device, (grlibCan_msg_t *)msg->i.data, (uint32_t)msg->i.size / sizeof(grlibCan_msg_t)); + msg->o.err = ret; + return ret; + case can_readAsync: + ret = grlibCan_recvAsync(device, (grlibCan_msg_t *)msg->o.data, (uint32_t)msg->o.size / sizeof(grlibCan_msg_t)); + msg->o.err = ret; + return ret; + default: + return -1; + } +} + +static int grlibCan_dispatchMsg(msg_t *msg, grlibCan_dev_t *devices, int num) +{ + id_t id = msg->oid.id; + if (id < num && devices[id].canId != -1) { + return grlibCan_handleDevCtl(msg, &devices[id]); + } + return -1; +} + +static void grlibCan_messageThread(void *args) +{ +#ifdef VERBOSE + debug("grlib-can: Entering main message thread\n"); +#endif + + grlibCan_driver_t *driverInstance = (grlibCan_driver_t *)args; + grlibCan_dev_t *devices = driverInstance->devices; + int num = driverInstance->num; + + msg_t msg; + msg_rid_t rid; + + for (;;) { + while (msgRecv(driverInstance->port, &msg, &rid) < 0) { } + switch (msg.type) { + case mtDevCtl: + if (msg.oid.id < num && devices[msg.oid.id].canId != -1 && devices[msg.oid.id].ownerPid == msg.pid) { + grlibCan_dispatchMsg(&msg, devices, num); + } + else { + msg.o.err = -EBUSY; + } + break; + case mtOpen: + if (msg.oid.id < num && devices[msg.oid.id].canId != -1 && devices[msg.oid.id].ownerPid == 0) { + devices[msg.oid.id].ownerPid = msg.pid; + msg.o.err = EOK; + } + else { + msg.o.err = -EBUSY; + } + break; + case mtClose: + if (msg.oid.id < num && devices[msg.oid.id].canId != -1 && devices[msg.oid.id].ownerPid == msg.pid) { + devices[msg.oid.id].ownerPid = 0; + msg.o.err = EOK; + } + else { + msg.o.err = -EBUSY; + } + break; + default: + msg.o.err = -ENOSYS; + break; + } + + msgRespond(driverInstance->port, &msg, rid); + } +} + +int main(int argc, char **argv) +{ +#ifdef VERBOSE + debug("grlib-can: Driver process started\n"); +#endif + + grlibCan_dev_t devices[GRLIB_MAX_CAN_DEVICES]; + oid_t oid; + + int detectedDevices = grlibCan_queryForDevices(devices); + + + if (detectedDevices == 0) { +#ifdef VERBOSE + debug("grlib-can: There are no GRCANFD devices\n"); +#endif + return EXIT_FAILURE; + } + + /* Create driver port */ + if (portCreate(&oid.port) < 0) { +#ifdef VERBOSE + debug("grlib-can: Failed to create port\n"); +#endif + return EXIT_FAILURE; + } + + grlibCan_driver_t driverInstance = { + .devices = devices, + .num = detectedDevices, + .port = oid.port + }; + + if (grlibCan_initDevices(driverInstance.devices, driverInstance.num) < 0) { +#ifdef VERBOSE + debug("grlib-can: Failed to initialise devices\n"); +#endif + portDestroy(oid.port); + return EXIT_FAILURE; + } + + if (grlibCan_registerDevices(&oid, driverInstance.devices, driverInstance.num) < 0) { +#ifdef VERBOSE + debug("grlib-can: Failed to register devices in file system\n"); +#endif + for (int i = 0; i < driverInstance.num; i++) + grlibCan_cleanupResources(&driverInstance.devices[i]); + portDestroy(oid.port); + return EXIT_FAILURE; + } + + grlibCan_messageThread(&driverInstance); + + grlibCan_cleanupResources(driverInstance.devices); + portDestroy(driverInstance.port); + grlibCan_unregisterDevices(&oid, driverInstance.devices, driverInstance.num); + + return EXIT_SUCCESS; +} diff --git a/can/grlib-can/grlib-can-if.c b/can/grlib-can/grlib-can-if.c new file mode 100644 index 00000000..23ebf398 --- /dev/null +++ b/can/grlib-can/grlib-can-if.c @@ -0,0 +1,123 @@ +/* + * Phoenix-RTOS + * + * GRCANFD driver + * + * GRLIB CANFD driver interface library + * + * Copyright 2025 Phoenix Systems + * Author: Mikolaj Matalowski + * + * %LICENSE% + */ +#include "grlib-can-if.h" + +int grlibCan_open(oid_t port) +{ + msg_t msg = { 0 }; + msg.type = mtOpen; + msg.oid.id = port.id; + + if (msgSend(port.port, &msg) < 0) { + return -1; + } + + return msg.o.err; +} + +void grlibCan_close(oid_t port) +{ + msg_t msg = { 0 }; + msg.type = mtClose; + msg.oid.id = port.id; + + msgSend(port.port, &msg); +} + +int grlibCan_setConfig(oid_t port, grlibCan_config_t *config) +{ + msg_t msg = { 0 }; + msg.type = mtDevCtl; + msg.oid.id = port.id; + + ((grlibCan_devCtrl_t *)msg.i.raw)->type = can_setConfig; + msg.i.data = (void *)config; + msg.i.size = sizeof(grlibCan_config_t); + + if (msgSend(port.port, &msg) < 0) { + return -1; + } + + return msg.o.err; +} + +int grlibCan_getConfig(oid_t port, grlibCan_config_t *config) +{ + msg_t msg = { 0 }; + msg.type = mtDevCtl; + msg.oid.id = port.id; + + ((grlibCan_devCtrl_t *)msg.i.raw)->type = can_getConfig; + msg.o.data = (void *)config; + msg.o.size = sizeof(grlibCan_config_t); + + if (msgSend(port.port, &msg) < 0) { + return -1; + } + + return msg.o.err; +} + +int grlibCan_Send(oid_t port, grlibCan_msg_t *buffer, size_t size, bool block) +{ + msg_t msg = { 0 }; + msg.type = mtDevCtl; + msg.oid.id = port.id; + + ((grlibCan_devCtrl_t *)msg.i.raw)->type = block ? can_writeSync : can_writeAsync; + + msg.i.data = buffer; + msg.i.size = size * sizeof(grlibCan_msg_t); + + if (msgSend(port.port, &msg) < 0) { + return -1; + } + + return msg.o.err; +} + +int grlibCan_Recv(oid_t port, grlibCan_msg_t *buffer, size_t size, bool block) +{ + msg_t msg = { 0 }; + msg.type = mtDevCtl; + msg.oid.id = port.id; + + ((grlibCan_devCtrl_t *)msg.i.raw)->type = block ? can_readSync : can_readAsync; + + msg.o.data = buffer; + msg.o.size = size * sizeof(grlibCan_msg_t); + + if (msgSend(port.port, &msg) < 0) { + return -1; + } + + return msg.o.err; +} + +int grlibCan_getStatus(oid_t port, uint32_t *status) +{ + msg_t msg = { 0 }; + msg.type = mtDevCtl; + msg.oid.id = port.id; + + ((grlibCan_devCtrl_t *)msg.i.raw)->type = can_getStatus; + + msg.o.data = status; + msg.o.size = sizeof(uint32_t); + + if (msgSend(port.port, &msg) < 0) { + return -1; + } + + return msg.o.err; +} diff --git a/can/grlib-can/grlib-can-if.h b/can/grlib-can/grlib-can-if.h new file mode 100644 index 00000000..34c0e3e4 --- /dev/null +++ b/can/grlib-can/grlib-can-if.h @@ -0,0 +1,37 @@ +/* + * Phoenix-RTOS + * + * GRCANFD driver + * + * GRLIB CANFD driver interface library + * + * Copyright 2025 Phoenix Systems + * Author: Mikolaj Matalowski + * + * %LICENSE% + */ + +#ifndef GRLIB_CAN_IF_H +#define GRLIB_CAN_IF_H + +#include +#include +#include + +#include "grlib-can-shared.h" + +int grlibCan_open(oid_t port); + +void grlibCan_close(oid_t port); + +int grlibCan_setConfig(oid_t port, grlibCan_config_t *config); + +int grlibCan_getConfig(oid_t port, grlibCan_config_t *config); + +int grlibCan_Send(oid_t port, grlibCan_msg_t *buffer, size_t size, bool block); + +int grlibCan_Recv(oid_t port, grlibCan_msg_t *buffer, size_t size, bool block); + +int grlibCan_getStatus(oid_t port, uint32_t *status); + +#endif diff --git a/can/grlib-can/grlib-can-receiver-stress-test.c b/can/grlib-can/grlib-can-receiver-stress-test.c new file mode 100644 index 00000000..b75d7b04 --- /dev/null +++ b/can/grlib-can/grlib-can-receiver-stress-test.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include + +#include "grlib-can-core.h" +#include "grlib-can-shared.h" + +#define TEST_MSG_BUFFER_SIZE 256 +#define TEST_BAUDRATE 1000000 + +int frames; +char my_stack[4096]; + +void temp(void *) +{ + for (;;) { + printf("%d\n", frames); + sleep(1); + } +} + +int main(int argc, char **argv) +{ + /* Set priority of current thread for RX interrupt */ + priority(1); + + /* Query for devices and configure */ + grlibCan_dev_t devices[GRLIB_MAX_CAN_DEVICES]; + grlibCan_queryForDevices(devices); + + grlibCan_dev_t *device = &devices[0]; + grlibCan_initDevices(device, 1); + + grlibCan_applyDefConf(device); + + grlibCan_config_t config; + grlibCan_copyConfig(device, &config); + + config.dataBdRate = TEST_BAUDRATE; + config.nomBdRate = TEST_BAUDRATE; + + grlibCan_applyConfig(device, &config); + + + /* Read 1MB of data */ + int bytes_to_read = 1024 * 1024; + + grlibCan_msg_t msg[TEST_MSG_BUFFER_SIZE]; + + beginthread(temp, 4, my_stack, 4096, NULL); + + printf("Starting listening\n"); + time_t start = time(NULL); + + uint32_t pending; + + while (bytes_to_read > 0) { + int ret = grlibCan_recvSync(device, msg, 1, &pending); + + frames += ret * 8; + + for (int i = 0; i < ret; i++) { + bytes_to_read -= (msg[i].frame.stat >> 28); + } + } + + time_t end = time(NULL); + + printf("RECV: Managed to read 1MB of data"); + printf("RECV: Took %lld s\n", end - start); + + return 0; +} diff --git a/can/grlib-can/grlib-can-sender-stress-test.c b/can/grlib-can/grlib-can-sender-stress-test.c new file mode 100644 index 00000000..b2ef5ab9 --- /dev/null +++ b/can/grlib-can/grlib-can-sender-stress-test.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include + +#include "grlib-can-core.h" +#include "grlib-can-shared.h" + +#define TEST_MSG_BUFFER_SIZE 200 +#define TEST_BAUDRATE 1000000 + +int main(int argc, char **argv) +{ + /* Query for devices and configure */ + grlibCan_dev_t devices[GRLIB_MAX_CAN_DEVICES]; + grlibCan_queryForDevices(devices); + + grlibCan_dev_t *device = &devices[1]; + grlibCan_initDevices(device, 1); + + grlibCan_applyDefConf(device); + + grlibCan_config_t config; + grlibCan_copyConfig(device, &config); + + config.dataBdRate = TEST_BAUDRATE; + config.nomBdRate = TEST_BAUDRATE; + + /* Turn off loopback */ + config.conf &= ~((1 << 7) | (1 << 6)); + + grlibCan_applyConfig(device, &config); + + /* Send 1MB of data */ + int bytes_to_send = 1024 * 1024; + + grlibCan_msg_t msg_buffer[TEST_MSG_BUFFER_SIZE]; + for (int i = 0; i < TEST_MSG_BUFFER_SIZE; i++) { + msg_buffer[i].frame.head = (1 << 31) | (0xFA << 17); + msg_buffer[i].frame.stat = 8 << 28; + memset(msg_buffer[i].frame.payload, 0xFA, 8); + } + + time_t start = time(NULL); + + while (bytes_to_send > 0) { + int ret = grlibCan_transmitAsync(device, msg_buffer, 1); + if (ret <= 0) { + continue; + } + + bytes_to_send -= 8 * ret; + } + + time_t end = time(NULL); + + printf("SEND: Sent 1MB of data, took: %lld\n", end - start); + printf("SEND: Avg rate: %fbps effective\n", (float)(1024 * 1024 * 8) / (end - start)); + + return 0; +} diff --git a/can/grlib-can/grlib-can-shared.h b/can/grlib-can/grlib-can-shared.h new file mode 100644 index 00000000..093e06f4 --- /dev/null +++ b/can/grlib-can/grlib-can-shared.h @@ -0,0 +1,69 @@ +/* + * Phoenix-RTOS + * + * GRCANFD driver + * + * GRLIB CANFD driver file + * + * Copyright 2025 Phoenix Systems + * Author: Mikolaj Matalowski + * + * %LICENSE% + */ + +#ifndef GRLIB_CAN_SHARED_H +#define GRLIB_CAN_SHARED_H + +#include +#include +#include +#include + +typedef struct +{ + union { + struct { + uint32_t head; /* Head contains CAN packet mode and IDs */ + uint32_t stat; + + uint8_t payload[8]; /* Payload */ + } frame; + + uint8_t payload[16]; + }; +} grlibCan_msg_t; + +typedef struct +{ + /* Base configuration */ + uint32_t conf; + uint32_t syncMask; + uint32_t syncCode; + + uint32_t nomBdRate; /* Nominal baud-rate */ + uint32_t dataBdRate; /* Data transfer baud-rate */ + uint32_t transDelComp; /* Tranmission delay compensation */ + + /* CANopen currently omitted */ + + /* TX configuration */ + uint32_t txCtrlReg; + + /* RX configuration */ + uint32_t rxCtrlReg; + uint32_t rxAccMask; + uint32_t rxAccCode; +} grlibCan_config_t; + +typedef struct { + enum { can_setConfig = 0, + can_getConfig, + can_getStatus, + can_reset, + can_writeSync, + can_readSync, + can_writeAsync, + can_readAsync } type; +} grlibCan_devCtrl_t; + +#endif