diff --git a/arch/riscv32/soc/riscv32-qemu/Kbuild b/arch/riscv32/soc/riscv32-qemu/Kbuild new file mode 100644 index 00000000000..95029914792 --- /dev/null +++ b/arch/riscv32/soc/riscv32-qemu/Kbuild @@ -0,0 +1,7 @@ +ccflags-y +=-I$(srctree)/include +ccflags-y +=-I$(srctree)/include/drivers +ccflags-y +=-I$(srctree)/drivers + +asflags-y := ${ccflags-y} + +obj-y = soc_irq.o vector.o qemu_irq.o diff --git a/arch/riscv32/soc/riscv32-qemu/Kconfig.defconfig b/arch/riscv32/soc/riscv32-qemu/Kconfig.defconfig new file mode 100644 index 00000000000..e2a0f3d310a --- /dev/null +++ b/arch/riscv32/soc/riscv32-qemu/Kconfig.defconfig @@ -0,0 +1,43 @@ +if SOC_RISCV32_QEMU + +config SOC + string + default "riscv32-qemu" + +config SYS_CLOCK_HW_CYCLES_PER_SEC + int + default 10000000 + +config RISCV_SOC_INTERRUPT_INIT + bool + default y + +config INCLUDE_RESET_VECTOR + bool + default y + +config NUM_IRQS + int + default 32 + +config ATOMIC_OPERATIONS_C + bool + default y + +config VECTOR_BASE_ADDR + hex + default 0x00001000 + +config VECTOR_SIZE + hex + default 0x1000 + +config RAM_BASE_ADDR + hex + default 0x80000000 + +config RAM_SIZE_MB + int + default 32 + +endif # SOC_RISCV32_QEMU diff --git a/arch/riscv32/soc/riscv32-qemu/Kconfig.soc b/arch/riscv32/soc/riscv32-qemu/Kconfig.soc new file mode 100644 index 00000000000..dd57df07520 --- /dev/null +++ b/arch/riscv32/soc/riscv32-qemu/Kconfig.soc @@ -0,0 +1,2 @@ +config SOC_RISCV32_QEMU + bool "riscv32_qemu SOC implementation" diff --git a/arch/riscv32/soc/riscv32-qemu/Makefile b/arch/riscv32/soc/riscv32-qemu/Makefile new file mode 100644 index 00000000000..8bca0cb6624 --- /dev/null +++ b/arch/riscv32/soc/riscv32-qemu/Makefile @@ -0,0 +1 @@ +soc-cflags := -I/$(srctree)/arch/$(ARCH)/soc/$(SOC_PATH)/ diff --git a/arch/riscv32/soc/riscv32-qemu/linker.ld b/arch/riscv32/soc/riscv32-qemu/linker.ld new file mode 100644 index 00000000000..71320cb58aa --- /dev/null +++ b/arch/riscv32/soc/riscv32-qemu/linker.ld @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2016 Jean-Paul Etienne + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief Linker script for riscv32-qemu + */ + +#include diff --git a/arch/riscv32/soc/riscv32-qemu/qemu_irq.c b/arch/riscv32/soc/riscv32-qemu/qemu_irq.c new file mode 100644 index 00000000000..3e6e8ba3345 --- /dev/null +++ b/arch/riscv32/soc/riscv32-qemu/qemu_irq.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016 Jean-Paul Etienne + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * @brief riscv32-qemu interrupt management code + */ +#include +#include + +void _arch_irq_enable(unsigned int irq) +{ + uint32_t mie; + + /* + * Since only internal Timer device has interrupt within in + * riscv32-qemu, use only mie CSR register to enable device interrupt. + * CSR mie register is updated using atomic instruction csrrs + * (atomic read and set bits in CSR register) + */ + __asm__ volatile ("csrrs %0, mie, %1\n" + : "=r" (mie) + : "r" (1 << irq)); +} + +void _arch_irq_disable(unsigned int irq) +{ + uint32_t mie; + + /* + * Use atomic instruction csrrc to disable device interrupt in mie CSR. + * (atomic read and clear bits in CSR register) + */ + __asm__ volatile ("csrrc %0, mie, %1\n" + : "=r" (mie) + : "r" (1 << irq)); +}; + +int _arch_irq_is_enabled(unsigned int irq) +{ + uint32_t mie; + + __asm__ volatile ("csrr %0, mie" : "=r" (mie)); + + return !!(mie & (1 << irq)); +} + +#if defined(CONFIG_RISCV_SOC_INTERRUPT_INIT) +void soc_interrupt_init(void) +{ + /* ensure that all interrupts are disabled */ + (void)irq_lock(); + + __asm__ volatile ("csrwi mie, 0\n" + "csrwi sie, 0\n" + "csrwi mip, 0\n" + "csrwi sip, 0\n"); +} +#endif diff --git a/arch/riscv32/soc/riscv32-qemu/soc.h b/arch/riscv32/soc/riscv32-qemu/soc.h new file mode 100644 index 00000000000..1adcff16136 --- /dev/null +++ b/arch/riscv32/soc/riscv32-qemu/soc.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2016 Jean-Paul Etienne + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file SoC configuration macros for the riscv-qemu + */ + +#ifndef __RISCV32_QEMU_SOC_H_ +#define __RISCV32_QEMU_SOC_H_ + +/* CSR Registers */ +#define RISCV_QEMU_MSTATUS mstatus /* Machine Status Register */ + +/* IRQ numbers */ +#define RISCV_QEMU_TIMER_IRQ 7 /* Timer Interrupt */ + +/* Exception numbers */ +#define RISCV_QEMU_ECALL_EXP 11 /* ECALL Instruction */ + +/* + * SOC-specific MSTATUS related info + */ +/* MSTATUS register to save/restore upon interrupt/exception/context switch */ +#define SOC_MSTATUS_REG RISCV_QEMU_MSTATUS + +#define SOC_MSTATUS_IEN (1 << 3) /* Machine Interrupt Enable bit */ + +/* Previous Privilege Mode - Machine Mode */ +#define SOC_MSTATUS_MPP_M_MODE (3 << 11) +/* Interrupt Enable Bit in Previous Privilege Mode */ +#define SOC_MSTATUS_MPIE (1 << 7) + +/* + * Default MSTATUS register value to restore from stack + * upon scheduling a thread for the first time + */ +#define SOC_MSTATUS_DEF_RESTORE (SOC_MSTATUS_MPP_M_MODE | SOC_MSTATUS_MPIE) + + +/* SOC-specific MCAUSE bitfields */ +/* Exception code Mask */ +#define SOC_MCAUSE_IRQ_MASK 0x7FFFFFFF +/* ECALL exception number */ +#define SOC_MCAUSE_ECALL_EXP RISCV_QEMU_ECALL_EXP + +/* SOC-Specific EXIT ISR command */ +#define SOC_ERET mret + +/* UART configuration */ +#define RISCV_QEMU_UART_BASE 0x40002000 + +/* Timer configuration */ +#define RISCV_QEMU_TIMER_BASE 0x40000000 + +#ifndef _ASMLANGUAGE +#include +#include + +#if defined(CONFIG_RISCV_SOC_INTERRUPT_INIT) +void soc_interrupt_init(void); +#endif + +/* lib-c hooks required RAM defined variables */ +#define RISCV_RAM_BASE CONFIG_RAM_BASE_ADDR +#define RISCV_RAM_SIZE MB(CONFIG_RAM_SIZE_MB) + +#endif /* !_ASMLANGUAGE */ + +#endif /* __RISCV32_QEMU_SOC_H_ */ diff --git a/arch/riscv32/soc/riscv32-qemu/soc_irq.S b/arch/riscv32/soc/riscv32-qemu/soc_irq.S new file mode 100644 index 00000000000..bc673cd6ba7 --- /dev/null +++ b/arch/riscv32/soc/riscv32-qemu/soc_irq.S @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016 Jean-Paul Etienne + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _ASMLANGUAGE + +#include +#include +#include +#include +#include + +/* exports */ +GTEXT(__soc_is_irq) +GTEXT(__soc_handle_irq) + +/* + * SOC-specific function to handle pending IRQ number generating the interrupt. + * Exception number is given as parameter via register a0. + */ +SECTION_FUNC(exception.other, __soc_handle_irq) + /* Clear exception number from CSR mip register */ + li t1, 1 + sll t0, t1, a0 + csrrc t1, mip, t0 + + /* Return */ + jalr x0, ra + + +/* + * SOC-specific function to determine if the exception is the result of a + * an interrupt or an exception + * return 1 (interrupt) or 0 (exception) + */ +SECTION_FUNC(exception.other, __soc_is_irq) + /* Get exception number from the mcause CSR register. */ + csrr t0, mcause + li t1, SOC_MCAUSE_IRQ_MASK + and t0, t0, t1 + + /* if IRQ number != RISCV_QEMU_TIMER_IRQ, not interrupt */ + li t1, RISCV_QEMU_TIMER_IRQ + addi a0, x0, 0 + bne t0, t1, not_interrupt + addi a0, a0, 1 + +not_interrupt: + /* return */ + jalr x0, ra diff --git a/arch/riscv32/soc/riscv32-qemu/vector.S b/arch/riscv32/soc/riscv32-qemu/vector.S new file mode 100644 index 00000000000..761b2fddb47 --- /dev/null +++ b/arch/riscv32/soc/riscv32-qemu/vector.S @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016 Jean-Paul Etienne + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _ASMLANGUAGE + +#include + +/* imports */ +GTEXT(__reset) +GTEXT(__irq_wrapper) + +/* + * following riscv32-qemu specs + * IVT is placed at 0x000001000 and is mapped as follows: + * 0x00001000: reset + * 0x00001004: non-maskable interrupt (nmi) vector + * 0x00001010: machine trap (mt) vector + * + * Call __irq_wrapper to handle all interrupts/exceptions/faults + */ +SECTION_FUNC(vectors, vinit) + .option norvc; + + /* + * jal instruction cannot be used to jump to address whose offset + * is > 12-bits wide. In this case, we have to use a call or tail + * instruction to jump to a far-away sub-routine. + * + * Given that IVT is found at a different address-space than the + * RAM in riscv32-qemu, we have to use call or tail instructions + * to jump to __reset or __isr_wrapper subroutines. + * However, call or tail instructions are pseudo instructions, + * which generate two base-instructions upon compilation. In this case, + * using them at a particular entry in the IVT will overwrite the next + * entry in the IVT. For example, using tail instruction in the + * reset vector, will overwrite the nmi-vector entry. To prevent this, + * perform a two-phase jump instructions to __reset or __irq_wrapper + * subroutines. The first jump performs a jal instruction, which will + * jump to an offset in the same vector address-space, but outside the + * IVT. The second jump performs a tail instruction to the __reset + * or __irq_wrapper subroutines. + */ + + /* Call __reset for reset vector */ + jal x0, do_reset + + /* Call __irq_wrapper for nmi vector */ + jal x0, do_irq_wrapper + + .org 0x10 + /* Call __irq_wrapper for mt vector */ + jal x0, do_irq_wrapper + + .org 0x400 /* we are outside IVT */ +do_reset: + /* + * Set mtvec (Machine Trap-Vector Base-Address Register) + * to __irq_wrapper, so that we jump directly to __irq_wrapper, + * instead to the default machine trap vector address in IVT. + * This will preserve us from performing two jump instructions upon + * an interrupt. + */ + la t0, __irq_wrapper + csrw mtvec, t0 + + /* Jump to __reset */ + tail __reset + +do_irq_wrapper: + tail __irq_wrapper diff --git a/include/arch/riscv32/arch.h b/include/arch/riscv32/arch.h index a1beeb817e0..e8f55a269b1 100644 --- a/include/arch/riscv32/arch.h +++ b/include/arch/riscv32/arch.h @@ -140,6 +140,8 @@ static ALWAYS_INLINE void _arch_irq_unlock(unsigned int key) #if defined(CONFIG_SOC_RISCV32_PULPINO) #include +#elif defined(CONFIG_SOC_RISCV32_QEMU) +#include #endif #ifdef __cplusplus diff --git a/include/arch/riscv32/riscv32-qemu/asm_inline.h b/include/arch/riscv32/riscv32-qemu/asm_inline.h new file mode 100644 index 00000000000..6dc9700fd36 --- /dev/null +++ b/include/arch/riscv32/riscv32-qemu/asm_inline.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016 Jean-Paul Etienne + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ASM_INLINE_PUBLIC_H +#define _ASM_INLINE_PUBLIC_H + +/* + * The file must not be included directly + * Include arch/cpu.h instead + */ + +#if defined(__GNUC__) +#include +#else +#error "Supports only GNU C compiler" +#endif + +#endif /* _ASM_INLINE_PUBLIC_H */ diff --git a/include/arch/riscv32/riscv32-qemu/asm_inline_gcc.h b/include/arch/riscv32/riscv32-qemu/asm_inline_gcc.h new file mode 100644 index 00000000000..1ddb4056329 --- /dev/null +++ b/include/arch/riscv32/riscv32-qemu/asm_inline_gcc.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016 Jean-Paul Etienne + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ASM_INLINE_GCC_H +#define _ASM_INLINE_GCC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The file must not be included directly + * Include arch/cpu.h instead + * riscv32-qemu does not have bit manipulation asm opcodes. + * Handle find_lsb_set and find_msb_set in C. + */ + +#ifndef _ASMLANGUAGE + +#include + +/** + * + * @brief find least significant bit set in a 32-bit word + * + * This routine finds the first bit set starting from the least significant bit + * in the argument passed in and returns the index of that bit. Bits are + * numbered starting at 1 from the least significant bit. A return value of + * zero indicates that the value passed is zero. + * + * @return least significant bit set, 0 if @a op is 0 + */ +static ALWAYS_INLINE unsigned int find_lsb_set(uint32_t op) +{ + return __builtin_ffs(op); +} + +/** + * + * @brief find most significant bit set in a 32-bit word + * + * This routine finds the first bit set starting from the most significant bit + * in the argument passed in and returns the index of that bit. Bits are + * numbered starting at 1 from the least significant bit. A return value of + * zero indicates that the value passed is zero. + * + * @return most significant bit set, 0 if @a op is 0 + */ +static ALWAYS_INLINE unsigned int find_msb_set(uint32_t op) +{ + if (!op) + return 0; + + return 32 - __builtin_clz(op); +} + +#endif /* _ASMLANGUAGE */ + +#ifdef __cplusplus +} +#endif + +#endif /* _ASM_INLINE_GCC_PUBLIC_GCC_H */ diff --git a/include/arch/riscv32/riscv32-qemu/linker.ld b/include/arch/riscv32/riscv32-qemu/linker.ld new file mode 100644 index 00000000000..9651d7b2b6b --- /dev/null +++ b/include/arch/riscv32/riscv32-qemu/linker.ld @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2016 Jean-Paul Etienne + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * @brief Linker command/script file + * + * Linker script for the riscv32-qemu platform + */ + +#define _LINKER +#define _ASMLANGUAGE + +#include +#include + +#include +#include + +#define ROMABLE_REGION RAM +#define RAMABLE_REGION RAM + +#define _VECTOR_SECTION_NAME vector +#define _EXCEPTION_SECTION_NAME exceptions +#define _RESET_SECTION_NAME reset + +#define RAM_LENGTH (CONFIG_RAM_SIZE_MB * 1024 * 1024) + +ENTRY(__reset) + +MEMORY + { + VECTOR (rx) : ORIGIN = CONFIG_VECTOR_BASE_ADDR, LENGTH = CONFIG_VECTOR_SIZE + RAM (rwx) : ORIGIN = CONFIG_RAM_BASE_ADDR, LENGTH = RAM_LENGTH +} + +SECTIONS + { + + GROUP_START(VECTOR) + + SECTION_PROLOGUE(_VECTOR_SECTION_NAME,,) + { + . = ALIGN(4); + KEEP(*(.vectors.*)) + } GROUP_LINK_IN(VECTOR) + + GROUP_END(VECTOR) + + GROUP_START(RAM) + + SECTION_PROLOGUE(_RESET_SECTION_NAME,,) + { + KEEP(*(.reset.*)) + } GROUP_LINK_IN(RAM) + + SECTION_PROLOGUE(_EXCEPTION_SECTION_NAME,,) + { + KEEP(*(".exception.entry.*")) + *(".exception.other.*") + } GROUP_LINK_IN(RAM) + + SECTION_PROLOGUE(_TEXT_SECTION_NAME,,) + { + . = ALIGN(4); + + _image_text_start = .; + *(.text) + *(".text.*") + *(.gnu.linkonce.t.*) + } GROUP_LINK_IN(RAM) + + _image_text_end = .; + + GROUP_END(RAM) + + GROUP_START(RAMABLE_REGION) + +#include + + SECTION_PROLOGUE(_RODATA_SECTION_NAME,,) + { + . = ALIGN(4); + *(.rodata) + *(".rodata.*") + *(.gnu.linkonce.r.*) + } GROUP_LINK_IN(RAMABLE_REGION) + +#include + + SECTION_DATA_PROLOGUE(_DATA_SECTION_NAME,,) + { + + . = ALIGN(4); + KEEP(*(.isr_irq*)) + + /* sections for IRQ0-9 */ + KEEP(*(SORT(.gnu.linkonce.isr_irq[0-9]))) + + /* sections for IRQ10-99 */ + KEEP(*(SORT(.gnu.linkonce.isr_irq[0-9][0-9]))) + + /* sections for IRQ100-999 */ + KEEP(*(SORT(.gnu.linkonce.isr_irq[0-9][0-9][0-9]))) + + *(.data) + *(".data.*") + + *(.sdata .sdata.* .gnu.linkonce.s.*) + *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) + + } GROUP_LINK_IN(RAMABLE_REGION) + + SECTION_DATA_PROLOGUE(_BSS_SECTION_NAME,(NOLOAD),) + { + /* + * For performance, BSS section is assumed to be 4 byte aligned and + * a multiple of 4 bytes + */ + . = ALIGN(4); + __bss_start = .; + *(.sbss) + *(".sbss.*") + *(.bss) + *(".bss.*") + COMMON_SYMBOLS + /* + * As memory is cleared in words only, it is simpler to ensure the BSS + * section ends on a 4 byte boundary. This wastes a maximum of 3 bytes. + */ + __bss_end = ALIGN(4); + } GROUP_LINK_IN(RAMABLE_REGION) + + SECTION_PROLOGUE(_NOINIT_SECTION_NAME,(NOLOAD),) + { + /* + * This section is used for non-initialized objects that + * will not be cleared during the boot process. + */ + *(.noinit) + *(".noinit.*") + } GROUP_LINK_IN(RAMABLE_REGION) + + _end = .; /* end of image */ + + GROUP_END(RAMABLE_REGION) +}