43 comments

[ 3.2 ms ] story [ 104 ms ] thread
I always wonder why Niklaus Wirth's RISC5 architecture[1] (not to be confused with RISC-V) doesn't get more love. From my perspective (but I'm no expert on this) it seems exceptionally simple and elegant while still being able to run a desktop OS [2].

[1] https://inf.ethz.ch/personal/wirth/FPGA-relatedWork/RISC.pdf

[2] https://i.redd.it/ukagn6imtiv01.png

Probably doesn't help that googling for "risc5" brings back RISC-V results.
True, the name is unfortunate, but it predates RISC-V quite a bit.

Using: 'risc wirth' as a query yields more accurate results.

Of course developers are free to do what they like, but I think it's a bit of a shame that it's not RISC-V. He's missing out on all the toolchain support in GCC and Clang, plus the ability to run various embedded operating systems including these days seL4. RISC-V has an embedded profile (RV32E) and a tiny implementation of that (PicoRV32) so I'm fairly sure it could be suitable for this.
He points out the reasons it is the way it is here: http://zipcpu.com/about/zipcpu.html
I don't see anything specific about riscv here, am I missing something?
Reasons like learning, very small #LUT devices, etc, that wouldn't be satisfied with 32 bit Linux capable RISCV.

For example "I wanted to know how to build a CPU. This includes learning how to build not only a CPU, but also backends for the C-library, GCC and binutils (GDB soon to come)."

ZipCPU predates the Risc-V buzz and is more a lightweight alternative to things like the microblaze. Its for when you want a microcontroller on your FPGA.
As others have said, this pre-dates RISC-V. The author built it as a teaching / learning aid and blogs about it excellently: https://zipcpu.com/

More generally, anyone building their own CPU and architecture just for kicks gets serious kudos in my book. It's not a shame, it's brilliant!

Next week's project will be programming the software for a PicoRV inside the bus structure created by AutoFPGA, so I am very aware of the RISC-V architectures ... now.

I would very much like to build a RISC-V 64-bit variant (there are already many 32-bit variants), but so far the task isn't funded. Well, neither was the ZipCPU when I built it, but now such an upgrade would need to compete with other paying projects I'm working on whereas I didn't have an income when I started working on the ZipCPU.

GPLv3 may cause issues when embedding it in your own SoC or whatever, especially given the very proprietary nature of toolchains. Not that I mind, just sayin'
> Because Gisselquist Technology, LLC, owns all of the code for the ZipCPU and its peripherals, proprietary licenses may be purchased.
It’s open source, but not free. MicroBlaze MCS is free, but not open source: https://www.xilinx.com/products/design-tools/mb-mcs.html

Thank you very much for this comment about proprietary license!

I don't understand why you're saying it isn't free. Are you saying that because it's dual licensed it's not free? Users would still be able to modify and fork the GPL version in all the normal ways.
If I want to use it in my closed source project I must pay to Gisselquist Technology, LLC. Or I am wrong here?
If you want to use it in your closed source project, give me a holler. I'm sure we could work something out. Even if we don't, I'd love to have the opportunity to hear from you, meet you, say hello, and encourage you in any way I can.
They could modify and fork it, but they would also have to release the source code of their full project. That makes it a no-go for regular commercial projects.

At least, that's my interpretation: it's always a struggle to apply software oriented licenses to hardware.

Last time I checked, "freedom to include the code in a proprietary product" was not among the freedoms considered essential by the FSF. So GPL with an option to get a commercial license would, at worst, make it free but not open-source.
Realistically, those who've wanted to use the ZipCPU tend to just chat with me about it and we work out a handshake agreement or some such.

Were someone to want to use it in a commercial product, I'd be more interested in some form of compensation. For now it's filling the education role quite nicely -- which is somewhat of a surprise for me since I never saw that coming when I started working on it in the first place.

Awesome! Love that you are flexible in that regard.

I was merely pointing out to GP that it wasn’t “only GPL licensed”, there are other options you provide.

This guy posts some amazing content on Twitter, his account is really fun to follow.
Found some info in the repo's issues about how much room ZipCPU takes on an FPGA:

https://github.com/ZipCPU/zipcpu/issues/8

PDFs:

https://github.com/ZipCPU/zipcpu/blob/master/doc/orconf.pdf

https://github.com/ZipCPU/zipcpu/blob/master/doc/orconf2017....

https://github.com/ZipCPU/zipcpu/blob/master/doc/orconf2018....

Also a list of FPGA dev boards as of 2018:

https://www.reddit.com/r/FPGA/comments/9yutk8/best_100300_fp...

So looks like ZipCPU takes between 2400 and 7372 LUTs, depending on how optimized the routing is. An Ultra96 with 154k LUTs could hold between about 20 and 64 ZipCPUs, although busses would be an issue.

I'd like to see more of these projects start from a stance of connecting to other CPUs on the same chip rather than out to main memory. Or rather, a spec should only be considered complete when it has designs for both an in-mesh core to be placed beside its neighbors in a grid, and a bus-facing core for connecting out to main memory.

I think the future is going to be a series of basic blocks like CPUs, RAMs, maybe even GPUs that can be placed arbitrarily and run as clusters for tackling big data problems using techniques like map reduce. Or better yet, maybe FPGAs that look like multiple FPGAs (each with their own RAM) running on the same chip.

Noob question: would what you describe be equivalent to what transputers were intended for?
I have yet to place more than two ZipCPU's on a board, mostly because I'm still working out the various bus details associated with making multiple CPUs the master of the same bus. For example, should they all have the same perspective of the address map? This I'm still working out.
Cache coherency? Proper, proper hard.
Oh cool, good to hear from you! If you happen to read this, I would like to give you a vote on the direction I'm hoping for. My background is in video games, blitters, MATLAB, basically swizzling large amounts of data in various DSP-like ways. A cache miss can easily cost 100 cycles, so processors are sitting around waiting for data 99% of the time sometimes. IMHO pretty much all software today is RAM bus bound, not CPU bound. And this was true in the late 90s, so is even more true today by about 2-4 orders of magnitude (that's why single-threaded CPU performance hasn't changed much since the early 2000s).

In the beginning, caching was a nice idea to get around bus latency. But the last processor design I saw in 1999 when I graduated college had about 75% of the chip devoted to cache. I would have much preferred to have 3 more cores. But the industry kept doubling down on that, so today we're still stuck with CPUs with maybe 4, 8 or 16 cores, but only one bus to RAM.

So to me, hardware caching has been a waste of time when it comes to parallel computation. I think a better model is a software cache running at a privileged level above userland. In other words, a smart MMU per CPU, written in microcode or software that presents a uniform address space to the application. Each MMU could/should have a hash for each (say 4k) page's data, and then each CPU/MMU core would act like a router, either returning the contents of a page for a given hash, or asking its 4 or 8 neighbors if they contain the page (returning it if so, otherwise asking the next neighbors):

https://en.wikipedia.org/wiki/Content-addressable_memory

From the software side, this would look like a single deduplicated virtual memory where each unique page would appear once in the address space, usually out in the mesh somewhere (not resident) unless its core had cached the page locally while its code operated on it. At most, a page would only be an x+y hop from any of the neighboring cores, and a thread in each core should block until the page is resident (just like with any other cache or virtual memory).

The next step is to deal with mutation via a copy-on-write mechanism:

https://en.wikipedia.org/wiki/Copy-on-write

https://en.wikipedia.org/wiki/Software_transactional_memory

So if an app has the same string allocated 10 times, only one page would hold the original data. The other 9 would be mapped to that first page via virtual memory. Everything could/should be immutable, so when a string is modified, it allocates a new page for the data with its own unique hash. The original string would eventually be deallocated through a least recently used (LRU) strategy, or perhaps each MMU could have some notion of garbage collection that preserves a single unique page somewhere in the mesh until all cores no longer reference it. A quick and dirty way to deal with this later would be to recommend that the mesh be hooked up to a large flash memory (over 1 GB) where old pages could be written to be stored indefinitely until storage runs out (the same as with traditional virtual memory).

The most elegant way I know to implement this would be to run something like Clojure in each MMU:

https://clojure.org/about/state

https://clojure.org/reference/refs

A more "hands on" way to think of this with ugly syntax is Redux:

I'm not sure I followed all of that, but let me at least try to look up the links and see what I can learn. Maybe they'll help me follow the concepts you are suggesting.

Dan

I realized after sleeping on it that I should clarify the core idea. With computer architecture, it's easy to get hung up thinking about things like I/O, busses, caching, etc. But I think the future is distributed content-addressable computing, where the state is just "out there" in the internet somewhere, and the runtime handles all the data locality issues so it looks like one continuous memory to the application. If we have that, then it's trivial to add processors either in the local mesh or even out on the internet somewhere.

Thinking in terms of cores connected to their 4 neighbors with a routing protocol like "do you have the data for hash ABC, otherwise ask your neighbors" or "store this data with hash XYZ" reduces the problem space to a key-value store similar to redis, so you don't have to worry about hardware caching.

For now, the network protocol could probably just be something like multicast. Packets would go to all neighbors, and they would be unconnected, unreliable like UDP. The header would either be a REQUEST with an N bit hash, or a RESPONSE with the N bit hash and the data associated with it. There might also be a TTL field that goes up to roughly sqrt(# of cores) so that packets diffuse through the network once but don't bounce around endlessly. I think that cores would be broadcasting new [hash][data] packets for every RAM write, and other cores would save all of them and evict their oldest hashes (which allows the cores to pre-cache data that's likely to be used soon). Then upon requesting a RESPONSE for a certain hash, a core would block until it arrived from the cluster in roughly sqrt(# of cores) cycles.

From a code standpoint, a page in the 32 bit address space would have a real pointer but it would map through the virtual memory manager (VMM) to one of these [hash][data] pages. So two processes in the cluster wouldn't have the same address for the same piece of data. This can be looked at as a feature, not a bug, because it enforces process separation similar to Erlang. So when a page's data is updated, the page gets swapped out by the VMM and re-hashed to a new content-addressable page out in the cluster. So it might make sense to have a coprocessor per-cpu that hashes pages for every RAM write. Or maybe the hashing could happen in the RAM write interrupt and we'd settle for the speed hit, not sure. Also it (might) be good to choose a hash that implements something where you can re-compute a partial range inside the hash rather than having to recompute the whole thing if a single byte changes. Like maybe a custom hasher could be designed that uses a divide and conquer strategy to find which half changed, and then the half in the half that changed, down to some level and append them like with the MD5 appending flaw where they make a document that looks like another document but has different data in the middle that both hash to the same hash so the documents have very different behavior. What I mean is, maybe there is a hashing algorithm that allows you to recalculate a half, or a quarter, or an eighth, down to some granularity, so the hashing could be optimized from O(n) to O(n/64) or whatever. Also after writing this out, I realized that the VMM would store (for SHA-1) a 20 byte hash for every 4k block, so roughly 1/200 of each core's RAM would go to this storage scheme.

Would this VMM be easier to build than an MMU? I don't know, but I do know that software is cheaper than hardware so it's maybe possible.

I didn't know yosys did formal verification now: http://zipcpu.com/blog/2017/10/19/formal-intro.html

Cool.

If that article interests you, you might find this one even more interesting:

http://zipcpu.com/blog/2018/04/02/formal-cpu-bugs.html

Basically, I was shocked when I applied SymbiYosys to the ZipCPU at how many bugs I found. Bug lists like this are typically company secrets, since no one wants to reveal how many bugs their product has. In my case, part of my success has been my openness.

Just out of curiosity: What exactly would you need this for?

Is it just about learning how to design a CPU? Is it about solving a specific task a soft core on an FPGA can do better? What kind of use cases are there where an FPGA with a RISC core is better suited than some kind of microprocessor?

Sometimes when designing for an FPGA/ASIC, the easiest way to implement some sort of complex control logic is to use a small CPU.

For example, the Nvidia Tegra X1 SoC contains a handful of Falcon CPUs (proprietary ISA and core), responsible for amongst others:

- USB 3.0 command/event processing

- Video Image Compositor 'inverse telecine' processing

- HDMI security

Same goes for your nVidia PC GPUs - they have Falcons sprinkled across the entire thing.

Heck, one SoC I know of has an entire cortex M3 core to manage entering and exiting sleep modes.
Think of it this way, what do you need an FPGA for? Now, when budgets are tight, how do you pack more bang for your buck? Rather than placing more and more state machines on an FPGA, you can offload such task loads onto a CPU. Indeed, the more (slow, not time-sensitive, complex, etc) tasks you can off load onto the CPU, the more fabric is available for ... whatever task you actually wanted to place onto the FPGA.
Hm, but then why don't they use a discrete CPU built for this purpose? Many FPGA development boards already come with an ARM MCU.

I'd guess that when the CPU core is on the FPGA, the communication between the CPU and whatever the custom logic does is faster and more in-sync.

Thank you for answering. I have seen a couple of resources on using FPGAs, with those being increasingly accessible to "hobbyists". I often fail to see the "why" in the examples.

Arm cpus are proprietary licensed IP.

This is a cpu made out of pure open source software thin air, which is a special and incalculable open-eneded value all it's own.

You still have to buy an fpga to run it, but you don't have to buy any particular fpga from any single particular vendor. It can be any fpga, including ones like ice40 which at least do not require any closed vendor software to program them.

This means also including theoretical future fpgas that are themselves some open hardware design that any fab could make to order, ie even more open than Lattice, like ordering a pcb is today.

Also, unlike any hardware cpu, a user can modify this cpu in whatever unknown unpredictable ways they might want. If I like this cpu, but I need to add a counter register or some weird tri-state interrupt line or magic instruction, or ... lets try a real idea, how about make the cpu hide it's activity from external analysis for security reasons, by making the cpu always be eecuting some sort of instruction at all times, no no-op (no-op becomes rand-op). This burns power, but also this means you can not tell when it is decrypting an ssl packet. A user can do something unpredictably kooky like that the minute they merely have the idea or the wish. They don't have to beg Arm or Intel to make them a cpu that does it, or live without the odd thing they want.

It's a completely worthy project, whether or not it has any immediate commercial application.

As Mithro of SymbiFlow points out, you would be insane to use a proprietary vendor c compoler today. The open source ones have outclassed them all decades ago.

So it is with practically everything, whether it knows it yet or not, including in this case, cpu design.

I assume such projects to be worthy. When I don't understand why though, I discovered this question to be very valuable to ask.

Clearly, when people invest so much time in one project, they have a good reason (for them). Even when they are emulating an apollo guidance computer in minecraft redstone.

Jan Gray is, I think, more-or-less the Godfather of this approach. I especially enjoyed his Circuit Cellar 3-part series where he describes and builds the xr16 -- a 16-bit machine whose instruction set is designed to well-match the LUT capabilities of an FPGA. I implemented it, and with the LCC C compiler, I was able to run plenty of code. Start here: https://github.com/BigEd/XSOC-xr16/blob/master/doc/xsoc-talk... which is the presentation; then check out the rest of the repo doc: https://github.com/BigEd/XSOC-xr16/tree/master/doc and although it's not maintained these days, his old site is also useful: http://www.fpgacpu.org/xsoc/xr16.html for more modern stuff, see http://fpga.org/
Jan Gray is one of my heroes. I definitely look up to him, and I've enjoyed my interactions with him and the encouragement and advice he's offered me.