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:
parent
d3560e5772
commit
b1d8fe61f8
litex/soc/cores/cpu
|
@ -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,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from litex.soc.cores.cpu.femtorv.core import FemtoRV
|
|
@ -0,0 +1,4 @@
|
|||
.section .text, "ax", @progbits
|
||||
.global boot_helper
|
||||
boot_helper:
|
||||
jr x13
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
||||
|
||||
/***************************************************************************/
|
|
@ -0,0 +1,4 @@
|
|||
#ifndef __IRQ_H
|
||||
#define __IRQ_H
|
||||
|
||||
#endif /* __IRQ_H */
|
|
@ -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 */
|
Loading…
Reference in New Issue