修复关于C / C ++和注册访问的知识

不可否认,我是C ++的新手。 不幸的是,我见过的大多数代码都使用asm调用或定义了一个extern函数,它的主体位于汇编文件中。

这就是为什么我很想找到下面的代码。 我已经研究了3天的代码库 。

从语法上讲,我理解下面代码的每一行; 除了那个重要的因为我不知道它是如何工作的!

  1. types.h定义了uintptruintptr (我见过它们)
  2. 当C和C ++代码混合时需要ifdef __cplusplus。 特别是因为extern C是C ++特定的。 如果gcc具有-fno-exceptions参数,则可以省略
  3. volatile用于防止编译器进行任何优化,因为地址必须是完美的,因为它是从寄存器写入/读取的。

总而言之,我仍然不知道这段代码实际上是如何从寄存器写入或读取的。

 #include  #ifdef __cplusplus extern "C" { #endif static inline u32 read32 (uintptr nAddress) { return *(u32 volatile *) nAddress; } static inline void write32 (uintptr nAddress, u32 nValue) { *(u32 volatile *) nAddress = nValue; } #ifdef __cplusplus } #endif #endif 

*(unsigned int *)是什么意思? 这是如何用来读写寄存器的? 那个nAddress是物理地址吗?

您正在寻找“ 内存映射输入输出 ”。

CPU与外部硬件通信的最常见方式是通过内存总线 – 用于访问普通内存的相同总线。

首先,请记住,CPU与内存的交互不仅涉及读写,还涉及总线error handling(无效访问),仲裁(访问同一内存的多个设备)和路由(CPU可能希望访问多个内存设备)等问题。 。 为了处理这个问题,使用总线协议

要写入或读取外部存储器,CPU必须启动事务 。 它的确切顺序由使用的总线协议定义,但它通常包括以下步骤:

  • 发送交易地址,类型,长度等。
  • 接收响应 – 允许或拒绝(总线错误)。
  • 如果允许交易,则传输实际数据。

启动transcation的设备称为masterinitiator ,而负责处理事务的设备称为slavetarget

决定从属设备处理事务的设备称为解码器路由器 。 因此,事务通常从主设备到解码器再到从设备。

总线协议必须提供一种向设备传输数据或从设备传输数据的方法。 该设备可以是存储设备或其他任何设备。 在存储器设备的情况下,其控制器通过向存储器单元arrays写入或从存储器单元arrays读取数据来处理转换。

如果您熟悉面向对象编程,则可以将CPU视为通过接口连接到外部设备,该接口允许在指定地址进行读写。 这个接口的实现可以做任何事情 。 这就是内存映射I / O的完成方式–CPU连接到一堆设备,每个设备接收特定地址范围的事务。 将数据写入一个地址,这些数据将由存储设备接收,存储设备将其存储到存储单元arrays,将数据写入另一个地址,它将由SD控制器接收,并将被解释为“发送SEND_STATUS命令到SD卡”。

如果您也熟悉现代操作系统,您可以想到“一切都是文件”抽象。 有些文件只是普通文件,例如它们充当内存。 其他文件不同。 就像在Linux上读取/proc/cpuinfo为您提供有关CPU的信息一样,在某个地址读取可以为您提供有关IRQ当前待处理的信息,或者告诉您邮箱目前有多少传入邮件。

总线协议的示例是AXI和AHB 。 AHB更简单,AXI更复杂,协议更快。 在Raspberry PI的情况下,很可能是用于将CPU连接到硬件的AXI协议。


因此,关于您的问题,这两个函数用于通过内存映射I / O访问外部设备的寄存器。 你得到的其他一切:

  • volatile用于防止编译器删除,重新排序或以任何方式更改这些内存访问 – 没有这些硬件将无法执行我们想要的操作。
  • u32是因为许多设备甚至不支持32位访问以保持硬件简单。

(unsigned int *)是指向unsigned int的指针的类型转换。 解除引用运算符*用于访问该指针指向的内存位置。