Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relocatable UART #62

Merged
merged 10 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/headsail-bsp-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@ unsafe fn rust_main() -> ! {

#[no_mangle]
pub extern "C" fn putc(byte: u8) {
headsail_bsp::apb_uart::putc(byte)
let mut uart = unsafe { headsail_bsp::apb_uart::ApbUart0::instance() };
uart.putc(byte)
}
4 changes: 2 additions & 2 deletions examples/headsail-bsp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ path = "examples/uart0.rs"
required-features = ["rt"]

[[example]]
name = "uart0-read"
path = "examples/uart0-read.rs"
name = "uart0_read"
path = "examples/uart0_read.rs"
required-features = ["alloc", "rt", "sdram"]

[[example]]
Expand Down
12 changes: 12 additions & 0 deletions examples/headsail-bsp/Justfile
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
run example target='riscv64imac':
cargo run --example {{example}} -Fhpc-rt -Fpanic-apb-uart0 --target {{target}}-unknown-none-elf

check-hpc:
cargo check --examples -Fhpc-rt -Fpanic-apb-uart0 -Falloc --target riscv64imac-unknown-none-elf

check-sysctrl:
cargo check --examples -Fsysctrl-rt -Fpanic-apb-uart0 --target riscv32im-unknown-none-elf

clippy-hpc:
cargo clippy --examples -Fhpc-rt -Fpanic-apb-uart0 -Falloc --target riscv64imac-unknown-none-elf -- -Dclippy::style

clippy-sysctrl:
cargo clippy --examples -Fsysctrl-rt -Fpanic-apb-uart0 --target riscv32im-unknown-none-elf -- -Dclippy::style
6 changes: 4 additions & 2 deletions examples/headsail-bsp/examples/uart0.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#![no_std]
#![no_main]

use headsail_bsp::{apb_uart::uart_write, rt::entry};
use headsail_bsp::{apb_uart::ApbUart0, rt::entry};

#[entry]
fn main() -> ! {
uart_write("Hello world!");
let (soc_freq, baud) = (30_000_000, 115_200);
let mut uart = ApbUart0::init(soc_freq, baud);
uart.write_str("Hello world!");
loop {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
#![no_main]

extern crate alloc;
use headsail_bsp::{apb_uart::uart_read_to_heap, init_alloc, rt::entry, sprint, sprintln};
use headsail_bsp::{apb_uart::ApbUart0, init_alloc, rt::entry, sprint, sprintln};

#[entry]
fn main() -> ! {
let (soc_freq, baud) = (30_000_000, 115_200);
let mut uart = ApbUart0::init(soc_freq, baud);

sprintln!("Connect to APB UART 0 with: screen /tmp/uart0");
init_alloc();
loop {
let res = uart_read_to_heap(8);
let res = uart.read_to_heap(8);
for x in res {
if x != 0 {
sprint!("{:x} ", x)
Expand Down
169 changes: 106 additions & 63 deletions examples/headsail-bsp/src/apb_uart.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
mmap::{UART0_ADDR, UART0_THR, UART_DATA_READY_OFFSET},
mmap::{UART0_ADDR, UART1_ADDR},
read_u8, write_u8,
};

Expand All @@ -8,77 +8,120 @@ extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;

#[cfg(feature = "asic")]
#[inline]
pub fn init_uart(freq: u32, baud: u32) {
use crate::mmap::{
UART0_DIV_LSB, UART0_DIV_MSB, UART0_FIFO_CONTROL, UART0_INTERRUPT_ENABLE,
UART0_LINE_CONTROL, UART0_MODEM_CONTROL,
};
const PERIPH_CLK_DIV: u32 = 2;
let divisor: u32 = freq / PERIPH_CLK_DIV / (baud << 4);

// Safety: unknown; we don't know if 8-bit writes will succeed
unsafe {
// Disable all interrupts
write_u8(UART0_INTERRUPT_ENABLE, 0x00);
// Enable DLAB (set baud rate divisor)
write_u8(UART0_LINE_CONTROL, 0x80);
// Divisor (lo byte)
write_u8(UART0_DIV_LSB, divisor as u8);
// Divisor (hi byte)
write_u8(UART0_DIV_MSB, (divisor >> 8) as u8);
// 8 bits, no parity, one stop bit
write_u8(UART0_LINE_CONTROL, 0x03);
// Enable FIFO, clear them, with 14-byte threshold
write_u8(UART0_FIFO_CONTROL, 0xC7);
// Autoflow mode
write_u8(UART0_MODEM_CONTROL, 0x20);
}
}
/// Relocatable driver for NS16550 UART IP
///
/// The generic represents the base address for the UART. This driver is
/// compatible with both ASIC and the VP. Use
///
/// * `-Fasic` for ASIC implementation
/// * and `-Fvp` for VP implementation.
pub struct ApbUart<const BASE_ADDRESS: usize>;

/// Dummy definition for VP, as the UART on VP does not require configuration
#[cfg(feature = "vp")]
pub fn init_uart(_freq: u32, _baud: u32) {}
/// Type alias for APB UART 0
pub type ApbUart0 = ApbUart<UART0_ADDR>;

#[cfg(feature = "asic")]
#[inline]
fn is_transmit_empty() -> bool {
// Safety: UART_LINE_STATUS is 4-byte aligned
unsafe { (read_u8(crate::mmap::UART0_LINE_STATUS) & 0x20) != 0 }
}
/// Type alias for APB UART 1
pub type ApbUart1 = ApbUart<UART1_ADDR>;

#[inline]
pub fn uart_write(s: &str) {
for b in s.as_bytes() {
putc(*b);
impl<const BASE_ADDR: usize> ApbUart<BASE_ADDR> {
/// # Parameters
///
/// * `soc_freq`- used to calculate BAUD rate together with divisor
/// * `baud` - target BAUD (sa. UART protocol)
#[allow(unused_variables)]
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,
};

// Safety: unknown; we don't know if 8-bit writes will succeed
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);
}
}
Self {}
}

/// # Safety
///
/// Returns a potentially uninitialized instance of APB UART. On ASIC, make
/// sure to call [ApbUart::init] prior to this call, otherwise the UART
/// won't behave properly.
pub const unsafe fn instance() -> Self {
Self {}
}
}

#[inline]
pub fn putc(c: u8) {
// Wait for hardware to report completion
#[cfg(feature = "asic")]
while !is_transmit_empty() {}
#[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 }
}

// Safety: UART_THR is 4-byte aligned
unsafe { write_u8(UART0_THR, c) };
}
#[inline]
pub fn write(&mut self, buf: &[u8]) {
for b in buf {
self.putc(*b);
}
}

#[inline]
pub fn getc() -> u8 {
// Wait for data to become ready
while unsafe { read_u8(UART0_ADDR + UART_DATA_READY_OFFSET) } & 1 == 0 {}
#[inline]
pub fn write_str(&mut self, s: &str) {
self.write(s.as_bytes());
}

// SAFETY: UART0_ADDR is 4-byte aligned
unsafe { read_u8(UART0_ADDR) }
}
/// Flush this output stream, blocking until all intermediately buffered contents reach their
/// destination.
#[inline]
pub fn flush(&mut self) {
// Wait for hardware to report completion
#[cfg(feature = "asic")]
while !self.is_transmit_empty() {}
}

#[cfg(feature = "alloc")]
pub fn uart_read_to_heap(bytes: usize) -> Vec<u8> {
let mut result = Vec::with_capacity(bytes);
for _ in 0..bytes {
result.push(getc())
#[inline]
pub fn putc(&mut self, c: u8) {
// Wait for hardware to report completion
#[cfg(feature = "asic")]
while !self.is_transmit_empty() {}

// Safety: UART_THR is 4-byte aligned
unsafe { write_u8(BASE_ADDR + crate::mmap::UART_THR_OFFSET, 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 {}

// SAFETY: UART0_ADDR is 4-byte aligned
unsafe { read_u8(UART0_ADDR) }
}

#[cfg(feature = "alloc")]
pub fn read_to_heap(&mut self, bytes: usize) -> Vec<u8> {
let mut result = Vec::with_capacity(bytes);
for _ in 0..bytes {
result.push(self.getc())
}
result
}
result
}
24 changes: 14 additions & 10 deletions examples/headsail-bsp/src/mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@
pub(crate) const UART0_ADDR: usize = 0x1FFF00000;
#[cfg(not(feature = "hpc"))]
pub(crate) const UART0_ADDR: usize = 0xFFF00000;
pub(crate) const UART0_THR: usize = UART0_ADDR;

#[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 {
use super::UART0_ADDR;

pub(crate) const UART0_DIV_LSB: usize = UART0_ADDR + 0;
pub(crate) const UART0_DIV_MSB: usize = UART0_ADDR + 1;
pub(crate) const UART0_INTERRUPT_ENABLE: usize = UART0_ADDR + 1;
pub(crate) const UART0_FIFO_CONTROL: usize = UART0_ADDR + 2;
pub(crate) const UART0_LINE_CONTROL: usize = UART0_ADDR + 3;
pub(crate) const UART0_MODEM_CONTROL: usize = UART0_ADDR + 4;
pub(crate) const UART0_LINE_STATUS: usize = UART0_ADDR + 5;
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;
}
#[cfg(feature = "asic")]
pub(crate) use self::asic_uart::*;
Expand Down
13 changes: 6 additions & 7 deletions examples/headsail-bsp/src/sprintln.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
//! Macros to implement Rust-style print formatting using `print`/`println`
use crate::apb_uart::uart_write;

pub struct Uart;
use crate::apb_uart::ApbUart0;
pub const UART: ApbUart0 = unsafe { ApbUart0::instance() };

#[macro_export]
macro_rules! sprint {
($s:expr) => {{
use $crate::{sprintln, ufmt};
ufmt::uwrite!(sprintln::Uart {}, $s).unwrap()
ufmt::uwrite!(sprintln::UART, $s).unwrap()
}};
($($tt:tt)*) => {{
use $crate::{sprintln, ufmt};
ufmt::uwrite!(sprintln::Uart, $($tt)*).unwrap()
ufmt::uwrite!(sprintln::UART, $($tt)*).unwrap()
}};
}

Expand All @@ -29,11 +28,11 @@ macro_rules! sprintln {
}};
}

impl ufmt::uWrite for Uart {
impl ufmt::uWrite for ApbUart0 {
type Error = core::convert::Infallible;

fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
uart_write(s);
self.write_str(s);
Ok(())
}
}
11 changes: 7 additions & 4 deletions examples/headsail-bsp/src/tb.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Common testbench definitions to retain a consistent test setup

use crate::apb_uart::uart_write;
use crate::apb_uart::ApbUart0;

pub const TAG_FAIL: &str = "[FAIL]";
pub const TAG_PASS: &str = "[PASS]";
Expand All @@ -9,13 +9,16 @@ pub const TAG_PASS: &str = "[PASS]";
pub const TAG_OK: &str = "[OK]";

pub fn report_pass() {
uart_write(TAG_PASS);
let mut uart = unsafe { ApbUart0::instance() };
uart.write_str(TAG_PASS);
}

pub fn report_fail() {
uart_write(TAG_FAIL);
let mut uart = unsafe { ApbUart0::instance() };
uart.write_str(TAG_FAIL);
}

pub fn report_ok() {
uart_write(TAG_OK);
let mut uart = unsafe { ApbUart0::instance() };
uart.write_str(TAG_OK);
}
3 changes: 3 additions & 0 deletions examples/hpc/dla-driver/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"rust-analyzer.cargo.target": "riscv64imac-unknown-none-elf",
"rust-analyzer.check.allTargets": false,
"rust-analyzer.cargo.extraArgs": [
"--examples"
]
}
Loading