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.

[参考译文] CCS/MSP430FR5969:了解MPU

Guru**** 2386620 points
Other Parts Discussed in Thread: MSP430FR5969, MSP430FR5959
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/643170/ccs-msp430fr5969-understanding-mpu

部件号:MSP430FR5969
主题中讨论的其他部件: MSP430FR5959

工具/软件:Code Composer Studio

我认为如果覆盖代码会使处理器过了,那就更好了。 于是我开始看MPU。 关于它应该如何工作,有各种信息。 但是我有点困惑它是如何自动工作的。 这意味着只要您在项目的CCS常规属性中勾选了"启用MPU",它就应该"正常工作"而不做任何事情。 但是我不太相信...  

这是MSP430FR5969的默认链接器文件的MPU位的外观:

#ifdef _MPU_enable
#define MPUPW (0xA500)/* MPU Access Password */
#define MPUENA (0x0001)/* MPU Enable */
#define MPULOCK */
#define MPUSEGIE (0x0010)/* MPU Enable NMI on segment violation */

_MPU_enable;
//段定义
#ifdef _mpu_manual //用于在GUI中选择的自定义尺寸
MPU_SEGMENT_Border1 =_MPU_SEGB1 >> 4;
MPU_SEGMENT_Border2 =_MPU_SEGB2 >> 4;
MPU_SAM_VALUE =(_MPU_SAM0 <<12)|(_MPU_SAM3 <<8)|(_MPU_SAM2 <<4)|_MPU_SAM1;
#else // Linker生成的自动尺寸
#ifdef _ipe_enable //如果项目中也使用IPE
//seg1 =任何读+写持久性变量
//seg2 = ipe =读+写+执行访问
//seg3 =代码,只读+执行
MPU_SEGMENT_Border1 = fram_IPE_START >> 4;
MPU_SEGMENT_Border2 = fram_rx_start >> 4;
MPU_SAM_VALUE = 0x1573;// Info R,Seg3 RX,Seg2 rwx,Seg1 RW
#否则
MPU_SEGMENT_Border1 = fram_rx_start >> 4;
MPU_SEGMENT_Border2 = fram_rx_start >> 4;
MPU_SAM_VALUE = 0x1513;// Info R,Seg3 RX,Seg2 R,Seg1 RW
#endif
#endif
#ifdef _mpu_lock
#ifdef _mpu_enable_NMI
MPU_ctl0_value = MPUPW | MPUENA | MPULOCK | MPUSEGIE;
#否则
MPU_ctl0_value = MPUPW | MPUENA | MPULOCK;
#endif
#else
#ifdef _mpu_enable_NMI
MPU_ctl0_value = MPUPW | MPUENA | MPUSEGIE;
#否则
MPU_ctl0_value = MPUPW | MPUENA;
#endif
#endif
#endif 

首先,从链接程序文件中可以看出,如果不在某处定义_MPU_ENable_NMI,MPU基本上不执行任何操作。 是否有一个GUI tickbox用于此操作? 在我看来,只有一个被保护的内存区域,称为fram_rx_start。 那么实际上,在FRAM和FRAM2之间拆分代码不是按预期工作的吗? 默认链接程序文件在FRAM2和FRAM之间分割代码。 "FRAM_Rx_start"似乎是在IPENCAPSULATED_MEMORY的末尾自动设置的。 由于我没有激活IPE,是否会将fram_rx_start分配给FRAM2的开头?

如果IDE实际上像它所暗示的那样聪明,那么将_MPU_ENable_NMI放入“预定义符号”并向NMI ISR添加一行就足够了,如MPU driverlib示例中所示?

或者,我可以在 链接程序文件中执行整个日志并定义自己的内存区域,以此类推。 这就是我在开始思考MPU如何准确设置之前所做的事情。 内存上的标志实际上有什么功能吗? 我修改了链接程序文件,为代码/常量/ISR分配4+16kB,如下所示:

内存
{
SFR :原点= 0x0000,长度= 0x0010
外围设备_8位 :原点= 0x0010,长度= 0x00F0
外围设备_16BIT :原点= 0x0100,长度= 0x0100
RAM :原点= 0x1C00,长度= 0x0800
信息A. :原点= 0x1980,长度= 0x0080
信息 :原点= 0x1900,长度= 0x0080
信息 :原点= 0x1880,长度= 0x0080
信息 :原点= 0x1800,长度= 0x0080
FRAM :原点= 0x4400,长度= 0xAF80
FRAMRO (RXI)			:原点= 0xF380,长度= 0x0C00
FRAMRO2 (RXi) :原点= 0x1万,长度= 0x4000 

这将再次暗示设置(RXi)将自动使该区域"脱离",但是... 在标志的主题上,初始化标志我不确定。 何时初始化? 启动后? 在闪烁期间,#pragma持久性内容是否初始化一次?

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

    您好,Olli,

    首先,尝试破解链接程序文件初始化MPU和分区内存的方式可能会非常混乱,正如您已经发现的那样。 此外,许多变量似乎是"自动"设置的,但我会尝试分解一切的工作方式。

    有三种方法可以设置MPU,但让我们先从编译器在默认情况下如何处理它开始:

    默认情况下,编译器将创建3个具有以下权限和地址的内存段

    1. 段1:持久变量,I/O缓冲区,动态内存分配
      1. 注意:只有当代码中存在这些变量时,才需要执行此操作。 否则长度= 0
      2. 权限:可读和可写
      3. 起始地址:FRAM的起始地址
      4. 结束地址:段2的开始(与0x0400对齐)
    2. 段2:知识产权封装(IPE)代码
      1. 注:仅当启用IPE时才会出现此情况。 否则截面长度= 0。
      2. 权限:可读,可写和可执行
      3. 起始地址:段1结束或FRAM起始(如果段1长度=0)。
      4. 结束地址:段3的开始(与0x0400对齐)
    3. 段3:代码
      1. 权限:可读和可执行
      2. 起始地址:第2段结束,第1段结束(如果第2段长度=0),或第1段和第2段长度=0时FRAM开始
      3. 结束地址:FRAM2结束


    所有这些段边界都由链接程序文件中的此部分代码“自动”确定:

    GROU(LW_IPE)
    {
    
    GROUGE(READ_WRIT_MEMORE)
    {
    
    .TI.PERTI.PERTI={}/* for #pragma persistent */
    .CIO:{}/* C I/O Buffer*/
    .sysmem:{}/*动态内存分配区*/}
    PALIIGN(0x0400),run_start(fram_ENRW_start) Group_.IPE*
    
    
    
    
    :{_IPE_IPE*:{_IPE_IPE*:{_IPE_IPE*:{} CAST_IPE*:{_IPE*_IPE*:{数据结构/.IPE*/.IPE*:
    
    {}/* IPE保护常量*/
    .IPE:_ISR :{}/* IPE ISRs*/
    } PALIGN(0x0400),run_start(fram_IPE_start) run_end(fram_IPE_end) run_end(fram_rx_start)
    
    }> 0x4000 

    run_start宏将相应组的起始地址分配给传递给它的变量。 同样,run_end宏将相应组的结束地址分配给传递给它的变量。 最后,PALIGN宏将内存与传递给它的边界对齐。 例如,假设您有500kB的持久性变量,2kB的IPE数据和10kB的代码。 如果使用MSP430FR5959,则上述变量将按如下方式分配:

    • FRAM_RW_START = 0x4400
    • FRAM_IPE_START = 0x4800 (由于0x0400对齐)
    • frm_rx_start = 0x5000

    这将使分段边界如下所示:

    • 网段1 (RW):0x4400到0x47FF
    • 段2 (rwx):0x4800至0x4FFF
    • 网段3 (RX):0x5000至0x13FFF

    然后,此代码将在链接程序文件中设置此链接:

    //段定义
    #ifdef _MPU_MANUAL //用于在GUI中选择的自定义尺寸
    MPU_SEGM1 =_MPU_SEGB1 >> 4;
    MPU_SEGM2 =_MPU_SEGB2 >> 4;
    MPU_SAM_VALUE =(_MPU_SAM0 <12)|(_MPU_SAM3 =_SAM3;<MPU_4)
    #else //链接器生成的自动大小
    #ifdef _ipe_enable //如果项目中也使用IPE
    //seg1 =任何读取+写入持久性变量
    //seg2 = ipe =读取+写入+执行访问
    //seg3 =代码,只读+执行
    MPU_segment Border1 = fram_ipe_start >> 4;
    MPU_SEGM_Border2 = fram_rx_start >> 4;
    MPU_SAM_VALUE = 0x1573;// Info R,Seg3 RX,Seg2 rwx, Seg1 RW
    #else
    MPU_SEGM_Border1 = fram_Rx_start >> 4;
    MPU_SEGM_Border2 = fram_Rx_start >> 4;
    MPU_SAM_VALUE = 0x1513;// Info R,Seg3 RX,Seg2 R, Seg1 RW
    #endif
    #endif 

    您会注意到,启用IPE时,此代码集的句段1边界到IPE节的开头,句段2边界到代码段的开头。 这将有效地创建3个内存部分,如下所示:

    • 0x4400到fram_ipe_start
    • FRAM_IPE_START至fram_rx_start
    • FRAM_Rx_start到FRAM2的末尾

    您还会注意到,如果IPE被禁用,MPU_SEGMENT_Border1 = MPU_SEGMENT_Border2。 这是有目的且有效地将IPE段(段2)的长度设置为0。 在此方案中,创建了3个部分,如下所示:

    • 0x4400到fram_rx_start
    • frm_rx_start到fram_rx_start (长度为0)
    • FRAM_Rx_start到FRAM2结束

    此外,允许编译器设置MPU时,默认情况下,MPU生成的非屏蔽中断(NMI)将被禁用。 这并不意味着MPU不保护这些内存部分。 这只是意味着如果尝试访问冲突,但仍保护内存不被错误访问,则不会生成任何中断。 如您所述,您可以启用NMI,同时也允许编译器通过预定义_MPU_ENable_NMI来处理MPU设置。 在这种情况下,您需要为代码中的NMI向量提供中断服务例程。

    最后,您会注意到此代码为编译器分配的部分设置了权限:

    MPU_SAM_VALUE = 0x1513;// Info R,Seg3 RX,Seg2 R,Seg1 RW 

    您可以并且应该更改此设置以满足应用程序的要求。

    设置MPU的第二种方法是在项目属性中选择“手动指定内存段边界和访问权限”单选按钮:

    您可以在此处设置边界并访问应用程序所需的任何内容。 唯一的区别是节可能没有优化到每个段所需的大小,就像编译器处理它一样。 此外,您可以选择“Lock configuration”(锁定配置)和/或“Enable NMI”(启用NMI)复选框,以自动预定义_MPU_LOCK和_MPU_ENable_NMI定义。

    设置MPU的最后一种方法是在执行开始时在代码中。 这很困难,但可以通过仔细遵循用户指南来设置寄存器以满足应用程序的需要来完成。

    最后,我不熟悉RXi旗帜,也不知道您这一问题的含义,请您详细说明一下:

    [QUOTE USER="Olli Mannisto"关于标志的主题,初始化标志我不确定。 何时初始化? 启动后? 在闪烁期间,#pragma持久性内容初始化一次?

    此致,
    Caleb Overbay

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

    初始化标志可在MSP430汇编语言参考SLAU131章节8.5 .4.2 中找到。

    attr指定与命名区域关联的一到四个属性。 属性是可选的;
    使用时,必须将其括在括号中。 属性限制的分配
    将部分输出到特定内存范围。 如果不使用任何属性,则可以使用
    将任何输出部分分配到任何范围,没有任何限制。 没有的任何内存
    指定的属性(包括默认模型中的所有内存)具有所有四个属性。
    有效属性包括:

    R 指定可以读取内存。
    指定内存可以写入。
    X 指定内存可以包含可执行代码。
    指定可以初始化内存

    因此,构建我指定的自己的内存分区

    FRAMRO (RX) :原点= 0x4400,长度= 0x0C00
    FRAM 			:原点= 0x5000,长度= 0xAF80
    FRAMRO2 (RX) :原点= 0x1万,长度= 0x4000 

    现在这些标志实际上有什么影响吗?  

    在您的示例中,您将为代码(RX)分配大部分FRAM。 我个人尝试实现的是在FRAM的48kB块和16kB块的开头创建两个只读段。 我想避免将"中间"部分声明为RX,因为它包含许多"其他"内容,即签名和中断向量。 我认为它们实际上应该是RO,既不包含代码,也不应该在运行时被篡改。  

    根据您的解释,我将删除那些手动分配的句段,因为我必须临时照看代码用法。 这实际上是写在任何地方吗? 例如,SLAA685,SLAA628和SPMA044A都解决了内存保护问题,但没有说明它如何在代码编写器中自动工作。 当然,其中一些是相当老的,例如SPMA044A是2012年的。  

    如果没有PUC或NMI,则不会显示访问故障,这是失控指针的标志,在最佳情况下很难调试。 因此,我最好分配NMI来启动CPU并增加监视器标志:

    案例0x0E: //MPUSEG2IFG
    //清除违规中断标志
    SYSNMIFLAG =1; //设置标志
    MyConfig_RW.watchdog l++;
    if(!myconfig_rw.watchdog _l){
    MyConfig_RW.watchdog _h+;
    }
    /*
    *再见残酷的世界!
    */
    pm_trigPOR();
    中断; 

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

    仅在编译时使用引用的标志,以便让编译器知道在何处分配代码,只读和可写内存。 它实际上并不设置MPU。 因此,在使用标记而不设置MPU时,内存将保持未受保护。

    此外,我认为中断向量需要可执行才能正常运行,这就是为什么默认设置会授予它们RX权限。 默认设置背后的概念是,代码通常会占用应用程序中的大部分内存,并且您希望避免代码在两个部分之间拆分。 这就是为什么在代码部分中包含较大的内存部分(FRAM2)的原因。

    最后,我不认为链接程序文件处理MPU设置的方式记录在用户指南或类似内容中。 我将向我们的内部团队提供此反馈。 我还要指出,SPMA044A不适用于MSP430微处理器系列。

    此致,
    Caleb Overbay
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    在MPS430FR5969中,FRAM2实际上是16kB,FRAM是48kB。 代码确实会在FRAM2和FRAM之间拆分,而大多数代码在FRAM2中。 我是否应该对链接程序文件进行操作,以便对代码使用FRAM而不是FRAM2,并对常量和持久性数据使用FRAM2?

    在几次错误启动后,我使MPU NMI按预期工作,手动构造0x0.1万指针写入触发它。 我花了一段时间才知道编译器定义/宏不会转移到链接器。 因此,必须为链接器明确定义_MPU_ENable_NMI。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好,Olli,

    “对不起,我说‘FRAM2’是记忆的最大部分,这是不正确的。 在大多数具有较高地址空间的MSP430器件中,FRAM2大于FRAM,但您已经指出,此处没有这种情况。 在FRAM和FRAM2之间拆分代码仍然可以。 当我之前说过您不想将代码拆分为两个"部分"时,我应该说句段。 您真的不想在两个MPU段之间拆分代码,这就是为什么最大的段是代码段的原因。

    我很高兴听到您能够使事情正常工作。 如果您还有其他问题,请告诉我!

    此致,
    Caleb Overbay