From aa57c7a25ec10f7736febf2435c813c306ab9dc3 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Tue, 28 Jul 2020 16:20:16 +0200 Subject: [PATCH] soc/cores/cpu/vexriscv_smp integration --- litex/soc/cores/cpu/__init__.py | 2 + litex/soc/cores/cpu/vexriscv_smp/__init__.py | 1 + .../soc/cores/cpu/vexriscv_smp/boot-helper.S | 29 ++ litex/soc/cores/cpu/vexriscv_smp/core.py | 317 ++++++++++++++++++ litex/soc/cores/cpu/vexriscv_smp/crt0.S | 107 ++++++ litex/soc/cores/cpu/vexriscv_smp/csr-defs.h | 11 + litex/soc/cores/cpu/vexriscv_smp/irq.h | 42 +++ litex/soc/cores/cpu/vexriscv_smp/system.h | 59 ++++ litex_setup.py | 1 + 9 files changed, 569 insertions(+) create mode 100644 litex/soc/cores/cpu/vexriscv_smp/__init__.py create mode 100644 litex/soc/cores/cpu/vexriscv_smp/boot-helper.S create mode 100644 litex/soc/cores/cpu/vexriscv_smp/core.py create mode 100644 litex/soc/cores/cpu/vexriscv_smp/crt0.S create mode 100644 litex/soc/cores/cpu/vexriscv_smp/csr-defs.h create mode 100644 litex/soc/cores/cpu/vexriscv_smp/irq.h create mode 100644 litex/soc/cores/cpu/vexriscv_smp/system.h diff --git a/litex/soc/cores/cpu/__init__.py b/litex/soc/cores/cpu/__init__.py index 874214e1..4d0a0463 100644 --- a/litex/soc/cores/cpu/__init__.py +++ b/litex/soc/cores/cpu/__init__.py @@ -70,6 +70,7 @@ from litex.soc.cores.cpu.serv import SERV from litex.soc.cores.cpu.picorv32 import PicoRV32 from litex.soc.cores.cpu.minerva import Minerva from litex.soc.cores.cpu.vexriscv import VexRiscv +from litex.soc.cores.cpu.vexriscv_smp import VexRiscvSMP from litex.soc.cores.cpu.cv32e40p import CV32E40P # RISC-V (64-bit) @@ -98,6 +99,7 @@ CPUS = { "picorv32" : PicoRV32, "minerva" : Minerva, "vexriscv" : VexRiscv, + "vexriscv_smp": VexRiscvSMP, "cv32e40p" : CV32E40P, # RISC-V (64-bit) diff --git a/litex/soc/cores/cpu/vexriscv_smp/__init__.py b/litex/soc/cores/cpu/vexriscv_smp/__init__.py new file mode 100644 index 00000000..0643118d --- /dev/null +++ b/litex/soc/cores/cpu/vexriscv_smp/__init__.py @@ -0,0 +1 @@ +from litex.soc.cores.cpu.vexriscv_smp.core import VexRiscvSMP diff --git a/litex/soc/cores/cpu/vexriscv_smp/boot-helper.S b/litex/soc/cores/cpu/vexriscv_smp/boot-helper.S new file mode 100644 index 00000000..daaa82cd --- /dev/null +++ b/litex/soc/cores/cpu/vexriscv_smp/boot-helper.S @@ -0,0 +1,29 @@ +.section .text, "ax", @progbits +.global boot_helper +.global smp_lottery_target +.global smp_lottery_lock +.global smp_lottery_args +.global smp_slave + +boot_helper: + sw x10, smp_lottery_args , x14 + sw x11, smp_lottery_args+4, x14 + sw x12, smp_lottery_args+8, x14 + sw x13, smp_lottery_target, x14 + fence w, w + li x15, 1 + sw x15, smp_lottery_lock, x14 + jr x13 + +smp_slave: + lw a0, smp_lottery_lock + beqz a0, smp_slave + fence r, r + + .word(0x100F) //i$ flush + lw x10, smp_lottery_args + lw x11, smp_lottery_args+4 + lw x12, smp_lottery_args+8 + lw x13, smp_lottery_target + jr x13 + diff --git a/litex/soc/cores/cpu/vexriscv_smp/core.py b/litex/soc/cores/cpu/vexriscv_smp/core.py new file mode 100644 index 00000000..c775ffa2 --- /dev/null +++ b/litex/soc/cores/cpu/vexriscv_smp/core.py @@ -0,0 +1,317 @@ +# This file is Copyright (c) 2020 Florent Kermarrec +# This file is Copyright (c) 2020 Dolu1990 +# License: BSD + +import os +from os import path + +from litex import get_data_mod +from migen import * + +from litex.soc.interconnect import wishbone +from litex.soc.interconnect.csr import * +from litex.soc.cores.cpu import CPU, CPU_GCC_TRIPLE_RISCV32 + +from litedram.common import LiteDRAMNativePort + +import os +import os.path + + +CPU_VARIANTS = { + "linux": "VexRiscv", +} + +class Open(Signal): pass + +class VexRiscvSMP(CPU): + name = "vexriscv" + human_name = "VexRiscv SMP" + 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 + + cpu_count = 1 + dcache_size = 8192 + icache_size = 8192 + dcache_ways = 2 + icache_ways = 2 + coherent_dma = False + litedram_width = 128 + dbus_width = 64 + ibus_width = 64 + + @staticmethod + def args_fill(parser): + parser.add_argument("--cpu-count", default=1, help="") + parser.add_argument("--dcache-size", default=8192, help="") + parser.add_argument("--dcache-ways", default=2, help="") + parser.add_argument("--icache-size", default=8192, help="") + parser.add_argument("--icache-ways", default=2, help="") + + + @staticmethod + def args_read(args): + VexRiscvSMP.cpu_count = args.cpu_count + VexRiscvSMP.dcache_size = args.dcache_size + VexRiscvSMP.icache_size = args.icache_size + VexRiscvSMP.dcache_ways = args.dcache_ways + VexRiscvSMP.icache_ways = args.icache_ways + + @property + def mem_map(self): + return { + "rom": 0x00000000, + "sram": 0x10000000, + "main_ram": 0x40000000, + "csr": 0xf0000000, + "clint": 0xf0010000, + } + + @property + def gcc_flags(self): + flags = " -march=rv32ima -mabi=ilp32" + flags += " -D__vexriscv__" + flags += " -DUART_POLLING" + return flags + + @staticmethod + def generate_cluster_name(): + VexRiscvSMP.cluster_name = f"VexRiscvLitexSmpCluster_Cc{VexRiscvSMP.cpu_count}_Iw{VexRiscvSMP.ibus_width}Is{VexRiscvSMP.icache_size}Iy{VexRiscvSMP.icache_ways}_Dw{VexRiscvSMP.dbus_width}Ds{VexRiscvSMP.dcache_size}Dy{VexRiscvSMP.dcache_ways}_Ldw{VexRiscvSMP.litedram_width}{'_Cdma' if VexRiscvSMP.coherent_dma else ''}" + + @staticmethod + def generate_default_configs(): + VexRiscvSMP.ibus_width = 64 + VexRiscvSMP.dbus_width = 64 + VexRiscvSMP.dcache_size = 8192 + VexRiscvSMP.icache_size = 8192 + VexRiscvSMP.dcache_ways = 2 + VexRiscvSMP.icache_ways = 2 + VexRiscvSMP.litedram_width = 128 + + VexRiscvSMP.coherent_dma = True + for core_count in [1,2,4]: + VexRiscvSMP.cpu_count = core_count + VexRiscvSMP.generate_cluster_name() + VexRiscvSMP.generate_netlist() + + VexRiscvSMP.coherent_dma = False + for core_count in [1]: + VexRiscvSMP.cpu_count = core_count + VexRiscvSMP.generate_cluster_name() + VexRiscvSMP.generate_netlist() + + + @staticmethod + def generate_netlist(): + print(f"Generating cluster netlist") + vdir = get_data_mod("cpu", "vexriscv_smp").data_location + + gen_args = [] + if(VexRiscvSMP.coherent_dma) : gen_args.append("--coherent-dma") + gen_args.append(f"--cpu-count={VexRiscvSMP.cpu_count}") + gen_args.append(f"--ibus-width={VexRiscvSMP.ibus_width}") + gen_args.append(f"--dbus-width={VexRiscvSMP.dbus_width}") + gen_args.append(f"--dcache-size={VexRiscvSMP.dcache_size}") + gen_args.append(f"--icache-size={VexRiscvSMP.icache_size}") + gen_args.append(f"--dcache-ways={VexRiscvSMP.dcache_ways}") + gen_args.append(f"--icache-ways={VexRiscvSMP.icache_ways}") + gen_args.append(f"--litedram-width={VexRiscvSMP.litedram_width}") + gen_args.append(f"--netlist-name={VexRiscvSMP.cluster_name}") + gen_args.append(f"--netlist-directory={vdir}") + + cmd = 'cd {path} && sbt "runMain vexriscv.demo.smp.VexRiscvLitexSmpClusterCmdGen {args}"'.format(path=os.path.join(vdir, "ext", "VexRiscv"), args=" ".join(gen_args)) + os.system(cmd) + + def __init__(self, platform, variant): + self.platform = platform + self.variant = "standard" + self.human_name = self.human_name + "-" + variant.upper() + self.reset = Signal() + self.jtag_clk = Signal() + self.jtag_enable = Signal() + self.jtag_capture = Signal() + self.jtag_shift = Signal() + self.jtag_update = Signal() + self.jtag_reset = Signal() + self.jtag_tdo = Signal() + self.jtag_tdi = Signal() + self.interrupt = Signal(32) + self.pbus = pbus = wishbone.Interface() + self.cbus = cbus = wishbone.Interface() + self.plicbus = plicbus = wishbone.Interface() + + self.periph_buses = [pbus] + self.memory_buses = [] # Added dynamically + + VexRiscvSMP.generate_cluster_name() + + # # # + self.cpu_params = dict( + # Clk / Rst + i_debugCd_external_clk = ClockSignal(), + i_debugCd_external_reset = ResetSignal() | self.reset, + + # Interrupts + i_interrupts = self.interrupt, + + # JTAG + i_jtag_clk = self.jtag_clk, + i_debugPort_enable = self.jtag_enable, + i_debugPort_capture = self.jtag_capture, + i_debugPort_shift = self.jtag_shift, + i_debugPort_update = self.jtag_update, + i_debugPort_reset = self.jtag_reset, + i_debugPort_tdi = self.jtag_tdi, + o_debugPort_tdo = self.jtag_tdo, + + # Peripheral Bus (Master) + o_peripheral_CYC = pbus.cyc, + o_peripheral_STB = pbus.stb, + i_peripheral_ACK = pbus.ack, + o_peripheral_WE = pbus.we, + o_peripheral_ADR = pbus.adr, + i_peripheral_DAT_MISO = pbus.dat_r, + o_peripheral_DAT_MOSI = pbus.dat_w, + o_peripheral_SEL = pbus.sel, + i_peripheral_ERR = pbus.err, + o_peripheral_CTI = pbus.cti, + o_peripheral_BTE = pbus.bte, + + # CLINT Bus (Slave) + i_clintWishbone_CYC = cbus.cyc, + i_clintWishbone_STB = cbus.stb, + o_clintWishbone_ACK = cbus.ack, + i_clintWishbone_WE = cbus.we, + i_clintWishbone_ADR = cbus.adr, + o_clintWishbone_DAT_MISO = cbus.dat_r, + i_clintWishbone_DAT_MOSI = cbus.dat_w, + + # PLIC Bus (Slave) + i_plicWishbone_CYC = plicbus.cyc, + i_plicWishbone_STB = plicbus.stb, + o_plicWishbone_ACK = plicbus.ack, + i_plicWishbone_WE = plicbus.we, + i_plicWishbone_ADR = plicbus.adr, + o_plicWishbone_DAT_MISO = plicbus.dat_r, + i_plicWishbone_DAT_MOSI = plicbus.dat_w + ) + + if self.coherent_dma: + self.dma_bus = dma_bus = wishbone.Interface(data_width=64) + + dma_bus_stall = Signal() + dma_bus_inhibit = Signal() + + self.cpu_params.update( + i_dma_wishbone_CYC = dma_bus.cyc, + i_dma_wishbone_STB = dma_bus.stb & ~dma_bus_inhibit, + o_dma_wishbone_ACK = dma_bus.ack, + i_dma_wishbone_WE = dma_bus.we, + i_dma_wishbone_SEL = dma_bus.sel, + i_dma_wishbone_ADR = dma_bus.adr, + o_dma_wishbone_DAT_MISO = dma_bus.dat_r, + i_dma_wishbone_DAT_MOSI = dma_bus.dat_w, + o_dma_wishbone_STALL = dma_bus_stall + ) + + self.sync += [ + If(dma_bus.stb & dma_bus.cyc & ~dma_bus_stall, + dma_bus_inhibit.eq(1), + ), + If(dma_bus.ack, + dma_bus_inhibit.eq(0) + ) + ] + + if "mp" in variant: + ncpus = int(variant[-2]) # FIXME + for n in range(ncpus): + ibus = LiteDRAMNativePort(mode="both", address_width=32, data_width=128) + dbus = LiteDRAMNativePort(mode="both", address_width=32, data_width=128) + self.memory_buses.append(ibus) + self.memory_buses.append(dbus) + self.cpu_params.update({ + # Instruction Memory Bus (Master) + "o_io_iMem_{}_cmd_valid".format(n) : ibus.cmd.valid, + "i_io_iMem_{}_cmd_ready".format(n) : ibus.cmd.ready, + "o_io_iMem_{}_cmd_payload_we".format(n) : ibus.cmd.we, + "o_io_iMem_{}_cmd_payload_addr".format(n) : ibus.cmd.addr, + "o_io_iMem_{}_wdata_valid".format(n) : ibus.wdata.valid, + "i_io_iMem_{}_wdata_ready".format(n) : ibus.wdata.ready, + "o_io_iMem_{}_wdata_payload_data".format(n) : ibus.wdata.data, + "o_io_iMem_{}_wdata_payload_we".format(n) : ibus.wdata.we, + "i_io_iMem_{}_rdata_valid".format(n) : ibus.rdata.valid, + "o_io_iMem_{}_rdata_ready".format(n) : ibus.rdata.ready, + "i_io_iMem_{}_rdata_payload_data".format(n) : ibus.rdata.data, + + # Data Memory Bus (Master) + "o_io_dMem_{}_cmd_valid".format(n) : dbus.cmd.valid, + "i_io_dMem_{}_cmd_ready".format(n) : dbus.cmd.ready, + "o_io_dMem_{}_cmd_payload_we".format(n) : dbus.cmd.we, + "o_io_dMem_{}_cmd_payload_addr".format(n) : dbus.cmd.addr, + "o_io_dMem_{}_wdata_valid".format(n) : dbus.wdata.valid, + "i_io_dMem_{}_wdata_ready".format(n) : dbus.wdata.ready, + "o_io_dMem_{}_wdata_payload_data".format(n) : dbus.wdata.data, + "o_io_dMem_{}_wdata_payload_we".format(n) : dbus.wdata.we, + "i_io_dMem_{}_rdata_valid".format(n) : dbus.rdata.valid, + "o_io_dMem_{}_rdata_ready".format(n) : dbus.rdata.ready, + "i_io_dMem_{}_rdata_payload_data".format(n) : dbus.rdata.data, + }) + else: + ibus = LiteDRAMNativePort(mode="both", address_width=32, data_width=128) + dbus = LiteDRAMNativePort(mode="both", address_width=32, data_width=128) + self.memory_buses.append(ibus) + self.memory_buses.append(dbus) + self.cpu_params.update( + # Instruction Memory Bus (Master) + o_iBridge_dram_cmd_valid = ibus.cmd.valid, + i_iBridge_dram_cmd_ready = ibus.cmd.ready, + o_iBridge_dram_cmd_payload_we = ibus.cmd.we, + o_iBridge_dram_cmd_payload_addr = ibus.cmd.addr, + o_iBridge_dram_wdata_valid = ibus.wdata.valid, + i_iBridge_dram_wdata_ready = ibus.wdata.ready, + o_iBridge_dram_wdata_payload_data = ibus.wdata.data, + o_iBridge_dram_wdata_payload_we = ibus.wdata.we, + i_iBridge_dram_rdata_valid = ibus.rdata.valid, + o_iBridge_dram_rdata_ready = ibus.rdata.ready, + i_iBridge_dram_rdata_payload_data = ibus.rdata.data, + + # Data Memory Bus (Master) + o_dBridge_dram_cmd_valid = dbus.cmd.valid, + i_dBridge_dram_cmd_ready = dbus.cmd.ready, + o_dBridge_dram_cmd_payload_we = dbus.cmd.we, + o_dBridge_dram_cmd_payload_addr = dbus.cmd.addr, + o_dBridge_dram_wdata_valid = dbus.wdata.valid, + i_dBridge_dram_wdata_ready = dbus.wdata.ready, + o_dBridge_dram_wdata_payload_data = dbus.wdata.data, + o_dBridge_dram_wdata_payload_we = dbus.wdata.we, + i_dBridge_dram_rdata_valid = dbus.rdata.valid, + o_dBridge_dram_rdata_ready = dbus.rdata.ready, + i_dBridge_dram_rdata_payload_data = dbus.rdata.data, + ) + + # Add verilog sources + self.add_sources(platform, variant) + + def set_reset_address(self, reset_address): + assert not hasattr(self, "reset_address") + self.reset_address = reset_address + assert reset_address == 0x00000000 + + def add_sources(self, platform, variant): + vdir = get_data_mod("cpu", "vexriscv_smp").data_location + print(f"VexRiscv cluster : {self.cluster_name}") + if not path.exists(os.path.join(vdir, self.cluster_name + ".v")): + self.generate_netlist() + + platform.add_source(os.path.join(vdir, "RamXilinx.v"), "verilog") + platform.add_source(os.path.join(vdir, self.cluster_name + ".v"), "verilog") + + def do_finalize(self): + assert hasattr(self, "reset_address") + self.specials += Instance(self.cluster_name, **self.cpu_params) diff --git a/litex/soc/cores/cpu/vexriscv_smp/crt0.S b/litex/soc/cores/cpu/vexriscv_smp/crt0.S new file mode 100644 index 00000000..fdc12de0 --- /dev/null +++ b/litex/soc/cores/cpu/vexriscv_smp/crt0.S @@ -0,0 +1,107 @@ +.global main +.global isr +.global _start + +.global smp_lottery_target +.global smp_lottery_lock +.global smp_lottery_args +.global smp_slave + +_start: + j crt_init + nop + nop + nop + nop + nop + nop + nop + +.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, trap_entry + csrw mtvec, a0 + sw x0, smp_lottery_lock, a1 + +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: + +smp_tyranny: + csrr a0, mhartid + beqz a0, bss_init + call smp_slave + + + +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: + + call main +infinit_loop: + j infinit_loop + + + +//Initialized to avoid having them set to zero by BSS clear +.bss + smp_lottery_target: .word 0 + smp_lottery_args: .word 0; .word 0; .word 0 + smp_lottery_lock: .word 0 + diff --git a/litex/soc/cores/cpu/vexriscv_smp/csr-defs.h b/litex/soc/cores/cpu/vexriscv_smp/csr-defs.h new file mode 100644 index 00000000..d98e8dfb --- /dev/null +++ b/litex/soc/cores/cpu/vexriscv_smp/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/vexriscv_smp/irq.h b/litex/soc/cores/cpu/vexriscv_smp/irq.h new file mode 100644 index 00000000..558adc4f --- /dev/null +++ b/litex/soc/cores/cpu/vexriscv_smp/irq.h @@ -0,0 +1,42 @@ +#ifndef __IRQ_H +#define __IRQ_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +static inline unsigned int irq_getie(void) +{ + return 0; +} + +static inline void irq_setie(unsigned int ie) +{ + +} + +static inline unsigned int irq_getmask(void) +{ + + return 0; +} + +static inline void irq_setmask(unsigned int mask) +{ + +} + +static inline unsigned int irq_pending(void) +{ + return 0; +} + +#ifdef __cplusplus +} +#endif + +#endif /* __IRQ_H */ diff --git a/litex/soc/cores/cpu/vexriscv_smp/system.h b/litex/soc/cores/cpu/vexriscv_smp/system.h new file mode 100644 index 00000000..a3759aec --- /dev/null +++ b/litex/soc/cores/cpu/vexriscv_smp/system.h @@ -0,0 +1,59 @@ +#ifndef __SYSTEM_H +#define __SYSTEM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +__attribute__((unused)) static void flush_cpu_icache(void) +{ + asm volatile( + ".word(0x100F)\n" + "nop\n" + "nop\n" + "nop\n" + "nop\n" + "nop\n" + ); +} + +__attribute__((unused)) static void flush_cpu_dcache(void) +{ + asm volatile(".word(0x500F)\n"); +} + +void flush_l2_cache(void); + +void busy_wait(unsigned int ms); + +#include + +#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_setup.py b/litex_setup.py index fde423c1..9eb91a7f 100755 --- a/litex_setup.py +++ b/litex_setup.py @@ -46,6 +46,7 @@ repos = [ ("pythondata-cpu-picorv32", ("https://github.com/litex-hub/", False, True, None)), ("pythondata-cpu-serv", ("https://github.com/litex-hub/", False, True, None)), ("pythondata-cpu-vexriscv", ("https://github.com/litex-hub/", False, True, None)), + ("pythondata-cpu-vexriscv_smp",("https://github.com/litex-hub/", True, True, None)), ("pythondata-cpu-rocket", ("https://github.com/litex-hub/", False, True, None)), ("pythondata-cpu-minerva", ("https://github.com/litex-hub/", False, True, None)), ("pythondata-cpu-microwatt", ("https://github.com/litex-hub/", False, True, 0xba76652)), -- 2.30.2