修复 C/C++ 知识和寄存器访问方面的差距 [英] Fixing Gap in knowledge about C/C++ and register access

查看:22
本文介绍了修复 C/C++ 知识和寄存器访问方面的差距的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

诚然,我是 C++ 的新手.不幸的是,我看到的大多数代码要么使用 asm 调用,要么定义了一个 extern 函数,其主体位于汇编文件中.

Admittedly I am new to C++. Unfortunately most of the code I have seen either uses the asm call or defines an extern function whose body is in an assembly file.

这就是为什么我非常兴奋地找到以下代码.我已经研究 代码库 3 天了.

Which is why I was very exited to find the below code. I have been studying the codebase for 3 days now.

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

Syntactically I understand every line of the code below; except the one that counts because I don't get how it works!

  1. types.h 定义了 u32uintptr(我见过它们)
  2. ifdef __cplusplus 当 C 和 C++ 代码混合时需要.特别是因为 extern C 是 C++ 特定的.如果 gcc 具有 -fno-exceptions 参数
  3. ,则可以省略
  4. volatile 用于防止编译器进行任何优化,因为地址必须是完美的,因为它正在向/从寄存器写入/读取.
  1. types.h defines the u32 and uintptr ( I have seen them)
  2. ifdef __cplusplus is needed when C and C++ code are mixed. Particularly because of the extern C which is C++ specific. Can be omitted if gcc has the -fno-exceptions argument
  3. volatile is used to prevent the compiler from doing any optimization because the address has to be bite perfect since it's writing/reading to/from registers.

说了这么多,我仍然不知道这段代码实际上是如何写入或读取寄存器的.

All that said, I still have no idea how this code actually writes or reads from the register.

#include <circle/types.h>
#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 不是必须是物理地址吗

What does *(unsigned int *) mean? How is this used to read and write to a register? Wouldn't that nAddress have to be the physical address

推荐答案

您正在寻找 "内存映射输入输出".

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

The most common way for CPU to talk to external hardware is through memory bus - the same bus that is used to access normal memory.

首先,请记住,CPU 与内存的交互不仅涉及读取和写入,还涉及总线错误处理(无效访问)、仲裁(多个设备访问同一内存)和路由(CPU 可能想要访问多个存储设备).为了处理这个问题,使用了总线协议.

First, keep in mind that CPUs interaction with memory involves not just reads and writes, but also things like bus error handling (invalid access), arbitration (multiple devices accessing the same memory) and routing (CPU might want to access multiple memory devices). To handles this, a bus protocol is used.

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

To write or read external memory CPU has to initiate a transaction. The exact sequence of this is defined by used bus protocol, but it usually involves steps like:

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

发起事务的设备称为masterinitiator,而负责处理事务的设备称为slave目标.

The device that initiates transcation is called master or initiator, while device that is responsible for handling the transaction is called slave or target.

决定哪个从设备处理事务的设备称为解码器路由器.所以事务通常会从主设备到解码器,然后再到从设备.

Device that decides what slave handles the transaction is called decoder or router. So transaction will usually go from master to decoder and then to slave device.

总线协议本质上提供了一种向设备传输数据或从设备传输数据的方法.该设备可以是存储设备或其他任何设备.对于存储设备,其控制器通过向存储单元阵列写入数据或从存储单元阵列读取数据来处理事务.

Bus protocol essentialy provides a way to transfer data to or from a device. This device can be a memory device or anything else. In case of memory device, its controller handles transcations by writing or reading data to or from memory cell array.

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

If you are familiar with Object Oriented Programming, you can think of CPU being connected to external devices via interface which allows reading and writing at specified address. Implementation of this interface can do anything. And this how memory mapped I/O is done - CPU gets connected to a bunch of devices, each receiving transactions at specific address range. Write data to one address and this data will be received by memory device which will store it to memory cell array, write data to another address and it will be received by, lets say, SD controller and will be interpreted as "send SEND_STATUS command to SD card".

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

If you are also familiar with modern operating systems, you can think of "everything is a file" abstraction. Some files are just plain files e.g. they act as memory. Other files are different. Just like reading /proc/cpuinfo on Linux gives you information about your CPUs, reading at some address can provide you with information on what IRQs are currently pending or tell you how many incoming messages mailbox has at the moment.

总线协议的例子是AXIAHB.AHB 更简单,AXI 是更复杂和更快的协议.就 Raspberry PI 而言,它很可能是用于将 CPU 连接到硬件的 AXI 协议.

Examples of bus protocols are AXI and AHB. AHB is simpler, AXI is more complex and faster protocol. In case of Raspberry PI it is most likely AXI protocol that is used to connect CPU to hardware.

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

So regarding your question, those two functions are used to access registers of external devices via memory mapped I/O. Everything else you got right:

  • volatile 用于防止编译器删除、重新排序或以任何方式更改这些内存访问 - 没有这个硬件将无法执行我们想要的操作.
  • 使用
  • u32 是因为许多设备甚至不支持 32 位访问以保持硬件简单.
  • volatile is used to prevent compiler from removing, reordering or in any way changing those memory accesses - without this hardware will not do what we want it to do.
  • u32 is used because many device will not even support anything but 32-bit accesses to keep hardware simple.

这篇关于修复 C/C++ 知识和寄存器访问方面的差距的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