Skip to content

Conversation

@NanaHigh
Copy link
Member

  • Add interrupt support for bl808 m0 and lp core.
  • Add new example adc-dma-demo, adc-async-demo.
    adc-async-demo
微信图片_20250916185808_31_121 adc-dma-demo 微信图片_20250916192615_32_121
  • Add opening uart console support , running on default config and more interactive console for blri.

    • We can use the following command to run project and open uart:
    # Normal output mode
    cargo run --target riscv64imac-unknown-none-elf --release -p uart-demo -- --reset
    # Console mode
    cargo run --target riscv64imac-unknown-none-elf --release -p uart-demo -- --reset --console
    # Run with default config automatically saved in ./target/settings.toml
    cargo blri-default
    record.mp4
  • Update README for blri.

- Add `adc-async-demo` and `adc-dma-demo` examples

Signed-off-by: Chongbing Yu <[email protected]>
- Add BlriConfig for persistent project settings with auto-save/load
- Implement two-step confirmation workflow for configuration conflicts
- Add integrated serial console/monitor with Ctrl+C exit support
- Add 'default' command for quick runs using saved configuration
- Support auto path detection for blri subdirectory execution
- Add cargo aliases: 'blri-default' for streamlined workflow
- Update README with comprehensive English documentation and examples

Signed-off-by: Chongbing Yu <[email protected]>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds interrupt support for BL808 M0 and LP cores, introduces two new ADC demo examples with DMA and async functionality, and enhances the blri tool with UART console support and configuration management.

  • Implements interrupt handling infrastructure for BL808 M0 and LP cores with trampoline functions and handlers
  • Adds adc-dma-demo and adc-async-demo examples showcasing different ADC usage patterns
  • Enhances blri with interactive console mode, configuration saving, and improved user experience

Reviewed Changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
examples/peripherals/adc-dma-demo/* New example demonstrating ADC with DMA transfers
examples/peripherals/adc-async-demo/* New example showcasing async/await ADC operations with interrupt-driven design
bouffalo-rt/src/soc/bl808.rs Implements interrupt support for M0/LP cores with trampolines and handlers
bouffalo-rt/macros/src/soc/* Adds interrupt validation for M0/LP cores
bouffalo-hal/src/gpip/* Extends GPIP with async ADC functionality and interrupt management
blri/src/* Major enhancements including console mode, configuration management, and improved UX

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 118 to 124
pub fn delay(tim: u32) {
unsafe {
for _ in 0..tim * 100 {
core::arch::asm!("nop");
}
}
}
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The delay function is a busy-wait implementation that doesn't actually provide accurate timing. Consider using a proper timer-based delay or documenting that this is an approximate delay for demonstration purposes only.

Copilot uses AI. Check for mistakes.
Comment on lines 114 to 116
let mut fut = core::pin::pin!(async_main(p, c));
let waker = Waker::noop();
let mut ctx = Context::from_waker(waker);
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Waker::noop() in an async context can lead to tasks never being woken up properly. The async functionality depends on proper waker implementation to function correctly.

Copilot uses AI. Check for mistakes.
Comment on lines +142 to +144
" la t0, {trap_entry}
ori t0, t0, {trap_mode}
csrw mtvec, t0",
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trap setup code is being executed in LP core initialization but the LP core uses RV32E architecture which has different register constraints. The trap_vectored function should be validated for RV32E compatibility.

Copilot uses AI. Check for mistakes.
Comment on lines 159 to 160
/* exceptions */
PROVIDE(exceptions = default_handler);
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The linker script includes duplicate exception handler definitions for both MCU and LP cores. Consider consolidating these definitions or using conditional compilation to avoid duplication.

