diff --git a/litex/soc/cores/cpu/picorv32/core.py b/litex/soc/cores/cpu/picorv32/core.py index 3e9464366..9a0af3a37 100644 --- a/litex/soc/cores/cpu/picorv32/core.py +++ b/litex/soc/cores/cpu/picorv32/core.py @@ -6,10 +6,11 @@ from litex.soc.interconnect import wishbone class PicoRV32(Module): - def __init__(self, platform, progaddr_reset): + def __init__(self, platform, progaddr_reset, variant): self.ibus = i = wishbone.Interface() self.dbus = d = wishbone.Interface() self.interrupt = Signal(32) + self.trap = Signal() # # # @@ -34,9 +35,10 @@ class PicoRV32(Module): p_CATCH_MISALIGN=1, p_CATCH_ILLINSN=1, p_ENABLE_PCPI=0, - p_ENABLE_MUL=0, + p_ENABLE_MUL=1, + p_ENABLE_DIV=1, p_ENABLE_FAST_MUL=0, - p_ENABLE_IRQ=0, + p_ENABLE_IRQ=1, p_ENABLE_IRQ_QREGS=1, p_ENABLE_IRQ_TIMER=1, p_ENABLE_TRACE=0, @@ -51,7 +53,7 @@ class PicoRV32(Module): i_resetn=~ResetSignal(), # trap - o_trap=Signal(), # not used + o_trap=self.trap, # memory interface o_mem_valid=mem_valid, diff --git a/litex/soc/cores/cpu/picorv32/verilog b/litex/soc/cores/cpu/picorv32/verilog index e630bedda..a9e0ea54c 160000 --- a/litex/soc/cores/cpu/picorv32/verilog +++ b/litex/soc/cores/cpu/picorv32/verilog @@ -1 +1 @@ -Subproject commit e630bedda4f16d5f061f93879177a2d6b2a66d29 +Subproject commit a9e0ea54cffa162cfe901ff8d30d8877a18c6d8e diff --git a/litex/soc/integration/builder.py b/litex/soc/integration/builder.py index 3cfe1ab29..066c847a1 100644 --- a/litex/soc/integration/builder.py +++ b/litex/soc/integration/builder.py @@ -130,13 +130,17 @@ class Builder: def _initialize_rom(self): bios_file = os.path.join(self.output_dir, "software", "bios", "bios.bin") + endianness = cpu_interface.cpu_endianness[self.soc.cpu_type] with open(bios_file, "rb") as boot_file: boot_data = [] while True: w = boot_file.read(4) if not w: break - boot_data.append(struct.unpack(">I", w)[0]) + if endianness == 'little': + boot_data.append(struct.unpack("I", w)[0]) self.soc.initialize_rom(boot_data) def build(self, toolchain_path=None, **kwargs): @@ -157,9 +161,11 @@ class Builder: if self.gateware_toolchain_path is not None: toolchain_path = self.gateware_toolchain_path + + if 'run' not in kwargs: + kwargs['run'] = self.compile_gateware vns = self.soc.build(build_dir=os.path.join(self.output_dir, "gateware"), - run=self.compile_gateware, toolchain_path=toolchain_path, - **kwargs) + toolchain_path=toolchain_path, **kwargs) return vns diff --git a/litex/soc/integration/cpu_interface.py b/litex/soc/integration/cpu_interface.py index 38c5d5e72..fd1e4afb9 100644 --- a/litex/soc/integration/cpu_interface.py +++ b/litex/soc/integration/cpu_interface.py @@ -35,7 +35,7 @@ def get_cpu_mak(cpu): elif cpu == "riscv32": assert not clang, "riscv32 not supported with clang." triple = "riscv32-unknown-elf" - cpuflags = "-mno-save-restore" + cpuflags = "-mno-save-restore -march=rv32im -mabi=ilp32" clang = False else: raise ValueError("Unsupported CPU type: "+cpu) diff --git a/litex/soc/software/bios/main.c b/litex/soc/software/bios/main.c index 64ca2e0bb..082aa62fb 100644 --- a/litex/soc/software/bios/main.c +++ b/litex/soc/software/bios/main.c @@ -497,7 +497,7 @@ int main(int i, char **c) printf("\e[1mLM32\e[0m\n"); #elif __or1k__ printf("\e[1mOR1K\e[0m\n"); -#elif __riscv__ +#elif __riscv printf("\e[1mRISC-V\n"); #else printf("\e[1mUnknown\e[0m\n"); diff --git a/litex/soc/software/bios/sdram.c b/litex/soc/software/bios/sdram.c index 5b1ebd701..7550282ad 100644 --- a/litex/soc/software/bios/sdram.c +++ b/litex/soc/software/bios/sdram.c @@ -18,7 +18,7 @@ static void cdelay(int i) __asm__ volatile("nop"); #elif defined (__or1k__) __asm__ volatile("l.nop"); -#elif defined (__riscv__) +#elif defined (__riscv) __asm__ volatile("nop"); #else #error Unsupported architecture diff --git a/litex/soc/software/common.mak b/litex/soc/software/common.mak index 11e8fc385..82cb37c0c 100644 --- a/litex/soc/software/common.mak +++ b/litex/soc/software/common.mak @@ -1,7 +1,7 @@ TARGET_PREFIX=$(TRIPLE)- RM ?= rm -f -PYTHON ?= python3 +PYTHON ?= python ifeq ($(CLANG),1) CC_normal := clang -target $(TRIPLE) -integrated-as diff --git a/litex/soc/software/include/base/irq.h b/litex/soc/software/include/base/irq.h index 331d6e6f2..a042384c2 100644 --- a/litex/soc/software/include/base/irq.h +++ b/litex/soc/software/include/base/irq.h @@ -5,6 +5,27 @@ extern "C" { #endif +#ifdef __riscv +// PicoRV32 has a very limited interrupt support, implemented via custom +// instructions. It also doesn't have a global interrupt enable/disable, so +// we have to emulate it via saving and restoring a mask and using 0/~1 as a +// hardware mask. +// Due to all this somewhat low-level mess, all of the glue is implementein +// the RiscV crt0, and this header is kept as a thin wrapper. Since interrupts +// managed by this layer, do not call interrupt instructions directly, as the +// state will go out of sync with the hardware. + +// Read only. +extern unsigned int _irq_pending; +// Read only. +extern unsigned int _irq_mask; +// Read only. +extern unsigned int _irq_enabled; +extern void _irq_enable(void); +extern void _irq_disable(void); +extern void _irq_setmask(unsigned int); +#endif + #ifdef __or1k__ #include #endif @@ -17,9 +38,8 @@ static inline unsigned int irq_getie(void) return ie; #elif defined (__or1k__) return !!(mfspr(SPR_SR) & SPR_SR_IEE); -#elif defined (__riscv__) - /* FIXME */ - return 0; +#elif defined (__riscv) + return _irq_enabled != 0; #else #error Unsupported architecture #endif @@ -34,9 +54,11 @@ static inline void irq_setie(unsigned int ie) mtspr(SPR_SR, mfspr(SPR_SR) | SPR_SR_IEE); else mtspr(SPR_SR, mfspr(SPR_SR) & ~SPR_SR_IEE); -#elif defined (__riscv__) - /* FIXME */ - return 0; +#elif defined (__riscv) + if (ie & 0x1) + _irq_enable(); + else + _irq_disable(); #else #error Unsupported architecture #endif @@ -50,9 +72,10 @@ static inline unsigned int irq_getmask(void) return mask; #elif defined (__or1k__) return mfspr(SPR_PICMR); -#elif defined (__riscv__) - /* FIXME */ - return 0; +#elif defined (__riscv) + // PicoRV32 interrupt mask bits are high-disabled. This is the inverse of how + // LiteX sees things. + return ~_irq_mask; #else #error Unsupported architecture #endif @@ -64,9 +87,10 @@ static inline void irq_setmask(unsigned int mask) __asm__ __volatile__("wcsr IM, %0" : : "r" (mask)); #elif defined (__or1k__) mtspr(SPR_PICMR, mask); -#elif defined (__riscv__) - /* FIXME */ - return 0; +#elif defined (__riscv) + // PicoRV32 interrupt mask bits are high-disabled. This is the inverse of how + // LiteX sees things. + _irq_setmask(~mask); #else #error Unsupported architecture #endif @@ -80,9 +104,8 @@ static inline unsigned int irq_pending(void) return pending; #elif defined (__or1k__) return mfspr(SPR_PICSR); -#elif defined (__riscv__) - /* FIXME */ - return 0; +#elif defined (__riscv) + return _irq_pending; #else #error Unsupported architecture #endif diff --git a/litex/soc/software/include/base/picorv32-extraops.S b/litex/soc/software/include/base/picorv32-extraops.S new file mode 100644 index 000000000..9443177da --- /dev/null +++ b/litex/soc/software/include/base/picorv32-extraops.S @@ -0,0 +1,101 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#define regnum_q0 0 +#define regnum_q1 1 +#define regnum_q2 2 +#define regnum_q3 3 + +#define regnum_x0 0 +#define regnum_x1 1 +#define regnum_x2 2 +#define regnum_x3 3 +#define regnum_x4 4 +#define regnum_x5 5 +#define regnum_x6 6 +#define regnum_x7 7 +#define regnum_x8 8 +#define regnum_x9 9 +#define regnum_x10 10 +#define regnum_x11 11 +#define regnum_x12 12 +#define regnum_x13 13 +#define regnum_x14 14 +#define regnum_x15 15 +#define regnum_x16 16 +#define regnum_x17 17 +#define regnum_x18 18 +#define regnum_x19 19 +#define regnum_x20 20 +#define regnum_x21 21 +#define regnum_x22 22 +#define regnum_x23 23 +#define regnum_x24 24 +#define regnum_x25 25 +#define regnum_x26 26 +#define regnum_x27 27 +#define regnum_x28 28 +#define regnum_x29 29 +#define regnum_x30 30 +#define regnum_x31 31 + +#define regnum_zero 0 +#define regnum_ra 1 +#define regnum_sp 2 +#define regnum_gp 3 +#define regnum_tp 4 +#define regnum_t0 5 +#define regnum_t1 6 +#define regnum_t2 7 +#define regnum_s0 8 +#define regnum_s1 9 +#define regnum_a0 10 +#define regnum_a1 11 +#define regnum_a2 12 +#define regnum_a3 13 +#define regnum_a4 14 +#define regnum_a5 15 +#define regnum_a6 16 +#define regnum_a7 17 +#define regnum_s2 18 +#define regnum_s3 19 +#define regnum_s4 20 +#define regnum_s5 21 +#define regnum_s6 22 +#define regnum_s7 23 +#define regnum_s8 24 +#define regnum_s9 25 +#define regnum_s10 26 +#define regnum_s11 27 +#define regnum_t3 28 +#define regnum_t4 29 +#define regnum_t5 30 +#define regnum_t6 31 + +// x8 is s0 and also fp +#define regnum_fp 8 + +#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \ +.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0)) + +#define picorv32_getq_insn(_rd, _qs) \ +r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_setq_insn(_qd, _rs) \ +r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011) + +#define picorv32_retirq_insn() \ +r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) + +#define picorv32_maskirq_insn(_rd, _rs) \ +r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + +#define picorv32_waitirq_insn(_rd) \ +r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_timer_insn(_rd, _rs) \ +r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) diff --git a/litex/soc/software/libbase/crt0-riscv32.S b/litex/soc/software/libbase/crt0-riscv32.S index 4b7a39222..1d6faa851 100644 --- a/litex/soc/software/libbase/crt0-riscv32.S +++ b/litex/soc/software/libbase/crt0-riscv32.S @@ -1,5 +1,145 @@ +/* + * Copyright 2018, Serge Bazanski + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + */ + +#include "picorv32-extraops.S" + +/* + * Interrupt vector. + */ .global _start _start: + +.org 0x00000000 # Reset + j _crt0 + +.org 0x00000010 # IRQ +_irq_vector: + j _irq + + +/* + * IRQ handler, branched to from the vector. + */ +_irq: + /* save x1/x2 to q1/q2 */ + picorv32_setq_insn(q2, x1) + picorv32_setq_insn(q3, x2) + + /* use x1 to index into irq_regs */ + lui x1, %hi(irq_regs) + addi x1, x1, %lo(irq_regs) + + /* use x2 as scratch space for saving registers */ + + /* q0 (== x1), q2(== x2), q3 */ + picorv32_getq_insn(x2, q0) + sw x2, 0*4(x1) + picorv32_getq_insn(x2, q2) + sw x2, 1*4(x1) + picorv32_getq_insn(x2, q3) + sw x2, 2*4(x1) + + /* save x3 - x31 */ + sw x3, 3*4(x1) + sw x4, 4*4(x1) + sw x5, 5*4(x1) + sw x6, 6*4(x1) + sw x7, 7*4(x1) + sw x8, 8*4(x1) + sw x9, 9*4(x1) + sw x10, 10*4(x1) + sw x11, 11*4(x1) + sw x12, 12*4(x1) + sw x13, 13*4(x1) + sw x14, 14*4(x1) + sw x15, 15*4(x1) + sw x16, 16*4(x1) + sw x17, 17*4(x1) + sw x18, 18*4(x1) + sw x19, 19*4(x1) + sw x20, 20*4(x1) + sw x21, 21*4(x1) + sw x22, 22*4(x1) + sw x23, 23*4(x1) + sw x24, 24*4(x1) + sw x25, 25*4(x1) + sw x26, 26*4(x1) + sw x27, 27*4(x1) + sw x28, 28*4(x1) + sw x29, 29*4(x1) + sw x30, 30*4(x1) + sw x31, 31*4(x1) + + /* update _irq_pending to the currently pending interrupts */ + picorv32_getq_insn(t0, q1) + la t1, (_irq_pending) + sw t0, 0(t1) + + /* prepare C handler stack */ + lui sp, %hi(_irq_stack) + addi sp, sp, %lo(_irq_stack) + + /* call C handler */ + jal ra, isr + + /* use x1 to index into irq_regs */ + lui x1, %hi(irq_regs) + addi x1, x1, %lo(irq_regs) + + /* restore q0 - q2 */ + lw x2, 0*4(x1) + picorv32_setq_insn(q0, x2) + lw x2, 1*4(x1) + picorv32_setq_insn(q1, x2) + lw x2, 2*4(x1) + picorv32_setq_insn(q2, x2) + + /* restore x3 - x31 */ + lw x3, 3*4(x1) + lw x4, 4*4(x1) + lw x5, 5*4(x1) + lw x6, 6*4(x1) + lw x7, 7*4(x1) + lw x8, 8*4(x1) + lw x9, 9*4(x1) + lw x10, 10*4(x1) + lw x11, 11*4(x1) + lw x12, 12*4(x1) + lw x13, 13*4(x1) + lw x14, 14*4(x1) + lw x15, 15*4(x1) + lw x16, 16*4(x1) + lw x17, 17*4(x1) + lw x18, 18*4(x1) + lw x19, 19*4(x1) + lw x20, 20*4(x1) + lw x21, 21*4(x1) + lw x22, 22*4(x1) + lw x23, 23*4(x1) + lw x24, 24*4(x1) + lw x25, 25*4(x1) + lw x26, 26*4(x1) + lw x27, 27*4(x1) + lw x28, 28*4(x1) + lw x29, 29*4(x1) + lw x30, 30*4(x1) + lw x31, 31*4(x1) + + /* restore x1 - x2 from q registers */ + picorv32_getq_insn(x1, q1) + picorv32_getq_insn(x2, q2) + + /* return from interrupt */ + picorv32_retirq_insn() + +/* + * Reset handler, branched to from the vector. + */ +_crt0: /* zero-initialize all registers */ addi x1, zero, 0 addi x2, zero, 0 @@ -33,5 +173,103 @@ _start: addi x30, zero, 0 addi x31, zero, 0 + /* mask all interrupts */ + li t0, 0xffffffff + picorv32_maskirq_insn(zero, t0) + /* reflect that in _irq_mask */ + la t1, _irq_mask + sw t0, 0(t1) + + /* set main stack */ + la sp, _fstack + /* jump to main */ jal ra, main + +1: + /* loop forever */ + j 1b + + +/* + * Enable interrupts by copying the software mask to the hardware mask + */ +.global _irq_enable +_irq_enable: + /* Set _irq_enabled to true */ + la t0, _irq_enabled + addi t1, zero, 1 + sw t1, 0(t0) + /* Set the HW IRQ mask to _irq_mask */ + la t0, _irq_mask + lw t0, 0(t0) + picorv32_maskirq_insn(zero, t0) + ret + +/* + * Disable interrupts by masking all interrupts (the mask should already be + * up to date) + */ +.global _irq_disable +_irq_disable: + /* Mask all IRQs */ + li t0, 0xffffffff + picorv32_maskirq_insn(zero, t0) + /* Set _irq_enabled to false */ + la t0, _irq_enabled + sw zero, (t0) + ret + +/* + * Set interrrupt mask. + * This updates the software mask (for readback and interrupt inable/disable) + * and the hardware mask. + * 1 means interrupt is masked (disabled). + */ +.global _irq_setmask +_irq_setmask: + /* Update _irq_mask */ + la t0, _irq_mask + sw a0, (t0) + /* Are interrupts enabled? */ + la t0, _irq_enabled + lw t0, 0(t0) + beq t0, zero, 1f + /* If so, update the HW IRQ mask */ + picorv32_maskirq_insn(zero, a0) +1: + ret + + +.section .bss +irq_regs: + /* saved interrupt registers, x0 - x31 */ + .fill 32,4 + + /* interrupt stack */ + .fill 256,4 +_irq_stack: + +/* + * Bitfield of pending interrupts, updated on ISR entry. + */ +.global _irq_pending +_irq_pending: + .word 0 + +/* + * Software copy of enabled interrupts. Do not write directly, use + * _irq_set_mask instead. + */ +.global _irq_mask +_irq_mask: + .word 0 + +/* + * Software state of global interrupts being enabled or disabled. Do not write + * directly, use _irq_disable / _irq_enable instead. + */ +.global _irq_enabled +_irq_enabled: + .word 0 + diff --git a/litex/soc/software/libbase/system.c b/litex/soc/software/libbase/system.c index 4306b9320..f23dced81 100644 --- a/litex/soc/software/libbase/system.c +++ b/litex/soc/software/libbase/system.c @@ -34,7 +34,7 @@ void flush_cpu_icache(void) for (i = 0; i < cache_size; i += cache_block_size) mtspr(SPR_ICBIR, i); -#elif defined (__riscv__) +#elif defined (__riscv) /* no instruction cache */ asm volatile("nop"); #else @@ -65,7 +65,7 @@ void flush_cpu_dcache(void) for (i = 0; i < cache_size; i += cache_block_size) mtspr(SPR_DCBIR, i); -#elif defined (__riscv__) +#elif defined (__riscv) /* no data cache */ asm volatile("nop"); #else @@ -86,7 +86,7 @@ void flush_l2_cache(void) __asm__ volatile("lw %0, (%1+0)\n":"=r"(dummy):"r"(addr)); #elif defined (__or1k__) __asm__ volatile("l.lwz %0, 0(%1)\n":"=r"(dummy):"r"(addr)); -#elif defined (__riscv__) +#elif defined (__riscv) /* FIXME */ asm volatile("nop"); #else