diff --git a/litex/soc/cores/cpu/__init__.py b/litex/soc/cores/cpu/__init__.py index d861bf07e..12e2d0574 100644 --- a/litex/soc/cores/cpu/__init__.py +++ b/litex/soc/cores/cpu/__init__.py @@ -57,6 +57,7 @@ from litex.soc.cores.cpu.minerva import Minerva from litex.soc.cores.cpu.vexriscv import VexRiscv from litex.soc.cores.cpu.rocket import RocketRV64 from litex.soc.cores.cpu.blackparrot import BlackParrotRV64 +from litex.soc.cores.cpu.cv32e40p import CV32E40P CPUS = { # None @@ -76,6 +77,7 @@ CPUS = { "picorv32" : PicoRV32, "minerva" : Minerva, "vexriscv" : VexRiscv, + "cv32e40p" : CV32E40P, # RISC-V 64-bit "rocket" : RocketRV64, diff --git a/litex/soc/cores/cpu/cv32e40p/__init__.py b/litex/soc/cores/cpu/cv32e40p/__init__.py new file mode 100644 index 000000000..5bb212294 --- /dev/null +++ b/litex/soc/cores/cpu/cv32e40p/__init__.py @@ -0,0 +1 @@ +from litex.soc.cores.cpu.cv32e40p.core import CV32E40P diff --git a/litex/soc/cores/cpu/cv32e40p/boot-helper.S b/litex/soc/cores/cpu/cv32e40p/boot-helper.S new file mode 100644 index 000000000..6dd74aaeb --- /dev/null +++ b/litex/soc/cores/cpu/cv32e40p/boot-helper.S @@ -0,0 +1,4 @@ +.section .text, "ax", @progbits +.global boot_helper +boot_helper: + jr x13 diff --git a/litex/soc/cores/cpu/cv32e40p/core.py b/litex/soc/cores/cpu/cv32e40p/core.py new file mode 100644 index 000000000..9f8d6ff91 --- /dev/null +++ b/litex/soc/cores/cpu/cv32e40p/core.py @@ -0,0 +1,417 @@ +#!/usr/bin/env python3 + +import os +import re + +from migen import * +from migen.fhdl.specials import Tristate + +from litex import get_data_mod +from litex.soc.interconnect import wishbone, stream +from litex.soc.interconnect.csr import * +from litex.soc.cores.cpu import CPU, CPU_GCC_TRIPLE_RISCV32 + + +CPU_VARIANTS = ["standard", "full"] + +GCC_FLAGS = { + # /-------- Base ISA + # |/------- Hardware Multiply + Divide + # ||/----- Atomics + # |||/---- Compressed ISA + # ||||/--- Single-Precision Floating-Point + # |||||/-- Double-Precision Floating-Point + # imacfd + "standard": "-march=rv32imc -mabi=ilp32 ", + "full": "-march=rv32imfc -mabi=ilp32 ", +} + +obi_layout = [ + ("req", 1), + ("gnt", 1), + ("addr", 32), + ("we", 1), + ("be", 4), + ("wdata", 32), + ("rvalid", 1), + ("rdata", 32), +] + +apb_layout = [ + ("paddr", 32), + ("pwdata", 32), + ("pwrite", 1), + ("psel", 1), + ("penable", 1), + ("prdata", 32), + ("pready", 1), + ("pslverr", 1), +] + +trace_layout = [ + ("ivalid", 1), + ("iexception", 1), + ("interrupt", 1), + ("cause", 5), + ("tval", 32), + ("priv", 3), + ("iaddr", 32), + ("instr", 32), + ("compressed", 1), +] + +def add_manifest_sources(platform, manifest): + basedir = get_data_mod("cpu", "cv32e40p").data_location + with open(os.path.join(basedir, manifest), 'r') as f: + for l in f: + res = re.search('\$\{DESIGN_RTL_DIR\}/(.+)', l) + if res and not re.match('//', l): + if re.match('\+incdir\+', l): + platform.add_verilog_include_path(os.path.join(basedir, 'rtl', res.group(1))) + else: + platform.add_source(os.path.join(basedir, 'rtl', res.group(1))) + + +class OBI2Wishbone(Module): + def __init__(self, obi, wb): + dat_r_d = Signal().like(wb.dat_r) + addr_d = Signal().like(obi.addr) + ack_d = Signal() + + self.sync += [ + dat_r_d.eq(wb.dat_r), + ack_d.eq(wb.ack), + addr_d.eq(obi.addr), + ] + + self.comb += [ + wb.adr.eq(obi.addr[2:32]), + wb.stb.eq(obi.req & (~ack_d)), + wb.dat_w.eq(obi.wdata), + wb.cyc.eq(obi.req), + wb.sel.eq(obi.be), + wb.we.eq(obi.we), + obi.gnt.eq(wb.ack & (addr_d == obi.addr)), + obi.rvalid.eq(ack_d), + obi.rdata.eq(dat_r_d), + ] + +class Wishbone2OBI(Module): + def __init__(self, wb, obi): + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + If(wb.cyc & wb.stb, + obi.req.eq(1), + NextState("ACK"), + ) + ) + fsm.act("ACK", + wb.ack.eq(1), + NextState("IDLE"), + ) + + self.comb += [ + obi.we.eq(wb.we), + obi.be.eq(wb.sel), + obi.addr.eq(Cat(0, 0, wb.adr)), + obi.wdata.eq(wb.dat_w), + wb.dat_r.eq(obi.rdata), + ] + +class Wishbone2APB(Module): + def __init__(self, wb, apb): + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + If(wb.cyc & wb.stb, + NextState("ACK"), + ) + ) + fsm.act("ACK", + apb.penable.eq(1), + wb.ack.eq(1), + NextState("IDLE"), + ) + + self.comb += [ + apb.paddr.eq(Cat(0, 0, wb.adr)), + apb.pwrite.eq(wb.we), + apb.psel.eq(1), + apb.pwdata.eq(wb.dat_w), + wb.dat_r.eq(apb.prdata), + ] + +class TraceCollector(Module, AutoCSR): + def __init__(self, trace_depth=16384): + self.bus = bus = wishbone.Interface() + self.sink = sink = stream.Endpoint([("data", 32)]) + + clear = Signal() + enable = Signal() + pointer = Signal(32) + + self._enable = CSRStorage() + self._clear = CSRStorage() + self._pointer = CSRStatus(32) + + mem = Memory(32, trace_depth) + rd_port = mem.get_port() + wr_port = mem.get_port(write_capable=True) + + self.specials += rd_port, wr_port, mem + + self.sync += [ + # wishbone + bus.ack.eq(0), + If(bus.cyc & bus.stb & ~bus.ack, bus.ack.eq(1)), + # trace core + If(clear, pointer.eq(0)).Else( + If(sink.ready & sink.valid, pointer.eq(pointer+1)), + ), + ] + + self.comb += [ + # wishbone + rd_port.adr.eq(bus.adr), + bus.dat_r.eq(rd_port.dat_r), + # trace core + wr_port.adr.eq(pointer), + wr_port.dat_w.eq(sink.data), + wr_port.we.eq(sink.ready & sink.valid), + sink.ready.eq(enable & (pointer < trace_depth)), + # csrs + enable.eq(self._enable.storage), + clear.eq(self._clear.storage), + self._pointer.status.eq(pointer), + ] + +class TraceDebugger(Module): + def __init__(self): + self.bus = wishbone.Interface() + self.source = source = stream.Endpoint([("data", 32)]) + self.trace_if = trace_if = Record(trace_layout) + + apb = Record(apb_layout) + + self.submodules.bus_conv = Wishbone2APB(self.bus, apb) + + self.trace_params = dict( + i_clk_i=ClockSignal(), + i_rst_ni=~ResetSignal(), + i_test_mode_i=0, + # cpu interface + i_ivalid_i=trace_if.ivalid, + i_iexception_i=trace_if.iexception, + i_interrupt_i=trace_if.interrupt, + i_cause_i=trace_if.cause, + i_tval_i=trace_if.tval, + i_priv_i=trace_if.priv, + i_iaddr_i=trace_if.iaddr, + i_instr_i=trace_if.instr, + i_compressed_i=trace_if.compressed, + # apb interface + i_paddr_i=apb.paddr, + i_pwdata_i=apb.pwdata, + i_pwrite_i=apb.pwrite, + i_psel_i=apb.psel, + i_penable_i=apb.penable, + o_prdata_o=apb.prdata, + o_pready_o=apb.pready, + o_pslverr_o=apb.pslverr, + # data output + o_packet_word_o=source.data, + o_packet_word_valid_o=source.valid, + i_grant_i=source.ready, + ) + self.specials += Instance("trace_debugger", **self.trace_params) + + @staticmethod + def add_sources(platform): + add_manifest_sources(platform, "cv32e40p_trace_manifest.flist") + +class DebugModule(Module): + jtag_layout = [ + ("tck", 1), + ("tms", 1), + ("trst", 1), + ("tdi", 1), + ("tdo", 1), + ] + def __init__(self, pads=None): + self.dmbus = wishbone.Interface() + self.sbbus = wishbone.Interface() + dmbus = Record(obi_layout) + sbbus = Record(obi_layout) + + self.submodules.sbbus_conv = OBI2Wishbone(sbbus, self.sbbus) + self.submodules.dmbus_conv = Wishbone2OBI(self.dmbus, dmbus) + + self.debug_req = Signal() + self.ndmreset = Signal() + + tdo_i = Signal() + tdo_o = Signal() + tdo_oe = Signal() + + if pads is None: + pads = Record(self.jtag_layout) + + self.pads = pads + + self.specials += Tristate(pads.tdo, tdo_o, tdo_oe, tdo_i) + + self.dm_params = dict( + i_clk=ClockSignal(), + i_rst_n=~ResetSignal(), + o_ndmreset=self.ndmreset, + o_debug_req=self.debug_req, + # slave bus + i_dm_req=dmbus.req, + i_dm_we=dmbus.we, + i_dm_addr=dmbus.addr, + i_dm_be=dmbus.be, + i_dm_wdata=dmbus.wdata, + o_dm_rdata=dmbus.rdata, + # master bus + o_sb_req=sbbus.req, + o_sb_addr=sbbus.addr, + o_sb_we=sbbus.we, + o_sb_wdata=sbbus.wdata, + o_sb_be=sbbus.be, + i_sb_gnt=sbbus.gnt, + i_sb_rvalid=sbbus.rvalid, + i_sb_rdata=sbbus.rdata, + # jtag + i_tck=pads.tck, + i_tms=pads.tms, + i_trst_n=pads.trst, + i_tdi=pads.tdi, + o_tdo=tdo_o, + o_tdo_oe=tdo_oe, + ) + + self.comb += [ + dmbus.gnt.eq(dmbus.req), + dmbus.rvalid.eq(dmbus.gnt), + ] + + self.specials += Instance("dm_wrap", **self.dm_params) + + @staticmethod + def add_sources(platform): + add_manifest_sources(platform, "cv32e40p_dm_manifest.flist") + + +class CV32E40P(CPU): + name = "cv32e40p" + human_name = "CV32E40P" + data_width = 32 + endianness = "little" + gcc_triple = CPU_GCC_TRIPLE_RISCV32 + linker_output_format = "elf32-littleriscv" + nop = "nop" + io_regions = {0x80000000: 0x80000000} # origin, length + + has_fpu = ["full"] + + @property + def gcc_flags(self): + flags = GCC_FLAGS[self.variant] + flags += "-D__cv32e40p__ " + + return flags + + def __init__(self, platform, variant="standard"): + assert variant in CPU_VARIANTS, "Unsupported variant %s" % variant + self.platform = platform + self.variant = variant + self.reset = Signal() + self.ibus = wishbone.Interface() + self.dbus = wishbone.Interface() + self.periph_buses = [self.ibus, self.dbus] + self.memory_buses = [] + self.interrupt = Signal(15) + + ibus = Record(obi_layout) + dbus = Record(obi_layout) + + self.submodules.ibus_conv = OBI2Wishbone(ibus, self.ibus) + self.submodules.dbus_conv = OBI2Wishbone(dbus, self.dbus) + + self.comb += [ + ibus.we.eq(0), + ibus.be.eq(1111), + ] + + self.cpu_params = dict( + i_clk_i=ClockSignal(), + i_rst_ni=~ResetSignal(), + i_clock_en_i=1, + i_test_en_i=0, + i_fregfile_disable_i=0, + i_core_id_i=0, + i_cluster_id_i=0, + # ibus + o_instr_req_o=ibus.req, + i_instr_gnt_i=ibus.gnt, + i_instr_rvalid_i=ibus.rvalid, + o_instr_addr_o=ibus.addr, + i_instr_rdata_i=ibus.rdata, + # dbus + o_data_req_o=dbus.req, + i_data_gnt_i=dbus.gnt, + i_data_rvalid_i=dbus.rvalid, + o_data_we_o=dbus.we, + o_data_be_o=dbus.be, + o_data_addr_o=dbus.addr, + o_data_wdata_o=dbus.wdata, + i_data_rdata_i=dbus.rdata, + # apu + i_apu_master_gnt_i=0, + i_apu_master_valid_i=0, + # irq + i_irq_sec_i=0, + i_irq_software_i=0, + i_irq_external_i=0, + i_irq_fast_i=self.interrupt, + i_irq_nmi_i=0, + i_irq_fastx_i=0, + # debug + i_debug_req_i=0, + # cpu control + i_fetch_enable_i=1, + ) + + # add verilog sources + add_manifest_sources(platform, 'cv32e40p_manifest.flist') + + if variant in self.has_fpu: + self.cpu_params.update(p_FPU=1) + add_manifest_sources(platform, 'cv32e40p_fpu_manifest.flist') + + def add_debug_module(self, dm): + self.cpu_params.update(i_debug_req_i=dm.debug_req) + self.cpu_params.update(i_rst_ni=~(ResetSignal() | dm.ndmreset)) + + def add_trace_core(self, trace): + trace_if = trace.trace_if + + self.cpu_params.update( + o_ivalid_o=trace_if.ivalid, + o_iexception_o=trace_if.iexception, + o_interrupt_o=trace_if.interrupt, + o_cause_o=trace_if.cause, + o_tval_o=trace_if.tval, + o_priv_o=trace_if.priv, + o_iaddr_o=trace_if.iaddr, + o_instr_o=trace_if.instr, + o_compressed_o=trace_if.compressed, + ) + + def set_reset_address(self, reset_address): + assert not hasattr(self, "reset_address") + self.reset_address = reset_address + self.cpu_params.update(i_boot_addr_i=Signal(32, reset=reset_address)) + + def do_finalize(self): + assert hasattr(self, "reset_address") + self.specials += Instance("riscv_core", **self.cpu_params) diff --git a/litex/soc/cores/cpu/cv32e40p/crt0.S b/litex/soc/cores/cpu/cv32e40p/crt0.S new file mode 100644 index 000000000..30b372ab0 --- /dev/null +++ b/litex/soc/cores/cpu/cv32e40p/crt0.S @@ -0,0 +1,112 @@ +.global main +.global isr +.global _start + +_start: + j crt_init + nop + nop + nop + nop + nop + nop + nop + +.balign 256 + +vector_table: + j trap_entry # 0 unused + j trap_entry # 1 unused + j trap_entry # 2 unused + j trap_entry # 3 software + j trap_entry # 4 unused + j trap_entry # 5 unused + j trap_entry # 6 unused + j trap_entry # 7 timer + j trap_entry # 8 unused + j trap_entry # 9 unused + j trap_entry # 10 unused + j trap_entry # 11 external + j trap_entry # 12 unused + j trap_entry # 13 unused + j trap_entry # 14 unused + j trap_entry # 15 unused + j trap_entry # 16 firq0 + j trap_entry # 17 firq1 + j trap_entry # 18 firq2 + j trap_entry # 19 firq3 + j trap_entry # 20 firq4 + j trap_entry # 21 firq5 + j trap_entry # 22 firq6 + j trap_entry # 23 firq7 + j trap_entry # 24 firq8 + j trap_entry # 25 firq9 + j trap_entry # 26 firq10 + j trap_entry # 27 firq11 + j trap_entry # 28 firq12 + j trap_entry # 29 firq13 + j trap_entry # 30 firq14 + j trap_entry # 31 unused + +.global trap_entry +trap_entry: + sw x1, - 1*4(sp) + sw x5, - 2*4(sp) + sw x6, - 3*4(sp) + sw x7, - 4*4(sp) + sw x10, - 5*4(sp) + sw x11, - 6*4(sp) + sw x12, - 7*4(sp) + sw x13, - 8*4(sp) + sw x14, - 9*4(sp) + sw x15, -10*4(sp) + sw x16, -11*4(sp) + sw x17, -12*4(sp) + sw x28, -13*4(sp) + sw x29, -14*4(sp) + sw x30, -15*4(sp) + sw x31, -16*4(sp) + addi sp,sp,-16*4 + call isr + lw x1 , 15*4(sp) + lw x5, 14*4(sp) + lw x6, 13*4(sp) + lw x7, 12*4(sp) + lw x10, 11*4(sp) + lw x11, 10*4(sp) + lw x12, 9*4(sp) + lw x13, 8*4(sp) + lw x14, 7*4(sp) + lw x15, 6*4(sp) + lw x16, 5*4(sp) + lw x17, 4*4(sp) + lw x28, 3*4(sp) + lw x29, 2*4(sp) + lw x30, 1*4(sp) + lw x31, 0*4(sp) + addi sp,sp,16*4 + mret + .text + + +crt_init: + la sp, _fstack + 4 + la a0, vector_table + csrw mtvec, a0 + +bss_init: + la a0, _fbss + la a1, _ebss +bss_loop: + beq a0,a1,bss_done + sw zero,0(a0) + add a0,a0,4 + j bss_loop +bss_done: + + li a0, 0x7FFF0880 //7FFF0880 enable timer + external interrupt + fast interrupt sources (until mstatus.MIE is set, they will never trigger an interrupt) + csrw mie,a0 + + j main +infinit_loop: + j infinit_loop diff --git a/litex/soc/cores/cpu/cv32e40p/csr-defs.h b/litex/soc/cores/cpu/cv32e40p/csr-defs.h new file mode 100644 index 000000000..d98e8dfb7 --- /dev/null +++ b/litex/soc/cores/cpu/cv32e40p/csr-defs.h @@ -0,0 +1,11 @@ +#ifndef CSR_DEFS__H +#define CSR_DEFS__H + +#define CSR_MSTATUS_MIE 0x8 + +#define CSR_IRQ_MASK 0xBC0 +#define CSR_IRQ_PENDING 0xFC0 + +#define CSR_DCACHE_INFO 0xCC0 + +#endif /* CSR_DEFS__H */ diff --git a/litex/soc/cores/cpu/cv32e40p/irq.h b/litex/soc/cores/cpu/cv32e40p/irq.h new file mode 100644 index 000000000..f1dd4c285 --- /dev/null +++ b/litex/soc/cores/cpu/cv32e40p/irq.h @@ -0,0 +1,40 @@ +#ifndef __IRQ_H +#define __IRQ_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +static inline unsigned int irq_getie(void) +{ + return (csrr(mstatus) & CSR_MSTATUS_MIE) != 0; +} + +static inline void irq_setie(unsigned int ie) +{ + if(ie) csrs(mstatus,CSR_MSTATUS_MIE); else csrc(mstatus,CSR_MSTATUS_MIE); +} + +static inline unsigned int irq_getmask(void) +{ + return 0; // FIXME +} + +static inline void irq_setmask(unsigned int mask) +{ + // FIXME +} + +static inline unsigned int irq_pending(void) +{ + return 0;// FIXME +} + +#ifdef __cplusplus +} +#endif + +#endif /* __IRQ_H */ diff --git a/litex/soc/cores/cpu/cv32e40p/system.h b/litex/soc/cores/cpu/cv32e40p/system.h new file mode 100644 index 000000000..b787320a2 --- /dev/null +++ b/litex/soc/cores/cpu/cv32e40p/system.h @@ -0,0 +1,52 @@ +#ifndef __SYSTEM_H +#define __SYSTEM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +__attribute__((unused)) static void flush_cpu_icache(void) +{ + // FIXME + asm volatile("nop"); +} + +__attribute__((unused)) static void flush_cpu_dcache(void) +{ + // FIXME + asm volatile("nop"); +} + +void flush_l2_cache(void); + +void busy_wait(unsigned int ms); + +#define csrr(reg) ({ unsigned long __tmp; \ + asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \ + __tmp; }) + +#define csrw(reg, val) ({ \ + if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \ + asm volatile ("csrw " #reg ", %0" :: "i"(val)); \ + else \ + asm volatile ("csrw " #reg ", %0" :: "r"(val)); }) + +#define csrs(reg, bit) ({ \ + if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \ + asm volatile ("csrrs x0, " #reg ", %0" :: "i"(bit)); \ + else \ + asm volatile ("csrrs x0, " #reg ", %0" :: "r"(bit)); }) + +#define csrc(reg, bit) ({ \ + if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \ + asm volatile ("csrrc x0, " #reg ", %0" :: "i"(bit)); \ + else \ + asm volatile ("csrrc x0, " #reg ", %0" :: "r"(bit)); }) + +#ifdef __cplusplus +} +#endif + +#endif /* __SYSTEM_H */ diff --git a/litex/soc/software/bios/isr.c b/litex/soc/software/bios/isr.c index 0044f3d16..dafa2c184 100644 --- a/litex/soc/software/bios/isr.c +++ b/litex/soc/software/bios/isr.c @@ -62,6 +62,41 @@ void isr(void) *((unsigned int *)PLIC_CLAIM) = claim; } } +#elif defined(__cv32e40p__) + +#define FIRQ_OFFSET 16 +#define IRQ_MASK 0x7FFFFFFF +#define INVINST 2 +#define ECALL 11 +#define RISCV_TEST + +void isr(void) +{ + unsigned int cause = csrr(mcause) & IRQ_MASK; + + if (csrr(mcause) & 0x80000000) { +#ifndef UART_POLLING + if (cause == (UART_INTERRUPT+FIRQ_OFFSET)){ + uart_isr(); + } +#endif + } else { +#ifdef RISCV_TEST + int gp; + asm volatile ("mv %0, gp" : "=r"(gp)); + printf("E %d\n", cause); + if (cause == INVINST) { + printf("Inv Instr\n"); + for(;;); + } + if (cause == ECALL) { + printf("Ecall (gp: %d)\n", gp); + csrw(mepc, csrr(mepc)+4); + } +#endif + } +} + #else void isr(void) { diff --git a/litex_setup.py b/litex_setup.py index cd0d43a01..c95e3fd1b 100755 --- a/litex_setup.py +++ b/litex_setup.py @@ -50,6 +50,7 @@ repos = [ ("pythondata-cpu-minerva", ("https://github.com/litex-hub/", False, True, None)), ("pythondata-cpu-microwatt", ("https://github.com/litex-hub/", False, True, 0xa7859fb)), ("pythondata-cpu-blackparrot", ("https://github.com/litex-hub/", False, True, None)), + ("pythondata-cpu-cv32e40p", ("https://github.com/litex-hub/", False, True, None)), ] repos = OrderedDict(repos)