Copilot uses AI. Check for mistakes.
/// Use this waker set to handle ADC interrupt.
#[inline]
pub fn on_interrupt(&self) {
let gpip = unsafe { &*(self.ref_to_gpip.load(Ordering::Acquire) as *const RegisterBlock) };
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using raw pointer dereference without validation could lead to undefined behavior if the stored address is invalid. Consider adding bounds checking or a validity flag.

Suggested change
let gpip = unsafe { &*(self.ref_to_gpip.load(Ordering::Acquire) as *const RegisterBlock) };
let ptr = self.ref_to_gpip.load(Ordering::Acquire);
if ptr == 0 {
// Pointer is invalid; do not attempt to dereference.
return;
}
let gpip = unsafe { &*(ptr as *const RegisterBlock) };

Copilot uses AI. Check for mistakes.
let clock_state = if let (Some(glb_reg), Some(hbn_reg)) = (glb, hbn) {
Some(efuse_switch_cpu_clock_save(glb_reg, hbn_reg))
let clock_state = if let (Some(glb_reg), Some(hbn_reg)) = (_glb, _hbn) {
Some(efuse_switch_cpu_clock_save(_glb_reg, hbn_reg))
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable _glb_reg is used but glb_reg was destructured from the tuple. This should be glb_reg instead of _glb_reg.

Suggested change
Some(efuse_switch_cpu_clock_save(_glb_reg, hbn_reg))
Some(efuse_switch_cpu_clock_save(glb_reg, hbn_reg))

Copilot uses AI. Check for mistakes.
blri/src/main.rs Outdated

// Extract build parameters from environment and binary path
let args: Vec<String> = std::env::args().collect();
println!("🔧 Program args: {:?}", args);
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug output should be conditional or removed in production code. Consider using a proper logging framework or feature flag for debug output.

Suggested change
println!("🔧 Program args: {:?}", args);
if is_debug_enabled() {
println!("🔧 Program args: {:?}", args);
}

Copilot uses AI. Check for mistakes.
hbn.gpadc_interrupt_state
.modify(|val| val.set_pos_satur_interrupt(true));
hbn.gpadc_interrupt_state
.modify(|val| val.set_pos_satur_interrupt(true));
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The positive saturation interrupt clear sequence sets the bit to true twice consecutively, which may not properly clear the interrupt. The pattern should be set(false), set(true), set(false) for proper clearing.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

examples/peripherals/uart-async-demo/src/main.rs:1

  • This null pointer check pattern is duplicated in both uart and gpip async modules. Consider extracting this into a shared utility function or macro to reduce code duplication and ensure consistent safety checks.
#![no_std]

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 779 to 840
self.gpip
.gpadc_config
.modify(|val| val.set_adc_ready_clr_bit(0));
self.gpip
.gpadc_config
.modify(|val| val.set_adc_ready_clr_bit(1));
self.gpip
.gpadc_config
.modify(|val| val.set_adc_ready_clr_bit(0));
}

// Clear FIFO underrun interrupt
if int_clear.fifo_underrun {
self.gpip
.gpadc_config
.modify(|val| val.set_fifo_underrun_clr_bit(0));
self.gpip
.gpadc_config
.modify(|val| val.set_fifo_underrun_clr_bit(1));
self.gpip
.gpadc_config
.modify(|val| val.set_fifo_underrun_clr_bit(0));
}

// Clear FIFO overrun interrupt
if int_clear.fifo_overrun {
self.gpip
.gpadc_config
.modify(|val| val.set_fifo_overrun_clr_bit(0));
self.gpip
.gpadc_config
.modify(|val| val.set_fifo_overrun_clr_bit(1));
self.gpip
.gpadc_config
.modify(|val| val.set_fifo_overrun_clr_bit(0));
}

// Clear positive saturation interrupt
if int_clear.pos_saturation {
hbn.gpadc_interrupt_state
.modify(|val| val.set_pos_satur_interrupt(false));
hbn.gpadc_interrupt_state
.modify(|val| val.set_pos_satur_interrupt(true));
hbn.gpadc_interrupt_state
.modify(|val| val.set_pos_satur_interrupt(false));
}

// Clear negative saturation interrupt
if int_clear.neg_saturation {
hbn.gpadc_interrupt_state
.modify(|val| val.set_neg_satur_interrupt(false));
hbn.gpadc_interrupt_state
.modify(|val| val.set_neg_satur_interrupt(true));
hbn.gpadc_interrupt_state
.modify(|val| val.set_neg_satur_interrupt(false));
}
}
}
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The repetitive three-step clear pattern (0→1→0) is duplicated for each interrupt type. Consider extracting this into a helper function that takes a closure to reduce code duplication and improve readability.

