Merge pull request #58 from q3k/for-upstream/picorv32-support
Implement IRQ for PicoRV32 on LiteX
This commit is contained in:
commit
d07ddd11d9
|
@ -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,
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit e630bedda4f16d5f061f93879177a2d6b2a66d29
|
||||
Subproject commit a9e0ea54cffa162cfe901ff8d30d8877a18c6d8e
|
|
@ -130,12 +130,16 @@ 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
|
||||
if endianness == 'little':
|
||||
boot_data.append(struct.unpack("<I", w)[0])
|
||||
else:
|
||||
boot_data.append(struct.unpack(">I", w)[0])
|
||||
self.soc.initialize_rom(boot_data)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
TARGET_PREFIX=$(TRIPLE)-
|
||||
|
||||
RM ?= rm -f
|
||||
PYTHON ?= python3
|
||||
PYTHON ?= python
|
||||
|
||||
ifeq ($(CLANG),1)
|
||||
CC_normal := clang -target $(TRIPLE) -integrated-as
|
||||
|
|
|
@ -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 <system.h>
|
||||
#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
|
||||
|
|
|
@ -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)
|
|
@ -1,5 +1,145 @@
|
|||
/*
|
||||
* Copyright 2018, Serge Bazanski <serge@bazanski.pl>
|
||||
*
|
||||
* 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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue