cores/cpu: Add initial FemtoRV support.

FemtoRV is a minimalist RISC-V CPU with design process documented and
available at https://github.com/BrunoLevy/learn-fpga.

This CPU is a very nice way to discover/learn RISC-V and this LiteX support
can be useful to learn how to integrate a custom CPU with LiteX.

With this support, FemtoRV is now directly usable with LiteX Sim:
$litex_sim --cpu-type=femtorv

This should also enable its use on all boards (> 50) available in LiteX-Boards
repository (but hasn't been tested yet), ex:
$python3 -m litex_boards.targets.digilent_arty --cpu-type=femtorv --build
This commit is contained in:
Florent Kermarrec 2021-05-20 08:37:35 +02:00
parent d3560e5772
commit b1d8fe61f8
8 changed files with 290 additions and 0 deletions

View File

@ -74,6 +74,7 @@ from litex.soc.cores.cpu.microwatt import Microwatt
# RISC-V (32-bit)
from litex.soc.cores.cpu.serv import SERV
from litex.soc.cores.cpu.femtorv import FemtoRV
from litex.soc.cores.cpu.picorv32 import PicoRV32
from litex.soc.cores.cpu.minerva import Minerva
from litex.soc.cores.cpu.vexriscv import VexRiscv
@ -107,6 +108,7 @@ CPUS = {
# RISC-V (32-bit)
"serv" : SERV,
"femtorv" : FemtoRV,
"picorv32" : PicoRV32,
"minerva" : Minerva,
"vexriscv" : VexRiscv,

View File

@ -0,0 +1 @@
from litex.soc.cores.cpu.femtorv.core import FemtoRV

View File

@ -0,0 +1,4 @@
.section .text, "ax", @progbits
.global boot_helper
boot_helper:
jr x13

View File

@ -0,0 +1,152 @@
#
# This file is part of LiteX.
#
# Copyright (c) 2021 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
import os
from migen import *
from litex.soc.interconnect import wishbone
from litex.soc.cores.cpu import CPU, CPU_GCC_TRIPLE_RISCV32
# Variants -----------------------------------------------------------------------------------------
CPU_VARIANTS = ["standard"]
# FemtoRV ------------------------------------------------------------------------------------------
class FemtoRV(CPU):
name = "femtorv"
human_name = "FemtoRV"
variants = CPU_VARIANTS
data_width = 32
endianness = "little"
gcc_triple = CPU_GCC_TRIPLE_RISCV32
linker_output_format = "elf32-littleriscv"
nop = "nop"
io_regions = {0x80000000: 0x80000000} # Origin, Length.
# GCC Flags.
@property
def gcc_flags(self):
flags = "-march=rv32i "
flags += "-mabi=ilp32 "
flags += "-D__femtorv__ "
return flags
def __init__(self, platform, variant="standard"):
self.platform = platform
self.variant = variant
self.reset = Signal()
self.idbus = idbus = wishbone.Interface()
self.periph_buses = [idbus] # Peripheral buses (Connected to main SoC's bus).
self.memory_buses = [] # Memory buses (Connected directly to LiteDRAM).
# # #
# FemtoRV Mem Bus.
# ----------------
mbus = Record([
("addr", 32),
("wdata", 32),
("wmask", 4),
("rdata", 32),
("rstrb", 1),
("rbusy", 1),
("wbusy", 1),
])
# FemtoRV Instance.
# -----------------
self.cpu_params = dict(
# Parameters.
p_ADDR_WIDTH = 32,
p_RESET_ADDR = Constant(0, 32),
# Clk / Rst.
i_clk = ClockSignal("sys"),
i_reset = ~ResetSignal("sys"), # Active Low.
# I/D Bus.
o_mem_addr = mbus.addr,
o_mem_wdata = mbus.wdata,
o_mem_wmask = mbus.wmask,
i_mem_rdata = mbus.rdata,
o_mem_rstrb = mbus.rstrb,
i_mem_rbusy = mbus.rbusy,
i_mem_wbusy = mbus.wbusy,
)
# Adapt FemtoRV Mem Bus to Wishbone.
# ----------------------------------
# Bytes to Words addressing conversion.
self.comb += idbus.adr.eq(mbus.addr[2:])
# Wdata/WMask direct connection.
self.comb += idbus.dat_w.eq(mbus.wdata)
self.comb += idbus.sel.eq(mbus.wmask)
# Control adaptation.
latch = Signal()
write = mbus.wmask != 0
read = mbus.rstrb
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
idbus.stb.eq(read | write),
idbus.cyc.eq(read | write),
idbus.we.eq(write),
If(read,
mbus.rbusy.eq(1),
NextState("READ")
).Elif(write,
mbus.wbusy.eq(1),
NextState("WRITE")
)
)
fsm.act("READ",
idbus.stb.eq(1),
idbus.cyc.eq(1),
mbus.rbusy.eq(1),
If(idbus.ack,
latch.eq(1),
NextState("IDLE")
)
)
fsm.act("WRITE",
idbus.stb.eq(1),
idbus.cyc.eq(1),
idbus.we.eq(1),
mbus.wbusy.eq(1),
If(idbus.ack,
NextState("IDLE")
)
)
# Latch RData on Wishbone ack.
self.sync += If(latch, mbus.rdata.eq(idbus.dat_r))
# Add Verilog sources.
# --------------------
self.add_sources(platform)
def set_reset_address(self, reset_address):
assert not hasattr(self, "reset_address")
self.reset_address = reset_address
self.cpu_params.update(p_RESET_ADDR=Constant(reset_address, 32))
@staticmethod
def add_sources(platform):
if not os.path.exists("femtorv32_quark.v"):
# Get FemtoRV32 source.
os.system("wget https://raw.githubusercontent.com/BrunoLevy/learn-fpga/master/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v")
# FIXME: Patch it to fix compilation issue with Verilator, report issue.
os.system(f"patch -p0 < {os.path.dirname(os.path.realpath(__file__))}/femtorv32_quark.patch")
platform.add_source("femtorv32_quark.v")
def do_finalize(self):
assert hasattr(self, "reset_address")
self.specials += Instance("FemtoRV32", **self.cpu_params)

View File

@ -0,0 +1,75 @@
#define MIE_MEIE 0x800
.global _start
_start:
j reset_vector
reset_vector:
la sp, _fstack
la t0, trap_vector
csrw mtvec, t0
// initialize .data
la t0, _fdata
la t1, _edata
la t2, _fdata_rom
1: beq t0, t1, 2f
lw t3, 0(t2)
sw t3, 0(t0)
addi t0, t0, 4
addi t2, t2, 4
j 1b
2:
// initialize .bss
la t0, _fbss
la t1, _ebss
1: beq t0, t1, 3f
sw zero, 0(t0)
addi t0, t0, 4
j 1b
3:
// enable external interrupts
li t0, MIE_MEIE
csrs mie, t0
call main
1: j 1b
trap_vector:
addi sp, sp, -16*4
sw ra, 0*4(sp)
sw t0, 1*4(sp)
sw t1, 2*4(sp)
sw t2, 3*4(sp)
sw a0, 4*4(sp)
sw a1, 5*4(sp)
sw a2, 6*4(sp)
sw a3, 7*4(sp)
sw a4, 8*4(sp)
sw a5, 9*4(sp)
sw a6, 10*4(sp)
sw a7, 11*4(sp)
sw t3, 12*4(sp)
sw t4, 13*4(sp)
sw t5, 14*4(sp)
sw t6, 15*4(sp)
call isr
lw ra, 0*4(sp)
lw t0, 1*4(sp)
lw t1, 2*4(sp)
lw t2, 3*4(sp)
lw a0, 4*4(sp)
lw a1, 5*4(sp)
lw a2, 6*4(sp)
lw a3, 7*4(sp)
lw a4, 8*4(sp)
lw a5, 9*4(sp)
lw a6, 10*4(sp)
lw a7, 11*4(sp)
lw t3, 12*4(sp)
lw t4, 13*4(sp)
lw t5, 14*4(sp)
lw t6, 15*4(sp)
addi sp, sp, 16*4
mret

View File

@ -0,0 +1,33 @@
diff --git a/femtorv32_quark_fix.v femtorv32_quark.v
index 220b8d4..31f6c08 100644
--- a/femtorv32_quark_fix.v
+++ femtorv32_quark.v
@@ -44,8 +44,6 @@ module FemtoRV32(
parameter RESET_ADDR = 32'h00000000;
parameter ADDR_WIDTH = 24;
- localparam ADDR_PAD = {(32-ADDR_WIDTH){1'b0}}; // 32-bits padding for addrs
-
/***************************************************************************/
// Instruction decoding.
/***************************************************************************/
@@ -205,7 +203,7 @@ module FemtoRV32(
wire [ADDR_WIDTH-1:0] loadstore_addr = rs1[ADDR_WIDTH-1:0] +
(instr[5] ? Simm[ADDR_WIDTH-1:0] : Iimm[ADDR_WIDTH-1:0]);
- assign mem_addr = {ADDR_PAD,
+ assign mem_addr = {
state[WAIT_INSTR_bit] | state[FETCH_INSTR_bit] ?
PC : loadstore_addr
};
@@ -220,8 +218,8 @@ module FemtoRV32(
/* verilator lint_on WIDTH */
(isLUI ? Uimm : 32'b0) | // LUI
(isALU ? aluOut : 32'b0) | // ALUreg, ALUimm
- (isAUIPC ? {ADDR_PAD,PCplusImm} : 32'b0) | // AUIPC
- (isJALR | isJAL ? {ADDR_PAD,PCplus4 } : 32'b0) | // JAL, JALR
+ (isAUIPC ? {PCplusImm} : 32'b0) | // AUIPC
+ (isJALR | isJAL ? {PCplus4 } : 32'b0) | // JAL, JALR
(isLoad ? LOAD_data : 32'b0); // Load
/***************************************************************************/

View File

@ -0,0 +1,4 @@
#ifndef __IRQ_H
#define __IRQ_H
#endif /* __IRQ_H */

View File

@ -0,0 +1,19 @@
#ifndef __SYSTEM_H
#define __SYSTEM_H
#ifdef __cplusplus
extern "C" {
#endif
__attribute__((unused)) static void flush_cpu_icache(void){}; /* No instruction cache */
__attribute__((unused)) static void flush_cpu_dcache(void){}; /* No instruction cache */
void flush_l2_cache(void);
void busy_wait(unsigned int ms);
void busy_wait_us(unsigned int us);
#ifdef __cplusplus
}
#endif
#endif /* __SYSTEM_H */