This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

PROCESSOR-SDK-AM335X: DCAN access from PRU

Part Number: PROCESSOR-SDK-AM335X


I am trying to set up a system on an AM335x chip where the CPU sends messages to the PRU via RPMsg to have the PRU send those messages over the CAN interface at precise times. Since the PRU cannot take advantage of the standard method for sending and receiving over CAN that the CPU side has (i.e. configure a network interface and use the cansend and candump utilities) I figured that I have to perform the initialization manually in the PRU's firmware by following the steps outlined in the AM335X technical manual.

I have the OCP master port enabled by setting STANDBY_INIT = 0, and I see DCAN0 and DCAN1 in the memory map of the AM335X_PRU.cmd file. However, I am unable to actually write to the memory addresses corresponding with the DCAN peripherals. I have no problem writing to peripherals local to the PRUSS, such as UART or the IEP. Should it be possible for the PRU to write to the DCAN register location (for me it is 0x441CC000 as per the .cmd file), and if so, what am I doing wrong?

  • Hello Julian,

    Could you post the terminal output when you try to write to the DCAN?

    If the PRU is using a peripheral, you want that peripheral disabled in the Linux driver tree so that the ARM does not try to mess with the peripheral while the PRU is using the peripheral. However, sometimes that means that the clock that sources the peripheral does not get initialized. If that is the case, the PRU must enable the DCAN's clocks before it tries to access the DCAN's registers.

    Regards,

    Nick

  • Hi Nick,

    Thank you for your quick reply, enabling the DCAN0 clock worked! For anyone reading the forum, I enabled the clock by setting values in the CM_PER_DCAN0_CLKCTRL Register at address 0x44E000C0 in the PRU. See code below:

    volatile __far uint32_t *dcan_clock_hard = (volatile uint32_t *) 0x44E000C0;
    *dcan_clock_hard |= 0x00000002;
    CT_CAN.CTL_bit.init = 1;
    CT_CAN.CTL_bit.CCE = 1;
                   
    The CT_CAN is a struct from a pru_can.h file I wrote myself following the AM335X technical register document's DCAN register diagram, and I modeled after the structs defined in other PRU .h files, such as pru_iep.h, but hardcoding the address of CT_CAN.CTL register works as well.
  • Hi Nick,

    Thank you for your help with this issue the other day. I have now run into another problem, where I cannot perform initialization of the DCAN ram using the control module's dcan_raminit register. My code is as follows:

    CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
    // Enable the DCAN0 clock, otherwise we cannot write to the DCAN0 registers
    volatile __far uint32_t *dcan_clock_hard = (volatile uint32_t *) 0x44E000C0;
    *dcan_clock_hard |= 0x00000002;
    __delay_cycles(10000000);
    // Start initialization
    CT_CAN.CTL_bit.init = 1;
    CT_CAN.CTL_bit.CCE = 1;
    while(CT_CAN.CTL_bit.init != 1){};
    // Configure bit timing
    CT_CAN.BTR = 0x00000014;
    // Finish initialization
    CT_CAN.CTL_bit.CCE = 0;
    CT_CAN.CTL_bit.init = 0;
    while(CT_CAN.CTL_bit.init != 0){};
    // Enable DCAN RAM for message objects
    volatile __far uint32_t *dcan_ram_init_register = (volatile uint32_t *) 0x44E10644;
    *dcan_ram_init_register &= ~0x00000001;
    *dcan_ram_init_register |= 0x00000001;
    while (!(*dcan_ram_init_register & (0x1<<8))){};
    Having tested the various while loops I know that the program runs up until the last one, where I am waiting for the dcan_raminit register to indicate the the RAM for DCAN0 is ready. Any ideas about why this never terminates? A missing clock initialization perhaps, or I need to configure message objects before doing so? The technical manual doesn't offer any immediate explanations as far as I can see.
  • Hello Julian,

    1) Start by taking a look at the CAN Linux driver to see how they initialize the RAM - it might have something useful. If not we can look at other resources.

    drivers/net/can/c_can/c_can_platform.c

    2) Let's take a quick step back. Could you help me understand why your application has the PRU controlling the CAN rather than the ARM?

    I would typically expect you to use a software stack with CAN. The software stack has been implemented in Linux on ARM, but we have not implemented it on PRU. So I imagine it's possible to get it working on PRU, but depending on your use case PRU <-> CAN might not be the optimal solution.

    Regards,

    Nick

  • Hi Nick,

    We're simply looking to be able to use the PRU to send messages, in hopes that it can send them with slightly more time precision than the CPU. While the PRU can only get to the CAN controller by crossing a series of interconnects, it doesn't have to perform context switching, cache checking, etc. like the CPU does. I have found codebases for how the CAN controller initialization is done on the CPU side, so my choice is to either to convert it to run on the PRU, or initialize the CAN controller from the CPU side and just do the message sending from the PRU. Is it feasible to do the latter?

    Thanks,

    Julian

  • Hello Julian,

    Ok. My initial suggestion would be to look at using our RT Linux SDK to see if you can get the desired performance using existing ARM code (e.g., increase the priority of the DCAN code above 50). That seems like the easiest way to get things working.

    If you decide to go with RT Linux controlling the CAN, but you need additional help, feel free to create a new thread and it will get assigned over to our Linux DCAN expert.

    If you decide that you need to use the PRU, then I would suggest doing both CAN controller initialization and message sending from the PRU. I have seen problems pop up when multiple cores are trying to share a peripheral.

    Regards,

    Nick

  • Hi Nick,

    Thanks for the help.

    For the moment I'm opting to try initialization from the PRU. I've adapted part of the StarterWare code to run on the PRU, but I still read all 0's from the DCAN_RAMINIT register at 0x44E10644, which leads me to believe I'm not reading from it at all. I notice that the register's memory range is not mapped in the AM335x.cmd file that describes the PRU's memory map - would modifying that file fix things?

    - Julian

  • Hello Julian,

    With your OCP master port enabled, I would expect that the PRU is correctly reading the registers. Typically I see errors (similar to what you saw earlier) if the PRU is trying to read memory that is not clocked, or if it attempts to access a memory space that requires elevated permissions (e.g., pinmux registers).

    I can only offer limited assistance in the process of developing your PRU code. Maybe adding debug code to the Linux driver will help establish what "expected" values look like during proper initialization? That could give you something to compare against when writing your code.

    Regards,

    Nick