I built Fursoy Slice-1, a 64-bit out-of-order superscalar RISC-V core (RV64IMAFDV + Zba/Zbb/Zicsr/Zifencei) in SystemVerilog, along with a minimal SoC (5-master crossbar, CLINT, PLIC, 16550 UART, and custom SBI v2.0 firmware).
At a high level, the core is 2-wide fetch/decode/rename/retire with a 128-entry ROB, 160-entry integer PRF, 8 branch checkpoints for single-cycle speculative RAT rollback, and separate Int/Mem/FP/Vec issue queues. On the memory side, it has a dual-port Sv39 DTLB (64-entry 4-way Tree-PLRU + 8-entry 2 MiB/1 GiB superpage CAM), ITLB, and a 16 KiB 8-bank non-blocking L1D. Instead of flushing dirty lines on every page-table walk, the hardware PTW snoops dirty L1D sets non-destructively so Linux page-table updates are visible directly, paired with store-queue drain on SFENCE.VMA.
In Verilator, it boots mainline Linux 6.6.50 and BusyBox userspace in ~89.8M cycles (~76.2M retired instructions, 2,255 page faults handled) while checking an 8-bucket single-owner CPI conservation invariant every cycle with 0 cycle leakage. Synthesizing the 8 main core logic blocks standalone with sv2v + Yosys + OpenSTA on Nangate 45nm (SRAMs/PRFs black-boxed, pre-P&R) meets 500 MHz across all 8/8 blocks at ~219 kGE, after rebuilding the ALU and branch unit as Kogge-Stone prefix trees and balancing high-fanout CSR/TLB paths.
I used an AI-agent-assisted workflow for RTL iteration and verification scaffolding while driving the microarchitecture, timing closure, and debugging myself. The nastiest late bug was a Linux boot livelock around ~90M cycles: per RISC-V Priv Spec §3.1.9, CSR read-modify-write instructions on `mip` have to operate on the software-writable `mip_q[9]` bit rather than the live `irq_seip_i` wire—our CSR block was feeding `mip_val` back into RMW, so whenever SBI cleared `STIP` while a PLIC IRQ was high, it permanently latched `SEIP` and trapped the kernel in `plic_handle_irq`.
For folks who have built Sv39 cores or open-source ASIC flows: do you prefer letting the PTW snoop dirty L1D lines directly like this, or forcing a clean/drain path? Also curious what people would tackle next on a single-core tree like this—an inclusive L2 cache or jumping straight to dual-core coherency.
Who hurt you? This is a fantastic project with effort put into simulating the RTL against a PDK. I don’t understand why you would gate your approval. If someone showed off a game on HN would you say it wasn’t a real game until you could buy it at your local Best Buy or GameStop?
At a high level, the core is 2-wide fetch/decode/rename/retire with a 128-entry ROB, 160-entry integer PRF, 8 branch checkpoints for single-cycle speculative RAT rollback, and separate Int/Mem/FP/Vec issue queues. On the memory side, it has a dual-port Sv39 DTLB (64-entry 4-way Tree-PLRU + 8-entry 2 MiB/1 GiB superpage CAM), ITLB, and a 16 KiB 8-bank non-blocking L1D. Instead of flushing dirty lines on every page-table walk, the hardware PTW snoops dirty L1D sets non-destructively so Linux page-table updates are visible directly, paired with store-queue drain on SFENCE.VMA.
In Verilator, it boots mainline Linux 6.6.50 and BusyBox userspace in ~89.8M cycles (~76.2M retired instructions, 2,255 page faults handled) while checking an 8-bucket single-owner CPI conservation invariant every cycle with 0 cycle leakage. Synthesizing the 8 main core logic blocks standalone with sv2v + Yosys + OpenSTA on Nangate 45nm (SRAMs/PRFs black-boxed, pre-P&R) meets 500 MHz across all 8/8 blocks at ~219 kGE, after rebuilding the ALU and branch unit as Kogge-Stone prefix trees and balancing high-fanout CSR/TLB paths.
I used an AI-agent-assisted workflow for RTL iteration and verification scaffolding while driving the microarchitecture, timing closure, and debugging myself. The nastiest late bug was a Linux boot livelock around ~90M cycles: per RISC-V Priv Spec §3.1.9, CSR read-modify-write instructions on `mip` have to operate on the software-writable `mip_q[9]` bit rather than the live `irq_seip_i` wire—our CSR block was feeding `mip_val` back into RMW, so whenever SBI cleared `STIP` while a PLIC IRQ was high, it permanently latched `SEIP` and trapped the kernel in `plic_handle_irq`.
For folks who have built Sv39 cores or open-source ASIC flows: do you prefer letting the PTW snoop dirty L1D lines directly like this, or forcing a clean/drain path? Also curious what people would tackle next on a single-core tree like this—an inclusive L2 cache or jumping straight to dual-core coherency.
Build it and then I would be fan number 1!