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-AM437X: Error when accessing peripherals from PRU

Part Number: PROCESSOR-SDK-AM437X

Hello,

I tried to access ARM's peripherals in PRU but received error.

SDK version I used is ti-processor-sdk-linux-rt-am437x-evm-02.00.02.11.

The PRU I used was PRUSS0. 

I have already enable the ocp master of PRUSS1 by below code, and RPMsg is working now.

/* Enable the OCP master port of PRUSS1 to read external memories */

PRUSS1_CFG_SYSCFG &= ~(STANDY_INIT_BIT);

The accessing code in PRU is: put_uint32(*(volatile uint32_t *)(0x481CC000));


I tried to access DMTIMER2, I2C, ePWM0~2.   Only DMTIMER2 could be accessed.

The error message is as follow:

[ 234.724970] ------------[ cut here ]------------
[ 234.728502] WARNING: CPU: 0 PID: 22 at drivers/bus/omap_l3_noc.c:147 l3_interrupt_handler+0x25c/0x36c()
[ 234.737069] 44000000.ocp:L3 Custom Error: MASTER ICSS0 TARGET L4_PER_1 (Read): Data Access in User mode during Functional access
[ 234.738798] remoteproc0: kicking vq index: 0
[ 234.738804] remoteproc0: kicking vqid 0 on PRU0
[ 234.738815] virtio_rpmsg_bus virtio0: rpmsg host is online
[ 234.738895] remoteproc0: registered virtio0 (type 7)
[ 234.738963] pru-rproc 54474000.pru0: PRU rproc node /ocp/pruss@54440000/pru0@54474000 probed successfully
[ 234.766900] Modules linked in: mod_pru_rproc(O) mod_pruss(O) mod_sram(O) mod_rtc(O) mod_powerfail(O) mod_iobus(O) mod_coupler(O)
[ 234.777820] CPU: 0 PID: 22 Comm: irq/19-l3-app-i Tainted: G O 4.1.20-rt23-yocto-preempt-rt #51
[ 234.787646] Hardware name: Generic AM43 (Flattened Device Tree)
[ 234.792580] [<c00173e8>] (unwind_backtrace) from [<c0013eb0>] (show_stack+0x20/0x24)
[ 234.800408] [<c0013eb0>] (show_stack) from [<c042735c>] (dump_stack+0x24/0x28)
[ 234.806693] [<c042735c>] (dump_stack) from [<c003acdc>] (warn_slowpath_common+0x98/0xc4)
[ 234.813892] [<c003acdc>] (warn_slowpath_common) from [<c003ad48>] (warn_slowpath_fmt+0x40/0x48)
[ 234.822640] [<c003ad48>] (warn_slowpath_fmt) from [<c0296d00>] (l3_interrupt_handler+0x25c/0x36c)
[ 234.830669] [<c0296d00>] (l3_interrupt_handler) from [<c0077e90>] (irq_forced_thread_fn+0x30/0x64)
[ 234.839662] [<c0077e90>] (irq_forced_thread_fn) from [<c00780f4>] (irq_thread+0x11c/0x1fc)
[ 234.847051] [<c00780f4>] (irq_thread) from [<c0056ee0>] (kthread+0xd4/0xec)
[ 234.853043] [<c0056ee0>] (kthread) from [<c000fc08>] (ret_from_fork+0x14/0x2c)
[ 234.860195] ---[ end trace 0000000000000002 ]---

Could you give me some advice?

Best Regards
Jerry

  • The software team have been notified. They will respond here.
  • Hi Jerry,

    Which I2C did you try to access? Was the EPWM used by another kernel process?

    Also are you sure you accessed the correct address space?

    Best Regards,
    Yordan
  • Hello Yordan,

    Sorry for reply late.

    I tried to access 0x4802A000 (I2C1), 0x4819C000 (I2C2)  and 0x48040000 (DMTIMER2) according to the AM437x Spec.

    Only DMTIMER2 could access normally. The other two access will lead to exception. 

    I'm sure that I2C1 is not used by ARM core because its status in DTS is "disabled".

    The whole process I operated is as follow:

    1. send message via "rpmsg" to pru. command is echo "0" > rpmsg_pru30
    2. When pru received message, it will try to access variant device based on the message.
    3. Got exception from serial port. 

    Below is by code in PRU. Please ignore the put** method. They are just used for formatting message and sending them to rpmsg.  

    while(1){

        __R30 ^= gpio;
        __delay_cycles(100000000);
        if(CT_MBX.MESSAGE[MB_FROM_ARM_HOST] == 1){
            /* Receive the message */
        if(pru_rpmsg_receive(&transport, &src, &dst, payload, &len) == PRU_RPMSG_SUCCESS){
            if (len > 1)
            {
                switch (payload[0])
                {
                case '0':
                    put_str("0  ");
                    put_uint32(*(volatile uint32_t *)(0x4802A000));
                    put_cr();
                    send_msg();
                    break;
                case '1':
                    put_str("1 ");
                    put_uint32(PWMSS1.IDVER);
                    put_cr();
                    send_msg();
                    break;
                case '2':
                    put_str("2 ");
                    put_uint32(PWMSS2.IDVER);
                    put_cr();
                    send_msg();
                    break;
                case '3':
                    put_str("3  ");
                    put_uint32(*(volatile uint32_t *)(0x4819C000));
                    put_cr();
                    send_msg();
                    break;
                case '4':
                    put_str("4  ");
                    put_uint32(*(volatile uint32_t *)(0x48040000));
                    put_cr();
                    send_msg();
                    break;
                }
            }
            /* Echo the message back to the same address from which we just received */
            pru_rpmsg_send(&transport, dst, src, "finished\n", strlen("finished\n"));
        }
    }

    Best Regards

    Jerry

  • Hi,

    This function:
    put_uint32(*(volatile uint32_t *)(0x481CC000))
    is executed in linux user space, right?

    I am not sure how this is defined. Does it remap the physical address 0x481CC000 to the virtual kernel space? See the implementation of devmem2 to see how physical addresses should be remapped to virtual addresses in linux environment.

    Best Regards,
    Yordan
  • Hello Yordan,

    All the code are running on PRU, not on arm.  I'm trying to access peripherals from PRU.

    I give you all the code I have.

    /*
     * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ 
     *  
     *  
     * Redistribution and use in source and binary forms, with or without 
     * modification, are permitted provided that the following conditions 
     * are met:
     * 
     *  * Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 
     *  * Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the
     *    distribution.
     * 
     *  * Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     * 
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
     * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
     * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
     * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
     * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
     * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
     * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    #include <stdint.h>
    #include <pru_cfg.h>
    #include "resource_table_empty.h"
    
    #include <stdint.h>
    #include <stdio.h>
    #include <pru_cfg.h>
    #include <rsc_types.h>
    #include <pru_virtqueue.h>
    #include <pru_rpmsg.h>
    #include <sys_mailbox.h>
    #include <sys_pwmss.h>
    
    /* The mailboxes used for RPMsg are defined in the Linux device tree
     * PRU0 uses mailboxes 2 (From ARM) and 3 (To ARM)
     * PRU1 uses mailboxes 4 (From ARM) and 5 (To ARM)
     */
    #define MB_TO_ARM_HOST              3
    #define MB_FROM_ARM_HOST            2
    
    /*
     * Using the name 'rpmsg-pru' will probe the rpmsg_pru driver found
     * at linux-x.y.z/drivers/rpmsg/rpmsg_pru.c
     */
    #define CHAN_NAME                   "rpmsg-pru"
    #define CHAN_DESC                   "Channel 30"
    #define CHAN_PORT                   30
    
    /*
     * Used to make sure the Linux drivers are ready for RPMsg communication
     * Found at linux-x.y.z/include/uapi/linux/virtio_config.h
     */
    #define VIRTIO_CONFIG_S_DRIVER_OK   4
    #define PRUSS1_CFG_SYSCFG       *(volatile unsigned int *) 0x66004
    #define STANDY_INIT_BIT             (1 << 4)
    uint16_t src, dst;
    struct pru_rpmsg_transport transport;
    uint8_t payload[32];
    
    volatile register uint32_t __R30;
    volatile register uint32_t __R31;
    
    uint8_t u32_buf[32];
    
    uint8_t h2a[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    uint32_t send_buf_idx;
    uint8_t send_buf[512];
    void put_str(char *buf)
    {
        while(*buf)
        {
            send_buf[send_buf_idx] = *buf;
            send_buf_idx++;
            buf++;
        }
    }
    
    void put_uint8(uint8_t data)
    {
        send_buf[send_buf_idx] = h2a[data / 16];
        send_buf_idx++;
    
        send_buf[send_buf_idx] = h2a[data % 16];
        send_buf_idx++;
    }
    
    void put_uint32(uint32_t data)
    {
        put_uint8((data >> 24) & 0xFF);
        put_uint8((data >> 16) & 0xFF);
        put_uint8((data >> 8) & 0xFF);
        put_uint8(data & 0xFF);
    }
    
    void put_cr(void)
    {
        send_buf[send_buf_idx] = '\n';
        send_buf_idx++;
    }
    
    void send_msg(void)
    {
    
        pru_rpmsg_send(&transport, dst, src, send_buf, send_buf_idx);
        send_buf_idx = 0;
    }
    void main(){
        uint32_t i = 0;
        volatile uint32_t gpio;
    
        uint16_t len;
        volatile uint8_t *status;
    
        /* Enable the OCP master port of PRUSS1 to read external memories */
        //PRUSS1_CFG_SYSCFG &= ~(STANDY_INIT_BIT);
    
        /* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */
        CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
    
        /* Toggle GPO pins TODO: Figure out which to use */
        gpio = 0xFFFFF;
    
        /* Make sure the Linux drivers are ready for RPMsg communication */
        status = &resourceTable.rpmsg_vdev.status;
        while (!(*status & VIRTIO_CONFIG_S_DRIVER_OK))
        {
            ;
        }
    
        /* Initialize pru_virtqueue corresponding to vring0 (PRU to ARM Host direction) */
        pru_virtqueue_init(&transport.virtqueue0, &resourceTable.rpmsg_vring0, &CT_MBX.MESSAGE[MB_TO_ARM_HOST], &CT_MBX.MESSAGE[MB_FROM_ARM_HOST]);
        /* Initialize pru_virtqueue corresponding to vring1 (ARM Host to PRU direction) */
        pru_virtqueue_init(&transport.virtqueue1, &resourceTable.rpmsg_vring1, &CT_MBX.MESSAGE[MB_TO_ARM_HOST], &CT_MBX.MESSAGE[MB_FROM_ARM_HOST]);
    
        /* Create the RPMsg channel between the PRU and ARM user space using the transport structure.
         * The name 'rpmsg-pru' corresponds to the rpmsg_pru driver found
         * at linux-x.y.z/drivers/rpmsg/rpmsg_pru.c
         */
        while(pru_rpmsg_channel(RPMSG_NS_CREATE, &transport, CHAN_NAME, CHAN_DESC, CHAN_PORT) != PRU_RPMSG_SUCCESS)
        {
            ;
        }
    
        for (i = 0 ; i < 32 ; i++)
        {
            u32_buf[i] = ((PWMSS0.SYSCONFIG >> (31 - i)) & 0x1) + '0';
        }
        /* TODO: Create stop condition, else it will toggle indefinitely */
        while(1){
            __R30 ^= gpio;
            __delay_cycles(100000000);
            if(CT_MBX.MESSAGE[MB_FROM_ARM_HOST] == 1){
                /* Receive the message */
                if(pru_rpmsg_receive(&transport, &src, &dst, payload, &len) == PRU_RPMSG_SUCCESS){
                    if (len > 1)
                    {
                        switch (payload[0])
                        {
                        case '0':
                            put_str("0  ");
                            put_uint32(*(volatile uint32_t *)(0x4802A000));
                            put_cr();
                            send_msg();
                            break;
                        case '1':
                            put_str("1 ");
                            put_uint32(PWMSS1.IDVER);
                            put_cr();
                            send_msg();
                            break;
                        case '2':
                            put_str("2 ");
                            put_uint32(PWMSS2.IDVER);
                            put_cr();
                            send_msg();
                            break;
                        case '3':
                            put_str("3  ");
                            put_uint32(*(volatile uint32_t *)(0x4819C000));
                            put_cr();
                            send_msg();
                            break;
                        case '4':
                            put_str("4  ");
                            put_uint32(*(volatile uint32_t *)(0x48040000));
                            put_cr();
                            send_msg();
                            break;
                        }
                    }
                    /* Echo the message back to the same address from which we just received */
                    pru_rpmsg_send(&transport, dst, src, "finished\n", strlen("finished\n"));
                }
            }
        }
    }
    

    Best Regards

    Jerry

  • Hello Yordan,

    I think I found the reason.
    Before access peripherals in PRU, the peripherals should be enabled in DTS. I haven't do this before.

    I checked the PWM driver, found that probe function in driver called pm_runtime_enable function. I think that the root reason that it should be enable in DTS .

    Best Regards
    Jerry
  • Hi,

    This is very likely to be the root cause of your problem.
    If modules are NOT enabled in dts, then functional clocks to them are not enabled => their address space is NOT accessible, hence the L3 error:
    Data Access in User mode during Functional access

    The error is custom, because the addresses are present in the system, they are not protected, but they are NOT accessible since there is no functional clock to the modules.

    Best Regards,
    Yordan