Suggested change
self.gpip
.gpadc_config
.modify(|val| val.set_adc_ready_clr_bit(0));
self.gpip
.gpadc_config
.modify(|val| val.set_adc_ready_clr_bit(1));
self.gpip
.gpadc_config
.modify(|val| val.set_adc_ready_clr_bit(0));
}
// Clear FIFO underrun interrupt
if int_clear.fifo_underrun {
self.gpip
.gpadc_config
.modify(|val| val.set_fifo_underrun_clr_bit(0));
self.gpip
.gpadc_config
.modify(|val| val.set_fifo_underrun_clr_bit(1));
self.gpip
.gpadc_config
.modify(|val| val.set_fifo_underrun_clr_bit(0));
}
// Clear FIFO overrun interrupt
if int_clear.fifo_overrun {
self.gpip
.gpadc_config
.modify(|val| val.set_fifo_overrun_clr_bit(0));
self.gpip
.gpadc_config
.modify(|val| val.set_fifo_overrun_clr_bit(1));
self.gpip
.gpadc_config
.modify(|val| val.set_fifo_overrun_clr_bit(0));
}
// Clear positive saturation interrupt
if int_clear.pos_saturation {
hbn.gpadc_interrupt_state
.modify(|val| val.set_pos_satur_interrupt(false));
hbn.gpadc_interrupt_state
.modify(|val| val.set_pos_satur_interrupt(true));
hbn.gpadc_interrupt_state
.modify(|val| val.set_pos_satur_interrupt(false));
}
// Clear negative saturation interrupt
if int_clear.neg_saturation {
hbn.gpadc_interrupt_state
.modify(|val| val.set_neg_satur_interrupt(false));
hbn.gpadc_interrupt_state
.modify(|val| val.set_neg_satur_interrupt(true));
hbn.gpadc_interrupt_state
.modify(|val| val.set_neg_satur_interrupt(false));
}
}
}
Self::clear_interrupt_pattern(|v| self.gpip.gpadc_config.modify(|val| val.set_adc_ready_clr_bit(v)), 0, 1);
}
// Clear FIFO underrun interrupt
if int_clear.fifo_underrun {
Self::clear_interrupt_pattern(|v| self.gpip.gpadc_config.modify(|val| val.set_fifo_underrun_clr_bit(v)), 0, 1);
}
// Clear FIFO overrun interrupt
if int_clear.fifo_overrun {
Self::clear_interrupt_pattern(|v| self.gpip.gpadc_config.modify(|val| val.set_fifo_overrun_clr_bit(v)), 0, 1);
}
// Clear positive saturation interrupt
if int_clear.pos_saturation {
Self::clear_interrupt_pattern(|v| hbn.gpadc_interrupt_state.modify(|val| val.set_pos_satur_interrupt(v)), false, true);
}
// Clear negative saturation interrupt
if int_clear.neg_saturation {
Self::clear_interrupt_pattern(|v| hbn.gpadc_interrupt_state.modify(|val| val.set_neg_satur_interrupt(v)), false, true);
}
}
}
/// Helper for the three-step clear pattern (0→1→0 or false→true→false).
#[inline]
fn clear_interrupt_pattern<F, T>(mut f: F, zero: T, one: T)
where
F: FnMut(T),
T: Copy,
{
f(zero);
f(one);
f(zero);
}

Copilot uses AI. Check for mistakes.
blri/src/main.rs Outdated
Comment on lines 1211 to 1238
if !release {
println!("🔧 Program args: {:?}", args);
println!("🔧 Binary path: {}", binary_path);

// Print environment variables for debugging
for (key, value) in &env_vars {
if key.starts_with("CARGO_") {
println!(" {}: {}", key, value);
}
}
}
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug output is controlled by the release flag, but this creates inconsistent behavior where debug builds always show verbose output. Consider using a proper logging framework or adding a dedicated --verbose flag for better control over debug output.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated 6 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@@ -43,10 +44,13 @@ Suspendisse potenti. Nam bibendum, velit quis ullamcorper blandit, nunc odio ult
}

static UART3_STATE: SerialState = SerialState::new();
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding documentation to explain the purpose of TASK_WAKE_FLAG and its relationship with the interrupt handler and main task synchronization.

Suggested change
static UART3_STATE: SerialState = SerialState::new();
static UART3_STATE: SerialState = SerialState::new();
/// TASK_WAKE_FLAG is used to synchronize between the UART3 interrupt handler and the main async task.
///
/// - The interrupt handler sets this flag to `true` when an interrupt occurs, indicating that the main task should wake up and poll the future again.
/// - The main task checks this flag in its loop; if set, it clears the flag and continues polling without sleeping.
/// - This mechanism ensures that the main task responds promptly to UART events signaled by the interrupt.

Copilot uses AI. Check for mistakes.

gpip.adc_parse_result(value, result, &p.hbn);

