Skip to content

Rust no-std, esp-hal with ESP32-C3 UART hangs at Uart::new() #1053

@Droog71

Description

@Droog71

This only happens on Wokwi. It works fine with the actual hardware.
Also, everything based on ESP-IDF, ie: esp-idf-hal, ESP-IDF and the Arduino framework all work fine.
The issue is only with Rust no-std (esp-hal) on Wokwi.

Here is a minimal example to reproduce, using LEDs to debug.
It hangs at Uart::new() or possibly Config::default().

#![no_std]
#![no_main]

use esp_hal::{
    clock::CpuClock,
    gpio::{Level, Output, OutputConfig},
    main,
    time::{Duration, Instant},
    uart::{Config, Uart},
};

#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
    loop {}
}

#[main]
fn main() -> ! {
    let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
    let peripherals = esp_hal::init(config);

    let mut led1 = Output::new(peripherals.GPIO5, Level::High, OutputConfig::default());
    let mut led2 = Output::new(peripherals.GPIO6, Level::High, OutputConfig::default());

    for _ in 0..16 {
        led1.toggle();
        let delay_start = Instant::now();
        while delay_start.elapsed() < Duration::from_millis(125) {}
    }

    let uart_cfg = Config::default().with_baudrate(115200);
    let mut uart = Uart::new(peripherals.UART0, uart_cfg)
        .unwrap()
        .with_rx(peripherals.GPIO20)
        .with_tx(peripherals.GPIO21);

    for _ in 0..16 {
        led2.toggle();
        let delay_start = Instant::now();
        while delay_start.elapsed() < Duration::from_millis(125) {}
    }

    loop {
        let mut error = false;
        let mut buf: [u8; 1] = [0; 1];

        while uart.read_ready() {
            match uart.read(&mut buf) {
                Ok(_) => {
                    match uart.write(&buf) {
                        Ok(_) => (),
                        Err(_) => error = true,
                    };
                }
                Err(_) => {
                    error = true;
                }
            };
        }

        if error {
            for _ in 0..16 {
                led1.toggle();
                led2.toggle();
                let delay_start = Instant::now();
                while delay_start.elapsed() < Duration::from_millis(125) {}
            }
        }
    }
}
[package]
edition = "2021"
name    = "uart-example"
version = "0.1.0"

[[bin]]
name = "uart-example"
path = "./src/bin/main.rs"

[dependencies]
critical-section = "1.2.0"
esp-hal = { version = "1.0.0-beta.0", features = ["esp32c3", "unstable"] }

[profile.dev]
# Rust debug is too slow.
# For debug builds always builds with some optimization
opt-level = "s"

[profile.release]
codegen-units    = 1     # LLVM can perform better optimizations using a single thread
debug            = 2
debug-assertions = false
incremental      = false
lto              = 'fat'
opt-level        = 's'
overflow-checks  = false

println! is working.
You can also write directly to the UART FIFO register.
For example:

#![no_std]
#![no_main]

use esp_backtrace as _;
use esp_println::println;
use esp_hal::{clock::ClockControl, delay::Delay, peripherals::Peripherals, prelude::*};

fn uart_tx(data: u8) {
    unsafe {
        core::arch::asm!("mv x13, {0}", in(reg) data);
        core::arch::asm!("li x12, 0x60000000");
        core::arch::asm!("andi x13, x13, 255");
        core::arch::asm!("sw x13, 0(x12)");
    }
}

#[entry]
fn main() -> ! {
    let peripherals = Peripherals::take();
    let system = peripherals.SYSTEM.split();
    let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
    let delay = Delay::new(&clocks);
    loop {
        for b in b"UART\n" {
            uart_tx(*b);
        }
        delay.delay_millis(1000u32);
        println!("PRINTLN");
        delay.delay_millis(1000u32);
    }
}

The output of this code is the following:

UART
PRINTLN
UART
PRINTLN

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions