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:从 PRU 访问外设时出错

Guru**** 2589245 points


请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/processors-group/processors/f/processors-forum/590303/processor-sdk-am437x-error-when-accessing-peripherals-from-pru

器件型号:PROCESSOR-SDK-AM437X

您好!

我尝试在 PRU 中访问 ARM 的外设、但收到错误。

使用的 SDK 版本 I 是 ti-processor-sdk-linux-rt-AM437X-EVM-02.00.02.11。

我使用的 PRU 是 PRUSS0。  

我已经通过以下代码启用了 PRUSS1的 OCP 主器件、RPMsg 现在正在工作。

/*启用 PRUSS1的 OCP 主端口以读取外部存储器*/

PRUSS1_CFG_SYSCFG &=~(standy_init_bit);

PRU 中的访问代码为: PUT_UINT32 (*(volatile UINT32_t *)(0x481CC000));


我尝试访问 DMTIMER2、I2C、ePWM0~2。  只能访问 DMTIMER2。

错误消息如下所示:

[234.724970]------ [在此处剪切]-----
[234.728502]警告:CPU:0 PID:22 at drivers/bus/omap_l3_oc.c:147 l3_interrupt_handler+0x25c/0x36c ()
[234.737069] 440000.OCP:L3自定义错误:主 ICSS0目标 L4_PER_1 (读取):在功能访问期间以用户模式访问数据
[234.738798] Remotepproc0:踢 VQ 指数:0
[234.738804] remoteproc0:在 PRU0上踢 vqid 0
[234.738815] virtio_rpmsg_bus virtio0:rpmsg 主机处于联机状态
[234.738895] remoteproc0:registered virtio0 (类型7)
[234.738963] PRU-rproc 54474000.pru0:PRU rproc 节点/ocp/pruss@54440000/pru0@54474000探测成功
[234.766900]链接的模块:mod_pru_rproc (O) mod_pruss (O) mod_SRAM (O) mod_rtc (O) mod_powerfail (O) mod_iobus (O) mod_f耦 合器(O)
[234.777820] CPU:0 PID:22 Comm:IRQ/19-L3-APP-I 被污染:G O 4.1.20-rt23-Yocto-preempe-RT #51
[234.787646]硬件名称:通用 AM43 (平展器件树)
[234.792580][ ](展开回扫)从[ ](show_stack+0x20/0x24)
[234.800408][ ](show_stack)从[ ](dump_stack+0x24/0x28)
[234.806393][ ](dump_stack)从[ ](warn_slicpath_common+0x98/0xc4)
[234.813892][ ](warn_slowpath_common)、来自[ ](WARN_RASPH_FMt+0x40/0x48)
[234.822640][ ](warn_slowpath_fmt)、来自[ ](L3_INTERRUPT_Handler+0x25c/0x36c)
[234.830669][ ](L3_interrupt_handler)、来自[ ](IRQ_Forced_thread_fn+0x30/0x64)
[234.839662][ ](IRQ_Forced_thread_fn)、来自[ ](IRQ_THREAD+0x11c/0x1fc)
[234.847051][ ](IRQ_THread)、来自[ ](kthread+0xd4/0xec)
[234.853043][ ](kthread)、来自[ ](RET_FANK_F叉+0x14/0x2C)
[234.860195]--[结束线迹0000000000000002 ]--

可以给我一些建议吗?

