From ef473cdeca7c9f48d9c63944148baaff067da85d Mon Sep 17 00:00:00 2001 From: Wojciech Olech Date: Mon, 21 Aug 2023 15:02:35 +0200 Subject: [PATCH 1/5] Timer: Added Copy derive to WaveformModeConfig --- arch/cortex-m/samv71-hal/src/drivers/timer/waveform_config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/cortex-m/samv71-hal/src/drivers/timer/waveform_config.rs b/arch/cortex-m/samv71-hal/src/drivers/timer/waveform_config.rs index 1c133f7e..6c0dc5af 100644 --- a/arch/cortex-m/samv71-hal/src/drivers/timer/waveform_config.rs +++ b/arch/cortex-m/samv71-hal/src/drivers/timer/waveform_config.rs @@ -5,7 +5,7 @@ use pac::tc0::tc_channel::cmr_waveform_mode::{ }; /// Structure representing waveform mode configuration. -#[derive(Debug, Default, Clone, Eq, PartialEq)] +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)] pub struct WaveformModeConfig { /// RC Compare event effect on timer's counter state. pub rc_compare_effect: RcCompareEffect, From 55df5fb8af2c0c140e39abb9e95293b012eb5554 Mon Sep 17 00:00:00 2001 From: Wojciech Olech Date: Mon, 21 Aug 2023 16:56:18 +0200 Subject: [PATCH 2/5] HAL: Implemented `get_system_time` --- arch/cortex-m/samv71-hal/src/error.rs | 2 + arch/cortex-m/samv71-hal/src/hal.rs | 170 +++++++++++++++++- .../samv71-hal/src/system_peripherals.rs | 17 +- 3 files changed, 185 insertions(+), 4 deletions(-) diff --git a/arch/cortex-m/samv71-hal/src/error.rs b/arch/cortex-m/samv71-hal/src/error.rs index a21cc1bf..d0137e55 100644 --- a/arch/cortex-m/samv71-hal/src/error.rs +++ b/arch/cortex-m/samv71-hal/src/error.rs @@ -7,4 +7,6 @@ pub enum HalError { HalAlreadyCreated, /// Error indicating that HAL has already been configured. HalAlreadyConfigured, + /// Error indicating that the requested operation was called before HAL initialization. + HalNotInitializedYet, } diff --git a/arch/cortex-m/samv71-hal/src/hal.rs b/arch/cortex-m/samv71-hal/src/hal.rs index 2f5998e0..7e574dc5 100644 --- a/arch/cortex-m/samv71-hal/src/hal.rs +++ b/arch/cortex-m/samv71-hal/src/hal.rs @@ -4,13 +4,21 @@ use aerugo_cortex_m::Mutex; use aerugo_hal::system_hal::{SystemHal, SystemHardwareConfig}; use bare_metal::CriticalSection; +use cortex_m::asm; + +use crate::drivers::timer::channel_config::ChannelClock; +use crate::drivers::timer::timer_config::{ExternalClock, ExternalClockSource}; +use crate::drivers::timer::waveform_config::{ + ComparisonEffect, OutputSignalEffects, WaveformModeConfig, +}; +use crate::drivers::timer::{Ch0, Ch1, Ch2, Channel, Timer, Waveform}; use crate::drivers::watchdog::watchdog_config::WatchdogConfig; use crate::drivers::watchdog::Watchdog; use crate::error::HalError; use crate::system_peripherals::SystemPeripherals; use crate::user_peripherals::UserPeripherals; use internal_cell::InternalCell; -use pac; +use pac::{self, PMC, TC0}; /// This lock will prevent from creating HAL instance twice in the system. /// Since HAL manages the access to peripherals, creating and using multiple @@ -66,6 +74,11 @@ impl Hal { let system_peripherals = SystemPeripherals { watchdog: Watchdog::new(mcu_peripherals.WDT), + timer: Timer::new(mcu_peripherals.TC0), + timer_ch0: None, + timer_ch1: None, + timer_ch2: None, + pmc: Some(mcu_peripherals.PMC), }; let user_peripherals = UserPeripherals { @@ -73,7 +86,7 @@ impl Hal { timer_counter1: Some(mcu_peripherals.TC1), timer_counter2: Some(mcu_peripherals.TC2), timer_counter3: Some(mcu_peripherals.TC3), - pmc: Some(mcu_peripherals.PMC), + pmc: None, nvic: Some(core_peripherals.NVIC), }; @@ -110,11 +123,56 @@ impl SystemHal for Hal { Err(_) => return Err(HalError::HalAlreadyConfigured), }; + if let Some(pmc) = peripherals.pmc.take() { + let (ch0, ch1, ch2) = configure_timer_for_hal(&mut peripherals.timer, &pmc); + + peripherals.timer_ch0.replace(ch0); + peripherals.timer_ch1.replace(ch1); + peripherals.timer_ch2.replace(ch2); + + if let Some(user_peripherals) = self.user_peripherals.as_mut() { + user_peripherals.pmc.replace(pmc); + } else { + // That should never happen, as both system and user peripherals are created at + // the same time, but to prevent hard-to-detect issues in the future, this will + // throw an error anyway. + return Err(HalError::HalNotInitializedYet); + } + } else { + // If PMC is not there, it means that the system has already been initialized. + return Err(HalError::HalAlreadyConfigured); + } + + // Start system timer + peripherals.timer.trigger_all_channels(); + Ok(()) } fn get_system_time(&self) -> Self::Instant { - crate::time::TimerInstantU64::from_ticks(0) // TODO: replace this stub with correct implementation + // SAFETY: This is safe, because this is a single-core system, + // and no other references to system peripherals should exist. + let peripherals = unsafe { self.system_peripherals.as_ref() }; + + let ch0 = peripherals + .timer_ch0 + .as_ref() + .expect("get_system_time called before HAL initialization"); + let ch1 = peripherals + .timer_ch1 + .as_ref() + .expect("get_system_time called before HAL initialization"); + let ch2 = peripherals + .timer_ch2 + .as_ref() + .expect("get_system_time called before HAL initialization"); + + let time_ch0 = ch0.counter_value(); + let time_ch1 = ch1.counter_value(); + let time_ch2 = ch2.counter_value(); + + // Timer's clock is 1MHz, so returned value is in microseconds. + crate::time::TimerInstantU64::from_ticks(as_48bit_unsigned(time_ch0, time_ch1, time_ch2)) } fn feed_watchdog(&mut self) { @@ -140,3 +198,109 @@ impl SystemHal for Hal { cortex_m::interrupt::free(f) } } + +/// Type representing all TC0 channels in Waveform mode. +type Tc0Channels = ( + Channel, + Channel, + Channel, +); + +/// Configures a timer for HAL usage. +/// +/// This function configures Timer (using hardware TC0 instance) in Waveform mode with proper +/// input clocks (configured via PMC), and chains it's channels to achieve high-resolution +/// time source for the system. +/// +/// Timer's source clock first goes into channel 0, which generates RC compare events that +/// toggle it's TIOA0 output, effectively dividing the input frequency by the value of RC register. +/// TIOA0 is connected via XC1 to channel 1, which does the same thing for TIOA1 output, which is +/// connected via XC2 to channel 2. +/// +/// # Parameters +/// * `timer` - HAL Timer instance +/// * `pmc` - PAC PMC instance +fn configure_timer_for_hal(timer: &mut Timer, pmc: &PMC) -> Tc0Channels { + configure_pmc_for_timer(pmc); + + // If any of the configurations is not available, user cannot do anything about it and it + // certainly should not pass any tests, so just hard fault it. + timer + .configure_external_clock_source(ExternalClock::XC1, ExternalClockSource::TIOA0) + .expect("Cannot connect TIOA0 to XC1"); + timer + .configure_external_clock_source(ExternalClock::XC2, ExternalClockSource::TIOA1) + .expect("Cannot connect TIOA1 to XC2"); + + // If any of the channels is not available, it's a hard fault as it's an internal bug in Aerugo + let ch0 = timer.channel_0.take().expect("TC0 CH0 already taken"); + let ch1 = timer.channel_1.take().expect("TC0 CH1 already taken"); + let ch2 = timer.channel_2.take().expect("TC0 CH2 already taken"); + + let waveform_config = WaveformModeConfig { + tioa_effects: OutputSignalEffects { + software_trigger: ComparisonEffect::Clear, + rc_comparison: ComparisonEffect::Toggle, + ..Default::default() + }, + ..Default::default() + }; + + let ch0 = ch0.into_waveform_channel(waveform_config); + let ch1 = ch1.into_waveform_channel(waveform_config); + let ch2 = ch2.into_waveform_channel(waveform_config); + + // Set RC values for all channels to max, so we can achieve full 48-bit resolution + ch0.set_rc(u16::MAX); + ch1.set_rc(u16::MAX); + ch2.set_rc(u16::MAX); + + ch0.set_clock_source(ChannelClock::PmcPeripheralClock); + ch1.set_clock_source(ChannelClock::XC1); + ch2.set_clock_source(ChannelClock::XC2); + + ch0.enable(); + ch1.enable(); + ch2.enable(); + + (ch0, ch1, ch2) +} + +/// Configures PMC for TC0 operation with 3 chained channels +/// +/// Enables TC0 CH0, CH1 and CH2 peripheral clocks, and configures PCK6 +/// to generate proper clock for the timers. +/// +/// PCK6 uses MAINCK clock source (which is 12MHz by default), and divides it by 12 to get +/// 1MHz input clock, used by the timer to achieve 1ns resolution. +/// +fn configure_pmc_for_timer(pmc: &PMC) { + // Configure PCK6 for 1MHz TC0 output + // Source: MAINCK (12MHz by default) + // Divider: /6 (TODO: is there a hidden /2 prescaler somewhere?) + pmc.pck[6].write(|w| w.css().main_clk().pres().variant(5)); + + // Enable TC0 CH0, CH1 and CH2 peripheral clocks + pmc.pcer0 + .write(|w| w.pid23().set_bit().pid24().set_bit().pid25().set_bit()); + + // Enable PCK6 + pmc.scer.write(|w| w.pck6().set_bit()); + + // Wait until PCK6 is ready + while pmc.sr.read().pckrdy6().bit_is_clear() { + asm::nop(); + } +} + +/// Converts three 16-bit values into single 48-bit value. +/// +/// Returns it as u64, shifted to left. +/// +/// # Parameters +/// * `lsb` - Least significant bytes +/// * `mid` - Middle bytes +/// * `msb` - Most significant bytes +fn as_48bit_unsigned(lsb: u16, mid: u16, msb: u16) -> u64 { + ((msb as u64) << 32) | ((mid as u64) << 16) | (lsb as u64) +} diff --git a/arch/cortex-m/samv71-hal/src/system_peripherals.rs b/arch/cortex-m/samv71-hal/src/system_peripherals.rs index 62551ad0..a19b4f66 100644 --- a/arch/cortex-m/samv71-hal/src/system_peripherals.rs +++ b/arch/cortex-m/samv71-hal/src/system_peripherals.rs @@ -1,10 +1,25 @@ //! Module representing peripherals internally used by Aerugo. -use crate::drivers::watchdog::Watchdog; +use pac::{PMC, TC0}; + +use crate::drivers::{ + timer::{Ch0, Ch1, Ch2, Channel, Timer, Waveform}, + watchdog::Watchdog, +}; /// System peripherals structure. These peripherals are represented as HAL drivers. /// They are initialized on system init, and used directly by HAL to provide core functionality. pub struct SystemPeripherals { /// Watchdog instance. pub watchdog: Watchdog, + /// Timer instance. + pub timer: Timer, + /// Timer's channel 0 instance. + pub timer_ch0: Option>, + /// Timer's channel 1 instance. + pub timer_ch1: Option>, + /// Timer's channel 2 instance. + pub timer_ch2: Option>, + /// PMC instance. This will be stored only temporarily here, between HAL init and system config + pub pmc: Option, } From f138d226fa830196e3ef23f04b7bff6d35d65121 Mon Sep 17 00:00:00 2001 From: Wojciech Olech Date: Mon, 21 Aug 2023 16:56:58 +0200 Subject: [PATCH 3/5] Calldwell: Separated Calldwell's RTT facilities from protocol-agnostic RTT client --- calldwell/rtt_client.py | 80 +++++++++++++------- calldwell/rust_helpers.py | 14 ++-- tests/requirements/test/test_hal_timer.py | 6 +- tests/requirements/test/test_hal_watchdog.py | 2 +- tests/requirements/test/test_utils.py | 4 +- 5 files changed, 64 insertions(+), 42 deletions(-) diff --git a/calldwell/rtt_client.py b/calldwell/rtt_client.py index 091df225..085a5a5c 100644 --- a/calldwell/rtt_client.py +++ b/calldwell/rtt_client.py @@ -7,13 +7,8 @@ class RTTClient: - """Class acting as RTT front-end. Provides bidirectional communication with debugged program.""" - - class StreamMarker(IntEnum): - """Enumeration listing Calldwell stream markers""" - - Start = 0xDD - End = 0xEE + """Class acting as RTT front-end. Provides buffered, bidirectional communication with + debugged program. Can also be used as a convenient base for custom protocols.""" def __init__(self, host: str, port: int, default_chunk_size: int = 1024) -> None: """Create instance of RTT client. Connects to RTT server via TCP socket. @@ -33,7 +28,44 @@ def close(self) -> None: self._socket.shutdown(socket.SHUT_RDWR) self._socket.close() - def receive_bytes(self) -> bytes: + def receive(self) -> bytes: + self._receive() + data = self._data_buffer.copy() + self._data_buffer.clear() + return data + + def transmit(self, data: bytes) -> None: + self._transmit(data) + + def receive_string(self) -> str: + return self.receive().decode("utf-8") + + def transmit_string(self, data: str) -> None: + self.transmit(data.encode("utf-8")) + + def _receive(self, chunk_size: Optional[int] = None) -> None: + """Receives raw data from RTT target to internal buffer""" + if chunk_size is None: + chunk_size = self._default_chunk_size + + received_bytes = self._socket.recv(chunk_size) + self._data_buffer.extend(received_bytes) + + def _transmit(self, data: bytes) -> None: + """Transmits raw data to RTT target.""" + self._socket.send(data) + + +class CalldwellRTTClient(RTTClient): + """Class providing bidirectional communication with program using Calldwell streams""" + + class StreamMarker(IntEnum): + """Enumeration listing Calldwell stream markers""" + + Start = 0xDD + End = 0xEE + + def receive_bytes_stream(self) -> bytes: """Receives data via Calldwell stream from RTT target""" stream_data = self._extract_stream_data_from_recv_buffer() while stream_data is None: @@ -42,27 +74,29 @@ def receive_bytes(self) -> bytes: return stream_data - def transmit_bytes(self, data: bytes) -> None: + def transmit_bytes_stream(self, data: bytes) -> None: """Transmits data via Calldwell stream to RTT target""" - self._transmit_stream_marker(RTTClient.StreamMarker.Start) + self._transmit_stream_marker(CalldwellRTTClient.StreamMarker.Start) self._transmit(data) - self._transmit_stream_marker(RTTClient.StreamMarker.End) + self._transmit_stream_marker(CalldwellRTTClient.StreamMarker.End) - def receive_string(self) -> str: + def receive_string_stream(self) -> str: """Receives an UTF-8 string via Calldwell stream from RTT target""" - return self.receive_bytes().decode("utf-8") + return self.receive_bytes_stream().decode("utf-8") - def transmit_string(self, message: str) -> None: + def transmit_string_stream(self, message: str) -> None: """Transmits an UTF-8 string via Calldwell stream to RTT target""" - self.transmit_bytes(message.encode("utf-8")) + self.transmit_bytes_stream(message.encode("utf-8")) def _extract_stream_data_from_recv_buffer(self) -> Optional[bytes]: """Looks for valid Calldwell stream in reception buffer, and returns it's data if found""" - start_marker_index = self._data_buffer.find(RTTClient.StreamMarker.Start) + start_marker_index = self._data_buffer.find(CalldwellRTTClient.StreamMarker.Start) if start_marker_index == -1: return None - end_marker_index = self._data_buffer.find(RTTClient.StreamMarker.End, start_marker_index) + end_marker_index = self._data_buffer.find( + CalldwellRTTClient.StreamMarker.End, start_marker_index + ) if end_marker_index == -1: return None @@ -79,15 +113,3 @@ def _extract_stream_data_from_recv_buffer(self) -> Optional[bytes]: def _transmit_stream_marker(self, marker: StreamMarker) -> None: # byteorder doesn't matter, but mypy asks for it self._transmit(marker.to_bytes(length=1, signed=False, byteorder="big")) - - def _receive(self, chunk_size: Optional[int] = None) -> None: - """Receives raw data from RTT target to internal buffer""" - if chunk_size is None: - chunk_size = self._default_chunk_size - - received_bytes = self._socket.recv(chunk_size) - self._data_buffer.extend(received_bytes) - - def _transmit(self, data: bytes) -> None: - """Transmits raw data to RTT target.""" - self._socket.send(data) diff --git a/calldwell/rust_helpers.py b/calldwell/rust_helpers.py index 3e1b27fb..817ae399 100644 --- a/calldwell/rust_helpers.py +++ b/calldwell/rust_helpers.py @@ -3,7 +3,7 @@ from typing import Any, Callable, Optional, Tuple from .gdb_client import GDBClient -from .rtt_client import RTTClient +from .rtt_client import CalldwellRTTClient RTT_SECTION_SYMBOL_NAME = "_SEGGER_RTT" """Section name of RTT symbol. Hard-coded in `rtt_target` library.""" @@ -35,7 +35,7 @@ def init_remote_calldwell_rs_session( log_execution: bool = False, pre_handshake_hook: Optional[Callable[[GDBClient, Optional[Any]], None]] = None, pre_handshake_hook_argument: Optional[Any] = None, -) -> Optional[Tuple[GDBClient, RTTClient]]: +) -> Optional[Tuple[GDBClient, CalldwellRTTClient]]: """Initializes Calldwell-rs test session by connecting to GDB server (like OpenOCD), starting RTT server, flashing the executable, waiting until `calldwell::initialize` executes, and performing handshake (and optional pre-handshake hook, if provided). @@ -86,7 +86,7 @@ def init_remote_calldwell_rs_session( logging.error(f"Could not start RTT server @ TCP port {rtt_server_port}") return None - rtt = RTTClient(gdb_server_hostname, rtt_server_port) + rtt = CalldwellRTTClient(gdb_server_hostname, rtt_server_port) if not gdb.load_executable(path_to_test_executable): logging.error(f"Could not load executable {path_to_test_executable} into MCU memory") @@ -136,11 +136,11 @@ def init_remote_calldwell_rs_session( return gdb, rtt -def _perform_handshake(rtt: RTTClient) -> bool: +def _perform_handshake(rtt: CalldwellRTTClient) -> bool: """Performs Calldwell handshake after it's RTT facilities are started. This acts like a mini self-test of RTT communication, to guarantee that it works correctly. """ - init_message = rtt.receive_string() + init_message = rtt.receive_string_stream() if init_message != EXPECTED_MCU_INIT_MESSAGE: logging.error( @@ -149,8 +149,8 @@ def _perform_handshake(rtt: RTTClient) -> bool: ) return False - rtt.transmit_string(HOST_HANDSHAKE_MESSAGE) - response = rtt.receive_string() + rtt.transmit_string_stream(HOST_HANDSHAKE_MESSAGE) + response = rtt.receive_string_stream() if response != EXPECTED_MCU_HANDSHAKE_MESSAGE: logging.error( diff --git a/tests/requirements/test/test_hal_timer.py b/tests/requirements/test/test_hal_timer.py index 84c42a79..66384c75 100644 --- a/tests/requirements/test/test_hal_timer.py +++ b/tests/requirements/test/test_hal_timer.py @@ -21,21 +21,21 @@ def main(): # First 10 messages should contain fast-changing timer IRQ count fast_irq_counts: List[int] = list() for _ in range(10): - fast_irq_counts.append(int(rtt.receive_bytes().decode())) + fast_irq_counts.append(int(rtt.receive_bytes_stream().decode())) avg_diffs_fast = average_difference(fast_irq_counts) # After 10 messages, tasklet should disable the timer, so incoming IRQ counts # should not change stopped_irq_counts: List[int] = list() for _ in range(10): - stopped_irq_counts.append(int(rtt.receive_bytes().decode())) + stopped_irq_counts.append(int(rtt.receive_bytes_stream().decode())) avg_diffs_stopped = average_difference(stopped_irq_counts) # After another 10 messages, tasklet should switch timer's source to slower one # and enable it, returning IRQ count that's changing slower slow_irq_counts: List[int] = list() for _ in range(10): - slow_irq_counts.append(int(rtt.receive_bytes().decode())) + slow_irq_counts.append(int(rtt.receive_bytes_stream().decode())) avg_diffs_slow = average_difference(slow_irq_counts) diff --git a/tests/requirements/test/test_hal_watchdog.py b/tests/requirements/test/test_hal_watchdog.py index 8c114e7b..d59a367f 100644 --- a/tests/requirements/test/test_hal_watchdog.py +++ b/tests/requirements/test/test_hal_watchdog.py @@ -16,7 +16,7 @@ def main(): ] for message in expected_messages: - received_message = rtt.receive_bytes().decode() + received_message = rtt.receive_bytes_stream().decode() print(received_message) if received_message != message: print( diff --git a/tests/requirements/test/test_utils.py b/tests/requirements/test/test_utils.py index cc6933ff..4b6f6341 100644 --- a/tests/requirements/test/test_utils.py +++ b/tests/requirements/test/test_utils.py @@ -3,7 +3,7 @@ from typing import Tuple from calldwell.gdb_client import GDBClient from calldwell.ssh_client import SSHClient -from calldwell.rtt_client import RTTClient +from calldwell.rtt_client import CalldwellRTTClient from calldwell.rust_helpers import init_remote_calldwell_rs_session BOARD_LOGIN = str(os.environ.get("AERUGO_BOARD_LOGIN")) @@ -14,7 +14,7 @@ GDB_EXECUTABLE = "arm-none-eabi-gdb" -def init_test(test_binary_path: str) -> Tuple[GDBClient, RTTClient, SSHClient]: +def init_test(test_binary_path: str) -> Tuple[GDBClient, CalldwellRTTClient, SSHClient]: """Creates SSH connection to target board, initializes Calldwell""" logging.info("Starting the test, initializing the environment...") ssh = SSHClient(BOARD_HOSTNAME, BOARD_LOGIN, BOARD_PASSWORD) From 32cfb45f290f8a57f696f63a012302e45b6449b6 Mon Sep 17 00:00:00 2001 From: Wojciech Olech Date: Mon, 21 Aug 2023 16:57:15 +0200 Subject: [PATCH 4/5] Scripts: Added script for listening to app's output via RTT --- scripts/rtt.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 scripts/rtt.py diff --git a/scripts/rtt.py b/scripts/rtt.py new file mode 100644 index 00000000..e850d86e --- /dev/null +++ b/scripts/rtt.py @@ -0,0 +1,34 @@ +from typing import Tuple +from calldwell.rtt_client import RTTClient +import sys + + +def get_args() -> Tuple[str, int]: + if len(sys.argv) != 2: + print(f"Usage: {sys.argv[0]} hostname:port") + exit(1) + + full_hostname = sys.argv[1].split(":") + + if len(full_hostname) != 2: + print("Invalid hostname") + exit(2) + + host, port = full_hostname + return host, int(port) + + +def main(): + host, port = get_args() + + rtt = RTTClient(host, port) + + while True: + try: + print(rtt.receive_string(), end="") + except KeyboardInterrupt: + exit(0) + + +if __name__ == "__main__": + main() From f1eaabdd44555d909ee3cc36f7db9cfa635cc825 Mon Sep 17 00:00:00 2001 From: Wojciech Olech Date: Mon, 21 Aug 2023 16:57:29 +0200 Subject: [PATCH 5/5] Examples: Added example that checks system time on SAMV71 --- .../samv71-system-time/.cargo/config.toml | 11 +++ examples/samv71-system-time/Cargo.toml | 20 ++++++ examples/samv71-system-time/build.rs | 14 ++++ examples/samv71-system-time/memory.x | 6 ++ examples/samv71-system-time/src/main.rs | 70 +++++++++++++++++++ 5 files changed, 121 insertions(+) create mode 100644 examples/samv71-system-time/.cargo/config.toml create mode 100644 examples/samv71-system-time/Cargo.toml create mode 100644 examples/samv71-system-time/build.rs create mode 100644 examples/samv71-system-time/memory.x create mode 100644 examples/samv71-system-time/src/main.rs diff --git a/examples/samv71-system-time/.cargo/config.toml b/examples/samv71-system-time/.cargo/config.toml new file mode 100644 index 00000000..be9455f4 --- /dev/null +++ b/examples/samv71-system-time/.cargo/config.toml @@ -0,0 +1,11 @@ +[build] +target = "thumbv7em-none-eabihf" + +[env] +AERUGO_TASKLET_COUNT = { value = "2" } + +[target.thumbv7em-none-eabihf] +rustflags = [ + "-C", "link-arg=--nmagic", # Disable page alignment of sections (to prevent issues with binary size) + "-C", "link-arg=-Tlink.x", # Use cortex-m-rt's linker script +] diff --git a/examples/samv71-system-time/Cargo.toml b/examples/samv71-system-time/Cargo.toml new file mode 100644 index 00000000..4125cf7e --- /dev/null +++ b/examples/samv71-system-time/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "samv71-system-time" +authors = ["Wojciech Olech "] +edition = "2021" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +aerugo = { version = "0.1.0", path = "../..", features = [ + "use-aerugo-cortex-m", +] } +cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.3" +panic-rtt-target = { version = "0.1.2", features = ["cortex-m"] } +rtt-target = "0.4.0" + +[profile.release] +debug = true +lto = true diff --git a/examples/samv71-system-time/build.rs b/examples/samv71-system-time/build.rs new file mode 100644 index 00000000..d26b3397 --- /dev/null +++ b/examples/samv71-system-time/build.rs @@ -0,0 +1,14 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put the linker script somewhere the linker can find it + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); +} diff --git a/examples/samv71-system-time/memory.x b/examples/samv71-system-time/memory.x new file mode 100644 index 00000000..62221839 --- /dev/null +++ b/examples/samv71-system-time/memory.x @@ -0,0 +1,6 @@ +/* Linker script for SAMV71Q21 */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00400000, LENGTH = 0x00200000 + RAM (rwx) : ORIGIN = 0x20400000, LENGTH = 0x00060000 +} diff --git a/examples/samv71-system-time/src/main.rs b/examples/samv71-system-time/src/main.rs new file mode 100644 index 00000000..887394ec --- /dev/null +++ b/examples/samv71-system-time/src/main.rs @@ -0,0 +1,70 @@ +#![no_std] +#![no_main] + +extern crate cortex_m; +extern crate cortex_m_rt as runtime; +extern crate panic_rtt_target; +extern crate rtt_target as rtt; + +use aerugo::{ + time::MillisDurationU32, InitApi, RuntimeApi, SystemHardwareConfig, TaskletConfig, + TaskletStorage, AERUGO, +}; +use rtt::rprintln; +use runtime::entry; + +#[derive(Default)] +struct DummyTaskContext { + acc: u16, +} + +fn dummy_task(_: (), context: &mut DummyTaskContext, api: &'static dyn RuntimeApi) { + context.acc = context.acc.wrapping_add(1); + if context.acc % 300 == 0 { + let time = api.get_system_time().duration_since_epoch().to_secs(); + rprintln!("Current time is {}s", time); + } +} + +static DUMMY_TASK_STORAGE: TaskletStorage<(), DummyTaskContext, 0> = TaskletStorage::new(); + +#[entry] +fn main() -> ! { + rtt::rtt_init_print!(); + + rprintln!("Hello, world! Initializing Aerugo..."); + + AERUGO.initialize(SystemHardwareConfig { + watchdog_timeout: MillisDurationU32::secs(5), + }); + + rprintln!("Creating tasks..."); + let dummy_task_config = TaskletConfig { + name: "DummyTask", + ..Default::default() + }; + let dummy_task_context = DummyTaskContext::default(); + + AERUGO + .create_tasklet_with_context( + dummy_task_config, + dummy_task, + dummy_task_context, + &DUMMY_TASK_STORAGE, + ) + .expect("Unable to create dummy task!"); + + let dummy_task_handle = DUMMY_TASK_STORAGE + .create_handle() + .expect("Unable to create handle to dummy task!"); + + rprintln!("Subscribing tasks..."); + + AERUGO + .subscribe_tasklet_to_cyclic(&dummy_task_handle, None) + .expect("Unable to subscribe dummy task to cyclic execution!"); + + rprintln!("Starting the system!"); + + AERUGO.start(); +}