first commit: rpi3 blinking led
This commit is contained in:
commit
0c7793fe1f
39 changed files with 1138 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
/target
|
||||
kernel8.img
|
||||
__pycache__/
|
||||
.doit.db
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "judas"
|
||||
version = "0.1.0"
|
20
Cargo.toml
Normal file
20
Cargo.toml
Normal file
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "judas"
|
||||
version = "0.1.0"
|
||||
authors = ["Jean-Marie Mineau <histausse@protonmail.com>"]
|
||||
edition = "2021"
|
||||
description = "A OS for raspberry pi, for fun"
|
||||
build = "build.rs"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
target_rpi3 = []
|
||||
target_rpi4 = []
|
||||
|
||||
[profile.release]
|
||||
# LLVM link time optimizations, obtimize the code, longer linking time
|
||||
lto = true
|
||||
|
||||
[[bin]]
|
||||
name = "kernel"
|
||||
path = "src/main.rs"
|
5
README.md
Normal file
5
README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Juda
|
||||
|
||||
Juda is a toy raspberry pi bear metal project that maybe will become some sort of OS (or not).
|
||||
|
||||
It is heavily inspired by [rust-raspberrypi-OS-tutorials](https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials), although I'm trying to make it my own.
|
BIN
bootloader_files/rpi3/bootcode.bin
Normal file
BIN
bootloader_files/rpi3/bootcode.bin
Normal file
Binary file not shown.
3
bootloader_files/rpi3/config.txt
Normal file
3
bootloader_files/rpi3/config.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
arm_64bit=1
|
||||
init_uart_clock=48000000
|
||||
enable_jtag_gpio=1
|
BIN
bootloader_files/rpi3/fixup.dat
Normal file
BIN
bootloader_files/rpi3/fixup.dat
Normal file
Binary file not shown.
BIN
bootloader_files/rpi3/start.elf
Normal file
BIN
bootloader_files/rpi3/start.elf
Normal file
Binary file not shown.
BIN
bootloader_files/rpi4/bcm2711-rpi-4-b.dtb
Normal file
BIN
bootloader_files/rpi4/bcm2711-rpi-4-b.dtb
Normal file
Binary file not shown.
3
bootloader_files/rpi4/config.txt
Normal file
3
bootloader_files/rpi4/config.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
arm_64bit=1
|
||||
init_uart_clock=48000000
|
||||
enable_jtag_gpio=1
|
BIN
bootloader_files/rpi4/fixup4.dat
Normal file
BIN
bootloader_files/rpi4/fixup4.dat
Normal file
Binary file not shown.
BIN
bootloader_files/rpi4/start4.elf
Normal file
BIN
bootloader_files/rpi4/start4.elf
Normal file
Binary file not shown.
4
build.rs
Normal file
4
build.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
fn main() {
|
||||
let linker_script = "linker.ld";
|
||||
println!("cargo:rerun-if-changed={linker_script}");
|
||||
}
|
BIN
doc/VideoCoreIV-AG100-R.pdf
Normal file
BIN
doc/VideoCoreIV-AG100-R.pdf
Normal file
Binary file not shown.
BIN
doc/as.pdf
Normal file
BIN
doc/as.pdf
Normal file
Binary file not shown.
BIN
doc/bcm2711-peripherals-raspy4.pdf
Normal file
BIN
doc/bcm2711-peripherals-raspy4.pdf
Normal file
Binary file not shown.
BIN
doc/bcm2837-peripherals-raspy3.pdf
Normal file
BIN
doc/bcm2837-peripherals-raspy3.pdf
Normal file
Binary file not shown.
BIN
doc/ld.pdf
Normal file
BIN
doc/ld.pdf
Normal file
Binary file not shown.
135
dodo.py
Normal file
135
dodo.py
Normal file
|
@ -0,0 +1,135 @@
|
|||
""" The doit file. Because makefiles are old and hard to read.
|
||||
Can't promise this will be more readable, but at least it wont be old :)
|
||||
"""
|
||||
|
||||
import os
|
||||
from enum import Enum
|
||||
from types import SimpleNamespace
|
||||
from typing import Iterator
|
||||
from pathlib import Path
|
||||
|
||||
from doit.tools import Interactive
|
||||
|
||||
class BoardTarget(Enum):
|
||||
RPI3B = 1
|
||||
RPI4 = 2
|
||||
|
||||
## Target
|
||||
BOARD_TARGET = BoardTarget.RPI3B
|
||||
|
||||
## Common constants
|
||||
consts = SimpleNamespace()
|
||||
consts.WORK_DIR = os.getcwd()
|
||||
# Rust equivalent to -Werror
|
||||
consts.RUSTC_FLAGS = "-D warnings "
|
||||
# Force documentation
|
||||
consts.RUSTC_FLAGS += "-D missing_docs"
|
||||
consts.CARGO_COMPILE_FLAGS = "--release"
|
||||
|
||||
if BOARD_TARGET == BoardTarget.RPI3B:
|
||||
consts.TARGET = "aarch64-unknown-none-softfloat"
|
||||
consts.KERNEL_NAME = "kernel8.img"
|
||||
consts.QEMU_CMD = "qemu-system-aarch64"
|
||||
consts.QEMU_MACHINE = "raspi3b"
|
||||
consts.QEMU_ARGS = "-serial stdio -display none"
|
||||
consts.OBJDUMP_CMD = "aarch64-none-elf-objdump"
|
||||
consts.OBJDUMP_ARGS = "-D"
|
||||
consts.NM_CMD = "aarch64-none-elf-nm"
|
||||
consts.READELF_CMD = "aarch64-none-elf-readelf"
|
||||
consts.LD_SCRIPT_PATH = f"{consts.WORK_DIR}/linker.ld"
|
||||
consts.RUSTC_FLAGS = f"-C target-cpu=cortex-a53 -C link-arg=--script={consts.LD_SCRIPT_PATH} {consts.RUSTC_FLAGS}"
|
||||
consts.CARGO_COMPILE_FLAGS = f"--features target_rpi3 {consts.CARGO_COMPILE_FLAGS}"
|
||||
elif BOARD_TARGET == BoardTarget.RPI4:
|
||||
consts.TARGET = "aarch64-unknown-none-softfloat"
|
||||
consts.KERNEL_NAME = "kernel8.img"
|
||||
consts.QEMU_CMD = "qemu-system-aarch64"
|
||||
consts.QEMU_MACHINE = None
|
||||
consts.QEMU_ARGS = "-serial stdio -display none"
|
||||
consts.OBJDUMP_CMD = "aarch64-none-elf-objdump"
|
||||
consts.OBJDUMP_ARGS = "-D"
|
||||
consts.NM_CMD = "aarch64-none-elf-nm"
|
||||
consts.READELF_CMD = "aarch64-none-elf-readelf"
|
||||
consts.LD_SCRIPT_PATH = f"{consts.WORK_DIR}/linker.ld"
|
||||
consts.RUSTC_FLAGS = f"-C target-cpu=cortex-a72 -C link-arg=--script={consts.LD_SCRIPT_PATH} {consts.RUSTC_FLAGS}"
|
||||
consts.CARGO_COMPILE_FLAGS = f"--features target_rpi4 {consts.CARGO_COMPILE_FLAGS}"
|
||||
|
||||
DOIT_CONFIG = {
|
||||
'default_tasks': ['build'],
|
||||
}
|
||||
|
||||
def get_rust_files() -> Iterator[str]:
|
||||
""" Get a list of the rust source files.
|
||||
"""
|
||||
return map(lambda p: str(p.resolve()), Path('./src').glob('**/*.rs'))
|
||||
|
||||
def get_asm_files() -> Iterator[str]:
|
||||
""" Get a list of the assembly source files.
|
||||
"""
|
||||
return map(lambda p: str(p.resolve()), Path('./src').glob('**/*.s'))
|
||||
|
||||
def get_build_dep() -> list[str]:
|
||||
""" Get a list of the files impacting the binary.
|
||||
"""
|
||||
# TODO: add dodo.py to the list?
|
||||
return [
|
||||
consts.LD_SCRIPT_PATH,
|
||||
*get_rust_files(),
|
||||
*get_asm_files(),
|
||||
]
|
||||
|
||||
def task_install_dep():
|
||||
""" Install some required tools.
|
||||
"""
|
||||
return {
|
||||
'actions': [
|
||||
'cargo install cargo-binutils',
|
||||
f'rustup target add {consts.TARGET}',
|
||||
'rustup component add llvm-tools-preview',
|
||||
],
|
||||
}
|
||||
|
||||
def task_compile():
|
||||
""" Compile the sources.
|
||||
"""
|
||||
return {
|
||||
'file_dep': get_build_dep(),
|
||||
'targets': [f'{consts.WORK_DIR}/target/{consts.TARGET}/release/kernel'],
|
||||
'actions': [f'RUSTFLAGS="{consts.RUSTC_FLAGS}" cargo rustc --target={consts.TARGET} {consts.CARGO_COMPILE_FLAGS}'],
|
||||
'clean': [f'cargo clean']
|
||||
}
|
||||
|
||||
def task_build():
|
||||
""" Strip the binary from ELF format to raw binary.
|
||||
"""
|
||||
return {
|
||||
'file_dep': [f'{consts.WORK_DIR}/target/{consts.TARGET}/release/kernel'],
|
||||
'targets': [f'{consts.WORK_DIR}/consts.KERNEL_NAME'],
|
||||
'actions': [
|
||||
f'rust-objcopy --strip-all -O binary {consts.WORK_DIR}/target/{consts.TARGET}/release/kernel {consts.WORK_DIR}/{consts.KERNEL_NAME}',
|
||||
],
|
||||
'clean': True,
|
||||
}
|
||||
|
||||
def task_qemu():
|
||||
""" Run the kernel in qemu.
|
||||
"""
|
||||
if consts.QEMU_MACHINE is None:
|
||||
raise Exception("qemu does not support emulation for this board yet")
|
||||
r = {
|
||||
'file_dep': [f'{consts.WORK_DIR}/{consts.KERNEL_NAME}'],
|
||||
'uptodate': [False], # Always run the cmd
|
||||
'actions': [Interactive(f'{consts.QEMU_CMD} -M {consts.QEMU_MACHINE} {consts.QEMU_ARGS} -kernel {consts.KERNEL_NAME}')],
|
||||
}
|
||||
print(r)
|
||||
return r
|
||||
|
||||
def task_objdump():
|
||||
""" Inspect the ELF binary file.
|
||||
"""
|
||||
r = {
|
||||
'file_dep': [f'{consts.WORK_DIR}/target/{consts.TARGET}/release/kernel'],
|
||||
'uptodate': [False], # Always run the cmd
|
||||
'actions': [Interactive(f'{consts.OBJDUMP_CMD} {consts.OBJDUMP_ARGS} {consts.WORK_DIR}/target/{consts.TARGET}/release/kernel')]
|
||||
}
|
||||
print(r)
|
||||
return r
|
49
linker.ld
Normal file
49
linker.ld
Normal file
|
@ -0,0 +1,49 @@
|
|||
|
||||
MEMORY {
|
||||
sdram_for_arm_1 (rwx) : ORIGIN = 0x00000000, LENGTH = 256M /* Lenght should be 1G - gpu_mem */
|
||||
}
|
||||
|
||||
__kernel_load_addr = 0x80000; /* 0x80000 because AArch64 (0x8000 is for AArch32) */
|
||||
|
||||
ENTRY(__kernel_load_addr)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
segment_core_stack PT_LOAD FLAGS(6);
|
||||
segment_code PT_LOAD FLAGS(5);
|
||||
segment_data PT_LOAD FLAGS(6);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.core_stack (NOLOAD) :
|
||||
{
|
||||
__core_stack_start = .;
|
||||
. += __kernel_load_addr; /* TODO: Don't like this, it only work because it starts at 0x0 */
|
||||
__core_stack_end = .;
|
||||
|
||||
} > sdram_for_arm_1 : segment_core_stack
|
||||
.text :
|
||||
{
|
||||
. = __kernel_load_addr;
|
||||
KEEP(*(.text._start))
|
||||
*(.text._start_arg) /* Static read by _start() */
|
||||
*(.text._start_rust) /* Enty point in rust */
|
||||
*(.text*) /* The rest of the code */
|
||||
} > sdram_for_arm_1 :segment_code
|
||||
.rodata : ALIGN(8) { *(.rodata) } > sdram_for_arm_1 :segment_code
|
||||
/* TODO: not sure having the RODATA in an executable
|
||||
segment is a good practice? */
|
||||
.data : { *(.data) } > sdram_for_arm_1 :segment_data
|
||||
.bss (NOLOAD) : ALIGN(16)
|
||||
{
|
||||
__bss_start = .;
|
||||
*(.bss*);
|
||||
. = ALIGN(16);
|
||||
__bss_end = .;
|
||||
} > sdram_for_arm_1 :segment_data
|
||||
|
||||
/* GOT? */
|
||||
}
|
||||
|
||||
ENTRY(_start)
|
3
rust-toolchain.toml
Normal file
3
rust-toolchain.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "nightly"
|
||||
targets = ["aarch64-unknown-none-softfloat"]
|
35
src/boot.s
Normal file
35
src/boot.s
Normal file
|
@ -0,0 +1,35 @@
|
|||
.section .text._start // Make sure the linker put this at the begining of the
|
||||
// .text section thanks to the `KEEP(*(.text._start))`
|
||||
|
||||
_start:
|
||||
mrs x1, MPIDR_EL1
|
||||
and x1, x1, #3
|
||||
mov x2, #0
|
||||
cmp x1, x2
|
||||
b.ne ._exit // if core id != 0, wait forever
|
||||
|
||||
// Set bss to 0
|
||||
ldr x0,=__bss_start
|
||||
ldr x1,=__bss_end
|
||||
._bss_loop:
|
||||
cmp x0, x1
|
||||
b.eq ._set_sp
|
||||
stp xzr, xzr, [x0], #16
|
||||
b ._bss_loop
|
||||
|
||||
// Set the stack pointer
|
||||
._set_sp:
|
||||
ldr x1,=__core_stack_end // the stack goest down
|
||||
mov sp, x1
|
||||
|
||||
// go to rust
|
||||
b _start_rust
|
||||
|
||||
._exit:
|
||||
wfe // 'Wait For Event', put the cpu in low power mode
|
||||
b ._exit
|
||||
|
||||
// define _start as a function
|
||||
.size _start, . - _start
|
||||
.type _start, function
|
||||
.global _start
|
10
src/bsp/mod.rs
Normal file
10
src/bsp/mod.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
//! Board Support Package: module containing implementation
|
||||
//! specific to a board.
|
||||
|
||||
pub mod qemu;
|
||||
|
||||
#[cfg(feature = "target_rpi3")]
|
||||
pub mod rpi3;
|
||||
|
||||
#[cfg(feature = "target_rpi4")]
|
||||
pub mod rpi4;
|
85
src/bsp/qemu/console.rs
Normal file
85
src/bsp/qemu/console.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
//! Implement the Qemu magic UART.
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use crate::traits::console::{Console, Write};
|
||||
|
||||
use crate::traits::synchronization::{DummyMutex, Mutex};
|
||||
|
||||
/// The address for the magic qemu output
|
||||
const QEMU_MAGIC_OUTPUT_ADDR: *mut u8 = 0x3F20_1000 as *mut u8;
|
||||
|
||||
/// The unique qemu output allowing access to the qemu output.
|
||||
static QEMU_OUTPUT: QemuOutput = QemuOutput::new();
|
||||
|
||||
/// A structure allowing access to the qemu magic output.
|
||||
struct QemuOutput {
|
||||
inner: DummyMutex<QemuOutputInner>,
|
||||
}
|
||||
|
||||
/// Inner Qemu output.
|
||||
struct QemuOutputInner;
|
||||
|
||||
impl QemuOutputInner {
|
||||
/// Constructor for [`QemuOutputInner`].
|
||||
const fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
/// Write a character to the output.
|
||||
fn write_char(&mut self, c: char) {
|
||||
unsafe {
|
||||
core::ptr::write_volatile(QEMU_MAGIC_OUTPUT_ADDR, c as u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl QemuOutput {
|
||||
/// Constructor for [`QemuOutput`].
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
inner: DummyMutex::new(QemuOutputInner::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow to use QemuOutputInner for print! and formating macros.
|
||||
/// `write_str` needs `&mut self` (mutable ref), so we can implement
|
||||
/// it only on the inner type, the `QemuOutput` need to be manipulable
|
||||
/// using unmutable references (`&self`), so we will use a custom
|
||||
/// interface for it.
|
||||
impl fmt::Write for QemuOutputInner {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for c in s.chars() {
|
||||
// \n -> \r\n
|
||||
if c == '\n' {
|
||||
self.write_char('\r');
|
||||
}
|
||||
self.write_char(c);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for QemuOutput {
|
||||
fn write_char(&self, c: char) {
|
||||
self.inner.lock(|q_out| q_out.write_char(c))
|
||||
}
|
||||
|
||||
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result {
|
||||
self.inner.lock(|q_out| fmt::Write::write_fmt(q_out, args))
|
||||
}
|
||||
|
||||
/// Empty function, the qemu uart has no buffering afaik
|
||||
fn flush(&self) {
|
||||
// self.inner.lock(|q_out| q_out.flush())
|
||||
}
|
||||
}
|
||||
|
||||
impl Console for QemuOutput {}
|
||||
|
||||
// TODO: move?
|
||||
/// Return a reference to the Qemu Output.
|
||||
pub fn console() -> &'static dyn Console {
|
||||
&QEMU_OUTPUT
|
||||
}
|
7
src/bsp/qemu/mod.rs
Normal file
7
src/bsp/qemu/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
//! Implement the features specific to code running inside qemu.
|
||||
//!
|
||||
//! # TODO
|
||||
//!
|
||||
//! Move this somewhere else?
|
||||
|
||||
pub mod console;
|
101
src/bsp/rpi3/gpio.rs
Normal file
101
src/bsp/rpi3/gpio.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
//! GPIO module.
|
||||
|
||||
use super::memory_map;
|
||||
use crate::println;
|
||||
|
||||
|
||||
/// The number of pin for the raspberry 3.
|
||||
const NUMBER_PIN: usize = 54;
|
||||
|
||||
/// Value to select the INPUT function of a PIN.
|
||||
const FSEL_INPUT: u32 = 0b000;
|
||||
/// Value to select the OUTPUT function of a PIN.
|
||||
const FSEL_OUTPUT: u32 = 0b001;
|
||||
/// Value to select the Alternate function 0 of a PIN.
|
||||
const FSEL_ALTF0: u32 = 0b100;
|
||||
/// Value to select the Alternate function 1 of a PIN.
|
||||
const FSEL_ALTF1: u32 = 0b101;
|
||||
/// Value to select the Alternate function 2 of a PIN.
|
||||
const FSEL_ALTF2: u32 = 0b110;
|
||||
/// Value to select the Alternate function 3 of a PIN.
|
||||
const FSEL_ALTF3: u32 = 0b111;
|
||||
/// Value to select the Alternate function 4 of a PIN.
|
||||
const FSEL_ALTF4: u32 = 0b011;
|
||||
/// Value to select the Alternate function 5 of a PIN.
|
||||
const FSEL_ALTF5: u32 = 0b010;
|
||||
|
||||
/// The possible PIN functions.
|
||||
pub enum PinFunction {
|
||||
Input,
|
||||
Output,
|
||||
AltFunc0,
|
||||
AltFunc1,
|
||||
AltFunc2,
|
||||
AltFunc3,
|
||||
AltFunc4,
|
||||
AltFunc5,
|
||||
}
|
||||
|
||||
/// The possible PIN state when in Output mode.
|
||||
pub enum PinOutputState {
|
||||
High,
|
||||
Low,
|
||||
}
|
||||
|
||||
impl From<PinFunction> for u32 {
|
||||
fn from(function: PinFunction) -> u32 {
|
||||
match function {
|
||||
PinFunction::Input => FSEL_INPUT,
|
||||
PinFunction::Output => FSEL_OUTPUT,
|
||||
PinFunction::AltFunc0 => FSEL_ALTF0,
|
||||
PinFunction::AltFunc1 => FSEL_ALTF1,
|
||||
PinFunction::AltFunc2 => FSEL_ALTF2,
|
||||
PinFunction::AltFunc3 => FSEL_ALTF3,
|
||||
PinFunction::AltFunc4 => FSEL_ALTF4,
|
||||
PinFunction::AltFunc5 => FSEL_ALTF5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove Result and use an enum instead of usize
|
||||
/// Set the function of a Pin.
|
||||
///
|
||||
/// # Result
|
||||
///
|
||||
/// Return an error if the PIN does not exist.
|
||||
pub fn set_pin_fonction(n: usize, function: PinFunction) -> Result<(), &'static str> {
|
||||
if n >= NUMBER_PIN {
|
||||
return Err("The pin does not exist.");
|
||||
}
|
||||
let field = memory_map::get_fsel(n);
|
||||
// TODO: SYNC
|
||||
let mut val = unsafe { core::ptr::read_volatile(field.get_address() as *mut u32) };
|
||||
val &= !field.get_mask();
|
||||
val |= Into::<u32>::into(function) << field.get_offset();
|
||||
let address = field.get_address();
|
||||
println!("Set {val:b} at 0x{address:x}");
|
||||
unsafe { core::ptr::write_volatile(address as *mut u32, val); }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: remove Result and use an enum instead of usize
|
||||
/// Set the state of an output Pin.
|
||||
///
|
||||
/// # Result
|
||||
///
|
||||
/// Return an error if the PIN does not exist.
|
||||
pub fn set_pin_output_state(n: usize, state: PinOutputState) -> Result<(), &'static str> {
|
||||
if n >= NUMBER_PIN {
|
||||
return Err("The pin does not exist.");
|
||||
}
|
||||
let field = match state {
|
||||
PinOutputState::High => memory_map::get_set(n),
|
||||
PinOutputState::Low => memory_map::get_clr(n),
|
||||
};
|
||||
let val = field.get_mask();
|
||||
let address = field.get_address();
|
||||
println!("Set {val:b} at 0x{address:x}");
|
||||
// 0 has no effect on this field: no nead to read-modify-write
|
||||
unsafe { core::ptr::write_volatile(address as *mut u32, val); }
|
||||
Ok(())
|
||||
}
|
220
src/bsp/rpi3/memory_map.rs
Normal file
220
src/bsp/rpi3/memory_map.rs
Normal file
|
@ -0,0 +1,220 @@
|
|||
//! The memory map of the board.
|
||||
|
||||
use crate::utils::field::Field;
|
||||
|
||||
pub const GPIO_OFFSET: usize = 0x0020_0000;
|
||||
pub const UART0_OFFSET: usize = 0x0020_1000;
|
||||
|
||||
pub const START_PHYSICAL_ADDRESS: usize = 0x3F00_0000;
|
||||
|
||||
pub const UART0_START: usize = START_PHYSICAL_ADDRESS + UART0_OFFSET;
|
||||
|
||||
// ** GPIO addresses **
|
||||
/// Beginning of the GPIO adresses
|
||||
pub const GPIO_START: usize = START_PHYSICAL_ADDRESS + GPIO_OFFSET;
|
||||
|
||||
// Function Select
|
||||
/// GPIO Function Select 0, R/W register.
|
||||
/// bits 29-27: FSEL9
|
||||
/// bits 26-24: FSEL8
|
||||
/// bits 23-21: FSEL7
|
||||
/// bits 20-18: FSEL6
|
||||
/// bits 17-15: FSEL5
|
||||
/// bits 14-12: FSEL4
|
||||
/// bits 11-9: FSEL3
|
||||
/// bits 8-6: FSEL2
|
||||
/// bits 5-3: FSEL1
|
||||
/// bits 2-0: FSEL0
|
||||
pub const GPFSEL0: usize = GPIO_START + 0x00;
|
||||
/// GPIO Function Select 1, R/W register.
|
||||
/// bits 29-27: FSEL19
|
||||
/// bits 26-24: FSEL18
|
||||
/// bits 23-21: FSEL17
|
||||
/// bits 20-18: FSEL16
|
||||
/// bits 17-15: FSEL15
|
||||
/// bits 14-12: FSEL14
|
||||
/// bits 11-9: FSEL13
|
||||
/// bits 8-6: FSEL12
|
||||
/// bits 5-3: FSEL11
|
||||
/// bits 2-0: FSEL10
|
||||
pub const GPFSEL1:usize = GPIO_START + 0x04;
|
||||
/// GPIO Function Select 2, R/W register.
|
||||
/// bits 29-27: FSEL29
|
||||
/// bits 26-24: FSEL28
|
||||
/// bits 23-21: FSEL27
|
||||
/// bits 20-18: FSEL26
|
||||
/// bits 17-15: FSEL25
|
||||
/// bits 14-12: FSEL24
|
||||
/// bits 11-9: FSEL23
|
||||
/// bits 8-6: FSEL22
|
||||
/// bits 5-3: FSEL21
|
||||
/// bits 2-0: FSEL20
|
||||
pub const GPFSEL2:usize = GPIO_START + 0x08;
|
||||
/// GPIO Function Select 3, R/W register.
|
||||
/// bits 29-27: FSEL39
|
||||
/// bits 26-24: FSEL38
|
||||
/// bits 23-21: FSEL37
|
||||
/// bits 20-18: FSEL36
|
||||
/// bits 17-15: FSEL35
|
||||
/// bits 14-12: FSEL34
|
||||
/// bits 11-9: FSEL33
|
||||
/// bits 8-6: FSEL32
|
||||
/// bits 5-3: FSEL31
|
||||
/// bits 2-0: FSEL30
|
||||
pub const GPFSEL3:usize = GPIO_START + 0x0C;
|
||||
/// GPIO Function Select 4, R/W register.
|
||||
/// bits 29-27: FSEL49
|
||||
/// bits 26-24: FSEL48
|
||||
/// bits 23-21: FSEL47
|
||||
/// bits 20-18: FSEL46
|
||||
/// bits 17-15: FSEL45
|
||||
/// bits 14-12: FSEL44
|
||||
/// bits 11-9: FSEL43
|
||||
/// bits 8-6: FSEL42
|
||||
/// bits 5-3: FSEL41
|
||||
/// bits 2-0: FSEL40
|
||||
pub const GPFSEL4:usize = GPIO_START + 0x10;
|
||||
/// GPIO Function Select 5, R/W register.
|
||||
/// bits 11-9: FSEL53
|
||||
/// bits 8-6: FSEL52
|
||||
/// bits 5-3: FSEL51
|
||||
/// bits 2-0: FSEL50
|
||||
pub const GPFSEL5:usize = GPIO_START + 0x14;
|
||||
|
||||
/// Return the field FSELn.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// Panic if the pin `n` does not exist.
|
||||
pub const fn get_fsel(n: usize) -> Field {
|
||||
if n > 53 {
|
||||
panic!("The PIN does not exist");
|
||||
}
|
||||
let address = [GPFSEL0, GPFSEL1, GPFSEL2, GPFSEL3, GPFSEL4, GPFSEL5][n/10];
|
||||
let offset = 3*(n%10);
|
||||
let size = 3;
|
||||
Field::new(address, offset, size)
|
||||
}
|
||||
|
||||
// Pin Output Set
|
||||
/// Pin Output Set 0, W register.
|
||||
/// bit 31: SET31
|
||||
/// ...
|
||||
/// bit 0: SET0
|
||||
pub const GPSET0:usize = GPIO_START + 0x1C;
|
||||
/// Pin Output Set 1, W register.
|
||||
/// bit 21: SET53
|
||||
/// ...
|
||||
/// bit 0: SET32
|
||||
pub const GPSET1:usize = GPIO_START + 0x20;
|
||||
|
||||
/// Return the field SETn.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// Panic if the pin `n` does not exist.
|
||||
pub const fn get_set(n: usize) -> Field {
|
||||
if n > 53 {
|
||||
panic!("The PIN does not exist");
|
||||
}
|
||||
let (address, offset) = if n < 32 {
|
||||
(GPSET0, n)
|
||||
} else {
|
||||
(GPSET1, n-32)
|
||||
};
|
||||
let size = 1;
|
||||
Field::new(address, offset, size)
|
||||
}
|
||||
|
||||
// Pin Output Clear
|
||||
/// Pin Output Clear 0, W register.
|
||||
/// bit 31: CLR31
|
||||
/// ...
|
||||
/// bit 0: CLR0
|
||||
pub const GPCLR0:usize = GPIO_START + 0x28;
|
||||
/// Pin Output Clear 1, W register.
|
||||
/// bit 21: CLR53
|
||||
/// ...
|
||||
/// bit 0: CLR32
|
||||
pub const GPCLR1:usize = GPIO_START + 0x2C;
|
||||
|
||||
/// Return the field CLRn.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// Panic if the pin `n` does not exist.
|
||||
pub const fn get_clr(n: usize) -> Field {
|
||||
if n > 53 {
|
||||
panic!("The PIN does not exist");
|
||||
}
|
||||
let (address, offset) = if n < 32 {
|
||||
(GPCLR0, n)
|
||||
} else {
|
||||
(GPCLR1, n-32)
|
||||
};
|
||||
let size = 1;
|
||||
Field::new(address, offset, size)
|
||||
}
|
||||
|
||||
// Pin Level
|
||||
/// Pin Level 0, R register.
|
||||
pub const GPLEV0:usize = GPIO_START + 0x34;
|
||||
/// Pin Level 1, R register.
|
||||
pub const GPLEV1:usize = GPIO_START + 0x38;
|
||||
|
||||
// Pin Event Detect Status
|
||||
/// Pin Event Detect Status 0, R/W register.
|
||||
pub const GPEDS0:usize = GPIO_START + 0x40;
|
||||
/// Pin Event Detect Status 1, R/W register.
|
||||
pub const GPEDS1:usize = GPIO_START + 0x44;
|
||||
|
||||
// Pin Rising Edge Detect Enable
|
||||
/// Pin Rising Edge Detect Enable 0, R/W register.
|
||||
pub const GPREN0:usize = GPIO_START + 0x4C;
|
||||
/// Pin Rising Edge Detect Enable 1, R/W register.
|
||||
pub const GPREN1:usize = GPIO_START + 0x50;
|
||||
|
||||
// Pin Falling Edge Detect Enable
|
||||
/// Pin Falling Edge Detect Enable 0, R/W register.
|
||||
pub const GPFEN0:usize = GPIO_START + 0x58;
|
||||
/// Pin Falling Edge Detect Enable 1, R/W register.
|
||||
pub const GPFEN1:usize = GPIO_START + 0x5C;
|
||||
|
||||
// Pin High Detect Enable
|
||||
/// Pin High Detect Enable 0, R/W register.
|
||||
pub const GPHEN0:usize = GPIO_START + 0x64;
|
||||
/// Pin High Detect Enable 1, R/W register.
|
||||
pub const GPHEN1:usize = GPIO_START + 0x68;
|
||||
|
||||
// Pin Low Detect Enable
|
||||
/// Pin Low Detect Enable 0, R/W register.
|
||||
pub const GPLEN0:usize = GPIO_START + 0x70;
|
||||
/// Pin Low Detect Enable 1, R/W register.
|
||||
pub const GPLEN1:usize = GPIO_START + 0x74;
|
||||
|
||||
// Pin Async, Rising Edge Detect
|
||||
/// Pin Async, Rising Edge Detect 0, R/W register.
|
||||
pub const GPAREN0:usize = GPIO_START + 0x7C;
|
||||
/// Pin Async, Rising Edge Detect 1, R/W register.
|
||||
pub const GPAREN1:usize = GPIO_START + 0x80;
|
||||
|
||||
// Pin Async, Falling Edge Detect
|
||||
/// Pin Async, Falling Edge Detect 0, R/W register.
|
||||
pub const GPAFEN0:usize = GPIO_START + 0x88;
|
||||
/// Pin Async, Falling Edge Detect1, R/W register.
|
||||
pub const GPAFEN1:usize = GPIO_START + 0x8C;
|
||||
|
||||
// Pin Pull-up/down Enable, R/W
|
||||
/// Pin Pull-up/down Enable, R/W register.
|
||||
pub const GPPUD:usize = GPIO_START + 0x94;
|
||||
|
||||
// Pin Pull-up/down enable clock, R/W
|
||||
/// Pin Pull-up/down enable clock 0, R/W register.
|
||||
pub const GPPUDCLK0:usize = GPIO_START + 0x98;
|
||||
/// Pin Pull-up/down enable clock 1, R/W register.
|
||||
pub const GPPUDCLK1:usize = GPIO_START + 0x9C;
|
||||
|
||||
// Test ?, R/W, 4 bits
|
||||
/// Test register? only 4 bits long.
|
||||
pub const GPIO_TEST:usize = GPIO_START + 0xB0;
|
||||
// ** GPIO addresses **
|
6
src/bsp/rpi3/mod.rs
Normal file
6
src/bsp/rpi3/mod.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
//! Module specific to the raspberry pi 3 implementations.
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(self) mod memory_map;
|
||||
|
||||
pub mod gpio;
|
15
src/bsp/rpi4/memory_map.rs
Normal file
15
src/bsp/rpi4/memory_map.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
//! The memory map of the board.
|
||||
|
||||
// use crate::utils::field::Field;
|
||||
|
||||
pub const GPIO_OFFSET: usize = 0x0020_0000;
|
||||
pub const UART0_OFFSET: usize = 0x0020_1000;
|
||||
|
||||
pub const START_LOW_PERIPHERAL_MODE: usize = 0xFE00_0000;
|
||||
pub const GPIO_START: usize = START_LOW_PERIPHERAL_MODE + GPIO_OFFSET;
|
||||
pub const UART0_START: usize = START_LOW_PERIPHERAL_MODE + UART0_OFFSET;
|
||||
pub const UART2_START: usize = START_LOW_PERIPHERAL_MODE + 0x0020_1400;
|
||||
pub const UART3_START: usize = START_LOW_PERIPHERAL_MODE + 0x0020_1600;
|
||||
pub const UART4_START: usize = START_LOW_PERIPHERAL_MODE + 0x0020_1800;
|
||||
pub const UART5_START: usize = START_LOW_PERIPHERAL_MODE + 0x0020_1A00;
|
||||
|
4
src/bsp/rpi4/mod.rs
Normal file
4
src/bsp/rpi4/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
//! Module specific to the raspberry pi 4 implementations.
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(self) mod memory_map;
|
66
src/main.rs
Normal file
66
src/main.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
//! The Judas kernel for raspberry pi.
|
||||
//!
|
||||
//! This is a kernel written in rust for raspberry pi 3 and 4.
|
||||
//! This project is heavily inspired by https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials,
|
||||
//! but it's a project for discovering rust on embedded, so its architecture is less generic and
|
||||
//! I'm trying to reuse knowledge from Telecom-Paris SE203 class as much as I can.
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
#![feature(format_args_nl)]
|
||||
#![feature(panic_info_message)]
|
||||
|
||||
mod traits;
|
||||
mod bsp;
|
||||
mod utils;
|
||||
|
||||
mod panic;
|
||||
mod print;
|
||||
|
||||
use core::arch::global_asm;
|
||||
use core::arch::asm;
|
||||
|
||||
// TODO: handle this with features
|
||||
use crate::bsp::qemu::console::console;
|
||||
use crate::bsp::rpi3::gpio;
|
||||
|
||||
// TODO: move this to BSP
|
||||
/// Pause the core with a infinit loop
|
||||
#[inline(always)]
|
||||
pub fn wait_forever() -> ! {
|
||||
loop {
|
||||
unsafe { asm!("wfe"); }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move this to BSP
|
||||
global_asm!(include_str!("boot.s"));
|
||||
|
||||
/// Start the rust part of the kernel
|
||||
#[no_mangle]
|
||||
pub unsafe fn _start_rust() -> ! {
|
||||
match gpio::set_pin_fonction(21, gpio::PinFunction::Output) {
|
||||
Ok(()) => println!("Successfully set pin to output"),
|
||||
Err(err) => println!("Failled to set pin: {err}"),
|
||||
}
|
||||
loop {
|
||||
match gpio::set_pin_output_state(21, gpio::PinOutputState::High) {
|
||||
Ok(()) => println!("Successfully set pin to HIGH"),
|
||||
Err(err) => println!("Failled to set pin: {err}"),
|
||||
}
|
||||
for _ in 0..5000000 {
|
||||
asm!("nop");
|
||||
}
|
||||
match gpio::set_pin_output_state(21, gpio::PinOutputState::Low) {
|
||||
Ok(()) => println!("Successfully set pin to HIGH"),
|
||||
Err(err) => println!("Failled to set pin: {err}"),
|
||||
}
|
||||
for _ in 0..5000000 {
|
||||
asm!("nop");
|
||||
}
|
||||
}
|
||||
/*
|
||||
println!("Hello there");
|
||||
panic!("Paniccccccc")
|
||||
*/
|
||||
}
|
42
src/panic.rs
Normal file
42
src/panic.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
//! The module handelling kernel panic.
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
use crate::println;
|
||||
use crate::wait_forever;
|
||||
|
||||
/// Avoid nested panic
|
||||
fn panic_prevent_reenter() {
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
// This code is safe to use with AArch64, if using another
|
||||
// arch, check the safety first
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
compile_error!(
|
||||
"The following code is safe for aarch64, \
|
||||
check the safety before using with another arch"
|
||||
);
|
||||
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
|
||||
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
|
||||
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
|
||||
return;
|
||||
}
|
||||
wait_forever()
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
panic_prevent_reenter();
|
||||
|
||||
let (location, line, column) = match info.location() {
|
||||
Some(loc) => (loc.file(), loc.line(), loc.column()),
|
||||
_ => ("???", 0, 0),
|
||||
};
|
||||
|
||||
println!(
|
||||
"Kernel panic!\n\n\
|
||||
Panic location:\n File: '{location}', line {line}, column {column}\n\n\
|
||||
{}",
|
||||
info.message().unwrap_or(&format_args!(""))
|
||||
);
|
||||
wait_forever()
|
||||
}
|
25
src/print.rs
Normal file
25
src/print.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
//! Module implementing the `print!`/`println!` macro.
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use crate::console;
|
||||
|
||||
/// The backend for printing to the console.
|
||||
pub fn _print(args: fmt::Arguments) {
|
||||
console().write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
/// The printing macro.
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
/// The line printing macro.
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
() => ($crate::print!("\n"));
|
||||
($($arg:tt)*) => ({
|
||||
$crate::print::_print(format_args_nl!($($arg)*));
|
||||
})
|
||||
}
|
33
src/traits/console.rs
Normal file
33
src/traits/console.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
//! The trait implemented by console structures.
|
||||
|
||||
use core::fmt;
|
||||
|
||||
/// Trait allowing a structure to be used as a console.
|
||||
pub trait Console: Write {}
|
||||
|
||||
/// Trait allowing to write to an object.
|
||||
pub trait Write {
|
||||
/// Write a single character.
|
||||
fn write_char(&self, c: char);
|
||||
|
||||
/// Write a Rust format string.
|
||||
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
|
||||
|
||||
/// Block until the last buffered character has been consumed.
|
||||
fn flush(&self);
|
||||
}
|
||||
|
||||
/// A Dummy Console.
|
||||
///
|
||||
/// The DummyConsole implement the [`Console`] trait, and do nothing.
|
||||
pub struct DummyConsole;
|
||||
|
||||
impl Write for DummyConsole {
|
||||
fn write_char(&self, _c: char) {}
|
||||
|
||||
fn write_fmt(&self, _args: fmt::Arguments) -> fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
12
src/traits/mod.rs
Normal file
12
src/traits/mod.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
//! This module regroup the traits used accros the projet.
|
||||
//!
|
||||
//! Most of those traits come with a dummy implementation.
|
||||
//! Actual implementations are either in generic modules or
|
||||
//! in target specific modules.
|
||||
//!
|
||||
//! # TODO
|
||||
//!
|
||||
//! How to select the implementation to use?
|
||||
|
||||
pub mod console;
|
||||
pub mod synchronization;
|
117
src/traits/print.rs
Normal file
117
src/traits/print.rs
Normal file
|
@ -0,0 +1,117 @@
|
|||
//! The module implementing the printing features.
|
||||
//!
|
||||
//! For now, the console is implemented here, this may change in the future.
|
||||
//! The console is implemented using the magic qemu output right now.
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use crate::synchronization::PseudoLock;
|
||||
use crate::synchronization::Mutex;
|
||||
|
||||
/// This trait implement the same features as `core::fmt::Write`,
|
||||
/// except it operate on shared references (`&self`) instead of
|
||||
/// mutable references (`&mut self`).
|
||||
pub trait Write {
|
||||
/// Write a single character.
|
||||
fn write_char(&self, c: char);
|
||||
|
||||
/// Write a Rust format string.
|
||||
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
|
||||
|
||||
/// Block until the last buffered character has been physically put on the TX wire.
|
||||
fn flush(&self);
|
||||
}
|
||||
|
||||
/// Inner Qemu output.
|
||||
struct QemuOutputInner;
|
||||
|
||||
/// Qemu output, access to the inner ressources is protected by
|
||||
/// a mutex.
|
||||
struct QemuOutput {
|
||||
inner: PseudoLock<QemuOutputInner>,
|
||||
}
|
||||
|
||||
/// The address for the magic qemu output
|
||||
const QEMU_MAGIC_OUTPUT_ADDR: *mut u8 = 0x3F20_1000 as *mut u8;
|
||||
|
||||
static QEMU_OUTPUT: QemuOutput = QemuOutput::new();
|
||||
|
||||
impl QemuOutputInner {
|
||||
/// Constructor for QemuOutputInner.
|
||||
const fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
/// Write a character to the output.
|
||||
fn write_char(&mut self, c: char) {
|
||||
unsafe {
|
||||
core::ptr::write_volatile(QEMU_MAGIC_OUTPUT_ADDR, c as u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl QemuOutput {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
inner: PseudoLock::new(QemuOutputInner::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow to use QemuOutputInner for print! and formating macros.
|
||||
/// `write_str` needs `&mut self` (mutable ref), so we can implement
|
||||
/// it only on the inner type, the `QemuOutput` need to be manipulable
|
||||
/// using unmutable references (`&self`), so we will use a custom
|
||||
/// interface for it.
|
||||
impl fmt::Write for QemuOutputInner {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for c in s.chars() {
|
||||
// \n -> \r\n
|
||||
if c == '\n' {
|
||||
self.write_char('\r');
|
||||
}
|
||||
self.write_char(c);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for QemuOutput {
|
||||
fn write_char(&self, c: char) {
|
||||
self.inner.lock(|q_out| q_out.write_char(c))
|
||||
}
|
||||
|
||||
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result {
|
||||
self.inner.lock(|q_out| fmt::Write::write_fmt(q_out, args))
|
||||
}
|
||||
|
||||
/// Empty function, the qemu uart has no buffering afaik
|
||||
fn flush(&self) {
|
||||
// self.inner.lock(|q_out| q_out.flush())
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a reference to the Qemu Output.
|
||||
pub fn console() -> &'static dyn Write {
|
||||
&QEMU_OUTPUT
|
||||
}
|
||||
|
||||
/// The backend for printing to the console.
|
||||
pub fn _print(args: fmt::Arguments) {
|
||||
console().write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
/// The printing macro.
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
/// The line printing macro.
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
() => ($crate::print!("\n"));
|
||||
($($arg:tt)*) => ({
|
||||
$crate::print::_print(format_args_nl!($($arg)*));
|
||||
})
|
||||
}
|
41
src/traits/synchronization.rs
Normal file
41
src/traits/synchronization.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
//! Traits for syncronization primitives.
|
||||
|
||||
use core::cell::UnsafeCell;
|
||||
|
||||
/// For the duration of the closure `f`, an object implementing this trait
|
||||
/// guarantees exclusive access to the data wrapped by the Mutex object.
|
||||
pub trait Mutex {
|
||||
/// The type of the data wrapped by the mutex.
|
||||
type Data;
|
||||
/// Locks the mutex and grants access to the wrapped data to the closure.
|
||||
fn lock<'a, R>(&'a self, f: impl FnOnce(&'a mut Self::Data) -> R) -> R;
|
||||
}
|
||||
|
||||
/// Dummy Mutex that does not protect agains anythings.
|
||||
/// This in only save in monocore monothread context, without
|
||||
/// interrupt.
|
||||
pub struct DummyMutex<T: ?Sized> {
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized + Send> Send for DummyMutex<T> {}
|
||||
unsafe impl<T: ?Sized + Send> Sync for DummyMutex<T> {}
|
||||
|
||||
impl<T> DummyMutex<T> {
|
||||
/// Constructor for [`DummyMutex`].
|
||||
pub const fn new(data: T) -> Self {
|
||||
// panic!("PseudoMutex is verry unsafe");
|
||||
Self {
|
||||
data: UnsafeCell::new(data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Mutex for DummyMutex<T> {
|
||||
type Data = T;
|
||||
|
||||
fn lock<'a, R>(&'a self, f: impl FnOnce(&'a mut Self::Data) -> R) -> R {
|
||||
let data = unsafe { &mut *self.data.get() };
|
||||
f(data)
|
||||
}
|
||||
}
|
83
src/utils/field.rs
Normal file
83
src/utils/field.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
//! Implement the Field structure.
|
||||
|
||||
/// Structure representing the location of a register.
|
||||
/// This structure does not provide any kind of safety, it
|
||||
/// is only descriptif.
|
||||
///
|
||||
/// Fields represent a continuous set of bits in an alligned
|
||||
/// `u32`. This means that the address is a multiple of 4,
|
||||
/// the offset is < 32, and offset + size <= 32.
|
||||
///
|
||||
/// The constructor of the is class ensure that the field is
|
||||
/// always inside the `u32` at `address`, and that `address`
|
||||
/// is alligned.
|
||||
pub struct Field {
|
||||
/// The address of the discribed field.
|
||||
address: usize,
|
||||
/// The offset of the first bit of the field relative to the
|
||||
/// address.
|
||||
offset: usize,
|
||||
/// The size of the field in bit.
|
||||
size: usize,
|
||||
/// The mask for the field in the `u32` at `address`.
|
||||
mask: u32
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
impl Field {
|
||||
/// Constructor for a Field.
|
||||
/// The field is defined by the `size` bits at `offset` bits after
|
||||
/// `address`. (**`address` is in bytes, not bits**)
|
||||
pub const fn new(address: usize, offset: usize, size: usize) -> Self {
|
||||
// align the address
|
||||
let offset = offset + 8 * (address % 4);
|
||||
let address = address - (address % 4);
|
||||
// make sure the field is in the u32 at address
|
||||
let address = address + 4 * (offset / 32);
|
||||
let offset = offset % 32;
|
||||
// make sure the field does not overlap with the next u32
|
||||
if offset + size > 32 {
|
||||
panic!("A field can not overlap two aligned `u32`");
|
||||
}
|
||||
|
||||
let mask = Self::compute_mask(offset, size);
|
||||
|
||||
Self { address, offset, size, mask }
|
||||
}
|
||||
|
||||
/// Compute mask for the field.
|
||||
const fn compute_mask(offset: usize, size: usize) -> u32 {
|
||||
let mut mask = 0u32;
|
||||
// Const functions don't allow for loops.
|
||||
let mut i = 0;
|
||||
while i < size {
|
||||
mask <<= 1;
|
||||
mask |= 1;
|
||||
i += 1;
|
||||
}
|
||||
mask <<= offset;
|
||||
mask
|
||||
}
|
||||
|
||||
/// Return the address of the `u32` containing the field.
|
||||
pub const fn get_address(&self) -> usize {
|
||||
self.address
|
||||
}
|
||||
|
||||
/// Return the offset of the field in relation to the address, in bits.
|
||||
#[allow(dead_code)]
|
||||
pub const fn get_offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
|
||||
/// Return the size of the field in bit.
|
||||
#[allow(dead_code)]
|
||||
pub const fn get_size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
|
||||
/// Return the mask of the field in the `u32` at `address`.
|
||||
pub const fn get_mask(&self) -> u32 {
|
||||
self.mask
|
||||
}
|
||||
}
|
3
src/utils/mod.rs
Normal file
3
src/utils/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
//! Utility structure an functions.
|
||||
|
||||
pub mod field;
|
Loading…
Reference in a new issue