diff --git a/litex/soc/cores/cpu/picorv32/core.py b/litex/soc/cores/cpu/picorv32/core.py index 7fe830d66..93f3358a2 100644 --- a/litex/soc/cores/cpu/picorv32/core.py +++ b/litex/soc/cores/cpu/picorv32/core.py @@ -9,10 +9,12 @@ class PicoRV32(Module): name = "picorv32" endianness = "little" gcc_triple = ("riscv64-unknown-elf", "riscv32-unknown-elf") - gcc_flags = "-D__picorv32__ -mno-save-restore -march=rv32im -mabi=ilp32" + gcc_flags_template = "-D__picorv32__ -mno-save-restore -march=rv32{ext} -mabi=ilp32" linker_output_format = "elf32-littleriscv" def __init__(self, platform, progaddr_reset, variant): + self.gcc_flags = "" + self.reset = Signal() self.ibus = i = wishbone.Interface() self.dbus = d = wishbone.Interface() @@ -29,31 +31,53 @@ class PicoRV32(Module): mem_wstrb = Signal(4) mem_rdata = Signal(32) + # PicoRV32 parameters. To create a new variant, modify this dictionary + # and change the desired parameters. + picorv32_params = { + "p_ENABLE_COUNTERS" : 1, + "p_ENABLE_COUNTERS64" : 1, + # Changing REGS has no effect as on FPGAs, the registers are + # implemented using a register file stored in DPRAM. + "p_ENABLE_REGS_16_31" : 1, + "p_ENABLE_REGS_DUALPORT" : 1, + "p_LATCHED_MEM_RDATA" : 0, + "p_TWO_STAGE_SHIFT" : 1, + "p_TWO_CYCLE_COMPARE" : 0, + "p_TWO_CYCLE_ALU" : 0, + "p_CATCH_MISALIGN" : 1, + "p_CATCH_ILLINSN" : 1, + "p_ENABLE_PCPI" : 0, + "p_ENABLE_MUL" : 1, + "p_ENABLE_DIV" : 1, + "p_ENABLE_FAST_MUL" : 0, + "p_ENABLE_IRQ" : 1, + "p_ENABLE_IRQ_QREGS" : 1, + "p_ENABLE_IRQ_TIMER" : 1, + "p_ENABLE_TRACE" : 0, + "p_MASKED_IRQ" : 0x00000000, + "p_LATCHED_IRQ" : 0xffffffff, + "p_PROGADDR_RESET" : progaddr_reset, + "p_PROGADDR_IRQ" : progaddr_reset + 0x00000010, + "p_STACKADDR" : 0xffffffff + } + + if variant == None: + self.gcc_flags = PicoRV32.gcc_flags_template.format(ext="im") + elif variant == "minimal": + picorv32_params.update({ + "p_ENABLE_COUNTERS" : 0, + "p_ENABLE_COUNTERS64" : 0, + "p_TWO_STAGE_SHIFT" : 0, + "p_CATCH_MISALIGN" : 0, + "p_ENABLE_MUL" : 0, + "p_ENABLE_DIV" : 0, + "p_ENABLE_IRQ_TIMER" : 0 + }) + self.gcc_flags = PicoRV32.gcc_flags_template.format(ext="i") + self.specials += Instance("picorv32", - # parameters - p_ENABLE_COUNTERS=1, - p_ENABLE_COUNTERS64=1, - p_ENABLE_REGS_16_31=1, - p_ENABLE_REGS_DUALPORT=1, - p_LATCHED_MEM_RDATA=0, - p_TWO_STAGE_SHIFT=1, - p_TWO_CYCLE_COMPARE=0, - p_TWO_CYCLE_ALU=0, - p_CATCH_MISALIGN=1, - p_CATCH_ILLINSN=1, - p_ENABLE_PCPI=0, - p_ENABLE_MUL=1, - p_ENABLE_DIV=1, - p_ENABLE_FAST_MUL=0, - p_ENABLE_IRQ=1, - p_ENABLE_IRQ_QREGS=1, - p_ENABLE_IRQ_TIMER=1, - p_ENABLE_TRACE=0, - p_MASKED_IRQ=0x00000000, - p_LATCHED_IRQ=0xffffffff, - p_PROGADDR_RESET=progaddr_reset, - p_PROGADDR_IRQ=0x00000010, - p_STACKADDR=0xffffffff, + # parameters dictionary + **picorv32_params, # clock / reset i_clk=ClockSignal(), diff --git a/litex/soc/software/libbase/crt0-picorv32.S b/litex/soc/software/libbase/crt0-picorv32.S index 1d6faa851..7e4c712b3 100644 --- a/litex/soc/software/libbase/crt0-picorv32.S +++ b/litex/soc/software/libbase/crt0-picorv32.S @@ -18,7 +18,23 @@ _start: .org 0x00000010 # IRQ _irq_vector: - j _irq + addi sp, sp, -16 + sw t0, 4(sp) + sw ra, 8(sp) + /* By convention, q2 holds true IRQ vector, but remains caller-save. + We rely on the assumption that compiler-generated code will never touch + the QREGs. q3 is truly scratch/caller-save. */ + picorv32_getq_insn(t0, q2) + sw t0, 12(sp) + + jalr t0 // Call the true IRQ vector. + + lw t0, 12(sp) + picorv32_setq_insn(q2, t0) // Restore the true IRQ vector. + lw ra, 8(sp) + lw t0, 4(sp) + addi sp, sp, 16 + picorv32_retirq_insn() // return from interrupt /* @@ -132,9 +148,7 @@ _irq: /* restore x1 - x2 from q registers */ picorv32_getq_insn(x1, q1) picorv32_getq_insn(x2, q2) - - /* return from interrupt */ - picorv32_retirq_insn() + ret /* * Reset handler, branched to from the vector. @@ -180,9 +194,23 @@ _crt0: la t1, _irq_mask sw t0, 0(t1) + /* Clear BSS */ + la t0, _fbss + la t1, _ebss +2: + sw zero, 0(t0) + addi t0, t0, 4 + bltu t0, t1, 2b + /* set main stack */ la sp, _fstack + /* Set up address to IRQ handler since vector is hardcoded. + By convention, q2 keeps the pointer to the true IRQ handler, + to emulate relocatable interrupts. */ + la t0, _irq + picorv32_setq_insn(q2, t0) + /* jump to main */ jal ra, main @@ -206,7 +234,7 @@ _irq_enable: picorv32_maskirq_insn(zero, t0) ret -/* +/* * Disable interrupts by masking all interrupts (the mask should already be * up to date) */ @@ -239,7 +267,7 @@ _irq_setmask: picorv32_maskirq_insn(zero, a0) 1: ret - + .section .bss irq_regs: @@ -272,4 +300,3 @@ _irq_mask: .global _irq_enabled _irq_enabled: .word 0 -