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.

[参考译文] AM625:通过 R31检查 PRU 中断

Guru**** 2466550 points


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

https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1468291/am625-checking-pru-interrupts-through-r31

器件型号:AM625

工具与软件:

大家好!

从 AM62 TRM 来看、似乎可以直接在__R31的位30或31上检查 PRU 的某些中断。

具体而言、我希望能够 尽快检查 ECAP APCM CMPEQ_FLG (比较等态标志)的挂起情况、如果我至少可以在 R31[30]或 R31[31]上获得此中断标志的指示、这将是可行的。

*是否完全可以配置 PRU 中断控制器以便其中一个 R31位能够提供挂起中断的指示?

*如果是这样,用这种方式配置中断控制器会有什么程序? 我很乐意参考任何文档或指南。

谢谢!

António μ A

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

    读完很长时间后、我想我找到了该怎么做、但是除了如何分辨与 PRUSS 的每个中断匹配的事件 ID 之外。

    的符号  7.4.6.1.1:

    所有系统事件都是中断输入、事件0至31是 PRUSS 源的内部事件。 这是否意味着中断标志可用作可映射到主机中断并最终通过 R31[31、30]公开的事件?

    • 执行 ECINT_EN_FLG.CMPEQ_FLG 中断( 14.8.5.2.8.1. )具有允许将其路由到 R31的匹配事件 ID?
    • 如果是、在哪里可以找到外设中断和系统事件 ID 之间的映射?

    假设此中断具有可映射到主机中断的匹配事件 ID、使用 AM62 TRM (修订版 B)、我认为这是使用中的信息对其进行路由的工作流程 7.4.6.1.2.3

    1. 找到匹配的 CH_MAP_Regi ( 14.3.5.1.61. )并将 CH_MAP 位设置为  0b000 、以便将此中断映射到通道0
    2. INHINT_MAP_REG0 (14.3.5.1.77.1) 将 HINT_MAP0位设置为  0b0000 、因此通道0映射到主机通道0
    3. ENABLE_SET_INDEX_REG ( 14.3.5.1.48.1 )、写入0x1、以启用中断通道0
    4. INHINT_ENABLE_SET_INDEX_REG (.. 14.3.5.1.50.1 )、写入0x1、以启用主机通道0
    5. GLOBAL_ENABLE_HINT_REG ( 14.3.5.1.44.1. )、写入0x1、以启用任何主机中断。

    这是否是在 R31上公开中断的正确过程?

    关于这个问题的一点背景:

    我正在编写一个固件、该固件在驱动其他信号的同时产生稳定的时钟信号。 我希望此时钟尽可能无抖动、因此我将 ECAP 配置为 PWM、并设置产生所需输出频率的间隔。 大多数 μ naïve 固件实现、循环等待  ECEINT_EN_FLG.CMPEQ_FLG 、发送时钟脉冲、清除中断并返回到等待中断。

    这种方法的问题是检查中断寄存器(存储器映射)时需要几条汇编指令。 我不知道 PRU 组装、但我会想到这样的东西:

    • 将寄存器的地址加载到寄存器 A
    • 将寄存器 A 指向的内存加载到寄存器 B 中
    • 如果 B 的一个位未被置位则分支(返回到开头)

    根据固件到达该等待时间的先后顺序、它可能需要迭代、并且实际触发的中断与由固件检测和处理中断之间会存在不同的不匹配情况。

    因此有这个问题、 因为我知道快速分支指令直接在 PRU 内核寄存器上运行、并且需要单个周期来执行。 在这种情况下、只要固件在中断触发之前到达等待循环(非常容易确保)、时钟脉冲就始终与中断事件具有相同的偏移、从而使时钟几乎无抖动。

    虽然我的问题的范围主要是关于中断路由机制以及如何使用它们、但如果您有任何建议、可以将软件生成的时钟信号完美地同步到 ECAP PWM 计数器、我很高兴听到这些建议。

    注意:  我认为 PRU ECAP 的 PWM 输出可从 SoC 获取并直接用作时钟信号。 我们试图避免这种情况、是因为当发生我们无法在固件中预测的非常令人讨厌的情况时、可能会丢失同步。 关于这一备选办法的任何建议显然也是值得欢迎的。

    谢谢!

    António μ A

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

    您好、 António

    我已将您的主题分配给我们的专家、请让他们留出一些时间来审查您的方法并提供他们的意见。  

    此致、

    Nitika

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

    您好、 António

    基本概览:针对 INTC 理论
    PRU 中断控制器(INTC)有3个部分:主机中断、中断通道和系统事件(或中断源)。 有关更多详细信息、请参阅技术参考手册(TRM)的"PRUSS 本地 INTC"部分。

    您想要转到 PRU 的所有中断都必须路由到主机中断0或1、它们在 R31中显示为位30和位31。 可以将多个系统事件路由到单个通道、可以将多个通道路由到单个主机中断。

    我将链接一些资源:

    如何从 Linux 执行 PRU INTC 配置:
    https://software-dl.ti.com/processor-sdk-linux/esd/AM62X/10_01_10_04/exports/docs/common/PRU-ICSS/INTC_Configuration.html

    https://git.ti.com/cgit/pru-software-support-package/pru-software-support-package/tree/examples/am62x/PRU_Direct_Connect 0/ intc_map_0.h

    直接从 PRU 固件设置 INTC 寄存器:

    请参阅 AM335x PRU 动手实验的实验2。 概念适用、细节可能略有不同:
    https://software-dl.ti.com/processor-sdk-linux/esd/AM62X/10_01_10_04/exports/docs/common/PRU-ICSS/PRU-Hands-on-Labs.html#id25
    https://git.ti.com/cgit/pru-software-support-package/pru-software-support-package/tree/labs/Hands_on_Labs/lab_2/solution

    顺便说一下:汇编代码入门  

    我想您已经了解当前的 PRU 入门实验室、但为了以防万一、我们在此处提供了一些有关编写混合 C 语言和汇编语言代码的指导:
    https://software-dl.ti.com/processor-sdk-linux/esd/AM62X/10_01_10_04/exports/docs/common/PRU-ICSS/PRU-Getting-Started-Labs_Lab2.html

    生成 PWM 信号的设计?  

    我不确定我是否理解您的设计(看起来 PWM 实际上并未自行生成波形、而是想要针对每次时钟切换从 PRU 内核进行写入?)、因此我现在不会做太多评论。

    如果这是您的计划、请记住、也可以通过直接写入 PRU GPO 信号来生成波形(假设您有 PRU 时钟周期来在所需的脉冲宽度窗口内执行所有其他计算)。 如需查看我们在 AM335x 上编程的实现此功能的示例、请访问: https://git.ti.com/cgit/apps/tida01555/tree/PRU_ADS8688_Interface/PRU_ADS8688_Interface.asm
    (整个系统环境位于此处: https://www.ti.com/tool/TIDA-01555 >设计指南)

    此致、

    Nick

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

    您好、Nick、感谢您的分享。

    实验室链接对于编写我自己的 AM62等效解决方案非常有用。 遗憾的是,它还没有工作。

    首先要说的是:ECAP 中断的事件 ID 是什么?在哪里可以找到它? 我到处搜索这个,没有找到任何东西。 我怀疑这可能是来自 AM335x 的 TRM 的事件编号15、但我不能简单地假设它会很高兴、因此我运行了一个测试:

    int main()
    {
        CT_ECAP.ECCTL2_ECCTL1_bit.TSCNTSTP = 0;
        CT_ECAP.CAP1 = 61; // 333MHz/62 = 5.37MHz
        CT_ECAP.CAP2 = 31; // 50% Duty Cycle
        CT_ECAP.CNTPHS = 0;
        CT_ECAP.ECCTL2_ECCTL1_bit.CAP_APWM = 1;
        CT_ECAP.ECCTL2_ECCTL1_bit.APWMPOL  = 1;
        CT_ECAP.ECCTL2_ECCTL1_bit.SYNCI_EN = 0;
        CT_ECAP.ECCTL2_ECCTL1_bit.SYNCO_SEL = 0x3;
        CT_ECAP.ECCTL2_ECCTL1_bit.FREE    = 1; // run always, regardless of suspend
        CT_ECAP.ECFLG_ECEINT_bit.EN_PRDEQ = 1;
        CT_ECAP.ECFLG_ECEINT_bit.EN_CMPEQ = 0;
    
        __R31 = 0x00000000;
        CT_INTC.CH_MAP_REG3_bit.CH_MAP_15 = 0;
        CT_INTC.HINT_MAP_REG0_bit.HINT_MAP_0 = 0;
        CT_INTC.STATUS_CLR_INDEX_REG_bit.STATUS_CLR_INDEX = 15;
        CT_INTC.ENABLE_SET_INDEX_REG_bit.ENABLE_SET_INDEX = 15;
        CT_INTC.HINT_ENABLE_SET_INDEX_REG_bit.HINT_ENABLE_SET_INDEX = 0x1;
        CT_INTC.GLOBAL_ENABLE_HINT_REG_bit.ENABLE_HINT_ANY = 1;
    
    
        CT_ECAP.ECCTL2_ECCTL1_bit.TSCNTSTP = 1;
    
    
        while (1) *((uint32_t* volatile) 0x9c600000) = __R31;
    }

    该固件在启动后执行的第一个操作。 除非另有说明、否则之前在 PRU 上没有运行任何其他内容。

    • 如果、而不是设置  TSCNTSTP 最终目的  1. 设置为多高  0、  RAW_STATUS_REG0 (0x30060200)包含该值  0x00000000
    • 所需的工具 TSCNTSTP 设置为 1. RAW_STATUS_REG0  包含该值 0x00008000。  这似乎是一个可靠的指示、表明 ECAP 中断为事件15。  您能确认吗?

    现在对于部件:

    上面的代码似乎直接使 https://git.ti.com/cgit/pru-software-support-package/pru-software-support-package/tree/labs/Hands_on_Labs/lab_2/solution 适应 AM62、但我要将事件路由到主通道0。 然而、从 R31复制到存储器位置的值始终读取0。

    我还注意到、如果我在启动后第二次以上运行固件 RAW_STATUS_REG0 保持在 0x00000000  以及所有后续运行中的测量值。 换句话说、只有在第一次执行测试时、我才会看到 RAW_STATUS_REG0、指示活动事件15。  我缺少什么?

    我得承认、在阅读 AM62 TRM 上的 PRU 文档时遇到了很多麻烦。 例如:

    *没有 INTC 支持的事件 IDS 列表,并清楚地描述了它们的范围(哪些中断导致所述事件被触发)

    *对于如何映射中断、没有明确、客观、程序性的解释。 我知道 INTC 部分确实提到了如何执行此映射、但对于不了解 PRU 内部器件的人而言、这些指令的含义尚待解释。 当某件事情无法 正常工作时、对问题进行故障排除会变得非常令人沮丧。

    该文档在 TRM 上似乎相当明确地说明了外设是事件源 、并且事件可以路由到 R31位30和31。 因此、请帮助我了解如何:

    *弄清哪个事件与 PRU 的 ECAP PWM 周期中断标志相对应;

    *了解如何将此事件映射到 INTC 的主机通道0

    *了解如何在设置该事件后清除该事件

    如果我可以做些什么来使其更易于理解、请告诉我。

    生成方波是如何满足这一切的? 简而言之、固件应等待 ECAP 中断。 当中断命中时、固件会生成一个时钟脉冲(设置 H、稍后设置 L)并返回到等待状态。 如果通过单周期分支指令执行等待(使用基于寄存器的检查可以实现这一点)、那么我们就能够生成与 ECAP 间隔同步的输出时钟、几乎没有抖动。

    此致、

    António μ A

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

    终于!!!

    我想出了如何完成这一点,以及为什么我的第一次尝试失败。

    主要问题是:

    • 我所面临的核心问题是以不正确的顺序清除中断。  在清除 INTC 上的事件之前、清除 ECAP 中断绝对至关重要。 如果未遵循此顺序、它看起来像是 PRU 挂起或 INTC 中断不会再次触发 (我没有进行足够的测试来了解真正发生了什么)。

    障碍:

    • 这也许只是我的看法、但我发现 AM62 TRM 的 PRU 部分几乎无法跟进。 它似乎是由机器生成的、而不是经过单次校对通行证、这将很容易发现可以在源材料中解决的措辞问题:
    • AM62 TRM 上缺少系统事件列表。 我很幸运在 AM33 PRU-ICSS 上找到了一个、结果也适用于 AM62。 我还使用原始旗帜来尝试和猜测事件的内饰...
    • 在 AM62 TRM 中、不清楚某些寄存器是对位字段还是整数输入进行操作。 AM33 TRM 在这方面要清晰得多、  静止 、 CT_INTC.HIEISR |=(1 << 0)中的 lab2示例;/*TODO:启用合适的事件*/ 语句似乎暗示位字段、当我的所有测试(以及我对 TRM 的解释)表明这是一个必须写入的整数值时。
    • AM62 TRM 上的寄存器说明缺少有关其用途的任何说明。 只有字段是(机器?) 以一种很难遵循的方式进行记录。

    很抱歉听到负面的声音,你们可能有大量的东西需要摆弄和支持,它不容易跟上如此广泛和复杂的文档,更不用说花哨的用例,客户决定尝试基于它... 如果在任何时候对文档进行审查和更新、我会尝试提出一些可能需要改进的地方。

    话虽如此、实践实验室示例对于浏览这些疑虑并开始了解事情的实际工作原理非常重要。 再次感谢您的宝贵意见!

    在我们开始使用它之后、PRU 实际上是一个可供使用的有趣外设。 它的实时功能使它成为一个真正有趣的选择,考虑一些任务,它甚至可能看到更多的使用,如果习惯它的功能和文档怪物不是如此陡峭的山。

    解决方案:

    对于任何可能需要有关 AM62 INTC 路由和处理的人员、以下是最终解决方案:

    #include <stdint.h>
    #include <pru_cfg.h>
    #include <pru_ctrl.h>
    #include <pru_intc.h>
    #include <pru_ecap.h>
    
    volatile register uint32_t __R30;
    volatile register uint32_t __R31;
    
    #define BIT(n) (1u<<n)
    
    static inline void set_clk_l()
    {
        __R30 &= ~BIT(6); // modify as needed
    }
    
    static inline void set_clk_h()
    {
        __R30 |= BIT(6); // modify as needed
    }
    
    static inline int has_ecap_irq()
    {
        return __R31&BIT(30);
    }
    
    static inline void clear_ecap_irq()
    {
        /* The order **MATTERS!!!**
         * FIRST Clear IRQ on ECAP so that INTC doesn't see it active
         * when we go about clearing event 15 on the INTC.
         *
         * Not following this sequence somehow prevents R31.t30
         * interrupt (host 0) from triggering again
         */
        CT_ECAP.ECCLR = BIT(6) | BIT(0);
        CT_INTC.STATUS_CLR_INDEX_REG_bit.STATUS_CLR_INDEX = 15;
    }
    
    int main()
    {
        // Setup ECAP as PWM. Do not start it...
        CT_ECAP.ECCTL2_ECCTL1 = 0x02c0c000;
        CT_ECAP.CAP1 = 61; // 333MHz/62 = 5.37MHz
        CT_ECAP.CAP2 = 0; // Duty cycle is ignored.
        CT_ECAP.CNTPHS = 0;
        // Make sure IRQs
        // Make sure all ECAP interrupts are cleared, otherwise
        // clearing INTC event will **not** work.
        CT_ECAP.ECCLR = 0xff;
        CT_ECAP.ECFLG_ECEINT = BIT(6); // Enable Period interrupt
    
        // Setup INTC:
        // ECAP_PWM_PERIOD --> INTC --> R31.t30
        // PWM_PERIOD_INT (15) --> channel_0 --> host_channel_0 --> R31.t30
        __R31 = 0x00000000;
        // all registers below operate on indeces.
        // THEY ARE NOT BIT FIELDS!!!
        CT_INTC.CH_MAP_REG3_bit.CH_MAP_15 = 0;
        CT_INTC.HINT_MAP_REG0_bit.HINT_MAP_0 = 0;
        CT_INTC.STATUS_CLR_INDEX_REG_bit.STATUS_CLR_INDEX = 15;
        __delay_cycles(5);
        CT_INTC.ENABLE_SET_INDEX_REG_bit.ENABLE_SET_INDEX = 15;
        CT_INTC.HINT_ENABLE_SET_INDEX_REG_bit.HINT_ENABLE_SET_INDEX = 0;
        CT_INTC.GLOBAL_ENABLE_HINT_REG_bit.ENABLE_HINT_ANY = 1;
    
        // Start the ECAP counter, APWM node
        CT_ECAP.ECCTL2_ECCTL1 = 0x02d0c000;
    
        for (;;) {
    
            // single cycle-based wait for IRQ (asm: QBBC)
            while ( ! has_ecap_irq() )
                ;
    
            set_clk_h();
            clear_ecap_irq();
            __delay_cycles(5);
            set_clk_l();
            // Placeholder for custom code...
        }
    }

    简而言之、此代码运行基于单周期的 ECAP 中断等待(通过将中断路由到 R31来实现)、并在检测到中断后立即生成 CLK 上升沿。 剩余的时间可用于运行自定义应用程序代码。

    潜在死亡陷阱:

    如果循环的迭代时间太长、且到达 R31检查时间太晚、则似乎无法正确检查 Host 0中断。 我找不到合理的解释。 如果有人知道、我很乐意知道。 同时、绝对重要的是、循环恰好在设置主机0中断时或之前返回到检查 R31。

    谢谢、

    António μ A

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

    您好、 António

    很高兴你能让它工作!

    感谢文档反馈。 我们当然仍然在努力改进 PRU 的 TRM 文档。 遗憾的是、这是一个相当慢的过程。 我已经针对缺失的 PRU_CTRL 寄存器(这些寄存器)归档了很多错误、并且每个寄存器部分的顶部都没有寄存器摘要、因此可以更轻松地导航至所需的特定寄存器。

    此处列出了系统事件(不过我们在不同的位置使用不同的术语):
    TRM 表"PRUSS IP 内部中断"
    其中 ECAP 是条目15

    我对 TRM 的控制有限。 我可以针对它归档错误、但到目前为止就这些。 这也是我的团队开始在单独的 PRU 学院工作的部分原因、该学院更专注于教学、因此我们可以更轻松地介绍这些内容。

    如果您记得 AM335x 中哪些通用寄存器组的记录比 AM62x 中更好、我可以获取该信息并提交一些额外的错误报告、以确保在团队最终回圈为 AM62x 发布新的 TRM 版本时、这些描述得到了触及。

    此致、

    Nick

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

    谢谢 Nick!

    总的来说、我想说 AM335x 手册在总结中能够更好地描述寄存器的用途和用法。

    应该对这些东西使用一种通用的命名法。 例如、AM62x TRM 的图7-42显示了"系统事件的通道映射""Sys_event *"。  诚然、它 在部分也提到了"内部中断"  7.4.6.1.  但是  7.4.6.1.1 7.4.6.1.2  用 "系统事件"来描述事情。 这就使得很难猜出什么来搜索内容、也使得我们更容易错过这些内容、然后在 TI E2E 上寻找实际上在 TRM 中的答案  

    我对解决这些问题的建议是:

    • 对系统中断使用通用命名法(任何东西都没问题、只要 同一个东西的名称始终相同)
    • 指向表格的链接  7-64 。   7.4.6.1.
    • 的说明  7.4.6.2. 使用与寄存器部分中的寄存器名称和字段名称相匹配的寄存器名称和字段名称、使搜索更简单(可能吗?)
    • 提供每个寄存器的摘要、以阐明其作用或工作原理

    衷心感谢您花时间阅读并确认这些文档问题。 感谢您对其进行深入研究、并为我最终找到解决方案提供了宝贵的指导。

    谢谢、

    António μ A