Skip to content

Commit

Permalink
Bulk rewrite and migration to Amaranth 0.5.
Browse files Browse the repository at this point in the history
Notable changes:
- Removed support for RISC-V Debug (poorly tested and likely broken).
- irq_mask/irq_pending CSRs are removed and replaced with custom fields
  in MIE/MIP CSRs (mfie and mfip).
- Resized top-level `external_interrupt` port to 1 bit (instead of 32).
- Added top-level `fast_interrupt` port, with support for 16 IRQ lines.
- Converted `ibus` and `dbus` ports to Amaranth SoC Wishbone interfaces.
- Improved CSR file, with support for per-field validation.
- Improved GPR file.
- Improved L1 cache.
  • Loading branch information
jfng committed Sep 30, 2024
1 parent 5bac31a commit 2c435cc
Show file tree
Hide file tree
Showing 39 changed files with 2,290 additions and 2,873 deletions.
22 changes: 3 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## A 32-bit RISC-V soft processor

Minerva is a CPU core that currently implements the [RISC-V][1] RV32IM instruction set. Its microarchitecture is described in plain Python code using [Amaranth HDL][2].
Minerva is a CPU core that implements the [RISC-V][1] `RV32IMZicsr` instruction set. Its microarchitecture is described in plain Python code using [Amaranth HDL][2].

### Quick start

Expand Down Expand Up @@ -54,33 +54,17 @@ The following parameters can be used to configure the Minerva core.
| `dcache_nwords` | `4` | Number of words in a line of the data cache |
| `dcache_base` | `0x00000000` | Base of the data cache address space |
| `dcache_limit` | `0x80000000` | Limit of the data cache address space |
| `wrbuf_depth` | `8` | Depth of the write buffer FIFO |
| `with_muldiv` | `False` | Enable RV32M support |
| `with_debug` | `False` | Enable the Debug Module |
| `with_trigger` | `False` | Enable the Trigger Module |
| `nb_triggers` | `8` | Number of triggers |
| `with_rvfi` | `False` | Enable the riscv-formal interface |

### Testing

A riscv-formal testbench for Minerva is available [here](https://github.com/minerva-cpu/riscv-formal/tree/minerva/cores/minerva).

### Possible improvements

In no particular order:

* RV64I
* Floating Point Unit
* Stateful branch prediction
* MMU
* ...

If you are interested in sponsoring new features or improvements, get in touch at contact [at] lambdaconcept.com .

### License

Minerva is released under the permissive two-clause BSD license.
See LICENSE file for full copyright and license information.

[1]: https://riscv.org/specifications/
[2]: https://github.com/amaranth-lang/amaranth/
[2]: https://amaranth-lang.org/
[3]: https://github.com/m-labs/lm32/
24 changes: 2 additions & 22 deletions cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import argparse
import warnings
from amaranth import cli

from minerva.core import Minerva
Expand All @@ -21,12 +20,6 @@ def main():
parser.add_argument("--with-muldiv",
default=False, action="store_true",
help="enable RV32M support")
parser.add_argument("--with-debug",
default=False, action="store_true",
help="enable the Debug Module")
parser.add_argument("--with-trigger",
default=False, action="store_true",
help="enable the Trigger Module")
parser.add_argument("--with-rvfi",
default=False, action="store_true",
help="enable the riscv-formal interface")
Expand Down Expand Up @@ -68,47 +61,34 @@ def main():
type=int, default=8,
help="write buffer depth")

trigger_group = parser.add_argument_group("trigger options")
trigger_group.add_argument("--nb-triggers",
type=int, default=8,
help="number of triggers")

cli.main_parser(parser)

args = parser.parse_args()

if args.with_debug and not args.with_trigger:
warnings.warn("Support for hardware breakpoints requires --with-trigger")

cpu = Minerva(args.reset_addr,
args.with_icache, args.icache_nways, args.icache_nlines, args.icache_nwords,
args.icache_base, args.icache_limit,
args.with_dcache, args.dcache_nways, args.dcache_nlines, args.dcache_nwords,
args.dcache_base, args.dcache_limit,
args.wrbuf_depth,
args.with_muldiv,
args.with_debug,
args.with_trigger, args.nb_triggers,
args.with_rvfi)

