# ZipCPU: a 29-instruction RISC core small enough to read in an afternoon

> The ZipCPU is a 32-bit pipelined soft core in Verilog with a load/store architecture, two privilege modes, and no interrupt vectors at all: interrupts switch between two register sets instead. It ships with a full toolchain, including an assembler, binutils, and a GCC port, and the source is GPLv3.

**ZipCPU/zipcpu** — A small, light weight, RISC CPU soft core

- Repository: https://github.com/ZipCPU/zipcpu
- Stars: 1,597 · Forks: 182
- Language: Verilog
- License: not declared
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/zipcpu-zipcpu

## The design goals, which are all about size

The Zip CPU is described as a small, light-weight RISC CPU, and the specific design goals are listed so that each one can be checked against a requirement.

It is 32-bit, with all registers, addresses, and instructions 32 bits in length. It is a RISC CPU where instructions nominally complete in one cycle each, with exceptions for multiplies, divides, memory accesses, and eventually floating point instructions. The README adds a note that is worth reading carefully: the ZipCPU is not a RISC-V CPU, nor does it copy from any other instruction set but its own.

It is a load/store architecture, so only load and store instructions may access memory. It includes Wishbone, AXI4-Lite, and AXI4 memory options. It is a minimally Von-Neumann architecture, where the Wishbone wrappers share buses for instructions and data while the AXI4-Lite and AXI4 wrappers have separate bus interfaces for each, though the address space itself needs to be common. It is pipelined, with stages for prefetch, decode, read of operands, a combined stage containing the ALU, memory, divide, and floating point units, and then the final write-back stage. And it is a two mode machine, supervisor and user, with each mode having a different access level.

That last point, the two modes, is what makes the absence of an MMU survivable. A memory management unit is the standard way to separate kernel from user address spaces, and this CPU uses privilege levels plus register sets instead. It is a different trade, not a worse one, but it is a trade you need to understand before you rely on it.

The project is completely open source, licensed under the GPLv3.

## The instruction set is the interesting constraint

Only 29 instructions are currently implemented, and six additional instructions have been reserved for a floating point unit that has yet to be implemented.

That number is the reason to look at this project. A general purpose RISC instruction set is in the hundreds, and every one of them is a decision you have to understand to read the disassembly. Twenty-nine is a set you can hold in your head, which means the CPU, the assembler, and the compiler backend can all be understood as a whole.

Almost all instructions can be executed conditionally, and the exceptions are listed: load immediate, the debug break instruction, the bus lock, the simulation instructions, and the no-operation instruction. There is a practical consequence here, because conditional execution is what lets the compiler emit a branch and a compare as a single instruction, which is most of the performance benefit of the design.

The exception to the exception is handled by the assembler rather than by hardware. The assembler will quietly turn a conditional load immediate into a two-instruction equivalent, using BREV and LDILO. So a conditional LDI is not a single instruction at the hardware level, and code that counts cycles has to know that.

The CPU makes heavy use of pipelining wherever and whenever it can, so when using a pipelined memory core, loading two values in a row may cost only one clock more than loading the one value. That is the second big design choice, and it is the one that makes a 29-instruction machine perform respectably: the instruction count is small because the pipeline does the work, and the memory system is where the parallelism comes from.

## Interrupts without vectors, and why that is not a shortcut

The CPU has no interrupt vectors, but rather two register sets. On any interrupt, the CPU just switches from the user register set to the supervisor register set.

The stated benefit is that this simplifies interrupt handling, since the CPU automatically saves, preserves, and restores the supervisor's context between enabling interrupts and receiving the next interrupt. That is a real simplification, and it comes with a real cost that a reader should weigh.

With vectors, the hardware tells the software where to jump, so a single handler can decide what to do based on the reason. With register sets, the hardware decides where the context goes, so the software has to work out what happened from the state it finds. For a system with one interrupt source, or with a small fixed set, that is simpler. For a system with a timer, a serial port, and a bus error, the dispatch has to live somewhere, and it will not be free.

There is also an optional interrupt peripheral, rtl/peripherals/icontrol.v, which may be used to combine multiple external interrupts into a single interrupt line. That is the piece that makes the two-register-set design workable: the peripheral does the prioritisation, and the CPU just switches contexts.

Combined with the absence of an MMU, the picture is a CPU designed for a system where interrupts are rare and the address space is flat. It is an unusually good fit for a small microcontroller-class design, and an unusual poor fit for anything expecting a Linux-capable processor.

## The repository, and where the peripherals live

The top level of the repository is a CPU plus a toolchain, and the README is explicit that the CPU comes without the parts it needs to run.

This implementation requires a bus with peripherals hanging off of it, things such as RAM, flash for ROM, and a serial port. The README is clear that this is where the author keeps the CPU apart from any necessary peripherals, and points to the zbasic repository for a ready-made environment with block RAM, QSPI flash, and SD card access over the SPI protocol.

The top level entries are rtl/ for the Verilog, sw/ for the software toolchain, doc/ for the documentation including the CPU specification PDF, sim/ for the simulation environment, bench/ for the benchmark and debug facility, and INSTALL.md and Makefile. There is also a zip.vim file, which is the syntax highlighting definition for the assembler, and the doc/ directory holds the specification that the Makefile's doc target builds.

The sim/ subdirectory is what most people will actually start with. It contains a version of the ZipCPU in a usable environment for simulation purposes, including the CPU, possibly more CPUs for a multiprocessor environment, a bus interconnect, memory, a simulated serial port, and a couple more peripherals. That is a complete system you can run on a workstation without touching an FPGA.

