From 43ccb866971a21f86afb2521a0a6cdf3f99bbe0f Mon Sep 17 00:00:00 2001 From: occheung Date: Mon, 14 Sep 2020 15:36:03 +0800 Subject: [PATCH] scpi: adopt rust standard on result/option handling --- Cargo.toml | 7 +- examples/ethernet.rs | 9 -- examples/fpga_config.rs | 3 - examples/mqtt_client.rs | 91 ++++-------- examples/mqtt_hello_world.rs | 266 +++++++++++++++++++++++++++++++++++ examples/tcp_client.rs | 32 ++--- examples/tcp_stack.rs | 180 ++++++++++++++++++++++++ examples/util/logger.rs | 20 +-- gdb_config/fpga_config.gdb | 2 +- src/lib.rs | 1 + src/nal_tcp_client.rs | 7 + src/scpi.rs | 193 ++++++++++--------------- 12 files changed, 577 insertions(+), 234 deletions(-) create mode 100644 examples/mqtt_hello_world.rs create mode 100644 examples/tcp_stack.rs diff --git a/Cargo.toml b/Cargo.toml index 7622219..d007502 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,10 +20,8 @@ heapless = "0.5.5" # Logging and Panicking panic-itm = "0.4.1" -panic-semihosting = { version = "0.5.3", features = [ "exit" ] } cortex-m-rtic = "0.5.3" -cortex-m-log = { version = "0.6.2", features = [ "itm", "log-integration", "semihosting" ] } -cortex-m-semihosting = "0.3.3" +cortex-m-log = { version = "0.6.2", features = [ "itm", "log-integration" ] } log = {version = "0.4.11"} lazy_static = { version = "1.4.0", features = ["spin_no_std"] } @@ -45,6 +43,9 @@ name = "tcp_client" [[example]] name = "mqtt_client" +[[example]] +name = "mqtt_hello_world" + # Uncomment for the allocator example. # alloc-cortex-m = "0.3.5" diff --git a/examples/ethernet.rs b/examples/ethernet.rs index c48e32d..f68176e 100644 --- a/examples/ethernet.rs +++ b/examples/ethernet.rs @@ -1,28 +1,19 @@ #![no_main] #![no_std] -// extern crate cortex_m_rt as rt; use core::sync::atomic::{AtomicU32, Ordering}; #[macro_use] extern crate log; -// extern crate cortex_m; -use panic_semihosting as _; - use cortex_m; use cortex_m::asm::nop; use cortex_m_rt::{ entry, exception, }; -use cortex_m_semihosting::hprintln; - extern crate smoltcp; -// Ethernet crate for STM32H7 has been merged into HAL in the latest commit -// extern crate stm32h7_ethernet as ethernet; - use stm32h7xx_hal::ethernet; use stm32h7xx_hal::gpio::Speed; use stm32h7xx_hal::hal::digital::v2::{ diff --git a/examples/fpga_config.rs b/examples/fpga_config.rs index d844f0e..39c1bb1 100644 --- a/examples/fpga_config.rs +++ b/examples/fpga_config.rs @@ -3,9 +3,6 @@ #[macro_use] extern crate log; - -use panic_semihosting as _; - use stm32h7xx_hal::hal::digital::v2::{ InputPin, OutputPin, diff --git a/examples/mqtt_client.rs b/examples/mqtt_client.rs index 2c79153..367aa12 100644 --- a/examples/mqtt_client.rs +++ b/examples/mqtt_client.rs @@ -19,10 +19,6 @@ use cortex_m_rt::{ entry, exception, }; -use cortex_m_semihosting::hprintln; - -// use panic_halt as _; - use rtic::cyccnt::{Instant, U32Ext}; use minimq::{ @@ -67,8 +63,6 @@ macro_rules! add_socket { #[entry] fn main() -> ! { - // logger::semihosting_init(); - let mut cp = cortex_m::Peripherals::take().unwrap(); let dp = pac::Peripherals::take().unwrap(); @@ -84,15 +78,17 @@ fn main() -> ! { let rcc = dp.RCC.constrain(); let ccdr = rcc - .use_hse(16.mhz()) + .use_hse(8.mhz()) .sysclk(400.mhz()) .hclk(200.mhz()) - .per_ck(100.mhz()) .pll1_q_ck(48.mhz()) // for SPI - .pll2_p_ck(100.mhz()) - .pll2_q_ck(100.mhz()) + .pll1_r_ck(400.mhz()) // for TRACECK .freeze(vos, &dp.SYSCFG); + unsafe { + logger::enable_itm(&dp.DBGMCU, &mut cp.DCB, &mut cp.ITM); + } + logger::init(); let mut delay = cp.SYST.delay(ccdr.clocks); let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); @@ -103,13 +99,6 @@ fn main() -> ! { let gpiof = dp.GPIOF.split(ccdr.peripheral.GPIOF); let gpiog = dp.GPIOG.split(ccdr.peripheral.GPIOG); - let mut yellow_led = gpioe.pe1.into_push_pull_output(); - yellow_led.set_low().unwrap(); - let mut red_led = gpiob.pb14.into_push_pull_output(); - red_led.set_high().unwrap(); - let mut green_led = gpiob.pb0.into_push_pull_output(); - green_led.set_low().unwrap(); - // Configure ethernet IO { let _rmii_refclk = gpioa.pa1.into_alternate_af11().set_speed(Speed::VeryHigh); @@ -125,7 +114,7 @@ fn main() -> ! { // Configure ethernet let mac_addr = net::wire::EthernetAddress([0xAC, 0x6F, 0x7A, 0xDE, 0xD6, 0xC8]); - let (eth_dma, _eth_mac) = unsafe { + let (eth_dma, mut eth_mac) = unsafe { ethernet::new_unchecked( dp.ETHERNET_MAC, dp.ETHERNET_MTL, @@ -143,15 +132,10 @@ fn main() -> ! { let neighbor_cache = net::iface::NeighborCache::new(&mut store.neighbor_cache[..]); - let default_v4_gw = net::wire::Ipv4Address::new(192, 168, 1, 1); - let mut routes = net::iface::Routes::new(&mut store.routes_cache[..]); - routes.add_default_ipv4_route(default_v4_gw).unwrap(); - let mut net_interface = net::iface::EthernetInterfaceBuilder::new(eth_dma) .ethernet_addr(mac_addr) .neighbor_cache(neighbor_cache) .ip_addrs(&mut store.ip_addrs[..]) - // .routes(routes) .finalize(); /* @@ -192,13 +176,13 @@ fn main() -> ! { cp.SCB.invalidate_icache(); cp.SCB.enable_icache(); - // cp.SCB.clean_dcache(&mut cp.CPUID); - // cp.SCB.disable_dcache(&mut cp.CPUID); - // cp.SCB.enable_dcache(&mut cp.CPUID); + // Time unit in ms let mut time: u32 = 0; - let mut next_ms = Instant::now(); + // Cycle counter for 1 ms + // This effectively provides a conversion from rtic unit to ms + let mut next_ms = Instant::now(); next_ms += 400_000.cycles(); let mut socket_set_entries: [_; 8] = Default::default(); @@ -212,50 +196,33 @@ fn main() -> ! { tcp_stack, ) .unwrap(); - - delay.delay_ms(1000_u16); - - client.network_stack.update(time); - green_led.set_high().unwrap(); + let mut tick = false; loop { - let tick = Instant::now() > next_ms; - - if tick { - next_ms += 400_000.cycles(); + // Update time accumulator in ms + // Tick once every ms + if Instant::now() > next_ms { + tick = true; time += 1; + next_ms += 400_000.cycles(); } - client.network_stack.update(time); + // Poll if necessary + if tick && client.network_stack.update_delay(time) == 0 && eth_mac.phy_poll_link() { + client.network_stack.update(time); + } - client + let connection = client .poll(|_client, topic, message, _properties| match topic { _ => info!("On '{:?}', received: {:?}", topic, message), - }) - .unwrap(); + }).is_ok(); - match client.is_connected() { - true => { - yellow_led.set_high().unwrap(); - red_led.set_low().unwrap(); - }, - _ => { - yellow_led.set_low().unwrap(); - red_led.set_high().unwrap(); - }, - }; - - if tick && (time % 1000) == 0 { - client - .publish("nucleo", "Hello, World!".as_bytes(), QoS::AtMostOnce, &[]) - .unwrap(); + if connection && tick && (time % 3000) == 0 { + info!("Feedback from print publish: {:?}", client + .publish("Channel1/Switch", "Hello, World!".as_bytes(), QoS::AtMostOnce, &[])); } - // Update the TCP stack. - // let sleep = client.network_stack.update(time); - // if sleep { - // //cortex_m::asm::wfi(); - // cortex_m::asm::nop(); - // } + // Reset tick flag + tick = false; } -} +} \ No newline at end of file diff --git a/examples/mqtt_hello_world.rs b/examples/mqtt_hello_world.rs new file mode 100644 index 0000000..2333af1 --- /dev/null +++ b/examples/mqtt_hello_world.rs @@ -0,0 +1,266 @@ + +#![no_std] +#![no_main] + +#[macro_use] +extern crate log; + +use smoltcp as net; +// use stm32h7_ethernet as ethernet; +use stm32h7xx_hal::{gpio::Speed, prelude::*, ethernet}; + +use heapless::{consts, String}; +// use si7021::Si7021; + +use cortex_m; + +use panic_halt as _; +// use serde::{Deserialize, Serialize}; + +use rtic::cyccnt::{Instant, U32Ext}; + +mod tcp_stack; + +#[path = "util/logger.rs"] +mod logger; + +use minimq::{ + embedded_nal::{IpAddr, Ipv4Addr}, + MqttClient, QoS, +}; +use tcp_stack::NetworkStack; + +pub struct NetStorage { + ip_addrs: [net::wire::IpCidr; 1], + neighbor_cache: [Option<(net::wire::IpAddress, net::iface::Neighbor)>; 8], +} + +static mut NET_STORE: NetStorage = NetStorage { + // Placeholder for the real IP address, which is initialized at runtime. + ip_addrs: [net::wire::IpCidr::Ipv6( + net::wire::Ipv6Cidr::SOLICITED_NODE_PREFIX, + )], + neighbor_cache: [None; 8], +}; + +#[link_section = ".sram3.eth"] +static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new(); + +// #[derive(Serialize, Deserialize)] +// struct Temperature { +// temperature_c: f32, +// } + +type NetworkInterface = + net::iface::EthernetInterface<'static, 'static, 'static, ethernet::EthernetDMA<'static>>; + +macro_rules! add_socket { + ($sockets:ident, $tx_storage:ident, $rx_storage:ident) => { + let mut $rx_storage = [0; 4096]; + let mut $tx_storage = [0; 4096]; + + let tcp_socket = { + let tx_buffer = net::socket::TcpSocketBuffer::new(&mut $tx_storage[..]); + let rx_buffer = net::socket::TcpSocketBuffer::new(&mut $rx_storage[..]); + + net::socket::TcpSocket::new(tx_buffer, rx_buffer) + }; + + let _handle = $sockets.add(tcp_socket); + }; +} + +// #[cfg(not(feature = "semihosting"))] +// fn init_log() {} + +// #[cfg(feature = "semihosting")] +// fn init_log() { +// use cortex_m_log::log::{init as init_log, Logger}; +// use cortex_m_log::printer::semihosting::{hio::HStdout, InterruptOk}; +// use log::LevelFilter; + +// static mut LOGGER: Option>> = None; + +// let logger = Logger { +// inner: InterruptOk::<_>::stdout().unwrap(), +// level: LevelFilter::Info, +// }; + +// let logger = unsafe { LOGGER.get_or_insert(logger) }; + +// init_log(logger).unwrap(); +// } + +#[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] +const APP: () = { + struct Resources { + net_interface: NetworkInterface, + // si7021: Si7021>, + } + + #[init] + fn init(c: init::Context) -> init::LateResources { + let mut cp = unsafe { + cortex_m::Peripherals::steal() + }; + cp.DWT.enable_cycle_counter(); + + // Enable SRAM3 for the descriptor ring. + c.device.RCC.ahb2enr.modify(|_, w| w.sram3en().set_bit()); + + let rcc = c.device.RCC.constrain(); + let pwr = c.device.PWR.constrain(); + let vos = pwr.freeze(); + + let ccdr = rcc + .sysclk(400.mhz()) + .hclk(200.mhz()) + .per_ck(100.mhz()) + .pll1_r_ck(400.mhz()) // for TRACECK + .pll2_p_ck(100.mhz()) + .pll2_q_ck(100.mhz()) + .freeze(vos, &c.device.SYSCFG); + + unsafe { + logger::enable_itm(&c.device.DBGMCU, &mut cp.DCB, &mut cp.ITM); + } + logger::init(); + + let gpioa = c.device.GPIOA.split(ccdr.peripheral.GPIOA); + let gpiob = c.device.GPIOB.split(ccdr.peripheral.GPIOB); + let gpioc = c.device.GPIOC.split(ccdr.peripheral.GPIOC); + let gpiof = c.device.GPIOF.split(ccdr.peripheral.GPIOF); + let gpiog = c.device.GPIOG.split(ccdr.peripheral.GPIOG); + + // Configure ethernet IO + { + let _rmii_refclk = gpioa.pa1.into_alternate_af11().set_speed(Speed::VeryHigh); + let _rmii_mdio = gpioa.pa2.into_alternate_af11().set_speed(Speed::VeryHigh); + let _rmii_mdc = gpioc.pc1.into_alternate_af11().set_speed(Speed::VeryHigh); + let _rmii_crs_dv = gpioa.pa7.into_alternate_af11().set_speed(Speed::VeryHigh); + let _rmii_rxd0 = gpioc.pc4.into_alternate_af11().set_speed(Speed::VeryHigh); + let _rmii_rxd1 = gpioc.pc5.into_alternate_af11().set_speed(Speed::VeryHigh); + let _rmii_tx_en = gpiog.pg11.into_alternate_af11().set_speed(Speed::VeryHigh); + let _rmii_txd0 = gpiog.pg13.into_alternate_af11().set_speed(Speed::VeryHigh); + let _rmii_txd1 = gpiob.pb13.into_alternate_af11().set_speed(Speed::VeryHigh); + } + + // Configure ethernet + let net_interface = { + let mac_addr = net::wire::EthernetAddress([0xAC, 0x6F, 0x7A, 0xDE, 0xD6, 0xC8]); + let (eth_dma, mut _eth_mac) = unsafe { + ethernet::new_unchecked( + c.device.ETHERNET_MAC, + c.device.ETHERNET_MTL, + c.device.ETHERNET_DMA, + &mut DES_RING, + mac_addr.clone(), + ) + }; + + while !_eth_mac.phy_poll_link() {} + + unsafe { ethernet::enable_interrupt() } + + let store = unsafe { &mut NET_STORE }; + + store.ip_addrs[0] = net::wire::IpCidr::new(net::wire::IpAddress::v4(192, 168, 1, 200), 24); + + let neighbor_cache = net::iface::NeighborCache::new(&mut store.neighbor_cache[..]); + + net::iface::EthernetInterfaceBuilder::new(eth_dma) + .ethernet_addr(mac_addr) + .neighbor_cache(neighbor_cache) + .ip_addrs(&mut store.ip_addrs[..]) + .finalize() + }; + + // Configure I2C + // let si7021 = { + // let i2c_sda = gpiof.pf0.into_open_drain_output().into_alternate_af4(); + // let i2c_scl = gpiof.pf1.into_open_drain_output().into_alternate_af4(); + // let i2c = c.device.I2C2.i2c( + // (i2c_scl, i2c_sda), + // 100.khz(), + // ccdr.peripheral.I2C2, + // &ccdr.clocks, + // ); + + // Si7021::new(i2c) + // }; + + cp.SCB.enable_icache(); + + init::LateResources { + net_interface: net_interface, + // si7021: si7021, + } + } + + #[idle(resources=[net_interface])] + fn idle(c: idle::Context) -> ! { + let mut time: u32 = 0; + let mut next_ms = Instant::now(); + + next_ms += 400_00.cycles(); + + let mut socket_set_entries: [_; 8] = Default::default(); + let mut sockets = net::socket::SocketSet::new(&mut socket_set_entries[..]); + add_socket!(sockets, rx_storage, tx_storage); + + let tcp_stack = NetworkStack::new(c.resources.net_interface, sockets); + let mut client = MqttClient::::new( + IpAddr::V4(Ipv4Addr::new(192, 168, 1, 125)), + "nucleo", + tcp_stack, + ) + .unwrap(); + + loop { + let tick = Instant::now() > next_ms; + + if tick { + next_ms += 400_000.cycles(); + time += 1; + } + + if tick && (time % 1000) == 0 { + client + .publish("nucleo", "Hello, World!".as_bytes(), QoS::AtMostOnce, &[]) + .unwrap(); + + // let temperature = Temperature { + // temperature_c: c.resources.si7021.temperature_celsius().unwrap(), + // }; + // let temperature: String = + // serde_json_core::to_string(&temperature).unwrap(); + // client + // .publish( + // "temperature", + // &temperature.into_bytes(), + // QoS::AtMostOnce, + // &[], + // ) + // .unwrap(); + } + + client + .poll(|_client, topic, message, _properties| match topic { + _ => info!("On '{:?}', received: {:?}", topic, message), + }); + // .unwrap(); + + // Update the TCP stack. + let sleep = client.network_stack.update(time); + if sleep { + //cortex_m::asm::wfi(); + cortex_m::asm::nop(); + } + } + } + + #[task(binds=ETH)] + fn eth(_: eth::Context) { + unsafe { ethernet::interrupt_handler() } + } +}; \ No newline at end of file diff --git a/examples/tcp_client.rs b/examples/tcp_client.rs index 0666f11..66272ed 100644 --- a/examples/tcp_client.rs +++ b/examples/tcp_client.rs @@ -85,7 +85,7 @@ fn main() -> ! { let ccdr = rcc .use_hse(8.mhz()) .sysclk(400.mhz()) - // .hclk(200.mhz()) + .hclk(200.mhz()) // .per_ck(100.mhz()) .pll1_q_ck(48.mhz()) // for SPI .pll1_r_ck(400.mhz()) // for TRACECK @@ -133,7 +133,7 @@ fn main() -> ! { // Configure ethernet let mac_addr = net::wire::EthernetAddress([0xAC, 0x6F, 0x7A, 0xDE, 0xD6, 0xC8]); - let (eth_dma, _eth_mac) = unsafe { + let (eth_dma, mut eth_mac) = unsafe { ethernet::new_unchecked( dp.ETHERNET_MAC, dp.ETHERNET_MTL, @@ -219,12 +219,13 @@ fn main() -> ! { }; let handle = sockets.add(tcp_socket); - delay.delay_ms(2000_u16); + // delay.delay_ms(2000_u16); green_led.set_high().unwrap(); { let mut socket = sockets.get::(handle); socket.connect((net::wire::IpAddress::v4(192, 168, 1, 125), 1883), 49500).unwrap(); + socket.set_timeout(Some(net::time::Duration::from_millis(2000))); debug!("connect!"); } @@ -232,14 +233,15 @@ fn main() -> ! { red_led.set_high().unwrap(); let mut green = true; - // let mut eth_status = 0; - // unsafe { - // eth_status = (*pac::ETHERNET_MAC::ptr()).macdr.read().bits(); - // debug!("eth_status: {:X}", eth_status); - // } + let mut connected = false; + + debug!("Poll link status: {}", eth_mac.phy_poll_link()); + while !eth_mac.phy_poll_link() {} + debug!("Poll link status: {}", eth_mac.phy_poll_link()); loop { // let timestamp = net::time::Instant::from_millis(TIME.load(Ordering::Relaxed) as i64); + while !eth_mac.phy_poll_link() {} match net_interface.poll(&mut sockets, clock.elapsed()) { Ok(_) => {}, Err(e) => { @@ -247,20 +249,11 @@ fn main() -> ! { } } - // unsafe { - // // let p = &DES_RING as *const _; - // // let tdeses = core::ptr::read_volatile(p as *const [u32; 16]); - // // debug!("{:X?}", core::ptr::read_volatile(tdeses[0] as *const [u32; 20])) - // let new_eth_status = (*pac::ETHERNET_MAC::ptr()).macdr.read().bits(); - // if new_eth_status != eth_status { - // eth_status = new_eth_status; - // debug!("eth_status: {:X}", eth_status); - // } - // } - { let mut socket = sockets.get::(handle); + info!("Socket state: {} at time {}", socket.state(), clock.elapsed()); + if socket.may_recv() { yellow_led.set_high().unwrap(); red_led.set_low().unwrap(); @@ -281,6 +274,7 @@ fn main() -> ! { match net_interface.poll_delay(&sockets, clock.elapsed()) { Some(net::time::Duration {millis :0}) => { + clock.advance(net::time::Duration::from_millis(1)); continue; } Some(time_delay) => { diff --git a/examples/tcp_stack.rs b/examples/tcp_stack.rs new file mode 100644 index 0000000..7fb1295 --- /dev/null +++ b/examples/tcp_stack.rs @@ -0,0 +1,180 @@ +use core::cell::RefCell; +use nb; + +use heapless::{consts, Vec}; + +use super::{net, NetworkInterface}; +use minimq::embedded_nal; + +#[derive(Debug)] +pub enum NetworkError { + NoSocket, + ConnectionFailure, + ReadFailure, + WriteFailure, +} + +// TODO: The network stack likely needs a time-tracking mechanic here to support +// blocking/nonblocking/timeout based operations. +pub struct NetworkStack<'a, 'b, 'c, 'n> { + network_interface: &'n mut NetworkInterface, + sockets: RefCell>, + next_port: RefCell, + unused_handles: RefCell>, +} + +impl<'a, 'b, 'c, 'n> NetworkStack<'a, 'b, 'c, 'n> { + pub fn new( + interface: &'n mut NetworkInterface, + sockets: net::socket::SocketSet<'a, 'b, 'c>, + ) -> Self { + let mut unused_handles: Vec = Vec::new(); + for socket in sockets.iter() { + unused_handles.push(socket.handle()).unwrap(); + } + + NetworkStack { + network_interface: interface, + sockets: RefCell::new(sockets), + next_port: RefCell::new(49152), + unused_handles: RefCell::new(unused_handles), + } + } + + pub fn update(&mut self, time: u32) -> bool { + match self.network_interface.poll( + &mut self.sockets.borrow_mut(), + net::time::Instant::from_millis(time as i64), + ) { + Ok(changed) => changed == false, + Err(e) => { + info!("{:?}", e); + true + } + } + } + + fn get_ephemeral_port(&self) -> u16 { + // Get the next ephemeral port + let current_port = self.next_port.borrow().clone(); + + let (next, wrap) = self.next_port.borrow().overflowing_add(1); + *self.next_port.borrow_mut() = if wrap { 49152 } else { next }; + + return current_port; + } +} + +impl<'a, 'b, 'c, 'n> embedded_nal::TcpStack for NetworkStack<'a, 'b, 'c, 'n> { + type TcpSocket = net::socket::SocketHandle; + type Error = NetworkError; + + fn open(&self, _mode: embedded_nal::Mode) -> Result { + // TODO: Handle mode? + match self.unused_handles.borrow_mut().pop() { + Some(handle) => { + // Abort any active connections on the handle. + let mut sockets = self.sockets.borrow_mut(); + let internal_socket: &mut net::socket::TcpSocket = &mut *sockets.get(handle); + internal_socket.abort(); + + Ok(handle) + } + None => Err(NetworkError::NoSocket), + } + } + + fn connect( + &self, + socket: Self::TcpSocket, + remote: embedded_nal::SocketAddr, + ) -> Result { + // TODO: Handle socket mode? + + let mut sockets = self.sockets.borrow_mut(); + let internal_socket: &mut net::socket::TcpSocket = &mut *sockets.get(socket); + + // If we're already in the process of connecting, ignore the request silently. + if internal_socket.is_open() { + return Ok(socket); + } + + match remote.ip() { + embedded_nal::IpAddr::V4(addr) => { + let address = { + let octets = addr.octets(); + net::wire::Ipv4Address::new(octets[0], octets[1], octets[2], octets[3]) + }; + internal_socket + .connect((address, remote.port()), self.get_ephemeral_port()) + .map_err(|_| NetworkError::ConnectionFailure)?; + // internal_socket.set_timeout(Some(net::time::Duration::from_millis(2000))); + } + embedded_nal::IpAddr::V6(addr) => { + let address = { + let octets = addr.segments(); + net::wire::Ipv6Address::new( + octets[0], octets[1], octets[2], octets[3], octets[4], octets[5], + octets[6], octets[7], + ) + }; + internal_socket + .connect((address, remote.port()), self.get_ephemeral_port()) + .map_err(|_| NetworkError::ConnectionFailure)?; + // internal_socket.set_timeout(Some(net::time::Duration::from_millis(2000))); + } + }; + + Ok(socket) + } + + fn is_connected(&self, socket: &Self::TcpSocket) -> Result { + let mut sockets = self.sockets.borrow_mut(); + let socket: &mut net::socket::TcpSocket = &mut *sockets.get(*socket); + + Ok(socket.may_send() && socket.may_recv()) + } + + fn write(&self, socket: &mut Self::TcpSocket, buffer: &[u8]) -> nb::Result { + // TODO: Handle the socket mode. + + let mut sockets = self.sockets.borrow_mut(); + let socket: &mut net::socket::TcpSocket = &mut *sockets.get(*socket); + + let result = socket.send_slice(buffer); + + match result { + Ok(num_bytes) => Ok(num_bytes), + Err(_) => Err(nb::Error::Other(NetworkError::WriteFailure)), + } + } + + fn read( + &self, + socket: &mut Self::TcpSocket, + buffer: &mut [u8], + ) -> nb::Result { + // TODO: Handle the socket mode. + + let mut sockets = self.sockets.borrow_mut(); + let socket: &mut net::socket::TcpSocket = &mut *sockets.get(*socket); + + let result = socket.recv_slice(buffer); + + match result { + Ok(num_bytes) => Ok(num_bytes), + Err(_) => Err(nb::Error::Other(NetworkError::ReadFailure)), + } + } + + fn close(&self, socket: Self::TcpSocket) -> Result<(), Self::Error> { + // TODO: Free the ephemeral port in use by the socket. + + let mut sockets = self.sockets.borrow_mut(); + let internal_socket: &mut net::socket::TcpSocket = &mut *sockets.get(socket); + internal_socket.close(); + + self.unused_handles.borrow_mut().push(socket).unwrap(); + Ok(()) + } +} \ No newline at end of file diff --git a/examples/util/logger.rs b/examples/util/logger.rs index c6ab530..76e1784 100644 --- a/examples/util/logger.rs +++ b/examples/util/logger.rs @@ -48,7 +48,7 @@ pub unsafe fn enable_itm( ); } -// use panic_itm as _; +use panic_itm as _; use lazy_static::lazy_static; use log::LevelFilter; @@ -75,22 +75,4 @@ lazy_static! { pub fn init() { cortex_m_log::log::init(&LOGGER).unwrap(); -} - -// use panic_semihosting as _; - -use cortex_m_log::printer::semihosting; -use cortex_m_log::printer::semihosting::Semihosting; -use cortex_m_log::modes::InterruptOk; -use cortex_m_semihosting::hio::HStdout; - -lazy_static! { - static ref HLOGGER: Logger> = Logger { - level: LevelFilter::Trace, - inner: semihosting::InterruptOk::<_>::stdout().expect("Get Semihosting stdout"), - }; -} - -pub fn semihosting_init() { - cortex_m_log::log::init(&HLOGGER).unwrap(); } \ No newline at end of file diff --git a/gdb_config/fpga_config.gdb b/gdb_config/fpga_config.gdb index 94b8ca1..c9502bd 100644 --- a/gdb_config/fpga_config.gdb +++ b/gdb_config/fpga_config.gdb @@ -12,7 +12,7 @@ break HardFault break rust_begin_unwind # break at line 130 to auto quit -break examples/fpga_config.rs:143 +break examples/fpga_config.rs:140 # print using semihosting, slow af # monitor arm semihosting enable diff --git a/src/lib.rs b/src/lib.rs index 3f24b64..7523da5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,6 +55,7 @@ pub enum Error { ParameterError, } +#[derive(Debug)] pub enum ClockSource { OSC, SMA, diff --git a/src/nal_tcp_client.rs b/src/nal_tcp_client.rs index 850b814..026ee9f 100644 --- a/src/nal_tcp_client.rs +++ b/src/nal_tcp_client.rs @@ -52,6 +52,13 @@ impl<'a, 'b, 'c, 'n> NetworkStack<'a, 'b, 'c, 'n> { } } + pub fn update_delay(&mut self, time: u32) -> u32 { + self.network_interface.poll_delay( + &mut self.sockets.borrow_mut(), + net::time::Instant::from_millis(time as i64), + ).map_or(1000, |next_poll_time| next_poll_time.total_millis() as u32) + } + pub fn update(&mut self, time: u32) -> bool { match self.network_interface.poll( &mut self.sockets.borrow_mut(), diff --git a/src/scpi.rs b/src/scpi.rs index 7d0ee61..7efec61 100644 --- a/src/scpi.rs +++ b/src/scpi.rs @@ -152,20 +152,14 @@ impl Command for Channel0SwitchCommand { nquery!(); fn event(&self, context: &mut Context, args: &mut Tokenizer) -> Result<()> { - match context.device.get_channel_switch_status(0) { - Ok(status) => { - let next_state: bool = match args.next_data(true) { - Ok(Some(token)) => token.try_into()?, - Ok(None) => !status, - Err(_) => return Err(ErrorCode::IllegalParameterValue.into()), - }; - match context.device.set_channel_switch(0, next_state) { - Ok(()) => Ok(()), - Err(_) => Err(Error::new(ErrorCode::HardwareError)), - } - }, - Err(_) => Err(Error::new(ErrorCode::HardwareError)), - } + let next_state: bool = args.next_data(true)? + .map_or( + context.device.get_channel_switch_status(0) + .map(|current| !current) + .map_err(|_| Error::new(ErrorCode::HardwareError)), + |token| token.try_into() + )?; + context.device.set_channel_switch(0, next_state).map_err(|_| Error::new(ErrorCode::HardwareError)) } } @@ -173,20 +167,14 @@ impl Command for Channel1SwitchCommand { nquery!(); fn event(&self, context: &mut Context, args: &mut Tokenizer) -> Result<()> { - match context.device.get_channel_switch_status(1) { - Ok(status) => { - let next_state: bool = match args.next_data(true) { - Ok(Some(token)) => token.try_into()?, - Ok(None) => !status, - Err(_) => return Err(ErrorCode::IllegalParameterValue.into()), - }; - match context.device.set_channel_switch(1, next_state) { - Ok(()) => Ok(()), - Err(_) => Err(Error::new(ErrorCode::HardwareError)), - } - }, - Err(_) => Err(Error::new(ErrorCode::HardwareError)), - } + let next_state: bool = args.next_data(true)? + .map_or( + context.device.get_channel_switch_status(1) + .map(|current| !current) + .map_err(|_| Error::new(ErrorCode::HardwareError)), + |token| token.try_into() + )?; + context.device.set_channel_switch(1, next_state).map_err(|_| Error::new(ErrorCode::HardwareError)) } } @@ -194,20 +182,14 @@ impl Command for Channel2SwitchCommand { nquery!(); fn event(&self, context: &mut Context, args: &mut Tokenizer) -> Result<()> { - match context.device.get_channel_switch_status(2) { - Ok(status) => { - let next_state: bool = match args.next_data(true) { - Ok(Some(token)) => token.try_into()?, - Ok(None) => !status, - Err(_) => return Err(ErrorCode::IllegalParameterValue.into()), - }; - match context.device.set_channel_switch(2, next_state) { - Ok(()) => Ok(()), - Err(_) => Err(Error::new(ErrorCode::HardwareError)), - } - }, - Err(_) => Err(Error::new(ErrorCode::HardwareError)), - } + let next_state: bool = args.next_data(true)? + .map_or( + context.device.get_channel_switch_status(2) + .map(|current| !current) + .map_err(|_| Error::new(ErrorCode::HardwareError)), + |token| token.try_into() + )?; + context.device.set_channel_switch(2, next_state).map_err(|_| Error::new(ErrorCode::HardwareError)) } } @@ -215,74 +197,32 @@ impl Command for Channel3SwitchCommand { nquery!(); fn event(&self, context: &mut Context, args: &mut Tokenizer) -> Result<()> { - match context.device.get_channel_switch_status(3) { - Ok(status) => { - let next_state: bool = match args.next_data(true) { - Ok(Some(token)) => token.try_into()?, - Ok(None) => !status, - Err(_) => return Err(ErrorCode::IllegalParameterValue.into()), - }; - match context.device.set_channel_switch(3, next_state) { - Ok(()) => Ok(()), - Err(_) => Err(Error::new(ErrorCode::HardwareError)), - } - }, - Err(_) => Err(Error::new(ErrorCode::HardwareError)), - } + let next_state: bool = args.next_data(true)? + .map_or( + context.device.get_channel_switch_status(3) + .map(|current| !current) + .map_err(|_| Error::new(ErrorCode::HardwareError)), + |token| token.try_into() + )?; + context.device.set_channel_switch(3, next_state).map_err(|_| Error::new(ErrorCode::HardwareError)) } } +// Handle CLOCK:SOURCE command, setup the proper source for the system clock +// Leave clock division to CLOCK:DIVision command impl Command for ClockSourceCommand { nquery!(); fn event(&self, context: &mut Context, args: &mut Tokenizer) -> Result<()> { + // next_data() fucntion call can never return CharacterProgramData, could be an oversight let s: &[u8] = match args.next_data(false)? { Some(Token::CharacterProgramData(s)) => s, _ => return Err(ErrorCode::IllegalParameterValue.into()), }; - /* - debug!("Converted data: {:?}", data); - if let Ok(str_param) = str::from_utf8(data) { - if let Ok(cmd) = match str_param { - "OSC" => context.device.set_clock_source(ClockSource::OSC), - "MMCX" => context.device.set_clock_source(ClockSource::MMCX), - "SMA" => context.device.set_clock_source(ClockSource::SMA), - _ => { - return Err(ErrorCode::IllegalParameterValue.into()); - } - } { - debug!("Finished parsing source"); - } - } else { - return Err(ErrorCode::IllegalParameterValue.into()); - } - let s: &[u8] = args - .next_data(false)? - .unwrap_or(Token::CharacterProgramData(b"default")) - .try_into()?; - */ let s_str: &str = str::from_utf8(s) - .map_err(|_| ErrorCode::CharacterDataError)?; - match s_str { - source if source.eq_ignore_ascii_case("OSC") => { - trace!("Changing clock source to OSC"); - context.device.set_clock_source(ClockSource::OSC); - }, - source if source.eq_ignore_ascii_case("MMCX") => { - trace!("Changing clock source to MMCX"); - context.device.set_clock_source(ClockSource::MMCX); - }, - source if source.eq_ignore_ascii_case("SMA") => { - trace!("Changing clocksource to SMA"); - context.device.set_clock_source(ClockSource::SMA); - }, - _ => { - warn!("Clock selection failed! Argument error!"); - return Err(ErrorCode::IllegalParameterValue.into()); - }, - }; + .map_err(|_| ErrorCode::CharacterDataError)?; let frequency: f64::Frequency = args.next_data(true)? .map_or(Ok(f64::Frequency::new::(0.0)), |t| { @@ -291,8 +231,34 @@ impl Command for ClockSourceCommand { _ => Err(ErrorCode::IllegalParameterValue.into()), }) })?; - debug!("Received frequency: {:?}", frequency); - Ok(()) + trace!("Received frequency: {:?}", frequency); + + let clock_source = match s_str { + source if source.eq_ignore_ascii_case("OSC") => { + // If clock source is OSC, it must be 100MHz (not configurable) + if frequency.get::() != 100.0 { + warn!("Clock selection failed! OSC must be 100 MHz"); + return Err(ErrorCode::IllegalParameterValue.into()); + } + ClockSource::OSC + }, + source if source.eq_ignore_ascii_case("MMCX") => { + // TODO: Implement frequency check for MMCX + ClockSource::MMCX + }, + source if source.eq_ignore_ascii_case("SMA") => { + // TODO: Implement frequency check for SMA + ClockSource::SMA + }, + _ => { + warn!("Clock selection failed! Argument error!"); + return Err(ErrorCode::IllegalParameterValue.into()); + }, + }; + + trace!("Changing clock source to {:?} at {:?}", clock_source, frequency); + context.device.set_clock_source(clock_source) + .map_err(|_| Error::new(ErrorCode::HardwareError)) } } @@ -300,26 +266,17 @@ impl Command for ClockDivisionCommand { nquery!(); fn event(&self, context: &mut Context, args: &mut Tokenizer) -> Result<()> { - let data :u8 = match args.next_data(false)? { - Some(token) => { - match f32::try_from(token) { - Ok(val) => { - if val == 1.0 || val == 2.0 || val == 4.0 { - val as u8 - } else { - return Err(ErrorCode::IllegalParameterValue.into()) - } - }, - Err(_e) => { - return Err(ErrorCode::IllegalParameterValue.into()) - }, - } - }, - _ => return Err(ErrorCode::IllegalParameterValue.into()), - }; - match context.device.set_clock_division(data) { - Ok(()) => Ok(()), - Err(_) => Err(Error::new(ErrorCode::HardwareError)), + let div :f32 = args.next_data(false)? + .map_or(Err(Error::new(ErrorCode::IllegalParameterValue)), + |token| token.try_into())?; + trace!("Received master clock division factor: {}", div); + match div { + 1.0 | 2.0 | 4.0 => { + debug!("Set master clock division as {}", div); + context.device.set_clock_division(div as u8) + .map_err(|_| Error::new(ErrorCode::HardwareError)) + } + _ => Err(Error::new(ErrorCode::IllegalParameterValue)), } } }