diff --git a/CHANGES.md b/CHANGES.md index c5b688bef4..659b82ff17 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,7 +6,8 @@ [> Added -------- - cpu/zynqmp : Added SGMII support via PL andoptional PTP (#2095). - - liteeth/phy : Improved 1000BaseX/2500BaseX PCS/PHYs (https://github.com/enjoy-digital/liteeth/pull/174). + - liteeth/phy : Improved 1000BaseX/2500BaseX PCS/PHYs (https://github.com/enjoy-digital/liteeth/pull/174).* + - cpu/urv : Added uRV CPU support (RISC-V CPU use in White Rabbit project). [> Changed ---------- diff --git a/litex/soc/cores/cpu/urv/__init__.py b/litex/soc/cores/cpu/urv/__init__.py new file mode 100644 index 0000000000..e4ef785b61 --- /dev/null +++ b/litex/soc/cores/cpu/urv/__init__.py @@ -0,0 +1 @@ +from litex.soc.cores.cpu.urv.core import uRV diff --git a/litex/soc/cores/cpu/urv/boot-helper.S b/litex/soc/cores/cpu/urv/boot-helper.S new file mode 100644 index 0000000000..336a4d4f39 --- /dev/null +++ b/litex/soc/cores/cpu/urv/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/urv/core.py b/litex/soc/cores/cpu/urv/core.py new file mode 100644 index 0000000000..1270b678b1 --- /dev/null +++ b/litex/soc/cores/cpu/urv/core.py @@ -0,0 +1,238 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2024 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +import os + +from migen import * + +from litex.gen import * + +from litex.soc.interconnect import stream + +from litex.soc.interconnect import wishbone +from litex.soc.cores.cpu import CPU, CPU_GCC_TRIPLE_RISCV32 + +# Variants ----------------------------------------------------------------------------------------- + +CPU_VARIANTS = { + "standard": "urv", +} + +# GCC Flags ---------------------------------------------------------------------------------------- + +GCC_FLAGS = { + # /------------ Base ISA + # | /------- Hardware Multiply + Divide + # | |/----- Atomics + # | ||/---- Compressed ISA + # | |||/--- Single-Precision Floating-Point + # | ||||/-- Double-Precision Floating-Point + # i macfd + "standard": "-march=rv32i2p0_m -mabi=ilp32", +} + +# uRV ------------------------------------------------------------------------------------------ + +class uRV(CPU): + category = "softcore" + family = "riscv" + name = "urv" + human_name = "urv" + variants = CPU_VARIANTS + data_width = 32 + endianness = "little" + gcc_triple = CPU_GCC_TRIPLE_RISCV32 + linker_output_format = "elf32-littleriscv" + nop = "nop" + io_regions = {0x8000_0000: 0x8000_0000} # Origin, Length. + + # GCC Flags. + @property + def gcc_flags(self): + flags = GCC_FLAGS[self.variant] + flags += " -D__urv__ " + return flags + + def __init__(self, platform, variant="standard"): + self.platform = platform + self.variant = variant + self.human_name = f"uRV-{variant.upper()}" + self.reset = Signal() + self.ibus = ibus = wishbone.Interface(data_width=32, address_width=32, addressing="byte") + self.dbus = dbus = wishbone.Interface(data_width=32, address_width=32, addressing="byte") + self.periph_buses = [ibus, dbus] # Peripheral buses (Connected to main SoC's bus). + self.memory_buses = [] # Memory buses (Connected directly to LiteDRAM). + + # uRV Signals. + # ------------ + im_addr = Signal(32) + im_rd = Signal() + im_data = Signal(32) + im_valid = Signal() + + dm_addr = Signal(32) + dm_data_s = Signal(32) + dm_data_l = Signal(32) + dm_data_select = Signal(4) + dm_store = Signal() + dm_load = Signal() + dm_load_done = Signal() + dm_store_done = Signal() + + # uRV Instance. + # ------------- + self.cpu_params = dict( + # Parameters. + p_g_timer_frequency = 1000, # FIXME. + p_g_clock_frequency = 100000000, # FIXME. + p_g_with_hw_div = 1, + p_g_with_hw_mulh = 1, + p_g_with_hw_mul = 1, + p_g_with_hw_debug = 0, + p_g_with_ecc = 0, + p_g_with_compressed_insns = 0, + + # Clk / Rst. + i_clk_i = ClockSignal("sys"), + i_rst_i = ResetSignal("sys") | self.reset, + + # Instruction Mem Bus. + o_im_addr_o = im_addr, + o_im_rd_o = im_rd, + i_im_data_i = im_data, + i_im_valid_i = im_valid, + + # Data Mem Bus. + o_dm_addr_o = dm_addr, + o_dm_data_s_o = dm_data_s, + i_dm_data_l_i = dm_data_l, + o_dm_data_select_o = dm_data_select, + + o_dm_store_o = dm_store, + o_dm_load_o = dm_load, + i_dm_load_done_i = dm_load_done, + i_dm_store_done_i = dm_store_done, + ) + + # uRV Instruction Bus. + # -------------------- + if True: + from litex.soc.integration.common import get_mem_data + self.rom = Memory(32, depth=131072//4) + self.rom_port = self.rom.get_port() + + self.sync += im_valid.eq(1), + self.comb += [ + self.rom_port.adr.eq(im_addr[2:]), + im_data.eq(self.rom_port.dat_r), + ] + else: + # FIXME: Try to implement im_bus -> Wishbone correctly (if possible). + im_addr_d = Signal(32, reset=0xffffffff) + self.sync += im_addr_d.eq(im_addr) + self.i_fsm = i_fsm = FSM(reset_state="IDLE") + i_fsm.act("IDLE", + If(im_addr != im_addr_d, + NextValue(im_valid, 0), + NextState("READ") + ) + ) + i_fsm.act("READ", + ibus.stb.eq(1), + ibus.cyc.eq(1), + ibus.we.eq(0), + ibus.adr.eq(im_addr), + ibus.sel.eq(0b1111), + If(ibus.ack, + NextValue(im_valid, 1), + NextValue(im_data, ibus.dat_r), + NextState("IDLE") + ) + ) + + # uRV Data Bus. + # ------------- + self.dm_fifo = dm_fifo = stream.SyncFIFO( + layout = [("addr", 32), ("we", 1), ("data", 32), ("sel", 4)], + depth = 16, + ) + self.comb += [ + dm_fifo.sink.valid.eq(dm_store | dm_load), + dm_fifo.sink.we.eq(dm_store), + dm_fifo.sink.addr.eq(dm_addr), + dm_fifo.sink.data.eq(dm_data_s), + dm_fifo.sink.sel.eq(dm_data_select), + ] + self.dm_fsm = dm_fsm = FSM(reset_state="IDLE") + dm_fsm.act("IDLE", + If(dm_fifo.source.valid, + If(dm_fifo.source.we, + NextState("WRITE") + ).Else( + NextState("READ") + ) + ) + ) + dm_fsm.act("WRITE", + dbus.stb.eq(1), + dbus.cyc.eq(1), + dbus.we.eq(1), + dbus.adr.eq(dm_fifo.source.addr), + dbus.sel.eq(dm_fifo.source.sel), + dbus.dat_w.eq(dm_fifo.source.data), + If(dbus.ack, + dm_fifo.source.ready.eq(1), + dm_store_done.eq(1), + NextState("IDLE") + ) + ) + dm_fsm.act("READ", + dbus.stb.eq(1), + dbus.cyc.eq(1), + dbus.we.eq(0), + dbus.adr.eq(dm_fifo.source.addr), + dbus.sel.eq(dm_fifo.source.sel), + If(dbus.ack, + dm_fifo.source.ready.eq(1), + dm_load_done.eq(1), + dm_data_l.eq(dbus.dat_r), + NextState("IDLE") + ) + ) + + # Add Verilog sources. + # -------------------- + self.add_sources(platform, variant) + + def set_reset_address(self, reset_address): + assert reset_address == 0 + self.reset_address = reset_address + + @staticmethod + def add_sources(platform, variant): + if not os.path.exists("urv-core"): + os.system(f"git clone https://ohwr.org/project/urv-core/") + vdir = "urv-core/rtl" + platform.add_verilog_include_path("urv-core/rtl") + platform.add_sources([ + "urv-core/rtl/urv_cpu.v", + "urv-core/rtl/urv_exec.v", + "urv-core/rtl/urv_fetch.v", + "urv-core/rtl/urv_decode.v", + "urv-core/rtl/urv_regfile.v", + "urv-core/rtl/urv_writeback.v", + "urv-core/rtl/urv_shifter.v", + "urv-core/rtl/urv_multiply.v", + "urv-core/rtl/urv_divide.v", + "urv-core/rtl/urv_csr.v", + "urv-core/rtl/urv_timer.v", + "urv-core/rtl/urv_exceptions.v", + "urv-core/rtl/urv_iram.v", + "urv-core/rtl/urv_ecc.v", + ]) + + def do_finalize(self): + self.specials += Instance("urv_cpu", **self.cpu_params) diff --git a/litex/soc/cores/cpu/urv/crt0.S b/litex/soc/cores/cpu/urv/crt0.S new file mode 100644 index 0000000000..683f6ad784 --- /dev/null +++ b/litex/soc/cores/cpu/urv/crt0.S @@ -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 diff --git a/litex/soc/cores/cpu/urv/irq.h b/litex/soc/cores/cpu/urv/irq.h new file mode 100644 index 0000000000..1aa55bc8e4 --- /dev/null +++ b/litex/soc/cores/cpu/urv/irq.h @@ -0,0 +1,4 @@ +#ifndef __IRQ_H +#define __IRQ_H + +#endif /* __IRQ_H */ diff --git a/litex/soc/cores/cpu/urv/system.h b/litex/soc/cores/cpu/urv/system.h new file mode 100644 index 0000000000..828c87bc1d --- /dev/null +++ b/litex/soc/cores/cpu/urv/system.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 */ diff --git a/litex/soc/integration/builder.py b/litex/soc/integration/builder.py index 027862e8f6..a436ba52d6 100644 --- a/litex/soc/integration/builder.py +++ b/litex/soc/integration/builder.py @@ -339,6 +339,11 @@ def _initialize_rom_software(self): # Initialize SoC with with BIOS data. self.soc.init_rom(name="rom", contents=bios_data) + # FIXME: Remove uRV ROM Init Workaround. + from litex.soc.cores.cpu.urv import uRV + if isinstance(self.soc.cpu, uRV): + self.soc.cpu.rom.init = bios_data + def build(self, **kwargs): # Pass Output Directory to Platform. self.soc.platform.output_dir = self.output_dir