diff --git a/litex/soc/cores/cpu/cv32e41p/__init__.py b/litex/soc/cores/cpu/cv32e41p/__init__.py new file mode 100644 index 000000000..b440ef91b --- /dev/null +++ b/litex/soc/cores/cpu/cv32e41p/__init__.py @@ -0,0 +1 @@ +from litex.soc.cores.cpu.cv32e41p.core import CV32E41P diff --git a/litex/soc/cores/cpu/cv32e41p/boot-helper.S b/litex/soc/cores/cpu/cv32e41p/boot-helper.S new file mode 100644 index 000000000..6dd74aaeb --- /dev/null +++ b/litex/soc/cores/cpu/cv32e41p/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/cv32e41p/core.py b/litex/soc/cores/cpu/cv32e41p/core.py new file mode 100644 index 000000000..c7f63e940 --- /dev/null +++ b/litex/soc/cores/cpu/cv32e41p/core.py @@ -0,0 +1,343 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2020 Antmicro +# SPDX-License-Identifier: BSD-2-Clause + +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 + +# Variants ----------------------------------------------------------------------------------------- + +CPU_VARIANTS = ["standard"] + +# GCC Flags ---------------------------------------------------------------------------------------- + +GCC_FLAGS = { + # /-------- Base ISA + # |/------- Hardware Multiply + Divide + # ||/----- Atomics + # |||/---- Compressed ISA + # ||||/--- Single-Precision Floating-Point + # |||||/-- Double-Precision Floating-Point + # imacfd + "standard": "-march=rv32imc -mabi=ilp32 ", +} + +# OBI / APB / Trace Layouts ------------------------------------------------------------------------ + +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), +] + +# Helpers ------------------------------------------------------------------------------------------ + +def add_manifest_sources(platform, manifest): + basedir = get_data_mod("cpu", "cv32e41p").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))) + +# OBI <> Wishbone ---------------------------------------------------------------------------------- + +class OBI2Wishbone(Module): + def __init__(self, obi, wb): + addr = Signal.like(obi.addr) + be = Signal.like(obi.be) + we = Signal.like(obi.we) + wdata = Signal.like(obi.wdata) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + # On OBI request: + If(obi.req, + # Drive Wishbone bus from OBI bus. + wb.adr.eq(obi.addr[2:32]), + wb.stb.eq( 1), + wb.dat_w.eq( obi.wdata), + wb.cyc.eq( 1), + wb.sel.eq( obi.be), + wb.we.eq( obi.we), + + # Store OBI bus values. + NextValue(addr, obi.addr), + NextValue(be, obi.be), + NextValue(we, obi.we), + NextValue(wdata, obi.wdata), + + # Now we need to wait Wishbone Ack. + NextState("ACK") + ), + obi.gnt.eq(1), # Always ack OBI request in Idle. + ) + fsm.act("ACK", + # Drive Wishbone bus from stored OBI bus values. + wb.adr.eq(addr[2:32]), + wb.stb.eq( 1), + wb.dat_w.eq( wdata), + wb.cyc.eq( 1), + wb.sel.eq( be), + wb.we.eq( we), + + # On Wishbone Ack: + If(wb.ack, + # Generate OBI response. + obi.rvalid.eq(1), + obi.rdata.eq(wb.dat_r), + + # Return to Idle. + NextState("IDLE") + ) + ) + +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(Signal(2), wb.adr)), + obi.wdata.eq(wb.dat_w), + wb.dat_r.eq(obi.rdata), + ] + +# Wishbone <> APB ---------------------------------------------------------------------------------- + +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(Signal(2), wb.adr)), + apb.pwrite.eq(wb.we), + apb.psel.eq(1), + apb.pwdata.eq(wb.dat_w), + wb.dat_r.eq(apb.prdata), + ] + +# Debug Module ------------------------------------------------------------------------------------- + +class DebugModule(Module): + jtag_layout = [ + ("tck", 1), + ("tms", 1), + ("trst", 1), + ("tdi", 1), + ("tdo", 1), + ] + def __init__(self, pads=None): + if pads is None: + pads = Record(self.jtag_layout) + self.pads = pads + 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() + + self.specials += Tristate(pads.tdo, tdo_o, tdo_oe, tdo_i) + + self.dm_params = dict( + # Clk / Rst. + i_clk = ClockSignal("sys"), + i_rst_n = ~ResetSignal("sys"), + 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) + +# CV32E41P ----------------------------------------------------------------------------------------- + +class CV32E41P(CPU): + family = "riscv" + name = "cv32e41p" + human_name = "CV32E41P" + 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 = GCC_FLAGS[self.variant] + flags += "-D__cv32e41p__ " + return flags + + def __init__(self, platform, variant="standard"): + 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(16) + self.interrupt_padding = Signal(16) + + ibus = Record(obi_layout) + dbus = Record(obi_layout) + + # OBI <> Wishbone. + 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( + # Clk / Rst. + i_clk_i = ClockSignal("sys"), + i_rst_ni = ~ResetSignal("sys"), + + # Controls. + i_pulp_clock_en_i = 1, + i_scan_cg_en_i = 0, + i_mtvec_addr_i = 0, + i_dm_halt_addr_i = 0, + i_hart_id_i = 0, + i_dm_exception_addr_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_gnt_i = 0, + i_apu_rvalid_i = 0, + + # IRQ. + i_irq_i = Cat(self.interrupt_padding,self.interrupt), + + # Debug. + i_debug_req_i = 0, + + # CPU Control. + i_fetch_enable_i = 1, + ) + + # Add Verilog sources. + add_manifest_sources(platform, 'cv32e41p_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 set_reset_address(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("cv32e41p_core", **self.cpu_params) diff --git a/litex/soc/cores/cpu/cv32e41p/crt0.S b/litex/soc/cores/cpu/cv32e41p/crt0.S new file mode 100644 index 000000000..7ad687836 --- /dev/null +++ b/litex/soc/cores/cpu/cv32e41p/crt0.S @@ -0,0 +1,125 @@ +.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 firq15 + +.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 + la a0, vector_table + csrw mtvec, a0 + +data_init: + la a0, _fdata + la a1, _edata + la a2, _fdata_rom +data_loop: + beq a0,a1,data_done + lw a3,0(a2) + sw a3,0(a0) + add a0,a0,4 + add a2,a2,4 + j data_loop +data_done: + +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/cv32e41p/csr-defs.h b/litex/soc/cores/cpu/cv32e41p/csr-defs.h new file mode 100644 index 000000000..d98e8dfb7 --- /dev/null +++ b/litex/soc/cores/cpu/cv32e41p/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/cv32e41p/irq.h b/litex/soc/cores/cpu/cv32e41p/irq.h new file mode 100644 index 000000000..f1dd4c285 --- /dev/null +++ b/litex/soc/cores/cpu/cv32e41p/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/cv32e41p/system.h b/litex/soc/cores/cpu/cv32e41p/system.h new file mode 100644 index 000000000..fa134fc38 --- /dev/null +++ b/litex/soc/cores/cpu/cv32e41p/system.h @@ -0,0 +1,53 @@ +#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); +void busy_wait_us(unsigned int us); + +#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 fa6e8db96..4ec7b8c2f 100644 --- a/litex/soc/software/bios/isr.c +++ b/litex/soc/software/bios/isr.c @@ -76,6 +76,40 @@ void isr(void) #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 + } +} +#elif defined(__cv32e41p__) + +#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; diff --git a/litex_setup.py b/litex_setup.py index 8567bbea0..55b77aa9e 100755 --- a/litex_setup.py +++ b/litex_setup.py @@ -104,6 +104,7 @@ git_repos = { "pythondata-cpu-microwatt": GitRepo(url="https://github.com/litex-hub/", sha1=0xb940b55acff), "pythondata-cpu-blackparrot": GitRepo(url="https://github.com/litex-hub/"), "pythondata-cpu-cv32e40p": GitRepo(url="https://github.com/litex-hub/", clone="recursive"), + "pythondata-cpu-cv32e41p": GitRepo(url="https://github.com/litex-hub/", clone="recursive"), "pythondata-cpu-ibex": GitRepo(url="https://github.com/litex-hub/", clone="recursive", sha1=0xd3d53df), "pythondata-cpu-marocchino": GitRepo(url="https://github.com/litex-hub/"), }