diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0e86507..e922cde 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,4 +31,7 @@ jobs: cd ../hpm-data/build/ git clone https://github.com/hpmicro-rs/hpm-metapac.git - name: Build Only - run: cargo build --release --examples + run: + for d in $(ls -1 ./examples); do + (cd ./examples/$d && cargo build --release) + done diff --git a/Cargo.toml b/Cargo.toml index 82c9bf7..03f1b97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,12 +15,7 @@ license = "MIT/Apache-2.0" [dependencies] # path = "../hpm-data/build/hpm-metapac", # git = "https://github.com/hpmicro-rs/hpm-metapac.git", -hpm-metapac = { version = "0.0.3", path = "../hpm-data/build/hpm-metapac", features = [ - "rt", - # temp feature for demo - # "memory-x", - -] } +hpm-metapac = { version = "0.0.3", path = "../hpm-data/build/hpm-metapac" } riscv = { version = "0.11", features = ["critical-section-single-hart"] } embedded-hal = { version = "1.0.0" } @@ -55,12 +50,18 @@ proc-macro2 = "1.0.85" quote = "1.0.15" [features] -default = ["hpm5361", "rt", "embassy", "defmt", "time"] -rt = ["dep:riscv-rt"] +default = ["rt", "embassy", "defmt", "time"] + +rt = ["dep:riscv-rt", "hpm-metapac/rt"] defmt = ["dep:defmt"] time = ["dep:embassy-time"] -embassy = ["dep:embassy-sync", "dep:embassy-futures", "dep:embassy-time-driver", "dep:futures-util"] +embassy = [ + "dep:embassy-sync", + "dep:embassy-futures", + "dep:embassy-time-driver", + "dep:futures-util", +] hpm5301 = ["hpm-metapac/hpm5301"] hpm5321 = ["hpm-metapac/hpm5321"] @@ -90,27 +91,7 @@ hpm6754 = ["hpm-metapac/hpm6754"] hpm6830 = ["hpm-metapac/hpm6830"] hpm6850 = ["hpm-metapac/hpm6850"] hpm6880 = ["hpm-metapac/hpm6880"] -futures-util = ["dep:futures-util"] - - -[dev-dependencies] -panic-halt = "0.2.0" -riscv-rt = "0.12.2" - -embassy-time = { version = "0.3.0", features = ["tick-hz-1_000_000"] } -embassy-executor = { version = "0.5.0", features = [ - "nightly", - "integrated-timers", - "arch-riscv32", - "executor-thread", -] } -defmt = "0.3.8" -defmt-rtt = "0.4.1" -embedded-graphics = "0.8.1" - - -[profile.release] -strip = false # symbols are not flashed to the microcontroller, so don't strip them. -lto = true -opt-level = "z" # Optimize for size. -debug = 2 +hpm6e50 = ["hpm-metapac/hpm6e50"] +hpm6e60 = ["hpm-metapac/hpm6e60"] +hpm6e70 = ["hpm-metapac/hpm6e70"] +hpm6e80 = ["hpm-metapac/hpm6e80"] diff --git a/HPMicro.yaml b/HPMicro.yaml index 91230c4..b1256fa 100644 --- a/HPMicro.yaml +++ b/HPMicro.yaml @@ -41,7 +41,7 @@ flash_algorithms: - name: algorithm-test description: A flash algorithm under test default: true - instructions: EwEB3CMuESIjLIEiIyqRIiMoISO3BQAABUV9Fo1GI4SlSGN51gy3FQD0A6AFgH1WI6DFgAOgBYEjqMWAI6KlkGgAEwYAEIFFlwAAAOeAIBsjLgEQIywBEDcF+fwJBSMmoRAZRSMooRAFZSMqoRA3BQIgAyVF8XRF0cI3BQDzbABwAjcJAPOCliqEKemyRCMAAQQMCAgSEwbAD5cAAADngOApaAxsAlFGlwAAAOeAACk3BQAAIyiVNpMEBTcThUQADBITBgARlwAAAOeAICcjqiQRNwUAAIVFIwS1SCKFgyDBIwMkgSODJEEjAykBIxMBASSCgJcAAADngEANlwAAAOeAwA63BQAAA8aFSAVFAcYBRSOEBUiCgLcFAACDxYVIncG3BQIgg6VF8ZxRmc+3BQAAE4YFNwMnRhG3BgCAqY6RRTqFgocFRYKAlwAAAOeA4Am3BgAAA8eGSAXLLoi3BQIgg6VF8QOjhQJjAgMCsoa3BQAAE4YFN4MnRhE3BwCAKY+RRT6FwocCgwVFgoCXAAAA54CgBTcFAAADRYVIGc03BQIgAyVF8VxNmcs3BQAAEwYFNwMlRhGRRYKHBUWCgJcAAADngIACQREGxiLEAAiXAAAA54CAAkERBsYixAAIlwAAAOeAgP5BEQbGIsQACJcAAADngID+AaBBEQbGIsQACLJAIkRBARcDAABnAGMMQREGxiLEAAjBRmNr1gazBqBAE/g2ALMDBQFjDAgAqoeuhgPHBgAjgOcAhQeFBuPqd/6ziAUBMwgGQZNyyP+T9TgAs4ZTAKHBY1lQBJOVOAAT84UBk/fI/5BDswWwQBP+hQGRB5hDM1ZmALMVxwHRjSOgswCRA5EHOobj5dP+Maiqhg3iDahjWlAAxoWQQSOgwwCRA5EF4+vT/rOFWAATdjgAEco2lgPHBQAjgOYAhQaFBePqxv6yQCJEQQGCgEERBsYixAAIwUZjZNYEswagQI2KMwfVAJnGqocjgLcAhQfj7ef+FY6Td8b/swb3AGNe8AAT+PUPtwcBAZOHFxCzB/gCHMMRB+Nu1/4NigHmCaiqhhnGNpYjgLYAhQbj7cb+skAiREEBgoBBEQbGIsQACLJAIkRBARcDAABnAKPsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + instructions: EwEB3CMuESIjLIEiIyqRIiMoISO3BQAABUV9Fo1GI4SlSGN51gy3FQD0A6AFgH1WI6DFgAOgBYEjqMWAI6KlkGgAEwYAEIFFlwAAAOeAAC8jLgEQIywBEDcF+fwJBSMmoRAZRSMooRAFZSMqoRA3BQIgAyVF8XRF0cI3BQDzbABwAjcJAPOCliqEKemyRCMAAQQMCAgSEwbAD5cAAADngAAWaAxsAlFGlwAAAOeAIBU3BQAAIyiVNpMEBTcThUQADBITBgARlwAAAOeAQBMjqiQRNwUAAIVFIwS1SCKFgyDBIwMkgSODJEEjAykBIxMBASSCgJcAAADngEANlwAAAOeAwA63BQAAA8aFSAVFAcYBRSOEBUiCgLcFAACDxYVIncG3BQIgg6VF8ZxRmc+3BQAAE4YFNwMnRhG3BgCAqY6RRTqFgocFRYKAlwAAAOeA4Am3BgAAA8eGSAXLLoi3BQIgg6VF8QOjhQJjAgMCsoa3BQAAE4YFN4MnRhE3BwCAKY+RRT6FwocCgwVFgoCXAAAA54CgBTcFAAADRYVIGc03BQIgAyVF8VxNmcs3BQAAEwYFNwMlRhGRRYKHBUWCgJcAAADngIACQREGxiLEAAiXAAAA54CAAkERBsYixAAIlwAAAOeAgP5BEQbGIsQACJcAAADngID+AaBBEQbGIsQACLJAIkRBARcDAABnAIMAQREGxiLEAAjBRmNr1gazBqBAE/g2ALMDBQFjDAgAqoeuhgPHBgAjgOcAhQeFBuPqd/6ziAUBMwgGQZNyyP+T9TgAs4ZTAKHBY1lQBJOVOAAT84UBk/fI/5BDswWwQBP+hQGRB5hDM1ZmALMVxwHRjSOgswCRA5EHOobj5dP+Maiqhg3iDahjWlAAxoWQQSOgwwCRA5EF4+vT/rOFWAATdjgAEco2lgPHBQAjgOYAhQaFBePqxv6yQCJEQQGCgEERBsYixAAIwUZjZNYEswagQI2KMwfVAJnGqocjgLcAhQfj7ef+FY6Td8b/swb3AGNe8AAT+PUPtwcBAZOHFxCzB/gCHMMRB+Nu1/4NigHmCaiqhhnGNpYjgLYAhQbj7cb+skAiREEBgoBBEQbGIsQACLJAIkRBARcDAABnAIP4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA load_address: 0x20 pc_init: 0x0 pc_uninit: 0x104 @@ -53,7 +53,7 @@ flash_algorithms: address_range: start: 0x80000000 end: 0x80100000 - page_size: 0x100 + page_size: 0x1000 erased_byte_value: 0xff program_page_timeout: 1000 erase_sector_timeout: 2000 diff --git a/README.md b/README.md index e36b829..a35e404 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ This crate is a working-in-progress and not ready for use. - [x] RTT support (defmt, defmt-rtt) - [x] UART blocking TX, RX - [x] I2C blocking + - [x] MBX, blocking and async - MCUs - HPM5300 - currently it's the only supported series diff --git a/build.rs b/build.rs index 45957d2..e0bf1a4 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::ffi::OsString; use std::fmt::Write as _; use std::io::Write as _; @@ -166,6 +166,44 @@ fn main() { } } + // ======== + // Generate dma_trait_impl! + let signals: HashMap<_, _> = [ + // (kind, signal) => trait + (("uart", "RX"), quote!(crate::uart::RxDma)), + (("uart", "TX"), quote!(crate::uart::TxDma)), + (("i2c", "GLOBAL"), quote!(crate::i2c::I2cDma)), + ] + .into(); + + for p in METADATA.peripherals { + if let Some(regs) = &p.registers { + let mut dupe = HashSet::new(); + for ch in p.dma_channels { + if let Some(tr) = signals.get(&(regs.kind, ch.signal)) { + let peri = format_ident!("{}", p.name); + + let key = (ch.signal, ch.request.unwrap().to_string()); + if !dupe.insert(key) { + continue; + } + + // request number for peripheral DMA + let request = ch.request.expect("DMA request must be specified") as u8; + + // let channel = format_ident!("{}", ch.name); + + for channel in METADATA.dma_channels { + let channel_ident = format_ident!("{}", channel.name); + g.extend(quote! { + dma_trait_impl!(#tr, #peri, #channel_ident, #request); + }); + } + } + } + } + } + // ======== // Write foreach_foo! macrotables @@ -178,6 +216,32 @@ fn main() { pins_table.push(vec![p.name.to_string(), p.index.to_string()]); } + let mut dmas = TokenStream::new(); + for ch in METADATA.dma_channels.iter() { + let name = format_ident!("{}", ch.name); + let idx = ch.channel as u8; + + let ch_num = ch.channel as usize; + let mux_num = ch.dmamux_channel as usize; + + // HDMA or XDMA + let dma_name = format_ident!("{}", ch.name.split_once('_').expect("DMA channel name format").0); + + g.extend(quote!(dma_channel_impl!(#name, #idx);)); + + dmas.extend(quote! { + crate::dma::ChannelInfo { + dma: crate::dma::DmaInfo::#dma_name(crate::pac::#dma_name), + num: #ch_num, + mux_num: #mux_num, + }, + }); + } + + g.extend(quote! { + pub(crate) const DMA_CHANNELS: &[crate::dma::ChannelInfo] = &[#dmas]; + }); + for p in METADATA.peripherals { let Some(regs) = &p.registers else { continue; diff --git a/examples/blinky.rs b/examples/blinky.rs deleted file mode 100644 index 01d3b69..0000000 --- a/examples/blinky.rs +++ /dev/null @@ -1,29 +0,0 @@ -#![no_main] -#![no_std] - -use embedded_hal::delay::DelayNs; -use hal::gpio::{Level, Output, Speed}; -use riscv::delay::McycleDelay; -use {defmt_rtt as _, hpm_hal as hal, panic_halt as _, riscv_rt as _}; - -#[hal::entry] -fn main() -> ! { - let p = hal::init(Default::default()); - - let mut delay = McycleDelay::new(hal::sysctl::clocks().hart0.0); - - defmt::info!("Board init!"); - - let mut led = Output::new(p.PA10, Level::Low, Speed::default()); - // let mut led = Output::new(p.PA23, Level::Low, Speed::default()); - - loop { - defmt::info!("tick"); - - led.set_high(); - delay.delay_ms(1000); - - led.set_low(); - delay.delay_ms(1000); - } -} diff --git a/examples/hpm5300evk/.cargo/config.toml b/examples/hpm5300evk/.cargo/config.toml new file mode 100644 index 0000000..f8f5b35 --- /dev/null +++ b/examples/hpm5300evk/.cargo/config.toml @@ -0,0 +1,34 @@ +[build] +target = "riscv32imafc-unknown-none-elf" + +[target.riscv32imafc-unknown-none-elf] +# runner = 'riscv64-unknown-elf-gdb -x ../../openocd.gdb' +runner = "probe-rs run --chip HPM5361 --protocol jtag --chip-description-path ../../HPMicro.yaml" +# runner = "probe-rs run --chip HPM5361 --protocol jtag" + +rustflags = [ + # Target features: + # The default for imacf is is "+m,+a,+c,+f" + "-C", + "target-feature=+d,+zba,+zbb,+zbc,+zbs", + # Linker scripts: + "-C", + "link-arg=-Tmemory.x", + "-C", + "link-arg=-Tdevice.x", # __VECTORED_INTERRUPTS + "-C", + "link-arg=-Tlink-fixed.x", + "-C", + "link-arg=-Tdefmt.x", + "-C", + "link-arg=-nmagic", + + # "--emit", "obj", + # "--emit", "asm", +] + +[unstable] +build-std = ["core"] + +[env] +DEFMT_LOG = "info" diff --git a/examples/hpm5300evk/Cargo.toml b/examples/hpm5300evk/Cargo.toml new file mode 100644 index 0000000..82179cf --- /dev/null +++ b/examples/hpm5300evk/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "hpm5300evk" +version = "0.1.0" +edition = "2021" + +[dependencies] +hpm-hal = { path = "../..", features = ["rt", "embassy", "hpm5361"] } + +panic-halt = "0.2.0" +riscv-rt = "0.12.2" + +embassy-time = { version = "0.3.0", features = ["tick-hz-1_000_000"] } +embassy-executor = { version = "0.5.0", features = [ + "nightly", + "integrated-timers", + "arch-riscv32", + "executor-thread", +] } +defmt = "0.3.8" +defmt-rtt = "0.4.1" +embedded-graphics = "0.8.1" +riscv = { version = "0.11.1", features = ["critical-section-single-hart"] } +embedded-hal = "1.0.0" +embedded-io = "0.6.1" +futures-util = { version = "0.3.30", default-features = false } + + +[profile.release] +strip = false # symbols are not flashed to the microcontroller, so don't strip them. +lto = true +opt-level = "z" # Optimize for size. +debug = 2 diff --git a/link-fixed.x b/examples/hpm5300evk/link-fixed.x similarity index 100% rename from link-fixed.x rename to examples/hpm5300evk/link-fixed.x diff --git a/memory.x b/examples/hpm5300evk/memory.x similarity index 100% rename from memory.x rename to examples/hpm5300evk/memory.x diff --git a/examples/BANNER b/examples/hpm5300evk/src/bin/BANNER similarity index 100% rename from examples/BANNER rename to examples/hpm5300evk/src/bin/BANNER diff --git a/examples/hpm5300evk/src/bin/blinky.rs b/examples/hpm5300evk/src/bin/blinky.rs new file mode 100644 index 0000000..c89ab6b --- /dev/null +++ b/examples/hpm5300evk/src/bin/blinky.rs @@ -0,0 +1,34 @@ +#![no_main] +#![no_std] + +use embedded_hal::delay::DelayNs; +use hal::gpio::{Level, Output, Speed}; +use riscv::delay::McycleDelay; +use {defmt_rtt as _, hpm_hal as hal, riscv_rt as _}; + +#[hal::entry] +fn main() -> ! { + let p = hal::init(Default::default()); + + let mut delay = McycleDelay::new(hal::sysctl::clocks().cpu0.0); + + defmt::info!("Board init!"); + + defmt::info!("CPU0 clock: {}Hz", hal::sysctl::clocks().cpu0.0); + + // let mut led = Output::new(p.PA10, Level::Low, Speed::default()); + let mut led = Output::new(p.PA23, Level::Low, Speed::default()); + + loop { + defmt::info!("tick"); + + led.toggle(); + delay.delay_ms(1000); + } +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + defmt::info!("panic"); + loop {} +} diff --git a/examples/blocking_uart_tx.rs b/examples/hpm5300evk/src/bin/blocking_uart_tx.rs similarity index 84% rename from examples/blocking_uart_tx.rs rename to examples/hpm5300evk/src/bin/blocking_uart_tx.rs index 47a07b2..9ec623a 100644 --- a/examples/blocking_uart_tx.rs +++ b/examples/hpm5300evk/src/bin/blocking_uart_tx.rs @@ -8,16 +8,20 @@ use hpm_hal::uart::UartTx; use riscv::delay::McycleDelay; use {defmt_rtt as _, hpm_hal as hal, panic_halt as _, riscv_rt as _}; +const BANNER: &str = include_str!("./BANNER"); + #[hal::entry] fn main() -> ! { let p = hal::init(Default::default()); - let mut delay = McycleDelay::new(hal::sysctl::clocks().hart0.0); + let mut delay = McycleDelay::new(hal::sysctl::clocks().cpu0.0); defmt::info!("Board init!"); let mut tx = UartTx::new_blocking(p.UART0, p.PA00, Default::default()).unwrap(); + writeln!(tx, "{}", BANNER).unwrap(); + tx.blocking_write(b"Hello, board!\r\n").unwrap(); writeln!(tx, "Clocks {:#?}", hal::sysctl::clocks()).unwrap(); diff --git a/examples/button.rs b/examples/hpm5300evk/src/bin/button.rs similarity index 88% rename from examples/button.rs rename to examples/hpm5300evk/src/bin/button.rs index c762895..945a124 100644 --- a/examples/button.rs +++ b/examples/hpm5300evk/src/bin/button.rs @@ -10,7 +10,7 @@ use {defmt_rtt as _, hpm_hal as hal, panic_halt as _, riscv_rt as _}; fn main() -> ! { let p = hal::init(Default::default()); - let mut delay = McycleDelay::new(hal::sysctl::clocks().hart0.0); + let mut delay = McycleDelay::new(hal::sysctl::clocks().cpu0.0); defmt::info!("Board init!"); diff --git a/examples/embassy_blinky.rs b/examples/hpm5300evk/src/bin/embassy_blinky.rs similarity index 95% rename from examples/embassy_blinky.rs rename to examples/hpm5300evk/src/bin/embassy_blinky.rs index ac75f18..a220fec 100644 --- a/examples/embassy_blinky.rs +++ b/examples/hpm5300evk/src/bin/embassy_blinky.rs @@ -35,7 +35,7 @@ async fn main(spawner: Spawner) -> ! { println!("=============================="); println!(" {} clock summary", BOARD_NAME); println!("=============================="); - println!("cpu0:\t\t {}Hz", hal::sysctl::clocks().hart0.0); + println!("cpu0:\t\t {}Hz", hal::sysctl::clocks().cpu0.0); println!("ahb:\t\t {}Hz", hal::sysctl::clocks().ahb.0); println!("=============================="); diff --git a/examples/embassy_button.rs b/examples/hpm5300evk/src/bin/embassy_button.rs similarity index 100% rename from examples/embassy_button.rs rename to examples/hpm5300evk/src/bin/embassy_button.rs diff --git a/examples/embassy_mbx_fifo.rs b/examples/hpm5300evk/src/bin/embassy_mbx_fifo.rs similarity index 100% rename from examples/embassy_mbx_fifo.rs rename to examples/hpm5300evk/src/bin/embassy_mbx_fifo.rs diff --git a/examples/embassy_mbx_message.rs b/examples/hpm5300evk/src/bin/embassy_mbx_message.rs similarity index 100% rename from examples/embassy_mbx_message.rs rename to examples/hpm5300evk/src/bin/embassy_mbx_message.rs diff --git a/examples/hpm5300evk/src/bin/embassy_tmpl.rs b/examples/hpm5300evk/src/bin/embassy_tmpl.rs new file mode 100644 index 0000000..85d30a8 --- /dev/null +++ b/examples/hpm5300evk/src/bin/embassy_tmpl.rs @@ -0,0 +1,84 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use defmt::println; +use embassy_executor::Spawner; +use embassy_time::Timer; +use embedded_io::Write as _; +use hal::gpio::{AnyPin, Flex, Pin}; +use hal::pac; +use hal::pac::MCHTMR; +use hpm_hal::mode::Blocking; +use {defmt_rtt as _, hpm_hal as hal}; + +const BOARD_NAME: &str = "HPM5300EVK"; +const BANNER: &str = include_str!("./BANNER"); + +#[embassy_executor::task] +async fn blink(pin: AnyPin) { + let mut led = Flex::new(pin); + led.set_as_output(Default::default()); + led.set_high(); + + loop { + led.toggle(); + + Timer::after_millis(500).await; + } +} + +macro_rules! println { + ($($arg:tt)*) => { + let _ = writeln!(unsafe {UART.as_mut().unwrap()}, $($arg)*); + }; +} + +static mut UART: Option> = None; + +#[embassy_executor::main(entry = "hpm_hal::entry")] +async fn main(spawner: Spawner) -> ! { + let p = hal::init(Default::default()); + // let button = Input::new(p.PA03, Pull::Down); // hpm5300evklite, BOOT1_KEY + let uart = hal::uart::Uart::new_blocking(p.UART0, p.PA01, p.PA00, Default::default()).unwrap(); + unsafe { + UART = Some(uart); + } + + println!("{}", BANNER); + println!("{} init OK!", BOARD_NAME); + + println!("Clock summary:"); + println!(" CPU0:\t{}Hz", hal::sysctl::clocks().cpu0.0); + println!(" AHB:\t{}Hz", hal::sysctl::clocks().ahb.0); + println!( + " XPI0:\t{}Hz", + hal::sysctl::clocks().get_clock_freq(hal::pac::clocks::XPI0).0 + ); + println!( + " MTMR:\t{}Hz", + hal::sysctl::clocks().get_clock_freq(pac::clocks::MCT0).0 + ); + + println!("=============================="); + + println!("Hello, world!"); + + //let mie = riscv::register::mie::read(); + //println!("mie: {:?}", mie); + + spawner.spawn(blink(p.PA23.degrade())).unwrap(); + + loop { + Timer::after_millis(1000).await; + + defmt::info!("tick {}", MCHTMR.mtime().read()); + } +} + +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + println!("\n\n\nPANIC:\n{}", info); + + loop {} +} diff --git a/examples/i2c_oled.rs b/examples/hpm5300evk/src/bin/i2c_oled.rs similarity index 92% rename from examples/i2c_oled.rs rename to examples/hpm5300evk/src/bin/i2c_oled.rs index 8b0eb56..68f7e0e 100644 --- a/examples/i2c_oled.rs +++ b/examples/hpm5300evk/src/bin/i2c_oled.rs @@ -291,30 +291,9 @@ static mut UART: Option> = None; #[hal::entry] fn main() -> ! { - let mut config = hal::Config::default(); - { - use hal::sysctl::*; - - // 24MHz * 40 = 960MHz - // PLL0CLK0 = 960 M - // PLL0CLK1 = 960 / 1.6 = 600 M - // PLL0CLK2 = 960 / 2.4 = 400 M - config.sysctl.pll0 = Some(Pll { - mfi: 40, - mfn: 0, - mfd: 240000000, - div: (0, 3, 7), // 960, 600, 400 - }); - // CPU0 = PLL0CLK0 / 2 = 480 M - // AHB = CPU0 / 3 = 160 M - config.sysctl.cpu0 = ClockConfig::new(ClockMux::PLL0CLK0, 2); - config.sysctl.ahb_div = AHBDiv::DIV3; - } - - defmt::info!("Board preinit!"); - let p = hal::init(config); + let p = hal::init(Default::default()); - let mut delay = McycleDelay::new(hal::sysctl::clocks().hart0.0); + let mut delay = McycleDelay::new(hal::sysctl::clocks().cpu0.0); let uart_config = hal::uart::Config::default(); let uart = hal::uart::Uart::new_blocking(p.UART0, p.PA01, p.PA00, uart_config).unwrap(); @@ -331,7 +310,7 @@ fn main() -> ! { writeln!(uart, "Rust SDK: hpm-hal v0.0.1").unwrap(); writeln!(uart, "Clock summary:").unwrap(); - writeln!(uart, " CPU0:\t{}Hz", hal::sysctl::clocks().hart0.0).unwrap(); + writeln!(uart, " CPU0:\t{}Hz", hal::sysctl::clocks().cpu0.0).unwrap(); writeln!(uart, " AHB:\t{}Hz", hal::sysctl::clocks().ahb.0).unwrap(); writeln!( uart, diff --git a/examples/pll_setting.rs b/examples/hpm5300evk/src/bin/pll_setting.rs similarity index 50% rename from examples/pll_setting.rs rename to examples/hpm5300evk/src/bin/pll_setting.rs index 80df54e..716e449 100644 --- a/examples/pll_setting.rs +++ b/examples/hpm5300evk/src/bin/pll_setting.rs @@ -3,24 +3,14 @@ use embedded_hal::delay::DelayNs; use embedded_io::Write as _; // `writeln!` provider +use hal::gpio::{Level, Output, Speed}; use hal::pac; -use hpm_hal::gpio::{Level, Output, Speed}; -use hpm_hal::uart::UartTx; -use hpm_metapac::MCHTMR; +use hal::uart::UartTx; +use hpm_hal::time::Hertz; use riscv::delay::McycleDelay; use {defmt_rtt as _, hpm_hal as hal, panic_halt as _, riscv_rt as _}; -const BANNER: &str = r#" ----------------------------------------------------------------------- -$$\ $$\ $$$$$$$\ $$\ $$\ $$\ -$$ | $$ |$$ __$$\ $$$\ $$$ |\__| -$$ | $$ |$$ | $$ |$$$$\ $$$$ |$$\ $$$$$$$\ $$$$$$\ $$$$$$\ -$$$$$$$$ |$$$$$$$ |$$\$$\$$ $$ |$$ |$$ _____|$$ __$$\ $$ __$$\ -$$ __$$ |$$ ____/ $$ \$$$ $$ |$$ |$$ / $$ | \__|$$ / $$ | -$$ | $$ |$$ | $$ |\$ /$$ |$$ |$$ | $$ | $$ | $$ | -$$ | $$ |$$ | $$ | \_/ $$ |$$ |\$$$$$$$\ $$ | \$$$$$$ | -\__| \__|\__| \__| \__|\__| \_______|\__| \______/ -----------------------------------------------------------------------"#; +const BANNER: &str = include_str!("./BANNER"); #[hal::entry] fn main() -> ! { @@ -33,20 +23,22 @@ fn main() -> ! { // PLL0CLK1 = 960 / 1.2 = 800 M // PLL0CLK2 = 960 / 1.6 = 600 M config.sysctl.pll0 = Some(Pll { - mfi: 40, - mfn: 0, - mfd: 240000000, - div: (0, 1, 3), + freq_in: Hertz::mhz(980), + /* PLL0CLK0: 720MHz */ + /* PLL0CLK1: 450MHz */ + /* PLL0CLK2: 300MHz */ + div: (0, 3, 7), }); - // CPU0 = PLL0CLK0 / 2 = 480 M + config.sysctl.cpu0 = ClockConfig::new(ClockMux::PLL0CLK0, 2); + config.sysctl.ahb_div = AHBDiv::DIV3; } defmt::info!("Board preinit!"); let p = hal::init(config); defmt::info!("Board init!"); - let mut delay = McycleDelay::new(hal::sysctl::clocks().hart0.0); + let mut delay = McycleDelay::new(hal::sysctl::clocks().cpu0.0); let mut tx = UartTx::new_blocking(p.UART0, p.PA00, Default::default()).unwrap(); @@ -54,7 +46,7 @@ fn main() -> ! { writeln!(tx, "Board inited OK!").unwrap(); writeln!(tx, "Clock summary:").unwrap(); - writeln!(tx, " CPU0:\t{}Hz", hal::sysctl::clocks().hart0.0).unwrap(); + writeln!(tx, " CPU0:\t{}Hz", hal::sysctl::clocks().cpu0.0).unwrap(); writeln!(tx, " AHB:\t{}Hz", hal::sysctl::clocks().ahb.0).unwrap(); writeln!( tx, @@ -69,17 +61,40 @@ fn main() -> ! { ) .unwrap(); + // using SYSCTL.MONITOR to measure the frequency of CPU0 + { + pac::SYSCTL.monitor(0).control().modify(|w| { + w.set_accuracy(true); // 1Hz + w.set_reference(true); // 24M + w.set_mode(true); // save to min and max + w.set_selection(pac::sysctl::vals::MonitorSelection::CLK_TOP_CPU0); // pll0 clk0 + w.set_start(true); + }); + + while !pac::SYSCTL.monitor(0).control().read().valid() {} + + writeln!( + tx, + "Monitor 0 measure: {} min={} max={}!", + pac::SYSCTL.monitor(0).current().read().frequency(), + pac::SYSCTL.monitor(0).low_limit().read().frequency(), + pac::SYSCTL.monitor(0).high_limit().read().frequency() + ) + .unwrap(); + } + let mut led = Output::new(p.PA23, Level::Low, Speed::default()); - loop { - let tick = MCHTMR.mtime().read(); - writeln!(tx, "tick! {}", tick).unwrap(); + let mut tick = riscv::register::mcycle::read64(); - defmt::info!("tick!"); + loop { led.set_high(); delay.delay_ms(500); led.set_low(); delay.delay_ms(500); + + writeln!(tx, "tick {}", riscv::register::mcycle::read64() - tick).unwrap(); + tick = riscv::register::mcycle::read64(); } } diff --git a/examples/hpm5300evk/src/bin/tsns.rs b/examples/hpm5300evk/src/bin/tsns.rs new file mode 100644 index 0000000..ae062b6 --- /dev/null +++ b/examples/hpm5300evk/src/bin/tsns.rs @@ -0,0 +1,76 @@ +//! Temperature Sensor Example +#![no_main] +#![no_std] + +use embedded_hal::delay::DelayNs; +use embedded_io::Write as _; // `writeln!` provider +use hal::gpio::{Level, Output, Speed}; +use hal::pac; +use hpm_hal::time::Hertz; +use riscv::delay::McycleDelay; +use {defmt_rtt as _, hpm_hal as hal, riscv_rt as _}; + +const BANNER: &str = include_str!("./BANNER"); + +#[hal::entry] +fn main() -> ! { + let mut config = hal::Config::default(); + { + use hal::sysctl::*; + + // 24MHz * 40 = 960MHz + // PLL0CLK0 = 960 M + // PLL0CLK1 = 960 / 1.2 = 800 M + // PLL0CLK2 = 960 / 1.6 = 600 M + config.sysctl.pll0 = Some(Pll { + freq_in: Hertz::mhz(780), + div: (0, 1, 3), + }); + // CPU0 = PLL0CLK0 / 2 = 480 M + config.sysctl.cpu0 = ClockConfig::new(ClockMux::PLL0CLK0, 2); + config.sysctl.ahb_div = AHBDiv::DIV2; + } + + defmt::info!("Board preinit!"); + let p = hal::init(config); + + let mut delay = McycleDelay::new(hal::sysctl::clocks().cpu0.0); + let uart_config = hal::uart::Config::default(); + let mut uart = hal::uart::Uart::new_blocking(p.UART0, p.PA01, p.PA00, uart_config).unwrap(); + + defmt::info!("Board init!"); + + writeln!(uart, "{}", BANNER).unwrap(); + + writeln!(uart, " CPU0:\t{}Hz", hal::sysctl::clocks().cpu0.0).unwrap(); + writeln!(uart, " AHB:\t{}Hz", hal::sysctl::clocks().ahb.0).unwrap(); + + let mut led = Output::new(p.PA10, Level::Low, Speed::default()); + // let mut led = Output::new(p.PA23, Level::Low, Speed::default()); + + // TSNS + pac::TSNS.config().modify(|w| { + w.set_enable(true); + w.set_continuous(true); + }); + + loop { + while !pac::TSNS.status().read().valid() {} + + let t = pac::TSNS.t().read().t() as f32 / 256.0; // 8 bit fixed point + let max = pac::TSNS.tmax().read().0 as f32 / 256.0; + let min = pac::TSNS.tmin().read().0 as f32 / 256.0; + + writeln!(uart, "Temperature: {:.2}°C (max: {:.2}°C, min: {:.2}°C)", t, max, min).unwrap(); + defmt::info!("Temperature: {=f32}°C (max: {=f32}°C, min: {=f32}°C)", t, max, min); + + led.toggle(); + delay.delay_ms(1000); + } +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + defmt::error!("panic!"); + loop {} +} diff --git a/examples/hpm5300evk/src/lib.rs b/examples/hpm5300evk/src/lib.rs new file mode 100644 index 0000000..0c9ac1a --- /dev/null +++ b/examples/hpm5300evk/src/lib.rs @@ -0,0 +1 @@ +#![no_std] diff --git a/.cargo/config.toml b/examples/hpm6e00evk/.cargo/config.toml similarity index 74% rename from .cargo/config.toml rename to examples/hpm6e00evk/.cargo/config.toml index 151fb17..f3037f9 100644 --- a/.cargo/config.toml +++ b/examples/hpm6e00evk/.cargo/config.toml @@ -2,14 +2,13 @@ target = "riscv32imafc-unknown-none-elf" [target.riscv32imafc-unknown-none-elf] -# runner = 'riscv64-unknown-elf-gdb -x openocd.gdb' -# runner = "probe-rs run --chip HPM5361 --protocol jtag --chip-description-path ./HPMicro.yaml" -runner = "probe-rs run --chip HPM5361 --protocol jtag" +runner = 'riscv64-unknown-elf-gdb -x ../../openocd.gdb' +# runner = "probe-rs run --chip HPM6E80 --protocol jtag --chip-description-path ./HPMicro.yaml" +# runner = "probe-rs run --chip HPM6E80 --protocol jtag" rustflags = [ # Target features: # The default for imacf is is "+m,+a,+c,+f" - # HPM6xxx does not have B extension "-C", "target-feature=+d,+zba,+zbb,+zbc,+zbs", # Linker scripts: diff --git a/examples/hpm6e00evk/Cargo.toml b/examples/hpm6e00evk/Cargo.toml new file mode 100644 index 0000000..636c294 --- /dev/null +++ b/examples/hpm6e00evk/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "hpm6e00evk" +version = "0.1.0" +edition = "2021" +resolver = "2" + +[dependencies] +# git = "https://github.com/hpmicro-rs/hpm-metapac.git", tag = "hpm-data-86124df0e9c855436aecece4a17469dd7ac6baf6", +hpm-metapac = { version = "0.0.3", path = "../../../hpm-data/build/hpm-metapac", features = [ + "rt", + "pac", + "hpm6e80", +] } + +# TODO +# hpm-hal = { path = "../../", features = ["hpm6e80"]} +panic-halt = "0.2.0" +riscv-rt = { version = "0.12.2", features = ["single-hart"] } + +defmt = "0.3.8" +defmt-rtt = "0.4.1" +embedded-hal = "1.0.0" +riscv = { version = "0.11.1", features = ["critical-section-single-hart"] } + + +[profile.release] +strip = false # symbols are not flashed to the microcontroller, so don't strip them. +lto = true +opt-level = "z" # Optimize for size. +debug = 2 diff --git a/examples/hpm6e00evk/link-fixed.x b/examples/hpm6e00evk/link-fixed.x new file mode 100644 index 0000000..b5a058e --- /dev/null +++ b/examples/hpm6e00evk/link-fixed.x @@ -0,0 +1,225 @@ +/* Temp fix for https://github.com/rust-embedded/riscv/issues/196 */ + +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut _heap_size }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol if not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- In this linker script, you may find symbols that look like `${...}` (e.g., `4`). + These are wildcards used by the `build.rs` script to adapt to different target particularities. + Check `build.rs` for more details about these symbols. + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all `4`-byte aligned. These alignments are assumed by the RAM + initialization routine. There's also a second benefit: `4`-byte aligned boundaries + means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +PROVIDE(_stext = ORIGIN(REGION_TEXT)); +PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler);; +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > REGION_TEXT + + .text _stext : + { + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + *(.trap); + *(.trap.rust); + *(.text.abort); + *(.text .text.*); + } > REGION_TEXT + + /* Fast code section */ + .fast : ALIGN(4) + { + _sifast = LOADADDR(.fast); + _sfast = .; + *(.fast) + *(.fast.*) + . = ALIGN(4); + PROVIDE(_efast= .); /* No idea why `PROVIDE` is needed here */ + } > REGION_FASTTEXT AT > REGION_RODATA + + .rodata : ALIGN(4) + { + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + } > REGION_RODATA + + .data : ALIGN(4) + { + _sidata = LOADADDR(.data); + _sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(4); + _edata = .; + } > REGION_DATA AT > REGION_RODATA + + .bss (NOLOAD) : ALIGN(4) + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(4); + _ebss = .; + } > REGION_BSS + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > REGION_HEAP + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > REGION_STACK + + /* fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect + relocatable code in the input files and raise an error if relocatable code + is found */ + .got (INFO) : + { + KEEP(*(.got .got.*)); + } + + /*.eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) }*/ + .eh_frame : { KEEP(*(.eh_frame)) } > REGION_TEXT + .eh_frame_hdr : { *(.eh_frame_hdr) } > REGION_TEXT +} + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_DATA) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_DATA must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_HEAP) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_STACK) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 4 == 0 && _edata % 4 == 0, " +BUG(riscv-rt): .data is not 4-byte aligned"); + +ASSERT(_sidata % 4 == 0, " +BUG(riscv-rt): the LMA of .data is not 4-byte aligned"); + +ASSERT(_sbss % 4 == 0 && _ebss % 4 == 0, " +BUG(riscv-rt): .bss is not 4-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), " +ERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region. +Set _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ diff --git a/examples/hpm6e00evk/memory.x b/examples/hpm6e00evk/memory.x new file mode 100644 index 0000000..647f719 --- /dev/null +++ b/examples/hpm6e00evk/memory.x @@ -0,0 +1,35 @@ +MEMORY +{ + XPI0_HEADER : ORIGIN = 0x80000000, LENGTH = 0x3000 /* bootheader */ + XPI0_APP : ORIGIN = 0x80003000, LENGTH = 1024K - 0x3000 /* app firmware */ + DLM0 : ORIGIN = 0x00200000, LENGTH = 256K /* data local memory */ + ILM0 : ORIGIN = 0x00000000, LENGTH = 256K /* instruction local memory */ + AAXI_SRAM : ORIGIN = 0x01200000, LENGTH = 1M +} +REGION_ALIAS("REGION_TEXT", XPI0_APP); +REGION_ALIAS("REGION_RODATA", XPI0_APP); +REGION_ALIAS("REGION_DATA", DLM0); +REGION_ALIAS("REGION_BSS", DLM0) +REGION_ALIAS("REGION_HEAP", DLM0); +REGION_ALIAS("REGION_STACK", DLM0); +REGION_ALIAS("REGION_FASTTEXT", ILM0); + + +/* +SECTIONS +{ + .fast : ALIGN(4) + { + . = ALIGN(8); + __fast_load_addr__ = LOADADDR(.fast); + __fast_start_addr__ = .; + PROVIDE(__ramfunc_start__ = .); + *(.fast) + *(.fast.*) + . = ALIGN(8); + PROVIDE(__ramfunc_end__ = .); + __fast_end_addr__ = .; + } > ILM +} + +*/ diff --git a/examples/hpm6e00evk/src/bin/blinky.rs b/examples/hpm6e00evk/src/bin/blinky.rs new file mode 100644 index 0000000..9593353 --- /dev/null +++ b/examples/hpm6e00evk/src/bin/blinky.rs @@ -0,0 +1,63 @@ +#![no_main] +#![no_std] + +use embedded_hal::delay::DelayNs; +use hpm_metapac::gpiom::vals; +use riscv::delay::McycleDelay; +use {defmt_rtt as _, hpm_metapac as pac, panic_halt as _, riscv_rt as _}; + +#[riscv_rt::entry] +fn main() -> ! { + // default clock + let mut delay = McycleDelay::new(600_000_000); + + // ugly but works + pac::SYSCTL.group0(0).set().modify(|w| w.0 = 0xFFFFFFFF); + pac::SYSCTL.group0(1).set().modify(|w| w.0 = 0xFFFFFFFF); + pac::SYSCTL.group0(2).set().modify(|w| w.0 = 0xFFFFFFFF); + pac::SYSCTL.group0(3).set().modify(|w| w.0 = 0xFFFFFFFF); + + pac::SYSCTL.affiliate(0).set().write(|w| w.set_link(1)); + + /* + pac::IOC.pad(142).func_ctl().modify(|w| w.set_alt_select(0)); + pac::IOC.pad(142).pad_ctl().write(|w| { + w.set_pe(true); + }); + */ + + const PE: usize = 4; + pac::GPIOM.assign(PE).pin(14).modify(|w| { + w.set_select(vals::PinSelect::CPU0_FGPIO); // FGPIO0 + w.set_hide(0b01); // invisible to GPIO0 + }); + pac::GPIOM.assign(PE).pin(15).modify(|w| { + w.set_select(vals::PinSelect::CPU0_FGPIO); // FGPIO0 + w.set_hide(0b01); // invisible to GPIO0 + }); + pac::GPIOM.assign(PE).pin(4).modify(|w| { + w.set_select(vals::PinSelect::CPU0_FGPIO); // FGPIO0 + w.set_hide(0b01); // invisible to GPIO0 + }); + + pac::FGPIO + .oe(PE) + .set() + .write(|w| w.set_direction((1 << 14) | (1 << 15) | (1 << 4))); + + defmt::info!("Board init!"); + + loop { + defmt::info!("tick"); + + pac::FGPIO.do_(PE).toggle().write(|w| w.set_output(1 << 14)); + + delay.delay_ms(100); + + pac::FGPIO.do_(PE).toggle().write(|w| w.set_output(1 << 15)); + + delay.delay_ms(100); + + pac::FGPIO.do_(PE).toggle().write(|w| w.set_output(1 << 4)); + } +} diff --git a/examples/hpm6e00evk/src/lib.rs b/examples/hpm6e00evk/src/lib.rs new file mode 100644 index 0000000..0c9ac1a --- /dev/null +++ b/examples/hpm6e00evk/src/lib.rs @@ -0,0 +1 @@ +#![no_std] diff --git a/run-openocd.sh b/run-openocd.sh index 3b1a69f..dc91c18 100755 --- a/run-openocd.sh +++ b/run-openocd.sh @@ -1,13 +1,15 @@ #!/bin/bash -export HPM_SDK_BASE=/path/to/hpm_sdk +# export HPM_SDK_BASE=/path/to/hpm_sdk + +export HPM_SDK_BASE=`pwd`/../hpm_sdk # openocd -f $OPENOCD_CFG/probes/ft2232.cfg -f $OPENOCD_CFG/soc/hpm5300.cfg -f $OPENOCD_CFG/boards/hpm5300evk.cfg -export BOARD=hpm5301evklite -# export BOARD=hpm5300evk -export PROBE=cmsis_dap -# export PROBE=ft2232 +# export BOARD=hpm5301evklite +export BOARD=hpm5300evk +# export PROBE=cmsis_dap +export PROBE=ft2232 openocd -c "set HPM_SDK_BASE ${HPM_SDK_BASE}; set BOARD ${BOARD}; set PROBE ${PROBE};" -f ${HPM_SDK_BASE}/boards/openocd/hpm5300_all_in_one.cfg diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 13e91b9..0000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,5 +0,0 @@ -[toolchain] -channel = "nightly-2024-06-12" -targets = [ - "riscv32imafc-unknown-none-elf" -] diff --git a/src/dma/dmamux.rs b/src/dma/dmamux.rs new file mode 100644 index 0000000..856c28b --- /dev/null +++ b/src/dma/dmamux.rs @@ -0,0 +1,15 @@ +#![macro_use] + +use crate::pac; + +pub(crate) struct DmamuxInfo { + pub(crate) num: usize, +} + +pub(crate) fn configure_dmamux(info: &DmamuxInfo, request: u8) { + let ch_mux_regs = pac::DMAMUX.muxcfg(info.num); + ch_mux_regs.write(|reg| { + reg.set_enable(true); + reg.set_source(request); + }); +} diff --git a/src/dma/mod.rs b/src/dma/mod.rs new file mode 100644 index 0000000..ffe3da7 --- /dev/null +++ b/src/dma/mod.rs @@ -0,0 +1,121 @@ +//! Direct Memory Access (DMA), and DMAMUX +//! +//! Features: +//! - HAS_IDLE_FLAG: v62, v53, v68 +//! - TRANSFER_WIDTH_MAX: double-word(XDMA) or word +//! - PER_BURST_MAX: 1024 for XDMA or 128 +//! - CHANNEL_NUM: 8 or 32 +//! - MAX_COUNT: whether has XDMA +//! +//! [Peripheral DMA] -> DMAMUX -> DMA channel -> DMA request +#![macro_use] + +mod dmamux; +pub(crate) use dmamux::*; +use embassy_hal_internal::{impl_peripheral, Peripheral}; + +pub mod word; + +use crate::{interrupt, pac}; + +pub(crate) struct ChannelInfo { + pub(crate) dma: DmaInfo, + /// Input channel ID of DMA(HDMA, XDMA) + pub(crate) num: usize, + /// Output channel ID of DMAMUX + pub(crate) mux_num: usize, +} + +#[derive(Clone, Copy)] +pub(crate) enum DmaInfo { + HDMA(pac::dma::Dma), + XDMA(pac::dma::Dma), +} + +/// DMA request type alias. (also known as DMA channel number) +pub type Request = u8; + +pub(crate) trait SealedChannel { + /// Channel ID, output channel ID of DMAMUX + fn id(&self) -> u8; +} +/// DMA channel. +#[allow(private_bounds)] +pub trait Channel: SealedChannel + Peripheral

