UART improvements #83

Merged
muellerr merged 1 commits from update-uart-interrupt-reception into main 2026-05-15 14:37:36 +02:00
3 changed files with 39 additions and 12 deletions
@@ -50,6 +50,7 @@ use zynq7000_hal::{
uart::{ClockConfig, Config, Uart},
};
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum UartMode {
Uart0ToUartlite,
Uart0ToUart16550,
@@ -74,6 +75,14 @@ const AXI_UAR16550_BASE_ADDR: u32 = 0x43C0_0000;
pub const UARTLITE_PL_INT_ID: usize = 0;
pub const UART16550_PL_INT_ID: usize = 1;
pub const UART_SPEED: u32 = 115_200;
// Other common baud rates to test with:
// pub const UART_SPEED: u32 = 9600;
// pub const UART_SPEED: u32 = 230_400;
// pub const UART_SPEED: u32 = 912_600;
const RB_SIZE: usize = 512;
// These queues are used to send all data received in the UART interrupt handlers to the main
@@ -239,15 +248,25 @@ async fn main(spawner: Spawner) -> ! {
Output::new_for_emio(gpio_pins.emio.take(9).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(10).unwrap(), PinState::Low),
]);
let mut uart_speed = UART_SPEED;
match UART_MODE {
UartMode::Uart0ToUartlite => uart_mux.select(UartSel::Uart0ToUartlite),
UartMode::Uart0ToUart16550 => uart_mux.select(UartSel::Uart0ToUart16550),
UartMode::UartliteToUart16550 => uart_mux.select(UartSel::UartliteToUart16550),
}
if (UART_MODE == UartMode::Uart0ToUartlite || UART_MODE == UartMode::UartliteToUart16550)
&& uart_speed != 115200
{
log::warn!("UARTLITE speed is not configurable. Hardcoding UART speed to 115200");
uart_speed = 115200;
}
let uart0_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), uart_speed)
.unwrap()
.0;
// UART0 routed through EMIO to PL pins.
let uart_0 =
Uart::new_with_emio(dp.uart_0, Config::new_with_clk_config(uart_clk_config)).unwrap();
Uart::new_with_emio(dp.uart_0, Config::new_with_clk_config(uart0_clk_config)).unwrap();
// Safety: Valid address of AXI UARTLITE.
let mut uartlite = unsafe { AxiUartlite::new(AXI_UARTLITE_BASE_ADDR) };
// We need to call this before splitting the structure, because the interrupt signal is
@@ -256,10 +275,15 @@ async fn main(spawner: Spawner) -> ! {
let (clk_config, error) = axi_uart16550::ClockConfig::new_autocalc_with_error(
fugit_03::HertzU32::from_raw(clocks.pl_clocks()[0].to_raw()),
115200,
uart_speed,
)
.unwrap();
assert!(error < 0.02);
if error > 0.02 {
log::warn!(
"Calculated clock config for AXI UART16550 has error of {} %, which is higher than 2%. This may lead to incorrect baud rate. Consider changing the input clock or the target baud rate.",
(error * 100.0)
);
}
let _uart_16550 = unsafe {
AxiUart16550::new(
AXI_UAR16550_BASE_ADDR,
@@ -289,7 +313,7 @@ async fn main(spawner: Spawner) -> ! {
let (uartlite_prod, mut uartlite_cons) = QUEUE_UARTLITE.take().split();
let (uart16550_prod, mut uart16550_cons) = QUEUE_UART16550.take().split();
// Use our helper function to start RX handling.
uart_0_rx.start_interrupt_driven_reception();
uart_0_rx.start_interrupt_driven_reception(0xFF);
// Use our helper function to start RX handling.
uart_16550_rx.start_interrupt_driven_reception();
critical_section::with(|cs| {
+3
View File
@@ -22,6 +22,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Changed
- Increased reliabily of PS UART interrupt reception, which was proven to be buggy for higher baud
rates: Force user to configure RTO value, encouraging non-zero values, and use a RX FIFO trigger
value of FIFO depth divided by 2 by default.
- `devcfg` moved to `pl` module
- Added division by zero check in gtc frequency_to_ticks to avoid runtime panic
- Increased UART type safety by providing dedicated MIO constructors for UART 0 and UART 1
+8 -8
View File
@@ -114,8 +114,14 @@ impl Rx {
///
/// This should be called once at system start-up. After that, you only need to call
/// [Self::on_interrupt] in the interrupt handler for the UART peripheral.
pub fn start_interrupt_driven_reception(&mut self) {
///
/// You can also configure a RX timeout by setting the RX timeout value `rto` which has a unit
/// of bit periods times 4. Setting a value of 0 disables the timeout feature of the hardware,
/// but this is strongly discouraged.
pub fn start_interrupt_driven_reception(&mut self, rto: u8) {
self.soft_reset();
self.set_rx_fifo_trigger_level((FIFO_DEPTH / 2) as u8);
self.set_rx_timeout_value(rto);
self.clear_interrupts();
self.enable_interrupts();
}
@@ -170,13 +176,7 @@ impl Rx {
return result;
}
let isr = self.regs.read_interrupt_status();
if isr.rx_full() {
// Read all bytes in the full RX fifo.
for byte in buf.iter_mut() {
*byte = self.read_fifo_unchecked();
}
result.read_bytes = FIFO_DEPTH;
} else if isr.rx_trg() {
if self.regs.read_interrupt_status().rx_trg() {
// It is guaranteed that we can read the FIFO level amount of data
let fifo_trigger = self.regs.read_rx_fifo_trigger().trig().as_usize();
(0..fifo_trigger).for_each(|i| {