HCS12X–数据定义(如何在CodeWarrior中将数据定义到分页区)

由于在暑假匆忙接收的嵌入式项目中需要使用特别大的数组,非分页RAM的内存不够用了,没办法,硬着头皮尝试使用分页RAM,但是完全没有单片机的基础,导致极其的困难。之前写程序都是按照纯软件的思维,主要考虑架构,不会考虑到每个变量具体存在哪个物理地址这么底层的问题,结果被飞思卡尔这分页地址、prm文件什么的搞得一头雾水,而网上的资料又少,讲的又大同小异的笼统,最后写出来的程序因为这分页地址的原因存在各种问题(还以为把变量放到分页RAM了,结果现在稍微懂了点回去看,发现其实很多根本还是分配在非分页区。晕倒~。但是居然还能相对正常运行也是很神奇)。这些天各种找相关的资料,结果发现在CodeWarrior的官方文档资料里其实把我想知道的都讲的很清楚了(还是官方文档给力,以后学什么东西直接找官方文档,不去到处找网上一堆零零散散的资源来学了)。本着学习的态度,将逐步把官方文档翻译一遍,供大家一起交流学习进步。

翻译的资料是公开的,我想应该不会有什么版权问题,如涉及版权问题,请联系我删除文章,原文档在这里(https://www.nxp.com/pages/codewarrior-development-studio-for-hcs12x-microcontrollers-classic-ide-v5.2:CW-HCS12X?&tab=Documentation_Tab&linkline=Users-Guides),另感谢NXP提供的学习资料。

另外,本人有另一篇详解怎么定义及访问直接寻址区的博文 
http://blog.csdn.net/lin_strong/article/details/78535785


HCS12X – 数据定义

译者注:译者博客(http://blog.csdn.net/lin_strong),转载请保留这条。此为官方文档TN238,仅供学习交流使用,请勿用于商业用途。

这个文档描述了程序员要怎么样帮助HCS12X编译器来产生在数据访问上更加优化的代码。我们将讨论以下主题:

  • 分配在直接寻址区的变量
  • 分配在扩展寻址区的变量
  • 分配在banked寻址区的变量 — 使用逻辑寻址
  • 分配在banked寻址区的变量 — 使用全局寻址
  • Banked常量分配
  • 逻辑地址 vs. 全局地址

对于以上举例的每个变量类型,我们都会描述:

  • 怎么定义一个变量
  • 怎么声明一个变量
  • 生成来分为这变量的代码
  • 怎么定义一个指向这样一个变量的指针
  • 生成来访问指针和其指向的变量的代码
  • PRM文件中的位置
注意1:
这篇技术文档中描述的信息适用于SMALL(-Ms)以及BANKED(-Mb)地址模型。而不适用于LARGE(-Ml)地址模型。
注意2 :
对于少于32Kb代码的应用,我们通常推荐使用SMALL地址模型,或者使用BANKED地址模型。我们不推荐使用LARGE地址模型。

分配在直接寻址区的变量

为了使编译器将某个变量分配到直接寻址页,你必须定义(以及声明)它到个特别的segment中,这segment带有属性__SHORT_SEG。

  • 定义&访问直接寻址区的数据

1 像这样进行变量定义:

#pragma DATA_SEG __SHORT_SEG MyShortData
unsigned char rub_short_var; 
#pragma DATA_SEG DEFAULT
  • 1
  • 2
  • 3

2 像这样进行变量声明:

#pragma DATA_SEG __SHORT_SEG MyShortData
extern unsigned char rub_short_var;
#pragma DATA_SEG DEFAULT
  • 1
  • 2
  • 3

3 对这变量的访问会生成如下代码:

   37: rub_short_var = 2;
00E08002 C602 [1] LDAB #2
00E08004 5B08 [2] STAB $08
  • 1
  • 2
  • 3

4 没有用于访问直接寻址页变量的特殊指针类型。一个指向这样一个变量的指针将像这样定义:

unsigned char* ptr_on_short_var;
  • 1

5 初始化和访问被指向的对象会产生如下代码:

   46: ptr_on_short_var = &rub_short_var;
00E0801F 180320102102 MOVW #8208,$2102
   47: (*ptr_on_short_var)++;
00E08025 62FBA0D9 INC [$A0D9,PC] /* [ptr_on_short_var,PCR] */
  • 1
  • 2
  • 3
  • 4

6 对于定义在__SHORT_SEG section的变量,PRM文件中的SEGMENT和PLACEMENT是这样子的:

SEGMENTS
   DIRECT_PAGE = READ_WRITE 0x2010 TO 0x20FF;
   /* 这里省略了其他segment定义 */
END
PLACEMENT
   MyShortData INTO DIRECT_PAGE;
   /* 这里省略了其他placement定义 */
END
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
注意:
包含通过直接寻址模式访问的变量的section,应该被分配在用逻辑地址定义的segment中

配置直接寻址区

在HCS12X上,直接寻址页可以移动,并且不是硬编码到0x00..0xFF(HCS12上是硬编码到这的)。编译器需要进行特别的设置来支持这一特性。

  • 如果DIRECT寄存器包含任何东西但是默认值是0(译者注:意思是存在DIRECT寄存器?),编译器和汇编器就要启用-CpDirect选项,其后跟着可直接访问区的起始地址。比如:如果DIRECT初始化为0x20,那么直接访问窗口就是从0x2000到0x20FF,编译器和汇编器选项就是-CpDirect0x2000。编译器和汇编器都要加上这个选项。
  • DIRECT寄存器必须由用户代码来初始化。默认的启动代码不初始化DIRECT。
  • 在prm文件中,section MyShortData要被分配到这个区域(在上例中是从0x2000到0x20FF)。
  • 小心,不正确地分配直接访问section(也就是说MyShortData没有被正确地分配)不会产生链接器诊断消息。 
    对于HCS12,链接器会报告一个fixup溢出。但是对于HCS12X却无法做到这一点,因为直接访问页可以被映射。

分配在扩展寻址区的变量

为了使编译器将某个变量分配到扩展地址空间,你只需要用普通的方式定义(并声明)它。 
1 像这样进行变量定义:

unsigned char rub_var;
  • 1

2 像这样进行变量声明:

extern unsigned char rub_var;
  • 1

3 对这变量的访问会生成如下代码:

   39: rub_var =7;
00E08002 C607   [1] LDAB #7
00E08004 7B2001 [3] STAB $2001
  • 1
  • 2
  • 3

4 指向这个变量的指针可以像这样定义:

unsigned char * ptr_on_var;
  • 1

5 初始化和访问被指向的对象会产生如下代码:

   57: ptr_on_var = &rub_var;
00E0805F 18032100210C MOVW #8448,$210C
   58: (*ptr_on_var)++;
00E08065 62FBA0A3 INC [$A0A3,PC] /* [ptr_on_var,PCR] */
  • 1
  • 2
  • 3
  • 4

6 这种方式定义的变量被分配在预定义section DEFAULT_RAM中。在PRM文件中,这个section的SEGMENT和PLACEMENT定义是这样子的:

SEGMENTS
   RAM = READ_WRITE 0x2100 TO 0x3FFF;
 /* 这里省略了其他segment定义 */
END
PLACEMENT
   DEFAULT_RAM INTO RAM;
 /* 这里省略了其他placement定义 */
END
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
注意:
包含使用扩展寻址模式定义的变量的Section应该被分配在使用逻辑地址定义的segment中。

分配在Banked寻址区的变量

分配在banked RAM中的变量可以使用以下来访问:

  • 逻辑地址(使用RPAGE) 或
  • 全局地址

使用逻辑地址

为了告诉编译器你想要使用一个变量的逻辑地址来访问它,你得使用如下语法: 
1 像这样进行变量定义:

#pragma DATA_SEG __RPAGE_SEG PAGED_RAM
unsigned char rub_far_var;
#pragma DATA_SEG DEFAULT
  • 1
  • 2
  • 3

2 像这样进行变量声明:

#pragma DATA_SEG __RPAGE_SEG PAGED_RAM
extern unsigned char rub_far_var;
#pragma DATA_SEG DEFAULT
  • 1
  • 2
  • 3

3 对这变量的访问会生成如下代码:

   38: rub_far_var = 5;
00E08002 C6FB [1] LDAB #251 /* #PAGE(rub_far_var) */
00E08004 5B16 [2] STAB $16 /* RPAGE */
00E08006 C605 [1] LDAB #5
00E08008 7B1000 [3] STAB $1000
  • 1
  • 2
  • 3
  • 4
  • 5

4 指向这个变量的指针可以像这样定义:

unsigned char * __rptr ptr_on_far_var;
  • 1

5 初始化和访问被指向的对象会产生如下代码:

   50: ptr_on_far_var = &rub_far_var;
00E08029 180310002105 MOVW #4096,$2105
00E0802F 180BFB2104   MOVB #251,$2104
   51: (*ptr_on_far_var)++;
00E08034 FE2105 LDX  $2105 /* ptr_on_far_var:1 */
00E08037 F62104 LDAB $2104 /* ptr_on_far_var */
00E0803A 7B0016 STAB $0016 /* RPAGE */
00E0803D 6200   INC  0,X
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

6 对于定义在RPAGE section中的变量,对应PRM文件中的SEGMENT和PLACEMENT是这样子的:

SEGMENTS
   RAM_FB = READ_WRITE 0xFB1000 TO 0xFB1FFF;
   RAM_FC = READ_WRITE 0xFC1000 TO 0xFC1FFF;
   RAM_FD = READ_WRITE 0xFD1000 TO 0xFD1FFF;
   /* 这里省略了其他segment定义 */
END
PLACEMENT
   PAGED_RAM INTO RAM_FB, RAM_FC, RAM_FD;
   /* 这里省略了其他placement定义 */
END
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
注意:
包含通过逻辑寻址模式访问的变量的section,应该被分配在用逻辑地址定义的segment中
注意:
分配在RPAGE segment中的变量也可以通过使用far指针来访问,
unsigned char * __far ptr_on_far_var;
在这种情况下,将会使用全局寻址模式来访问指向的对象

使用全局地址

使用全局地址的主要原因是因为一个对象可能没法装进单页的逻辑地址中去。通过使用全局地址访问对象,就绕开了这个制约,对象就可以大到所有可用的内存大小或可访问的64KB。 
全局地址指针可以用于任何对象,对象不受到任何特殊限制。 
为了告诉编译器你想要访问一个使用全局地址的变量,你要使用以下语法: 
1 像这样进行变量定义:

#pragma DATA_SEG __GPAGE_SEG PAGED_RAM
unsigned char rub_far_var;
#pragma DATA_SEG DEFAULT
  • 1
  • 2
  • 3

2 像这样进行变量声明:

#pragma DATA_SEG __GPAGE_SEG PAGED_RAM
extern unsigned char rub_far_var;
#pragma DATA_SEG DEFAULT
  • 1
  • 2
  • 3

3 对这变量的访问会生成如下代码:

   35: rub_far_var = 7;
00E0802B C607 [1] LDAB #7
00E0802D 860F [1] LDAA #15 /* #GLOBAL_PAGE(rub_far_var)*/
00E0802F 5A10 [2] STAA $10 /* GPAGE*/
00E08031 187BE00F [4] GSTAB $E00F
  • 1
  • 2
  • 3
  • 4
  • 5

4 指向这个变量的指针可以像这样定义:

unsigned char *__far ptr_on_far_var;
  • 1

5 初始化和访问被指向的对象会产生如下代码:

   37: ptr_on_far_var = &rub_far_var;
00E08035 1803B0002101 MOVW  #45056,$2101
00E0803B 180B0F2100   MOVB  #15,$2100
   38: (*ptr_on_far_var)++;
00E08040 FE2101       LDX   $2101 /* ptr_on_far_var:1 */
00E08043 F62100       LDAB  $2100 /* ptr_on_far_var */
00E08046 5B10         STAB  $10   /* GPAGE */
00E08048 18A600       GLDAA 0,X
00E0804B 42           INCA
00E0804C 186A00       GSTAA 0,X
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

6 对于定义在 GPAGE section中的变量,对应PRM文件中的SEGMENT和PLACEMENT是这样子的:

SEGMENTS
   RAM_BANKED = NO_INIT  0xF9000'G  TO  0xFCFFF'G;
   /* 这里省略了其他segment定义 */
END
PLACEMENT
   PAGED_RAM INTO RAM RAM_BANKED;
   /* 这里省略了其他placement定义 */
END
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
注意:
包含通过全局寻址模式访问的变量的section可以被分配在使用逻辑或全局地址定义的segment中。所以PRM文件也可以看起来像这样:
SEGMENTS
   RAM_FB = READ_WRITE 0xFB1000 TO 0xFB1FFF;
   RAM_FC = READ_WRITE 0xFC1000 TO 0xFC1FFF;
   RAM_FD = READ_WRITE 0xFD1000 TO 0xFD1FFF;
   /* 这里省略了其他segments定义 */
END
PLACEMENT
   PAGED_RAM INTO RAM_FB, RAM_FC, RAM_FD;
   /* 这里省略了其他placement定义 */
END
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

分页常量分配

常量可以被分配在banked EEPROM或者在banked FLASH中,并且可以通过以下来访问:

  • EEPROM(使用EPAGE)中的逻辑地址 或
  • FLASH(使用PPAGE)中的逻辑地址 或
  • 全局地址

在EEPROM中使用逻辑地址

为了告知编译器你想要使用一个常量在EEPROM中的逻辑地址访问它,你得使用如下语法: 
1 像这样进行常量定义:

#pragma CONST_SEG __EPAGE_SEG PAGED_CONST
const unsigned char cub_far_const=1;
#pragma CONST_SEG DEFAULT
  • 1
  • 2
  • 3

2 像这样进行常量声明:

#pragma CONST_SEG __EPAGE_SEG PAGED_CONST
extern const unsigned char cub_far_const=1;
#pragma CONST_SEG DEFAULT
  • 1
  • 2
  • 3

3 指向这个变量的指针可以像这样定义:

const unsigned char *__eptr ptr_on_far_const;
  • 1
注意:
分配在一个EPAGE segment的常量还可以使用far指针来访问。
const unsigned char *__far ptr_on_far_const;
这种情况下,会使用全局寻址模式来访问指向的对象

在FLASH中使用逻辑地址

编译器只支持那些分配在非分页区的代码在FLASH中使用逻辑地址来寻址。因为通常不会这样做,我们推荐为所有在FLASH中的数据对象使用全局地址。 
为了实现使用逻辑地址,要使用segment qualifier __PPAGE_SEG 和pointer 
qualifier __pptr。

使用全局地址

1 像这样进行常量定义:

#pragma CONST_SEG __GPAGE_SEG PAGED_CONST
const unsigned char cub_far_const=1;
#pragma CONST_SEG DEFAULT
  • 1
  • 2
  • 3

2 像这样进行常量声明:

#pragma CONST_SEG __GPAGE_SEG PAGED_CONST
extern const unsigned char cub_far_const= 1;
#pragma CONST_SEG DEFAULT
  • 1
  • 2
  • 3

3 指向这个变量的指针可以像这样定义:

const unsigned char *__far ptr_on_far_const;
  • 1

逻辑地址 vs 全局地址

  • 对于那些大小不大于4K(Banked RAM窗口的大小)的变量,使用逻辑地址比使用全局地址更有效率。
事件 逻辑地址 全局地址
数据访问(代码大小) xy=3; 9字节 10字节
数据访问(执行速度) xy=3; 7周期 8周期
指针访问(代码大小) *xx=4; 11字节 12字节
指针访问(执行速度) *xx=4; 11周期 15周期
指针内容递增(代码大小) (*xx)++; 11字节 15字节
指针内容递增(执行速度) (*xx)++; 12周期 16周期
  • 如果一个变量的大小大于4K,连接器将不得不把它分配地横跨bank的边界。这种情况下,我们推荐使用全局寻址来访问这个变量
  • 我们推荐使用全局寻址模式来访问分配在FLASH中的常量或字符串常量
  • 我们推荐为所有那些在运行时使用逻辑地址的对象使用逻辑寻址。包括:栈,代码,直接寻址的变量,扩展寻址区的变量,I/O寄存器。
  • 为了定义个分页的变量,一定要使用DATA_SEG pragma。不要试图使用 __far关键字来干这件事。far关键字指定了编译器应该怎么访问一个变量,但它不会影响变量被分配的方式。

猜你喜欢

转载自blog.csdn.net/fz835304205/article/details/78677308