+ Into + 'static { + /// Type-erase (degrade) this pin into an `AnyChannel`. + /// + /// This converts DMA channel singletons (`DMA1_CH3`, `DMA2_CH1`, ...), which + /// are all different types, into the same type. It is useful for + /// creating arrays of channels, or avoiding generics. + #[inline] + fn degrade(self) -> AnyChannel { + AnyChannel { id: self.id() } + } +} + +/// Type-erased DMA channel. +pub struct AnyChannel { + /// Channel ID, output channel ID of DMAMUX + pub(crate) id: u8, +} +impl_peripheral!(AnyChannel); + +impl AnyChannel { + fn info(&self) -> &ChannelInfo { + &crate::_generated::DMA_CHANNELS[self.id as usize] + } +} + +impl SealedChannel for AnyChannel { + fn id(&self) -> u8 { + self.id + } +} +impl Channel for AnyChannel {} + +macro_rules! dma_channel_impl { + ($channel_peri:ident, $index:expr) => { + impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { + fn id(&self) -> u8 { + $index + } + } + /* impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri { + unsafe fn on_irq() { + crate::dma::AnyChannel { id: $index }.on_irq(); + } + } */ + + impl crate::dma::Channel for crate::peripherals::$channel_peri {} + + impl From for crate::dma::AnyChannel { + fn from(x: crate::peripherals::$channel_peri) -> Self { + crate::dma::Channel::degrade(x) + } + } + }; +} + +/// Linked descriptor +/// +/// It is consumed by DMA controlled directly +#[derive(Clone, Debug, PartialEq, Eq)] +#[repr(C, align(8))] +pub struct DmaLinkedDescriptor { + /// Control + pub ctrl: u32, + /// Transfer size in source width + pub trans_size: u32, + /// Source address + pub src_addr: u32, + /// Source address high 32-bit, only valid when bus width > 32bits + pub src_addr_high: u32, + /// Destination address + pub dst_addr: u32, + /// Destination address high 32-bit, only valid when bus width > 32bits + pub dst_addr_high: u32, + /// Linked descriptor address + pub linked_ptr: u32, + /// Linked descriptor address high 32-bit, only valid when bus width > 32bits + pub linked_ptr_high: u32, +} diff --git a/src/dma/word.rs b/src/dma/word.rs new file mode 100644 index 0000000..736002f --- /dev/null +++ b/src/dma/word.rs @@ -0,0 +1,24 @@ +//! DMA word sizes + +use crate::pac; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum WordSize { + OneByte, + TwoBytes, + FourBytes, + EightBytes, +} + +impl WordSize { + /// Amount of bytes of this word size. + pub fn bytes(&self) -> usize { + match self { + Self::OneByte => 1, + Self::TwoBytes => 2, + Self::FourBytes => 4, + Self::EightBytes => 8, + } + } +} diff --git a/src/i2c/mod.rs b/src/i2c/mod.rs index eb0009d..5c2d526 100644 --- a/src/i2c/mod.rs +++ b/src/i2c/mod.rs @@ -5,8 +5,10 @@ use core::marker::PhantomData; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; +use embedded_hal::delay::DelayNs; use embedded_hal::i2c::Operation; use hpm_metapac::i2c::vals; +use riscv::delay::McycleDelay; use crate::gpio::AnyPin; use crate::interrupt::typelevel::Interrupt as _; @@ -122,6 +124,7 @@ impl<'d> I2c<'d, Blocking> { ) -> Self { into_ref!(scl, sda); + // init pins // ALT, Open Drain, Pull-up scl.ioc_pad().func_ctl().write(|w| { w.set_alt_select(scl.alt_num()); @@ -142,6 +145,11 @@ impl<'d> I2c<'d, Blocking> { w.set_ps(true); }); + { + use crate::sysctl::*; + T::set_clock(ClockConfig::new(ClockMux::CLK_24M, 1)); + } + Self::new_inner(peri, Some(scl.map_into()), Some(sda.map_into()), config) } } @@ -215,6 +223,26 @@ impl<'d, M: Mode> I2c<'d, M> { w.set_iicen(true); w.set_master(true); }); + + + if r.status().read().linescl() == false { + defmt::info!("CLK is low, panic"); + loop {} + } + + if r.status().read().linesda() == false { + defmt::info!("SDA is low, reset bus"); + // i2s_gen_reset_signal + // generate SCL clock as reset signal + r.ctrl().modify(|w| { + w.set_reset_len(9); + w.set_reset_hold_sckin(true); + w.set_reset_on(true); + }); + McycleDelay::new(crate::sysctl::clocks().cpu0.0).delay_ms(100); + + defmt::info!("bus cleared"); + } } /// Blocking write, restart, read. @@ -626,8 +654,8 @@ peri_trait!( pin_trait!(SclPin, Instance); pin_trait!(SdaPin, Instance); -//dma_trait!(RxDma, Instance); -//dma_trait!(TxDma, Instance); + +dma_trait!(I2cDma, Instance); foreach_peripheral!( (i2c, $inst:ident) => { diff --git a/src/lib.rs b/src/lib.rs index 5231a9e..988dc35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,6 +45,8 @@ pub mod gpio; pub mod mbx; pub mod sysctl; +pub mod dma; + // other peripherals pub mod i2c; pub mod uart; @@ -134,13 +136,14 @@ pub struct Config { } pub fn init(config: Config) -> Peripherals { + #[cfg(hpm53)] + gpio::init_py_pins_as_gpio(); + + // board_init_clock unsafe { sysctl::init(config.sysctl); } - #[cfg(hpm53)] - gpio::init_py_pins_as_gpio(); - unsafe { gpio::input_future::init_gpio0_irq(); } diff --git a/src/macros.rs b/src/macros.rs index e005e72..13e6157 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -83,3 +83,27 @@ macro_rules! pin_trait_impl { } }; } + +// ========== +// DMA + +macro_rules! dma_trait { + ($signal:ident, $instance:path$(, $mode:path)?) => { + #[doc = concat!(stringify!($signal), " DMA request trait")] + pub trait $signal: crate::dma::Channel { + #[doc = concat!("Get the DMA request number needed to use this channel as", stringify!($signal))] + fn request(&self) -> crate::dma::Request; + } + }; +} + +#[allow(unused)] +macro_rules! dma_trait_impl { + (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $channel:ident, $request:expr) => { + impl crate::$mod::$trait for crate::peripherals::$channel { + fn request(&self) -> crate::dma::Request { + $request + } + } + }; +} diff --git a/src/sysctl/mod.rs b/src/sysctl/mod.rs index 6d2d296..935530b 100644 --- a/src/sysctl/mod.rs +++ b/src/sysctl/mod.rs @@ -5,6 +5,7 @@ use core::ptr::addr_of; use hpm_metapac::PLLCTL; +use crate::pac; pub use crate::pac::sysctl::vals::{ClockMux, SubDiv as AHBDiv}; use crate::pac::SYSCTL; use crate::time::Hertz; @@ -29,7 +30,7 @@ const F_REF: Hertz = CLK_24M; /// The default system clock configuration static mut CLOCKS: Clocks = Clocks { - hart0: CLK_HART0, + cpu0: CLK_HART0, ahb: CLK_AHB, pll0clk0: PLL0CLK0, pll0clk1: PLL0CLK1, @@ -43,7 +44,7 @@ static mut CLOCKS: Clocks = Clocks { #[derive(Clone, Copy, Debug)] pub struct Clocks { /// CPU0 - pub hart0: Hertz, + pub cpu0: Hertz, /// AHB clock: HDMA, HRAM, MOT, ACMP, GPIO, ADC/DAC pub ahb: Hertz, @@ -85,7 +86,6 @@ impl Clocks { pub struct Config { pub pll0: Option>, - // PLL1 is related to the XPI0, so better not to expose it // pub pll1: Option>, pub cpu0: ClockConfig, pub ahb_div: AHBDiv, @@ -96,10 +96,7 @@ impl Default for Config { Config { pll0: None, // pll1: None, - cpu0: ClockConfig { - src: ClockMux::PLL0CLK0, - raw_div: 1, // div 2 - }, + cpu0: ClockConfig::new(ClockMux::PLL0CLK0, 2), ahb_div: AHBDiv::DIV2, } } @@ -108,40 +105,30 @@ impl Default for Config { /// PLL configuration #[derive(Clone, Copy)] pub struct Pll { - // 13 to 42 - pub mfi: u8, - // u30 - pub mfn: u32, - // u30 - pub mfd: u32, - + pub freq_in: Hertz, pub div: D, } impl Pll { - pub(crate) fn check(self) -> Option { - if self.mfi < 13 || self.mfi > 42 { - return None; - } - if self.mfn > 0x3FFF_FFFF { - return None; - } - if self.mfd > 0x3FFF_FFFF { - return None; - } + /// (mfi, mfn) + pub(crate) fn get_params(&self) -> Option<(u8, u32)> { + const PLL_XTAL_FREQ: u32 = 24000000; - Some(self) - } + const PLL_FREQ_MIN: u32 = PLL_XTAL_FREQ * 16; // min MFI, when MFN = 0 + const PLL_FREQ_MAX: u32 = PLL_XTAL_FREQ * (42 + 1); // max MFI + MFN/MFD - pub fn output_freq(&self) -> Hertz { - let fref = F_REF.0 as u64; - let mfi = self.mfi as u64; - let mfn = self.mfn as u64; - let mfd = self.mfd as u64; + const MFN_FACTOR: u32 = 10; - let fvco = fref * (mfi + mfn / mfd); + let f_vco = self.freq_in.0; - Hertz(fvco as u32) + if f_vco < PLL_FREQ_MIN || f_vco > PLL_FREQ_MAX { + return None; + } + + let mfi = f_vco / PLL_XTAL_FREQ; + let mfn = f_vco % PLL_XTAL_FREQ; + + Some((mfi as u8, mfn * MFN_FACTOR)) } } @@ -154,7 +141,7 @@ pub struct ClockConfig { impl ClockConfig { pub const fn new(src: ClockMux, div: u16) -> Self { - assert!(div <= 256 || div > 0, "div must be in range 1 to 256"); + assert!(div <= 256 && div > 0, "div must be in range 1 to 256"); ClockConfig { src, raw_div: div as u8 - 1, @@ -162,51 +149,114 @@ impl ClockConfig { } } -pub(crate) unsafe fn init(config: Config) { - if let Some(pll0) = config.pll0.and_then(|pll| pll.check()) { - // set cpu0 to 24M - SYSCTL.clock_cpu(0).modify(|w| { - w.set_mux(ClockMux::CLK_24M); - w.set_div(0); - w.set_sub0_div(AHBDiv::DIV1); - }); - while SYSCTL.clock_cpu(0).read().glb_busy() {} +#[inline] +fn output_freq_of_pll(pll: usize) -> u64 { + let fref = F_REF.0 as f64; + let mfd = 240_000_000.0; // default value - // close PLL0 - // NOTE: MFI.enable is documented wrongly in v0.7 UM - PLLCTL.pll(0).mfi().modify(|w| w.set_enable(false)); + let mfi = PLLCTL.pll(pll).mfi().read().mfi() as f64; + let mfn = PLLCTL.pll(pll).mfn().read().mfn() as f64; - while PLLCTL.pll(0).mfi().read().busy() {} + let fvco = fref * (mfi + mfn / mfd); - // set PLL parameters and enable PLL - PLLCTL.pll(0).mfn().write(|w| w.set_mfn(pll0.mfn)); - PLLCTL.pll(0).mfd().write(|w| w.set_mfd(pll0.mfd)); // MFD 不支持运行时修改 + fvco as u64 +} - PLLCTL.pll(0).mfi().modify(|w| { - w.set_mfi(pll0.mfi); - w.set_enable(true) - }); +#[inline] +fn enable_group_resource(resource: usize) { + let resource_start = 256; - while PLLCTL.pll(0).mfi().read().busy() {} + if resource < resource_start { + return; + } - // Set DIVs - PLLCTL.pll(0).div(0).modify(|w| w.set_div(pll0.div.0)); - PLLCTL.pll(0).div(1).modify(|w| w.set_div(pll0.div.1)); - PLLCTL.pll(0).div(2).modify(|w| w.set_div(pll0.div.2)); + let index = (resource - resource_start) / 32; + let offset = (resource - resource_start) % 32; + + SYSCTL.group0(index).set().write(|w| w.set_link(1 << offset)); + + while SYSCTL.resource(resource).read().loc_busy() {} +} + +pub(crate) unsafe fn init(config: Config) { + if SYSCTL.clock_cpu(0).read().mux() == ClockMux::CLK_24M { + // TODO, enable XTAL + // SYSCTL.global00().modify(|w| w.set_mux(0b11)); + } + + enable_group_resource(pac::resources::CPU0); + enable_group_resource(pac::resources::AHB0); + enable_group_resource(pac::resources::LMM0); + enable_group_resource(pac::resources::MCT0); + enable_group_resource(pac::resources::ROM0); + enable_group_resource(pac::resources::TMR0); + enable_group_resource(pac::resources::TMR1); + enable_group_resource(pac::resources::I2C2); + enable_group_resource(pac::resources::SPI1); + enable_group_resource(pac::resources::URT0); + enable_group_resource(pac::resources::URT3); + + enable_group_resource(pac::resources::WDG0); + enable_group_resource(pac::resources::WDG1); + enable_group_resource(pac::resources::MBX0); + enable_group_resource(pac::resources::TSNS); + enable_group_resource(pac::resources::CRC0); + enable_group_resource(pac::resources::ADC0); + enable_group_resource(pac::resources::ACMP); + enable_group_resource(pac::resources::KMAN); + enable_group_resource(pac::resources::GPIO); + enable_group_resource(pac::resources::HDMA); + enable_group_resource(pac::resources::XPI0); + enable_group_resource(pac::resources::USB0); + + // Connect Group0 to CPU0 + SYSCTL.affiliate(0).set().write(|w| w.set_link(1 << 0)); + + // Bump up DCDC voltage to 1175mv (default is 1150) + pac::PCFG.dcdc_mode().modify(|w| w.set_volt(1175)); + + if let Some(pll) = config.pll0.as_ref() { + if let Some((mfi, mfn)) = pll.get_params() { + if PLLCTL.pll(0).mfi().read().mfi() == mfi { + PLLCTL.pll(0).mfi().modify(|w| { + w.set_mfi(mfi - 1); + }); + } + + PLLCTL.pll(0).mfi().modify(|w| { + w.set_mfi(mfi); + }); + + // Default mfd is 240M + PLLCTL.pll(0).mfn().write(|w| w.set_mfn(mfn)); + + while PLLCTL.pll(0).mfi().read().busy() {} + } + + let fvco = output_freq_of_pll(0); + + // set postdiv + PLLCTL.pll(0).div(0).write(|w| { + w.set_div(pll.div.0); + w.set_enable(true); + }); + PLLCTL.pll(0).div(1).write(|w| { + w.set_div(pll.div.1); + w.set_enable(true); + }); + PLLCTL.pll(0).div(2).write(|w| { + w.set_div(pll.div.2); + w.set_enable(true); + }); while PLLCTL.pll(0).div(0).read().busy() || PLLCTL.pll(0).div(1).read().busy() || PLLCTL.pll(0).div(2).read().busy() {} - let fvco = pll0.output_freq().0 as u64; // convert u64 to avoid overflow - - //let pll0clk0 = fvco / (pll0.div.0 as u32 / 5 + 1); - //let pll0clk1 = fvco / (pll0.div.1 as u32 / 5 + 1); - //let pll0clk2 = fvco / (pll0.div.2 as u32 / 5 + 1); - let pll0clk0 = fvco * 5 / (pll0.div.0 as u64 + 5); - let pll0clk1 = fvco * 5 / (pll0.div.1 as u64 + 5); - let pll0clk2 = fvco * 5 / (pll0.div.2 as u64 + 5); + let pll0clk0 = fvco * 5 / (PLLCTL.pll(0).div(0).read().div() as u64 + 5); + let pll0clk1 = fvco * 5 / (PLLCTL.pll(0).div(1).read().div() as u64 + 5); + let pll0clk2 = fvco * 5 / (PLLCTL.pll(0).div(2).read().div() as u64 + 5); unsafe { CLOCKS.pll0clk0 = Hertz(pll0clk0 as u32); @@ -223,18 +273,13 @@ pub(crate) unsafe fn init(config: Config) { while SYSCTL.clock_cpu(0).read().glb_busy() {} - let hart0_clk = clocks().get_freq(&config.cpu0); - let ahb_clk = hart0_clk / config.ahb_div; + let cpu0_clk = clocks().get_freq(&config.cpu0); + let ahb_clk = cpu0_clk / config.ahb_div; unsafe { - CLOCKS.hart0 = hart0_clk; + CLOCKS.cpu0 = cpu0_clk; CLOCKS.ahb = ahb_clk; } - - SYSCTL.group0(0).value().write(|w| w.0 = 0xFFFFFFFF); - SYSCTL.group0(1).value().write(|w| w.0 = 0xFFFFFFFF); - - SYSCTL.affiliate(0).set().write(|w| w.set_link(1)); } pub fn clocks() -> &'static Clocks { diff --git a/src/uart/mod.rs b/src/uart/mod.rs index 88f4d3c..6e1fe73 100644 --- a/src/uart/mod.rs +++ b/src/uart/mod.rs @@ -838,8 +838,8 @@ pin_trait!(CtsPin, Instance); pin_trait!(RtsPin, Instance); pin_trait!(DePin, Instance); -//dma_trait!(TxDma, Instance); -//dma_trait!(RxDma, Instance); +dma_trait!(TxDma, Instance); +dma_trait!(RxDma, Instance); macro_rules! impl_uart { ($inst:ident) => {