此致
Jerry

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    软件团队已收到通知。 他们将在这里作出回应。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Jerry、

    您尝试访问哪个 I2C? ePWM 是否由另一个内核进程使用?

    此外、您是否确定访问了正确的地址空间?

    此致、
    Yordan
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Yordan、您好!  

    很抱歉耽误你的答复。

    根据  AM437x 规范、我尝试访问0x4802A000 (I2C1)、0x4819C000 (I2C2)和0x48040000 (DMTIMER2)。

    只有 DMTIMER2可以正常访问。 另外两个访问将导致异常。  

    我确信 I2C1不会被 ARM 内核使用、因为它在 DTS 中的状态是"disabled"。

    我操作的整个过程如下:

    1. 通过"rpmsg"向 PRU 发送消息。 命令为  echo "0"> rpmsg_pru30
    2. 当 PRU 收到消息时、它将尝试根据消息访问变体器件。
    3. 从串行端口获得异常。  

    下面是 PRU 中的代码。 请忽略 PUP**方法。 它们仅用于格式化消息并将其发送到 rpmsg。  

    while (1){

    _R30 ^= GPIO;
    _delay_cycles (100000000);
    if (CT_MBX.MESSAGE[MB_From_ARM_HOST]= 1){
    /*接收消息*/
    if (PRU_rpmsg_receive (&transport、&src、&dst、PAYLOADY、&len)== PRU_RPMSG_SUCCESS){
    如果(len > 1)
    {
    开关(有效载荷[0])
    {
    情况"0":
    PUT_STr ("0");
    PUT_UINT32 (*(volatile UINT32_t *)(0x4802A000));
    PUT_CR ();
    send_msg();
    中断;
    案例"1":
    PUT_STr ("1");
    PUT_UINT32 (PWMSS1.IDVER);
    PUT_CR ();
    send_msg();
    中断;
    案例"2":
    PUT_STr ("2");
    PUT_UINT32 (PWMSS2.IDVER);
    PUT_CR ();
    send_msg();
    中断;
    案例"3":
    PUT_STr ("3");
    PUT_UINT32 (*(volatile UINT32_t *)(0x4819C000));
    PUT_CR ();
    send_msg();
    中断;
    案例"4":
    PUT_STr ("4");
    PUT_UINT32 (*(volatile UINT32_t *)(0x48040000));
    PUT_CR ();
    send_msg();
    中断;
    }
    }
    /*将邮件回送至我们刚刚收到的同一地址*/
    PRU_rpmsg_send (&transport、dst、src、"finished"、strlen ("finished\n"));
    }
    } 

    此致

    Jerry

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好!

    此函数:
    PUT_UINT32 (*(volatile UINT32_t *)(0x481CC000))
    在 Linux 用户空间中执行、对吧?

    我不确定这是如何定义的。 它是否将物理地址0x481CC000重新映射到虚拟内核空间? 请参见 devmem2的实现、了解如何在 Linux 环境中将物理地址重新映射到虚拟地址。

    此致、
    Yordan
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Yordan、您好!

    所有代码都在 PRU 上运行、而不是在 ARM 上运行。  我正在尝试从 PRU 访问外设。

    我为您提供我拥有的所有代码。

    /*
    版权所有(C) 2015德州仪器(TI)公司- http://www.ti.com/
    *
    
    *
    *只要
    满足以下条件*、就允许以源代码和二进制形式重新分发和使用*进行修改或不进行修改:
    *
    ***源代码的重新分发必须保留上述版权
    声明*、此条件列表和以下免责声明。
    *
    ***二进制形式的再发行必须在
    
    *
    发行版随附的*文档和/或其他材料中复制上述版权声明、本条件列表和以下免责声明。
    *
    ***未经
    
    事先书面许可、不得使用德州仪器公司的名称或*其贡献者的名称认可或推广从本软件衍生的产品*。
    *
    *本软件由版权所有者和贡献者
    *按原样"提供、
    
    且不承认任何明示或暗示的保证、包括但不限于*特定用途*的适销性和适用性的暗示保证。 在任何情况下、版权
    *所有者或贡献者都不对任何直接、间接、偶然、
    *特殊、模范、 或相应的损害(包括但不
    限于*采购替代产品或服务;丧失使用、
    *数据或利润; 或业务中断)、但出于
    任何*责任理论、无论是合同、严格责任还是侵权
    行为*(包括疏忽或其他原因)、即使
    被告知可能会造成此类损坏、也是出于此类责任理论。
    */
    
    #include 
    #include 
    #include "resource_table_empty.h"
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    /*用于 RPMsg 的邮箱在 Linux 器件树中定义
    * PRU0使用邮箱2 (来自 ARM)和3 (至 ARM)
    * PRU1使用邮箱4 (来自 ARM)和5 (至 ARM)
    */
    #define MB_TO_ARM_HOST 3
    #define MB_From_arm_host 2
    
    /*
    *使用名称'rpmsg-PRU'将探测在
    linux-x.y.z/drivers/rpmsg/rpmsg_pru.c
    中找到的 rpmsg_PRU 驱动程序*
    "rpmsg-PRU"
    #define CHAN_DESC "通道30"#define
    CHAN_PORT 30
    
    //*
    用于确保 Linux 驱动程序已准备好进行 RPMsg 通信
    *可在 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 pruu_rpmsg_transport;
    uint8_t PAY负载[32];
    
    易失性寄存器 uint32_t _r30;
    易失性寄存器 uint32_t _r31;
    
    uint8_t u32_buf[32];
    
    uint8_t H2a[]、'2'、'0'、'0'、'0'、'0'、 '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 pot_uint8 (uint8_t 数据)
    {
    send_buf[send_buf_idx]= h2a[数据/16];
    send_Buf_idx++;
    
    send_buf[send_buf_idx]= h2a[数据% 16];
    SEND_BUF_IDx++;
    }
    
    void PUT_UINT32 (uint32_t 数据)
    {
    PUT_uint8 ((数据>> 24)& 0xFF);
    PUT_uint8 ((数据>> 16)& 0xFF);
    PUT_uint8 ((数据>> 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 (传输、dst、src、send_buf、send_buf_idx);
    send_Buf_idx = 0;
    }
    void main(){
    uint32_t i = 0;
    易失性 uint32_t GPIO;
    
    uint16_t len;
    volatile uint8_t *status;
    
    /*启用 PRUSS1的 OCP 主端口以读取外部存储器*/
    //PRUSS1_CFG_SYSCFG &=~(standy_init_bit);
    
    /*清除 SYSCFG[STANDBY_INIT]以启用 OCP 主端口*/
    CT_CFG.SYSCFG_BIT.STANDBY_INIT = 0;
    
    /*将 GPO 引脚切换为执行以下操作:确定要使用的对象*/
    GPIO = 0xFFFFFFF;
    
    /*确保 Linux 驱动程序已准备好进行 RPMsg 通信*/
    状态=&resourceTable.rpmsg_vdev.status;
    while (!(* status & virtio_config_S_driver_OK))
    {
    ;
    }
    
    /*初始化与 vring0相对应的 PRU_virtqueue (PRU 至 ARM 主机方向)*/
    PRU_virtqueue_init (&translation.virtqueue0、&resourceTable.rpmsg_vring0、&CT_MBX.MESSAGE[MB_TO_ARM_HOST]、&CT_MBX.MESSAGE[MB_FIT_AND_HOST]);
    /*初始化与 vring1相对应的 PRU_virtqueue (ARM 主机到 PRU 方向)*/
    PRU_virtQueue_init (&translation.virtqueue1、&resourceTable.rpmsg_vring1、&CT_MBX.MESSAGE[MB_TO_ARM_HOST]、&CT_MBX.MESSAGE[MB_FIT_AND_HOST]);
    
    /*使用传输结构在 PRU 和 ARM 用户空间之间创建 RPMsg 通道。
    *名称'rpmsg-PRU'对应于找到的 rpmsg_PRU 驱动程序
    *位于 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)
    {
    ;
    }
    
    对于(i = 0;i < 32;i++)
    {
    u32_buf[i]=((PWMSS0.SysConfig >>(31 - i))& 0x1)+'0';
    }
    /* TODO:创建停止条件、否则它将无限期切换*/
    while (1){
    _R30 ^= GPIO;
    _delay_cycles (100000000);
    if (CT_MBX.MESSAGE[MB_From_ARM_HOST]= 1){
    /*接收消息*/
    if (PRU_rpmsg_receive (&transport、&src、&dst、PAYLOADY、&len)== PRU_RPMSG_SUCCESS){
    如果(len > 1)
    {
    开关(有效载荷[0])
    {
    情况"0":
    PUT_STr ("0");
    PUT_UINT32 (*(volatile UINT32_t *)(0x4802A000));
    PUT_CR ();
    send_msg();
    中断;
    案例"1":
    PUT_STr ("1");
    PUT_UINT32 (PWMSS1.IDVER);
    PUT_CR ();
    send_msg();
    中断;
    案例"2":
    PUT_STr ("2");
    PUT_UINT32 (PWMSS2.IDVER);
    PUT_CR ();
    send_msg();
    中断;
    案例"3":
    PUT_STr ("3");
    PUT_UINT32 (*(volatile UINT32_t *)(0x4819C000));
    PUT_CR ();
    send_msg();
    中断;
    案例"4":
    PUT_STr ("4");
    PUT_UINT32 (*(volatile UINT32_t *)(0x48040000));
    PUT_CR ();
    send_msg();
    中断;
    }
    }
    /*将邮件回送至我们刚刚收到的同一地址*/
    PRU_rpmsg_send (&transport、dst、src、"finished"、strlen ("finished\n"));
    }
    }
    
    }
    

    此致

    Jerry

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    Yordan、您好!

    我认为我找到了原因。
    在访问 PRU 中的外设之前、应在 DTS 中启用外设。 我以前没有这么做。

    我检查了 PWM 驱动程序、发现驱动程序中的探针函数称为 pm_runtime_enable 函数。 我认为在 DTS 中启用它的根本原因。

    此致
    Jerry
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好!

    这很可能是问题的根本原因。
    如果在 DTS 中没有启用模块、那么它们的功能时钟不被启用=>它们的地址空间不可访问、因此 L3错误:
    在功能访问期间以用户模式访问数据

    该错误是自定义的、因为地址存在于系统中、因此它们不受保护、但由于模块没有功能时钟、因此无法访问。

    此致、
    Yordan