编译代码以从外部RAM运行


13

我正在考虑基于PIC18F85J5的简约游戏系统的设计我设计的一部分是可以从SD卡加载游戏,而无需重新编程芯片或刷新程序存储器。我之所以选择该芯片,是因为它具有外部存储器接口,使我可以从外部SRAM运行代码。

基本思想是内部程序存储器将包含用于浏览sd卡的接口,并且一旦用户选择了程序,它将从sd卡复制一个十六进制文件到外部ram,然后将执行跳转到外部ram空间。

内部程序存储器还将具有用于图形,控制器输入和其他各种实用程序的各种库。

我非常有信心,我知道如何使内部固件正常工作。问题是创建要从外部RAM运行的程序。它感觉与以常规图片为目标并不相同,它需要了解内部存储器中可用的库函数,而不是重新编译它们,而仅链接到它们。它还需要在内部闪存的32k之后开始使用地址,而不是从零开始。是否有使用这些类型的约束来编译程序的好方法?

我正在使用MPLab IDE,但对它或如何进行这种自定义并不十分熟悉。


我一段时间以来在这里看到的最好的问题之一...我期待听到想法和答案。
乔恩·L


我看到了,但是他们大多建议写Flash,这是我想避免的。我的硬件设计将与接受答案中的应用笔记中的图6相似。
captncraig 2012年

Answers:


8

您有两个单独的问题:

  1. 构建外部RAM地址范围的代码。

    这实际上很容易。您要做的就是创建一个链接器文件,其中仅包含您希望代码占用的地址范围。请注意,您不仅需要为这些外部应用程序保留特定的程序存储器地址范围,而且还需要一些RAM空间。与程序存储器地址一样,此RAM空间需要固定并已知。只需在链接器文件中仅使用那些固定的已知地址范围即可。不要忘记使它们在基本代码链接器文件中不可用。

    基本代码将新应用加载到外部存储器后,它必须知道如何执行它。最简单的事情可能是从第一个外部RAM位置开始执行。这意味着您的代码将在该绝对起始地址处需要一个CODE部分。它在其余代码中都包含一个指向正确标签的GOTO,所有这些都可以重定位。

  2. 在基本代码中将应用程序链接到库例程。

    现有的Microchip工具没有立即实现此目的的简单方法,但也不错。

    一个更大的问题是您要如何处理基础代码更改。简单的策略是构建您的基本代码,在生成的映射文件上运行程序以获取全局地址,然后让它为所有全局定义的符号编写一个包含EQU语句的导入文件。然后,此导入文件将包含在所有应用程序代码中。没有什么可链接的,因为应用程序源代码本质上包含对基本代码入口点的固定地址引用。

    这很容易并且可以正常工作,但是请考虑一下在更改基本代码时会发生什么。即使是很小的错误修复,也可能导致所有地址移动,然后所有现有的应用程序代码将变得不好,必须重新构建。如果您从不打算在不更新所有应用程序的情况下提供基本代码更新,那么也许您可以避免这样做,但是我认为这是个坏主意。

    更好的方法是在基本代码中的选定固定已知地址处具有定义的接口区域。应用程式程式码可以呼叫的每个子程序都会有一个GOTO。这些GOTO将放置在固定的已知地址,并且外部应用程序将仅调用那些位置,然后这些位置将跳转到该子例程在该基本代码版本中实际结束的位置。每个导出的子例程花费2个程序存储字,并在运行时花费两个额外的周期,但是我认为这是非常值得的。

    为此,您需要自动化生成GOTO的过程以及外部应用程序将导入的导出文件,以获取子例程(实际上是GOTO重定向器)地址。您也许可以巧妙地使用MPASM宏,但是如果我这样做,我肯定会使用我的预处理器,因为它可以在预处理时写入外部文件。您可以编写一个预处理器宏,以便可以通过一行源代码定义每个重定向器。宏在后台执行所有令人讨厌的工作,即生成GOTO(对实际目标例程的外部引用),并使用该例程的已知常量地址将适当的行添加到导出文件中,并使用适当的名称。也许宏只是用常规名称(类似于运行时可扩展数组)制作了一堆预处理器变量,然后在所有宏调用之后将导出文件写入一次。我的预处理程序可以执行MPASM宏不能执行的许多操作之一,就是执行字符串操作以从其他名称构建新的符号名称。

    我的预处理器和许多其他相关资料可从www.embedinc.com/pic/dload.htm免费获得。


我真的很喜欢固定跳转表的想法。我可以从闪存的末尾开始,并为每个系统调用设置固定的位置。如果我无法弄清您描述的所有预处理器伏都教,我什至可以摆脱一个带有所有子例程地址的手动维护的头文件。
captncraig 2012年