ports = [
cpu.external_interrupt, cpu.timer_interrupt, cpu.software_interrupt,
cpu.fast_interrupt, cpu.external_interrupt, cpu.timer_interrupt, cpu.software_interrupt,
cpu.ibus.ack, cpu.ibus.adr, cpu.ibus.bte, cpu.ibus.cti, cpu.ibus.cyc, cpu.ibus.dat_r,
cpu.ibus.dat_w, cpu.ibus.sel, cpu.ibus.stb, cpu.ibus.we, cpu.ibus.err,
cpu.dbus.ack, cpu.dbus.adr, cpu.dbus.bte, cpu.dbus.cti, cpu.dbus.cyc, cpu.dbus.dat_r,
cpu.dbus.dat_w, cpu.dbus.sel, cpu.dbus.stb, cpu.dbus.we, cpu.dbus.err
]

if args.with_debug:
ports += [cpu.jtag.tck, cpu.jtag.tdi, cpu.jtag.tdo, cpu.jtag.tms]

if args.with_rvfi:
ports += [
cpu.rvfi.valid, cpu.rvfi.order, cpu.rvfi.insn, cpu.rvfi.trap, cpu.rvfi.halt,
cpu.rvfi.intr, cpu.rvfi.mode, cpu.rvfi.ixl, cpu.rvfi.rs1_addr, cpu.rvfi.rs2_addr,
cpu.rvfi.rs1_rdata, cpu.rvfi.rs2_rdata, cpu.rvfi.rd_addr, cpu.rvfi.rd_wdata,
cpu.rvfi.pc_rdata, cpu.rvfi.pc_wdata, cpu.rvfi.mem_addr, cpu.rvfi.mem_rmask,
cpu.rvfi.mem_wmask, cpu.rvfi.mem_rdata, cpu.rvfi.mem_wdata
cpu.rvfi.mem_wmask, cpu.rvfi.mem_rdata, cpu.rvfi.mem_wdata,
]

cli.main_runner(parser, args, cpu, name="minerva_cpu", ports=ports)
Expand Down
80 changes: 80 additions & 0 deletions minerva/arbiter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from amaranth import *
from amaranth.lib import wiring
from amaranth.lib.wiring import In, Out

from amaranth_soc import wishbone


__all__ = ["WishboneArbiter"]


class WishboneArbiter(wiring.Component):
bus: Out(wishbone.Signature(addr_width=30, data_width=32, granularity=8,
features=("err", "cti", "bte")))

def __init__(self):
self._port_map = dict()
super().__init__()

def port(self, priority):
if not isinstance(priority, int) or priority < 0:
raise TypeError("Priority must be a non-negative integer, not '{!r}'"
.format(priority))
if priority in self._port_map:
raise ValueError("Conflicting priority: '{!r}'".format(priority))
port = wishbone.Interface(addr_width=30, data_width=32, granularity=8,
features=("err", "cti", "bte"))
self._port_map[priority] = port
return port

def elaborate(self, platform):
m = Module()

ports = [port for priority, port in sorted(self._port_map.items())]

req = Signal(len(ports))
gnt = Signal.like(req)

with m.If(~self.bus.cyc):
for i, port in enumerate(ports):
m.d.sync += req[i].eq(port.cyc)

m.d.comb += gnt.eq(req & (-req)) # isolate rightmost 1-bit

bus_adr_mux = 0
bus_dat_w_mux = 0
bus_sel_mux = 0
bus_cyc_mux = 0
bus_stb_mux = 0
bus_we_mux = 0
bus_cti_mux = 0
bus_bte_mux = 0

for i, port in enumerate(ports):
bus_adr_mux |= Mux(gnt[i], port.adr, 0)
bus_dat_w_mux |= Mux(gnt[i], port.dat_w, 0)
bus_sel_mux |= Mux(gnt[i], port.sel, 0)
bus_cyc_mux |= Mux(gnt[i], port.cyc, 0)
bus_stb_mux |= Mux(gnt[i], port.stb, 0)
bus_we_mux |= Mux(gnt[i], port.we, 0)
bus_cti_mux |= Mux(gnt[i], port.cti, 0)
bus_bte_mux |= Mux(gnt[i], port.bte, 0)

m.d.comb += [
port.dat_r.eq(self.bus.dat_r),
port.ack .eq(self.bus.ack & gnt[i]),
port.err .eq(self.bus.err & gnt[i]),
]

m.d.comb += [
self.bus.adr .eq(bus_adr_mux),
self.bus.dat_w.eq(bus_dat_w_mux),
self.bus.sel .eq(bus_sel_mux),
self.bus.cyc .eq(bus_cyc_mux),
self.bus.stb .eq(bus_stb_mux),
self.bus.we .eq(bus_we_mux),
self.bus.cti .eq(bus_cti_mux),
self.bus.bte .eq(bus_bte_mux),
]

return m
Loading

0 comments on commit 2c435cc

Please sign in to comment.