Skip to content

Commit

Permalink
Refactors & fixes based on ASIC testing (#64)
Browse files Browse the repository at this point in the history
## BSP

* add unsafe {mask/unmask}_u8
* make memory maps pub by default
    * this makes testing considerably easier, even if it breaks the HAL abstraction
    * expose top-level memory maps as well
* clean up UART driver & memory maps
* rerun build script on change
* fix clippy lints
* `Pads::steal`: add safety notice

## SysCtrl examples

* add timer0 test
* add APB UART0 (MMIO version): this is the most useful version for preliminary testing.
* APB UART0 (HAL version): update
* udma_uart: add missing periph_clk_div_set
* add Justfile
* fix clippy lints
* uDMA UART: now prints repeatedly
  • Loading branch information
hegza committed Sep 4, 2024
1 parent a74e029 commit f16e0e4
Show file tree
Hide file tree
Showing 18 changed files with 532 additions and 72 deletions.
3 changes: 3 additions & 0 deletions examples/headsail-bsp/Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ clippy-hpc:

clippy-sysctrl:
cargo clippy --examples -Fsysctrl-rt -Fpanic-apb-uart0 --target riscv32im-unknown-none-elf -- -Dclippy::style

clippy: clippy-sysctrl clippy-hpc

2 changes: 2 additions & 0 deletions examples/headsail-bsp/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
use std::{env, fs, path};

fn main() {
println!("cargo:rerun-if-changed=build.rs");

// Put link script in our output directory and ensure it's on the linker search path
let out = &path::PathBuf::from(env::var_os("OUT_DIR").unwrap());
fs::copy("mem_hpc.x", out.join("mem_hpc.x")).unwrap();
Expand Down
3 changes: 3 additions & 0 deletions examples/headsail-bsp/examples/timer0.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! | Date | Status | Changes |
//! | :- | :-: | :- |
//! | 2024-09-04 | *Untested* | |
#![no_std]
#![no_main]

Expand Down
78 changes: 54 additions & 24 deletions examples/headsail-bsp/src/apb_uart.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use core::arch::asm;

use crate::{
mmap::{UART0_ADDR, UART1_ADDR},
mmap::{UART0_ADDR, UART1_ADDR, UART_LSR_RX_FIFO_VALID, UART_RBR_THR_DLL_OFS},
read_u8, write_u8,
};

Expand Down Expand Up @@ -32,30 +34,50 @@ impl<const BASE_ADDR: usize> ApbUart<BASE_ADDR> {
pub fn init(soc_freq: u32, baud: u32) -> Self {
#[cfg(feature = "asic")]
{
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,
use crate::{
mask_u8,
mmap::{
UART_FCR_FIFO_EN_BIT, UART_FCR_FIFO_RX_RESET_BIT, UART_FCR_FIFO_TX_RESET_BIT,
UART_FCR_TRIG_RX_LSB, UART_FCR_TRIG_RX_MSB, UART_IER_DLM_OFS, UART_IIR_FCR_OFS,
UART_LCR_DLAB_BIT, UART_LCR_OFS, UART_RBR_THR_DLL_OFS,
},
unmask_u8,
};

const PERIPH_CLK_DIV: u32 = 2;
#[repr(u8)]
pub enum UartLcrDataBits {
/*
Bits5 = 0b00,
Bits6 = 0b01,
Bits7 = 0b10,
*/
Bits8 = 0b11,
}

const PERIPH_CLK_DIV: u32 = 1;
let divisor: u32 = soc_freq / PERIPH_CLK_DIV / (baud << 4);

// Safety: unknown; we don't know if 8-bit writes will succeed
// Safety: all PULP APB UART registers are 4-byte aligned so no bus can stop us
unsafe {
// Disable all interrupts
write_u8(BASE_ADDR + UART_INTERRUPT_ENABLE_OFFSET, 0x00);
// Enable DLAB (set baud rate divisor)
write_u8(BASE_ADDR + UART_LINE_CONTROL_OFFSET, 0x80);
// Divisor (lo byte)
write_u8(BASE_ADDR + UART_DIV_LSB_OFFSET, divisor as u8);
// Divisor (hi byte)
write_u8(BASE_ADDR + UART_DIV_MSB_OFFSET, (divisor >> 8) as u8);
// 8 bits, no parity, one stop bit
write_u8(BASE_ADDR + UART_LINE_CONTROL_OFFSET, 0x03);
// Enable FIFO, clear them, with 14-byte threshold
write_u8(BASE_ADDR + UART_FIFO_CONTROL_OFFSET, 0xC7);
// Autoflow mode
write_u8(BASE_ADDR + UART_MODEM_CONTROL_OFFSET, 0x20);
// Enable DLAB (to set baud rate divisor)
mask_u8(BASE_ADDR + UART_LCR_OFS, UART_LCR_DLAB_BIT);
// Set low & high byte of divisor
write_u8(BASE_ADDR + UART_RBR_THR_DLL_OFS, divisor as u8);
write_u8(BASE_ADDR + UART_IER_DLM_OFS, (divisor >> 8) as u8);
// Data is 8 bits, one stop bit, no parity
write_u8(BASE_ADDR + UART_LCR_OFS, UartLcrDataBits::Bits8 as u8);
// Restore DLAB state
unmask_u8(BASE_ADDR + UART_LCR_OFS, UART_LCR_DLAB_BIT);

// Enable FIFO, clear RX & TX, use 14-byte threshold
write_u8(
BASE_ADDR + UART_IIR_FCR_OFS,
UART_FCR_FIFO_EN_BIT
| UART_FCR_FIFO_RX_RESET_BIT
| UART_FCR_FIFO_TX_RESET_BIT
| UART_FCR_TRIG_RX_LSB
| UART_FCR_TRIG_RX_MSB,
);
}
}
Self {}
Expand All @@ -73,8 +95,10 @@ impl<const BASE_ADDR: usize> ApbUart<BASE_ADDR> {
#[cfg(feature = "asic")]
#[inline]
fn is_transmit_empty(&self) -> bool {
// Safety: UART_LINE_STATUS is 4-byte aligned
unsafe { (read_u8(BASE_ADDR + crate::mmap::UART_LINE_STATUS_OFFSET) & 0x20) != 0 }
use crate::mmap::{UART_LSR_OFS, UART_LSR_TX_FIFO_EMPTY_BIT};

// Safety: UART_LSR is 4-byte aligned
(unsafe { read_u8(BASE_ADDR + UART_LSR_OFS) } & UART_LSR_TX_FIFO_EMPTY_BIT) != 0
}

#[inline]
Expand Down Expand Up @@ -104,14 +128,20 @@ impl<const BASE_ADDR: usize> ApbUart<BASE_ADDR> {
#[cfg(feature = "asic")]
while !self.is_transmit_empty() {}

for _ in 0..20_000 {
unsafe { asm!("nop") };
}

// Safety: UART_THR is 4-byte aligned
unsafe { write_u8(BASE_ADDR + crate::mmap::UART_THR_OFFSET, c) };
unsafe { write_u8(BASE_ADDR + UART_RBR_THR_DLL_OFS, c) };
}

#[inline]
pub fn getc(&mut self) -> u8 {
// Wait for data to become ready
while unsafe { read_u8(UART0_ADDR + crate::mmap::UART_DATA_READY_OFFSET) } & 1 == 0 {}
while unsafe { read_u8(UART0_ADDR + crate::mmap::UART_LSR_OFS) } & UART_LSR_RX_FIFO_VALID
== 0
{}

// SAFETY: UART0_ADDR is 4-byte aligned
unsafe { read_u8(UART0_ADDR) }
Expand Down
2 changes: 1 addition & 1 deletion examples/headsail-bsp/src/hpc/hart_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ unsafe impl HartIdNumber for HartId {
Err(number)
} else {
// SAFETY: valid context number
Ok(unsafe { core::mem::transmute(number) })
Ok(unsafe { core::mem::transmute::<u16, HartId>(number) })
}
}
}
2 changes: 1 addition & 1 deletion examples/headsail-bsp/src/hpc/interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ unsafe impl PriorityNumber for Priority {
Err(number)
} else {
// SAFETY: valid priority number
Ok(unsafe { core::mem::transmute(number) })
Ok(unsafe { core::mem::transmute::<u8, Priority>(number) })
}
}
}
Expand Down
18 changes: 17 additions & 1 deletion examples/headsail-bsp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub use ufmt;
pub mod alloc;
#[cfg(feature = "hpc")]
mod hpc;
mod mmap;
pub mod mmap;
#[cfg(feature = "panic-apb-uart0")]
mod ufmt_panic;

Expand Down Expand Up @@ -79,13 +79,29 @@ pub fn mask_u32(addr: usize, mask: u32) {
unsafe { core::ptr::write_volatile(addr as *mut _, r | mask) }
}

/// # Safety
///
/// Unaligned writes may fail to produce expected results on RISC-V.
#[inline(always)]
pub fn mask_u8(addr: usize, mask: u8) {
let r = unsafe { core::ptr::read_volatile(addr as *const u8) };
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) };
unsafe { core::ptr::write_volatile(addr as *mut _, r & !unmask) }
}

