Writing VMs is a great way to understand how a real CPU might work–not functionally, of course, but with a fairly good idea of some of the important parts. I had my students implement one (in C) for a class of mine, which made teaching a lot of higher-level topics much easier because they had implemented the levels underneath and understood better what was going on.
Implementing the Beta processor (32-bit RISC processor loosely based on the DEC Alpha) for 6.004 [0] was one of the most fun projects I've done. We wrote everything at the netlist level (or at least I'm told it's roughly equivalent to SPICE's netlists) rather than VHDL or Verilog, with some simple component libraries (mostly 2-,3-,and 4-input MUXes/DEMUXes) and a register file implementation provided by the instructors.
These days, I would hope they'd use a simplified ISA based on either RISC-V or something as close to 64-bit ARM as they could legally get away with.
Like others say, writing a VM is a great project for your own education! I thought it would be hard to target a real system like x86 but after trying to get started on older Nintendo systems I gave up and just did x86. It's simpler because it's more familiar and the tooling is more easily accessible.
Here is a series of tutorials I wrote on building an x86 emulator in JavaScript: