From 82b4052cd6b357f640d36d300dbb7e27c7eaff20 Mon Sep 17 00:00:00 2001 From: occheung Date: Wed, 14 Jul 2021 17:44:44 +0800 Subject: [PATCH] libboard_misoc: vexriscv integration Signed-off-by: occheung --- artiq/firmware/libboard_misoc/Cargo.toml | 2 + artiq/firmware/libboard_misoc/lib.rs | 12 +- .../libboard_misoc/riscv32imac/boot.rs | 48 +++++ .../libboard_misoc/riscv32imac/cache.rs | 35 ++++ .../libboard_misoc/riscv32imac/irq.rs | 46 +++++ .../libboard_misoc/riscv32imac/mod.rs | 3 + .../libboard_misoc/riscv32imac/vectors.S | 177 ++++++++++++++++++ 7 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 artiq/firmware/libboard_misoc/riscv32imac/boot.rs create mode 100644 artiq/firmware/libboard_misoc/riscv32imac/cache.rs create mode 100644 artiq/firmware/libboard_misoc/riscv32imac/irq.rs create mode 100644 artiq/firmware/libboard_misoc/riscv32imac/mod.rs create mode 100644 artiq/firmware/libboard_misoc/riscv32imac/vectors.S diff --git a/artiq/firmware/libboard_misoc/Cargo.toml b/artiq/firmware/libboard_misoc/Cargo.toml index 81ebf979f..2c817dd65 100644 --- a/artiq/firmware/libboard_misoc/Cargo.toml +++ b/artiq/firmware/libboard_misoc/Cargo.toml @@ -16,6 +16,8 @@ build_misoc = { path = "../libbuild_misoc" } byteorder = { version = "1.0", default-features = false } log = { version = "0.4", default-features = false, optional = true } smoltcp = { version = "0.6.0", default-features = false, optional = true } +riscv = { version = "0.6.0", features = ["inline-asm"] } +vexriscv = { git = "https://github.com/occheung/vexriscv-rust.git", features = ["inline-asm"] } [features] uart_console = [] diff --git a/artiq/firmware/libboard_misoc/lib.rs b/artiq/firmware/libboard_misoc/lib.rs index 5e8a92972..1d94270d8 100644 --- a/artiq/firmware/libboard_misoc/lib.rs +++ b/artiq/firmware/libboard_misoc/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![feature(asm, try_from)] +#![feature(llvm_asm)] extern crate byteorder; #[cfg(feature = "log")] @@ -11,6 +11,16 @@ extern crate smoltcp; #[path = "or1k/mod.rs"] mod arch; +#[cfg(target_arch = "riscv32")] +#[path = "riscv32imac/mod.rs"] +mod arch; + +#[cfg(target_arch = "riscv32")] +extern crate riscv; + +#[cfg(target_arch = "riscv32")] +extern crate vexriscv; + pub use arch::*; include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/mem.rs")); diff --git a/artiq/firmware/libboard_misoc/riscv32imac/boot.rs b/artiq/firmware/libboard_misoc/riscv32imac/boot.rs new file mode 100644 index 000000000..19f7b6fa6 --- /dev/null +++ b/artiq/firmware/libboard_misoc/riscv32imac/boot.rs @@ -0,0 +1,48 @@ +use super::{irq, cache}; + +pub unsafe fn reset() -> ! { + irq::set_ie(false); + llvm_asm!(r#" + j _reset_handler + nop + "# : : : : "volatile"); + loop {} +} + +pub unsafe fn jump(addr: usize) -> ! { + irq::set_ie(false); + cache::flush_cpu_icache(); + llvm_asm!(r#" + jalr x0, 0($0) + nop + "# : : "r"(addr) : : "volatile"); + loop {} +} + +pub unsafe fn hotswap(firmware: &[u8]) -> ! { + irq::set_ie(false); + llvm_asm!(r#" + # This loop overwrites itself, but it's structured in such a way + # that before that happens, it loads itself into I$$ fully. + lui a1, %hi(_reset_handler) + ori a1, a1, %lo(_reset_handler) + or a4, a1, zero + 0: bnez a2, 1f + nop + jr a4 + nop + 1: lw a3, 0(a0) + sw a3, 0(a1) + addi a0, a0, 4 + addi a1, a1, 4 + addi a2, a2, -4 + bnez a2, 0b + nop + "# + : + : "{a0}"(firmware.as_ptr() as usize), + "{a2}"(firmware.len()) + : + : "volatile"); + loop {} +} diff --git a/artiq/firmware/libboard_misoc/riscv32imac/cache.rs b/artiq/firmware/libboard_misoc/riscv32imac/cache.rs new file mode 100644 index 000000000..12fc9f3bb --- /dev/null +++ b/artiq/firmware/libboard_misoc/riscv32imac/cache.rs @@ -0,0 +1,35 @@ +#[cfg(has_ddrphy)] +use core::ptr; +#[cfg(has_ddrphy)] +use csr; +#[cfg(has_ddrphy)] +use mem; + +pub fn flush_cpu_icache() { + unsafe { + llvm_asm!(r#" + fence.i + nop + nop + nop + nop + nop + "# : : : : "volatile"); + } +} + +pub fn flush_cpu_dcache() { + unsafe { + llvm_asm!(".word(0x500F)" : : : : "volatile"); + } +} + +#[cfg(has_ddrphy)] +pub fn flush_l2_cache() { + unsafe { + for i in 0..2 * (csr::CONFIG_L2_SIZE as usize) / 4 { + let addr = mem::MAIN_RAM_BASE + i * 4; + ptr::read_volatile(addr as *const usize); + } + } +} diff --git a/artiq/firmware/libboard_misoc/riscv32imac/irq.rs b/artiq/firmware/libboard_misoc/riscv32imac/irq.rs new file mode 100644 index 000000000..4b4120fd1 --- /dev/null +++ b/artiq/firmware/libboard_misoc/riscv32imac/irq.rs @@ -0,0 +1,46 @@ +use core::{convert::TryFrom}; +use riscv::register::mstatus; +use vexriscv::register::{vmim, vmip}; + +#[inline] +pub fn get_ie() -> bool { + mstatus::read().mie() +} + +#[inline] +pub fn set_ie(ie: bool) { + unsafe { + if ie { + mstatus::set_mie() + } else { + mstatus::clear_mie() + } + } +} + +#[inline] +pub fn get_mask() -> u32 { + u32::try_from(vmim::read()).unwrap() +} + +#[inline] +pub fn set_mask(mask: u32) { + vmim::write(usize::try_from(mask).unwrap()) +} + +#[inline] +pub fn pending_mask() -> u32 { + u32::try_from(vmip::read()).unwrap() +} + +pub fn enable(irq: u32) { + set_mask(get_mask() | (1 << irq)) +} + +pub fn disable(irq: u32) { + set_mask(get_mask() & !(1 << irq)) +} + +pub fn is_pending(irq: u32) -> bool { + get_mask() & (1 << irq) != 0 +} diff --git a/artiq/firmware/libboard_misoc/riscv32imac/mod.rs b/artiq/firmware/libboard_misoc/riscv32imac/mod.rs new file mode 100644 index 000000000..8dd6e5b3c --- /dev/null +++ b/artiq/firmware/libboard_misoc/riscv32imac/mod.rs @@ -0,0 +1,3 @@ +pub mod irq; +pub mod cache; +pub mod boot; diff --git a/artiq/firmware/libboard_misoc/riscv32imac/vectors.S b/artiq/firmware/libboard_misoc/riscv32imac/vectors.S new file mode 100644 index 000000000..a571a4903 --- /dev/null +++ b/artiq/firmware/libboard_misoc/riscv32imac/vectors.S @@ -0,0 +1,177 @@ +# Adapted from riscv-rt project, with the following license: +# +# Copyright 2018 RISC-V team +# +# Permission to use, copy, modify, and/or distribute this software for any purpose +# with or without fee is hereby granted, provided that the above copyright notice and +# this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, +# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA +# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#if __riscv_xlen == 64 +# define STORE sd +# define LOAD ld +# define LOG_REGBYTES 3 +#else +# define STORE sw +# define LOAD lw +# define LOG_REGBYTES 2 +#endif +#define REGBYTES (1 << LOG_REGBYTES) + +/* + Entry point of all programs (_reset_handler). + + It initializes DWARF call frame information, the stack pointer, the + frame pointer (needed for closures to work in start_rust) and the global + pointer. Then it calls _start_rust. +*/ + +.section .vectors, "ax", @progbits +.global _reset_handler + +_reset_handler: + /* Jump to the absolute address defined by the linker script. */ + // for 32bit + .if __riscv_xlen == 32 + lui ra, %hi(_abs_start) + jr %lo(_abs_start)(ra) + .endif + + // for 64bit + .if __riscv_xlen == 64 +.option push +.option norelax // to prevent an unsupported R_RISCV_ALIGN relocation from being generated +1: + auipc ra, %pcrel_hi(1f) + ld ra, %pcrel_lo(1b)(ra) + jr ra + .align 3 +1: + .dword _abs_start +.option pop + .endif + +_abs_start: + .cfi_startproc + .cfi_undefined ra + + csrw mie, 0 + csrw mip, 0 + + li x1, 0 + li x2, 0 + li x3, 0 + li x4, 0 + li x5, 0 + li x6, 0 + li x7, 0 + li x8, 0 + li x9, 0 + li x10,0 + li x11,0 + li x12,0 + li x13,0 + li x14,0 + li x15,0 + li x16,0 + li x17,0 + li x18,0 + li x19,0 + li x20,0 + li x21,0 + li x22,0 + li x23,0 + li x24,0 + li x25,0 + li x26,0 + li x27,0 + li x28,0 + li x29,0 + li x30,0 + li x31,0 + + // Check hart id + csrr a2, mhartid + + // Allocate stacks + la sp, _fstack + + // Set frame pointer + add s0, sp, zero + + // Set trap vector + la t0, _start_trap + csrw mtvec, t0 + + // Zero initialize .bss + la t0, _fbss + la t1, _ebss + beq t0, t1, 2f +1: STORE zero, 0(t0) + addi t0, t0, REGBYTES + bne t0, t1, 1b +2: + // Enter main firmware + jal zero, main + + .cfi_endproc + +/* + Trap entry point (_start_trap) + + Saves caller saved registers ra, t0..6, a0..7, calls exception, + restores caller saved registers and then returns. +*/ +.section .trap, "ax" +.global _start_trap +/* Make it .weak so PAC/HAL can provide their own if needed. */ +.weak _start_trap + +_start_trap: + addi sp, sp, -16*REGBYTES + + STORE ra, 0*REGBYTES(sp) + STORE t0, 1*REGBYTES(sp) + STORE t1, 2*REGBYTES(sp) + STORE t2, 3*REGBYTES(sp) + STORE t3, 4*REGBYTES(sp) + STORE t4, 5*REGBYTES(sp) + STORE t5, 6*REGBYTES(sp) + STORE t6, 7*REGBYTES(sp) + STORE a0, 8*REGBYTES(sp) + STORE a1, 9*REGBYTES(sp) + STORE a2, 10*REGBYTES(sp) + STORE a3, 11*REGBYTES(sp) + STORE a4, 12*REGBYTES(sp) + STORE a5, 13*REGBYTES(sp) + STORE a6, 14*REGBYTES(sp) + STORE a7, 15*REGBYTES(sp) + + add a0, sp, zero + jal ra, exception + + LOAD ra, 0*REGBYTES(sp) + LOAD t0, 1*REGBYTES(sp) + LOAD t1, 2*REGBYTES(sp) + LOAD t2, 3*REGBYTES(sp) + LOAD t3, 4*REGBYTES(sp) + LOAD t4, 5*REGBYTES(sp) + LOAD t5, 6*REGBYTES(sp) + LOAD t6, 7*REGBYTES(sp) + LOAD a0, 8*REGBYTES(sp) + LOAD a1, 9*REGBYTES(sp) + LOAD a2, 10*REGBYTES(sp) + LOAD a3, 11*REGBYTES(sp) + LOAD a4, 12*REGBYTES(sp) + LOAD a5, 13*REGBYTES(sp) + LOAD a6, 14*REGBYTES(sp) + LOAD a7, 15*REGBYTES(sp) + + addi sp, sp, 16*REGBYTES + mret