修复关于C / C ++和注册访问的知识
不可否认,我是C ++的新手。 不幸的是,我见过的大多数代码都使用asm
调用或定义了一个extern
函数,它的主体位于汇编文件中。
这就是为什么我很想找到下面的代码。 我已经研究了3天的代码库 。
从语法上讲,我理解下面代码的每一行; 除了那个重要的因为我不知道它是如何工作的!
- types.h定义了
uintptr
和uintptr
(我见过它们) - 当C和C ++代码混合时需要ifdef __cplusplus。 特别是因为
extern C
是C ++特定的。 如果gcc具有-fno-exceptions
参数,则可以省略 -
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的设备称为master或initiator ,而负责处理事务的设备称为slave或target 。
决定从属设备处理事务的设备称为解码器或路由器 。 因此,事务通常从主设备到解码器再到从设备。
总线协议必须提供一种向设备传输数据或从设备传输数据的方法。 该设备可以是存储设备或其他任何设备。 在存储器设备的情况下,其控制器通过向存储器单元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
的指针的类型转换。 解除引用运算符*
用于访问该指针指向的内存位置。