From a74e029888e231f55eb895665ffed7bda07d9f08 Mon Sep 17 00:00:00 2001 From: Henri Lunnikivi Date: Sun, 25 Aug 2024 17:27:56 +0300 Subject: [PATCH] SysCtrl uDMA UART for ASIC (#63) BSP: * Depend optionally on headsail-pac * Expose PAC * Fix GPIO bug * Add periph_clk_div_set * Sanitize feature hierarchy * Add HAL for uDMA UART SysCtrl tests: * Update led test notes * Use debug syms in release builds * gdb(sysctrl): load program on connect * Use -Fpanic-apb-uart0 * Use -Fbsp/sysctrl-pac * Rename apb_uart0 * Add test: original raw led blinker * Add test: PAC-based uDMA UART * Add test: uDMA UART HAL --- examples/headsail-bsp/Cargo.toml | 7 +- examples/headsail-bsp/src/apb_uart.rs | 5 +- examples/headsail-bsp/src/lib.rs | 5 ++ examples/headsail-bsp/src/sysctrl/gpio.rs | 10 +-- examples/headsail-bsp/src/sysctrl/mmap.rs | 2 + examples/headsail-bsp/src/sysctrl/mod.rs | 2 + examples/headsail-bsp/src/sysctrl/soc_ctrl.rs | 9 +++ examples/headsail-bsp/src/sysctrl/udma.rs | 30 ++++++++ .../headsail-bsp/src/sysctrl/udma/uart.rs | 72 +++++++++++++++++++ examples/sysctrl/Cargo.toml | 3 + examples/sysctrl/hello-sysctrl/Cargo.toml | 3 +- .../hello-sysctrl/connect-and-load.gdb | 3 +- .../examples/{uart0.rs => apb_uart0.rs} | 5 +- .../sysctrl/hello-sysctrl/examples/led.rs | 3 +- .../sysctrl/hello-sysctrl/examples/led_raw.rs | 53 ++++++++++++++ .../hello-sysctrl/examples/udma_uart.rs | 38 ++++++++++ .../hello-sysctrl/examples/udma_uart_pac.rs | 63 ++++++++++++++++ 17 files changed, 295 insertions(+), 18 deletions(-) create mode 100644 examples/headsail-bsp/src/sysctrl/udma.rs create mode 100644 examples/headsail-bsp/src/sysctrl/udma/uart.rs rename examples/sysctrl/hello-sysctrl/examples/{uart0.rs => apb_uart0.rs} (76%) create mode 100644 examples/sysctrl/hello-sysctrl/examples/led_raw.rs create mode 100644 examples/sysctrl/hello-sysctrl/examples/udma_uart.rs create mode 100644 examples/sysctrl/hello-sysctrl/examples/udma_uart_pac.rs diff --git a/examples/headsail-bsp/Cargo.toml b/examples/headsail-bsp/Cargo.toml index 031a35e6..cef0a94a 100644 --- a/examples/headsail-bsp/Cargo.toml +++ b/examples/headsail-bsp/Cargo.toml @@ -20,9 +20,12 @@ alloc = ["dep:good_memory_allocator", "hpc"] sdram = [] vp = [] asic = [] +sysctrl-pac = ["dep:headsail-sysctrl-pac", "sysctrl", "pac"] +hpc-pac = ["dep:headsail-hpc-pac", "hpc", "pac"] -# This is generated by the above options, don't use directly +# These are generated by the above options, don't use directly rt = ["dep:riscv-rt"] +pac = [] [dependencies] ufmt = "0.2.0" @@ -32,6 +35,8 @@ riscv-peripheral = { version = "0.1.0", optional = true } riscv-pac = { version = "0.1.1", optional = true } good_memory_allocator = { version = "0.1.7", optional = true } bit_field = "0.10.2" +headsail-sysctrl-pac = { git = "https://github.com/soc-hub-fi/headsail-pac", version = "0.1.1", optional = true } +headsail-hpc-pac = { git = "https://github.com/soc-hub-fi/headsail-pac", version = "0.1.1", optional = true } [[example]] name = "panic" diff --git a/examples/headsail-bsp/src/apb_uart.rs b/examples/headsail-bsp/src/apb_uart.rs index 8e81153d..2b4e080f 100644 --- a/examples/headsail-bsp/src/apb_uart.rs +++ b/examples/headsail-bsp/src/apb_uart.rs @@ -32,13 +32,14 @@ impl ApbUart { pub fn init(soc_freq: u32, baud: u32) -> Self { #[cfg(feature = "asic")] { - const PERIPH_CLK_DIV: u32 = 2; - let divisor: u32 = soc_freq / PERIPH_CLK_DIV / (baud << 4); use crate::mmap::{ UART_DIV_LSB_OFFSET, UART_DIV_MSB_OFFSET, UART_FIFO_CONTROL_OFFSET, UART_INTERRUPT_ENABLE_OFFSET, UART_LINE_CONTROL_OFFSET, UART_MODEM_CONTROL_OFFSET, }; + const PERIPH_CLK_DIV: u32 = 2; + let divisor: u32 = soc_freq / PERIPH_CLK_DIV / (baud << 4); + // Safety: unknown; we don't know if 8-bit writes will succeed unsafe { // Disable all interrupts diff --git a/examples/headsail-bsp/src/lib.rs b/examples/headsail-bsp/src/lib.rs index d25cc8e7..579a9187 100644 --- a/examples/headsail-bsp/src/lib.rs +++ b/examples/headsail-bsp/src/lib.rs @@ -22,6 +22,10 @@ pub mod timer { pub use crate::timer_unit::*; } pub mod tb; +#[cfg(feature = "hpc-pac")] +pub use headsail_hpc_pac as pac; +#[cfg(feature = "sysctrl-pac")] +pub use headsail_sysctrl_pac as pac; #[cfg(not(feature = "vp"))] mod apb_timer; @@ -75,6 +79,7 @@ pub fn mask_u32(addr: usize, mask: u32) { unsafe { core::ptr::write_volatile(addr as *mut _, r | mask) } } +/// Unmasks supplied bits from given register #[inline(always)] pub fn unmask_u32(addr: usize, unmask: u32) { let r = unsafe { core::ptr::read_volatile(addr as *const u32) }; diff --git a/examples/headsail-bsp/src/sysctrl/gpio.rs b/examples/headsail-bsp/src/sysctrl/gpio.rs index 0400d79c..7bbba1a6 100644 --- a/examples/headsail-bsp/src/sysctrl/gpio.rs +++ b/examples/headsail-bsp/src/sysctrl/gpio.rs @@ -26,25 +26,19 @@ pub struct Gpio { pub type Gpio9 = Gpio<9, S>; -#[repr(u32)] -enum Dir { - In = 0, - Out = 1, -} - impl Gpio { pub(crate) fn new() -> Self { Self { _pd: PhantomData } } pub fn into_input(self) -> Gpio { - mask_u32(mmap::GPIO_DIR, Dir::In as u32); + unmask_u32(mmap::GPIO_DIR, 1 << IDX); Gpio { _pd: PhantomData } } pub fn into_output(self) -> Gpio { - mask_u32(mmap::GPIO_DIR, Dir::Out as u32); + mask_u32(mmap::GPIO_DIR, 1 << IDX); Gpio { _pd: PhantomData } } diff --git a/examples/headsail-bsp/src/sysctrl/mmap.rs b/examples/headsail-bsp/src/sysctrl/mmap.rs index 99323446..9fcea949 100644 --- a/examples/headsail-bsp/src/sysctrl/mmap.rs +++ b/examples/headsail-bsp/src/sysctrl/mmap.rs @@ -13,3 +13,5 @@ pub(crate) const PADMUX1: usize = SOC_CONTROL_ADDR + 0x14; pub(crate) const SS_RESET_EN: usize = SOC_CONTROL_ADDR + 0xb0; pub(crate) const SS_CLK_CTRL2: usize = SOC_CONTROL_ADDR + 0x9c; pub(crate) const SS_CLK_CTRL3: usize = SOC_CONTROL_ADDR + 0xb8; + +pub(crate) const PERIPH_CLK_DIV: usize = SOC_CONTROL_ADDR + 0xA8; diff --git a/examples/headsail-bsp/src/sysctrl/mod.rs b/examples/headsail-bsp/src/sysctrl/mod.rs index 124feeb0..deb2adff 100644 --- a/examples/headsail-bsp/src/sysctrl/mod.rs +++ b/examples/headsail-bsp/src/sysctrl/mod.rs @@ -1,6 +1,8 @@ //! Abstractions that only exist on SysCtrl pub mod gpio; pub mod soc_ctrl; +#[cfg(feature = "pac")] +pub mod udma; mod interrupt; mod mmap; diff --git a/examples/headsail-bsp/src/sysctrl/soc_ctrl.rs b/examples/headsail-bsp/src/sysctrl/soc_ctrl.rs index 502555e7..6c09db5a 100644 --- a/examples/headsail-bsp/src/sysctrl/soc_ctrl.rs +++ b/examples/headsail-bsp/src/sysctrl/soc_ctrl.rs @@ -101,3 +101,12 @@ pub fn clk2_set(conf_val: u32) { pub fn clk3_set(conf_val: u32) { write_u32(mmap::SS_CLK_CTRL3, conf_val); } + +/// # Parameters +/// +/// * `div` - value to set the `div` register to. Divider will be 1 << `div` +/// (unverified). +pub fn periph_clk_div_set(div: u32) { + let valid_bit = 0x400; + write_u32(mmap::PERIPH_CLK_DIV, valid_bit | div) +} diff --git a/examples/headsail-bsp/src/sysctrl/udma.rs b/examples/headsail-bsp/src/sysctrl/udma.rs new file mode 100644 index 00000000..1ec21eed --- /dev/null +++ b/examples/headsail-bsp/src/sysctrl/udma.rs @@ -0,0 +1,30 @@ +pub mod uart; + +use core::marker::PhantomData; + +use crate::pac; +pub use uart::UdmaUart; + +/// Type-state trait for uDMA peripherals in different states +pub trait UdmaPeriphState {} + +pub struct Enabled; +impl UdmaPeriphState for Enabled {} + +pub struct Disabled; +impl UdmaPeriphState for Disabled {} + +/// Relocatable driver for uDMA IP +pub struct Udma<'u>(pub &'u pac::sysctrl::Udma); + +pub struct UdmaParts<'u> { + pub uart: UdmaUart<'u, Disabled>, +} + +impl<'u> Udma<'u> { + pub fn split(self) -> UdmaParts<'u> { + UdmaParts { + uart: UdmaUart::(self.0, PhantomData::default()), + } + } +} diff --git a/examples/headsail-bsp/src/sysctrl/udma/uart.rs b/examples/headsail-bsp/src/sysctrl/udma/uart.rs new file mode 100644 index 00000000..68706852 --- /dev/null +++ b/examples/headsail-bsp/src/sysctrl/udma/uart.rs @@ -0,0 +1,72 @@ +use core::marker::PhantomData; + +use super::{Disabled, Enabled}; +use crate::pac; + +/// Obtain an instance by calling [Udma::split] +pub struct UdmaUart<'u, UdmaPeriphState>( + pub(crate) &'u pac::sysctrl::Udma, + pub(crate) PhantomData, +); + +type UartSetupW = pac::sysctrl::udma::uart_setup::W; + +impl<'u> UdmaUart<'u, Disabled> { + #[inline] + pub fn enable(self, setup_spec: F) -> UdmaUart<'u, Enabled> + where + F: FnOnce(&mut UartSetupW) -> &mut UartSetupW, + { + let udma = &self.0; + + // Turn on the clock gates for UART + udma.ctrl_cfg_cg().modify(|_r, w| w.cg_uart().set_bit()); + + // Setup UART + udma.uart_setup().write(|w| unsafe { w.bits(0) }); + udma.uart_setup().write(setup_spec); + + UdmaUart::(self.0, PhantomData::default()) + } +} + +impl<'u> UdmaUart<'u, Enabled> { + #[inline] + pub fn disable(self) -> UdmaUart<'u, Disabled> { + self.0.ctrl_cfg_cg().modify(|_r, w| w.cg_uart().clear_bit()); + UdmaUart::(self.0, PhantomData::default()) + } + + /// # Safety + /// + /// This will not configure the UART in any way. + #[inline] + pub unsafe fn steal(udma: &'static pac::sysctrl::Udma) -> Self { + Self(udma, PhantomData::default()) + } + + #[inline] + pub fn write(&mut self, buf: &[u8]) { + let udma = &self.0; + + // Write buffer location & len + udma.uart_tx_saddr() + .write(|w| unsafe { w.bits(buf.as_ptr() as u32) }); + udma.uart_tx_size() + .write(|w| unsafe { w.bits(buf.len() as u32) }); + + // Dispatch transmission + udma.uart_tx_cfg().write( + |w| w.en().set_bit(), // If we want "continuous mode". In continuous mode, uDMA reloads the address and transmits it again + //.continous().set_bit() + ); + + // Poll until finished (prevents `buf` leakage) + while udma.uart_tx_saddr().read().bits() != 0 {} + } + + #[inline] + pub fn write_str(&mut self, s: &str) { + self.write(s.as_bytes()); + } +} diff --git a/examples/sysctrl/Cargo.toml b/examples/sysctrl/Cargo.toml index 40160cc4..8e8b4960 100644 --- a/examples/sysctrl/Cargo.toml +++ b/examples/sysctrl/Cargo.toml @@ -1,3 +1,6 @@ [workspace] members = ["hello-sysctrl"] resolver = "2" + +[profile.release] +debug = true diff --git a/examples/sysctrl/hello-sysctrl/Cargo.toml b/examples/sysctrl/hello-sysctrl/Cargo.toml index 342bf6d2..550fadc7 100644 --- a/examples/sysctrl/hello-sysctrl/Cargo.toml +++ b/examples/sysctrl/hello-sysctrl/Cargo.toml @@ -11,5 +11,6 @@ asic = ["headsail-bsp/asic"] [dependencies] headsail-bsp = { version = "0.1.0", path = "../../headsail-bsp", features = [ "sysctrl-rt", + "sysctrl-pac", + "panic-apb-uart0", ] } -panic-halt = "0.2.0" diff --git a/examples/sysctrl/hello-sysctrl/connect-and-load.gdb b/examples/sysctrl/hello-sysctrl/connect-and-load.gdb index b7a2ce0d..bb5c832b 100644 --- a/examples/sysctrl/hello-sysctrl/connect-and-load.gdb +++ b/examples/sysctrl/hello-sysctrl/connect-and-load.gdb @@ -1,3 +1,4 @@ # Sysctrl is exposed at to 3333 target remote :3333 -backtrace \ No newline at end of file +load +backtrace diff --git a/examples/sysctrl/hello-sysctrl/examples/uart0.rs b/examples/sysctrl/hello-sysctrl/examples/apb_uart0.rs similarity index 76% rename from examples/sysctrl/hello-sysctrl/examples/uart0.rs rename to examples/sysctrl/hello-sysctrl/examples/apb_uart0.rs index 08a012f1..46aa565f 100644 --- a/examples/sysctrl/hello-sysctrl/examples/uart0.rs +++ b/examples/sysctrl/hello-sysctrl/examples/apb_uart0.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] -use headsail_bsp::{apb_uart::ApbUart0, rt::entry, sysctrl::soc_ctrl}; -use panic_halt as _; +use headsail_bsp::{apb_uart::ApbUart, rt::entry, sysctrl::soc_ctrl}; #[entry] fn main() -> ! { @@ -20,7 +19,7 @@ fn main() -> ! { soc_ctrl::clk3_set(conf_val); let (soc_freq, baud) = (30_000_000, 9600); - let mut uart = ApbUart0::init(soc_freq, baud); + let mut uart = ApbUart::<0xFFF00000>::init(soc_freq, baud); uart.write(b"Hello world!"); loop {} diff --git a/examples/sysctrl/hello-sysctrl/examples/led.rs b/examples/sysctrl/hello-sysctrl/examples/led.rs index a1a61c62..3770a385 100644 --- a/examples/sysctrl/hello-sysctrl/examples/led.rs +++ b/examples/sysctrl/hello-sysctrl/examples/led.rs @@ -3,13 +3,12 @@ //! | Date | Status | Changes | //! | :- | :-: | :- | //! | 2024-08-15 | *Works* | | -//! | 2024-08-15T1530 | Untested | Use HAL | +//! | 2024-08-23 | *Works* | Use HAL | #![no_std] #![no_main] use headsail_bsp::{rt::entry, sysctrl::soc_ctrl}; use hello_sysctrl::NOPS_PER_SEC; -use panic_halt as _; #[entry] fn main() -> ! { diff --git a/examples/sysctrl/hello-sysctrl/examples/led_raw.rs b/examples/sysctrl/hello-sysctrl/examples/led_raw.rs new file mode 100644 index 00000000..38a0a9f7 --- /dev/null +++ b/examples/sysctrl/hello-sysctrl/examples/led_raw.rs @@ -0,0 +1,53 @@ +//! Blinks a LED +//! +//! Tested working on ASIC: 2024-08-23 +#![no_std] +#![no_main] + +use core::ptr; +use headsail_bsp::rt::entry; + +// Below addresses are in SysCtrl memory space +const GPIO: usize = 0x1a10_1000; +const GPIO_DIR: usize = GPIO + 0x0; +const GPIO_OUT: usize = GPIO + 0xc; +const SOC_CONTROL: usize = 0x1a10_4000; +const PADMUX0: usize = SOC_CONTROL + 0x10; + +// Number of nops SysCtrl is capable of executing at 30 MHz reference clocks +const NOPS_PER_SEC: usize = match () { + #[cfg(debug_assertions)] + // This is an experimentally found value + () => 2_000_000 / 9, + #[cfg(not(debug_assertions))] + // This is just a guess for now (10x debug) + () => 200_000 / 9, +}; + +#[entry] +fn main() -> ! { + unsafe { + ptr::write_volatile(PADMUX0 as *mut _, 0); + ptr::write_volatile(GPIO_DIR as *mut _, 0); + + // Padmux enable GPIO9 + ptr::write_volatile(PADMUX0 as *mut _, 0x40000); + + // Set GPIO 9 as output + ptr::write_volatile(GPIO_DIR as *mut _, 1 << 9); + } + + loop { + unsafe { + // Toggle GPIO + let mut r = ptr::read_volatile(GPIO_OUT as *mut u32); + r ^= 1 << 9; + ptr::write_volatile(GPIO_OUT as *mut u32, r); + + // 1 second period + for _ in 0..NOPS_PER_SEC { + core::arch::asm!("nop"); + } + } + } +} diff --git a/examples/sysctrl/hello-sysctrl/examples/udma_uart.rs b/examples/sysctrl/hello-sysctrl/examples/udma_uart.rs new file mode 100644 index 00000000..90e8340a --- /dev/null +++ b/examples/sysctrl/hello-sysctrl/examples/udma_uart.rs @@ -0,0 +1,38 @@ +//! Prints over SysCtrl UART +#![no_std] +#![no_main] + +use headsail_bsp::{pac, rt::entry, sysctrl::udma::Udma}; + +#[entry] +fn main() -> ! { + let sysctrl = unsafe { pac::Sysctrl::steal() }; + let udma = Udma(sysctrl.udma()); + + // Set the bit length, enable TX, set clk_div + let (soc_freq, baud) = (30_000_000, 9600_u32); + let clk_div: u16 = (soc_freq / baud) as u16; + let mut uart = udma.split().uart.enable(|w| { + unsafe { + w + // Use this if using parity bit + .parity_ena() + .bit(false) + .bit_length() + .bits(0b11) + // Stop bit? + .stop_bits() + .bit(false) + .tx_ena() + .bit(true) + .rx_ena() + .bit(true) + .clkdiv() + .bits(clk_div) + } + }); + + uart.write(b"Hello uDMA UART HAL\r\n"); + + loop {} +} diff --git a/examples/sysctrl/hello-sysctrl/examples/udma_uart_pac.rs b/examples/sysctrl/hello-sysctrl/examples/udma_uart_pac.rs new file mode 100644 index 00000000..98a43f47 --- /dev/null +++ b/examples/sysctrl/hello-sysctrl/examples/udma_uart_pac.rs @@ -0,0 +1,63 @@ +//! Print using PAC-based register manipulation only. +//! +//! Tested working on ASIC: 2024-08-23 +#![no_std] +#![no_main] + +use headsail_bsp::{pac::Sysctrl, rt::entry, sysctrl::soc_ctrl}; + +#[entry] +fn main() -> ! { + let (soc_freq, baud) = (30_000_000, 9600_u32); + + soc_ctrl::periph_clk_div_set(0); + + let sysctrl = Sysctrl::ptr(); + + let udma = unsafe { (*sysctrl).udma() }; + // Enable UART clock pass-through at uDMA + udma.ctrl_cfg_cg().modify(|_r, w| w.cg_uart().set_bit()); + + // Reset configuration register prior to setting it up, this must be + // done to allow new configurations to take effect. + udma.uart_setup().write(|w| unsafe { w.bits(0) }); + + // Set the bit length, enable TX, set clk_div + let clk_div: u16 = (soc_freq / baud) as u16; + udma.uart_setup().write(|w| unsafe { + w + // Use this if using parity bit + .parity_ena() + .bit(false) + .bit_length() + .bits(0b11) + // Stop bit? + .stop_bits() + .bit(false) + .tx_ena() + .bit(true) + .rx_ena() + .bit(true) + .clkdiv() + .bits(clk_div) + }); + + let s = "[ok]\r\n"; + udma.uart_tx_saddr() + .write(|w| unsafe { w.bits(s.as_ptr() as u32) }); + udma.uart_tx_size() + .write(|w| unsafe { w.bits(s.len() as u32) }); + + // (3) Dispatch transmission + udma.uart_tx_cfg().write( + |w| w.en().set_bit(), // If we want "continuous mode". In continuous mode, uDMA reloads the address and transmits it again + //.continous().set_bit() + ); + + // (4) Poll until finished + while udma.uart_tx_saddr().read().bits() != 0 {} + + loop { + continue; + } +}