diff --git a/.gitignore b/.gitignore index 22282d68..3b8736bb 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ Dependencies/ *.wav *.bmp *.img +config.txt ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. @@ -31,7 +32,6 @@ x86/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ -[Bb]in/ [Oo]bj/ [Ll]og/ diff --git a/Cargo.lock b/Cargo.lock index d011b478..3ffd68db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,30 +25,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "baremetal" -version = "3.0.2" -dependencies = [ - "image_inter", - "lib_gb", - "log", -] - [[package]] name = "base64" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" -[[package]] -name = "bcm_host" -version = "3.0.2" -dependencies = [ - "cfg-if", - "libc", - "log", -] - [[package]] name = "bit_field" version = "0.10.1" @@ -141,14 +123,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "6127248204b9aba09a362f6c930ef6a78f2c1b2215f8a7b398c06e1083f17af0" dependencies = [ - "libc", + "js-sys", "num-integer", "num-traits", "time", + "wasm-bindgen", "winapi", ] @@ -178,6 +161,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "common" +version = "3.0.2" +dependencies = [ + "chrono", + "crossbeam-channel", + "fern", + "lib_gb", + "log", +] + [[package]] name = "core-foundation" version = "0.9.1" @@ -493,17 +487,11 @@ dependencies = [ name = "gb" version = "3.0.2" dependencies = [ - "bcm_host", "cfg-if", - "chrono", + "common", "crossbeam-channel", - "fern", - "image_inter", "lib_gb", - "libc", "log", - "nix", - "rppal", "sdl2", "wav", ] @@ -1231,13 +1219,26 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9b1a3d5f46d53f4a3478e2be4a5a5ce5108ea58b100dcd139830eae7f79a3a1" +[[package]] +name = "rpi" +version = "3.0.2" +dependencies = [ + "common", + "crossbeam-channel", + "image_inter", + "lib_gb", + "libc", + "log", + "nix", + "rppal", +] + [[package]] name = "rppal" -version = "0.13.1" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c88c9c6248de4d337747b619d8f671055ef48a87dc21b97998833f189a0bbd4f" +checksum = "612e1a22e21f08a246657c6433fe52b773ae43d07c9ef88ccfc433cc8683caba" dependencies = [ - "lazy_static", "libc", ] diff --git a/Cargo.toml b/Cargo.toml index c17f6ce9..1cacb7d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,8 @@ members = [ "gb", "lib_gb", "image_inter", - "bcm_host", - "baremetal" + "rpi", + "common" ] [workspace.package] diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 00000000..205ff01f --- /dev/null +++ b/Cross.toml @@ -0,0 +1,12 @@ + +[target.armv7-unknown-linux-gnueabihf] +pre-build = [ + "echo deb http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi >> /etc/apt/sources.list", + "touch /etc/apt/sources.list.d/raspi.list", + "echo deb http://archive.raspberrypi.org/debian/ buster main >> /etc/apt/sources.list.d/raspi.list", + "apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 9165938D90FDDD2E", + "apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 82B129927FA3303E", + "dpkg --add-architecture armhf", + "apt-get update", + "apt-get install -y libraspberrypi-dev" +] \ No newline at end of file diff --git a/Makefile.toml b/Makefile.toml new file mode 100644 index 00000000..3f3b91d6 --- /dev/null +++ b/Makefile.toml @@ -0,0 +1,42 @@ +[config] +default_to_workspace = false + +[tasks.all] +dependencies = ["test", "sdl", "rpios", "rpi_baremetal"] + +[tasks.test] +command = "cargo" +args = ["test", "--package", "lib_gb"] + +[tasks.sdl] +command = "cargo" +args = ["build", "--release", "--package", "gb"] + +[tasks.sdl.linux] +args = ["build", "--release", "--package", "gb", "--no-default-features"] +dependencies = ["install_sdl2_linux"] + +[tasks.install_sdl2_linux] +script = ["sudo apt-get install -y libsdl2-dev"] + +[tasks.rpios] +install_crate = {crate_name = "cross", binary = "cross", test_arg = "-h"} +command = "cross" +args = ["build", "--release", "--target", "armv7-unknown-linux-gnueabihf", "--bin", "rpios","--no-default-features", "--features", "std"] + +[tasks.rpi_baremetal] +toolchain = "nightly" +install_crate = "cargo-binutils" +command = "rust-objcopy" +args = ["target/armv7a-none-eabihf/release/baremetal", "-O", "binary", "kernel7.img"] +dependencies = ["build_rpi_baremetal","install_llvm_tools"] + +[tasks.build_rpi_baremetal] +toolchain = "nightly" +cwd = "./rpi/" +command = "cargo" +args = ["build", "--release", "--target", "armv7a-none-eabihf","--package", "rpi", "--bin", "baremetal", "-Z", "build-std=core", "--no-default-features", "--features", "rpi4"] + +[tasks.install_llvm_tools] +toolchain = "nightly" +install_crate = {rustup_component_name = "llvm-tools-preview"} \ No newline at end of file diff --git a/README.md b/README.md index 5b112db9..12a64fe7 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,20 @@ The main goal of this project is to be able to play Pokemon on my own emulator. ## Building +Install `cargo-make` +```sh +cargo install cargo-make +``` +verify you have docker or podman installed + ### Desktop +```sh +cargo make sdl +``` + +or with more configuration options: + ```shell cargo build --release --package gb --features [optional_features] ``` @@ -47,6 +59,12 @@ On by default ### Raspberry Pi Baremetal (with ili9341 display and gpio buttons) +```sh +cargo make rpi_baremetal +``` + +or manually: + 1. Install the rust nightly toolchain for `armv7a-none-eabihf`: ```shell rustup target add armv7a-none-eabihf --toolchain nightly diff --git a/baremetal/.cargo/config.toml b/baremetal/.cargo/config.toml deleted file mode 100644 index ba6123a2..00000000 --- a/baremetal/.cargo/config.toml +++ /dev/null @@ -1,6 +0,0 @@ -[build] -target = "armv7a-none-eabihf" -rustflags = [ - "-Clink-arg=--script=./baremetal/link.ld", - "-Ctarget-feature=+virtualization" -] \ No newline at end of file diff --git a/baremetal/Cargo.toml b/baremetal/Cargo.toml deleted file mode 100644 index 7fd40e9f..00000000 --- a/baremetal/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "baremetal" -version.workspace = true -authors.workspace = true -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -log = "0.4" -lib_gb = {path = "../lib_gb", features = ["u16pixel"]} -image_inter = {path = "../image_inter"} - -[features] -default = ["rpi4"] -rpi4 = [] -rpi2 = [] \ No newline at end of file diff --git a/baremetal/build.bat b/baremetal/build.bat deleted file mode 100644 index 9119f64b..00000000 --- a/baremetal/build.bat +++ /dev/null @@ -1,2 +0,0 @@ -cargo +nightly b -r -Z build-std=core -rust-objcopy ../target/armv7a-none-eabihf/release/baremetal -O binary kernel7.img \ No newline at end of file diff --git a/baremetal/build.rs b/baremetal/build.rs deleted file mode 100644 index 1b848f40..00000000 --- a/baremetal/build.rs +++ /dev/null @@ -1,5 +0,0 @@ -const LD_SCRIPT_PATH:&str = "link.ld"; - -fn main(){ - println!("cargo:rerun-if-changed={}", LD_SCRIPT_PATH); -} \ No newline at end of file diff --git a/baremetal/build.sh b/baremetal/build.sh deleted file mode 100644 index 9faf2e4b..00000000 --- a/baremetal/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -# armv7a-none-eabihf is not supported automacticaly by rust so the nightly toolchain is neccessary to build the core library -cargo +nightly b -r -Z build-std=core -rust-objcopy ../target/armv7a-none-eabihf/release/baremetal -O binary kernel7.img \ No newline at end of file diff --git a/baremetal/config.txt b/baremetal/config.txt deleted file mode 100644 index e4b6be5e..00000000 --- a/baremetal/config.txt +++ /dev/null @@ -1,7 +0,0 @@ -# configuration for the RPI -arm_64bit=0 # boot to 32 bit mode - -# fast boot -boot_delay=0 -disable_poe_fan=1 -disable_splash=1 \ No newline at end of file diff --git a/baremetal/src/drivers/gpio_joypad.rs b/baremetal/src/drivers/gpio_joypad.rs deleted file mode 100644 index 0aa33eaa..00000000 --- a/baremetal/src/drivers/gpio_joypad.rs +++ /dev/null @@ -1,42 +0,0 @@ -use lib_gb::keypad::{joypad::{Joypad, NUM_OF_KEYS},joypad_provider::JoypadProvider, button::Button}; - -use crate::peripherals::{PERIPHERALS, GpioPin, Mode, GpioPull}; - -const READ_THRESHOLD:u32 = 0x1000; - -pub struct GpioJoypadProvider{ - input_pins: [GpioPin; NUM_OF_KEYS], - read_threshold_counter: u32 -} - -impl GpioJoypadProvider{ - pub fn new(mapper:impl Fn(Button)->u8)->Self{ - let gpio = unsafe{PERIPHERALS.get_gpio()}; - let mut input_pins = [ - gpio.take_pin(mapper(Button::A), Mode::Input), - gpio.take_pin(mapper(Button::B), Mode::Input), - gpio.take_pin(mapper(Button::Start), Mode::Input), - gpio.take_pin(mapper(Button::Select), Mode::Input), - gpio.take_pin(mapper(Button::Up), Mode::Input), - gpio.take_pin(mapper(Button::Down), Mode::Input), - gpio.take_pin(mapper(Button::Right), Mode::Input), - gpio.take_pin(mapper(Button::Left), Mode::Input), - ]; - for i in &mut input_pins{ - i.set_pull(GpioPull::None); - } - return Self { input_pins, read_threshold_counter: 0 }; - } -} - -impl JoypadProvider for GpioJoypadProvider{ - fn provide(&mut self, joypad:&mut Joypad){ - self.read_threshold_counter = (self.read_threshold_counter + 1) % READ_THRESHOLD; - if self.read_threshold_counter != 0 { - return; - } - for i in 0..joypad.buttons.len(){ - joypad.buttons[i] = self.input_pins[i].read_state(); - } - } -} \ No newline at end of file diff --git a/baremetal/src/peripherals/gpio.rs b/baremetal/src/peripherals/gpio.rs deleted file mode 100644 index 21e11e03..00000000 --- a/baremetal/src/peripherals/gpio.rs +++ /dev/null @@ -1,128 +0,0 @@ -use crate::syncronization::Mutex; - -use super::{PERIPHERALS_BASE_ADDRESS, utils::{MmioReg32, compile_time_size_assert, memory_barrier}}; - -#[repr(C,align(4))] -struct GpioRegisters{ - gpfsel:[MmioReg32;6], - _pad0:u32, - gpset:[MmioReg32;2], - _pad1:u32, - gpclr:[MmioReg32;2], - _pad2:u32, - pglev:[MmioReg32;2], - _pad3:[u32;42], - gpio_pup_pdn_cntrl:[MmioReg32;4] -} -compile_time_size_assert!(GpioRegisters, 0xF4); - -#[repr(u8)] -#[derive(Clone, Copy, PartialEq)] -pub enum Mode{ - Input = 0, - Output = 1, - Alt0 = 4, - Alt5 = 2 -} - -pub enum GpioPull{ - None = 0, - _PullUp = 0b01, -} - - -const RPI4_GPIO_PINS_COUNT:usize = 58; -const BASE_GPIO_ADDRESS: usize = PERIPHERALS_BASE_ADDRESS + 0x20_0000; -static mut GPIO_REGISTERS:Option> = None; - -pub struct GpioManager{ - pins_availability:[bool;RPI4_GPIO_PINS_COUNT] -} - -impl GpioManager{ - pub(super) fn new()->GpioManager{ - unsafe{GPIO_REGISTERS = Some(Mutex::new(&mut *(BASE_GPIO_ADDRESS as *mut GpioRegisters)));} - GpioManager { pins_availability: [true;RPI4_GPIO_PINS_COUNT]} - } - - pub fn take_pin(&mut self, bcm_pin_number:u8, mode:Mode)->GpioPin{ - if self.pins_availability[bcm_pin_number as usize]{ - self.pins_availability[bcm_pin_number as usize] = false; - return GpioPin::new(bcm_pin_number, mode); - } - core::panic!("Pin {} is already taken", bcm_pin_number); - } -} - -pub struct GpioPin{ - mode:Mode, - registers:&'static mut Mutex<&'static mut GpioRegisters>, - bcm_pin_number:u8, -} - -impl GpioPin{ - pub(super) fn new(bcm_pin_number:u8, mode:Mode)->Self{ - let registers = unsafe{GPIO_REGISTERS.as_mut().unwrap()}; - let mut pin = Self{mode:Mode::Input, registers, bcm_pin_number}; - pin.set_mode(mode); - return pin; - } - - pub fn set_mode(&mut self, mode:Mode){ - let gpfsel_register = (self.bcm_pin_number / 10) as usize; // each registers contains 10 pins - let gpfsel_register_offset = (self.bcm_pin_number % 10) * 3; // each pin take 3 bits in the register - memory_barrier(); - self.registers.lock(|r|{ - let mut register_value = r.gpfsel[gpfsel_register].read(); - register_value &= !(0b111 << gpfsel_register_offset); // clear the specific bits - r.gpfsel[gpfsel_register].write(register_value | (mode as u32) << gpfsel_register_offset) - }); - memory_barrier(); - self.mode = mode; - } - - pub fn set_state(&mut self, state:bool){ - debug_assert!(self.mode == Mode::Output); - let register = self.bcm_pin_number / 32; // since each registers contains 32 pins - let value = 1 << (self.bcm_pin_number % 32); // get the position in the register - memory_barrier(); - if state{ - self.registers.lock(|r|r.gpset[register as usize].write(value)); - } - else{ - self.registers.lock(|r|r.gpclr[register as usize].write(value)); - } - memory_barrier(); - } - - pub fn read_state(&self)->bool{ - debug_assert!(self.mode == Mode::Input); - - let gplev_register = self.bcm_pin_number / 32; // since each registers contains 32 pins - let mask = 1 << (self.bcm_pin_number % 32); // get the position in the register - memory_barrier(); - let value = self.registers.lock(|r|r.pglev[gplev_register as usize].read()); - memory_barrier(); - return value & mask != 0; - } - - pub fn set_pull(&mut self, pull_mode:GpioPull){ - let register_index = self.bcm_pin_number / 16; - let offset = (self.bcm_pin_number % 16) * 2; - let mask:u32 = 0b11 << offset; - memory_barrier(); - let register_value = self.registers.lock(|r|r.gpio_pup_pdn_cntrl[register_index as usize].read()); - let new_value = (register_value & !mask) | ((pull_mode as u32) << offset as u32); - self.registers.lock(|r|r.gpio_pup_pdn_cntrl[register_index as usize].write(new_value)); - memory_barrier(); - } - - // Sugar syntax functions - pub fn set_high(&mut self){ - self.set_state(true) - } - - pub fn set_low(&mut self){ - self.set_state(false); - } -} \ No newline at end of file diff --git a/baremetal/src/peripherals/mailbox.rs b/baremetal/src/peripherals/mailbox.rs deleted file mode 100644 index 17f0bd51..00000000 --- a/baremetal/src/peripherals/mailbox.rs +++ /dev/null @@ -1,128 +0,0 @@ -// This module is wrriten using the following sources -// Linux kernel for the rpi version 4.9 as a referrence - https://github.com/raspberrypi/linux/blob/2aae4cc63e30f99dd152fc63cc4a67ca29e4647b/drivers/firmware/raspberrypi.c -// This tutorial - https://www.rpi4os.com/part5-framebuffer/ -// The official mailbox docs - https://github.com/raspberrypi/firmware/wiki/Mailboxes - -//Turns out this peripheral needs a non cached memory - -use core::mem::size_of; - -use super::{PERIPHERALS_BASE_ADDRESS, utils::{compile_time_size_assert, MmioReg32, memory_barrier}}; - -const MBOX_BASE_ADDRESS:usize = PERIPHERALS_BASE_ADDRESS + 0xB880; -const MBOX_CHANNEL:u32 = 8; // free channel for communication from the cpu to the core - -#[repr(C, align(4))] -struct PropertyTagHeader{ - tag:u32, - buffer_size:u32, - - // On submit, the length of the request (though it doesn't appear to be currently used by the firmware). - // On return, the length of the response (always 4 byte aligned), with the low bit set. - request_response_size:u32 -} - -#[repr(u32)] -#[derive(Clone, Copy, PartialEq)] -enum PropertyStatus{ - Request = 0, - Success = 0x8000_0000, - _Error = 0x8000_0001 -} -compile_time_size_assert!(PropertyStatus, 4); - -// The 28 first bits are the address but the last 4 bits represnt the mailbox channel -// by aligning to 16 the last 4 bit are always 0 -#[repr(C, align(16))] -struct Message{ - size:u32, - status:PropertyStatus, - tag_header:PropertyTagHeader, - data:[u32;DATA_LEN], - property_end_marker:u32 -} - -impl Message{ - const MBOX_PROPERTY_END:u32 = 0; - - fn new(tag:u32, data:[u32;DATA_LEN])->Self{ - Self{ - size: size_of::() as u32, - status: PropertyStatus::Request, - tag_header: PropertyTagHeader { - tag, - buffer_size: (size_of::() * DATA_LEN) as u32, - request_response_size: 0 // zero since unused by the firmare - }, - data, - property_end_marker: Self::MBOX_PROPERTY_END - } - } -} - -#[repr(C, align(4))] -struct MailboxRegisters{ - read_reg:MmioReg32, - _res:[u32;5], - status:MmioReg32, - _res1:u32, - write_reg:MmioReg32, -} -compile_time_size_assert!(MailboxRegisters, 0x24); - -const MAILBOX_BUFFER_SIZE:usize = 0x100; -#[repr(align(16))] -struct MailboxBuffer([u8;MAILBOX_BUFFER_SIZE]); -#[no_mangle] -#[link_section = ".uncached"] -static mut MAILBOX_UNCACHED_BUFFER:MailboxBuffer = MailboxBuffer([0;MAILBOX_BUFFER_SIZE]); - -pub struct Mailbox{ - registers:&'static mut MailboxRegisters, - uncached_buffer:&'static mut [u8] -} - -impl Mailbox{ - const STATUS_EMPTY:u32 = 0x4000_0000; - const STATUS_FULL:u32 = 0x8000_0000; - - pub(super) fn new()->Mailbox{ - let registers = unsafe{&mut *(MBOX_BASE_ADDRESS as *mut MailboxRegisters)}; - let uncached_buffer:&'static mut [u8] = unsafe{&mut MAILBOX_UNCACHED_BUFFER.0}; - return Mailbox { registers, uncached_buffer }; - } - - pub fn call(&mut self, tag:u32, data:[u32;DATA_LEN])->[u32;DATA_LEN]{ - if size_of::>() > self.uncached_buffer.len() { - core::panic!("Error, Message with data len of {} bytes is too large ({}) and cant fit a {} bytes buffer", - DATA_LEN, size_of::>(), self.uncached_buffer.len()); - } - - let uncached_message = unsafe{ - let message = Message::new(tag, data); - core::ptr::copy_nonoverlapping(&message as *const Message as *const u8, self.uncached_buffer.as_mut_ptr(), size_of::>()); - &*(self.uncached_buffer.as_ptr() as *const Message) - }; - let mut message_address = (self.uncached_buffer.as_ptr()) as *const Message as u32; - if message_address & 0xF != 0{ - core::panic!("Error! mbox message is not alligned for 16 bytes") - } - message_address += MBOX_CHANNEL; - - memory_barrier(); - while self.registers.status.read() & Self::STATUS_FULL != 0{} // blocks untill mbox is avaliable - self.registers.write_reg.write(message_address); - - loop{ - while self.registers.status.read() & Self::STATUS_EMPTY != 0{} // block untill there is a response (non empty mailbox) - if self.registers.read_reg.read() == message_address{ - memory_barrier(); - if uncached_message.status == PropertyStatus::Success{ - return uncached_message.data; - } - core::panic!("Error in mbox call! tag: {:#X}, req_data: {:?}, status: {:#X}, res_data: {:?}", - tag, data, uncached_message.status as u32, uncached_message.data); - } - } - } -} \ No newline at end of file diff --git a/bcm_host/build.rs b/bcm_host/build.rs deleted file mode 100644 index 974e86a6..00000000 --- a/bcm_host/build.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - // Checking for rpi - #[cfg(all(target_os = "linux", target_arch = "arm"))] - println!("cargo:rustc-link-lib=bcm_host"); -} \ No newline at end of file diff --git a/bcm_host/src/lib.rs b/bcm_host/src/lib.rs deleted file mode 100644 index 68fb6980..00000000 --- a/bcm_host/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Checking for rpi -cfg_if::cfg_if!{ if #[cfg(all(target_os = "linux", target_arch = "arm"))]{ - pub mod bcm; - pub use bcm::BcmHost; -}} \ No newline at end of file diff --git a/bcm_host/Cargo.toml b/common/Cargo.toml similarity index 67% rename from bcm_host/Cargo.toml rename to common/Cargo.toml index ffbc54b3..72dc98a2 100644 --- a/bcm_host/Cargo.toml +++ b/common/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "bcm_host" +name = "common" version.workspace = true authors.workspace = true edition = "2021" @@ -7,6 +7,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -libc = "0.2" +lib_gb = {path = "../lib_gb"} +crossbeam-channel = "0.5" log = "0.4" -cfg-if = "1.0" \ No newline at end of file +fern = "0.6" +chrono = "0.4" \ No newline at end of file diff --git a/gb/src/emulation_menu.rs b/common/src/emulation_menu.rs similarity index 100% rename from gb/src/emulation_menu.rs rename to common/src/emulation_menu.rs diff --git a/gb/src/joypad_menu/font.rs b/common/src/joypad_menu/font.rs similarity index 100% rename from gb/src/joypad_menu/font.rs rename to common/src/joypad_menu/font.rs diff --git a/gb/src/joypad_menu/joypad_gfx_menu.rs b/common/src/joypad_menu/joypad_gfx_menu.rs similarity index 100% rename from gb/src/joypad_menu/joypad_gfx_menu.rs rename to common/src/joypad_menu/joypad_gfx_menu.rs diff --git a/gb/src/joypad_menu/mod.rs b/common/src/joypad_menu/mod.rs similarity index 100% rename from gb/src/joypad_menu/mod.rs rename to common/src/joypad_menu/mod.rs diff --git a/common/src/lib.rs b/common/src/lib.rs new file mode 100644 index 00000000..5c996899 --- /dev/null +++ b/common/src/lib.rs @@ -0,0 +1,52 @@ +pub mod emulation_menu; +pub mod joypad_menu; +pub mod mbc_handler; +pub mod mpmc_gfx_device; + +use std::path::PathBuf; + +use joypad_menu::*; +use lib_gb::keypad::joypad_provider::JoypadProvider; + +pub const VERSION:&str = env!("CARGO_PKG_VERSION"); + +pub fn get_rom_selection, JP:MenuJoypadProvider + JoypadProvider>(roms_path:&str, menu_renderer:MR, jp:&mut JP)->String{ + let mut menu_options = Vec::new(); + let dir_entries = std::fs::read_dir(roms_path).expect(std::format!("Error openning the roms directory: {}",roms_path).as_str()); + for entry in dir_entries{ + let entry = entry.unwrap(); + let path = entry.path(); + if let Some(extension) = path.as_path().extension().and_then(std::ffi::OsStr::to_str){ + match extension { + "gb" | "gbc"=>{ + let filename = String::from(path.file_name().expect("Error should be a file").to_str().unwrap()); + let option = MenuOption{value: path, prompt: filename}; + menu_options.push(option); + }, + _=>{} + } + } + } + let mut menu = JoypadMenu::new(&menu_options, String::from("Choose ROM"), menu_renderer); + let result = menu.get_menu_selection(jp); + + return String::from(result.to_str().unwrap()); +} + +pub fn init_fern_logger()->Result<(), fern::InitError>{ + let fern_logger = fern::Dispatch::new() + .format(|out, message, record| { + out.finish(format_args!( + "{}[{}] {}", + chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S.%f]"), + record.level(), + message + )) + }) + .chain(std::io::stdout()) + .chain(fern::log_file("output.log")?); + + fern_logger.apply()?; + + Ok(()) +} \ No newline at end of file diff --git a/gb/src/mbc_handler.rs b/common/src/mbc_handler.rs similarity index 100% rename from gb/src/mbc_handler.rs rename to common/src/mbc_handler.rs diff --git a/gb/src/mpmc_gfx_device.rs b/common/src/mpmc_gfx_device.rs similarity index 100% rename from gb/src/mpmc_gfx_device.rs rename to common/src/mpmc_gfx_device.rs diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 0d53b020..626f8091 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -11,27 +11,17 @@ name = "magenboy" path = "src/main.rs" [dependencies] -lib_gb = {path = "../lib_gb/"} -image_inter = {path = "../image_inter", optional = true} -bcm_host = {path = "../bcm_host", optional = true} +lib_gb = {path = "../lib_gb/", features = ["apu"]} +common = {path = "../common/"} log = {version = "0.4", features = ["max_level_debug", "release_max_level_info"]} -fern = "0.6" -chrono = "0.4" -sdl2 = {version = "0.35", optional = true} -wav = {version = "1.0", optional = true} +sdl2 = "0.35" crossbeam-channel = "0.5" cfg-if = "1.0" -rppal = {version = "0.13", optional = true} -libc = {version = "0.2", optional = true} -nix = {version = "0.24", optional = true} +wav = "1.0" [features] -default = ["static-sdl", "apu"] -sdl = ["sdl2"] -sdl-resample = ["apu"] -push-audio = ["apu"] -static-sdl = ["sdl", "sdl2/bundled", "sdl2/static-link"] -u16pixel = ["lib_gb/u16pixel"] -apu = ["lib_gb/apu", "sdl", "wav"] -rpi = ["rppal", "u16pixel", "image_inter", "nix/signal"] -mmio = ["rpi", "nix/ioctl", "libc", "bcm_host"] # requires sudo \ No newline at end of file +default = ["static-sdl"] +sdl-resample = [] +push-audio = [] +static-sdl = ["sdl2/bundled", "sdl2/static-link"] +u16pixel = ["lib_gb/u16pixel"] \ No newline at end of file diff --git a/gb/src/audio/mod.rs b/gb/src/audio/mod.rs new file mode 100644 index 00000000..41345bda --- /dev/null +++ b/gb/src/audio/mod.rs @@ -0,0 +1,9 @@ +mod audio_resampler; +mod manual_audio_resampler; +mod multi_device_audio; +mod wav_file_audio_device; + +pub use audio_resampler::*; +pub use manual_audio_resampler::ManualAudioResampler; +pub use multi_device_audio::MultiAudioDevice; +pub use wav_file_audio_device::WavfileAudioDevice; \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index 95ad2c5e..ca278808 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -1,26 +1,10 @@ -mod mbc_handler; -mod mpmc_gfx_device; -mod joypad_menu; -mod emulation_menu; - -#[cfg(feature = "rpi")] -mod rpi_gpio; -mod audio{ - pub mod audio_resampler; - pub mod multi_device_audio; - #[cfg(feature = "apu")] - pub mod wav_file_audio_device; - #[cfg(not(feature = "sdl-resample"))] - pub mod manual_audio_resampler; -} -#[cfg(feature = "sdl")] +mod audio; mod sdl{ pub mod utils; pub mod sdl_gfx_device; #[cfg(feature = "sdl-resample")] pub mod sdl_audio_resampler; - #[cfg(feature = "apu")] cfg_if::cfg_if!{ if #[cfg(feature = "push-audio")]{ pub mod sdl_push_audio_device; @@ -31,7 +15,6 @@ mod sdl{ pub type ChosenAudioDevice = sdl_pull_audio_device::SdlPullAudioDevice; } } - #[cfg(not(feature = "rpi"))] pub mod sdl_joypad_provider; } @@ -40,78 +23,36 @@ cfg_if::cfg_if!{ pub type ChosenResampler = sdl::sdl_audio_resampler::SdlAudioResampler; } else{ - pub type ChosenResampler = audio::manual_audio_resampler::ManualAudioResampler; + pub type ChosenResampler = crate::audio::ManualAudioResampler; } } -use crate::{audio::multi_device_audio::*, mbc_handler::*, mpmc_gfx_device::MpmcGfxDevice, emulation_menu::MagenBoyMenu}; -use emulation_menu::MagenBoyState; -use joypad_menu::{JoypadMenu, MenuOption, MenuRenderer}; +use crate::audio::*; +use common::{emulation_menu::*, joypad_menu::*, mbc_handler::*, mpmc_gfx_device::*}; use lib_gb::{keypad::button::Button, apu::audio_device::*, machine::{gameboy::GameBoy, Mode}, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::{GfxDevice, Pixel}}, mmu::{GBC_BOOT_ROM_SIZE, external_memory_bus::Bootrom, GB_BOOT_ROM_SIZE}}; -use std::{fs, env, result::Result, vec::Vec, path::PathBuf, convert::TryInto}; +use std::{fs, env, result::Result, vec::Vec, convert::TryInto}; use log::info; -cfg_if::cfg_if! {if #[cfg(feature = "apu")]{ - use lib_gb::GB_FREQUENCY; - use crate::audio::audio_resampler::ResampledAudioDevice; -}} -#[cfg(feature = "sdl")] +use lib_gb::GB_FREQUENCY; use sdl2::sys::*; const TURBO_MUL:u8 = 1; -cfg_if::cfg_if!{ if #[cfg(feature = "rpi")] { - const RESET_PIN_BCM:u8 = 14; - const DC_PIN_BCM:u8 = 15; - const LED_PIN_BCM:u8 = 25; - const MENU_PIN_BCM:u8 = 3; // This pin is the turn on pin - use crate::rpi_gpio::gpio_joypad_provider::*; - fn buttons_mapper(button:&Button)->GpioBcmPin{ - match button{ - Button::A => 18, - Button::B => 17, - Button::Start => 22, - Button::Select => 23, - Button::Up => 19, - Button::Down => 16, - Button::Right => 20, - Button::Left => 21 - } +const SCREEN_SCALE:usize = 4; +use sdl2::sys::SDL_Scancode; +fn buttons_mapper(button:&Button)->SDL_Scancode{ + match button{ + Button::A => SDL_Scancode::SDL_SCANCODE_X, + Button::B => SDL_Scancode::SDL_SCANCODE_Z, + Button::Start => SDL_Scancode::SDL_SCANCODE_S, + Button::Select => SDL_Scancode::SDL_SCANCODE_A, + Button::Up => SDL_Scancode::SDL_SCANCODE_UP, + Button::Down => SDL_Scancode::SDL_SCANCODE_DOWN, + Button::Right => SDL_Scancode::SDL_SCANCODE_RIGHT, + Button::Left => SDL_Scancode::SDL_SCANCODE_LEFT } -} else if #[cfg(feature = "sdl")] { - const SCREEN_SCALE:usize = 4; - use sdl2::sys::SDL_Scancode; - fn buttons_mapper(button:&Button)->SDL_Scancode{ - match button{ - Button::A => SDL_Scancode::SDL_SCANCODE_X, - Button::B => SDL_Scancode::SDL_SCANCODE_Z, - Button::Start => SDL_Scancode::SDL_SCANCODE_S, - Button::Select => SDL_Scancode::SDL_SCANCODE_A, - Button::Up => SDL_Scancode::SDL_SCANCODE_UP, - Button::Down => SDL_Scancode::SDL_SCANCODE_DOWN, - Button::Right => SDL_Scancode::SDL_SCANCODE_RIGHT, - Button::Left => SDL_Scancode::SDL_SCANCODE_LEFT - } - } -}} - -fn init_logger()->Result<(), fern::InitError>{ - let fern_logger = fern::Dispatch::new() - .format(|out, message, record| { - out.finish(format_args!( - "{}[{}] {}", - chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S.%f]"), - record.level(), - message - )) - }) - .chain(std::io::stdout()) - .chain(fern::log_file("output.log")?); - - fern_logger.apply()?; - - Ok(()) } + fn check_for_terminal_feature_flag(args:&Vec::, flag:&str)->bool{ args.len() >= 3 && args.contains(&String::from(flag)) } @@ -121,75 +62,36 @@ fn get_terminal_feature_flag_value(args:&Vec, flag:&str, error_message:& return args.get(index + 1).expect(error_message).clone(); } -fn get_rom_selection>(roms_path:&str, menu_renderer:MR)->String{ - let mut menu_options = Vec::new(); - let dir_entries = std::fs::read_dir(roms_path).expect(std::format!("Error openning the roms directory: {}",roms_path).as_str()); - for entry in dir_entries{ - let entry = entry.unwrap(); - let path = entry.path(); - if let Some(extension) = path.as_path().extension().and_then(std::ffi::OsStr::to_str){ - match extension { - "gb" | "gbc"=>{ - let filename = String::from(path.file_name().expect("Error should be a file").to_str().unwrap()); - let option = MenuOption{value: path, prompt: filename}; - menu_options.push(option); - }, - _=>{} - } - } - } - cfg_if::cfg_if!{if #[cfg(feature = "rpi")]{ - let mut provider = GpioJoypadProvider::new(buttons_mapper); - } - else{ - let mut provider = sdl::sdl_joypad_provider::SdlJoypadProvider::new(buttons_mapper); - }} - let mut menu = JoypadMenu::new(&menu_options, String::from("Choose ROM"), menu_renderer); - let result = menu.get_menu_selection(&mut provider); - - return String::from(result.to_str().unwrap()); -} - // This is static and not local for the unix signal handler to access it static EMULATOR_STATE:MagenBoyState = MagenBoyState::new(); -const VERSION:&str = env!("CARGO_PKG_VERSION"); - fn main() { - let header = std::format!("MagenBoy v{}", VERSION); + let header = std::format!("MagenBoy v{}", common::VERSION); let args: Vec = env::args().collect(); - match init_logger(){ + match common::init_fern_logger(){ Result::Ok(())=>{}, Result::Err(error)=>std::panic!("error initing logger: {}", error) } // Initialize the gfx first cause it initialize both the screen and the sdl context for the joypad - cfg_if::cfg_if!{ if #[cfg(feature = "rpi")]{ - let mut gfx_device:rpi_gpio::ili9341_controller::Ili9341GfxDevice = rpi_gpio::ili9341_controller::Ili9341GfxDevice::new(RESET_PIN_BCM, DC_PIN_BCM, LED_PIN_BCM, TURBO_MUL, 0); - }else{ - let mut gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new(header.as_str(), SCREEN_SCALE, TURBO_MUL, - check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); - }} - - cfg_if::cfg_if!{if #[cfg(feature = "rpi")]{ - let provider = GpioJoypadProvider::new(buttons_mapper); - } - else{ - let provider = sdl::sdl_joypad_provider::SdlJoypadProvider::new(buttons_mapper); - }} - let mut emulation_menu = MagenBoyMenu::new(provider, header); + let mut gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new(header.as_str(), SCREEN_SCALE, TURBO_MUL, + check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); while !(EMULATOR_STATE.exit.load(std::sync::atomic::Ordering::Relaxed)){ + let mut provider = sdl::sdl_joypad_provider::SdlJoypadProvider::new(buttons_mapper); + let program_name = if check_for_terminal_feature_flag(&args, "--rom-menu"){ let roms_path = get_terminal_feature_flag_value(&args, "--rom-menu", "Error! no roms folder specified"); - let menu_renderer = joypad_menu::joypad_gfx_menu::GfxDeviceMenuRenderer::new(&mut gfx_device); - get_rom_selection(roms_path.as_str(), menu_renderer) + let menu_renderer = joypad_gfx_menu::GfxDeviceMenuRenderer::new(&mut gfx_device); + common::get_rom_selection(roms_path.as_str(), menu_renderer, &mut provider) } else{ args[1].clone() }; + let mut emulation_menu = MagenBoyMenu::new(provider, header.clone()); + let (s,r) = crossbeam_channel::bounded(BUFFERS_NUMBER - 1); let mpmc_device = MpmcGfxDevice::new(s); @@ -199,31 +101,19 @@ fn main() { ).unwrap(); unsafe{ - cfg_if::cfg_if!{ if #[cfg(feature = "rpi")]{ - let handler = nix::sys::signal::SigHandler::Handler(sigint_handler); - nix::sys::signal::signal(nix::sys::signal::Signal::SIGINT, handler).unwrap(); - let menu_pin = rppal::gpio::Gpio::new().unwrap().get(MENU_PIN_BCM).unwrap().into_input_pullup(); - } else if #[cfg(feature = "sdl")]{ - let mut event: std::mem::MaybeUninit = std::mem::MaybeUninit::uninit(); - }} + let mut event: std::mem::MaybeUninit = std::mem::MaybeUninit::uninit(); loop{ - cfg_if::cfg_if!{ if #[cfg(feature = "sdl")]{ - if SDL_PollEvent(event.as_mut_ptr()) != 0{ - let event: SDL_Event = event.assume_init(); - if event.type_ == SDL_EventType::SDL_QUIT as u32{ - EMULATOR_STATE.exit.store(true, std::sync::atomic::Ordering::Relaxed); - break; - } - else if event.type_ == SDL_EventType::SDL_KEYDOWN as u32 && event.key.keysym.scancode == SDL_Scancode::SDL_SCANCODE_ESCAPE{ - emulation_menu.pop_game_menu(&EMULATOR_STATE, &mut gfx_device, r.clone()); - } + if SDL_PollEvent(event.as_mut_ptr()) != 0{ + let event: SDL_Event = event.assume_init(); + if event.type_ == SDL_EventType::SDL_QUIT as u32{ + EMULATOR_STATE.exit.store(true, std::sync::atomic::Ordering::Relaxed); + break; } - } else if #[cfg(feature = "rpi")]{ - if menu_pin.is_low(){ + else if event.type_ == SDL_EventType::SDL_KEYDOWN as u32 && event.key.keysym.scancode == SDL_Scancode::SDL_SCANCODE_ESCAPE{ emulation_menu.pop_game_menu(&EMULATOR_STATE, &mut gfx_device, r.clone()); } - }} + } match r.recv() { Result::Ok(buffer) => gfx_device.swap_buffer(&*(buffer as *const [Pixel; SCREEN_WIDTH * SCREEN_HEIGHT])), @@ -239,49 +129,24 @@ fn main() { drop(gfx_device); - #[cfg(feature = "sdl")] unsafe{SDL_Quit();} - - #[cfg(feature = "rpi")] - if check_for_terminal_feature_flag(&args, "--shutdown-rpi"){ - log::info!("Shuting down the RPi! Goodbye"); - std::process::Command::new("shutdown").arg("-h").arg("now").spawn().expect("Failed to shutdown system"); - } -} - -#[cfg(feature = "rpi")] -extern "C" fn sigint_handler(_:std::os::raw::c_int){ - EMULATOR_STATE.running.store(false, std::sync::atomic::Ordering::Relaxed); - EMULATOR_STATE.exit.store(true, std::sync::atomic::Ordering::Relaxed); } // Receiving usize and not raw ptr cause in rust you cant pass a raw ptr to another thread fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_device: MpmcGfxDevice) { - cfg_if::cfg_if!{ - if #[cfg(feature = "apu")]{ - let mut devices: Vec::> = Vec::new(); - let audio_device = sdl::ChosenAudioDevice::::new(44100, TURBO_MUL); - devices.push(Box::new(audio_device)); - - if check_for_terminal_feature_flag(&args, "--file-audio"){ - let wav_ad = audio::wav_file_audio_device::WavfileAudioDevice::::new(44100, GB_FREQUENCY, "output.wav"); - devices.push(Box::new(wav_ad)); - log::info!("Writing audio to file: output.wav"); - } - } - else{ - let devices: Vec::> = Vec::new(); - } + let mut devices: Vec::> = Vec::new(); + let audio_device = sdl::ChosenAudioDevice::::new(44100, TURBO_MUL); + devices.push(Box::new(audio_device)); + + if check_for_terminal_feature_flag(&args, "--file-audio"){ + let wav_ad = WavfileAudioDevice::::new(44100, GB_FREQUENCY, "output.wav"); + devices.push(Box::new(wav_ad)); + log::info!("Writing audio to file: output.wav"); } + let audio_devices = MultiAudioDevice::new(devices); - cfg_if::cfg_if!{ - if #[cfg(feature = "rpi")]{ - let joypad_provider = GpioJoypadProvider::new(buttons_mapper); - } - else{ - let joypad_provider = sdl::sdl_joypad_provider::SdlJoypadProvider::new(buttons_mapper); - } - } + let joypad_provider = sdl::sdl_joypad_provider::SdlJoypadProvider::new(buttons_mapper); + let bootrom_path = if check_for_terminal_feature_flag(&args, "--bootrom"){ get_terminal_feature_flag_value(&args, "--bootrom", "Error! you must specify a value for the --bootrom parameter") }else{ diff --git a/gb/src/rpi_gpio/dma.rs b/gb/src/rpi_gpio/dma.rs deleted file mode 100644 index cb249215..00000000 --- a/gb/src/rpi_gpio/dma.rs +++ /dev/null @@ -1,442 +0,0 @@ -use std::ptr::{write_volatile, read_volatile}; - -use bcm_host::BcmHost; -use libc::{c_void, c_int}; - -use super::{*, ili9341_controller::SPI_BUFFER_SIZE}; - -// Mailbox messages need to be 16 byte alligned -#[repr(C, align(16))] -struct MailboxMessage{ - length:u32, - request:u32, - tag:u32, - buffer_length:u32, - data_length:u32, // not sure if neccessary - data:[u32;PAYLOAD_SIZE], - message_end_indicator:u32 -} - -impl MailboxMessage{ - fn new(tag:u32, data:[u32;PAYLOAD_SIZE])->Self{ - Self{ - length:std::mem::size_of::() as u32, - request:0, - tag, - buffer_length:(std::mem::size_of::()*PAYLOAD_SIZE) as u32, - data_length:(std::mem::size_of::()*PAYLOAD_SIZE) as u32, - data, - message_end_indicator:0 - } - } -} - -// Docs - https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface -struct Mailbox{ - mbox_fd: c_int, -} - -impl Mailbox{ - const MAILBOX_IOCTL_PROPERTY:libc::c_ulong = nix::request_code_readwrite!(100, 0, std::mem::size_of::<*mut libc::c_void>()); - - fn new()->Self{ - let fd = unsafe{libc::open(std::ffi::CStr::from_bytes_with_nul(b"/dev/vcio\0").unwrap().as_ptr(), 0)}; - if fd < 0{ - std::panic!("Error while opening vc mailbox"); - } - - Self { mbox_fd: fd } - } - - fn send_command(&self, tag:u32, data:[u32;SIZE])->u32{ - let mut message = MailboxMessage::::new(tag, data); - return self.send_message(&mut message); - } - - fn send_message(&self, message:&mut MailboxMessage)->u32{ - let raw_message = message as *mut MailboxMessage as *mut c_void; - let ret = unsafe{ - // Using libc::ioctl and not nix high level abstraction over it cause Im sending a *void and not more - // concrete type and the nix macro will mess the types for us. I belive it could work with nix after some modification - // of the way Im handling this but Im leaving this as it for now. sorry! - libc::ioctl(self.mbox_fd, Self::MAILBOX_IOCTL_PROPERTY, raw_message) - }; - if ret < 0{ - libc_abort("Error in ioctl call"); - } - - // The return value of the command is located at the first int in the data section (for more info see the Mailbox docs) - return message.data[0]; - } -} - -impl Drop for Mailbox{ - fn drop(&mut self) { - unsafe{ - let result = libc::close(self.mbox_fd); - if result != 0{ - libc_abort("Error while closing the mbox fd"); - } - } - } -} - - -// using GpuMemory cause I need a memory that is not cached by the cpu caches (L1, L2) -struct GpuMemory{ - virtual_address_ptr:usize, - bus_address:u32, - mailbox_memory_handle:u32, - size:u32 -} - -impl GpuMemory{ - const MEM_ALLOC_FLAG_DIRECT:usize = 1 << 2; - const MEM_ALLOC_FLAG_COHERENT:usize = 1 << 3; - const ALLOCATE_MEMORY_TAG:u32 = 0x3000C; - const LOCK_MEMORY_TAG:u32 = 0x3000D; - const UNLOCK_MEMORY_TAG:u32 = 0x3000E; - const RELEASE_MEMORY_TAG:u32 = 0x3000F; - const PAGE_SIZE:u32 = 4096; - - // This function converts the from the bus address of the SDRAM uncached memory to the arm physical address - // Notice that supposed to work only for this type of memory - const fn bus_to_phys(bus_address:u32)->u32{bus_address & !0xC000_0000} - - // Using the Mailbox interface to allocate memory on the gpu - fn allocate(mbox:&Mailbox, size:u32, mem_fd:c_int)->GpuMemory{ - let flags = (Self::MEM_ALLOC_FLAG_COHERENT | Self::MEM_ALLOC_FLAG_DIRECT) as u32; - let handle = mbox.send_command(Self::ALLOCATE_MEMORY_TAG, [size, Self::PAGE_SIZE, flags]); - // This is not documented well but after testing - on out of Gpu memory mailbox returns handle = 0 - if handle == 0{ - std::panic!("Error allocating Gpu memory! perhaps there is not enough free Gpu memory"); - } - let bus_address = mbox.send_command(Self::LOCK_MEMORY_TAG, [handle]); - // This is not documented well but after testing - on invalid handle mailbox returns bus_address = 0 - if bus_address == 0{ - std::panic!("Error locking Gpu memory!"); - } - - let virtual_address = unsafe{libc::mmap( - std::ptr::null_mut(), - size as libc::size_t, - libc::PROT_READ | libc::PROT_WRITE, - libc::MAP_SHARED, - mem_fd, - Self::bus_to_phys(bus_address) as libc::off_t - )}; - - return GpuMemory { virtual_address_ptr: virtual_address as usize, bus_address, mailbox_memory_handle:handle, size } - } - - fn release(&self, mbox:&Mailbox){ - unsafe{ - if libc::munmap(self.virtual_address_ptr as *mut c_void, self.size as libc::size_t) != 0 { - libc_abort("Error while trying to un map gpu memory"); - } - } - if mbox.send_command(Self::UNLOCK_MEMORY_TAG, [self.mailbox_memory_handle]) != 0{ - std::panic!("Error while trying to unlock gpu memory using mailbox"); - } - if mbox.send_command(Self::RELEASE_MEMORY_TAG, [self.mailbox_memory_handle]) != 0{ - std::panic!("Error while trying to release gpu memory using mailbox"); - } - } -} - -// The DMA control block registers are in a 32 byte alligned addresses so the stracture mapping them needs to be as well -// in order for me to cast some bytes to this stuct (at least I think so) -// Not valid for DMA4 channels -#[repr(C, align(32))] -struct DmaControlBlock{ - transfer_information:u32, - source_address:u32, - destination_address:u32, - trasnfer_length:u32, - _stride:u32, // Not avalibale on the lite channels - next_control_block_address:u32, - _reserved:[u32;2] -} - -impl DmaControlBlock{ - decl_write_volatile_field!(write_ti, transfer_information); - decl_write_volatile_field!(write_source_ad, source_address); - decl_write_volatile_field!(write_dest_ad, destination_address); - decl_write_volatile_field!(write_txfr_len, trasnfer_length); - decl_write_volatile_field!(write_nextconbk, next_control_block_address); -} - - -// Since Im casting an arbitary pointer to this struct it must be alligned by 4 bytes (with no gaps as well) -#[repr(C, align(4))] -struct DmaRegistersAccess{ - control_status:u32, - control_block_address:u32, - control_block:DmaControlBlock, - debug:u32 -} - -impl DmaRegistersAccess{ - decl_write_volatile_field!(write_cs, control_status); - decl_read_volatile_field!(read_cs, control_status); - decl_write_volatile_field!(write_conblk_ad, control_block_address); -} - -pub struct DmaSpiTransferer{ - tx_dma:*mut DmaRegistersAccess, - rx_dma:*mut DmaRegistersAccess, - mbox:Mailbox, - tx_control_block_memory:GpuMemory, - rx_control_block_memory:GpuMemory, - source_buffer_memory:GpuMemory, - dma_dynamic_memory:GpuMemory, - dma_constant_memory:GpuMemory, - dma_enable_register_ptr:*mut u32, -} - -impl DmaSpiTransferer{ - const BCM_DMA0_OFFSET:usize = 0x7000; - const BCM_DMA_ENABLE_REGISTER_OFFSET:usize = Self::BCM_DMA0_OFFSET + 0xFF0; - const DMA_CHANNEL_REGISTERS_SIZE:usize = 0x100; - - const DMA_CS_RESET:u32 = 1 << 31; - const DMA_CS_END:u32 = 1 << 1; - const DMA_CS_ACTIVE:u32 = 1; - - const DMA_TI_SRC_DREQ:u32 = 1 << 10; - const DMA_TI_SRC_INC:u32 = 1 << 8; - const DMA_TI_DEST_IGNORE:u32 = 1 << 7; - const DMA_TI_DEST_DREQ:u32 = 1 << 6; - const DMA_TI_DEST_INC:u32 = 1 << 4; - const DMA_TI_WAIT_RESP:u32 = 1 << 3; - - const DMA_DMA0_CS_PHYS_ADDRESS:u32 = 0x7E00_7000; - const DMA_DMA0_CONBLK_AD_PHYS_ADDRESS:u32 = 0x7E00_7004; - const fn dma_ti_permap(peripherial_mapping:u8)->u32{(peripherial_mapping as u32) << 16} - - const DMA_CONTROL_BLOCKS_PER_TRANSFER:u32 = 4; - const DMA_CONSTANT_MEMORY_SIZE:u32 = (std::mem::size_of::() * 2) as u32; - const DMA_SPI_HEADER_SIZE:u32 = std::mem::size_of::() as u32; - const RX_CHANNEL_NUMBER:u8 = 7; - const TX_CHANNEL_NUMBER:u8 = 1; - - const MAX_DMA_SPI_TRANSFER:usize = 0xFFE0; // must be smaller than max u16 and better be alligned for 32 bytes - const DMA_SPI_NUM_CHUNKS:usize = (SPI_BUFFER_SIZE / Self::MAX_DMA_SPI_TRANSFER) + ((SPI_BUFFER_SIZE % Self::MAX_DMA_SPI_TRANSFER) != 0) as usize; - const DMA_SPI_CHUNK_SIZE:usize = (SPI_BUFFER_SIZE / Self::DMA_SPI_NUM_CHUNKS) + 4; - const DMA_SPI_TRANSFER_SIZE:usize = Self::DMA_SPI_CHUNK_SIZE * Self::DMA_SPI_NUM_CHUNKS; - const DMA_TI_PERMAP_SPI_TX:u8 = 6; - const DMA_TI_PERMAP_SPI_RX:u8 = 7; - - const DMA_SPI_CS_PHYS_ADDRESS:u32 = 0x7E20_4000; - const DMA_SPI_FIFO_PHYS_ADDRESS:u32 = 0x7E20_4004; - - pub fn new(bcm_host:&BcmHost, spi_enable_dma_flag:u32)->Self{ - let mbox = Mailbox::new(); - let tx_registers = bcm_host.get_ptr(Self::BCM_DMA0_OFFSET + (Self::TX_CHANNEL_NUMBER as usize * Self::DMA_CHANNEL_REGISTERS_SIZE)) as *mut DmaRegistersAccess; - let rx_registers = bcm_host.get_ptr(Self::BCM_DMA0_OFFSET + (Self::RX_CHANNEL_NUMBER as usize * Self::DMA_CHANNEL_REGISTERS_SIZE)) as *mut DmaRegistersAccess; - - let dma_tx_control_block_memory = GpuMemory::allocate(&mbox, - std::mem::size_of::() as u32 * Self::DMA_CONTROL_BLOCKS_PER_TRANSFER * Self::DMA_SPI_CHUNK_SIZE as u32, - bcm_host.get_fd() - ); - let dma_rx_control_block_memory = GpuMemory::allocate(&mbox, - std::mem::size_of::() as u32 * Self::DMA_SPI_NUM_CHUNKS as u32, - bcm_host.get_fd() - ); - let dma_source_buffer_memory = GpuMemory::allocate(&mbox, (Self::DMA_SPI_TRANSFER_SIZE) as u32, bcm_host.get_fd()); - let dma_dynamic_memory = GpuMemory::allocate(&mbox, (std::mem::size_of::() * Self::DMA_SPI_NUM_CHUNKS) as u32, bcm_host.get_fd()); - let dma_constant_memory = GpuMemory::allocate(&mbox, Self::DMA_CONSTANT_MEMORY_SIZE, bcm_host.get_fd()); - - let dma_enable_register = bcm_host.get_ptr(Self::BCM_DMA_ENABLE_REGISTER_OFFSET) as *mut u32; - - unsafe{ - // setup constant data - let ptr = dma_constant_memory.virtual_address_ptr as *mut u32; - write_volatile(ptr, spi_enable_dma_flag); // this int enable spi with dma - write_volatile(ptr.add(1), Self::DMA_CS_ACTIVE | Self::DMA_CS_END); // this int starts the dma (set active and wrtie to end to reset it) - - // enable the rx & tx dma channels, on most of the cases this just to make sure they are enabled since they are enabled by default - let enable_value = read_volatile(dma_enable_register); - write_volatile(dma_enable_register, enable_value | 1 << Self::TX_CHANNEL_NUMBER | 1 << Self::RX_CHANNEL_NUMBER); - - //reset the dma channels - (*tx_registers).write_cs(Self::DMA_CS_RESET); - (*rx_registers).write_cs(Self::DMA_CS_RESET); - - // memset the memory - std::ptr::write_bytes(dma_rx_control_block_memory.virtual_address_ptr as *mut u8, 0, dma_rx_control_block_memory.size as usize); - std::ptr::write_bytes(dma_tx_control_block_memory.virtual_address_ptr as *mut u8, 0, dma_tx_control_block_memory.size as usize); - std::ptr::write_bytes(dma_source_buffer_memory.virtual_address_ptr as *mut u8, 0, dma_source_buffer_memory.size as usize); - std::ptr::write_bytes(dma_dynamic_memory.virtual_address_ptr as *mut u8, 0, dma_dynamic_memory.size as usize); - } - - let mut dma_controller = Self { - tx_dma: tx_registers, - rx_dma: rx_registers, - mbox, - rx_control_block_memory:dma_rx_control_block_memory, - tx_control_block_memory:dma_tx_control_block_memory, - source_buffer_memory:dma_source_buffer_memory, - dma_dynamic_memory, - dma_constant_memory, - dma_enable_register_ptr:dma_enable_register - }; - - unsafe{dma_controller.init_dma_control_blocks()}; - - log::info!("Initialized dma contorller"); - - return dma_controller; - } - - unsafe fn init_dma_control_blocks(&mut self) { - let mut rx_control_block = &mut *(self.rx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock); - rx_control_block.write_ti(Self::dma_ti_permap(Self::DMA_TI_PERMAP_SPI_RX) | Self::DMA_TI_SRC_DREQ | Self::DMA_TI_DEST_IGNORE); - rx_control_block.write_source_ad(Self::DMA_SPI_FIFO_PHYS_ADDRESS); - rx_control_block.write_dest_ad(0); - rx_control_block.write_txfr_len(Self::DMA_SPI_CHUNK_SIZE as u32 - Self::DMA_SPI_HEADER_SIZE); // without the 4 byte header - rx_control_block.write_nextconbk(0); - - let tx_control_block = &mut *(self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock); - tx_control_block.write_ti(Self::dma_ti_permap(Self::DMA_TI_PERMAP_SPI_TX) | Self::DMA_TI_DEST_DREQ | Self::DMA_TI_SRC_INC | Self::DMA_TI_WAIT_RESP); - tx_control_block.write_source_ad(self.source_buffer_memory.bus_address); - tx_control_block.write_dest_ad(Self::DMA_SPI_FIFO_PHYS_ADDRESS); - tx_control_block.write_txfr_len(Self::DMA_SPI_CHUNK_SIZE as u32); - tx_control_block.write_nextconbk(0); - for i in 1..Self::DMA_SPI_NUM_CHUNKS{ - let tx_cb_index = i * Self::DMA_CONTROL_BLOCKS_PER_TRANSFER as usize; - let set_tx_cb_index = tx_cb_index + 1; - let disable_tx_cb_index = set_tx_cb_index + 1; - let start_tx_cb_index = disable_tx_cb_index + 1; - - let tx_control_block = &mut *((self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(tx_cb_index)); - tx_control_block.write_ti(Self::dma_ti_permap(Self::DMA_TI_PERMAP_SPI_TX) | Self::DMA_TI_DEST_DREQ | Self::DMA_TI_SRC_INC | Self::DMA_TI_WAIT_RESP); - tx_control_block.write_source_ad(self.source_buffer_memory.bus_address + (i * Self::DMA_SPI_CHUNK_SIZE) as u32); - tx_control_block.write_dest_ad(Self::DMA_SPI_FIFO_PHYS_ADDRESS); - tx_control_block.write_txfr_len(Self::DMA_SPI_CHUNK_SIZE as u32); - tx_control_block.write_nextconbk(0); - - let set_dma_tx_address = &mut *((self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(set_tx_cb_index)); - let disable_dma_tx_address = &mut *((self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(disable_tx_cb_index)); - let start_dma_tx_address = &mut *((self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(start_tx_cb_index)); - - rx_control_block.write_nextconbk(self.tx_control_block_memory.bus_address + (set_tx_cb_index * std::mem::size_of::()) as u32); - - write_volatile((self.dma_dynamic_memory.virtual_address_ptr as *mut u32).add(i), self.tx_control_block_memory.bus_address + (tx_cb_index * std::mem::size_of::()) as u32); - - set_dma_tx_address.write_ti(Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); - set_dma_tx_address.write_source_ad(self.dma_dynamic_memory.bus_address + (i as u32 * Self::DMA_CONTROL_BLOCKS_PER_TRANSFER)); - set_dma_tx_address.write_dest_ad(Self::DMA_DMA0_CONBLK_AD_PHYS_ADDRESS + (Self::TX_CHANNEL_NUMBER as u32 * Self::DMA_CHANNEL_REGISTERS_SIZE as u32)); // channel control block address register - set_dma_tx_address.write_txfr_len(std::mem::size_of::() as u32); - set_dma_tx_address.write_nextconbk(self.tx_control_block_memory.bus_address + (disable_tx_cb_index * std::mem::size_of::()) as u32); - - - disable_dma_tx_address.write_ti(Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); - disable_dma_tx_address.write_source_ad(self.dma_constant_memory.bus_address); - disable_dma_tx_address.write_dest_ad(Self::DMA_SPI_CS_PHYS_ADDRESS); - disable_dma_tx_address.write_txfr_len(std::mem::size_of::() as u32); - disable_dma_tx_address.write_nextconbk(self.tx_control_block_memory.bus_address + (start_tx_cb_index * std::mem::size_of::()) as u32); - - - start_dma_tx_address.write_ti(Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); - start_dma_tx_address.write_source_ad(self.dma_constant_memory.bus_address + (i * std::mem::size_of::()) as u32); - start_dma_tx_address.write_dest_ad(Self::DMA_DMA0_CS_PHYS_ADDRESS + (Self::TX_CHANNEL_NUMBER as u32 * Self::DMA_CHANNEL_REGISTERS_SIZE as u32)); - start_dma_tx_address.write_txfr_len(std::mem::size_of::() as u32); - start_dma_tx_address.write_nextconbk(self.rx_control_block_memory.bus_address + (i * std::mem::size_of::()) as u32); - - - rx_control_block = &mut *((self.rx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(i)); - rx_control_block.write_ti(Self::dma_ti_permap(Self::DMA_TI_PERMAP_SPI_RX) | Self::DMA_TI_SRC_DREQ | Self::DMA_TI_DEST_IGNORE); - rx_control_block.write_source_ad(Self::DMA_SPI_FIFO_PHYS_ADDRESS); - rx_control_block.write_dest_ad(0); - rx_control_block.write_txfr_len(Self::DMA_SPI_CHUNK_SIZE as u32 - Self::DMA_SPI_HEADER_SIZE); // without the 4 byte header - rx_control_block.write_nextconbk(0); - } - } - - pub fn start_dma_transfer(&mut self, data:&[u8; SPI_BUFFER_SIZE], transfer_active_flag:u8){ - unsafe{ - if (*self.tx_dma).read_cs() & 0x100 != 0{ - log::error!("Error in the tx dma"); - } - - let data_len = Self::DMA_SPI_CHUNK_SIZE - Self::DMA_SPI_HEADER_SIZE as usize; // Removing the first 4 bytes from this length param - let header = [transfer_active_flag, 0, (data_len & 0xFF) as u8, /*making sure this is little endian order*/ (data_len >> 8) as u8]; - - let chunks = data.chunks_exact(Self::DMA_SPI_CHUNK_SIZE - Self::DMA_SPI_HEADER_SIZE as usize); - let mut array:[u8;Self::DMA_SPI_CHUNK_SIZE * Self::DMA_SPI_NUM_CHUNKS] = [0;Self::DMA_SPI_CHUNK_SIZE * Self::DMA_SPI_NUM_CHUNKS]; - let mut i = 0; - for chunk in chunks{ - std::ptr::copy_nonoverlapping(header.as_ptr(), array.as_mut_ptr().add(i * Self::DMA_SPI_CHUNK_SIZE), 4); - std::ptr::copy_nonoverlapping(chunk.as_ptr(), array.as_mut_ptr().add(4 + (i * Self::DMA_SPI_CHUNK_SIZE)), Self::DMA_SPI_CHUNK_SIZE - 4); - i += 1; - } - - std::ptr::copy_nonoverlapping(array.as_ptr(), self.source_buffer_memory.virtual_address_ptr as *mut u8, array.len()); - - (*self.tx_dma).write_conblk_ad(self.tx_control_block_memory.bus_address); - (*self.rx_dma).write_conblk_ad(self.rx_control_block_memory.bus_address); - - memory_barrier(); // Sync all the memory operations happened in this function - // Starting the dma transfer - (*self.tx_dma).write_cs(Self::DMA_CS_ACTIVE | Self::DMA_CS_END); - (*self.rx_dma).write_cs(Self::DMA_CS_ACTIVE | Self::DMA_CS_END); - // Since the DMA controller writes to the SPI registers adding a barrier (even though it wrties afterwards to the DMA registers) - memory_barrier(); // Change DMA to SPI - } - } - - pub fn end_dma_transfer(&self){ - const TIME_TO_ABORT_AS_MICRO:i32 = 1_000_000; - unsafe{ - // Wait for the last trasfer to end - let mut counter = 0; - while (*self.tx_dma).read_cs() & Self::DMA_CS_ACTIVE != 0 { - Self::sleep_us(1); - counter += 1; - if counter > TIME_TO_ABORT_AS_MICRO{ - std::panic!("ERROR! tx dma channel is not responding, a reboot is suggested"); - } - } - while (*self.rx_dma).read_cs() & Self::DMA_CS_ACTIVE != 0 { - Self::sleep_us(1); - counter += 1; - if counter > TIME_TO_ABORT_AS_MICRO{ - std::panic!("ERROR! rx dma channel is not responding, a reboot is suggested"); - } - } - } - } - - fn sleep_us(microseconds_to_sleep:u64){ - std::thread::sleep(std::time::Duration::from_micros(microseconds_to_sleep)); - } -} - -impl Drop for DmaSpiTransferer{ - fn drop(&mut self) { - // Finish current dma operation - self.end_dma_transfer(); - - // reset the dma channels before releasing the memory - unsafe{ - // reset the dma channels - (*self.tx_dma).write_cs(Self::DMA_CS_RESET); - (*self.rx_dma).write_cs(Self::DMA_CS_RESET); - // clear the permaps for the channels - (*self.tx_dma).control_block.write_ti(0); - (*self.rx_dma).control_block.write_ti(0); - // disable the channels I used - let mask = !((1 << Self::TX_CHANNEL_NUMBER) | (1 << Self::RX_CHANNEL_NUMBER)); - write_volatile(self.dma_enable_register_ptr, read_volatile(self.dma_enable_register_ptr) & mask); - } - - self.dma_constant_memory.release(&self.mbox); - self.dma_dynamic_memory.release(&self.mbox); - self.rx_control_block_memory.release(&self.mbox); - self.source_buffer_memory.release(&self.mbox); - self.tx_control_block_memory.release(&self.mbox); - - log::info!("Successfuly release dma resources"); - } -} \ No newline at end of file diff --git a/gb/src/rpi_gpio/gpio_joypad_provider.rs b/gb/src/rpi_gpio/gpio_joypad_provider.rs deleted file mode 100644 index 4e50b93e..00000000 --- a/gb/src/rpi_gpio/gpio_joypad_provider.rs +++ /dev/null @@ -1,53 +0,0 @@ -use lib_gb::keypad::{joypad::{Joypad, NUM_OF_KEYS},joypad_provider::JoypadProvider,button::Button}; -use lib_gb::utils::create_array; -use rppal::gpio::{Gpio, InputPin}; - -use crate::joypad_menu::MenuJoypadProvider; - -pub type GpioBcmPin = u8; - -static mut INPUT_PINS:Option<[InputPin; NUM_OF_KEYS]> = None; - -pub struct GpioJoypadProvider{ - input_pins:&'static [InputPin; NUM_OF_KEYS] -} - -impl GpioJoypadProvider{ - // FIXME: The mapper is used only the first this struct is initialized - pub fn newGpioBcmPin>(mapper:F)->Self{ - if unsafe{INPUT_PINS.is_none()}{ - let gpio = Gpio::new().unwrap(); - let mut counter:u8 = 0; - let generator_lambda = ||{ - let button:Button = unsafe{std::mem::transmute(counter)}; - let mut result = gpio.get(mapper(&button)).unwrap().into_input(); - result.set_interrupt(rppal::gpio::Trigger::RisingEdge).unwrap(); - counter += 1; - return result; - }; - unsafe{INPUT_PINS = Some(create_array(generator_lambda))}; - } - let input_pins:&'static [InputPin; NUM_OF_KEYS] = unsafe{INPUT_PINS.as_ref().unwrap()}; - return Self{input_pins}; - } -} - -impl JoypadProvider for GpioJoypadProvider{ - fn provide(&mut self, joypad:&mut Joypad){ - for i in 0..NUM_OF_KEYS{ - joypad.buttons[i] = self.input_pins[i].is_high(); - } - } -} - -impl MenuJoypadProvider for GpioJoypadProvider{ - fn poll(&mut self, mut joypad:&mut Joypad) { - let pins_refs:[&InputPin; NUM_OF_KEYS] = { - let pins = self.input_pins; - // Replace this line with each_ref() once it become stable - [&pins[0],&pins[1],&pins[2],&pins[3],&pins[4],&pins[5],&pins[6], &pins[7]] - }; - let _ = rppal::gpio::Gpio::new().unwrap().poll_interrupts( &pins_refs, true, None).unwrap().unwrap(); - self.provide(&mut joypad); - } -} \ No newline at end of file diff --git a/gb/src/rpi_gpio/ili9341_controller.rs b/gb/src/rpi_gpio/ili9341_controller.rs deleted file mode 100644 index a2245793..00000000 --- a/gb/src/rpi_gpio/ili9341_controller.rs +++ /dev/null @@ -1,226 +0,0 @@ -use std::ops::Add; - -use lib_gb::ppu::{gb_ppu::{SCREEN_WIDTH, SCREEN_HEIGHT}, gfx_device::{GfxDevice, Pixel}}; -use rppal::gpio::OutputPin; - -pub enum Ili9341Commands{ - SoftwareReset = 0x01, - SleepOut = 0x11, - GammaSet = 0x26, - DisplayOff = 0x28, - DisplayOn = 0x29, - ColumnAddressSet = 0x2A, // Set curosr X value - PageAddressSet = 0x2B, // Set cursor Y value - MemoryWrite = 0x2C, - MemoryAccessControl = 0x36, - PixelFormatSet = 0x3A, - FrameRateControl = 0xB1, - DisplayFunctionControl = 0xB6, - PowerControl1 = 0xC0, - PowerControl2 = 0xC1, - VcomControl1 = 0xC5, - VcomControl2 = 0xC7, - PowerControlA = 0xCB, - PowerControlB = 0xCF, - PossitiveGammaCorrection = 0xE0, - NegativeGammaCorrection = 0xE1, - DriverTimingControlA = 0xE8, - DriverTimingControlB = 0xEA, - PowerOnSequenceControl = 0xED, - Enable3G = 0xF2, -} - -const ILI9341_SCREEN_WIDTH:usize = 320; -const ILI9341_SCREEN_HEIGHT:usize = 240; -const SCALE:f32 = 5.0 / 3.0; // maximum scale to fit the ili9341 screen -pub (super) const TARGET_SCREEN_WIDTH:usize = (SCREEN_WIDTH as f32 * SCALE) as usize; -pub (super) const TARGET_SCREEN_HEIGHT:usize = (SCREEN_HEIGHT as f32 * SCALE) as usize; -const FRAME_BUFFER_X_OFFSET:usize = (ILI9341_SCREEN_WIDTH - TARGET_SCREEN_WIDTH) / 2; - -pub const SPI_BUFFER_SIZE:usize = TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * std::mem::size_of::(); - -pub trait SpiController { - fn new(dc_pin_number:u8)->Self; - fn fast_mode(&mut self); - fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]); - fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;SPI_BUFFER_SIZE]); -} - -struct Ili9341Contoller{ - spi:SC, - led_pin: OutputPin, - reset_pin: OutputPin -} - -impl Ili9341Contoller{ - const CLEAN_BUFFER:[u8;ILI9341_SCREEN_HEIGHT * ILI9341_SCREEN_WIDTH * std::mem::size_of::()] = [0; ILI9341_SCREEN_HEIGHT * ILI9341_SCREEN_WIDTH * std::mem::size_of::()]; - - pub fn new(reset_pin_bcm:u8, dc_pin_bcm:u8, led_pin_bcm:u8)->Self{ - - log::info!("Initalizing with screen size width: {}, hight: {}", TARGET_SCREEN_WIDTH, TARGET_SCREEN_HEIGHT); - - let gpio = rppal::gpio::Gpio::new().unwrap(); - let mut reset_pin = gpio.get(reset_pin_bcm).unwrap().into_output(); - let mut led_pin = gpio.get(led_pin_bcm).unwrap().into_output(); - - // toggling the reset pin to initalize the lcd - reset_pin.set_high(); - Self::sleep_ms(120); - reset_pin.set_low(); - Self::sleep_ms(120); - reset_pin.set_high(); - Self::sleep_ms(120); - - let mut spi:SC = SpiController::new(dc_pin_bcm); - - // This code snippets is ofcourse wrriten by me but took heavy insperation from fbcp-ili9341 (https://github.com/juj/fbcp-ili9341) - // I used the ili9341 application notes and the fbcp-ili9341 implementation in order to write it all down - // And later I twicked some params specific to my display (http://www.lcdwiki.com/3.2inch_SPI_Module_ILI9341_SKU:MSP3218) - - // There is another implementation in rust for an ili9341 controller which is much simpler and uses those commands: - // Sleepms(5), SoftwareReset, Sleepms(120), MemoryAccessControl, PixelFormatSet, SleepOut, Sleepms(5), DisplayOn - // minimal config based on rust ili9341 lib (https://github.com/yuri91/ili9341-rs) - - // fbcp-ili9341 inspired implementation: - /*---------------------------------------------------------------------------------------------------------------------- */ - // Reset the screen - spi.write(Ili9341Commands::SoftwareReset,&[]); - Self::sleep_ms(5); - spi.write(Ili9341Commands::DisplayOff,&[]); - - // Some power stuff, probably uneccessary but just for sure - spi.write(Ili9341Commands::PowerControlA, &[0x39, 0x2C, 0x0, 0x34, 0x2]); - spi.write(Ili9341Commands::PowerControlB, &[0x0, 0xC1, 0x30]); - spi.write(Ili9341Commands::DriverTimingControlA, &[0x85, 0x0, 0x78]); - spi.write(Ili9341Commands::DriverTimingControlB, &[0x0, 0x0]); - spi.write(Ili9341Commands::PowerOnSequenceControl, &[0x64, 0x3, 0x12, 0x81]); - spi.write(Ili9341Commands::PowerControl1, &[0x23]); - spi.write(Ili9341Commands::PowerControl2,&[0x10]); - spi.write(Ili9341Commands::VcomControl1, &[0xE3, 0x28]); - spi.write(Ili9341Commands::VcomControl2, &[0x86]); - - // Configuring the screen - spi.write(Ili9341Commands::MemoryAccessControl, &[0x28]); // This command tlit the screen 90 degree and set pixel BGR order - spi.write(Ili9341Commands::PixelFormatSet, &[0x55]); // set pixel format to 16 bit per pixel; - spi.write(Ili9341Commands::FrameRateControl, &[0x0, 0x10 /*According to the docs this is 119 hrz, setting this option in order to avoid screen tearing on rpi zero2 */]); - spi.write(Ili9341Commands::DisplayFunctionControl, &[0x8, 0x82, 0x27]); - - // Gamma values - pretty sure its redundant - spi.write(Ili9341Commands::Enable3G, &[0x2]); - spi.write(Ili9341Commands::GammaSet, &[0x1]); - spi.write(Ili9341Commands::PossitiveGammaCorrection,&[0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00]); - spi.write(Ili9341Commands::NegativeGammaCorrection, &[0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F]); - - // Turn screen on - spi.write(Ili9341Commands::SleepOut,&[]); - Self::sleep_ms(120); - spi.write(Ili9341Commands::DisplayOn,&[]); - /*---------------------------------------------------------------------------------------------------------------------- */ - //End of fbcp-ili9341 inpired implementation - - log::info!("Finish configuring ili9341"); - - // turn backlight on - led_pin.set_high(); - - // Clear screen - spi.write(Ili9341Commands::ColumnAddressSet, &[0,0,((ILI9341_SCREEN_WIDTH -1) >> 8) as u8, ((ILI9341_SCREEN_WIDTH -1) & 0xFF) as u8]); - spi.write(Ili9341Commands::PageAddressSet, &[0,0,((ILI9341_SCREEN_HEIGHT -1) >> 8) as u8, ((ILI9341_SCREEN_HEIGHT -1) & 0xFF) as u8]); - // using write and not write buffer since this is not the correct size - spi.write(Ili9341Commands::MemoryWrite, &Self::CLEAN_BUFFER); - - // need to sleep before changing the clock after transferring pixels to the lcd - Self::sleep_ms(120); - - spi.fast_mode(); - - log::info!("finish ili9341 device init"); - - return Ili9341Contoller { spi, led_pin, reset_pin}; - } - - - pub fn write_frame_buffer(&mut self, buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH]){ - let mut scaled_buffer: [u8;TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * 2] = [0;TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * 2]; - unsafe{image_inter::scale_nearest::(buffer.as_ptr(), scaled_buffer.as_mut_ptr())}; - - let end_x_index = TARGET_SCREEN_WIDTH + FRAME_BUFFER_X_OFFSET - 1; - self.spi.write(Ili9341Commands::ColumnAddressSet, &[ - (FRAME_BUFFER_X_OFFSET >> 8) as u8, - (FRAME_BUFFER_X_OFFSET & 0xFF) as u8, - (end_x_index >> 8) as u8, - (end_x_index & 0xFF) as u8 - ]); - self.spi.write(Ili9341Commands::PageAddressSet, &[ - 0x0, 0x0, - ((TARGET_SCREEN_HEIGHT - 1) >> 8) as u8, - ((TARGET_SCREEN_HEIGHT - 1) & 0xFF) as u8 - ]); - - self.spi.write_buffer(Ili9341Commands::MemoryWrite, &scaled_buffer); - } - - fn sleep_ms(milliseconds_to_sleep:u64){ - std::thread::sleep(std::time::Duration::from_millis(milliseconds_to_sleep)); - } -} - -impl Drop for Ili9341Contoller{ - fn drop(&mut self) { - self.spi.write(Ili9341Commands::DisplayOff, &[]); - self.led_pin.set_low(); - self.reset_pin.set_high(); - Self::sleep_ms(1); - self.reset_pin.set_low(); - } -} - -pub struct Ili9341GfxDevice{ - ili9341_controller:Ili9341Contoller, - turbo_mul:u8, - turbo_frame_counter:u8, - - frame_limiter:u32, - frames_counter: u32, - time_counter:std::time::Duration, - last_time: std::time::Instant, -} - -impl Ili9341GfxDevice{ - pub fn new(reset_pin_bcm:u8, dc_pin_bcm:u8, led_pin_bcm:u8, turbo_mul:u8, frame_limiter:u32)->Self{ - #[cfg(not(feature = "u16pixel"))] - std::compile_error("ili9341 gfx device must have Pixel type = u16"); - - let ili9341_controller = Ili9341Contoller::new(reset_pin_bcm, dc_pin_bcm, led_pin_bcm); - - Ili9341GfxDevice { - ili9341_controller,frames_counter:0, - time_counter: std::time::Duration::ZERO, last_time:std::time::Instant::now(), - turbo_mul, turbo_frame_counter:0, frame_limiter - } - } -} - -impl GfxDevice for Ili9341GfxDevice{ - fn swap_buffer(&mut self, buffer:&[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH]) { - self.turbo_frame_counter = (self.turbo_frame_counter + 1) % self.turbo_mul; - if self.turbo_frame_counter != 0{ - return; - } - - if self.frames_counter & self.frame_limiter == 0{ - self.ili9341_controller.write_frame_buffer(&buffer); - } - - // measure fps - self.frames_counter += 1; - let time = std::time::Instant::now(); - self.time_counter = self.time_counter.add(time.duration_since(self.last_time)); - self.last_time = time; - if self.time_counter.as_millis() > 1000{ - log::debug!("FPS: {}", self.frames_counter); - self.frames_counter = 0; - self.time_counter = std::time::Duration::ZERO; - } - } -} \ No newline at end of file diff --git a/gb/src/rpi_gpio/mmio_spi.rs b/gb/src/rpi_gpio/mmio_spi.rs deleted file mode 100644 index f1a49abd..00000000 --- a/gb/src/rpi_gpio/mmio_spi.rs +++ /dev/null @@ -1,181 +0,0 @@ -use bcm_host::BcmHost; -use rppal::gpio::{OutputPin, IoPin}; - -use crate::rpi_gpio::dma::DmaSpiTransferer; - -use super::{ili9341_controller::{Ili9341Commands, SpiController, SPI_BUFFER_SIZE}, decl_write_volatile_field, decl_read_volatile_field, memory_barrier}; - -const BCM_SPI0_BASE_ADDRESS:usize = 0x20_4000; -const FAST_SPI_CLOCK_DIVISOR:u32 = 4; // the smaller the faster (on my ili9341 screen below 4 there are currptions) -const INIT_SPI_CLOCK_DIVISOR:u32 = 34; // slow clock for verifiying the initialization goes smooth with no corruptions - -// The register are 4 bytes each so making sure the allignment and padding are correct -#[repr(C, align(4))] -struct SpiRegistersAccess{ - control_status:u32, - fifo:u32, - clock:u32, - data_length:u32 -} - -impl SpiRegistersAccess{ - decl_write_volatile_field!(write_cs, control_status); - decl_read_volatile_field!(read_cs, control_status); - decl_write_volatile_field!(write_fifo, fifo); - decl_write_volatile_field!(write_clk, clock); - decl_write_volatile_field!(write_dlen, data_length); -} - -pub struct MmioSpi{ - spi_registers: *mut SpiRegistersAccess, - dc_pin:OutputPin, - dma_transferer:DmaSpiTransferer, - last_transfer_was_dma:bool, - - // holding those pins in order to make sure they are configured correctly - // the state resets upon drop - _spi_pins:[IoPin;2], - _spi_cs0:OutputPin, - - // declared last in order for it to be freed last - // rust gurantee that the order of the droped values is the order of declaration - // keeping it last so it will be freed correctly - _bcm:BcmHost, -} - -impl MmioSpi{ - const SPI0_CE0_N_BCM_PIN:u8 = 8; - const SPI0_MOSI_BCM_PIN:u8 = 10; - const SPI0_SCLK_BCM_PIN:u8 = 11; - - const SPI_CS_RXF:u32 = 1 << 20; - const SPI_CS_RXR:u32 = 1 << 19; - const SPI_CS_TXD:u32 = 1 << 18; - const SPI_CS_DONE:u32 = 1 << 16; - const SPI_CS_DMAEN:u32 = 1 << 8; - const SPI_CS_TA:u32 = 1 << 7; - const SPI_CS_CLEAR:u32 = 3<<4; - const SPI_CS_CLEAR_RX:u32 = 1 << 5; - - fn new (dc_pin:OutputPin, spi_pins:[IoPin;2], mut spi_cs0: OutputPin)->Self{ - let bcm_host = BcmHost::new(); - - let spi_registers = bcm_host.get_ptr(BCM_SPI0_BASE_ADDRESS) as *mut SpiRegistersAccess; - - unsafe{ - // ChipSelect = 0, ClockPhase = 0, ClockPolarity = 0 - spi_cs0.set_low(); - Self::setup_poll_fast_transfer(&mut *spi_registers); - (*spi_registers).write_clk(INIT_SPI_CLOCK_DIVISOR); - } - - memory_barrier(); // Change SPI to DMA - let dma_transferer = DmaSpiTransferer::new(&bcm_host, Self::SPI_CS_DMAEN); - memory_barrier(); // Change DMA to SPI - - log::info!("Finish initializing spi mmio interface"); - MmioSpi { - _bcm:bcm_host, spi_registers, dc_pin, _spi_pins: spi_pins, _spi_cs0: spi_cs0, last_transfer_was_dma: false, - dma_transferer - } - } - - fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]){ - self.prepare_for_transfer(); - self.dc_pin.set_low(); - self.write_raw(&[command as u8]); - self.dc_pin.set_high(); - self.write_raw(data); - self.last_transfer_was_dma = false; - } - - fn prepare_for_transfer(&mut self) { - if self.last_transfer_was_dma{ - memory_barrier(); // Change SPI to DMA - self.dma_transferer.end_dma_transfer(); - memory_barrier(); // Change DMA to SPI - Self::setup_poll_fast_transfer(self.spi_registers); - } - } - - fn setup_poll_fast_transfer(spi_registers:*mut SpiRegistersAccess){ - unsafe{ - (*spi_registers).write_cs(Self::SPI_CS_TA | Self::SPI_CS_CLEAR); - - // poll mode speed up according to this forum post - https://forums.raspberrypi.com/viewtopic.php?f=44&t=181154 - (*spi_registers).write_dlen(2); - } - } - - fn write_raw(&mut self, data:&[u8;SIZE]){ - unsafe{ - let mut current_index = 0; - while current_index < SIZE { - let cs:u32 = (*self.spi_registers).read_cs(); - if cs & Self::SPI_CS_TXD != 0{ - (*self.spi_registers).write_fifo(data[current_index] as u32); - current_index += 1; - } - if (cs & (Self::SPI_CS_RXR | Self::SPI_CS_RXF)) != 0 { - (*self.spi_registers).write_cs(Self::SPI_CS_TA | Self::SPI_CS_CLEAR_RX); - } - } - - // wait for the last trasfer to finish - while ((*self.spi_registers).read_cs() & Self::SPI_CS_DONE) == 0 { - if ((*self.spi_registers).read_cs() & (Self::SPI_CS_RXR | Self::SPI_CS_RXF)) != 0{ - (*self.spi_registers).write_cs(Self::SPI_CS_TA | Self::SPI_CS_CLEAR_RX); - } - } - } - } - - fn write_dma(&mut self, command:Ili9341Commands, data:&[u8;SPI_BUFFER_SIZE]){ - self.prepare_for_transfer(); - - self.dc_pin.set_low(); - self.write_raw(&[command as u8]); - self.dc_pin.set_high(); - self.write_dma_raw(&data); - self.last_transfer_was_dma = true; - } - - - // Since generic_const_exprs is not stable yet Im reserving the first 4 bytes of the data variable for internal use - fn write_dma_raw(&mut self, data:&[u8;SPI_BUFFER_SIZE]){ - unsafe{(*self.spi_registers).write_cs(Self::SPI_CS_DMAEN | Self::SPI_CS_CLEAR)}; - memory_barrier(); // Change SPI to DMA - self.dma_transferer.start_dma_transfer(data, Self::SPI_CS_TA as u8); - memory_barrier(); // Change DMA to SPI - } -} - -impl SpiController for MmioSpi{ - fn new(dc_pin_number:u8)->Self { - let gpio = rppal::gpio::Gpio::new().unwrap(); - let spi0_ceo_n = gpio.get(Self::SPI0_CE0_N_BCM_PIN).unwrap().into_output(); - let spi0_mosi = gpio.get(Self::SPI0_MOSI_BCM_PIN).unwrap().into_io(rppal::gpio::Mode::Alt0); - let spi0_sclk = gpio.get(Self::SPI0_SCLK_BCM_PIN).unwrap().into_io(rppal::gpio::Mode::Alt0); - let dc_pin = gpio.get(dc_pin_number).unwrap().into_output(); - - MmioSpi::new(dc_pin, [spi0_mosi, spi0_sclk], spi0_ceo_n) - } - - fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]) { - self.write(command, data); - } - - fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;SPI_BUFFER_SIZE]) { - self.write_dma(command, data); - } - - fn fast_mode(&mut self) { - unsafe{(*self.spi_registers).write_clk(FAST_SPI_CLOCK_DIVISOR)}; - } -} - -impl Drop for MmioSpi{ - fn drop(&mut self) { - unsafe{(*self.spi_registers).write_cs(Self::SPI_CS_CLEAR)}; - } -} \ No newline at end of file diff --git a/gb/src/rpi_gpio/mod.rs b/gb/src/rpi_gpio/mod.rs deleted file mode 100644 index b317aafd..00000000 --- a/gb/src/rpi_gpio/mod.rs +++ /dev/null @@ -1,40 +0,0 @@ -pub mod ili9341_controller; -pub mod gpio_joypad_provider; - -cfg_if::cfg_if!{if #[cfg(feature = "mmio")]{ - mod dma; - mod mmio_spi; - pub type SpiType = mmio_spi::MmioSpi; -}else{ - mod rppal_spi; - pub type SpiType = rppal_spi::RppalSpi; -}} - - -fn libc_abort(message:&str){ - std::io::Result::<&str>::Err(std::io::Error::last_os_error()).expect(message); -} - -// According to the docs the raspberrypi requires memory barrier between reads and writes to differnet peripherals -#[inline] -pub(self) fn memory_barrier(){ - std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst); -} - -macro_rules! decl_write_volatile_field{ - ($function_name:ident, $field_name:ident) =>{ - #[inline] unsafe fn $function_name(&mut self,value:u32){ - std::ptr::write_volatile(&mut self.$field_name , value); - } - } -} - -macro_rules! decl_read_volatile_field{ - ($function_name:ident, $field_name:ident) =>{ - #[inline] unsafe fn $function_name(&mut self)->u32{ - std::ptr::read_volatile(&self.$field_name) - } - } -} - -pub(self) use {decl_read_volatile_field, decl_write_volatile_field}; \ No newline at end of file diff --git a/gb/src/rpi_gpio/rppal_spi.rs b/gb/src/rpi_gpio/rppal_spi.rs deleted file mode 100644 index 0042908f..00000000 --- a/gb/src/rpi_gpio/rppal_spi.rs +++ /dev/null @@ -1,52 +0,0 @@ -use rppal::gpio::{OutputPin, IoPin}; - -use super::{ili9341_controller::{Ili9341Commands, SPI_BUFFER_SIZE, SpiController}}; - -// This will work only if spi is enabled at /boot/config.txt (dtparam=spi=on) -pub struct RppalSpi{ - spi_device:rppal::spi::Spi, - dc_pin:OutputPin -} - -impl RppalSpi{ - const CLOCK_SPEED:u32 = 75_000_000; // chose based on trial and error - const SPI_TRANSFER_MAX_SIZE:usize = 4096; - - fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]){ - let command = command as u8; - self.dc_pin.set_low(); - self.spi_device.write(&[command]).expect("Error while writing to the spi device"); - self.dc_pin.set_high(); - let chunks = data.chunks(Self::SPI_TRANSFER_MAX_SIZE); - for chunk in chunks{ - self.spi_device.write(&chunk).expect(std::format!("Error wrting data to spi device for command: {:#X}",command).as_str() ); - } - } -} - -impl SpiController for RppalSpi{ - fn new(dc_pin:u8)->Self{ - let spi_device = rppal::spi::Spi::new( - rppal::spi::Bus::Spi0, - rppal::spi::SlaveSelect::Ss0/*pin 24*/, - Self::CLOCK_SPEED, - rppal::spi::Mode::Mode0 - ).expect("Error creating rppal spi device"); - - let dc_pin = rppal::gpio::Gpio::new().unwrap().get(dc_pin).unwrap().into_output(); - return RppalSpi { spi_device, dc_pin }; - } - - fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]) { - self.write(command, data); - } - - fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;SPI_BUFFER_SIZE]) { - self.write(command, data); - } - - fn fast_mode(&mut self) { - // No need to change the clock here, - // since we have no access directly to the clock register we cant set it high enough for it to matter :() - } -} \ No newline at end of file diff --git a/gb/src/sdl/sdl_joypad_provider.rs b/gb/src/sdl/sdl_joypad_provider.rs index 2ffd59dd..7007cd2b 100644 --- a/gb/src/sdl/sdl_joypad_provider.rs +++ b/gb/src/sdl/sdl_joypad_provider.rs @@ -1,6 +1,6 @@ use sdl2::sys::*; use lib_gb::{keypad::{joypad::{Joypad, NUM_OF_KEYS}, joypad_provider::JoypadProvider, button::Button}, utils::create_array}; -use crate::joypad_menu::MenuJoypadProvider; +use common::joypad_menu::MenuJoypadProvider; use super::utils::get_sdl_error_message; const PUMP_THRESHOLD:u32 = 500; diff --git a/gb/src/sdl/sdl_pull_audio_device.rs b/gb/src/sdl/sdl_pull_audio_device.rs index 2204f2e1..c29fb852 100644 --- a/gb/src/sdl/sdl_pull_audio_device.rs +++ b/gb/src/sdl/sdl_pull_audio_device.rs @@ -3,7 +3,7 @@ use std::{mem::ManuallyDrop, ffi::c_void}; use lib_gb::{GB_FREQUENCY, apu::audio_device::*}; use sdl2::sys::*; use crossbeam_channel::{Receiver, Sender, bounded}; -use crate::audio::audio_resampler::{AudioResampler, ResampledAudioDevice}; +use crate::audio::{AudioResampler, ResampledAudioDevice}; use super::utils::init_sdl_audio_device; const BUFFERS_NUMBER:usize = 3; diff --git a/gb/src/sdl/utils.rs b/gb/src/sdl/utils.rs index 537ff218..a648b468 100644 --- a/gb/src/sdl/utils.rs +++ b/gb/src/sdl/utils.rs @@ -1,6 +1,5 @@ use std::ffi::CStr; use sdl2::{libc::c_char, sys::*}; -#[cfg(feature = "apu")] use std::mem::MaybeUninit; pub fn get_sdl_error_message()->&'static str{ @@ -11,7 +10,6 @@ pub fn get_sdl_error_message()->&'static str{ } } -#[cfg(feature = "apu")] pub fn init_sdl_audio_device(audio_spec:&SDL_AudioSpec)->SDL_AudioDeviceID{ let mut uninit_audio_spec:MaybeUninit = MaybeUninit::uninit(); diff --git a/rpi/.cargo/config.toml b/rpi/.cargo/config.toml new file mode 100644 index 00000000..d93416cb --- /dev/null +++ b/rpi/.cargo/config.toml @@ -0,0 +1,5 @@ +[target.armv7a-none-eabihf] +rustflags = [ + "-Clink-arg=--script=./rpi/src/bin/baremetal/link.ld", + "-Ctarget-feature=+virtualization" +] \ No newline at end of file diff --git a/rpi/Cargo.toml b/rpi/Cargo.toml new file mode 100644 index 00000000..a3ed9a5b --- /dev/null +++ b/rpi/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "rpi" +version.workspace = true +authors.workspace = true +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lib_gb = {path = "../lib_gb", features = ["u16pixel"]} +common = {path = "../common", optional = true} +log = "0.4" +image_inter = {path = "../image_inter"} +libc = {version = "0.2", optional = true} +nix = {version = "0.24", optional = true} +crossbeam-channel = {version = "0.5", optional = true} +rppal = {version = "0.14", optional = true} + +[features] +default = ["rpi4"] +rpi4 = [] +rpi2 = [] +std = ["common", "libc", "nix/ioctl", "crossbeam-channel", "rppal"] + +[[bin]] +name = "rpios" +required-features = ["std"] + +[[bin]] +name = "baremetal" \ No newline at end of file diff --git a/rpi/build.rs b/rpi/build.rs new file mode 100644 index 00000000..01f573e2 --- /dev/null +++ b/rpi/build.rs @@ -0,0 +1,26 @@ +#[cfg(not(feature = "std"))] +mod config{ + pub const LD_SCRIPT_PATH:&str = "src/bin/baremetal/link.ld"; + pub const CONFIG_FILE_PATH: &str = "config.txt"; + pub const CONFIG_TXT_CONTENT:&str = + "# configuration for the RPI + arm_64bit=0 # boot to 32 bit mode + + # fast boot + boot_delay=0 + disable_poe_fan=1 + disable_splash=1"; +} + +fn main(){ + #[cfg(feature = "std")] + { + println!("cargo:rustc-link-lib=bcm_host"); + println!("cargo:rustc-link-search=/opt/vc/lib"); + } + #[cfg(not(feature = "std"))] + { + println!("cargo:rerun-if-changed={}", config::LD_SCRIPT_PATH); + std::fs::write(config::CONFIG_FILE_PATH, config::CONFIG_TXT_CONTENT).unwrap(); + } +} \ No newline at end of file diff --git a/baremetal/src/boot.rs b/rpi/src/bin/baremetal/boot.rs similarity index 80% rename from baremetal/src/boot.rs rename to rpi/src/bin/baremetal/boot.rs index 09a96c72..f6f02179 100644 --- a/baremetal/src/boot.rs +++ b/rpi/src/bin/baremetal/boot.rs @@ -1,7 +1,7 @@ use core::arch::{global_asm, asm}; #[no_mangle] -static PERIPHERALS_BASE_ADDRESS:u32 = crate::peripherals::PERIPHERALS_BASE_ADDRESS as u32; +static PERIPHERALS_BASE_ADDRESS:u32 = rpi::peripherals::PERIPHERALS_BASE_ADDRESS as u32; global_asm!(include_str!("boot_armv7a.s")); diff --git a/baremetal/src/boot_armv7a.s b/rpi/src/bin/baremetal/boot_armv7a.s similarity index 100% rename from baremetal/src/boot_armv7a.s rename to rpi/src/bin/baremetal/boot_armv7a.s diff --git a/baremetal/link.ld b/rpi/src/bin/baremetal/link.ld similarity index 100% rename from baremetal/link.ld rename to rpi/src/bin/baremetal/link.ld diff --git a/baremetal/src/logging.rs b/rpi/src/bin/baremetal/logging.rs similarity index 95% rename from baremetal/src/logging.rs rename to rpi/src/bin/baremetal/logging.rs index e89ed733..59ec642f 100644 --- a/baremetal/src/logging.rs +++ b/rpi/src/bin/baremetal/logging.rs @@ -1,6 +1,6 @@ use core::fmt::Write; -use crate::{peripherals::{MiniUart, PERIPHERALS}, syncronization::Mutex}; +use rpi::{peripherals::{MiniUart, PERIPHERALS}, syncronization::Mutex}; use log::{Record, Metadata, Log, LevelFilter}; diff --git a/baremetal/src/main.rs b/rpi/src/bin/baremetal/main.rs similarity index 86% rename from baremetal/src/main.rs rename to rpi/src/bin/baremetal/main.rs index b26745b0..571891cb 100644 --- a/baremetal/src/main.rs +++ b/rpi/src/bin/baremetal/main.rs @@ -2,17 +2,13 @@ #![no_std] mod boot; -mod syncronization; -mod peripherals; mod logging; -mod drivers; -mod configuration; use core::panic::PanicInfo; use lib_gb::{apu::audio_device::AudioDevice, machine::{gameboy::GameBoy, mbc_initializer::initialize_mbc}, mmu::external_memory_bus::Bootrom}; -use crate::{drivers::{GpioJoypadProvider, Ili9341GfxDevice}, peripherals::PERIPHERALS, configuration::{display::*, joypad::button_to_bcm_pin, emulation::*}}; +use rpi::{drivers::{GpioJoypadProvider, Ili9341GfxDevice}, peripherals::PERIPHERALS, configuration::{display::*, joypad::button_to_bcm_pin, emulation::*}}; struct BlankAudioDevice; impl AudioDevice for BlankAudioDevice{ diff --git a/rpi/src/bin/rpios/main.rs b/rpi/src/bin/rpios/main.rs new file mode 100644 index 00000000..a23eb6a5 --- /dev/null +++ b/rpi/src/bin/rpios/main.rs @@ -0,0 +1,150 @@ +use std::{env, fs}; + +use common::{emulation_menu::{MagenBoyMenu, MagenBoyState}, joypad_menu::joypad_gfx_menu, mpmc_gfx_device::MpmcGfxDevice, mbc_handler::{initialize_mbc, release_mbc}}; +use lib_gb::{ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_WIDTH, SCREEN_HEIGHT}, gfx_device::{GfxDevice, Pixel}}, apu::audio_device::AudioDevice, mmu::{GBC_BOOT_ROM_SIZE, external_memory_bus::Bootrom, GB_BOOT_ROM_SIZE}, machine::{Mode, gameboy::GameBoy}, keypad::joypad_provider::JoypadProvider}; +use log::info; +use rpi::{configuration::{emulation::*, display::*, joypad::*}, drivers::*, peripherals::PERIPHERALS}; + +const MENU_PIN_BCM:u8 = 3; // This pin is the turn on pin on thr RPI + +// This is static and not local for the unix signal handler to access it +static EMULATOR_STATE:MagenBoyState = MagenBoyState::new(); + +fn main(){ + unsafe{rpi::peripherals::PERIPHERALS.set_core_clock()}; + common::init_fern_logger().unwrap(); + let mut joypad_provider = GpioJoypadProvider::new(button_to_bcm_pin); + let mut gfx = Ili9341GfxDevice::new(RESET_PIN_BCM, LED_PIN_BCM, TURBO, FRAME_LIMITER); + + let args: Vec = env::args().collect(); + + let header = std::format!("MagenBoy v{}", common::VERSION); + + let mut emulation_menu = MagenBoyMenu::new(joypad_provider.clone(), header.clone()); + + while !(EMULATOR_STATE.exit.load(std::sync::atomic::Ordering::Relaxed)){ + + let program_name = if check_for_terminal_feature_flag(&args, "--rom-menu"){ + let roms_path = get_terminal_feature_flag_value(&args, "--rom-menu", "Error! no roms folder specified"); + let menu_renderer = joypad_gfx_menu::GfxDeviceMenuRenderer::new(&mut gfx); + common::get_rom_selection(roms_path.as_str(), menu_renderer, &mut joypad_provider) + } + else{ + args[1].clone() + }; + + let (s,r) = crossbeam_channel::bounded(BUFFERS_NUMBER - 1); + let mpmc_device = MpmcGfxDevice::new(s); + + let joypad_clone = joypad_provider.clone(); + let args_clone = args.clone(); + let emualation_thread = std::thread::Builder::new().name("Emualtion Thread".to_string()).spawn( + move || emulation_thread_main(args_clone, program_name, mpmc_device, joypad_clone) + ).unwrap(); + + unsafe{ + let handler = nix::sys::signal::SigHandler::Handler(sigint_handler); + nix::sys::signal::signal(nix::sys::signal::Signal::SIGINT, handler).unwrap(); + let menu_pin = PERIPHERALS.get_gpio().take_pin(MENU_PIN_BCM).into_input(rpi::peripherals::GpioPull::PullUp); + + loop{ + if menu_pin.read_state() == false{ + emulation_menu.pop_game_menu(&EMULATOR_STATE, &mut gfx, r.clone()); + } + + match r.recv() { + Result::Ok(buffer) => gfx.swap_buffer(&*(buffer as *const [Pixel; SCREEN_WIDTH * SCREEN_HEIGHT])), + Result::Err(_) => break, + } + } + + drop(r); + EMULATOR_STATE.running.store(false, std::sync::atomic::Ordering::Relaxed); + emualation_thread.join().unwrap(); + } + } + + if check_for_terminal_feature_flag(&args, "--shutdown-rpi"){ + log::info!("Shuting down the RPi! Goodbye"); + std::process::Command::new("shutdown").arg("-h").arg("now").spawn().expect("Failed to shutdown system"); + } +} + +struct BlankAudioDevice; +impl AudioDevice for BlankAudioDevice{ + fn push_buffer(&mut self, _:&[lib_gb::apu::audio_device::StereoSample; lib_gb::apu::audio_device::BUFFER_SIZE]) {} +} + +fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_device: MpmcGfxDevice, joypad_provider:impl JoypadProvider) { + + let bootrom_path = if check_for_terminal_feature_flag(&args, "--bootrom"){ + get_terminal_feature_flag_value(&args, "--bootrom", "Error! you must specify a value for the --bootrom parameter") + }else{ + String::from("dmg_boot.bin") + }; + + let bootrom = match fs::read(&bootrom_path){ + Result::Ok(file)=>{ + info!("found bootrom!"); + if file.len() == GBC_BOOT_ROM_SIZE{ + Bootrom::Gbc(file.try_into().unwrap()) + } + else if file.len() == GB_BOOT_ROM_SIZE{ + Bootrom::Gb(file.try_into().unwrap()) + } + else{ + std::panic!("Error! bootrom: \"{}\" length is invalid", bootrom_path); + } + } + Result::Err(_)=>{ + info!("Could not find bootrom... booting directly to rom"); + Bootrom::None + } + }; + + let mode = match bootrom{ + Bootrom::Gb(_) => Some(Mode::DMG), + Bootrom::Gbc(_)=> Some(Mode::CGB), + Bootrom::None=>{ + if check_for_terminal_feature_flag(&args, "--mode"){ + let mode = get_terminal_feature_flag_value(&args, "--mode", "Error: Must specify a mode"); + let mode = mode.as_str().try_into().expect(format!("Error! mode cannot be: {}", mode.as_str()).as_str()); + Some(mode) + } + else{ + Option::None + } + } + }; + + let mbc = initialize_mbc(&program_name, mode); + let mut gameboy = GameBoy::new(mbc, joypad_provider , BlankAudioDevice, spsc_gfx_device, bootrom, mode); + + info!("initialized gameboy successfully!"); + + EMULATOR_STATE.running.store(true, std::sync::atomic::Ordering::Relaxed); + while EMULATOR_STATE.running.load(std::sync::atomic::Ordering::Relaxed){ + if !EMULATOR_STATE.pause.load(std::sync::atomic::Ordering::SeqCst){ + let state = &EMULATOR_STATE; + let _mutex_ctx = state.state_mutex.lock().unwrap(); + gameboy.cycle_frame(); + } + } + drop(gameboy); + release_mbc(&program_name, mbc); + log::info!("released the gameboy succefully"); +} + +fn check_for_terminal_feature_flag(args:&Vec::, flag:&str)->bool{ + args.len() >= 3 && args.contains(&String::from(flag)) +} + +fn get_terminal_feature_flag_value(args:&Vec, flag:&str, error_message:&str)->String{ + let index = args.iter().position(|v| *v == String::from(flag)).unwrap(); + return args.get(index + 1).expect(error_message).clone(); +} + +extern "C" fn sigint_handler(_:std::os::raw::c_int){ + EMULATOR_STATE.running.store(false, std::sync::atomic::Ordering::Relaxed); + EMULATOR_STATE.exit.store(true, std::sync::atomic::Ordering::Relaxed); +} \ No newline at end of file diff --git a/baremetal/src/configuration.rs b/rpi/src/configuration.rs similarity index 94% rename from baremetal/src/configuration.rs rename to rpi/src/configuration.rs index 765200a6..97f2cff1 100644 --- a/baremetal/src/configuration.rs +++ b/rpi/src/configuration.rs @@ -26,7 +26,7 @@ pub mod peripherals{ pub const SPI0_DC_BCM_PIN:u8 = 12; pub const DMA_RX_CHANNEL_NUMBER:u8 = 7; pub const DMA_TX_CHANNEL_NUMBER:u8 = 1; - pub const FAST_SPI_CLOCK_DIVISOR:u32 = 6; // the smaller the faster + pub const FAST_SPI_CLOCK_DIVISOR:u32 = 8; // the smaller the faster pub const INIT_SPI_CLOCK_DIVISOR:u32 = 34; // slow clock for verifiying the initialization goes smooth with no corruptions } diff --git a/rpi/src/drivers/gpio_joypad.rs b/rpi/src/drivers/gpio_joypad.rs new file mode 100644 index 00000000..07d7a574 --- /dev/null +++ b/rpi/src/drivers/gpio_joypad.rs @@ -0,0 +1,62 @@ +use lib_gb::keypad::{joypad::{Joypad, NUM_OF_KEYS},joypad_provider::JoypadProvider, button::Button}; + +use crate::peripherals::{PERIPHERALS, GpioPull, Trigger, InputGpioPin}; + +const READ_THRESHOLD:u32 = 0x1000; + +pub struct GpioJoypadProvider{ + input_pins: [InputGpioPin; NUM_OF_KEYS], + read_threshold_counter: u32 +} + +impl GpioJoypadProvider{ + pub fn new(mapper:impl Fn(Button)->u8)->Self{ + let gpio = unsafe{PERIPHERALS.get_gpio()}; + let input_pins = [ + gpio.take_pin(mapper(Button::A)), + gpio.take_pin(mapper(Button::B)), + gpio.take_pin(mapper(Button::Start)), + gpio.take_pin(mapper(Button::Select)), + gpio.take_pin(mapper(Button::Up)), + gpio.take_pin(mapper(Button::Down)), + gpio.take_pin(mapper(Button::Right)), + gpio.take_pin(mapper(Button::Left)), + ]; + + return Self { input_pins:input_pins.map(|p|{ + let mut p = p.into_input(GpioPull::None); + p.set_interrupt(Trigger::RisingEdge); + return p; + }), read_threshold_counter: 0 }; + } +} + +impl JoypadProvider for GpioJoypadProvider{ + fn provide(&mut self, joypad:&mut Joypad){ + self.read_threshold_counter = (self.read_threshold_counter + 1) % READ_THRESHOLD; + if self.read_threshold_counter != 0 { + return; + } + for i in 0..joypad.buttons.len(){ + joypad.buttons[i] = self.input_pins[i].read_state(); + } + } +} + +#[cfg(feature = "std")] +impl common::joypad_menu::MenuJoypadProvider for GpioJoypadProvider { + fn poll(&mut self, joypad:&mut Joypad) { + let gpio = unsafe{PERIPHERALS.get_gpio()}; + gpio.poll_interrupts(&self.input_pins,false); + + for i in 0..joypad.buttons.len(){ + joypad.buttons[i] = self.input_pins[i].read_state(); + } + } +} + +impl Clone for GpioJoypadProvider{ + fn clone(&self) -> Self { + Self { input_pins: self.input_pins.clone(), read_threshold_counter: self.read_threshold_counter.clone() } + } +} \ No newline at end of file diff --git a/baremetal/src/drivers/ili9341_gfx_device.rs b/rpi/src/drivers/ili9341_gfx_device.rs similarity index 97% rename from baremetal/src/drivers/ili9341_gfx_device.rs rename to rpi/src/drivers/ili9341_gfx_device.rs index 876d2ce9..11cf5203 100644 --- a/baremetal/src/drivers/ili9341_gfx_device.rs +++ b/rpi/src/drivers/ili9341_gfx_device.rs @@ -1,6 +1,6 @@ use lib_gb::ppu::{gb_ppu::{SCREEN_WIDTH, SCREEN_HEIGHT}, gfx_device::{GfxDevice, Pixel}}; -use crate::peripherals::{GpioPin, Mode, Timer, Spi0, PERIPHERALS}; +use crate::peripherals::{Timer, Spi0, PERIPHERALS, OutputGpioPin}; const ILI9341_SCREEN_WIDTH:usize = 320; const ILI9341_SCREEN_HEIGHT:usize = 240; @@ -42,8 +42,8 @@ enum Ili9341Command{ struct Ili9341Contoller{ spi:Spi0, timer: Timer, - led_pin: GpioPin, - reset_pin: GpioPin + led_pin: OutputGpioPin, + reset_pin: OutputGpioPin } impl Ili9341Contoller{ @@ -53,8 +53,8 @@ impl Ili9341Contoller{ log::info!("Initalizing with screen size width: {}, hight: {}", TARGET_SCREEN_WIDTH, TARGET_SCREEN_HEIGHT); let gpio = unsafe{PERIPHERALS.get_gpio()}; - let reset_pin = gpio.take_pin(reset_pin_bcm, Mode::Output); - let led_pin = gpio.take_pin(led_pin_bcm, Mode::Output); + let reset_pin = gpio.take_pin(reset_pin_bcm).into_output(); + let led_pin = gpio.take_pin(led_pin_bcm).into_output(); let spi = unsafe{PERIPHERALS.take_spi0()}; let mut controller = Ili9341Contoller { spi, led_pin, reset_pin, timer: unsafe{PERIPHERALS.take_timer()}}; diff --git a/baremetal/src/drivers/mod.rs b/rpi/src/drivers/mod.rs similarity index 100% rename from baremetal/src/drivers/mod.rs rename to rpi/src/drivers/mod.rs diff --git a/rpi/src/lib.rs b/rpi/src/lib.rs new file mode 100644 index 00000000..3645c93e --- /dev/null +++ b/rpi/src/lib.rs @@ -0,0 +1,9 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", any(feature = "rpi4",feature = "rpi2")))] +core::compile_error!("rpiX features cant be combined with the std feature"); + +pub mod configuration; +pub mod peripherals; +pub mod drivers; +pub mod syncronization; \ No newline at end of file diff --git a/bcm_host/src/bcm.rs b/rpi/src/peripherals/bcm_host.rs similarity index 84% rename from bcm_host/src/bcm.rs rename to rpi/src/peripherals/bcm_host.rs index 4d54a4bb..7da35e5c 100644 --- a/bcm_host/src/bcm.rs +++ b/rpi/src/peripherals/bcm_host.rs @@ -1,6 +1,7 @@ - use libc::{c_uint, c_void}; +use super::utils::libc_abort; + // linking to - https://github.com/raspberrypi/firmware/blob/master/opt/vc/include/bcm_host.h extern "C"{ // There is no need to link to the init and deinit functions. it looks like they are needed only for the gpu dispnmx stuff @@ -14,8 +15,16 @@ pub struct BcmHost{ mem_fd: libc::c_int } +unsafe impl Sync for BcmHost{} + impl BcmHost { - pub fn new()->Self{ + pub fn get()->&'static mut Self{ + static mut HOST:Option = None; + + if let Some(host) = unsafe{&mut HOST} { + return host; + } + let mem_fd = unsafe{libc::open(std::ffi::CStr::from_bytes_with_nul(b"/dev/mem\0").unwrap().as_ptr(), libc::O_RDWR | libc::O_SYNC)}; if mem_fd < 0{ @@ -40,14 +49,17 @@ impl BcmHost { libc_abort("FATAL: mapping /dev/mem failed!"); } - BcmHost { ptr: bcm2835, mem_fd } + unsafe{ + HOST = Some(BcmHost { ptr: bcm2835, mem_fd }); + return HOST.as_mut().unwrap(); + } } pub fn get_ptr(&self, offset:usize)->*mut c_void{ unsafe{self.ptr.add(offset)} } - pub fn get_fd(&self)->libc::c_int{ + pub fn get_mem_fd(&self)->libc::c_int{ self.mem_fd } } @@ -67,8 +79,4 @@ impl Drop for BcmHost{ } } } -} - -fn libc_abort(message:&str){ - std::io::Result::<&str>::Err(std::io::Error::last_os_error()).expect(message); } \ No newline at end of file diff --git a/baremetal/src/peripherals/dma.rs b/rpi/src/peripherals/dma.rs similarity index 89% rename from baremetal/src/peripherals/dma.rs rename to rpi/src/peripherals/dma.rs index 47bc8a68..3a27e662 100644 --- a/baremetal/src/peripherals/dma.rs +++ b/rpi/src/peripherals/dma.rs @@ -1,6 +1,6 @@ use core::ptr::write_volatile; -use super::{utils::{MmioReg32, compile_time_size_assert, memory_barrier}, gpu::GpuMemory, PERIPHERALS_BASE_ADDRESS, spi::SPI_BUFFER_SIZE}; +use super::{utils::{MmioReg32, compile_time_size_assert, memory_barrier, get_static_peripheral}, gpu::GpuMemory, spi::SPI_BUFFER_SIZE}; // The DMA control block registers are in a 32 byte alligned addresses so the stracture mapping them needs to be as well // in order for me to cast some bytes to this stuct (at least I think so) @@ -44,8 +44,8 @@ pub struct DmaSpiTransferer{ } impl DmaSpiTransferer{ - const BCM_DMA0_ADDRESS:usize = PERIPHERALS_BASE_ADDRESS + 0x7_000; - const BCM_DMA_ENABLE_REGISTER_ADDRESS:usize = Self::BCM_DMA0_ADDRESS + 0xFF0; + const BCM_DMA0_OFFSET:usize = 0x7_000; + const BCM_DMA_ENABLE_REGISTER_OFFSET:usize = Self::BCM_DMA0_OFFSET + 0xFF0; const DMA_CHANNEL_REGISTERS_SIZE:usize = 0x100; const DMA_CS_RESET:u32 = 1 << 31; @@ -80,8 +80,8 @@ impl DmaSpiTransferer{ const DMA_SPI0_FIFO_PHYS_ADDRESS:u32 = 0x7E20_4004; pub fn new(spi_enable_dma_flag:u32)->Self{ - let tx_registers = unsafe{&mut *((Self::BCM_DMA0_ADDRESS + (Self::TX_CHANNEL_NUMBER as usize * Self::DMA_CHANNEL_REGISTERS_SIZE)) as *mut DmaRegistersAccess)}; - let rx_registers = unsafe{&mut *((Self::BCM_DMA0_ADDRESS + (Self::RX_CHANNEL_NUMBER as usize * Self::DMA_CHANNEL_REGISTERS_SIZE)) as *mut DmaRegistersAccess)}; + let tx_registers:&'static mut DmaRegistersAccess = get_static_peripheral(Self::BCM_DMA0_OFFSET + (Self::TX_CHANNEL_NUMBER as usize * Self::DMA_CHANNEL_REGISTERS_SIZE)); + let rx_registers:&'static mut DmaRegistersAccess = get_static_peripheral(Self::BCM_DMA0_OFFSET + (Self::RX_CHANNEL_NUMBER as usize * Self::DMA_CHANNEL_REGISTERS_SIZE)); let dma_tx_control_block_memory = GpuMemory::allocate( core::mem::size_of::() as u32 * Self::DMA_CONTROL_BLOCKS_PER_TRANSFER * Self::DMA_SPI_CHUNK_SIZE as u32 @@ -94,7 +94,7 @@ impl DmaSpiTransferer{ let dma_constant_memory = GpuMemory::allocate(Self::DMA_CONSTANT_MEMORY_SIZE); log::info!("Finish allocate gpu mem"); - let dma_enable_register = unsafe{&mut *(Self::BCM_DMA_ENABLE_REGISTER_ADDRESS as *mut MmioReg32)}; + let dma_enable_register:&mut MmioReg32 = get_static_peripheral(Self::BCM_DMA_ENABLE_REGISTER_OFFSET); unsafe{ // setup constant data @@ -136,6 +136,15 @@ impl DmaSpiTransferer{ return dma_controller; } + // This function initialize the DMA control blocks in order to create what I call a DMA Blocks Chain (lol) + // This chain can be triggered by writing the start address of the first block in the chain to the DMA control block register address + // The way the chains are constructed is - one standalone link that trasnfers a chunk of data (smaller than the DMA max transfer size) + // to the SPI and a Chain that reads the output of the SPI and discards it, once the SPI finishes transfering data back to us + // it means that the first link has already finished transfering data to the SPI. The chain will trigger another block that setups + // the next standalone link by writing to the DMA block address to the TX_DMA registers and starting it (This needs a chain of 3 links). + // The last link that triggers the nect chunk transferring will continue the chain to the link that will read the SPI output and discard it. + // This way Im constructing a chain long enough to transfer all the data even though its larger than the DMA max transfer size and triggering + // this chain by only coping the data to the DMA buffer and writing to the RX DMA and TX DMA start addresses. unsafe fn init_dma_control_blocks(&mut self) { let mut rx_control_block = &mut *(self.rx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock); rx_control_block.transfer_information.write(Self::dma_ti_permap(Self::DMA_TI_PERMAP_SPI_RX) | Self::DMA_TI_SRC_DREQ | Self::DMA_TI_DEST_IGNORE); diff --git a/rpi/src/peripherals/gpio.rs b/rpi/src/peripherals/gpio.rs new file mode 100644 index 00000000..2806eb05 --- /dev/null +++ b/rpi/src/peripherals/gpio.rs @@ -0,0 +1,339 @@ +#[cfg(not(feature = "std"))] +pub use no_std_impl::*; +#[cfg(feature = "std")] +pub use std_impl::*; + + +#[repr(u8)] +#[derive(Clone, Copy, PartialEq)] +pub enum Mode{ + Input = 0, + Output = 1, + Alt0 = 4, + Alt5 = 2 +} + +pub enum GpioPull{ + None = 0, + PullUp = 0b01, +} + +pub enum Trigger{ + RisingEdge +} + +#[cfg(not(feature = "std"))] +pub mod no_std_impl{ + use crate::{syncronization::Mutex, peripherals::utils::{compile_time_size_assert, MmioReg32, get_static_peripheral, memory_barrier}}; + use super::*; + + #[repr(C,align(4))] + struct GpioRegisters{ + gpfsel:[MmioReg32;6], + _pad0:u32, + gpset:[MmioReg32;2], + _pad1:u32, + gpclr:[MmioReg32;2], + _pad2:u32, + gplev:[MmioReg32;2], + _pad3:u32, + gpeds:[MmioReg32;2], + _pad4:u32, + gpren:[MmioReg32;2], + _pad5:[u32;36], + gpio_pup_pdn_cntrl:[MmioReg32;4] + } + compile_time_size_assert!(GpioRegisters, 0xF4); + + const RPI4_GPIO_PINS_COUNT:usize = 58; + const BASE_GPIO_OFFSET: usize = 0x20_0000; + static mut GPIO_REGISTERS:Option> = None; + + pub struct GpioManager{ + pins_availability:[bool;RPI4_GPIO_PINS_COUNT] + } + + impl GpioManager{ + pub(in crate::peripherals) fn new()->GpioManager{ + unsafe{GPIO_REGISTERS = Some(Mutex::new(get_static_peripheral(BASE_GPIO_OFFSET)));} + GpioManager { pins_availability: [true;RPI4_GPIO_PINS_COUNT]} + } + + pub fn take_pin(&mut self, bcm_pin_number:u8)->GpioPin{ + if self.pins_availability[bcm_pin_number as usize]{ + self.pins_availability[bcm_pin_number as usize] = false; + return GpioPin::new(bcm_pin_number); + } + core::panic!("Pin {} is already taken", bcm_pin_number); + } + + // This func is not tested + pub fn poll_interrupts(pins:&[InputGpioPin], reset_before_poll:bool){ + let gpio_registers = unsafe{GPIO_REGISTERS.as_mut().unwrap()}; + let pins_mask = pins + .iter() + .map(|p|p.inner.bcm_pin_number) + .fold(0_u64, |value, bcm_number| {value | (1 << bcm_number)}); + + memory_barrier(); + if reset_before_poll{ + gpio_registers.lock(|r|{ + // reset the event detection + r.gpeds[0].write(0); + r.gpeds[1].write(0); + }); + } + log::info!("polling gpio joypad input..."); + loop{ + let registers_value:u64 = gpio_registers.lock(|r|r.gpeds[0].read() as u64 | (r.gpeds[1].read() as u64) << 32); + log::info!("regs value: {:#X}", registers_value); + let detected_pins = registers_value & pins_mask; + if detected_pins != 0{ + log::info!("Detected gpio input interrupt"); + return; + } + } + } + } + + pub struct GpioPin{ + registers:&'static mut Mutex<&'static mut GpioRegisters>, + bcm_pin_number:u8, + } + + impl GpioPin{ + pub(super) fn new(bcm_pin_number:u8)->Self{ + let registers = unsafe{GPIO_REGISTERS.as_mut().unwrap()}; + return Self{registers, bcm_pin_number}; + } + + pub fn into_input(mut self, pull:GpioPull)->InputGpioPin{ + self.set_mode(Mode::Input); + self.set_pull(pull); + return InputGpioPin { inner: self }; + } + + pub fn into_output(mut self)->OutputGpioPin{ + self.set_mode(Mode::Output); + return OutputGpioPin { inner: self }; + } + + pub fn into_io(mut self, io_mode:Mode)->IoGpioPin{ + match io_mode{ + Mode::Alt0 | + Mode::Alt5 => self.set_mode(io_mode), + Mode::Input | + Mode::Output => core::panic!("set mode param must be alt: {}", io_mode as u8) + } + return IoGpioPin { inner: self }; + } + + fn set_pull(&mut self, pull_mode:GpioPull){ + let register_index = self.bcm_pin_number / 16; + let offset = (self.bcm_pin_number % 16) * 2; + let mask:u32 = 0b11 << offset; + memory_barrier(); + let register_value = self.registers.lock(|r|r.gpio_pup_pdn_cntrl[register_index as usize].read()); + let new_value = (register_value & !mask) | ((pull_mode as u32) << offset as u32); + self.registers.lock(|r|r.gpio_pup_pdn_cntrl[register_index as usize].write(new_value)); + memory_barrier(); + } + + fn get_single_bit_registers_offset_and_mask(&self)->(u8, u32){ + let offset = self.bcm_pin_number / 32; // since each registers contains 32 pins + let mask = 1 << (self.bcm_pin_number % 32); // get the position in the register + return (offset, mask); + } + + fn set_mode(&mut self, mode:Mode){ + let gpfsel_register = (self.bcm_pin_number / 10) as usize; // each registers contains 10 pins + let gpfsel_register_offset = (self.bcm_pin_number % 10) * 3; // each pin take 3 bits in the register + memory_barrier(); + self.registers.lock(|r|{ + let mut register_value = r.gpfsel[gpfsel_register].read(); + register_value &= !(0b111 << gpfsel_register_offset); // clear the specific bits + r.gpfsel[gpfsel_register].write(register_value | (mode as u32) << gpfsel_register_offset) + }); + memory_barrier(); + } + + fn set_state(&mut self, state:bool){ + let (register, value) = self.get_single_bit_registers_offset_and_mask(); + memory_barrier(); + if state{ + self.registers.lock(|r|r.gpset[register as usize].write(value)); + } + else{ + self.registers.lock(|r|r.gpclr[register as usize].write(value)); + } + memory_barrier(); + } + + fn read_state(&self)->bool{ + let (gplev_register, mask) = self.get_single_bit_registers_offset_and_mask(); + memory_barrier(); + let value = self.registers.lock(|r|r.gplev[gplev_register as usize].read()); + memory_barrier(); + return value & mask != 0; + } + } + + + pub struct InputGpioPin{ + inner: GpioPin + } + + impl InputGpioPin{ + pub fn read_state(&self)->bool{self.inner.read_state()} + + pub fn set_interrupt(&mut self, trigger:Trigger){ + let (register_index, mask) = self.inner.get_single_bit_registers_offset_and_mask(); + memory_barrier(); + self.inner.registers.lock(|r|{ + match trigger{ + Trigger::RisingEdge =>{ + let register = &mut r.gpren[register_index as usize]; + let value = register.read() | mask; + register.write(value); + } + } + }); + memory_barrier(); + } + } + + impl Clone for InputGpioPin{ + fn clone(&self) -> Self { + Self { + inner: GpioPin { + registers: unsafe{GPIO_REGISTERS.as_mut().unwrap()}, + bcm_pin_number: self.inner.bcm_pin_number + } + } + } + } + + pub struct OutputGpioPin{ + inner: GpioPin + } + + impl OutputGpioPin{ + pub fn set_state(&mut self, state:bool){self.inner.set_state(state)} + } + + pub struct IoGpioPin{ + inner: GpioPin + } + + impl IoGpioPin{ + pub fn set_pull(&mut self, pull:GpioPull){ + self.inner.set_pull(pull); + } + } +} + +#[cfg(feature = "std")] +pub mod std_impl{ + use rppal::gpio::{Gpio, InputPin, OutputPin, IoPin}; + use super::*; + + pub struct GpioManager{ + gpio:Gpio + } + + impl GpioManager{ + pub(in crate::peripherals) fn new()->Self{ + Self { gpio: Gpio::new().unwrap() } + } + + pub fn take_pin(&mut self, bcm_pin_number:u8)->GpioPin{ + return GpioPin { bcm_bumber: bcm_pin_number, gpio:self.gpio.clone()} + } + + /// Blocks untill there is an interrupt + pub fn poll_interrupts(&self, pins:&[InputGpioPin], reset_before_poll:bool){ + let pins:Vec<&InputPin> = pins.iter().map(|p|p.pin.as_ref()).collect(); + let (_pin, _level) = self.gpio.poll_interrupts(&pins, reset_before_poll, None).unwrap().unwrap(); + } + } + + pub struct GpioPin{ + bcm_bumber:u8, + gpio:Gpio + } + + impl GpioPin{ + pub fn into_input(self, pull:GpioPull)->InputGpioPin{ + let pin = self.gpio.get(self.bcm_bumber).unwrap(); + let pin = match pull{ + GpioPull::None => pin.into_input(), + GpioPull::PullUp => pin.into_input_pullup(), + }; + return InputGpioPin { pin: std::sync::Arc::new(pin) } + } + pub fn into_output(self)->OutputGpioPin{OutputGpioPin { pin: self.gpio.get(self.bcm_bumber).unwrap().into_output() }} + pub fn into_io(self, mode:Mode)->IoGpioPin{ + let pin = self.gpio.get(self.bcm_bumber).unwrap(); + let pin = match mode{ + Mode::Alt0 => pin.into_io(rppal::gpio::Mode::Alt0), + Mode::Alt5 => pin.into_io(rppal::gpio::Mode::Alt5), + Mode::Input | + Mode::Output => std::panic!("Cant set io pin to input or output") + }; + return IoGpioPin{pin}; + } + } + + pub struct InputGpioPin{ + pin:std::sync::Arc + } + + impl InputGpioPin{ + pub fn read_state(&self)->bool{self.pin.is_high()} + + pub fn set_interrupt(&mut self, t:Trigger){ + match t{ + Trigger::RisingEdge => std::sync::Arc::get_mut(&mut self.pin).unwrap().set_interrupt(rppal::gpio::Trigger::RisingEdge).unwrap(), + } + } + } + + impl Clone for InputGpioPin{ + fn clone(&self) -> Self { + Self { pin: self.pin.clone() } + } + } + + pub struct OutputGpioPin{ + pin:OutputPin + } + + impl OutputGpioPin{ + pub fn set_state(&mut self, state:bool){self.pin.write(state.into())} + } + + pub struct IoGpioPin{ + pin:IoPin + } + + impl IoGpioPin{ + pub fn set_pull(&mut self, pull_mode:GpioPull){ + match pull_mode{ + GpioPull::None => self.pin.set_pullupdown(rppal::gpio::PullUpDown::Off), + GpioPull::PullUp => todo!(), + } + } + } +} + + +// Sugar syntax functions +impl OutputGpioPin{ + pub fn set_high(&mut self){ + self.set_state(true) + } + + pub fn set_low(&mut self){ + self.set_state(false); + } +} \ No newline at end of file diff --git a/baremetal/src/peripherals/gpu.rs b/rpi/src/peripherals/gpu.rs similarity index 81% rename from baremetal/src/peripherals/gpu.rs rename to rpi/src/peripherals/gpu.rs index 1e83d050..f1833e4a 100644 --- a/baremetal/src/peripherals/gpu.rs +++ b/rpi/src/peripherals/gpu.rs @@ -42,11 +42,26 @@ impl GpuMemory{ } let address = Self::bus_to_phys(bus_address); + #[cfg(feature = "std")] + let address = unsafe{libc::mmap( + std::ptr::null_mut(), + size as libc::size_t, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED, + crate::peripherals::bcm_host::BcmHost::get().get_mem_fd(), + address as libc::off_t + )}; return GpuMemory { virtual_address_ptr: address as usize, bus_address, mailbox_memory_handle:handle, size } } fn release(&self){ + #[cfg(feature = "std")] + unsafe{ + if libc::munmap(self.virtual_address_ptr as *mut libc::c_void, self.size as libc::size_t) != 0 { + crate::peripherals::utils::libc_abort("Error while trying to unmap gpu memory"); + } + } let mbox = unsafe{PERIPHERALS.get_mailbox()}; if mbox.call(Self::UNLOCK_MEMORY_TAG, [self.mailbox_memory_handle])[0] != 0{ core::panic!("Error while trying to unlock gpu memory using mailbox"); diff --git a/rpi/src/peripherals/mailbox.rs b/rpi/src/peripherals/mailbox.rs new file mode 100644 index 00000000..d093484b --- /dev/null +++ b/rpi/src/peripherals/mailbox.rs @@ -0,0 +1,202 @@ +// This module is wrriten using the following sources +// Linux kernel for the rpi version 4.9 as a referrence - https://github.com/raspberrypi/linux/blob/2aae4cc63e30f99dd152fc63cc4a67ca29e4647b/drivers/firmware/raspberrypi.c +// This tutorial - https://www.rpi4os.com/part5-framebuffer/ +// The official mailbox docs - https://github.com/raspberrypi/firmware/wiki/Mailboxes + +//Turns out this peripheral needs a non cached memory + +use core::mem::size_of; + +use super::utils::compile_time_size_assert; + +#[cfg(feature = "std")] +pub use std_impl::Mailbox; +#[cfg(not(feature = "std"))] +pub use no_std_impl::Mailbox; + +#[repr(C, align(4))] +struct PropertyTagHeader{ + tag:u32, + buffer_size:u32, + + // On submit, the length of the request (though it doesn't appear to be currently used by the firmware). + // On return, the length of the response (always 4 byte aligned), with the low bit set. + request_response_size:u32 +} + +#[repr(u32)] +#[derive(Clone, Copy, PartialEq)] +enum PropertyStatus{ + Request = 0, + Success = 0x8000_0000, + _Error = 0x8000_0001 +} +compile_time_size_assert!(PropertyStatus, 4); + +// The 28 first bits are the address but the last 4 bits represnt the mailbox channel +// by aligning to 16 the last 4 bit are always 0 +#[repr(C, align(16))] +struct Message{ + size:u32, + status:PropertyStatus, + tag_header:PropertyTagHeader, + data:[u32;DATA_LEN], + property_end_marker:u32 +} + +impl Message{ + const MBOX_PROPERTY_END:u32 = 0; + + fn new(tag:u32, data:[u32;DATA_LEN])->Self{ + Self{ + size: size_of::() as u32, + status: PropertyStatus::Request, + tag_header: PropertyTagHeader { + tag, + buffer_size: (size_of::() * DATA_LEN) as u32, + request_response_size: 0 // zero since unused by the firmare + }, + data, + property_end_marker: Self::MBOX_PROPERTY_END + } + } +} + +#[cfg(not(feature = "std"))] +mod no_std_impl{ + use core::mem::size_of; + use crate::peripherals::utils::{MmioReg32, compile_time_size_assert, memory_barrier, get_static_peripheral}; + use super::*; + + const MBOX_BASE_OFFSET:usize = 0xB880; + const MBOX_CHANNEL:u32 = 8; // free channel for communication from the cpu to the core + + #[repr(C, align(4))] + struct MailboxRegisters{ + read_reg:MmioReg32, + _res:[u32;5], + status:MmioReg32, + _res1:u32, + write_reg:MmioReg32, + } + compile_time_size_assert!(MailboxRegisters, 0x24); + + pub struct Mailbox{ + registers:&'static mut MailboxRegisters, + uncached_buffer:&'static mut [u8] + } + + impl Mailbox{ + const STATUS_EMPTY:u32 = 0x4000_0000; + const STATUS_FULL:u32 = 0x8000_0000; + + pub(in crate::peripherals) fn new()->Mailbox{ + const MAILBOX_BUFFER_SIZE:usize = 0x100; + #[repr(align(16))] + struct MailboxBuffer([u8;MAILBOX_BUFFER_SIZE]); + #[no_mangle] + #[link_section = ".uncached"] + static mut MAILBOX_UNCACHED_BUFFER:MailboxBuffer = MailboxBuffer([0;MAILBOX_BUFFER_SIZE]); + + let registers:&mut MailboxRegisters = get_static_peripheral(MBOX_BASE_OFFSET); + let uncached_buffer:&'static mut [u8] = unsafe{&mut MAILBOX_UNCACHED_BUFFER.0}; + return Mailbox { registers, uncached_buffer }; + } + + pub fn call(&mut self, tag:u32, data:[u32;DATA_LEN])->[u32;DATA_LEN]{ + if size_of::>()> self.uncached_buffer.len() { + core::panic!("Error, Message with data len of {} bytes is too large ({}) and cant fit a {} bytes buffer", + DATA_LEN, size_of::>(), self.uncached_buffer.len()); + } + + let uncached_message = unsafe{ + let message = Message::new(tag, data); + core::ptr::copy_nonoverlapping(&message as *const Message as *const u8, self.uncached_buffer.as_mut_ptr(), size_of::>()); + &*(self.uncached_buffer.as_ptr() as *const Message) + }; + let mut message_address = (self.uncached_buffer.as_ptr()) as *const Message as u32; + if message_address & 0xF != 0{ + core::panic!("Error! mbox message is not alligned for 16 bytes") + } + message_address += MBOX_CHANNEL; + + memory_barrier(); + while self.registers.status.read() & Self::STATUS_FULL != 0{} // blocks untill mbox is avaliable + self.registers.write_reg.write(message_address); + + loop{ + while self.registers.status.read() & Self::STATUS_EMPTY != 0{} // block untill there is a response (non empty mailbox) + if self.registers.read_reg.read() == message_address{ + memory_barrier(); + if uncached_message.status == PropertyStatus::Success{ + return uncached_message.data; + } + core::panic!("Error in mbox call! tag: {:#X}, req_data: {:?}, status: {:#X}, res_data: {:?}", + tag, data, uncached_message.status as u32, uncached_message.data); + } + } + } + } +} + +#[cfg(feature = "std")] +mod std_impl{ + use libc::{c_int, c_void}; + + use crate::peripherals::utils::libc_abort; + + use super::*; + + pub struct Mailbox{ + mbox_fd: c_int, + } + + impl Mailbox{ + const MAILBOX_IOCTL_PROPERTY:libc::c_ulong = nix::request_code_readwrite!(100, 0, std::mem::size_of::<*mut libc::c_void>()); + + pub(in crate::peripherals) fn new()->Self{ + let fd = unsafe{libc::open(std::ffi::CStr::from_bytes_with_nul(b"/dev/vcio\0").unwrap().as_ptr(), 0)}; + if fd < 0{ + std::panic!("Error while opening vc mailbox"); + } + + Self { mbox_fd: fd } + } + + pub fn call(&mut self, tag:u32, data:[u32;SIZE])->[u32;SIZE]{ + let mut message = Message::::new(tag, data); + return self.send_message(&mut message); + } + + fn send_message(&self, message:&mut Message)->[u32;SIZE]{ + let raw_message = message as *mut Message as *mut c_void; + let ret = unsafe{ + // Using libc::ioctl and not nix high level abstraction over it cause Im sending a *void and not more + // concrete type and the nix macro will mess the types for us. I belive it could work with nix after some modification + // of the way Im handling this but Im leaving this as it for now. sorry! + libc::ioctl(self.mbox_fd, Self::MAILBOX_IOCTL_PROPERTY, raw_message) + }; + if ret < 0{ + libc_abort("Error in ioctl call"); + } + if message.status != PropertyStatus::Success{ + std::panic!("Error in mbox call! tag: {:#X}, data: {:?}, status: {:#X}", + message.tag_header.tag, message.data, message.status as u32); + } + + // The return value of the command is located at the first int in the data section (for more info see the Mailbox docs) + return message.data; + } + } + + impl Drop for Mailbox{ + fn drop(&mut self) { + unsafe{ + let result = libc::close(self.mbox_fd); + if result != 0{ + libc_abort("Error while closing the mbox fd"); + } + } + } + } +} \ No newline at end of file diff --git a/baremetal/src/peripherals/mini_uart.rs b/rpi/src/peripherals/mini_uart.rs similarity index 77% rename from baremetal/src/peripherals/mini_uart.rs rename to rpi/src/peripherals/mini_uart.rs index c224bed7..63536768 100644 --- a/baremetal/src/peripherals/mini_uart.rs +++ b/rpi/src/peripherals/mini_uart.rs @@ -1,9 +1,9 @@ -use super::{Mode, GpioPull, CORE_FREQ, PERIPHERALS_BASE_ADDRESS, utils::{MmioReg32, compile_time_size_assert, memory_barrier}, PERIPHERALS}; +use super::{Mode, GpioPull, CORE_FREQ, utils::{MmioReg32, compile_time_size_assert, memory_barrier, get_static_peripheral}, PERIPHERALS, IoGpioPin}; const UART_RX_PIN_BCM: u8 = 15; const UART_TX_PIN_BCM: u8 = 14; -const AUX_BASE_ADDRESS:usize = PERIPHERALS_BASE_ADDRESS + 0x21_5000; -const AUX_MINI_UART_ADDRESS:usize = AUX_BASE_ADDRESS + 0x40; +const AUX_BASE_OFFSET:usize = 0x21_5000; +const AUX_MINI_UART_OFFSET:usize = AUX_BASE_OFFSET + 0x40; #[repr(C, align(4))] struct AuxControlRegisters{ @@ -42,15 +42,16 @@ const AUX_ENABLES_ENABLE_UART:u32 = 1; pub struct MiniUart{ registers:&'static mut MiniUartRegisters, - _aux_control_registers:&'static AuxControlRegisters + _aux_control_registers:&'static AuxControlRegisters, + _pins:[IoGpioPin;2] } impl MiniUart{ - pub(super) fn new(baudrate:u32)->MiniUart{ + pub(super) fn new(baudrate:u32)->Self{ let gpio = unsafe{PERIPHERALS.get_gpio()}; // alt5 is the uart1 which the mini uart uses - let mut rx_pin = gpio.take_pin(UART_RX_PIN_BCM, Mode::Alt5); - let mut tx_pin = gpio.take_pin(UART_TX_PIN_BCM, Mode::Alt5); + let mut rx_pin = gpio.take_pin(UART_RX_PIN_BCM).into_io(Mode::Alt5); + let mut tx_pin = gpio.take_pin(UART_TX_PIN_BCM).into_io(Mode::Alt5); // set pull to none for the pins rx_pin.set_pull(GpioPull::None); @@ -58,8 +59,8 @@ impl MiniUart{ // the docs for bcm2835 says I might need to sleep here for 150 cycles, the bcm2711 omitted this and changed the registers - let control_regs = unsafe{&mut *(AUX_BASE_ADDRESS as *mut AuxControlRegisters)}; - let mini_uart_regs = unsafe{&mut *(AUX_MINI_UART_ADDRESS as *mut MiniUartRegisters)}; + let control_regs: &'static mut AuxControlRegisters = get_static_peripheral(AUX_BASE_OFFSET); + let mini_uart_regs: &'static mut MiniUartRegisters = get_static_peripheral(AUX_MINI_UART_OFFSET); // setup uart memory_barrier(); @@ -74,7 +75,7 @@ impl MiniUart{ memory_barrier(); // the pins are leaking right now, but I dont care - return MiniUart{registers:mini_uart_regs, _aux_control_registers:control_regs}; + return Self{registers:mini_uart_regs, _aux_control_registers:control_regs, _pins:[rx_pin, tx_pin]}; } pub fn send(&mut self, data:&[u8]){ diff --git a/baremetal/src/peripherals/mod.rs b/rpi/src/peripherals/mod.rs similarity index 84% rename from baremetal/src/peripherals/mod.rs rename to rpi/src/peripherals/mod.rs index da40318b..8b11486f 100644 --- a/baremetal/src/peripherals/mod.rs +++ b/rpi/src/peripherals/mod.rs @@ -6,21 +6,20 @@ mod gpu; mod spi; mod dma; mod timer; +#[cfg(feature = "std")] +mod bcm_host; pub use gpio::*; pub use mini_uart::MiniUart; pub use mailbox::*; pub use timer::*; pub use spi::Spi0; -use utils::Peripheral; +#[cfg(not(feature = "std"))] +pub use utils::PERIPHERALS_BASE_ADDRESS; +use utils::Peripheral; use crate::configuration::peripherals::*; -#[cfg(feature = "rpi4")] -pub const PERIPHERALS_BASE_ADDRESS:usize = 0xFE00_0000; -#[cfg(feature = "rpi2")] -pub const PERIPHERALS_BASE_ADDRESS:usize = 0x3F00_0000; - pub struct Peripherals{ gpio_manager: Peripheral, mini_uart: Peripheral, @@ -36,8 +35,8 @@ impl Peripherals{ const CORE_CLOCK_ID:u32 = 4; let mbox = self.get_mailbox(); let result = mbox.call(Self::SET_CLOCK_RATE_TAG, [CORE_CLOCK_ID, CORE_FREQ, 0]); - if result[1] != CORE_FREQ{ - core::panic!("Error, set core clock failed"); + if result[0] != CORE_CLOCK_ID || result[1] != CORE_FREQ{ + core::panic!("Error, set core clock failed, \nfreq: {}, clock id: {}", result[1], result[0]); } } pub fn take_mini_uart(&mut self)->mini_uart::MiniUart{ diff --git a/baremetal/src/peripherals/spi.rs b/rpi/src/peripherals/spi.rs similarity index 87% rename from baremetal/src/peripherals/spi.rs rename to rpi/src/peripherals/spi.rs index 5eb2dfe1..ca6f6c7a 100644 --- a/baremetal/src/peripherals/spi.rs +++ b/rpi/src/peripherals/spi.rs @@ -1,8 +1,8 @@ -use crate::{peripherals::{Mode, dma::DmaSpiTransferer, utils::memory_barrier, PERIPHERALS}, drivers, configuration::peripherals::*}; +use crate::{peripherals::{Mode, dma::DmaSpiTransferer, utils::{memory_barrier, get_static_peripheral}, PERIPHERALS}, drivers, configuration::peripherals::*}; -use super::{utils::{MmioReg32, compile_time_size_assert}, GpioPin, PERIPHERALS_BASE_ADDRESS}; +use super::{utils::{MmioReg32, compile_time_size_assert}, IoGpioPin, OutputGpioPin}; -const SPI0_BASE_ADDRESS:usize = PERIPHERALS_BASE_ADDRESS + 0x20_4000; +const SPI0_BASE_OFFSET:usize = 0x20_4000; // Fix this usafe of the driver module here pub(super)const SPI_BUFFER_SIZE:usize = drivers::SPI_BUFFER_SIZE; @@ -19,14 +19,14 @@ compile_time_size_assert!(SpiRegisters, 0x10); pub struct Spi0{ spi_registers: &'static mut SpiRegisters, - dc_pin:GpioPin, + dc_pin:OutputGpioPin, last_transfer_was_dma:bool, dma:DmaSpiTransferer, // holding those pins in order to make sure they are configured correctly // the state resets upon drop - _spi_pins:[GpioPin;2], - _spi_cs0:GpioPin, + _spi_pins:[IoGpioPin;2], + _spi_cs0:OutputGpioPin, } impl Spi0{ @@ -45,14 +45,14 @@ impl Spi0{ pub(super) fn new (dc_pin:u8)->Self{ let gpio = unsafe{PERIPHERALS.get_gpio()}; - let mut spi_cs0 = gpio.take_pin(Self::SPI0_CE0_N_BCM_PIN, Mode::Output); - let dc_pin = gpio.take_pin(dc_pin, Mode::Output); + let mut spi_cs0 = gpio.take_pin(Self::SPI0_CE0_N_BCM_PIN).into_output(); + let dc_pin = gpio.take_pin(dc_pin).into_output(); let spi_pins = [ - gpio.take_pin(Self::SPI0_MOSI_BCM_PIN, Mode::Alt0), - gpio.take_pin(Self::SPI0_SCLK_BCM_PIN, Mode::Alt0) + gpio.take_pin(Self::SPI0_MOSI_BCM_PIN).into_io(Mode::Alt0), + gpio.take_pin(Self::SPI0_SCLK_BCM_PIN).into_io(Mode::Alt0) ]; - let spi_registers = unsafe{&mut *(SPI0_BASE_ADDRESS as *mut SpiRegisters)}; + let spi_registers = get_static_peripheral(SPI0_BASE_OFFSET); // ChipSelect = 0, ClockPhase = 0, ClockPolarity = 0 spi_cs0.set_low(); diff --git a/baremetal/src/peripherals/timer.rs b/rpi/src/peripherals/timer.rs similarity index 85% rename from baremetal/src/peripherals/timer.rs rename to rpi/src/peripherals/timer.rs index e16b386f..d20c2227 100644 --- a/baremetal/src/peripherals/timer.rs +++ b/rpi/src/peripherals/timer.rs @@ -1,8 +1,8 @@ use core::time::Duration; -use super::{utils::{MmioReg32, compile_time_size_assert, memory_barrier}, PERIPHERALS_BASE_ADDRESS}; +use super::utils::{MmioReg32, compile_time_size_assert, memory_barrier, get_static_peripheral}; -const TIMER_BASE_ADDRESS:usize = PERIPHERALS_BASE_ADDRESS + 0x3000; +const TIMER_BASE_OFFSET:usize = 0x3000; // timer frequency is 1_000_000 hz // This number is based on this - https://www.youtube.com/watch?v=2dlBZoLCMSc @@ -22,7 +22,7 @@ pub struct Timer{ impl Timer{ pub(super) fn new()->Self{ - let registers = unsafe{&mut *(TIMER_BASE_ADDRESS as *mut TimerRegisters)}; + let registers = get_static_peripheral(TIMER_BASE_OFFSET); let mut timer = Self { registers, current_tick:0 }; // init current tick with valid value timer.current_tick = timer.get_timer_counter(); diff --git a/baremetal/src/peripherals/utils.rs b/rpi/src/peripherals/utils.rs similarity index 72% rename from baremetal/src/peripherals/utils.rs rename to rpi/src/peripherals/utils.rs index f9e3eff0..9f1fac6f 100644 --- a/baremetal/src/peripherals/utils.rs +++ b/rpi/src/peripherals/utils.rs @@ -50,4 +50,22 @@ impl Peripheral{ Self::Taken => core::panic!("Peripheral is unavaliable, its been taken"), }; } +} + + +#[cfg(feature = "rpi4")] +pub const PERIPHERALS_BASE_ADDRESS:usize = 0xFE00_0000; +#[cfg(feature = "rpi2")] +pub const PERIPHERALS_BASE_ADDRESS:usize = 0x3F00_0000; + +pub(super) fn get_static_peripheral(offset:usize)->&'static mut T{ + #[cfg(feature = "std")] + unsafe{&mut *(super::bcm_host::BcmHost::get().get_ptr(offset) as *mut T)} + #[cfg(not(feature = "std"))] + unsafe{&mut *((offset + PERIPHERALS_BASE_ADDRESS) as *mut T)} +} + +#[cfg(feature = "std")] +pub(super) fn libc_abort(message:&str){ + std::io::Result::<&str>::Err(std::io::Error::last_os_error()).expect(message); } \ No newline at end of file diff --git a/baremetal/src/syncronization.rs b/rpi/src/syncronization.rs similarity index 100% rename from baremetal/src/syncronization.rs rename to rpi/src/syncronization.rs