For debugging, there is an assembly level debugger, zipdbg, which you can use to stop and step the CPU, and an integrated wishbone scope, wbscope, which you can use to get traces from within the design while it is running. The scope is the more interesting of the two, because it is a hardware trace rather than a software trace, which is what you need when the bug is in the interaction between the CPU and a peripheral rather than in the CPU alone.

## The toolchain, and why the assembler is in the same repository

The Makefile describes itself as a grand makefile for the entire project, and the target list explains what it builds.

The bench target builds the C++ test bench and debugger facility. The doc target builds the ZipCPU chip specification and the GPL license, and the comment notes these should be distributed pre-built but you are welcome to rebuild them. The rtl target runs Verilator on the RTL. The sw target builds the assembler, binutils, and GCC, and by default also installs the compiler into the sw/install/ subdirectory. And the test target runs the test bench on the assembler test file.

So the toolchain is not a separate download. The assembler, the binutils, and a GCC port live in the sw/ directory, and the CPU was designed alongside them. That is the point of the project for most users: you are not just dropping in a core, you are getting the compiler that targets it, which is what makes the 29-instruction ISA usable rather than a curiosity.

The README gives the practical advice for getting started. If you want to try out the CPU, download and build the repository, using git clone with a depth of 1 because there is a lot of stuff in the git repo that you do not necessarily need. You need it for the binutils, GCC, and newlib support provided by it.

Then the README suggests the ZBasic repository, which places the CPU in an environment with block RAM, QSPI flash, and SD card access. From there you can either tweak the distro, adjusting main.v, regdefs.h, board.h, and board.ld, or use AutoFPGA to adjust your RAM size and add or remove peripherals while maintaining all of those files for you.

The blog articles linked at the end are the best free documentation, including an overview of the ZipCPU's instruction set, a how-to on building the toolchain and testing the CPU, and two articles on formal methods, one on the formal properties of a Wishbone bus and one on formally proving the prefetch.

## What is not integrated yet

The README has a section titled Not yet integrated, and it is the most useful part for anyone deciding whether the CPU fits.

The MMU that was written for the ZipCPU now needs to be rewritten in order to work in the new ZipCore context. The plan is to place the MMU between the ZipCore and the various bus aware wrappers, providing a similar interface to the one offered by the ZipCore, and that way caches will be based on physical addressing, which solves one of the bigger problems the author had when using the MMU.

FATFS support now exists for the SD card, but it is not yet integrated into the newlib C library. And ZipOS would greatly speed up and improve the bare bones newlib library, primarily by providing operating system level interrupt support when using the library, and that integration has not been accomplished either. The author's own comment on that is that if the MMU were available, he might rather just create a Linux distribution.

The three items together explain the project's boundary. A soft core with a compiler and a debugger is a complete embedded development environment. What is missing is the layer that turns it into a general purpose system: memory protection, a filesystem, and an operating system underneath newlib. Those are the pieces a Linux port needs, and they are the pieces not done.

The project also carries a section on commercial opportunities tied to the GPLv3 licence, which is the usual arrangement for a design that a company may want to derive from and commercialise under different terms. The code is a header with a full GPLv3 notice, and the copyright is Gisselquist Technology, LLC, with the creator listed as Dan Gisselquist, Ph.D.

## Conclusion

Adopt ZipCPU if you want a soft core you can understand completely, for a small embedded design where a 29-instruction ISA is enough. Do not adopt it if you need a standard instruction set, general purpose runtime support, or an MMU, since the README lists those as not yet integrated. Verify first that the instruction count and privilege model cover what your firmware will do, because there is no floating point unit and no MMU today.

## FAQ

### How many instructions does the ZipCPU have?

Only 29 instructions are currently implemented, with six more reserved for a floating point unit that has yet to be implemented. Almost all of them can be executed conditionally, with exceptions for load immediate, the debug break instruction, the bus lock, the simulation instructions, and the no-operation instruction.

### How does the ZipCPU handle interrupts without interrupt vectors?

It has two register sets instead. On any interrupt the CPU switches from the user register set to the supervisor register set, and the context is automatically saved, preserved, and restored. An optional interrupt peripheral can combine multiple external interrupts into a single line.

### What buses can the ZipCPU use?

Wishbone, AXI4-Lite, and AXI4 memory options are included. The Wishbone wrappers share buses for instructions and data, while the AXI4-Lite and AXI4 wrappers have separate bus interfaces for each, though the address space itself has to be common.

### What comes with the ZipCPU repository?

The CPU in rtl/core/zipcore.v, the documentation including doc/spec.pdf, and the toolchain in sw, which covers the assembler, binutils, and GCC. The Makefile targets build the C++ test bench, the chip specification, run Verilator on the RTL, build the toolchain, and run the test bench on the assembler test file.

### What is not yet integrated in the ZipCPU?

The MMU written for the older CPU needs rewriting for the ZipCore context, FATFS support for the SD card is not yet integrated into the newlib C library, and ZipOS, which would provide operating system level interrupt support, has not been integrated either.

## Sources

- [Issues](https://github.com/ZipCPU/zipcpu/issues)
- [README](https://github.com/ZipCPU/zipcpu/blob/master/README.md)
- [ZipCPU/zipcpu on GitHub](https://github.com/ZipCPU/zipcpu)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/zipcpu-zipcpu
