How does cpu communicate with peripherals?
The accepted answer is accurate, of course, but perhaps DrStrangeLove intended to address something else or, at least, the question acommodates another answers. In fact, when someone asks "How does the peripherals communicate with the CPU on the hardware level?", I think the answer should mention the role of the I/O modules (like the I/O adapters everyone knows about). This is important to emphasize because part of the logic required to speak with the I/O devices is embedded within the I/O modules, decreasing the need for CPU's attention when performing I/O operations. This sounds relevant to me in the context of the question because it asks about the hardware aspects of the I/O operations, and the adapters are hardware pieces that abstract the intrinsicacies of the I/O devices, hidding their complexities from the CPU (and from the OS as well). For example, disk adapters hide the disks' geometry aspects, freeing the CPU from running the logic required to spin the disks' plates, locate a cylinder and wait for the correct sector to pass under the read/write heads. Similar reasonings apply to other devices as video adapters, network cards and so on. In a nutshell, without the I/O modules the I/O tasks would overwhelm the CPU. To quote Stallings:
An I/O module functions to allow the processor to view a wide range of devices in a simple-minded way. There is a spectrum of capabilities that may be provided. The I/O module may hide the details of timing, formats, and the electromechanics of an external device so that the processor can function in terms of simple read and write commands, and possibly open and close file commands. In its simplest form, the I/O module may still leave much of the work of controlling a device (e.g., rewind a tape) visible to the processor. An I/O module that takes on most of the detailed processing burden, presenting a high-level interface to the processor, is usually referred to as an I/O channel or I/O processor. An I/O module that is quite primitive and requires detailed control is usually referred to as an I/O controller or device controller. I/O controllers are commonly seen on microcomputers, whereas I/O channels are used on mainframes.
Besides, as John Ripley correctly stated, there is an I/O space that is mapped in the same fashion as the RAM is. Indeed, the peripherals could be mapped directly into the memory address space (which is known as MMIO, Memory Mapped I/O), or in a separated adress space (PMIO, Port-Mapped I/O, which is also called "isolated I/O" because, unlike MMIO, the I/O addresses are entirely separated from those of the computer's RAM. That's why you have to use the in and out instructions to communicate with devices using PMIO).
By the above, either MMIO and PMIO treat I/O devices as memory positions - which is the essence of how the hardware deals with I/O operations, but some further details are worth mentioning here in order to get the rich conceptual load involved in I/O. Since each adapter have a limited address range, we must understand that such memory positions work as data buffers, which means that you have only a few bytes ("data blocks") to communicate with the device at a time. For this reason, it is common that the CPU does NOT use directly the data it reads from those memory positions: first, the data is read from the I/O device via the corresponding address, next this data is stored into the RAM and only then the CPU can use it. In order to get that, think in a large binary file that the CPU must execute: the disk adapter have a limited buffer bounded by its I/O addressing space (note I am not referring to the internal buffers of the adapter, but to its address space as seen by the CPU), so the adapter reads some data from the disk and warns the CPU when the buffer fills via an interrupt; next, the CPU interrupts whatever it is doing, reads the buffer, copies the buffer's content into the RAM and signals the adapter it can continue to bring more data from the disk. This cycle repeats until the binary file if completely loaded into the RAM. From that point, the read operation is declared finished and the file can finally be executed.
This cycle is is called interrupt-driven I/O and occurs totally in hardware (with some OS support to handle the interrupts), but please note that there are another two options to perform I/O operations. It is also possible to employ the so called PIO (Programmable I/O) where instead of using the interrupt mechanism, the CPU continuously pools the controller via a loop until all the required information is gathered, block-by block (when the last block is retrieved, the loop ends). Both interrupt-driven I/O and PIO wastes CPU time (particularly the latter) and have been superseded long time ago by Direct Memory Access (DMA), which allows the I/O device write (or read) its data directly to (or from) the RAM as instructed by the CPU. Because of its pooling nature, I believe PIO is fully implemented in software, but I could be wrong at this point. I have to remark that, although DMA made PIO and interrupt-driven I/O outdated, I am not sure both were banned from modern architetures.
Please correct me if I am wrong. Basically peripheral device communicate with processor using PCI(peripheral component interconnect), it a type of bus that connect the device directly to the processor. when a command is given through a peripheral device (say any button press on keyboard) it is converted into binary code and stored in memory in the form of cache memory and then required execution for that particular function is done by the processor.
That depends on what you mean by "direct access". A CPU core communicates with main memory (RAM) over a bus. (The core may have more direct access to relatively small amounts of memory (cache or registers), but that's a different issue.) The CPU also communicates with peripherals via buses. Some types of buses you might have heard about are universal serial bus (USB; typically for external devices), PCI, front-side bus (a type of bus connecting CPU cores and main memory), or Serial-ATA (SATA; often used for devices like hard disks).
ETA: I mentioned that in my comment below that device drivers handle the hardware-level communication between CPU and peripheral. The actual mechanics of the communication can involve using specific portions of the address space to transfer data (memory-mapped I/O), so that physically reading from or writing to a device looks like accessing ordinary memory. The device driver also deals with how a CPU will respond to interrupts from a device.
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.