找回密码
 注册
关于网站域名变更的通知
查看: 707|回复: 1
打印 上一主题 下一主题

关于ARM的统一编制与内存映射机制

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2021-1-28 13:53 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
       从CPU连出来一把线:数据总线、地址总线、控制总线,这把线上挂着N个接口,有相同的,有不同的,名字叫做存储器接口、中断控制接口、DMA接口、并行接口、串行接口、AD接口……一个设备要想接入,就用自己的接口和总线上的某个匹配接口对接……于是总线上出现了各种设备:内存、硬盘,鼠标、键盘,显示器……# C; x0 }  k7 l/ x3 j: j  p
        对于CPU而言,如果它要发数据到某个设备,其实是发到对应的接口,接口电路里有多个寄存器(也称为端口),访问设备实际上是访问相关的端口,所有的信息会由接口转给它的设备。那么CPU会准备数据到数据总线,但是诸多接口,该发给谁呢?这时就须要为各接口分配一个地址,然后把地址放在地址总线上,需要的控制信息放到控制总线上,就可以和设备通信了。
( b( Z2 c' @0 ^6 o2 P, Y6 j' S        对一个系统而言,通常会有多个外设,每个外设的接口电路中,又会有多个端口,每个端口都需要一个地址,为他们标识一个具体的地址值,是系统必须解决的事,与此同时,你还有个内存条,可能是512M或1G或更大的金士顿、现代ddr2之类,他们的每一个地址也都需要分配一个标识值,另外,很多外设有自己的内存、缓冲区,就像你的内存条一样,你同样需要为它们分配内存……你的CPU可能需要和它们的每一个字节都打交道,所以:别指望偷懒,它们的每一寸土地都要规划好!这听起来就很烦,做起来可能就直接导致脑细胞全部阵亡。但事情总是得有人去做,ARM可能会这样做:他这次设计的CPU是32位的,最多也就能寻址2^32=4G空间,于是把这4GB空间丢给内存和端口,让他们瓜分。但英特尔或许有更好的分配方式……8 t% E7 T9 z5 c0 n! C
(一)地址的概念; c! p- s5 K7 m3 U7 R. \) M
1)物理地址:CPU地址总线传来的地址,由硬件电路控制其具体含义。物理地址中很大一部分是留给内存条中的内存的,但也常被映射到其他存储器上(如显存、BIOS等)。在程序指令中的虚拟地址经过段映射和页面映射后,就生成了物理地址,这个物理地址被放到CPU的地址线上。( B5 _0 k% {2 ~) z3 b3 }
        物理地址空间,一部分给物理RAM(内存)用,一部分给总线用,这是由硬件设计来决定的,因此在32 bits地址线的x86处理器中,物理地址空间是2的32次方,即4GB,但物理RAM一般不能上到4GB,因为还有一部分要给总线用(总线上还挂着别的许多设备)。在PC机中,一般是把低端物理地址给RAM用,高端物理地址给总线用。; G" J6 t. U1 j* Z, }. q% u/ R
  d: V5 F2 _& Q: n  z! d; N- B
2)总线地址:总线的地址线或在地址周期上产生的信号。外设使用的是总线地址,CPU使用的是物理地址。0 H# x) |: s3 C- {
        物理地址与总线地址之间的关系由系统的设计决定的。在x86平台上,物理地址就是总线地址,这是因为它们共享相同的地址空间——这句话有点难理解,详见下面的“独立编址”。在其他平台上,可能需要转换/映射。比如:CPU需要访问物理地址是0xfa000的单元,那么在x86平台上,会产生一个PCI总线上对0xfa000地址的访问。因为物理地址和总线地址相同,所以凭眼睛看是不能确定这个地址是用在哪儿的,它或者在内存中,或者是某个卡上的存储单元,甚至可能这个地址上没有对应的存储器。
1 d0 V8 _8 Q6 [9 e7 x- }: v! T3)虚拟地址:现代操作系统普遍采用虚拟内存管理(Virtual Memory Management)机制,这需要MMU(Memory Management Unit)的支持。MMU通常是CPU的一部分,如果处理器没有MMU,或者有MMU但没有启用,CPU执行单元发出的内存地址将直接传到芯片引脚上,被内存芯片(物理内存)接收,这称为物理地址(Physical Address),如果处理器启用了MMU,CPU执行单元发出的内存地址将被MMU截获,从CPU到MMU的地址称为虚拟地址(Virtual Address),而MMU将这个地址翻译成另一个地址发到CPU芯片的外部地址引脚上,也就是将虚拟地址映射成物理地址。
3 e5 i% u+ R; Z, K1 ~" x" i; K; H1 `        Linux中,进程的4GB(虚拟)内存分为用户空间、内核空间。用户空间分布为0~3GB(即PAGE_OFFSET,在0X86中它等于0xC0000000), k, F) ?: k" O' n5 H1 r
,剩下的1G为内核空间。程序员只能使用虚拟地址。系统中每个进程有各自的私有用户空间(0~3G),这个空间对系统中的其他进程是不可见的。/ d; D! r% |, }" R$ w1 T1 t; O
        CPU发出取指令请求时的地址是当前上下文的虚拟地址,MMU再从页表中找到这个虚拟地址的物理地址,完成取指。同样读取数据的也是虚拟地址,比如mov ax, var. 编译时var就是一个虚拟地址,也是通过MMU从也表中来找到物理地址,再产生总线时序,完成取数据的。$ b" k* E! {* `+ O
