Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions arch/rv32i/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions arch/rv32i/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "rv32i"
version = "0.1.0"
authors = ["Tock Project Developers <[email protected]>"]
edition = "2018"

[dependencies]
kernel = { path = "../../kernel" }
tock_rt0 = { path = "../../libraries/tock-rt0" }
11 changes: 11 additions & 0 deletions arch/rv32i/README.md
Original file line number Diff line number Diff line change
@@ -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)
176 changes: 176 additions & 0 deletions arch/rv32i/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
#![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!(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A while ago we talked about migrating away from inline assembly and instead just relying on assembly, linked as C functions. IIRC this was because of the optimization tricks the compiler does such that the generated assembly may (unless you get all of the annotations right) not be what you wrote.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, yes, this will be fixed in the next PR. It's fixed in the riscv2 branch.

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() {
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.
///
/// 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
"#
);
92 changes: 92 additions & 0 deletions arch/rv32i/src/machine_timer.rs
Original file line number Diff line number Diff line change
@@ -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<MachineTimerRegisters> =
unsafe { StaticRef::new(0x0200_0000 as *const MachineTimerRegisters) };

#[repr(C)]
struct MachineTimerRegisters {
_reserved0: [u8; 0x4000],
mtimecmp: ReadWrite<u64, MTimeCmp::Register>,
_reserved1: [u8; 0x7FF0],
mtime: ReadOnly<u64, MTime::Register>,
}

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<MachineTimerRegisters>,
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
}
}
Loading