从MAXQ8913微控制器上的RAM执行应用代码

描述

MAXQ8913和其他MAXQ微控制器使用的哈佛存储器映射架构为用户提供了根据需要将不同的物理存储器段(如数据SRAM)映射到程序或数据存储器空间的灵活性。在某些情况下,从数据SRAM执行部分应用可以提高性能并降低功耗。这些好处是以增加应用程序复杂性为代价的。

概述

与许多其他MAXQ微控制器一样,MAXQ8913包括一个基于SRAM的内部数据存储器区域,该存储器可以映射到数据存储器空间,也可以映射到程序存储器空间。内部SRAM通常用作数据存储器,大部分代码执行发生在程序闪存或屏蔽ROM中。但是,在某些情况下,应用程序从内部SRAM执行其代码的有限部分可能很有用。

本应用笔记解释了如何配置和加载基于汇编的代码,以便从内部SRAM正确执行。讨论了这种执行的优缺点。本应用笔记的演示代码为MAXQ8913编写,采用基于汇编的MAX-IDE环境。本应用笔记中介绍的演示应用的代码和项目文件可供下载。

本应用笔记中讨论的代码专门针对MAXQ8913微控制器,但本文所示的原理和技术同样适用于其它基于MAXQ的微控制器,其内部SRAM可以映射到程序空间。其他能够以这种方式执行代码的MAXQ微控制器包括MAXQ2000、MAXQ2010和MAXQ3210/MAXQ3212。

该代码可在任何基于MAXQ8913的硬件上正常工作,该硬件为MAXQ232的串行端口8913提供串口接口(RS-0或USB转串口)。通过将终端仿真器连接到串行端口,可以查看演示代码的输出,波特率为9600波特,8个数据位,1个停止位,无奇偶校验。

MAX-IDE环境的最新安装包和文档可免费下载。

最大集成开发台安装

MAXQ磁芯组装指南

开发工具指南

从 RAM 执行代码的优点

通常,MAXQ微控制器上的大多数应用代码都设置为从主程序空间执行,这通常使用大型内部闪存或(对于屏蔽ROM器件)客户特定的应用ROM来实现。主程序空间是非易失性的,因此在大多数情况下将应用程序代码存储在那里是有意义的。内部SRAM用于存储变量、软件堆栈和类似数据,这些数据在器件断电时不需要保存。

但是,对于某些应用,从数据SRAM执行某些代码具有优势。

降低功耗

在大多数MAXQ微控制器中,从内部SRAM(或实用程序ROM)执行代码时,电源电流会降低,而不是程序闪存。之所以能节省功耗,是因为闪存在未被访问时可以动态关闭电源。如果应用通常花费大部分活动时间执行非常少量的代码,则从SRAM执行该代码可以显著降低总体功耗。

直接内存访问主程序空间

通常,从主程序闪存执行的代码不能直接读取也存储在主程序闪存中的数据。这种类型的数据可以包括常量字符串和应用程序数据中包含的数据表。要读取此数据,应用程序必须在实用程序ROM中调用专用数据传输函数。 从RAM执行代码会绕过此限制,并允许使用标准数据指针直接读取闪存中包含的数据。这加快了访问速度。如果小型算法花费大量时间遍历查找表或存储在闪存中的其他常量数据,则从RAM执行算法可以在更短的时间内完成操作。

整个闪存可以重写

MAXQ8913上的实用程序ROM与大多数基于闪存的MAXQ微控制器一样,包含标准功能,允许在应用控制下擦除和重写程序闪存。此过程允许实现用户加载程序,这些加载程序使用用户指定的接口(例如串行端口、SPI 或 I²C)重新加载部分或全部应用程序。但是,如果用户加载程序代码包含在闪存中,则无法擦除或重写它占用的闪存部分。从RAM执行用户加载器代码允许擦除整个闪存程序空间并使用新代码重写,包括用户加载器本身。

从 RAM 执行代码的缺点

从 RAM 执行应用程序代码时也存在缺点和限制。有些缺点是可以解决的,而另一些则是MAXQ架构固有的。

有限的代码空间

RAM 通常比程序闪存小得多,这意味着在任何给定时间只能从 RAM 执行少量代码。但是,可以从 RAM 运行一个例程,擦除它并加载第二个例程,运行第二个例程,依此类推。

代码必须镜像

在从 RAM 执行代码之前,必须将其复制到 RAM。此过程需要时间和代码空间来实现。此外,代码必须从某个地方复制,因此代码实际上存储两次:一次在闪存或程序ROM中,一次在RAM中。即使代码不打算从闪存执行,它仍然必须存储在那里,从而消耗额外的空间。

无法直接访问内存

从内部 RAM 执行代码时,RAM 在数据存储器空间中不再可见。这意味着数据指针不能用于直接读取或写入 RAM 位置。可以通过与从闪存运行的应用程序代码相同的方式解决此限制。使用实用程序ROM数据传输功能(UROM_moveDP0和类似功能)从RAM读取,并通过在闪存中写入类似的功能,对RAM执行间接写入。但是,此解决方法需要额外的时间和应用程序空间。