(二)编址方式
% y2 x- N; E  @6 T: l. k1)外设都是通过读写设备上的寄存器来进行的,外设寄存器也称为“I/O端口”,而IO端口有两种编址方式:独立编址和统一编制。
8 {' I, x% s+ M* W, d9 R3 u        统一编址:外设接口中的IO寄存器(即IO端口)与主存单元一样看待,每个端口占用一个存储单元的地址,将主存的一部分划出来用作IO地址空间,如,在PDP-11中,把最高的4K主存作为IO设备寄存器地址。端口占用了存储器的地址空间,使存储量容量减小。  n# ^8 ^# m  f$ k
       统一编址也称为“I/O内存”方式,外设寄存器位于“内存空间”(很多外设有自己的内存、缓冲区,外设的寄存器和内存统称“I/O空间”)。
: H, L1 k0 J! u! Y; E. t# |3 Y        如,Samsung的S3C2440,是32位ARM处理器,它的4GB地址空间被外设、RAM等瓜分:+ ?. ^  x/ A& v/ Q; @
0x8000 1000     LED 8*8点阵的地址( w+ h# F+ V. ], D
0x4800 0000 ~ 0x6000 0000   SFR(特殊暂存器)地址空间% ^6 j  ?( r! V; X2 L( I5 P
0x3800 1002    键盘地址
- m9 C) @0 \, S" U; A8 L3 J0x3000 0000 ~ 0x3400 0000   SDRAM空间
$ L+ b0 b+ i+ P) b0 X: T  l& p' D0x2000 0020 ~ 0x2000 002e   IDE
+ ?/ G6 g0 X$ T7 N1 w0 j* H5 |2 R0x1900 0300    CS8900
; J; D: r( A  G2 [+ j8 _/ Q        独立编址(单独编址):IO地址与存储地址分开独立编址,I/0端口地址不占用存储空间的地址范围,这样,在系统中就存在了另一种与存储地址无关的IO地址,CPU也必须具有专用与输入输出操作的IO指令(IN、OUT等)和控制逻辑。独立编址下,地址总线上过来一个地址,设备不知道是给IO端口的、还是给存储器的,于是处理器通过MEMR/MEMW和IOR/IOW两组控制信号来实现对I/O端口和存储器的不同寻址。如,intel 80x86就采用单独编址,CPU内存和I/O是一起编址的,就是说内存一部分的地址和I/O地址是重叠的。
$ g; K0 s' A7 j$ A        独立编址也称为“I/O端口”方式,外设寄存器位于“I/O(地址)空间”。
/ j+ b8 t4 g! L  Q8 U, P# [: P        对于x86架构来说,通过IN/OUT指令访问。PC架构一共有65536个8Bit的I/O端口,组成64K个I/O地址空间,编号从0~0xFFFF,有16位,80x86用低16位地址线A0-A15来寻址。连续两个8bit的端口可以组成一个16bit的端口,连续4个组成一个32bit的端口。I/O地址空间和CPU的物理地址空间是两个不同的概念,例如I/O地址空间为64K,一个32bit的CPU物理地址空间是4G。如,在Intel 8086+Redhat9.0 下用“more /proc/ioports”可看到:  T& j! |* j! C' U- \+ @
0000-001f : dma1: X& w* e' l! k, y- k0 m1 H% P* N
0020-003f : pic1
. J4 ~0 \7 j5 {9 N0040-005f : timer
1 ^$ G( s0 q% u) N, `0060-006f : keyboard: J" P2 a% X  W4 X; _
0070-007f : rtc( K# L! A8 @& w1 ]! A) V
0080-008f : dma page reg! Q' J8 {2 K. l) O$ Q+ S. K6 e' q( p
00a0-00bf : pic2, H1 o% H" k* X2 u3 h* g
00c0-00df : dma2! ?9 |9 F( C* K  R
00f0-00ff : fpu" d/ A4 Y6 m* c
0170-0177 : ide1
& k5 ]: d* y. [4 s' f……8 N* n, \0 F9 \6 i- T4 K/ x* D' a
        不过Intel x86平台普通使用了名为内存映射(MMIO)的技术,该技术是PCI规范的一部分,IO设备端口被映射到内存空间,映射后,CPU访问IO端口就如同访问内存一样。看Intel TA 719文档给出的x86/x64系统典型内存地址分配表:7 G8 F9 c( x& [5 h+ o, B
系统资源   占用5 X* P: s+ F2 c2 t* ]7 A
------------------------------------------------------------------------) v0 V. C9 p4 h4 W* K
BIOS   1M" w4 M5 X' y) v. {
本地APIC   4K
  E4 Y7 m5 B9 U* y# X4 V芯片组保留 2M
2 {0 c8 e% \6 F& Y2 L) UIO APIC   4K
  P) K, u. g$ f8 y. ]! ?7 h  TPCI设备   256M
' g' q& {: m  v( @' v5 c$ q$ ], l! tPCI Express设备 256M( X8 p; C6 B" l3 r0 |  i
PCI设备(可选) 256M
! Y3 B. y% K% h, k" W* p显示帧缓存 16M
5 k# X& ?+ o' g- [+ QTSEG   1M! D6 @, r& A  s7 o
        对于某一既定的系统,它要么是独立编址、要么是统一编址,具体采用哪一种则取决于CPU的体系结构。 如,PowerPC、m68k等采用统一编址,而X86等则采用独立编址,存在IO空间的概念。目前,大多数嵌入式微控制器如ARM、PowerPC等并不提供I/O空间,仅有内存空间,可直接用地址、指针访问。但对于Linux内核而言,它可能用于不同的CPU,所以它必须都要考虑这两种方式,于是它采用一种新的方法,将基于I/O映射方式的或内存映射方式的I/O端口通称为“I/O区域”(I/O region),不论你采用哪种方式,都要先申请IO区域:request_resource(),结束时释放它:release_resource()。0 h  d9 w" m, {9 p  b8 v
# U$ s$ R/ ?/ r5 b
2)对外设的访问
1 e- g0 `1 m4 ]* I& R1、访问I/O内存的流程是:request_mem_region() -> ioremap() -> ioread8()/iowrite8() -> iounmap() -> release_mem_region() 。
5 J6 ?  u' Z  ?; c6 E; I1 Y. m        前面说过,IO内存是统一编址下的概念,对于统一编址,IO地址空间是物理主存的一部分,对于编程而言,我们只能操作虚拟内存,所以,访问的第一步就是要把设备所处的物理地址映射到虚拟地址,Linux2.6下用ioremap():8 n- m$ {% U# E& A& p3 ]
        void *ioremap(unsigned long offset, unsigned long size);
& P+ g- W$ ^  h+ o0 [; n3 m然后,我们可以直接通过指针来访问这些地址,但是也可以用Linux内核的一组函数来读写:
# \% G8 d. n; R# k5 Uioread8(), iowrite16(), ioread8_rep(), iowrite8_rep()......
( c4 [6 c: W4 H& C/ l. d" h2、访问I/O端口1 W% |5 Z$ n2 f1 p9 c! b
        访问IO端口有2种途径:I/O映射方式(I/O-mapped)、内存映射方式(Memory-mapped)。前一种途径不映射到内存空间,直接使用intb()/outb()之类的函数来读写IO端口;后一种MMIO是先把IO端口映射到IO内存(“内存空间”),再使用访问IO内存的函数来访问IO端口。( r7 G% o$ m, D" @* C% n
        void ioport_map(unsigned long port, unsigned int count);
: H0 {7 |( {: J5 k4 p. _+ G7 i7 }通过这个函数,可以把port开始的count个连续的IO端口映射为一段“内存空间”,然后就可以在其返回的地址是像访问IO内存一样访问这些IO端口。
7 A% k8 F  }& S0 L3,优缺点比较& h! s. j& }- q' G7 B* \% B2 t2 Q* K
  I/0独立编址的优点是:不占用内存空间;使用I/O指令,程序清晰,很容易看出是I/O操作还是存储器操作;译码电路比较简单(因为I/0端口的地址空间一般较小,所用地址线也就较少)。其缺点是:只能用专门的I/0指令,访问端口的方法不如访问存储器的方法多。4 {9 C4 z1 g( F+ h( n, i
究竟采用哪一种取决于系统的总体设计。在一个系统中也可以同时使用两种方式,前提是首先要支持I/O独立编址。:Intel的x86微处理器都支持I/O独立编址,因为它们的指令系统中都有I/O指令,并设置了可以区分I/O访问和存储器访问的控制信号引脚。而一些微处理器或单片机,为了减少引脚,从而减少芯片占用面积,不支持I/O独立编址,只能采用存储器统一编址。5 Y: a: J0 M( s9 M7 w
  • TA的每日心情
    慵懒
    2022-12-26 15:28
  • 签到天数: 1 天

    [LV.1]初来乍到

    2#
    发表于 2021-1-28 15:02 | 只看该作者
    物理、总线、虚拟
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-11-24 19:37 , Processed in 0.187500 second(s), 23 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表