1. 程式人生 > >統一編址和獨立編址以及I/O埠與記憶體

統一編址和獨立編址以及I/O埠與記憶體

1、編址方式

1)外設都是通過讀寫裝置上的暫存器來進行的,外設暫存器也稱為“I/O埠”,而IO埠有兩種編址方式:獨立編址和統一編址。

統一編址:外設介面中的IO暫存器(即IO埠)與主存單元一樣看待,每個端口占用一個儲存單元的地址,將主存的一部分劃出來用作IO地址空間,如,在PDP-11中,把最高的4K主存作為IO裝置暫存器地址。端口占用了儲存器的地址空間,使儲存量容量減小。

統一編址也稱為“I/O記憶體”方式,外設暫存器位於“記憶體空間”(很多外設有自己的記憶體、緩衝區,外設的暫存器和記憶體統稱“I/O空間”)。

如,Samsung的S3C2440,是32位ARM處理器,它的4GB地址空間被外設、RAM等瓜分:

0x8000 1000 LED 8*8點陣的地址

0x4800 0000 ~ 0x6000 0000 SFR(特殊暫存器)地址空間

0x3800 1002 鍵盤地址

0x3000 0000 ~ 0x3400 0000 SDRAM空間

0x2000 0020 ~ 0x2000 002e IDE

0x1900 0300 CS8900

獨立編址(單獨編址):IO地址與儲存地址分開獨立編址,I/0埠地址不佔用儲存空間的地址範圍,這樣,在系統中就存在了另一種與儲存地址無關的IO地址,CPU也必須具有專用與輸入輸出操作的IO指令(IN、OUT等)和控制邏輯。獨立編址下,地址總線上過來一個地址,裝置不知道是給IO埠的、還是給儲存器的,於是處理器通過MEMR/MEMW和IOR/IOW兩組控制訊號來實現對I/O埠和儲存器的不同定址。如,intel 80x86就採用單獨編址,CPU記憶體和I/O是一起編址的,就是說記憶體一部分的地址和I/O地址是重疊的。

獨立編址也稱為“I/O埠”方式,外設暫存器位於“I/O(地址)空間”。

對於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”可看到:

0000-001f : dma1

0020-003f : pic1

0040-005f : timer

0060-006f : keyboard

0070-007f : rtc

0080-008f : dma page reg

00a0-00bf : pic2

00c0-00df : dma2

00f0-00ff : fpu

0170-0177 : ide1

……

不過Intel x86平臺普通使用了名為記憶體對映(MMIO)的技術,該技術是PCI規範的一部分,IO裝置埠被對映到記憶體空間,對映後,CPU訪問IO埠就如同訪問記憶體一樣。看Intel TA 719文件給出的x86/x64系統典型記憶體地址分配表:

系統資源 佔用

BIOS 1M

本地APIC 4K

晶片組保留 2M

IO APIC 4K

PCI裝置 256M

PCI Express裝置 256M

PCI裝置(可選) 256M

顯示幀快取 16M

TSEG 1M

對於某一既定的系統,它要麼是獨立編址、要麼是統一編址,具體採用哪一種則取決於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()。

2)對外設的訪問

1、訪問I/O記憶體的流程是: 
request_mem_region() -> ioremap() -> ioread8()/iowrite8() -> iounmap() -> release_mem_region() 。

前面說過,IO記憶體是統一編址下的概念,對於統一編址,IO地址空間是物理主存的一部分,對於程式設計而言,我們只能操作虛擬記憶體,所以,訪問的第一步就是要把裝置所處的實體地址對映到虛擬地址,Linux2.6下用ioremap():

void *ioremap(unsigned long offset, unsigned long size);

然後,我們可以直接通過指標來訪問這些地址,但是也可以用Linux核心的一組函式來讀寫:

ioread8(), iowrite16(), ioread8_rep(), iowrite8_rep()……

2、訪問I/O埠

訪問IO埠有2種途徑:I/O對映方式(I/O-mapped)、記憶體對映方式(Memory-mapped)。前一種途徑不對映到記憶體空間,直接使用intb()/outb()之類的函式來讀寫IO埠;後一種MMIO是先把IO埠對映到IO記憶體(“記憶體空間”),再使用訪問IO記憶體的函式來訪問IO埠。

void ioport_map(unsigned long port, unsigned int count);

通過這個函式,可以把port開始的count個連續的IO埠對映為一段“記憶體空間”,然後就可以在其返回的地址是像訪問IO記憶體一樣訪問這些IO埠。

I/0獨立編址的優點是:不佔用記憶體空間;使用I/O指令,程式清晰,很容易看出是I/O操作還是儲存器操作;譯碼電路比較簡單(因為I/0埠的地址空間一般較小,所用地址線也就較少)。其缺點是:只能用專門的I/0指令,訪問埠的方法不如訪問儲存器的方法多。 
究竟採用哪一種取決於系統的總體設計。在一個系統中也可以同時使用兩種方式,前提是首先要支援I/O獨立編址。:Intel的x86微處理器都支援I/O獨立編址,因為它們的指令系統中都有I/O指令,並設定了可以區分I/O訪問和儲存器訪問的控制訊號引腳。而一些微處理器或微控制器,為了減少引腳,從而減少晶片佔用面積,不支援I/O獨立編址,只能採用儲存器統一編址。