5

选项1:口译语言

这并不能直接回答问题(顺便说一句,顺便说一句,我希望可以从直接解决该问题的答案中学习),但是在做可以加载外部程序以在其中编写外部程序的项目时,这很常见。一种解释性语言。如果资源紧张(它们将在此处理器上使用,是否考虑过为此使用PIC32或小型ARM处理器?),通常将语言限制为完整规范的子集。甚至更进一步的是领域特定的语言,它仅能执行几项操作。

例如,elua项目就是一种低资源(64 kB RAM)解释语言的示例。如果删除某些功能,则可以将其压缩到32k RAM(注意:它不适用于当前的处理器(这是一种8位架构)。使用外部RAM对于图形可能会太慢)。它提供了一种快速,灵活的语言,如果您提供的API最少,新用户就可以轻松地编写游戏程序。在线有大量关于该语言的文档。还有其他语言(例如Forth和Basic)可以用类似的方式使用,但是我认为Lua是目前的最佳选择。

同样,您可以创建自己的域特定语言。您将不得不提供更加完善的API和外部文档,但是如果游戏都相似,那么这将不是一件容易的事。

无论如何,PIC18可能不是我要用于涉及自定义编程/脚本和图形的处理器。您可能对这类处理器很熟悉,但是我建议您现在是结合使用显示驱动程序和更多内存的好时机。

选项2:重新编写整个程序

但是,如果您已经在用C编程自己所有的比赛计划,那么不加载打扰刚刚从SD卡的游戏逻辑。您只有32kB的Flash可以重新编程,因此可以轻松获得4 GB的microSD卡。(注意:较大的卡通常是SDHC,很难与之连接)。假设您使用了32 kB的最后一个字节,那么在SD卡上就可以保留131,072份固件以及所需的任何游戏逻辑的空间。

有很多用于编写PIC引导加载程序的应用笔记,例如AN851。您需要将引导加载程序设计为占用特定的内存区域(可能在内存区域的顶部,您应该在链接器中指定该区域),并指定整个固件项目不到达该区域。该应用笔记详细说明了这一点。只需将“ PIC18F452的引导区”替换为“我在链接器中指定的引导区”即可,这一切都说得通。

然后,您的引导加载程序仅需要允许用户从SD卡中选择要运行的程序,然后将整个内容复制过来即可。UI可能是用户必须按住按钮才能进入选择模式。通常,引导加载程序会在重置时检查此按钮的状态,如果没有按下,则引导进入游戏。如果按住不放,则需要允许用户选择SD卡上的文件,复制程序,然后继续启动[新]游戏。

这是我目前的建议。

选项3:仅存储部分hex文件的深层魔术

您所设想的机制的问题在于,处理器不处理API和函数调用,而是处理数字 -指令指针可以跳转到的地址,并期望其中存在根据API规范执行函数调用的代码。如果您尝试仅编译程序的一部分,则链接器在调用check_button_status()或时将不知道该怎么做toggle_led()。您可能知道这些功能存在于处理器的hex文件中,但是它需要准确知道它们驻留在哪个地址。

链接器已经将您的代码分为多个部分;从理论上讲,您可以将其中的内容-section和内容分解为其他部分#pragma。我从未做过,也不知道如何做。直到以上两种方法使我失败(或有人在这里发布了一个很棒的答案),我才可能不会学习这种机制,因此无法向您教它。


我对数字2的异议是,闪存在芯片的使用寿命内具有有限的擦除周期数。我不想使用功能更强大的MCU,因为我要使用8位极简主义。
captncraig 2012年

@CMP-至少有10,000个擦除周期。如果有人每天玩不同的游戏,它将持续到2039年。Flash几乎肯定会比电路的其余部分寿命更长。我认为您不必为此担心,除非它每天在街机游戏中播放数十次。
凯文·维米尔

1
其次,8位的极简主义可能看起来很酷,但是它在编程时很烂。得到一个坚固的微控制器并使它看起来复古起来要容易得多,而不是因为要突破处理器的限制而被迫使其看起来复古。
Kevin Vermeer

1
两者都很公平。即使要减少零件数量,pic32的成本或外部组件也没有什么不同,并且,如果它具有512K内部闪存,它甚至会获胜。
captncraig 2012年

看来,实际的解决方案是使用pic32并编写引导加载程序以从SD卡刷新。我将很难重用引导加载程序和用户代码所需的功能,但是只要将其保留在12k引导闪存中,它就应该为用户代码提供整个芯片,并且它们可以在源代码中包含他们想要的任何库水平。
captncraig 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.