组装要从 RAM 执行的代码

在编写将从数据RAM执行的应用程序代码时,必须了解一个主要因素。每个代码字将在一个地址组装并加载到该地址的闪存中,但它将在不同地址的RAM中执行。例如,如果一段应用程序代码从程序字地址 0100h 开始加载到闪存中,并从数据字地址 0100h 开始复制到 RAM,则无法跳转到地址 0100h 以在 RAM 中执行代码。地址 0100h 仍然是闪存中代码的地址。程序空间中RAM中的代码地址是其数据存储器地址加上A000h的偏移量,如下图1所示。

存储器

图1.从RAM执行代码时MAXQ8913的存储器映射。

要执行复制到数据存储器地址 0100h 的 RAM 的应用程序代码,必须跳转到程序地址 A100h。

从RAM执行代码会给MAX-IDE汇编程序带来困难。MAX-IDE根本不知道您将在与组装代码不同的地址执行代码。例如,假设您有一个名为 subOne 的例程,它从闪存地址 0080h 开始,另一个例程位于 0300h 处,它调用第一个例程。此代码如下所示。

org 0080h

subOne:
   ....perform various calculations...
   ret

...

org 0300h

subTwo:
   call  subOne
   ...and so on...

如果将这两个例程都复制到 RAM 中并在那里执行,会发生什么?假设例程被复制到RAM中与它们在闪存中占用的程序存储器地址相同的数据存储器地址,则subOne将位于程序地址A080h,subTwo将位于A300h。

由于行“call subOne”和目标标签subOne之间的距离大于相对跳转距离(+127/-128字),因此必须将指令组装为绝对LCALL。但是,汇编程序对 subOne 的唯一地址是 0080h,因此指令将被组装为“LCALL 0080h”。当 subTwo 执行时,它不会调用位于 RAM 中的 subOne 的副本,而是调用位于闪存中的版本。

这种困境有两种可能的解决方法。第一种也是最简单的方法是强制汇编程序始终使用相对跳转和调用,并在 RAM 中保持例程足够接近,以便它们可以以这种方式相互调用。不要使用 JUMP 和 CALL 操作码(允许汇编程序选择短跳或长跳),而是始终使用 SJUMP 和 SCALL。这将强制使用指令的相对跳转版本。

但是,这种方法有一个警告。如果从 RAM 运行的代码量超过 128 个字,则相对跳转可能不足以让 RAM 中的一个例程调用另一个例程。在这种情况下,解决方案是使用 ORG 语句修复各种例程的地址,然后在 RAM 中定义包含其更正地址的等值。这些等价可用于LCALL和LJUMP语句,如下所示。

 

subOne  equ  0A080h

org 0080h

; subOne
   ....perform various calculations...
   ret

...

org 0300h

subTwo:
   lcall  #subOne
   ...and so on...

此过程强制汇编程序使用 LCALL 的正确地址。

将代码复制到内存

在从 RAM 执行代码之前,必须先将其复制到 RAM 中。将大量代码从闪存复制到RAM的最简单方法是使用实用程序ROM copyBuffer功能。此函数将两个数据指针(DP[0] 和 BP[Offs])和一个长度值 (LC[0]) 作为输入。它将指定的字节/字数从源 DP[0] 复制到目标 BP[Offs];它一次最多可以复制 256 个字节/字。

我们的演示应用程序会将自身的前 512 个单词从闪存复制到 RAM,然后跳转到 RAM 中的副本以开始执行代码。源指针 (DP[0]) 指向实用程序 ROM 内存映射中程序闪存的位置,该位置从 8000h 开始。请注意,为了避免无限循环,我们跳转到 RAM 中 RAM 复制代码后面的副本部分。

org 0020h                    

copyToRAM:
   move    DPC,   #1Ch       ; Ensure all pointers are operating in word mode.
   move    DP[0], #8000h     ; Start of program flash from UROM's perspective.
   move    BP,    #0         ; Start of data memory.
   move    Offs,  #0         
   move    LC[0], #256       ; The Offs register limits us to a 256-word copy.
   lcall   UROM_copyBuffer

   move    DP[0], #8100h     ; Copy second half.
   move    BP,    #0100h
   move    Offs,  #0
   move    LC[0], #256
   lcall   UROM_copyBuffer

   ljump   #0A040h           ; Begin execution of code from RAM.


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;  Executing from RAM
;;

org 0040h

   move    LC[0], #1000
delayLoop:
   move    LC[1], #8000
   sdjnz   LC[1], $
   sdjnz   LC[0], delayLoop

;; Initialize serial port.

   move    SCON.6, #1        ; Set to mode 1 (10-bit asynchronous).
   move    SMD.1,  #1        ; Baud rate = 16 x baud clock
   move    PR, #009D4h       ; P = 2^21 * 9600/8.000MHz
   move    SCON.1, #0        ; Clear transmit character flag.

数据传输操作

如上所述,从 RAM 执行代码时,内存映射的两件事会发生变化。首先,程序闪存现在映射到数据存储器中。这意味着我们可以使用任何数据指针直接从程序闪存中读取数据,如下所示。

;; Read the banner string from flash and output it over the serial port.  Since
;; we are running from RAM, we can read from the flash directly without having
;; to use the Utility ROM data transfer functions (moveDP0inc, etc...).

   move    SC.4,  #0
   move    DPC,   #0                  ; Set pointers to byte mode.
   move    DP[0], #(stringData * 2)   ; Point to byte address of string data.

stringLoop:
   move    Acc, @DP[0]++
   sjump   Z, stringEnd
   lcall   #TxChar
   sjump   stringLoop
stringEnd:
   move    DPC, #1Ch         ; Set pointers to word mode.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;  This portion of the code (addresses 200h and higher) will remain in flash.

org 0200h

stringData:
   db      0Dh, 0Ah, "Executing code from RAM....", 00h

请注意,如图1所示,SC.4(CDA0)位会影响程序闪存的哪一半(上页或下页)以字节模式映射到数据存储器。当使用字模式指针时,整个程序闪存立即映射到数据存储器中。

其次,虽然闪存现在可以在数据空间中访问,但SRAM不再可以直接访问。这意味着要从 SRAM 位置读取或写入 SRAM 位置,应用程序必须间接执行此操作。从SRAM位置读取的方式与闪存中运行的代码从闪存位置读取的方式相同 - 它使用实用程序ROM数据传输功能(moveDP0inc等)。但是,由于实用程序ROM中没有类似的函数来执行间接写入,因此应用程序必须包含一个保留在闪存中的小函数,该函数可由RAM驻留代码调用以执行写入。

下面的代码演示了用于读取和写入 RAM 变量 varA 的两种方法,其初始内容与位于地址范围 0000h-01FFh 中的其余应用程序代码一起从闪存复制到 RAM。

 

scall   printVar
   scall   incrVar
   scall   printVar
   scall   incrVar
   scall   printVar
   scall   incrVar

   move    Acc, #0Dh
   lcall   #TxChar
   move    Acc, #0Ah
   lcall   #TxChar
   
   sjump   $


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;  Variables stored in RAM (program) space.  They can be read using the 
;;  Utility ROM data transfer functions (such as UROM_moveDP0) and written 
;;  using the writeDP0 function which remains in flash.
;;

varA:
   dw 'A'


;==============================================================================
;=
;=  printVar
;=
;=  Reads the varA RAM variable value and sends it over the serial port.
;=

printVar:
   move    DPC, #1Ch         ; Word mode
   move    DP[0], #varA      ; Variable's location in UROM data space
   lcall   UROM_moveDP0      ; Moves variable value into GR.
   move    Acc, GR
   lcall   #TxChar
   ret


;==============================================================================
;=
;=  incrVar
;=
;=  Reads the varA RAM variable value, adds 1 to it, and stores it back in RAM.
;=

incrVar:
   move    DPC, #1Ch         ; Word mode
   move    DP[0], #varA      ; Variable's location in UROM data space
   lcall   UROM_moveDP0      ; Moves variable value into GR.

   move    Acc, GR
   add     #1
   move    GR, Acc
   lcall   writeDP0

   ret



;==============================================================================
;=
;=  TxChar
;=
;=  Outputs a character to the serial port.
;=
;=  Inputs  : Acc.L - Character to send.
;=

org 01F0h
   move    SBUF, Acc         ; Send character.
TxChar_Loop:
   move    C, SCON.1         ; Check transmit flag.
   sjump   NC, TxChar_Loop   ; Stall until last transmit has completed.
   move    SCON.1, #0        ; Clear the transmit flag.
   ret


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;  This portion of the code (addresses 200h and higher) will remain in flash.

org 0200h

stringData:
   db      0Dh, 0Ah, "Executing code from RAM....", 00h


;==============================================================================
;=
;=  WriteRAM
;=
;=  This is a routine that can be called by code running in the RAM to load
;=  a new value into a byte or word location in the RAM.
;=
;=  Inputs  : DP[0] - Location to write (absolute starting at 0000h) in RAM.
;=            GR    - Value to write to the RAM location.
;=  
;=  Notes   : DP[0] must be configured to operate in word or byte mode as
;=            desired before calling this function.  Following a call to this
;=            function, DP[0] must be refreshed before it is used to read data.
;=            

writeDP0:
   move    @DP[0], GR
   ret

执行时,演示代码通过串行端口输出以下文本(图2)。

存储器

图2.通过演示代码通过串行端口输出文本。

结论

MAXQ8913和其他MAXQ微控制器使用的哈佛存储器映射架构允许您将不同的物理存储器段(如数据SRAM)映射到程序或数据存储器空间。从数据SRAM执行部分应用可以提高性能并降低功耗。该过程确实需要额外的应用程序复杂性。

审核编辑:郭婷

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分