gpt4 book ai didi

c++ - 在C/C++中检测内存IO以进行硬件仿真

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:16:56 26 4
gpt4 key购买 nike

好吧,关于什么以及为什么的一些背景知识?

我想在台式机Linux上编译并运行微 Controller 固件(裸机,无操作系统)。我不想编写字节码解释器或二进制翻译器;我想编译原始源代码。将FW作为标准GUI应用程序运行具有许多优势,例如快速的开发迭代,高级的调试,自动测试,压力测试等。我之前使用AVR微 Controller 为一些项目完成了此操作,通常采取以下步骤:

  • 提供桌面上不存在的与硬件相关的 header (主要是MMIO寄存器定义->全局变量)
  • 实现外围仿真代码(lcd,eeprom)
  • 做一些GUI,以反射(reflect)原始设备的用户界面(lcd,按钮)
  • 将所有内容粘合在一起

  • 前三个步骤很简单(AVR的代码并不多),最后一个步骤很棘手。 FW中的某些构造在台式机版本中最终会导致无限循环(例如,等待外设寄存器更改或忙于中断处理程序更改内存的繁忙循环),另一些最终会导致无操作(写入MMIO,在实际系统上触发某些操作) ,并将FW的主循环与GUI lib的主循环融合在一起也需要一些创造力。如果FW分层良好,则可以用胶水功能替换低级代码,而不会造成太多黑客攻击。

    尽管总体行为受到这些更改的影响,但我发现最终结果在许多情况下非常有用。不幸的是,这种方法是侵入性的(FW修改),并且胶合逻辑高度依赖于FW的体系结构(每次都需要重新发明)。

    接近问题...

    从C / C++的 Angular 来看,FW和在适当的OS上运行的代码之间最重要的区别是MMIO。 MMIO访问具有副作用,对于读取和写入有不同的副作用。在桌面应用程序中,此概念不存在(除非您从用户空间戳硬件)。如果可以在读取或写入存储位置时定义一个挂钩,则可以进行适当的外设仿真,并且FW可以完整地编译。当然,这不能用C++来完成,本机语言的全部目的都与此相反。但是在调试的帮助下,内存调试器使用了相同的概念(跟踪内存访问运行时)。

    我对实现有一些想法,所以我的问题是您认为它们的可行性如何,或者还有其他方法可以实现相同的结果吗?
  • 完全没有工具。 x86可以发出信号,指示是否访问了内存位置,调试器使用它来实现观察点(内存访问中断)。作为概念证明,我创建了以下测试程序:
    #include <stdio.h>
    volatile int UDR;

    void read() { printf("UDR read\n"); }
    void write() { printf("UDR write\n"); }

    int main()
    {
    UDR=1;
    printf("%i\n", UDR);
    return 0;
    }

    UDR是我要跟踪的MMIO寄存器,如果我使用以下脚本在GDB下运行编译的程序,请执行以下操作:
    watch UDR
    commands
    call write()
    cont
    end

    rwatch UDR
    commands
    call read()
    cont
    end

    结果正是我想要的:
    UDR write
    UDR read
    1

    问题是我根本不知道它是否可以扩展。据我所知,监视点是一种有限的硬件资源,但无法确定x86的限制。我可能需要少于100个。GDB还支持软件观察点,但仅用于编写,因此实际上并不能用于此目的。另一个缺点是该代码只能在GDB session 下运行。
  • 运行时检测。如果我是正确的,Valgrind / libvex可以这样做:读取编译的二进制文件,然后在内存访问位置(以及其他许多位置)插入检测代码。我可以编写新的Valgrind工具,并将其配置为地址和回调作为上述GDB脚本,然后在应用程序中执行Valgrind session 。您认为这可行吗?我找到了一些有关创建新工具的文档,但这似乎并不容易。
  • 编译时间检测。 clang和gcc中的Memory and Address消毒器正以这种方式工作。这是一个分为两部分的游戏,编译器发出检测代码,并且一个消毒程序库(实现实际检查)链接到该应用程序。我的想法是用执行上述回调的自己的实现方式替换消毒剂库,而无需进行任何编译器修改(这可能超出了我的能力)。不幸的是,我没有找到很多有关所检测的代码和消毒程序库如何交互的文档,我只找到描述检查程序算法的论文。

  • 因此,这就是我的问题,对任何主题的任何评论都将受到赞赏。 :)

    最佳答案

    我没有时间回答您问题中的所有问题,但是要发表评论可能太久了...

    因此,关于调试器中的“监视点”,它们使用调试寄存器,并且您可以编写代码以自己使用这些寄存器(有API函数可以做到这一点-您需要处于内核模式才能写入这些寄存器),如下所示:声明自己,您将用光寄存器。这个数字也比您的100小得多。在x86处理器中,有4个调试位置寄存器,覆盖了对1-8字节宽位置的读取和/或写入。因此,如果您总共有少于32个字节的IO空间(分布在不超过4个块(每个块不超过8个字节)中),则它将起作用。

    选项2的问题是,您需要保证IO寄存器使用的区域不用于您的应用程序中的其他内容。如果所有的IO寄存器都位于前64KB中,这可能是“容易的”。否则,您必须尝试确定它是MMIO访问还是常规访问。除了编写自己的Valgrind版本之外,您并不会立即完成工作……即使您雇用了最初写valgrind的人,也是如此……

    在匹配地址方面,选项3与选项2的问题相同。我的感觉是,这对您没有太大帮助,而您最好以其他方式进行处理。

    我在各种芯片模拟器中看到的方法是将对实际硬件的访问修改为函数调用。您可以通过MSalters描述的方法在C++中实现。

    或通过修改代码来执行以下操作:

    MMIO_WRITE(UDR, 1);

    然后让 MMIO_WRITE转换为:
     #if REAL_HW
    MMIO_WRITE(x, y) x = y
    #else
    MMIO_WRITE(x, y) do_mmio_write(x, y)
    #endif
    do_mmio_write可以理解地址及其以某种方式做什么。

    当然,这就是我在工作中用于将我们即将制造的最新,最强大的GPU建模为芯片的GPU模型的方式,也是我所工作的前一家公司使用的GPU模型的模型。

    是的,您将不得不重写一些代码-理想情况下,编写代码时应使特定的小部分代码可以接触实际的硬件[如果您想从一种类型的微 Controller 迁移到另一种微 Controller ,这无疑是一个好习惯。另一个,因为在这种情况下,否则您还必须进行更多的重写]。

    正如马丁·詹姆斯(Martin James)指出的那样,任何此类仿真的问题在于,如果您的实际仿真器不是很好,就会遇到“兼容性问题”,尤其是诸如硬件与软件竞争条件之类的情况,其中您的软件与模拟的硬件模型,但实际的硬件将与软件异步执行操作,因此,两次读取两个寄存器将获得与软件模型不同的值,因为实际硬件中发生了一些任意更改,您的软件模型没有采用考虑到这一点-现在您有一个讨厌的错误之一,它只在蓝月亮中发生一次,并且仅在“无法调试”硬件变体上发生,而在软件模型中则从未发生。

    关于c++ - 在C/C++中检测内存IO以进行硬件仿真,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32266103/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com