for res in result.iter().skip(10) {
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The magic number '10' should be replaced with a named constant to clarify why the first 10 samples are being skipped.

Copilot uses AI. Check for mistakes.
Comment on lines 157 to 159
stack_protect_pmp_address_begin = const {0x62030000 >> 2},
stack_protect_pmp_address_end = const {(0x62030000 + 160 * 1024) >> 2},
stack_protect_pmp_flags = const 0b00001000 << 8,
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic numbers for PMP configuration should be replaced with named constants to improve readability and maintainability.

Copilot uses AI. Check for mistakes.
Comment on lines +849 to +860
f(zero);
f(one);
f(zero);
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This three-step clear pattern is important for interrupt clearing but lacks explanation. Add a comment explaining why this specific sequence (0→1→0) is required for the hardware.

Copilot uses AI. Check for mistakes.
blri/src/main.rs Outdated
Comment on lines 570 to 573
fn open_serial_console(port: &str, is_console_mode: bool, baudrate: u32) {
// Wait for device to start and collect initial output
println!("Waiting for device to start...");
thread::sleep(Duration::from_millis(1500));
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded delay value should be replaced with a named constant to clarify its purpose and make it configurable if needed.

Suggested change
fn open_serial_console(port: &str, is_console_mode: bool, baudrate: u32) {
// Wait for device to start and collect initial output
println!("Waiting for device to start...");
thread::sleep(Duration::from_millis(1500));
const DEVICE_STARTUP_DELAY_MS: u64 = 1500;
fn open_serial_console(port: &str, is_console_mode: bool, baudrate: u32) {
// Wait for device to start and collect initial output
println!("Waiting for device to start...");
thread::sleep(Duration::from_millis(DEVICE_STARTUP_DELAY_MS));

Copilot uses AI. Check for mistakes.
blri/src/main.rs Outdated
Comment on lines 1282 to 1288
#[allow(dead_code)]
fn extract_arg_value(args: &[String], flag: &str) -> Option<String> {
args.iter()
.position(|arg| arg == flag)
.and_then(|pos| args.get(pos + 1))
.cloned()
}
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is marked as dead_code but is still present. Either remove it if it's truly unused or implement its usage if it's intended for future use.

Suggested change
#[allow(dead_code)]
fn extract_arg_value(args: &[String], flag: &str) -> Option<String> {
args.iter()
.position(|arg| arg == flag)
.and_then(|pos| args.get(pos + 1))
.cloned()
}

Copilot uses AI. Check for mistakes.
@NanaHigh NanaHigh force-pushed the nanahigh/all branch 2 times, most recently from 2d7d3c5 to 518cb36 Compare September 17, 2025 10:47
Copy link
Member

@Placebo27 Placebo27 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job! This work helps users directly get the output from the serial port just interacting with console, which is very convenient.

@NanaHigh NanaHigh requested a review from Copilot September 17, 2025 11:02
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 3 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

}";

#[cfg(feature = "bl808-dsp")]
#[cfg(feature = "bl808-dsp")]
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate cfg attribute. Remove the duplicate #[cfg(feature = "bl808-dsp")] on line 314.

Suggested change
#[cfg(feature = "bl808-dsp")]

Copilot uses AI. Check for mistakes.
Comment on lines 849 to 855
// Hardware-required three-step sequence for proper interrupt clearing:
// 1. Write 0: Ensure the bit is in a known clear state
// 2. Write 1: Trigger the interrupt clear mechanism in hardware
// 3. Write 0: Return the bit to the inactive state
// This 0→1→0 pattern is necessary because some hardware interrupt controllers
// require a positive edge (0→1 transition) followed by a return to the
// inactive state to properly acknowledge and clear pending interrupts.
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This helper function should be documented with a doc comment explaining its purpose and the hardware requirement for the 0→1→0 pattern, rather than just inline comments.

Suggested change
// Hardware-required three-step sequence for proper interrupt clearing:
// 1. Write 0: Ensure the bit is in a known clear state
// 2. Write 1: Trigger the interrupt clear mechanism in hardware
// 3. Write 0: Return the bit to the inactive state
// This 0→1→0 pattern is necessary because some hardware interrupt controllers
// require a positive edge (0→1 transition) followed by a return to the
// inactive state to properly acknowledge and clear pending interrupts.
/// Performs the hardware-required three-step sequence for proper interrupt clearing.
///
/// Some hardware interrupt controllers require a 0→1→0 (or false→true→false) pattern
/// to properly acknowledge and clear pending interrupts. This sequence works as follows:
/// 1. Write 0: Ensure the bit is in a known clear state.
/// 2. Write 1: Trigger the interrupt clear mechanism in hardware.
/// 3. Write 0: Return the bit to the inactive state.
/// The positive edge (0→1 transition) followed by a return to inactive state is necessary
/// for proper hardware interrupt acknowledgement and clearing.

Copilot uses AI. Check for mistakes.
blri/src/main.rs Outdated
Comment on lines 327 to 332
#[allow(dead_code)]
fn use_or_select_flash_port(port_parameter: &Option<String>) -> String {
let (port, _) = use_or_select_flash_port_and_baudrate(port_parameter, Some(2000000));
port
}

Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is marked as dead_code but appears to be unused. Consider removing it to reduce code maintenance burden.

Suggested change
#[allow(dead_code)]
fn use_or_select_flash_port(port_parameter: &Option<String>) -> String {
let (port, _) = use_or_select_flash_port_and_baudrate(port_parameter, Some(2000000));
port
}

Copilot uses AI. Check for mistakes.
- Fix RV32E trap handlers and linker script consolidation.
- Add safety checks and fix interrupt clearing patterns.
- Replace Waker::noop() with proper async executor.
- Make debug output conditional on verbose mode.

Signed-off-by: Chongbing Yu <[email protected]>
Copy link
Member

@luojia65 luojia65 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Placebo27 Placebo27 merged commit ec7278b into rustsbi:main Sep 17, 2025
29 checks passed
@NanaHigh NanaHigh deleted the nanahigh/all branch September 25, 2025 13:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants