From 2b84c0f019f32b7e287cb4eeeff9110b01bbbd17 Mon Sep 17 00:00:00 2001 From: Brad Campbell Date: Mon, 3 Jun 2019 13:27:09 -0400 Subject: [PATCH 1/3] Add initial RISC-V support This commit is the first towards adding RISC-V support to Tock. It includes enough code that a RISC-V core will boot and can handle interrupts. It also includes basic support for the HiFive1 (a) board, and some SiFive peripherals. The HiFive1(a) board does not support User mode, so there is no support for userland (yet). This simply allows kernel code to boot and run. The SiFive peripherals are organized into their own folder because they are shared among multiple SiFive cores. --- arch/rv32i/Cargo.lock | 25 +++ arch/rv32i/Cargo.toml | 8 + arch/rv32i/README.md | 11 ++ arch/rv32i/src/lib.rs | 197 ++++++++++++++++++++++++ arch/rv32i/src/machine_timer.rs | 92 ++++++++++++ arch/rv32i/src/plic.rs | 93 ++++++++++++ arch/rv32i/src/support.rs | 27 ++++ boards/hifive1/Cargo.lock | 65 ++++++++ boards/hifive1/Cargo.toml | 25 +++ boards/hifive1/Makefile | 41 +++++ boards/hifive1/README.md | 12 ++ boards/hifive1/build.rs | 4 + boards/hifive1/layout.ld | 10 ++ boards/hifive1/src/io.rs | 40 +++++ boards/hifive1/src/main.rs | 134 +++++++++++++++++ boards/kernel_layout.ld | 40 ++++- chips/e310x/Cargo.lock | 42 ++++++ chips/e310x/Cargo.toml | 11 ++ chips/e310x/README.md | 6 + chips/e310x/src/chip.rs | 143 ++++++++++++++++++ chips/e310x/src/gpio.rs | 172 +++++++++++++++++++++ chips/e310x/src/interrupts.rs | 55 +++++++ chips/e310x/src/lib.rs | 15 ++ chips/e310x/src/prci.rs | 9 ++ chips/e310x/src/pwm.rs | 13 ++ chips/e310x/src/rtc.rs | 7 + chips/e310x/src/uart.rs | 7 + chips/e310x/src/watchdog.rs | 10 ++ chips/sifive/Cargo.lock | 33 ++++ chips/sifive/Cargo.toml | 10 ++ chips/sifive/README.md | 6 + chips/sifive/src/gpio.rs | 249 ++++++++++++++++++++++++++++++ chips/sifive/src/lib.rs | 13 ++ chips/sifive/src/prci.rs | 76 ++++++++++ chips/sifive/src/pwm.rs | 65 ++++++++ chips/sifive/src/rtc.rs | 55 +++++++ chips/sifive/src/uart.rs | 259 ++++++++++++++++++++++++++++++++ chips/sifive/src/watchdog.rs | 76 ++++++++++ 38 files changed, 2155 insertions(+), 1 deletion(-) create mode 100644 arch/rv32i/Cargo.lock create mode 100644 arch/rv32i/Cargo.toml create mode 100644 arch/rv32i/README.md create mode 100644 arch/rv32i/src/lib.rs create mode 100644 arch/rv32i/src/machine_timer.rs create mode 100644 arch/rv32i/src/plic.rs create mode 100644 arch/rv32i/src/support.rs create mode 100644 boards/hifive1/Cargo.lock create mode 100644 boards/hifive1/Cargo.toml create mode 100644 boards/hifive1/Makefile create mode 100644 boards/hifive1/README.md create mode 100644 boards/hifive1/build.rs create mode 100644 boards/hifive1/layout.ld create mode 100644 boards/hifive1/src/io.rs create mode 100644 boards/hifive1/src/main.rs create mode 100644 chips/e310x/Cargo.lock create mode 100644 chips/e310x/Cargo.toml create mode 100644 chips/e310x/README.md create mode 100644 chips/e310x/src/chip.rs create mode 100644 chips/e310x/src/gpio.rs create mode 100644 chips/e310x/src/interrupts.rs create mode 100644 chips/e310x/src/lib.rs create mode 100644 chips/e310x/src/prci.rs create mode 100644 chips/e310x/src/pwm.rs create mode 100644 chips/e310x/src/rtc.rs create mode 100644 chips/e310x/src/uart.rs create mode 100644 chips/e310x/src/watchdog.rs create mode 100644 chips/sifive/Cargo.lock create mode 100644 chips/sifive/Cargo.toml create mode 100644 chips/sifive/README.md create mode 100644 chips/sifive/src/gpio.rs create mode 100644 chips/sifive/src/lib.rs create mode 100644 chips/sifive/src/prci.rs create mode 100644 chips/sifive/src/pwm.rs create mode 100644 chips/sifive/src/rtc.rs create mode 100644 chips/sifive/src/uart.rs create mode 100644 chips/sifive/src/watchdog.rs diff --git a/arch/rv32i/Cargo.lock b/arch/rv32i/Cargo.lock new file mode 100644 index 0000000000..3cc681c72c --- /dev/null +++ b/arch/rv32i/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "kernel" +version = "0.1.0" +dependencies = [ + "tock-cells 0.1.0", + "tock-registers 0.3.0", +] + +[[package]] +name = "rv32i" +version = "0.1.0" +dependencies = [ + "kernel 0.1.0", +] + +[[package]] +name = "tock-cells" +version = "0.1.0" + +[[package]] +name = "tock-registers" +version = "0.3.0" + diff --git a/arch/rv32i/Cargo.toml b/arch/rv32i/Cargo.toml new file mode 100644 index 0000000000..df43c05cce --- /dev/null +++ b/arch/rv32i/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rv32i" +version = "0.1.0" +authors = ["Tock Project Developers "] +edition = "2018" + +[dependencies] +kernel = { path = "../../kernel" } diff --git a/arch/rv32i/README.md b/arch/rv32i/README.md new file mode 100644 index 0000000000..c88e2c3865 --- /dev/null +++ b/arch/rv32i/README.md @@ -0,0 +1,11 @@ +RISC-V 32 Bit Integer Architecture (rv32i) +========================================== + +This crate contains startup code and other base support for 32 bit RISC-V +chips. + + +ISA Documentation +----------------- + +- [Specifications](https://github.com/riscv/riscv-isa-manual/releases) diff --git a/arch/rv32i/src/lib.rs b/arch/rv32i/src/lib.rs new file mode 100644 index 0000000000..7eda8651d8 --- /dev/null +++ b/arch/rv32i/src/lib.rs @@ -0,0 +1,197 @@ +#![crate_name = "rv32i"] +#![crate_type = "rlib"] +#![feature(asm, const_fn, lang_items, global_asm)] +#![feature(crate_visibility_modifier)] +#![no_std] + +pub mod machine_timer; +pub mod plic; +pub mod support; + +extern "C" { + // Where the end of the stack region is (and hence where the stack should + // start). + static _estack: u32; + + // Address of _start_trap. + static _start_trap: u32; + + // Boundaries of the .bss section. + static mut _szero: u32; + static mut _ezero: u32; + + // Where the .data section is stored in flash. + static mut _etext: u32; + + // Boundaries of the .data section. + static mut _srelocate: u32; + static mut _erelocate: u32; +} + +// Entry point of all programs (_start). +// +// 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. +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +global_asm!( + r#" + .section .riscv.start, "ax" + .globl _start + _start: + .cfi_startproc + .cfi_undefined ra + + // Set the global pointer register using the variable defined in the linker + // script. This register is only set once. The global pointer is a method + // for sharing state between the linker and the CPU so that the linker can + // emit code with offsets that are relative to the gp register, and the CPU + // can successfully execute them. + // + // https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register + // https://groups.google.com/a/groups.riscv.org/forum/#!msg/sw-dev/60IdaZj27dY/5MydPLnHAQAJ + // https://www.sifive.com/blog/2017/08/28/all-aboard-part-3-linker-relaxation-in-riscv-toolchain/ + // + lui gp, %hi(__global_pointer$) + addi gp, gp, %lo(__global_pointer$) + + // Initialize the stack pointer register. This comes directly from the linker + // script. + lui sp, %hi(_estack) + addi sp, sp, %lo(_estack) + + // Set s0 (the frame pointer) to the start of the stack. + add s0, sp, zero + + // With that initial setup out of the way, we now branch to the main code, + // likely defined in a board's main.rs. + jal zero, reset_handler + + .cfi_endproc + "# +); + +/// Setup memory for the kernel. +/// +/// This moves the data segment from flash to RAM and zeros out the BSS section. +pub unsafe fn init_memory() { + // Relocate data segment. + // Assumes data starts right after text segment as specified by the linker + // file. + let mut pdest = &mut _srelocate as *mut u32; + let pend = &mut _erelocate as *mut u32; + let mut psrc = &_etext as *const u32; + + if psrc != pdest { + while (pdest as *const u32) < pend { + *pdest = *psrc; + pdest = pdest.offset(1); + psrc = psrc.offset(1); + } + } + + // Clear the zero segment (BSS) + let pzero = &_ezero as *const u32; + pdest = &mut _szero as *mut u32; + + while (pdest as *const u32) < pzero { + *pdest = 0; + pdest = pdest.offset(1); + } +} + +/// Tell the MCU what address the trap handler is located at. +/// +/// The trap handler is called on exceptions and for interrupts. +pub unsafe fn configure_trap_handler() { + asm!(" + // The csrw instruction writes a Control and Status Register (CSR) + // with a new value. + // + // CSR 0x305 (mtvec, 'Machine trap-handler base address.') sets the address + // of the trap handler. We do not care about its old value, so we don't + // bother reading it. + csrw 0x305, $0 // Write the mtvec CSR. + " + : + : "r"(&_start_trap) + : + : "volatile"); +} + +// Trap entry point (_start_trap) +// +// Saves caller saved registers ra, t0..6, a0..7, calls _start_trap_rust, +// restores caller saved registers and then returns. +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +global_asm!( + r#" + .section .riscv.trap, "ax" + .align 6 + //.p2align 6 + .global _start_trap + +_start_trap: + + // No usermode support, so we unconditionally assume we came from the kernel. + + addi sp, sp, -16*4 + + sw ra, 0*4(sp) + sw t0, 1*4(sp) + sw t1, 2*4(sp) + sw t2, 3*4(sp) + sw t3, 4*4(sp) + sw t4, 5*4(sp) + sw t5, 6*4(sp) + sw t6, 7*4(sp) + sw a0, 8*4(sp) + sw a1, 9*4(sp) + sw a2, 10*4(sp) + sw a3, 11*4(sp) + sw a4, 12*4(sp) + sw a5, 13*4(sp) + sw a6, 14*4(sp) + sw a7, 15*4(sp) + + jal ra, _start_trap_rust + + lw ra, 0*4(sp) + lw t0, 1*4(sp) + lw t1, 2*4(sp) + lw t2, 3*4(sp) + lw t3, 4*4(sp) + lw t4, 5*4(sp) + lw t5, 6*4(sp) + lw t6, 7*4(sp) + lw a0, 8*4(sp) + lw a1, 9*4(sp) + lw a2, 10*4(sp) + lw a3, 11*4(sp) + lw a4, 12*4(sp) + lw a5, 13*4(sp) + lw a6, 14*4(sp) + lw a7, 15*4(sp) + + addi sp, sp, 16*4 + + mret + "# +); + +/// Trap entry point rust (_start_trap_rust) +#[export_name = "_start_trap_rust"] +pub extern "C" fn start_trap_rust() {} + +// Make sure there is an abort when linking. +// +// I don't know why we need this, or why cortex-m doesn't seem to have it. +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +global_asm!( + r#" + .section .init + .globl abort + abort: + jal zero, _start + "# +); diff --git a/arch/rv32i/src/machine_timer.rs b/arch/rv32i/src/machine_timer.rs new file mode 100644 index 0000000000..1cac43f6ae --- /dev/null +++ b/arch/rv32i/src/machine_timer.rs @@ -0,0 +1,92 @@ +//! Create a timer using the Machine Timer registers. + +use kernel::common::cells::OptionalCell; +use kernel::common::registers::{register_bitfields, ReadOnly, ReadWrite}; +use kernel::common::StaticRef; +use kernel::hil; + +const MTIME_BASE: StaticRef = + unsafe { StaticRef::new(0x0200_0000 as *const MachineTimerRegisters) }; + +#[repr(C)] +struct MachineTimerRegisters { + _reserved0: [u8; 0x4000], + mtimecmp: ReadWrite, + _reserved1: [u8; 0x7FF0], + mtime: ReadOnly, +} + +register_bitfields![u64, + MTimeCmp [ + MTIMECMP OFFSET(0) NUMBITS(64) [] + ], + MTime [ + MTIME OFFSET(0) NUMBITS(64) [] + ] +]; + +pub static mut MACHINETIMER: MachineTimer = MachineTimer::new(); + +pub struct MachineTimer { + registers: StaticRef, + client: OptionalCell<&'static hil::time::Client>, +} + +impl MachineTimer { + const fn new() -> MachineTimer { + MachineTimer { + registers: MTIME_BASE, + client: OptionalCell::empty(), + } + } + + pub fn set_client(&self, client: &'static hil::time::Client) { + self.client.set(client); + } + + pub fn handle_interrupt(&self) { + self.disable_machine_timer(); + + self.client.map(|client| { + client.fired(); + }); + } + + fn disable_machine_timer(&self) { + // Disable by setting the mtimecmp register to its max value, which + // we will never hit. + self.registers + .mtimecmp + .write(MTimeCmp::MTIMECMP.val(0xFFFF_FFFF_FFFF_FFFF)); + } +} + +impl hil::time::Time for MachineTimer { + type Frequency = hil::time::Freq32KHz; + + fn disable(&self) { + self.disable_machine_timer(); + } + + fn is_armed(&self) -> bool { + // Check if mtimecmp is the max value. If it is, then we are not armed, + // otherwise we assume we have a value set. + self.registers.mtimecmp.get() != 0xFFFF_FFFF_FFFF_FFFF + } +} + +impl hil::time::Alarm for MachineTimer { + fn now(&self) -> u32 { + self.registers.mtime.get() as u32 + } + + fn set_alarm(&self, tics: u32) { + self.registers + .mtimecmp + .write(MTimeCmp::MTIMECMP.val(tics as u64)); + } + + fn get_alarm(&self) -> u32 { + self.registers.mtimecmp.get() as u32 + } +} diff --git a/arch/rv32i/src/plic.rs b/arch/rv32i/src/plic.rs new file mode 100644 index 0000000000..2f2ba78c57 --- /dev/null +++ b/arch/rv32i/src/plic.rs @@ -0,0 +1,93 @@ +//! Platform Level Interrupt Control + +use kernel::common::registers::{register_bitfields, ReadWrite}; +use kernel::common::StaticRef; + +#[repr(C)] +struct PlicRegisters { + /// Interrupt Priority Register + _reserved0: u32, + priority: [ReadWrite; 51], + _reserved1: [u8; 3888], + /// Interrupt Pending Register + pending: [ReadWrite; 2], + _reserved2: [u8; 4088], + /// Interrupt Enable Register + enable: [ReadWrite; 2], + _reserved3: [u8; 2088952], + /// Priority Threshold Register + threshold: ReadWrite, + /// Claim/Complete Register + claim: ReadWrite, +} + +register_bitfields![u32, + priority [ + Priority OFFSET(0) NUMBITS(3) [] + ] +]; + +const PLIC_BASE: StaticRef = + unsafe { StaticRef::new(0x0c00_0000 as *const PlicRegisters) }; + +/// Clear all pending interrupts. +pub unsafe fn clear_all_pending() { + let plic: &PlicRegisters = &*PLIC_BASE; + for pending in plic.pending.iter() { + pending.set(0); + } +} + +/// Enable all interrupts. +pub unsafe fn enable_all() { + let plic: &PlicRegisters = &*PLIC_BASE; + for enable in plic.enable.iter() { + enable.set(0xFFFF_FFFF); + } + + // Set some default priority for each interrupt. This is not really used + // at this point. + for priority in plic.priority.iter() { + priority.write(priority::Priority.val(4)); + } + + // Accept all interrupts. + plic.threshold.write(priority::Priority.val(0)); +} + +/// Disable all interrupts. +pub unsafe fn disable_all() { + let plic: &PlicRegisters = &*PLIC_BASE; + for enable in plic.enable.iter() { + enable.set(0); + } +} + +/// Get the index (0-256) of the lowest number pending interrupt, or `None` if +/// none is pending. RISC-V PLIC has a "claim" register which makes it easy +/// to grab the highest priority pending interrupt. +pub unsafe fn next_pending() -> Option { + let plic: &PlicRegisters = &*PLIC_BASE; + + let claim = plic.claim.get(); + if claim == 0 { + None + } else { + Some(claim) + } +} + +/// Signal that an interrupt is finished being handled. In Tock, this should be +/// called from the normal main loop (not the interrupt handler). +pub unsafe fn complete(index: u32) { + let plic: &PlicRegisters = &*PLIC_BASE; + plic.claim.set(index); +} + +/// Return `true` if there are any pending interrupts in the PLIC, `false` +/// otherwise. +pub unsafe fn has_pending() -> bool { + let plic: &PlicRegisters = &*PLIC_BASE; + + plic.pending.iter().fold(0, |i, pending| pending.get() | i) != 0 +} diff --git a/arch/rv32i/src/support.rs b/arch/rv32i/src/support.rs new file mode 100644 index 0000000000..c8981abc54 --- /dev/null +++ b/arch/rv32i/src/support.rs @@ -0,0 +1,27 @@ +use core::ops::FnOnce; + +#[inline(always)] +/// NOP instruction +pub fn nop() { + unsafe { + asm!("nop" :::: "volatile"); + } +} + +#[inline(always)] +/// WFI instruction +pub unsafe fn wfi() { + asm!("wfi" :::: "volatile"); +} + +/// TODO: implement +pub unsafe fn atomic(f: F) -> R +where + F: FnOnce() -> R, +{ + f() +} + +#[cfg(target_os = "none")] +#[lang = "eh_personality"] +pub extern "C" fn eh_personality() {} diff --git a/boards/hifive1/Cargo.lock b/boards/hifive1/Cargo.lock new file mode 100644 index 0000000000..85403a949c --- /dev/null +++ b/boards/hifive1/Cargo.lock @@ -0,0 +1,65 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "capsules" +version = "0.1.0" +dependencies = [ + "enum_primitive 0.1.0", + "kernel 0.1.0", +] + +[[package]] +name = "e310x" +version = "0.1.0" +dependencies = [ + "kernel 0.1.0", + "rv32i 0.1.0", + "sifive 0.1.0", +] + +[[package]] +name = "enum_primitive" +version = "0.1.0" + +[[package]] +name = "hifive1" +version = "0.1.0" +dependencies = [ + "capsules 0.1.0", + "e310x 0.1.0", + "kernel 0.1.0", + "rv32i 0.1.0", + "sifive 0.1.0", +] + +[[package]] +name = "kernel" +version = "0.1.0" +dependencies = [ + "tock-cells 0.1.0", + "tock-registers 0.3.0", +] + +[[package]] +name = "rv32i" +version = "0.1.0" +dependencies = [ + "kernel 0.1.0", +] + +[[package]] +name = "sifive" +version = "0.1.0" +dependencies = [ + "kernel 0.1.0", + "rv32i 0.1.0", +] + +[[package]] +name = "tock-cells" +version = "0.1.0" + +[[package]] +name = "tock-registers" +version = "0.3.0" + diff --git a/boards/hifive1/Cargo.toml b/boards/hifive1/Cargo.toml new file mode 100644 index 0000000000..7f2a06d9e7 --- /dev/null +++ b/boards/hifive1/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "hifive1" +version = "0.1.0" +authors = ["Tock Project Developers "] +build = "build.rs" +edition = "2018" + +[profile.dev] +panic = "abort" +lto = false +opt-level = "z" +debug = true + +[profile.release] +panic = "abort" +lto = true +opt-level = "z" +debug = true + +[dependencies] +rv32i = { path = "../../arch/rv32i" } +capsules = { path = "../../capsules" } +kernel = { path = "../../kernel" } +e310x = { path = "../../chips/e310x" } +sifive = { path = "../../chips/sifive" } diff --git a/boards/hifive1/Makefile b/boards/hifive1/Makefile new file mode 100644 index 0000000000..bd942deb10 --- /dev/null +++ b/boards/hifive1/Makefile @@ -0,0 +1,41 @@ +# Makefile for building the tock kernel for the HiFive1 platform + +TARGET=riscv32imac-unknown-none-elf +PLATFORM=hifive1 + +include ../Makefile.common + +flash: target/$(TARGET)/release/$(PLATFORM).elf + openocd \ + -c "source [find board/sifive-hifive1.cfg]; flash protect 0 64 last off; program $<; resume 0x20400000; exit" + + +# TOCKLOADER=tockloader + +# # Where in the SAM4L flash to load the kernel with `tockloader` +# KERNEL_ADDRESS=0x10000 + +# # Upload programs over uart with tockloader +# ifdef PORT +# TOCKLOADER_GENERAL_FLAGS += --port $(PORT) +# endif + +# TOCKLOADER_JTAG_FLAGS = --jtag --board hail --arch cortex-m4 --jtag-device ATSAM4LC8C + +# .PHONY: program +# program: target/$(TARGET)/release/$(PLATFORM).bin +# $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) $< + +# # upload kernel over JTAG +# .PHONY: flash +# flash: target/$(TARGET)/release/$(PLATFORM).bin +# $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) $(TOCKLOADER_JTAG_FLAGS) $< + +# .PHONY: flash-debug +# flash-debug: target/$(TARGET)/debug/$(PLATFORM).bin +# $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) $(TOCKLOADER_JTAG_FLAGS) $< + +# # Command to flash the bootloader. Flashes the bootloader onto the SAM4L. +# .PHONY: flash-bootloader +# flash-bootloader: bootloader/bootloader.bin +# $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address 0 $(TOCKLOADER_JTAG_FLAGS) $< diff --git a/boards/hifive1/README.md b/boards/hifive1/README.md new file mode 100644 index 0000000000..2633105f1d --- /dev/null +++ b/boards/hifive1/README.md @@ -0,0 +1,12 @@ +SiFive HiFive1 RISC-V Board +================= + +- https://www.sifive.com/boards/hifive1 + +Arduino-compatible dev board for RISC-V. This is the first release of this +board ("Rev A01"). + +The RISC-V core on this board only supports Machine mode. That means that this +board does not support running userland applications. It is useful for testing +RISC-V kernel code, however. + diff --git a/boards/hifive1/build.rs b/boards/hifive1/build.rs new file mode 100644 index 0000000000..3051054fc6 --- /dev/null +++ b/boards/hifive1/build.rs @@ -0,0 +1,4 @@ +fn main() { + println!("cargo:rerun-if-changed=layout.ld"); + println!("cargo:rerun-if-changed=../kernel_layout.ld"); +} diff --git a/boards/hifive1/layout.ld b/boards/hifive1/layout.ld new file mode 100644 index 0000000000..9e4a180b2c --- /dev/null +++ b/boards/hifive1/layout.ld @@ -0,0 +1,10 @@ +MEMORY +{ + rom (rx) : ORIGIN = 0x20400000, LENGTH = 1M + prog (rx) : ORIGIN = 0x20500000, LENGTH = 507M + ram (rwx) : ORIGIN = 0x80000000, LENGTH = 16K +} + +MPU_MIN_ALIGN = 1K; + +INCLUDE ../kernel_layout.ld diff --git a/boards/hifive1/src/io.rs b/boards/hifive1/src/io.rs new file mode 100644 index 0000000000..14a2b0aa3d --- /dev/null +++ b/boards/hifive1/src/io.rs @@ -0,0 +1,40 @@ +use core::fmt::Write; +use core::panic::PanicInfo; +use core::str; +use e310x; +use kernel::debug; +use kernel::hil::gpio; +use kernel::hil::led; +use rv32i; + +use crate::PROCESSES; + +struct Writer {} + +static mut WRITER: Writer = Writer {}; + +impl Write for Writer { + fn write_str(&mut self, s: &str) -> ::core::fmt::Result { + debug!("{}", s); + Ok(()) + } +} + +/// Panic handler. +#[cfg(not(test))] +#[no_mangle] +#[panic_handler] +pub unsafe extern "C" fn panic_fmt(pi: &PanicInfo) -> ! { + // turn off the non panic leds, just in case + let led_green = &e310x::gpio::PORT[19]; + gpio::Pin::make_output(led_green); + gpio::Pin::set(led_green); + + let led_blue = &e310x::gpio::PORT[21]; + gpio::Pin::make_output(led_blue); + gpio::Pin::set(led_blue); + + let led_red = &mut led::LedLow::new(&mut e310x::gpio::PORT[22]); + let writer = &mut WRITER; + debug::panic(&mut [led_red], writer, pi, &rv32i::support::nop, &PROCESSES) +} diff --git a/boards/hifive1/src/main.rs b/boards/hifive1/src/main.rs new file mode 100644 index 0000000000..c987ba1737 --- /dev/null +++ b/boards/hifive1/src/main.rs @@ -0,0 +1,134 @@ +//! Board file for SiFive HiFive1 RISC-V development platform. +//! +//! - +//! +//! This board is no longer being produced. However, many were made so it may +//! be useful for testing Tock with. +//! +//! The primary drawback is the original HiFive1 board did not support User +//! mode, so this board cannot run Tock applications. + +#![no_std] +#![no_main] +#![feature(asm)] + +use capsules::virtual_uart::{MuxUart, UartDevice}; +use kernel::capabilities; +use kernel::hil; +use kernel::Platform; +use kernel::{create_capability, debug, static_init}; + +pub mod io; + +// Actual memory for holding the active process structures. Need an empty list +// at least. +static mut PROCESSES: [Option<&'static kernel::procs::ProcessType>; 0] = []; + +/// Dummy buffer that causes the linker to reserve enough space for the stack. +#[no_mangle] +#[link_section = ".stack_buffer"] +pub static mut STACK_MEMORY: [u8; 0x1000] = [0; 0x1000]; + +/// A structure representing this platform that holds references to all +/// capsules for this platform. However, since this board does not support +/// userspace this can just be empty. +struct HiFive1 {} + +/// Mapping of integer syscalls to objects that implement syscalls. +impl Platform for HiFive1 { + fn with_driver(&self, driver_num: usize, f: F) -> R + where + F: FnOnce(Option<&kernel::Driver>) -> R, + { + match driver_num { + _ => f(None), + } + } +} + +/// Reset Handler. +/// +/// This function is called from the arch crate after some very basic RISC-V +/// setup. +#[no_mangle] +pub unsafe fn reset_handler() { + // Basic setup of the platform. + rv32i::init_memory(); + rv32i::configure_trap_handler(); + + e310x::watchdog::WATCHDOG.disable(); + e310x::rtc::RTC.disable(); + e310x::pwm::PWM0.disable(); + e310x::pwm::PWM1.disable(); + e310x::pwm::PWM2.disable(); + + e310x::prci::PRCI.set_clock_frequency(sifive::prci::ClockFrequency::Freq18Mhz); + + let main_loop_cap = create_capability!(capabilities::MainLoopCapability); + + let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&PROCESSES)); + + // Configure kernel debug gpios as early as possible + kernel::debug::assign_gpios( + Some(&e310x::gpio::PORT[22]), // Red + None, + None, + ); + + let chip = static_init!(e310x::chip::E310x, e310x::chip::E310x::new()); + + // Need to enable all interrupts for Tock Kernel + chip.enable_plic_interrupts(); + + // Create a shared UART channel for the console and for kernel debug. + let uart_mux = static_init!( + MuxUart<'static>, + MuxUart::new( + &e310x::uart::UART0, + &mut capsules::virtual_uart::RX_BUF, + 115200 + ) + ); + + uart_mux.initialize(); + + hil::uart::Transmit::set_transmit_client(&e310x::uart::UART0, uart_mux); + hil::uart::Receive::set_receive_client(&e310x::uart::UART0, uart_mux); + + // Initialize some GPIOs which are useful for debugging. + hil::gpio::Pin::make_output(&e310x::gpio::PORT[22]); + hil::gpio::Pin::set(&e310x::gpio::PORT[22]); + + hil::gpio::Pin::make_output(&e310x::gpio::PORT[19]); + hil::gpio::Pin::set(&e310x::gpio::PORT[19]); + + hil::gpio::Pin::make_output(&e310x::gpio::PORT[21]); + hil::gpio::Pin::clear(&e310x::gpio::PORT[21]); + + let hifive1 = HiFive1 {}; + + // Create virtual device for kernel debug. + let debugger_uart = static_init!(UartDevice, UartDevice::new(uart_mux, false)); + debugger_uart.setup(); + let debugger = static_init!( + kernel::debug::DebugWriter, + kernel::debug::DebugWriter::new( + debugger_uart, + &mut kernel::debug::OUTPUT_BUF, + &mut kernel::debug::INTERNAL_BUF, + ) + ); + hil::uart::Transmit::set_transmit_client(debugger_uart, debugger); + + let debug_wrapper = static_init!( + kernel::debug::DebugWriterWrapper, + kernel::debug::DebugWriterWrapper::new(debugger) + ); + kernel::debug::set_debug_writer_wrapper(debug_wrapper); + + e310x::uart::UART0.initialize_gpio_pins(&e310x::gpio::PORT[17], &e310x::gpio::PORT[16]); + + debug!("HiFive1 initialization complete. Entering main loop"); + + board_kernel.kernel_loop(&hifive1, chip, None, &main_loop_cap); +} diff --git a/boards/kernel_layout.ld b/boards/kernel_layout.ld index ed81dffae8..f733258a6d 100644 --- a/boards/kernel_layout.ld +++ b/boards/kernel_layout.ld @@ -75,6 +75,18 @@ SECTIONS KEEP(*(.vectors .vectors.*)) KEEP(*(.irqs)) + /* RISC-V + * There is no vector table in RISCV, so .vectors and .irqs will be + * empty. Instead, the _start function needs to be first in the binary + * for it to correctly be executed. We also need to include the trap + * handler assembly function. + * + * These are expected to just be empty on other platforms so they + * shouldn't have any effect. + */ + KEEP(*(.riscv.start)); + KEEP(*(.riscv.trap)); + /* .text and .rodata hold most program code and immutable constants */ /* .gnu.linkonce hold C++ elements with vague linkage https://gcc.gnu.org/onlinedocs/gcc/Vague-Linkage.html */ @@ -197,7 +209,7 @@ SECTIONS /* Required for objcopy mechanism to bundle tock kernel and apps into a single image to work */ - LONG(0) + LONG(0) } > prog @@ -218,6 +230,25 @@ SECTIONS { . = ALIGN(4); _srelocate = .; + + /* The Global Pointer is used by the RISC-V architecture to provide + * "gp-relative" addressing. The global pointer is set to the gp + * register once on boot, and the linker can then take advantage of this + * when emitting instructions by using offsets relative to this known + * value. Since RISC-V has only 12 bit immediates, this can help reduce + * code size. + * + * The standard is to set the global pointer to 0x800 past the beginning + * of the data section in RAM. This allows instructions to use 12 bit + * immediates to access the first 4KB of data memory. In theory the GP + * can be set to any value, but it should be placed near actual data for + * the compiler to actually be able to use it. + * + * Per convention, the variable _must_ be called __global_pointer$ for + * the linker to actually take advantage of it. + */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.ramfunc .ramfunc.*); *(.data .data.*); @@ -261,6 +292,13 @@ SECTIONS . = ALIGN(MPU_MIN_ALIGN); *(.app_memory) } > ram + + /* Discard RISC-V relevant .eh_frame, we are not doing unwind on panic + so it is not needed. */ + /DISCARD/ : + { + *(.eh_frame); + } } ASSERT((_etext-_stext) + (_erelocate-_srelocate) < LENGTH(rom), " diff --git a/chips/e310x/Cargo.lock b/chips/e310x/Cargo.lock new file mode 100644 index 0000000000..a1a15a9355 --- /dev/null +++ b/chips/e310x/Cargo.lock @@ -0,0 +1,42 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "e310x" +version = "0.1.0" +dependencies = [ + "kernel 0.1.0", + "rv32i 0.1.0", + "sifive 0.1.0", +] + +[[package]] +name = "kernel" +version = "0.1.0" +dependencies = [ + "tock-cells 0.1.0", + "tock-registers 0.3.0", +] + +[[package]] +name = "rv32i" +version = "0.1.0" +dependencies = [ + "kernel 0.1.0", +] + +[[package]] +name = "sifive" +version = "0.1.0" +dependencies = [ + "kernel 0.1.0", + "rv32i 0.1.0", +] + +[[package]] +name = "tock-cells" +version = "0.1.0" + +[[package]] +name = "tock-registers" +version = "0.3.0" + diff --git a/chips/e310x/Cargo.toml b/chips/e310x/Cargo.toml new file mode 100644 index 0000000000..3736f8572f --- /dev/null +++ b/chips/e310x/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "e310x" +version = "0.1.0" +authors = ["Tock Project Developers "] +edition = "2018" + +[dependencies] +sifive = { path = "../sifive" } +rv32i = { path = "../../arch/rv32i" } +kernel = { path = "../../kernel" } + diff --git a/chips/e310x/README.md b/chips/e310x/README.md new file mode 100644 index 0000000000..16cae98165 --- /dev/null +++ b/chips/e310x/README.md @@ -0,0 +1,6 @@ +HiFive Freedom E310 MCU +======================= + +- https://www.sifive.com/chip-designer#fe310 + +The E310 is a core developed by SiFive based on the RISC-V architecture. diff --git a/chips/e310x/src/chip.rs b/chips/e310x/src/chip.rs new file mode 100644 index 0000000000..f477931c99 --- /dev/null +++ b/chips/e310x/src/chip.rs @@ -0,0 +1,143 @@ +use core::fmt::Write; + +use kernel; +use kernel::debug; +use rv32i; +use rv32i::plic; + +use crate::gpio; +use crate::interrupts; +use crate::uart; + +#[derive(Copy, Clone, Default)] +pub struct RvStoredState {} + +pub struct NullSysCall(); + +impl NullSysCall { + pub const unsafe fn new() -> NullSysCall { + NullSysCall() + } +} + +impl kernel::syscall::UserspaceKernelBoundary for NullSysCall { + type StoredState = RvStoredState; + + unsafe fn get_syscall(&self, _stack_pointer: *const usize) -> Option { + None + } + + unsafe fn set_syscall_return_value(&self, _stack_pointer: *const usize, _return_value: isize) {} + + unsafe fn pop_syscall_stack_frame( + &self, + stack_pointer: *const usize, + _state: &mut RvStoredState, + ) -> *mut usize { + stack_pointer as *mut usize + } + + unsafe fn push_function_call( + &self, + stack_pointer: *const usize, + _remaining_stack_memory: usize, + _callback: kernel::procs::FunctionCall, + _state: &RvStoredState, + ) -> Result<*mut usize, *mut usize> { + Err(stack_pointer as *mut usize) + } + + unsafe fn switch_to_process( + &self, + stack_pointer: *const usize, + _state: &mut RvStoredState, + ) -> (*mut usize, kernel::syscall::ContextSwitchReason) { + ( + stack_pointer as *mut usize, + kernel::syscall::ContextSwitchReason::Fault, + ) + } + + unsafe fn fault_fmt(&self, _writer: &mut Write) {} + + unsafe fn process_detail_fmt( + &self, + _stack_pointer: *const usize, + _state: &RvStoredState, + _writer: &mut Write, + ) { + } +} + +pub struct E310x { + userspace_kernel_boundary: NullSysCall, +} + +impl E310x { + pub unsafe fn new() -> E310x { + E310x { + userspace_kernel_boundary: NullSysCall::new(), + } + } + + pub unsafe fn enable_plic_interrupts(&self) { + rv32i::plic::disable_all(); + rv32i::plic::clear_all_pending(); + rv32i::plic::enable_all(); + } +} + +impl kernel::Chip for E310x { + type MPU = (); + type UserspaceKernelBoundary = NullSysCall; + type SysTick = (); + + fn mpu(&self) -> &Self::MPU { + &() + } + + fn systick(&self) -> &Self::SysTick { + &() + } + + fn userspace_kernel_boundary(&self) -> &NullSysCall { + &(self.userspace_kernel_boundary) + } + + fn service_pending_interrupts(&self) { + unsafe { + while let Some(interrupt) = plic::next_pending() { + match interrupt { + interrupts::UART0 => uart::UART0.handle_interrupt(), + index @ interrupts::GPIO0..interrupts::GPIO31 => { + gpio::PORT[index as usize].handle_interrupt() + } + // _ => debug!("PLIC index not supported by Tock {}", interrupt), + _ => debug!("Pidx {}", interrupt), + } + + // Mark that we are done with this interrupt and the hardware + // can clear it. + plic::complete(interrupt); + } + } + } + + fn has_pending_interrupts(&self) -> bool { + unsafe { plic::has_pending() } + } + + fn sleep(&self) { + // unsafe { + // riscv32i::support::wfi(); + rv32i::support::nop(); + // } + } + + unsafe fn atomic(&self, f: F) -> R + where + F: FnOnce() -> R, + { + rv32i::support::atomic(f) + } +} diff --git a/chips/e310x/src/gpio.rs b/chips/e310x/src/gpio.rs new file mode 100644 index 0000000000..3499cdc1bb --- /dev/null +++ b/chips/e310x/src/gpio.rs @@ -0,0 +1,172 @@ +use core::ops::{Index, IndexMut}; + +use kernel::common::StaticRef; +use sifive::gpio::{pins, GpioPin, GpioRegisters}; + +const GPIO0_BASE: StaticRef = + unsafe { StaticRef::new(0x1001_2000 as *const GpioRegisters) }; + +pub struct Port { + pins: [GpioPin; 32], +} + +impl Index for Port { + type Output = GpioPin; + + fn index(&self, index: usize) -> &GpioPin { + &self.pins[index] + } +} + +impl IndexMut for Port { + fn index_mut(&mut self, index: usize) -> &mut GpioPin { + &mut self.pins[index] + } +} + +pub static mut PORT: Port = Port { + pins: [ + GpioPin::new(GPIO0_BASE, pins::pin0, pins::pin0::SET, pins::pin0::CLEAR), + GpioPin::new(GPIO0_BASE, pins::pin1, pins::pin1::SET, pins::pin1::CLEAR), + GpioPin::new(GPIO0_BASE, pins::pin2, pins::pin2::SET, pins::pin2::CLEAR), + GpioPin::new(GPIO0_BASE, pins::pin3, pins::pin3::SET, pins::pin3::CLEAR), + GpioPin::new(GPIO0_BASE, pins::pin4, pins::pin4::SET, pins::pin4::CLEAR), + GpioPin::new(GPIO0_BASE, pins::pin5, pins::pin5::SET, pins::pin5::CLEAR), + GpioPin::new(GPIO0_BASE, pins::pin6, pins::pin6::SET, pins::pin6::CLEAR), + GpioPin::new(GPIO0_BASE, pins::pin7, pins::pin7::SET, pins::pin7::CLEAR), + GpioPin::new(GPIO0_BASE, pins::pin8, pins::pin8::SET, pins::pin8::CLEAR), + GpioPin::new(GPIO0_BASE, pins::pin9, pins::pin9::SET, pins::pin9::CLEAR), + GpioPin::new( + GPIO0_BASE, + pins::pin10, + pins::pin10::SET, + pins::pin10::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin11, + pins::pin11::SET, + pins::pin11::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin12, + pins::pin12::SET, + pins::pin12::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin13, + pins::pin13::SET, + pins::pin13::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin14, + pins::pin14::SET, + pins::pin14::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin15, + pins::pin15::SET, + pins::pin15::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin16, + pins::pin16::SET, + pins::pin16::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin17, + pins::pin17::SET, + pins::pin17::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin18, + pins::pin18::SET, + pins::pin18::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin19, + pins::pin19::SET, + pins::pin19::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin20, + pins::pin20::SET, + pins::pin20::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin21, + pins::pin21::SET, + pins::pin21::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin22, + pins::pin22::SET, + pins::pin22::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin23, + pins::pin23::SET, + pins::pin23::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin24, + pins::pin24::SET, + pins::pin24::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin25, + pins::pin25::SET, + pins::pin25::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin26, + pins::pin26::SET, + pins::pin26::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin27, + pins::pin27::SET, + pins::pin27::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin28, + pins::pin28::SET, + pins::pin28::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin29, + pins::pin29::SET, + pins::pin29::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin30, + pins::pin30::SET, + pins::pin30::CLEAR, + ), + GpioPin::new( + GPIO0_BASE, + pins::pin31, + pins::pin31::SET, + pins::pin31::CLEAR, + ), + ], +}; diff --git a/chips/e310x/src/interrupts.rs b/chips/e310x/src/interrupts.rs new file mode 100644 index 0000000000..4d4965a92c --- /dev/null +++ b/chips/e310x/src/interrupts.rs @@ -0,0 +1,55 @@ +//! Named interrupts for the E310X chip. + +#![allow(dead_code)] + +pub const WATCHDOG: u32 = 1; +pub const RTC: u32 = 2; +pub const UART0: u32 = 3; +pub const UART1: u32 = 4; +pub const QSPI0: u32 = 5; +pub const QSPI1: u32 = 6; +pub const QSPI2: u32 = 7; +pub const GPIO0: u32 = 8; +pub const GPIO1: u32 = 9; +pub const GPIO2: u32 = 10; +pub const GPIO3: u32 = 11; +pub const GPIO4: u32 = 12; +pub const GPIO5: u32 = 13; +pub const GPIO6: u32 = 14; +pub const GPIO7: u32 = 15; +pub const GPIO8: u32 = 16; +pub const GPIO9: u32 = 17; +pub const GPIO10: u32 = 18; +pub const GPIO11: u32 = 19; +pub const GPIO12: u32 = 20; +pub const GPIO13: u32 = 21; +pub const GPIO14: u32 = 22; +pub const GPIO15: u32 = 23; +pub const GPIO16: u32 = 24; +pub const GPIO17: u32 = 25; +pub const GPIO18: u32 = 26; +pub const GPIO19: u32 = 27; +pub const GPIO20: u32 = 28; +pub const GPIO21: u32 = 29; +pub const GPIO22: u32 = 30; +pub const GPIO23: u32 = 31; +pub const GPIO24: u32 = 32; +pub const GPIO25: u32 = 33; +pub const GPIO26: u32 = 34; +pub const GPIO27: u32 = 35; +pub const GPIO28: u32 = 36; +pub const GPIO29: u32 = 37; +pub const GPIO30: u32 = 38; +pub const GPIO31: u32 = 39; +pub const PWM0CMP0: u32 = 40; +pub const PWM0CMP1: u32 = 41; +pub const PWM0CMP2: u32 = 42; +pub const PWM0CMP3: u32 = 43; +pub const PWM1CMP0: u32 = 44; +pub const PWM1CMP1: u32 = 45; +pub const PWM1CMP2: u32 = 46; +pub const PWM1CMP3: u32 = 47; +pub const PWM2CMP0: u32 = 48; +pub const PWM2CMP1: u32 = 49; +pub const PWM2CMP2: u32 = 50; +pub const PWM2CMP3: u32 = 51; diff --git a/chips/e310x/src/lib.rs b/chips/e310x/src/lib.rs new file mode 100644 index 0000000000..a6473fa377 --- /dev/null +++ b/chips/e310x/src/lib.rs @@ -0,0 +1,15 @@ +#![feature(asm, concat_idents, const_fn)] +#![feature(exclusive_range_pattern)] +#![no_std] +#![crate_name = "e310x"] +#![crate_type = "rlib"] + +mod interrupts; + +pub mod chip; +pub mod gpio; +pub mod prci; +pub mod pwm; +pub mod rtc; +pub mod uart; +pub mod watchdog; diff --git a/chips/e310x/src/prci.rs b/chips/e310x/src/prci.rs new file mode 100644 index 0000000000..a7d38ae30f --- /dev/null +++ b/chips/e310x/src/prci.rs @@ -0,0 +1,9 @@ +//! Power Reset Clock Interrupts + +use kernel::common::StaticRef; +use sifive::prci::{Prci, PrciRegisters}; + +pub static mut PRCI: Prci = Prci::new(PRCI_BASE); + +const PRCI_BASE: StaticRef = + unsafe { StaticRef::new(0x1000_8000 as *const PrciRegisters) }; diff --git a/chips/e310x/src/pwm.rs b/chips/e310x/src/pwm.rs new file mode 100644 index 0000000000..48be5a4f67 --- /dev/null +++ b/chips/e310x/src/pwm.rs @@ -0,0 +1,13 @@ +use kernel::common::StaticRef; +use sifive::pwm::{Pwm, PwmRegisters}; + +pub static mut PWM0: Pwm = Pwm::new(PWM0_BASE); +pub static mut PWM1: Pwm = Pwm::new(PWM1_BASE); +pub static mut PWM2: Pwm = Pwm::new(PWM2_BASE); + +const PWM0_BASE: StaticRef = + unsafe { StaticRef::new(0x10015000 as *const PwmRegisters) }; +const PWM1_BASE: StaticRef = + unsafe { StaticRef::new(0x10025000 as *const PwmRegisters) }; +const PWM2_BASE: StaticRef = + unsafe { StaticRef::new(0x10035000 as *const PwmRegisters) }; diff --git a/chips/e310x/src/rtc.rs b/chips/e310x/src/rtc.rs new file mode 100644 index 0000000000..283554a5e6 --- /dev/null +++ b/chips/e310x/src/rtc.rs @@ -0,0 +1,7 @@ +use kernel::common::StaticRef; +use sifive::rtc::{Rtc, RtcRegisters}; + +pub static mut RTC: Rtc = Rtc::new(RTC_BASE); + +const RTC_BASE: StaticRef = + unsafe { StaticRef::new(0x1000_0040 as *const RtcRegisters) }; diff --git a/chips/e310x/src/uart.rs b/chips/e310x/src/uart.rs new file mode 100644 index 0000000000..83dcff96bd --- /dev/null +++ b/chips/e310x/src/uart.rs @@ -0,0 +1,7 @@ +use kernel::common::StaticRef; +use sifive::uart::{Uart, UartRegisters}; + +pub static mut UART0: Uart = Uart::new(UART0_BASE); + +const UART0_BASE: StaticRef = + unsafe { StaticRef::new(0x1001_3000 as *const UartRegisters) }; diff --git a/chips/e310x/src/watchdog.rs b/chips/e310x/src/watchdog.rs new file mode 100644 index 0000000000..2c2c0e3e90 --- /dev/null +++ b/chips/e310x/src/watchdog.rs @@ -0,0 +1,10 @@ +//! Watchdog + +use kernel::common::StaticRef; + +use sifive::watchdog::{Watchdog, WatchdogRegisters}; + +pub static mut WATCHDOG: Watchdog = Watchdog::new(WATCHDOG_BASE); + +const WATCHDOG_BASE: StaticRef = + unsafe { StaticRef::new(0x1000_0000 as *const WatchdogRegisters) }; diff --git a/chips/sifive/Cargo.lock b/chips/sifive/Cargo.lock new file mode 100644 index 0000000000..08a268e629 --- /dev/null +++ b/chips/sifive/Cargo.lock @@ -0,0 +1,33 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "kernel" +version = "0.1.0" +dependencies = [ + "tock-cells 0.1.0", + "tock-registers 0.3.0", +] + +[[package]] +name = "rv32i" +version = "0.1.0" +dependencies = [ + "kernel 0.1.0", +] + +[[package]] +name = "sifive" +version = "0.1.0" +dependencies = [ + "kernel 0.1.0", + "rv32i 0.1.0", +] + +[[package]] +name = "tock-cells" +version = "0.1.0" + +[[package]] +name = "tock-registers" +version = "0.3.0" + diff --git a/chips/sifive/Cargo.toml b/chips/sifive/Cargo.toml new file mode 100644 index 0000000000..d763c437c2 --- /dev/null +++ b/chips/sifive/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "sifive" +version = "0.1.0" +authors = ["Tock Project Developers "] +edition = "2018" + +[dependencies] +rv32i = { path = "../../arch/rv32i" } +kernel = { path = "../../kernel" } + diff --git a/chips/sifive/README.md b/chips/sifive/README.md new file mode 100644 index 0000000000..ef39b774e8 --- /dev/null +++ b/chips/sifive/README.md @@ -0,0 +1,6 @@ +SiFive Peripherals +======================= + +This crate contains various peripherals shared between various SiFive +cores. + diff --git a/chips/sifive/src/gpio.rs b/chips/sifive/src/gpio.rs new file mode 100644 index 0000000000..75cc4dc7d4 --- /dev/null +++ b/chips/sifive/src/gpio.rs @@ -0,0 +1,249 @@ +use core::cell::Cell; + +use kernel::common::cells::OptionalCell; +use kernel::common::registers::{register_bitfields, Field, FieldValue, ReadOnly, ReadWrite}; +use kernel::common::StaticRef; +use kernel::hil; + +#[repr(C)] +pub struct GpioRegisters { + /// Pin value. + value: ReadOnly, + /// Pin Input Enable Register + input_en: ReadWrite, + /// Pin Output Enable Register + output_en: ReadWrite, + /// Output Port Value Register + port: ReadWrite, + /// Internal Pull-Up Enable Register + pullup: ReadWrite, + /// Drive Strength Register + drive: ReadWrite, + /// Rise Interrupt Enable Register + rise_ie: ReadWrite, + /// Rise Interrupt Pending Register + rise_ip: ReadWrite, + /// Fall Interrupt Enable Register + fall_ie: ReadWrite, + /// Fall Interrupt Pending Register + fall_ip: ReadWrite, + /// High Interrupt Enable Register + high_ie: ReadWrite, + /// High Interrupt Pending Register + high_ip: ReadWrite, + /// Low Interrupt Enable Register + low_ie: ReadWrite, + /// Low Interrupt Pending Register + low_ip: ReadWrite, + /// HW I/O Function Enable Register + iof_en: ReadWrite, + /// HW I/O Function Select Register + iof_sel: ReadWrite, + /// Output XOR (invert) Register + out_xor: ReadWrite, +} + +register_bitfields![u32, + pins [ + pin0 0, + pin1 1, + pin2 2, + pin3 3, + pin4 4, + pin5 5, + pin6 6, + pin7 7, + pin8 8, + pin9 9, + pin10 10, + pin11 11, + pin12 12, + pin13 13, + pin14 14, + pin15 15, + pin16 16, + pin17 17, + pin18 18, + pin19 19, + pin20 20, + pin21 21, + pin22 22, + pin23 23, + pin24 24, + pin25 25, + pin26 26, + pin27 27, + pin28 28, + pin29 29, + pin30 30, + pin31 31 + ] +]; + +pub struct GpioPin { + registers: StaticRef, + pin: Field, + set: FieldValue, + clear: FieldValue, + client_data: Cell, + client: OptionalCell<&'static hil::gpio::Client>, +} + +impl GpioPin { + pub const fn new( + base: StaticRef, + pin: Field, + set: FieldValue, + clear: FieldValue, + ) -> GpioPin { + GpioPin { + registers: base, + pin: pin, + set: set, + clear: clear, + client_data: Cell::new(0), + client: OptionalCell::empty(), + } + } + + pub fn set_client(&self, client: &'static C) { + self.client.set(client); + } + + /// Configure this pin as IO Function 0. What that maps to is chip- and pin- + /// specific. + pub fn iof0(&self) { + let regs = self.registers; + + regs.out_xor.modify(self.clear); + regs.iof_sel.modify(self.clear); + regs.iof_en.modify(self.set); + } + + /// Configure this pin as IO Function 1. What that maps to is chip- and pin- + /// specific. + pub fn iof1(&self) { + let regs = self.registers; + + regs.out_xor.modify(self.clear); + regs.iof_sel.modify(self.set); + regs.iof_en.modify(self.set); + } + + /// There are separate interrupts in PLIC for each pin, so the interrupt + /// handler only needs to exist on each pin. + pub fn handle_interrupt(&self) { + let regs = self.registers; + + // Clear the pending GPIO interrupt. + regs.rise_ip.modify(self.set); + regs.fall_ip.modify(self.set); + regs.high_ip.modify(self.set); + regs.low_ip.modify(self.set); + + self.client.map(|client| { + client.fired(self.client_data.get()); + }); + } +} + +impl hil::gpio::PinCtl for GpioPin { + fn set_input_mode(&self, mode: hil::gpio::InputMode) { + let regs = self.registers; + + match mode { + hil::gpio::InputMode::PullUp => { + regs.pullup.modify(self.set); + } + hil::gpio::InputMode::PullDown => { + regs.pullup.modify(self.clear); + } + hil::gpio::InputMode::PullNone => { + regs.pullup.modify(self.clear); + } + } + } +} + +impl hil::gpio::Pin for GpioPin { + fn disable(&self) { + // NOP. There does not seem to be a way to "disable" a GPIO pin on this + // chip. + } + + fn make_output(&self) { + let regs = self.registers; + + regs.drive.modify(self.clear); + regs.out_xor.modify(self.clear); + regs.output_en.modify(self.set); + regs.iof_en.modify(self.clear); + } + + fn make_input(&self) { + let regs = self.registers; + + regs.pullup.modify(self.clear); + regs.input_en.modify(self.set); + regs.iof_en.modify(self.clear); + } + + fn read(&self) -> bool { + let regs = self.registers; + + regs.value.is_set(self.pin) + } + + fn toggle(&self) { + let regs = self.registers; + + let current_outputs = regs.port.extract(); + if current_outputs.is_set(self.pin) { + regs.port.modify_no_read(current_outputs, self.clear); + } else { + regs.port.modify_no_read(current_outputs, self.set); + } + } + + fn set(&self) { + let regs = self.registers; + + regs.port.modify(self.set); + } + + fn clear(&self) { + let regs = self.registers; + + regs.port.modify(self.clear); + } + + fn enable_interrupt(&self, client_data: usize, mode: hil::gpio::InterruptMode) { + let regs = self.registers; + + regs.pullup.modify(self.clear); + regs.input_en.modify(self.set); + regs.iof_en.modify(self.clear); + + self.client_data.set(client_data); + + match mode { + hil::gpio::InterruptMode::RisingEdge => { + regs.rise_ie.modify(self.set); + } + hil::gpio::InterruptMode::FallingEdge => { + regs.fall_ie.modify(self.set); + } + hil::gpio::InterruptMode::EitherEdge => { + regs.rise_ie.modify(self.set); + regs.fall_ie.modify(self.set); + } + } + } + + fn disable_interrupt(&self) { + let regs = self.registers; + + regs.rise_ie.modify(self.clear); + regs.fall_ie.modify(self.clear); + } +} diff --git a/chips/sifive/src/lib.rs b/chips/sifive/src/lib.rs new file mode 100644 index 0000000000..93a1c88a1f --- /dev/null +++ b/chips/sifive/src/lib.rs @@ -0,0 +1,13 @@ +#![feature(asm, concat_idents, const_fn, core_intrinsics)] +#![feature(in_band_lifetimes)] +#![feature(exclusive_range_pattern)] +#![no_std] +#![crate_name = "sifive"] +#![crate_type = "rlib"] + +pub mod gpio; +pub mod prci; +pub mod pwm; +pub mod rtc; +pub mod uart; +pub mod watchdog; diff --git a/chips/sifive/src/prci.rs b/chips/sifive/src/prci.rs new file mode 100644 index 0000000000..a0ca45874c --- /dev/null +++ b/chips/sifive/src/prci.rs @@ -0,0 +1,76 @@ +//! Power Reset Clock Interrupts + +use kernel::common::registers::{register_bitfields, ReadWrite}; +use kernel::common::StaticRef; + +#[repr(C)] +pub struct PrciRegisters { + /// Clock Configuration Register + hfrosccfg: ReadWrite, + /// Clock Configuration Register + hfxosccfg: ReadWrite, + /// PLL Configuration Register + pllcfg: ReadWrite, + /// PLL Divider Register + plloutdiv: ReadWrite, + /// Clock Configuration Register + coreclkcfg: ReadWrite, +} + +register_bitfields![u32, + hfrosccfg [ + ready OFFSET(31) NUMBITS(1) [], + enable OFFSET(30) NUMBITS(1) [], + trim OFFSET(16) NUMBITS(5) [], + div OFFSET(0) NUMBITS(6) [] + ], + hfxosccfg [ + ready OFFSET(31) NUMBITS(1) [], + enable OFFSET(30) NUMBITS(1) [] + ], + pllcfg [ + lock OFFSET(31) NUMBITS(1) [], + bypass OFFSET(18) NUMBITS(1) [], + refsel OFFSET(17) NUMBITS(1) [], + sel OFFSET(16) NUMBITS(1) [], + pllq OFFSET(10) NUMBITS(2) [], + pllf OFFSET(4) NUMBITS(6) [], + pllr OFFSET(0) NUMBITS(3) [ + R1 = 0 + ] + ], + plloutdiv [ + divby1 OFFSET(8) NUMBITS(1) [], + div OFFSET(0) NUMBITS(6) [] + ] +]; + +pub enum ClockFrequency { + Freq18Mhz, + Freq384Mhz, +} + +pub struct Prci { + registers: StaticRef, +} + +impl Prci { + pub const fn new(base: StaticRef) -> Prci { + Prci { registers: base } + } + + pub fn set_clock_frequency(&self, frequency: ClockFrequency) { + let regs = self.registers; + + // debug!("reg {:#x}", regs.hfrosccfg.get()); + + // Assume a 72 MHz clock, then `div` is (72/frequency) - 1. + match frequency { + ClockFrequency::Freq18Mhz => { + // 4, // this seems wrong, but it works?? + regs.hfrosccfg.modify(hfrosccfg::div.val(4)); + } + ClockFrequency::Freq384Mhz => {} + }; + } +} diff --git a/chips/sifive/src/pwm.rs b/chips/sifive/src/pwm.rs new file mode 100644 index 0000000000..54fdb68c91 --- /dev/null +++ b/chips/sifive/src/pwm.rs @@ -0,0 +1,65 @@ +//! Pulse Width Modulation (PWM) Driver + +use kernel::common::registers::{register_bitfields, ReadWrite}; +use kernel::common::StaticRef; + +#[repr(C)] +pub struct PwmRegisters { + /// PWM Configuration Register + cfg: ReadWrite, + /// Counter Register + count: ReadWrite, + _reserved0: [u8; 8], + /// Scaled Halfword Counter Register + pwms: ReadWrite, + _reserved1: [u8; 12], + /// Compare Register + cmp0: ReadWrite, + /// Compare Register + cmp1: ReadWrite, + /// Compare Register + cmp2: ReadWrite, + /// Compare Register + cmp3: ReadWrite, +} + +register_bitfields![u32, + cfg [ + cmp3ip OFFSET(31) NUMBITS(1) [], + cmp2ip OFFSET(30) NUMBITS(1) [], + cmp1ip OFFSET(29) NUMBITS(1) [], + cmp0ip OFFSET(28) NUMBITS(1) [], + cmp3gang OFFSET(27) NUMBITS(1) [], + cmp2gang OFFSET(26) NUMBITS(11) [], + cmp1gang OFFSET(25) NUMBITS(1) [], + cmp0gang OFFSET(24) NUMBITS(1) [], + cmp3center OFFSET(19) NUMBITS(1) [], + cmp2center OFFSET(18) NUMBITS(1) [], + cmp1center OFFSET(17) NUMBITS(1) [], + cmp0center OFFSET(16) NUMBITS(1) [], + enoneshot OFFSET(13) NUMBITS(1) [], + enalways OFFSET(12) NUMBITS(1) [], + deglitch OFFSET(10) NUMBITS(1) [], + zerocmp OFFSET(9) NUMBITS(1) [], + sticky OFFSET(8) NUMBITS(1) [], + scale OFFSET(0) NUMBITS(4) [] + ] +]; + +pub struct Pwm { + registers: StaticRef, +} + +impl Pwm { + pub const fn new(base: StaticRef) -> Pwm { + Pwm { registers: base } + } + + /// Disable the PWM so it does not generate interrupts. + pub fn disable(&self) { + let regs = self.registers; + + // Turn the interrupt compare off so we don't get any RTC interrupts. + regs.cfg.write(cfg::enalways::CLEAR + cfg::enalways::CLEAR); + } +} diff --git a/chips/sifive/src/rtc.rs b/chips/sifive/src/rtc.rs new file mode 100644 index 0000000000..74dc9c05c1 --- /dev/null +++ b/chips/sifive/src/rtc.rs @@ -0,0 +1,55 @@ +//! Real Time Clock (RTC) Driver + +use kernel::common::registers::{register_bitfields, ReadWrite}; +use kernel::common::StaticRef; + +#[repr(C)] +pub struct RtcRegisters { + /// RTC Configuration Register + rtccfg: ReadWrite, + _reserved1: [u8; 4], + /// RTC Counter Low Register + rtclo: ReadWrite, + /// RTC Counter High Register + rtchi: ReadWrite, + /// RTC Scaled Counter Register + rtcs: ReadWrite, + _reserved2: [u8; 12], + /// RTC Compare Register + rtccmp: ReadWrite, +} + +register_bitfields![u32, + rtccfg [ + cmpip OFFSET(28) NUMBITS(1) [], + enalways OFFSET(12) NUMBITS(1) [], + scale OFFSET(0) NUMBITS(4) [] + ], + rtclo [ + rtclo OFFSET(0) NUMBITS(32) [] + ], + rtchi [ + rtchi OFFSET(0) NUMBITS(16) [] + ], + rtccmp [ + rtccmp OFFSET(0) NUMBITS(32) [] + ] +]; + +pub struct Rtc { + registers: StaticRef, +} + +impl Rtc { + pub const fn new(base: StaticRef) -> Rtc { + Rtc { registers: base } + } + + /// Disable the RTC so it does not generate interrupts. + pub fn disable(&self) { + let regs = self.registers; + + // Turn the interrupt compare off so we don't get any RTC interrupts. + regs.rtccfg.write(rtccfg::enalways::CLEAR); + } +} diff --git a/chips/sifive/src/uart.rs b/chips/sifive/src/uart.rs new file mode 100644 index 0000000000..dcbc3a42a0 --- /dev/null +++ b/chips/sifive/src/uart.rs @@ -0,0 +1,259 @@ +use core::cell::Cell; + +use crate::gpio; +use kernel::common::cells::OptionalCell; +use kernel::common::cells::TakeCell; +use kernel::common::registers::{register_bitfields, ReadOnly, ReadWrite}; +use kernel::common::StaticRef; +use kernel::hil; +use kernel::ReturnCode; + +#[repr(C)] +pub struct UartRegisters { + /// Transmit Data Register + txdata: ReadWrite, + /// Receive Data Register + rxdata: ReadWrite, + /// Transmit Control Register + txctrl: ReadWrite, + /// Receive Control Register + rxctrl: ReadWrite, + /// Interrupt Enable Register + ie: ReadWrite, + /// Interrupt Pending Register + ip: ReadOnly, + /// Baud Rate Divisor Register + div: ReadWrite, +} + +register_bitfields![u32, + txdata [ + full OFFSET(31) NUMBITS(1) [], + data OFFSET(0) NUMBITS(8) [] + ], + rxdata [ + empty OFFSET(31) NUMBITS(1) [], + data OFFSET(0) NUMBITS(8) [] + ], + txctrl [ + txcnt OFFSET(16) NUMBITS(3) [], + nstop OFFSET(1) NUMBITS(1) [ + OneStopBit = 0, + TwoStopBits = 1 + ], + txen OFFSET(0) NUMBITS(1) [] + ], + rxctrl [ + counter OFFSET(16) NUMBITS(3) [], + enable OFFSET(0) NUMBITS(1) [] + ], + interrupt [ + rxwm OFFSET(1) NUMBITS(1) [], + txwm OFFSET(0) NUMBITS(1) [] + ], + div [ + div OFFSET(0) NUMBITS(16) [] + ] +]; + +pub struct Uart<'a> { + registers: StaticRef, + tx_client: OptionalCell<&'a hil::uart::TransmitClient>, + rx_client: OptionalCell<&'a hil::uart::ReceiveClient>, + stop_bits: Cell, + buffer: TakeCell<'static, [u8]>, + len: Cell, + index: Cell, +} + +#[derive(Copy, Clone)] +pub struct UartParams { + pub baud_rate: u32, +} + +impl Uart<'a> { + pub const fn new(base: StaticRef) -> Uart<'a> { + Uart { + registers: base, + tx_client: OptionalCell::empty(), + rx_client: OptionalCell::empty(), + stop_bits: Cell::new(hil::uart::StopBits::One), + buffer: TakeCell::empty(), + len: Cell::new(0), + index: Cell::new(0), + } + } + + /// Configure GPIO pins for the UART. + pub fn initialize_gpio_pins(&self, tx: &gpio::GpioPin, rx: &gpio::GpioPin) { + tx.iof0(); + rx.iof0(); + } + + fn set_baud_rate(&self, baud_rate: u32) { + let regs = self.registers; + + // Assume that the clock is running at 384 MHz. + // let clock_speed = 384_000_000 as u32; + + let clock_speed = 18_000_000 as u32; + + // f_clk + // f_baud = --------- + // div + 1 + let divisor = (clock_speed / baud_rate) - 1; + + regs.div.write(div::div.val(divisor)); + } + + fn enable_tx_interrupt(&self) { + let regs = self.registers; + regs.ie.modify(interrupt::txwm::SET); + } + + fn disable_tx_interrupt(&self) { + let regs = self.registers; + regs.ie.modify(interrupt::txwm::CLEAR); + } + + pub fn handle_interrupt(&self) { + let regs = self.registers; + + // Get a copy so we can check each interrupt flag in the register. + let pending_interrupts = regs.ip.extract(); + + // Determine why an interrupt occurred. + if pending_interrupts.is_set(interrupt::txwm) { + // Got a TX interrupt which means the number of bytes in the FIFO + // has fallen to zero. If there is more to send do that, otherwise + // send a callback to the client. + if self.len.get() == self.index.get() { + // We are done. + regs.txctrl.write(txctrl::txen::CLEAR); + self.disable_tx_interrupt(); + + // Signal client write done + self.tx_client.map(|client| { + self.buffer.take().map(|buffer| { + client.transmitted_buffer(buffer, self.len.get(), ReturnCode::SUCCESS); + }); + }); + } else { + // More to send. Fill the buffer until it is full. + self.buffer.map(|buffer| { + for i in self.index.get()..self.len.get() { + // Write the byte from the array to the tx register. + regs.txdata.write(txdata::data.val(buffer[i] as u32)); + self.index.set(i + 1); + // Check if the buffer is full + if regs.txdata.is_set(txdata::full) { + // If it is, break and wait for the TX interrupt. + break; + } + } + }); + } + } + } +} + +impl hil::uart::UartData<'a> for Uart<'a> {} +impl hil::uart::Uart<'a> for Uart<'a> {} + +impl hil::uart::Configure for Uart<'a> { + fn configure(&self, params: hil::uart::Parameters) -> ReturnCode { + // This chip does not support these features. + if params.parity != hil::uart::Parity::None { + return ReturnCode::ENOSUPPORT; + } + if params.hw_flow_control != false { + return ReturnCode::ENOSUPPORT; + } + + // We can set the baud rate. + self.set_baud_rate(params.baud_rate); + + // We need to save the stop bits because it is set in the TX register. + self.stop_bits.set(params.stop_bits); + + ReturnCode::SUCCESS + } +} + +impl hil::uart::Transmit<'a> for Uart<'a> { + fn set_transmit_client(&self, client: &'a hil::uart::TransmitClient) { + self.tx_client.set(client); + } + + fn transmit_buffer( + &self, + tx_data: &'static mut [u8], + tx_len: usize, + ) -> (ReturnCode, Option<&'static mut [u8]>) { + let regs = self.registers; + + if tx_len == 0 { + return (ReturnCode::ESIZE, Some(tx_data)); + } + + // Enable the interrupt so we know when we can keep writing. + self.enable_tx_interrupt(); + + // Fill the TX buffer until it reports full. + for i in 0..tx_len { + // Write the byte from the array to the tx register. + regs.txdata.write(txdata::data.val(tx_data[i] as u32)); + self.index.set(i + 1); + // Check if the buffer is full + if regs.txdata.is_set(txdata::full) { + // If it is, break and wait for the TX interrupt. + break; + } + } + + // Save the buffer so we can keep sending it. + self.buffer.replace(tx_data); + self.len.set(tx_len); + + // Enable transmissions, and wait until the FIFO is empty before getting + // an interrupt. + let stop_bits = match self.stop_bits.get() { + hil::uart::StopBits::One => txctrl::nstop::OneStopBit, + hil::uart::StopBits::Two => txctrl::nstop::TwoStopBits, + }; + regs.txctrl + .write(txctrl::txen::SET + stop_bits + txctrl::txcnt.val(1)); + + (ReturnCode::SUCCESS, None) + } + + fn transmit_abort(&self) -> ReturnCode { + ReturnCode::FAIL + } + + fn transmit_word(&self, _word: u32) -> ReturnCode { + ReturnCode::FAIL + } +} + +impl hil::uart::Receive<'a> for Uart<'a> { + fn set_receive_client(&self, client: &'a hil::uart::ReceiveClient) { + self.rx_client.set(client); + } + + fn receive_buffer( + &self, + _rx_buffer: &'static mut [u8], + _rx_len: usize, + ) -> (ReturnCode, Option<&'static mut [u8]>) { + (ReturnCode::FAIL, None) + } + + fn receive_abort(&self) -> ReturnCode { + ReturnCode::FAIL + } + + fn receive_word(&self) -> ReturnCode { + ReturnCode::FAIL + } +} diff --git a/chips/sifive/src/watchdog.rs b/chips/sifive/src/watchdog.rs new file mode 100644 index 0000000000..44cd0c2e63 --- /dev/null +++ b/chips/sifive/src/watchdog.rs @@ -0,0 +1,76 @@ +//! Watchdog + +use kernel::common::registers::{register_bitfields, ReadWrite, WriteOnly}; +use kernel::common::StaticRef; + +#[repr(C)] +pub struct WatchdogRegisters { + /// Watchdog Configuration Register + wdogcfg: ReadWrite, + _reserved0: [u8; 4], + /// Watchdog Counter Register + wdogcount: ReadWrite, + _reserved1: [u8; 4], + /// Watchdog Scaled Counter Register + wdogs: ReadWrite, + _reserved2: [u8; 4], + /// Watchdog Feed Register + wdogfeed: ReadWrite, + /// Watchdog Key Register + wdogkey: WriteOnly, + /// Watchdog Compare Register + wdogcmp: ReadWrite, +} + +register_bitfields![u32, + cfg [ + cmpip OFFSET(28) NUMBITS(1) [], + encoreawake OFFSET(13) NUMBITS(1) [], + enalways OFFSET(12) NUMBITS(1) [], + zerocmp OFFSET(9) NUMBITS(1) [], + rsten OFFSET(8) NUMBITS(1) [], + scale OFFSET(0) NUMBITS(4) [] + ], + key [ + key OFFSET(0) NUMBITS(32) [] + ], + feed [ + feed OFFSET(0) NUMBITS(32) [] + ] +]; + +pub struct Watchdog { + registers: StaticRef, +} + +impl Watchdog { + pub const fn new(base: StaticRef) -> Watchdog { + Watchdog { registers: base } + } + + fn unlock(&self) { + let regs = &*self.registers; + regs.wdogkey.write(key::key.val(0x51F15E)); + } + + fn feed(&self) { + let regs = &*self.registers; + + self.unlock(); + regs.wdogfeed.write(feed::feed.val(0xD09F00D)); + } + + pub fn disable(&self) { + let regs = &*self.registers; + + self.unlock(); + regs.wdogcfg.write( + cfg::scale.val(0) + + cfg::rsten::CLEAR + + cfg::zerocmp::CLEAR + + cfg::enalways::CLEAR + + cfg::encoreawake::CLEAR, + ); + self.feed(); + } +} From a60951e66d9bae88ab655edf605f4f8b4b4b6c3e Mon Sep 17 00:00:00 2001 From: Brad Campbell Date: Tue, 4 Jun 2019 10:47:12 -0400 Subject: [PATCH 2/3] hifive1 updates - use tock-rt0 functions - remove old commented out code - add more to readme.md --- arch/rv32i/Cargo.toml | 1 + arch/rv32i/src/lib.rs | 25 ++----------------------- boards/hifive1/Cargo.lock | 5 +++++ boards/hifive1/Makefile | 31 ------------------------------- boards/hifive1/README.md | 9 +++++++++ chips/e310x/Cargo.lock | 5 +++++ chips/e310x/src/chip.rs | 1 - 7 files changed, 22 insertions(+), 55 deletions(-) diff --git a/arch/rv32i/Cargo.toml b/arch/rv32i/Cargo.toml index df43c05cce..20ee323572 100644 --- a/arch/rv32i/Cargo.toml +++ b/arch/rv32i/Cargo.toml @@ -6,3 +6,4 @@ edition = "2018" [dependencies] kernel = { path = "../../kernel" } +tock_rt0 = { path = "../../libraries/tock-rt0" } diff --git a/arch/rv32i/src/lib.rs b/arch/rv32i/src/lib.rs index 7eda8651d8..4a130b4d45 100644 --- a/arch/rv32i/src/lib.rs +++ b/arch/rv32i/src/lib.rs @@ -75,29 +75,8 @@ global_asm!( /// /// This moves the data segment from flash to RAM and zeros out the BSS section. pub unsafe fn init_memory() { - // Relocate data segment. - // Assumes data starts right after text segment as specified by the linker - // file. - let mut pdest = &mut _srelocate as *mut u32; - let pend = &mut _erelocate as *mut u32; - let mut psrc = &_etext as *const u32; - - if psrc != pdest { - while (pdest as *const u32) < pend { - *pdest = *psrc; - pdest = pdest.offset(1); - psrc = psrc.offset(1); - } - } - - // Clear the zero segment (BSS) - let pzero = &_ezero as *const u32; - pdest = &mut _szero as *mut u32; - - while (pdest as *const u32) < pzero { - *pdest = 0; - pdest = pdest.offset(1); - } + tock_rt0::init_data(&mut _etext, &mut _srelocate, &mut _erelocate); + tock_rt0::zero_bss(&mut _szero, &mut _ezero); } /// Tell the MCU what address the trap handler is located at. diff --git a/boards/hifive1/Cargo.lock b/boards/hifive1/Cargo.lock index 85403a949c..34713ade40 100644 --- a/boards/hifive1/Cargo.lock +++ b/boards/hifive1/Cargo.lock @@ -45,6 +45,7 @@ name = "rv32i" version = "0.1.0" dependencies = [ "kernel 0.1.0", + "tock_rt0 0.1.0", ] [[package]] @@ -63,3 +64,7 @@ version = "0.1.0" name = "tock-registers" version = "0.3.0" +[[package]] +name = "tock_rt0" +version = "0.1.0" + diff --git a/boards/hifive1/Makefile b/boards/hifive1/Makefile index bd942deb10..97303730dd 100644 --- a/boards/hifive1/Makefile +++ b/boards/hifive1/Makefile @@ -8,34 +8,3 @@ include ../Makefile.common flash: target/$(TARGET)/release/$(PLATFORM).elf openocd \ -c "source [find board/sifive-hifive1.cfg]; flash protect 0 64 last off; program $<; resume 0x20400000; exit" - - -# TOCKLOADER=tockloader - -# # Where in the SAM4L flash to load the kernel with `tockloader` -# KERNEL_ADDRESS=0x10000 - -# # Upload programs over uart with tockloader -# ifdef PORT -# TOCKLOADER_GENERAL_FLAGS += --port $(PORT) -# endif - -# TOCKLOADER_JTAG_FLAGS = --jtag --board hail --arch cortex-m4 --jtag-device ATSAM4LC8C - -# .PHONY: program -# program: target/$(TARGET)/release/$(PLATFORM).bin -# $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) $< - -# # upload kernel over JTAG -# .PHONY: flash -# flash: target/$(TARGET)/release/$(PLATFORM).bin -# $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) $(TOCKLOADER_JTAG_FLAGS) $< - -# .PHONY: flash-debug -# flash-debug: target/$(TARGET)/debug/$(PLATFORM).bin -# $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) $(TOCKLOADER_JTAG_FLAGS) $< - -# # Command to flash the bootloader. Flashes the bootloader onto the SAM4L. -# .PHONY: flash-bootloader -# flash-bootloader: bootloader/bootloader.bin -# $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address 0 $(TOCKLOADER_JTAG_FLAGS) $< diff --git a/boards/hifive1/README.md b/boards/hifive1/README.md index 2633105f1d..35ec23d02b 100644 --- a/boards/hifive1/README.md +++ b/boards/hifive1/README.md @@ -10,3 +10,12 @@ The RISC-V core on this board only supports Machine mode. That means that this board does not support running userland applications. It is useful for testing RISC-V kernel code, however. + +Programming +----------- + +Running `make flash` should load the kernel onto the board. You will need a +relatively new (i.e. from git) version of OpenOCD. + +The kernel also assumes there is the default HiFive1 software bootloader running +on the chip. diff --git a/chips/e310x/Cargo.lock b/chips/e310x/Cargo.lock index a1a15a9355..d0281c6302 100644 --- a/chips/e310x/Cargo.lock +++ b/chips/e310x/Cargo.lock @@ -22,6 +22,7 @@ name = "rv32i" version = "0.1.0" dependencies = [ "kernel 0.1.0", + "tock_rt0 0.1.0", ] [[package]] @@ -40,3 +41,7 @@ version = "0.1.0" name = "tock-registers" version = "0.3.0" +[[package]] +name = "tock_rt0" +version = "0.1.0" + diff --git a/chips/e310x/src/chip.rs b/chips/e310x/src/chip.rs index f477931c99..af58ea1c90 100644 --- a/chips/e310x/src/chip.rs +++ b/chips/e310x/src/chip.rs @@ -112,7 +112,6 @@ impl kernel::Chip for E310x { index @ interrupts::GPIO0..interrupts::GPIO31 => { gpio::PORT[index as usize].handle_interrupt() } - // _ => debug!("PLIC index not supported by Tock {}", interrupt), _ => debug!("Pidx {}", interrupt), } From 6eb36fdd1697588fcecc0e8d872686b46f8e99c5 Mon Sep 17 00:00:00 2001 From: Brad Campbell Date: Tue, 11 Jun 2019 11:18:27 -0400 Subject: [PATCH 3/3] hifive1: add comment to layout.ld --- boards/hifive1/layout.ld | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/boards/hifive1/layout.ld b/boards/hifive1/layout.ld index 9e4a180b2c..207ff93980 100644 --- a/boards/hifive1/layout.ld +++ b/boards/hifive1/layout.ld @@ -1,7 +1,13 @@ +/* The HiFive1a board has 512 MB of flash. The first 0x400000 is reserved for + * the default bootloader provided by SiFive. We also reserve room for apps to + * make all of the linker files work, but don't really support them on this + * chip. + */ + MEMORY { - rom (rx) : ORIGIN = 0x20400000, LENGTH = 1M - prog (rx) : ORIGIN = 0x20500000, LENGTH = 507M + rom (rx) : ORIGIN = 0x20400000, LENGTH = 0x30000 + prog (rx) : ORIGIN = 0x20430000, LENGTH = 512M-0x430000 ram (rwx) : ORIGIN = 0x80000000, LENGTH = 16K }