编译代码以从外部 RAM 运行

电器工程 图片 编译器 实验室
2022-01-17 02:26:24

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

基本思路是内部程序内存会包含一个浏览sd卡的界面,一旦用户选择了一个程序,就会从sd卡复制一个hex文件到外部ram,然后跳转到外部ram空间执行.

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

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

我正在使用 MPLab IDE,但我不是很熟悉它,或者如何进行这种自定义。

2个回答

你有两个不同的问题:

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

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

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

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

    使用现有的 Microchip 工具没有直接简单的方法来做到这一点,但也没有那么糟糕。

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

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

    更好的方法是在基本代码中选择的固定已知地址处定义接口区域。应用程序代码可以调用的每个子例程都有一个 GOTO。这些 GOTO 将被放置在固定的已知地址,外部应用程序只会调用这些位置,然后这些位置会跳转到子例程在该基本代码构建中实际结束的任何位置。每个导出的子程序需要 2 个程序内存字,并且在运行时需要两个额外的周期,但我认为这是值得的。

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

    我的预处理器和一堆其他相关的东西可以在www.embedinc.com/pic/dload.htm免费获得。

选项 1:解释语言

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

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

同样,您可以创建自己的特定领域语言。您必须提供更成熟的 API 和外部文档,但如果游戏都相似,那么这不会太难。

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

选项2:只需重新编程整个事情

但是,如果您已经计划自己用 C 语言编写所有游戏,那么就不必费心从 SD 卡加载游戏逻辑了。您只有 32kB 的闪存可供重新编程,并且可以轻松获得 4 GB 的 microSD 卡。(注意:较大的卡通常是 SDHC,更难连接)。假设您使用了 32 kB 的每个最后一个字节,那么 SD 卡上的空间可以容纳 131,072 个固件副本以及您需要的任何游戏逻辑。

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

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

这是我目前的建议。

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

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

链接器已经将您的代码分成多个部分;-section从理论上讲,您可以将其分解为带有一些#pragma咒语的附加部分。我从来没有这样做过,也不知道怎么做。直到上述两种方法都失败了(或者有人在这里发布了一个很棒的答案),我可能不会学习这个机制,所以我不能教给你。