3 ms·
It's also worth noting that "microcode" in a modern x86 CPU is not really the same thing as "microcode" in an older, "microcoded" CPU. What do I mean by this?
by bri3d 3y ago
It's also worth noting that "microcode" in a modern x86 CPU is not really the same thing as "microcode" in an older, "microcoded" CPU. What do I mean by this?
Some older CPUs were truly "microcoded." The heart of the CPU was an interpreter loop which took instructions, then invoked a ROM / microcode routine corresponding to the implementation for that instruction. Each instruction began with the runtime selecting an instruction and ended with the microcode routine returning to a loop. A good example of this is the 8086: http://www.righto.com/2022/11/how-8086-processors-microcode-engine.html http://www.righto.com/2022/11/how-8086-processors-microcode-... . These CPUs worked like a traditional "emulator."
That is NOT how a modern x86 CPU works. A modern CPU works a lot more like a JIT. In a modern x86 CPU, "microcode" runs _alongside_ the main CPU core execution. The microcode runtime can be thought of as a co-processor of sorts: some complex instructions or instructions with errata are redirected into the microcode co-processor, and it's responsible for breaking those instructions down and emitting their lower-level uOps back into the execution scheduler. However, most instructions never touch microcode at all: they are decoded purely in hardware and issued into the scheduler directly.
This is important because when people start talking about the x86 "microcode layer," they quickly get confused between the "microcode" (which is running _alongside_ the processor) and uOps, which are the lower level instructions issued into the processor's execution scheduler.
- allenrb 3y agoThanks, I’d been wondering about this for years. I couldn’t imagine how microcode would be directly involved with so many pipeline stages and execution units. But if it’s reserved for the “highly unusual”, it all fits together. And the explosion of transistor budgets since 8086 means we can afford to implement all but the wackiest stuff in hardware.
- peter_d_sherman 3y agoSome excellent points! uOps and how they function should be included in any serious study of x86 hardware. In an ideal world, they would be well documented by vendors. (Random Idea: I wonder if it is possible to use some feature of some newer x86 chip to issue an x86 instruction -- and then to have it retrieve the uOp structure for that instruction as data... sort of like a uOp proxy or debug facility... If newer x86 chips don't have that function -- then a future x86 chip (which doesn't necessarily have to be be Intel) should...) >In a modern x86 CPU, "microcode" runs _alongside_ the main CPU core execution. Indeed! The broader picture of a CPU, any CPU, is that there's a lot that is happening at the same time. A lot of signals (and data!) moving around, and changing around for various purposes! (In other words, modern CPU's don't work linearly, like a computer program -- unless perhaps we talk about the earliest oldest CPU's...) But the study of uOps should be undertaken in any serious study of the x86... It should be pointed out that the number of uOps -- should be less, way less, than the number of x86 instructions, since each x86 instruction is implemented in one to several uOps, and these uOps frequently repeat across instructions... Which again brings us the academic question of "if there are way fewer uOps than x86 instructions -- then can the set of uOps if taken by themselves be considered a RISC?" Why or why not? Which brings us to Christopher Domas' discovery that some VIA x86 Samuel 2 processors had an "Alternate Instruction Set" -- what is the Alternate Instruction Set's relation to uOps? Is the AIS direct uOps, or 1:1 mapping? Or was it implemented by microcode that translated each AIS instruction into multiple uOps, and if so, how was that microcode implemented? As an addendum, I found the following resource for uops online: https://uops.info/index.html https://uops.info/index.html Anyway, some excellent points in your post!
- allien_kse 3y ago> Which brings us to Christopher Domas' discovery that some VIA x86 Samuel 2 processors had an "Alternate Instruction Set" -- Domas found it independently by fuzzing. However, the publically available documentation of the time (16 years prior to Dumas's work) did say how to switch to AIS mode, the basic format of the AIS instructions (the needed prefix and that they're 32bit) and how to prevent access to AIS mode was actually well documented. Indeed, support for the "new branch instruction" was actually added to NASM back in 2002. This documentation also states that it is available on all VIA C3 processors (so also stuff like Eden). There are also hints that it was available earlier (eg., the documentation references an instruction being available on the "C2", which I assume is one of the IDT Winchip designs), and I've see rumours of something on the VIA Nano processors. So, a little reading would have saved Dumas quite a lot of time; although it's certainly a good demonstration of how one can go about finding similar features. What wasn't easy to find at the time was the actual AIS instruction set, this has now been uploaded to bitsavers, although it's a somewhat early and incomplete version. > what is the Alternate Instruction Set's relation to uOps? Is the AIS direct uOps, or 1:1 mapping? Or was it implemented by microcode that translated each AIS instruction into multiple uOps, and if so, how was that microcode implemented? The introduction of the now available application note, states that these are the micro-operations, albeit not the entire set: "the VIA C3 processor has a special operation mode where the front end logic can also fetch (selected) micro-operations (versus x86 instructions) and pass them directly to the internal execution unit." The (now available) Alternate Instruction Set Programming Reference states "the VIA C3 processor comprises two major components: a front-end that fetches x86 instruction bytes, and an internal microprocessor that executes these internal instructions." In particular it seems to be a MIPS core with extensions/modifications optimised for emulating an x86 processor - there are instructions with "special control fields to allow most ALU semantics to be specified in a single 32bit instruction" and instructions that are "needed to perform a single-cycle X86 [instruction name] function". Why MIPS? Because the Programming Reference says "There are no MIPS format load/store instructions and no way to perform load/stores to native-mode address space. All load/store operations use X86 address and load/store semantics". Without pulling up a MIPS instruction set, and comparing, it also looks a lot like MIPS (32 registers, constant 0 register etc.,) Notably, you can even interleave x86 instructions and these uOP instructions (with some restrictions), I doubt the internal core is microcoded, the references to "single-cycle" and "MIPS" would seem to preclude that. However, there may not be access to all the possible uOPs (the appnote says "selected" plus there are multiple references to a "translator", and registers used for "Xlator-ucode communication"). Of course, this translator could be hardware that, say, deals with all x86 instructions that map to a single uOP, perhaps skipping the MIPS decode. Thus the translation directly feeds the RISC core with single translated instructions where there is a mapping or with a ucode instruction stream from a ROM where the implementation requires multiple instructions. This format would make the wrapper instructions very easy to implement. If you're interested, there's a wikipedia page - https://en.wikipedia.org/wiki/Alternate_Instruction_Set https://en.wikipedia.org/wiki/Alternate_Instruction_Set - with links to the documentation.
- p_l 3y agoUmm, uOps are exactly like the old horizontal microcode, down to the ability to jump out to patched out instructions... The simplest horizontally microcoded CPUs had their "decoder" be essentially mapping into microcode ROM, where said microcode contained instructions that directly drove various components of the CPU, coupled with some flags regarding next micro-instruction to execute. Many instructions could in fact decode to single "wide" instruction, with sequencer-controlling bits telling it to load the next instruction and dispatch it. Others would jump to microcode "dispatch" routine which usually was uOP(s) that executed advancing of program counter and triggering load of next instruction to appropriate register then jumped based on that. Usually multiple wide instructions per macroinstruction happened either for complex designs (there were microcoded CPUs whose microcode was multitasking with priority levels!) or when decoded instruction mapped into a more complex sequence of operations (take for example a difference between an "ADD" between two registers and an "ADD" that does complex offset calculation for all operands - or PDP-10 style indirect addressing, where you'd loop memory load until hitting the final, non-indirect address). To make it simpler, it was common to make microcode ROMs have multiple empty slots between addresses generated by sequencer, so that you avoided jumps unless you got really hairy instruction (largest possible VAX instruction was longer than its page size).