Search code examples
hardwarecommunicationcpu

How does cpu communicate with peripherals?


i assume cpu has direct access to motherboard's BIOS and RAM.(correct me if i'm wrong)

But how does cpu communicate with other hardware like hdds, expansion cards, peripherals, other BIOSes etc.?

I know about OS and its drivers, but they are software- they're in RAM. How does cpu communicate with all this hardware on hardware level? Isn't it limited to only motherboard's BIOS and RAM?


Solution

  • In older architectures, peripherals were accessed via a separate mechanism to memory access with special I/O instructions. On x86, there were (and still are!) "in" and "out" instructions for transferring bytes between the CPU and a peripheral. Peripherals were given addresses, for example 0x80 for the keyboard. Simplifying a lot, doing "in 0x80" would read a byte from the keyboard controller to CPU register "AL".

    On modern architectures, peripherals are accessed in a similar way to memory: via mapped memory addresses on a bus. You shouldn't think of a bus as a way to access memory. It's more a way to address individual peripherals, of which memory (RAM/DDR) is just one type. For example, you might have 2GB of RAM at addresses 0x00000000..0x7fffffff. After that you might have a graphics card at 0x80000000..0x80001fff. The bus controller (PCIe or whatever) knows which address ranges go to which peripheral.

    Memory is usually special in that it can be cached, so individual reads/writes to memory tend not to translate directly to individual reads/writes to the RAM chips. Peripherals are marked as special - CPU accesses should go out to the peripheral exactly as written in your program.

    The language you talk to peripherals with is pretty much ad-hoc depending on the device. The general theme is that the peripheral is mapped somewhere in memory (e.g 0x80000000 for a few KB as above), with individual bit of state and actions controlled by different words (usually 32 or 64bit). A mythical example of a serial port at 0x80000000:

    • Write 32 bit word 'A' to 0x80000000, queueing up character 'A' in its output FIFO.
    • Write 32 bit word 0x1 to 0x80000004, which tells the serial port to send its queue.

    Again, totally made up just for sake of example, but a real serial port (uart) isn't all that different.

    The trouble is you won't actually see any of the above memory layout in a modern OS, because of virtual memory. The addresses above would be referred to as "physical memory addresses" (or bus addresses) - the actual addresses that go out onto the bus. The CPU instead sees virtual memory addresses. Individual peripherals will need to be mapped into virtual address space. This is kind of complicated to explain and probably best off in another Question, but the point is you're unlikely to access a peripheral by its actual physical address in a modern OS.