/// Unmasks supplied bits from given register
#[inline(always)]
pub fn unmask_u8(addr: usize, unmask: u8) {
let r = unsafe { core::ptr::read_volatile(addr as *const u8) };
unsafe { core::ptr::write_volatile(addr as *mut _, r & !unmask) }
}

#[inline(always)]
pub fn toggle_u32(addr: usize, toggle_bits: u32) {
let mut r = read_u32(addr);
Expand Down
173 changes: 143 additions & 30 deletions examples/headsail-bsp/src/mmap.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,145 @@
#[cfg(feature = "hpc")]
pub(crate) const UART0_ADDR: usize = 0x1FFF00000;
#[cfg(not(feature = "hpc"))]
pub(crate) const UART0_ADDR: usize = 0xFFF00000;

#[cfg(feature = "hpc")]
pub(crate) const UART1_ADDR: usize = 0x1FFF01000;
#[cfg(not(feature = "hpc"))]
pub(crate) const UART1_ADDR: usize = 0xFFF01000;

pub(crate) const UART_THR_OFFSET: usize = 0;

// NOTE: (20240614 [email protected]) This applies to renode NS16550 uart, but might not apply to headsail ASIC
pub(crate) const UART_DATA_READY_OFFSET: usize = 5;

#[cfg(feature = "asic")]
mod asic_uart {
pub(crate) const UART_DIV_LSB_OFFSET: usize = 0;
pub(crate) const UART_DIV_MSB_OFFSET: usize = 1;
pub(crate) const UART_INTERRUPT_ENABLE_OFFSET: usize = 1;
pub(crate) const UART_FIFO_CONTROL_OFFSET: usize = 2;
pub(crate) const UART_LINE_CONTROL_OFFSET: usize = 3;
pub(crate) const UART_MODEM_CONTROL_OFFSET: usize = 4;
pub(crate) const UART_LINE_STATUS_OFFSET: usize = 5;
// Allowed for extra clarity in certain cases
#![allow(clippy::identity_op)]

pub const EXT_ACCESS_BIT: usize = match () {
#[cfg(feature = "hpc")]
() => 1 << 32,
#[cfg(not(feature = "hpc"))]
() => 0,
};

mod uart {
use super::EXT_ACCESS_BIT;

/// Separation between each UART register
///
/// NS16550 specifies a byte-spaced UART API, and this is indeed implemented
/// by the VP. However, the ASIC implements the PULP APB UART which comes
/// with word-spaced (32-bit) registers.
const REG_SEP: usize = match () {
#[cfg(feature = "vp")]
() => 1,
#[cfg(not(feature = "vp"))]
() => 4,
};

/// Location of PULP APB UART #0
pub const UART0_ADDR: usize = 0xFFF00000 | EXT_ACCESS_BIT;
/// Location of PULP APB UART #1
pub const UART1_ADDR: usize = 0xFFF01000 | EXT_ACCESS_BIT;

/// Receiver Buffer Register (RBR) / Transmitter Holding Register (THR) /
/// Divisor Latch LSB (DLL)
///
/// - `LCR[7] == 0`: RBR and THR are accessible
/// - `LCR[7] == 1`: DLL is accessible
///
/// ## Receiver Buffer Register (read-only)
///
/// Returns the next character buffered on UART and clears the register.
///
/// ## Transmitter Holding Register (write-only)
///
/// Sets the byte to be transmitted over UART.
///
/// ## Divisor latch LSB (read-write)
///
/// Reads or writes the 8 LSBs of the divisor that --- together with clock
/// frequency --- determines resultant BAUD rate.
pub const UART_RBR_THR_DLL_OFS: usize = 0;

/// Interrupt Enable Register (IER) / Divisor Latch MSB (DLM)
///
/// - `LCR[7] == 0`: IER is accessible
/// - `LCR[7] == 1`: DLM is accessible
///
/// ## Interrupt Enable Register (read-write, `[0:2]`)
///
/// - `[0]`: Interrupt is raised when...
/// - (fifo disabled) received data is available
/// - (fifo enabled) trigger level has been reached (sa.
/// [UART_IIR_FCR_OFS])
/// - character timeout has been reached
/// - `[1]`: Interrupt is raised when [UART_RBR_THR_DLL_OFS] is empty
/// - `[2]`: Interrupt is raised on Overrun error, parity error, framing
/// error or break interrupt
///
/// ## Divisor latch LSB (read-write)
///
/// Reads or writes the 8 MSBs of the divisor that --- together with clock
/// frequency --- determines resultant BAUD rate.
pub const UART_IER_DLM_OFS: usize = 1 * REG_SEP;

/// Interrupt Identification Register (IIR) / FIFO Control Register (FCR)
///
/// ## Interrupt Identification Register (read-only)
///
/// ## FIFO Control Register (write-only)
///
/// - `[1]`: Clear the RX FIFO
/// - `[2]`: Clear the TX FIFO
/// - `[6:7]`: Set the trigger level
/// - `0b00`: trigger level is high when there is 1 element in the fifo
/// - `0b01`: trigger level is high when there are 4 elements in the fifo
/// - `0b10`: trigger level is high when there are 8 elements in the fifo
/// - `0b11`: trigger level is high when there are 14 elements in the fifo
pub const UART_IIR_FCR_OFS: usize = 2 * REG_SEP;

// FIFO_EN_BIT seems to be undocumented
pub const UART_FCR_FIFO_EN_BIT: u8 = 0b1;
pub const UART_FCR_FIFO_RX_RESET_BIT: u8 = 0b1 << 1;
pub const UART_FCR_FIFO_TX_RESET_BIT: u8 = 0b1 << 2;
pub const UART_FCR_TRIG_RX_LSB: u8 = 0b1 << 6;
pub const UART_FCR_TRIG_RX_MSB: u8 = 0b1 << 7;

/// Line Control Register
///
/// LCR configures the main operation of the uart.It configures the width of the received data, stop bit, parity, and DLAB
/// bit.
///
/// - `[0:1]`: data configuration bits
/// - `0b00`: data is configured to be 5 bits
/// - `0b01`: data is configured to be 6 bits
/// - `0b10`: data is configured to be 7 bits
/// - `0b11`: data is configured to be 8 bits
/// - `[2]`: stop bit configuration
/// - `0b0`: 1 stop bit
/// - `0b1`: 1.5 stop bits for 5 bits data word OR 2 stop bits 6, 7 or 8 bits data word
/// - `[3]`: parity enable bit
/// - `[7]`: divisor latch access bit (DLAB)
/// - `0b0`: RBR, THR and IER accessible
/// - `0b1`: DLL and DLM accessible
pub const UART_LCR_OFS: usize = 3 * REG_SEP;

/// Divisor Latch Access Bit
pub const UART_LCR_DLAB_BIT: u8 = 0b1 << 7;

/// Line Status Register
///
/// - `[0]`: RX FIFO data valid
/// - `[1]`: *not used*
/// - `[2]`: parity error from the RX FIFO
/// - `[3]`: *not used*
/// - `[4]`: *not used*
/// - `[5]`: the TX FIFO is empty
/// - `[6]`: shift register and TX FIFO are empty
pub const UART_LSR_OFS: usize = 5 * REG_SEP;

pub const UART_LSR_RX_FIFO_VALID: u8 = 0b1;
pub const UART_LSR_TX_FIFO_EMPTY_BIT: u8 = 1 << 5;

// The following registers are not used by either PULP APB UART implemented
// on Headsail:
/*
pub const UART_MCR: usize = 4 * REG_SEP;
pub const UART_MODEM_STATUS_OFFSET: usize = 6 * REG_SEP;
pub const UART_SCRATCH_OFFSET: usize = 7 * REG_SEP;
*/
}
#[cfg(feature = "asic")]
pub(crate) use self::asic_uart::*;
pub use self::uart::*;

pub(crate) const TIMER0_ADDR: usize = 0x5_0000;
pub(crate) const TIMER1_ADDR: usize = 0x5_0010;
pub(crate) const TIMER2_ADDR: usize = 0x5_0020;
pub(crate) const TIMER3_ADDR: usize = 0x5_0030;
// HPC's timers
pub const TIMER0_ADDR: usize = 0x5_0000;
pub const TIMER1_ADDR: usize = 0x5_0010;
pub const TIMER2_ADDR: usize = 0x5_0020;
pub const TIMER3_ADDR: usize = 0x5_0030;
14 changes: 8 additions & 6 deletions examples/headsail-bsp/src/sysctrl/mmap.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Allowed for extra clarity in certain cases
#![allow(clippy::identity_op)]
/// SysCtrl-specific, SysCtrl internal memory maps

pub(crate) const SYSCTRL_ADDR: usize = 0x1a10_0000;
Expand All @@ -7,11 +9,11 @@ pub(crate) const GPIO_DIR: usize = GPIO_ADDR + 0x0;
pub(crate) const GPIO_OUT: usize = GPIO_ADDR + 0xc;

pub(crate) const SOC_CONTROL_ADDR: usize = SYSCTRL_ADDR + 0x4000;
pub(crate) const PADMUX0: usize = SOC_CONTROL_ADDR + 0x10;
pub(crate) const PADMUX1: usize = SOC_CONTROL_ADDR + 0x14;
pub const PADMUX0: usize = SOC_CONTROL_ADDR + 0x10;
pub 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 const SS_RESET_EN: usize = SOC_CONTROL_ADDR + 0xb0;
pub const SS_CLK_CTRL2: usize = SOC_CONTROL_ADDR + 0x9c;
pub const SS_CLK_CTRL3: usize = SOC_CONTROL_ADDR + 0xb8;

pub(crate) const PERIPH_CLK_DIV: usize = SOC_CONTROL_ADDR + 0xA8;
pub const PERIPH_CLK_DIV: usize = SOC_CONTROL_ADDR + 0xA8;
Loading

0 comments on commit f16e0e4

Please sign in to comment.