This commit is contained in:
Miloslav Ciz 2024-02-19 23:59:22 +01:00
parent a508d177e9
commit 2eb10d9bba
15 changed files with 1821 additions and 1703 deletions

4
cpu.md
View file

@ -20,7 +20,7 @@ Let's take a look at how a typical CPU works. Remember that anything may differ
The CPU has some internal state (we can see it as a [state machine](finite_state_machine.md)), i.e. it has a few internal variables, called **[registers](register.md)**; these are NOT variables in RAM but rather in the CPU itself, there is only a few of them (there may be let's say 32) but they are extremely fast. What exactly these registers are, what they are called, how many [bits](bit.md) they can hold and what their purpose is depends again on the instruction set architecture. However there are usually a few special registers, notably the **program counter** which holds the address of the currently executed instruction. After executing an instruction program counter is incremented so that in the nest step the next instruction will be executed, AND we can also modify program counter (sometimes directly, sometimes by specialized instructions) to jump between instruction to implement branching, loops, function calls etc.
So at the beginning (when powered on) the CPU is set to some initial state, most notably it sets its program counter to some initial value (depending on each CPU, it may be e.g. 0) so that it points to the first instruction of the program. Then it performs so called **fetch, decode, execute** cycle, i.e. it reads the instruction, decodes what it means and does what it says. In simpler CPUs this functionality is hard wired, however more complex CPUs are programmed in so called [microcode](microcode.md), a code yet at the lower level than machine code, machine code execution is programmed in microcode -- microcode is something like "[firmware](firmware.md) for the CPU" (or a "CPU [shader](shader.md)"?), it basically allows later updates and reprogramming of how the CPU internally works. However this is pretty [overcomplicated](bloat.md) and you shouldn't make CPUs like this.
So at the beginning (when powered on) the CPU is set to some initial state, most notably it sets its program counter to some initial value (depending on each CPU, it may be e.g. 0) so that it points to the first instruction of the program. Then it performs so called **fetch, decode, execute** cycle, i.e. it reads the instruction, decodes what it means and does what it says. In simpler CPUs this functionality is hard wired, however more complex CPUs (especially [CISC](cisc.md)) are programmed in so called [microcode](microcode.md), a code yet at the lower level than machine code, machine code execution is programmed in microcode -- microcode is something like "[firmware](firmware.md) for the CPU" (or a "CPU [shader](shader.md)"?), it basically allows later updates and reprogramming of how the CPU internally works. However this is pretty [overcomplicated](bloat.md) and you shouldn't make CPUs like this.
A CPU works in **clock cycles**, i.e. it is a sequential circuit which has so called *clock* input; on this input voltage periodically switches between high and low (1 and 0) and each change makes the CPU perform another operation cycle. How fast the clock changes is determined by the clock **frequency** (nowadays usually around 3 GHz) -- the faster the frequency, the faster the CPU will compute, but the more it will also heat up (so we can't just set it up arbitrarily high, but we can [overclock](overclocking.md) it a bit if we are cooling it down). WATCH OUT: **one clock cycle doesn't necessarily equal one executed instruction**, i.e. frequency of 1 Hz doesn't have to mean the CPU will execute 1 instruction per second because executing an instruction may take several cycles (how many depends on each instruction and also other factors). The number saying how many cycles an instruction takes is called CPI (cycles per instruction) -- CPUs try to aim for CPI 1, i.e. they try to execute 1 instruction per cycle, but they can't always do it.
@ -38,7 +38,7 @@ Mainstream consoomer CPUs nowadays have multiple **[cores](core.md)** so that ea
**[Interrupts](interrput.md)** are an important concept for the CPU and for low level programming, they play a role e.g. in saving power -- high level programmers often don't know what interrupts are, to those interrupts can be likened to "event [callbacks](callback.md)". An interrupt happens on some kind of even, for example when a key is pressed, when timer ticks, when error occurred etc. (An interrupt can also be raised by the CPU itself, this is how operating system [syscalls](syscall.md) are often implemented). What kinds of interrupts there are depends on each CPU architecture (consult your datasheet) and one can usually configure which interrupts to enable and which "callbacks" to use for them -- this is often done through so called **[vector](vector.md) table**, a special area in memory that records addresses ("vectors") of routines (functions/subprograms) to be called on specified interrupts. When interrupt happens, the current program execution is paused and the CPU automatically jumps to the subroutine for handling the interrupt -- after returning from the subroutine the main program execution continues. Interrupts are contrasted with **[polling](polling.md)**, i.e. manually checking some state and handling things as part of the main program, e.g. executing an infinite loop in which we repeatedly check keyboard state until some key is pressed. However polling is inefficient, it wastes power by constantly performing computation just by waiting -- interrupts on the other hand are a hard wired functionality that just performs a task when it happens without any overhead of polling. Furthermore interrupts can make programming easier (you save many condition checks and memory reads) and mainly **interrupts allow CPU to go into sleep mode** and so save a lot of power. When a CPU doesn't have any computation to do, it can stop itself and go into waiting state, not executing any instructions -- however interrupts still work and when something happens, the CPU jumps back in to work. This is typically what the `sleep`/`wait` function in your programming language does -- it puts the CPU to sleep and sets a timer interrupt to wake up after given amount of time. As a programmer you should know that you should call this sleep/wait function in your main program loop to relieve the CPU -- if you don't, you will notice the **CPU utilization** (amount of time it is performing computations) will go to 100%, it will heat up, your computer starts spinning the fans and be noisy because you don't let it rest.
There are often several **modes** of operation in a CPU which is typically meant for operating systems -- there will usually be some kind of privileged mode in which the CPU can do whatever it wants (this is the mode for the OS kernel) and a restricted mode in which there are restrictions, e.g. on which areas of memory can be accessed or which instructions can be used (this will be used for user program). Thanks to this a user program won't be able to crash the operating system, it will at worst crash itself. See also *real mode* and *protected mode*.
Frequently there are several **modes** of operation in a CPU which is typically meant for operating systems -- there will usually be some kind of privileged mode in which the CPU can do whatever it wants (this is the mode for the OS kernel) and a restricted mode in which there are restrictions, e.g. on which areas of memory can be accessed or which instructions can be used (this will be used for user program). Thanks to this a user program won't be able to crash the operating system, it will at worst crash itself. Most notably x86 CPUs have the *real mode* (addresses correspond to real, physical addresses) and *protected mode* (memory is [virtualized](virtual_memory.md), protected, addresses don't generally correspond to physical addresses).
A CPU may also have integrated some **[coprocessors](coprocessor.md)**, though sometimes coprocessors are really a separate chip. Coprocessors that may be inside the CPU include e.g. the FPU ([floating point](float.md) unit) or encryption coprocessor. Again, this will make the CPU a lot more expensive.