diff options
-rw-r--r-- | .travis.yml | 17 | ||||
-rw-r--r-- | CHANGELOG.md | 37 | ||||
-rw-r--r-- | Cargo.toml | 9 | ||||
-rw-r--r-- | ci/install.sh | 23 | ||||
-rw-r--r-- | ci/script.sh | 6 | ||||
-rw-r--r-- | src/asm.rs | 56 | ||||
-rw-r--r-- | src/exception.rs | 1 | ||||
-rw-r--r-- | src/itm.rs | 129 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/peripheral/mod.rs | 60 | ||||
-rw-r--r-- | src/register/apsr.rs | 1 | ||||
-rw-r--r-- | src/register/control.rs | 7 | ||||
-rw-r--r-- | src/register/faultmask.rs | 2 | ||||
-rw-r--r-- | src/register/primask.rs | 2 |
14 files changed, 256 insertions, 95 deletions
diff --git a/.travis.yml b/.travis.yml index 721b6c7..dc66bdc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,25 +1,30 @@ -dist: trusty language: rust -rust: nightly-2017-03-04 -services: docker -sudo: required matrix: include: + - env: TARGET=x86_64-unknown-linux-gnu + rust: nightly - env: TARGET=thumbv6m-none-eabi + rust: nightly - env: TARGET=thumbv7m-none-eabi + rust: nightly - env: TARGET=thumbv7em-none-eabi + rust: nightly - env: TARGET=thumbv7em-none-eabihf -env: TARGET=x86_64-unknown-linux-gnu + rust: nightly + +before_install: set -e install: - sh ci/install.sh - - source ~/.cargo/env || true script: - sh ci/script.sh +after_script: set +e + cache: cargo + before_cache: - chmod -R a+r $HOME/.cargo; diff --git a/CHANGELOG.md b/CHANGELOG.md index f3777af..75616b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.2.10] - 2017-06-05 + +### Added + +- Functions for the instructions DMB, ISB and DSB + +### Changed + +- All the functions in the `asm` module are now `inline(always)` + +## [v0.2.9] - 2017-05-30 + +### Fixed + +- A bug in `itm::write_all` where it would ignore the length of the buffer and + serialize contents that come after the buffer. + +## [v0.2.8] - 2017-05-30 - YANKED + +### Added + +- An `itm::write_aligned` function to write 4 byte aligned buffers to an ITM + port. This function is faster than `itm::write_all` for small buffers but + requires the buffer to be aligned. + +## [v0.2.7] - 2017-05-23 + +### Added + +- `Dwt.enable_cycle_counter` + ## [v0.2.6] - 2017-05-08 ### Fixed @@ -265,7 +296,11 @@ fn main() { - Functions to get the vector table - Wrappers over miscellaneous instructions like `bkpt` -[Unreleased]: https://github.com/japaric/cortex-m/compare/v0.2.6...HEAD +[Unreleased]: https://github.com/japaric/cortex-m/compare/v0.2.10...HEAD +[v0.2.10]: https://github.com/japaric/cortex-m/compare/v0.2.9...v0.2.10 +[v0.2.9]: https://github.com/japaric/cortex-m/compare/v0.2.8...v0.2.9 +[v0.2.8]: https://github.com/japaric/cortex-m/compare/v0.2.7...v0.2.8 +[v0.2.7]: https://github.com/japaric/cortex-m/compare/v0.2.6...v0.2.7 [v0.2.6]: https://github.com/japaric/cortex-m/compare/v0.2.5...v0.2.6 [v0.2.5]: https://github.com/japaric/cortex-m/compare/v0.2.4...v0.2.5 [v0.2.4]: https://github.com/japaric/cortex-m/compare/v0.2.3...v0.2.4 @@ -7,10 +7,9 @@ keywords = ["arm", "cortex-m", "register", "peripheral"] license = "MIT OR Apache-2.0" name = "cortex-m" repository = "https://github.com/japaric/cortex-m" -version = "0.2.6" +version = "0.2.10" [dependencies] -volatile-register = "0.2.0" - -[dependencies.cortex-m-semihosting] -version = "0.1.3"
\ No newline at end of file +aligned = "0.1.1" +cortex-m-semihosting = "0.1.3" +volatile-register = "0.2.0"
\ No newline at end of file diff --git a/ci/install.sh b/ci/install.sh index 550a548..f978160 100644 --- a/ci/install.sh +++ b/ci/install.sh @@ -1,21 +1,14 @@ set -ex main() { - curl https://sh.rustup.rs -sSf | \ - sh -s -- -y --default-toolchain $TRAVIS_RUST_VERSION - - local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \ - | cut -d/ -f3 \ - | grep -E '^v[0-9.]+$' \ - | sort --version-sort \ - | tail -n1) - curl -LSfs http://japaric.github.io/trust/install.sh | \ - sh -s -- \ - --force \ - --git japaric/cross \ - --tag $tag \ - --target x86_64-unknown-linux-musl \ - --to ~/.cargo/bin + case $TARGET in + thumbv*-none-eabi*) + cargo install --list | grep xargo || \ + cargo install xargo + rustup component list | grep 'rust-src.*installed' || \ + rustup component add rust-src + ;; + esac } # NOTE(TRAVIS_BRANCH) Travis is configured to only build *pushes* (not PRs) diff --git a/ci/script.sh b/ci/script.sh index 82b3a1d..080e756 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -1,14 +1,12 @@ set -ex main() { - cargo generate-lockfile - case $TARGET in thumbv*-none-eabi*) - cross build --target $TARGET + xargo check --target $TARGET ;; *) - cross test --target $TARGET + cargo test --target $TARGET ;; esac } @@ -18,6 +18,7 @@ pub fn bkpt() { } /// A no-operation. Useful to prevent delay loops from being optimized away. +#[inline(always)] pub fn nop() { unsafe { asm!("nop" @@ -28,6 +29,7 @@ pub fn nop() { } } /// Wait For Event +#[inline(always)] pub fn wfe() { match () { #[cfg(target_arch = "arm")] @@ -44,6 +46,7 @@ pub fn wfe() { } /// Wait For Interrupt +#[inline(always)] pub fn wfi() { match () { #[cfg(target_arch = "arm")] @@ -58,3 +61,56 @@ pub fn wfi() { () => {} } } + +/// Instruction Synchronization Barrier +/// +/// Flushes the pipeline in the processor, so that all instructions following the `ISB` are fetched +/// from cache or memory, after the instruction has been completed. +#[inline(always)] +pub fn isb() { + match () { + #[cfg(target_arch = "arm")] + () => unsafe { + asm!("isb 0xF" : : : "memory" : "volatile"); + }, + #[cfg(not(target_arch = "arm"))] + () => {} + } +} + +/// Data Synchronization Barrier +/// +/// Acts as a special kind of memory barrier. No instruction in program order after this +/// instruction can execute until this instruction completes. This instruction completes only when +/// both: +/// +/// * any explicit memory access made before this instruction is complete +/// * all cache and branch predictor maintenance operations before this instruction complete +#[inline(always)] +pub fn dsb() { + match () { + #[cfg(target_arch = "arm")] + () => unsafe { + asm!("dsb 0xF" : : : "memory" : "volatile"); + }, + #[cfg(not(target_arch = "arm"))] + () => {} + } +} + +/// Data Memory Barrier +/// +/// Ensures that all explicit memory accesses that appear in program order before the `DMB` +/// instruction are observed before any explicit memory accesses that appear in program order +/// after the `DMB` instruction. +#[inline(always)] +pub fn dmb() { + match () { + #[cfg(target_arch = "arm")] + () => unsafe { + asm!("dmb 0xF" : : : "memory" : "volatile"); + }, + #[cfg(not(target_arch = "arm"))] + () => {} + } +} diff --git a/src/exception.rs b/src/exception.rs index f5b52f6..61b7415 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -190,6 +190,7 @@ where } /// Registers stacked during an exception +#[derive(Clone, Copy, Debug)] #[repr(C)] pub struct StackedRegisters { /// (General purpose) Register 0 @@ -1,37 +1,10 @@ //! Instrumentation Trace Macrocell -use core::{fmt, ptr, slice}; -use peripheral::Stim; - -fn round_up_to_multiple_of(x: usize, k: usize) -> usize { - let rem = x % k; - - if rem == 0 { x } else { x + k - rem } -} +use core::{fmt, mem, ptr, slice}; -fn round_down_to_multiple_of(x: usize, k: usize) -> usize { - x - (x % k) -} - -unsafe fn split(buffer: &[u8]) -> (&[u8], &[u32], &[u8]) { - let start = buffer.as_ptr(); - let end = start.offset(buffer.len() as isize); - let sbody = round_up_to_multiple_of(start as usize, 4); - let ebody = round_down_to_multiple_of(end as usize, 4); +use aligned::Aligned; - let head = slice::from_raw_parts(start, sbody - start as usize); - let body = slice::from_raw_parts(sbody as *const _, (ebody - sbody) >> 2); - let tail = slice::from_raw_parts(ebody as *const _, end as usize - ebody); - - (head, body, tail) -} - -fn write_bytes(stim: &Stim, bytes: &[u8]) { - for byte in bytes { - while !stim.is_fifo_ready() {} - stim.write_u8(*byte); - } -} +use peripheral::Stim; // NOTE assumes that `bytes` is 32-bit aligned unsafe fn write_words(stim: &Stim, bytes: &[u32]) { @@ -54,13 +27,95 @@ impl<'p> fmt::Write for Port<'p> { /// Writes a `buffer` to the ITM `port` pub fn write_all(port: &Stim, buffer: &[u8]) { - if buffer.len() < 7 { - write_bytes(port, buffer); - } else { - let (head, body, tail) = unsafe { split(buffer) }; - write_bytes(port, head); - unsafe { write_words(port, body) } - write_bytes(port, tail); + unsafe { + let mut len = buffer.len(); + let mut ptr = buffer.as_ptr(); + + if len == 0 { + return; + } + + // 0x01 OR 0x03 + if ptr as usize % 2 == 1 { + while !port.is_fifo_ready() {} + port.write_u8(*ptr); + + // 0x02 OR 0x04 + ptr = ptr.offset(1); + len -= 1; + } + + // 0x02 + if ptr as usize % 4 == 2 { + if len > 1 { + // at least 2 bytes + while !port.is_fifo_ready() {} + port.write_u16(ptr::read(ptr as *const u16)); + + // 0x04 + ptr = ptr.offset(2); + len -= 2; + } else { + if len == 1 { + // last byte + while !port.is_fifo_ready() {} + port.write_u8(*ptr); + } + + return; + } + } + + write_aligned(port, mem::transmute(slice::from_raw_parts(ptr, len))); + } +} + +/// Writes a 4-byte aligned `buffer` to the ITM `port` +/// +/// # Examples +/// +/// ``` ignore +/// let mut buffer: Aligned<u32, _> = Aligned([0; 14]); +/// +/// buffer.copy_from_slice(b"Hello, world!\n"); +/// +/// itm::write_aligned(&itm.stim[0], &buffer); +/// +/// // Or equivalently +/// itm::write_aligned(&itm.stim[0], &Aligned(*b"Hello, world!\n")); +/// ``` +pub fn write_aligned(port: &Stim, buffer: &Aligned<u32, [u8]>) { + unsafe { + let len = buffer.len(); + + if len == 0 { + return; + } + + let split = len & !0b11; + write_words( + port, + slice::from_raw_parts(buffer.as_ptr() as *const u32, split >> 2), + ); + + // 3 bytes or less left + let mut left = len & 0b11; + let mut ptr = buffer.as_ptr().offset(split as isize); + + // at least 2 bytes left + if left > 1 { + while !port.is_fifo_ready() {} + port.write_u16(ptr::read(ptr as *const u16)); + + ptr = ptr.offset(2); + left -= 2; + } + + // final byte + if left == 1 { + while !port.is_fifo_ready() {} + port.write_u8(*ptr); + } } } @@ -16,6 +16,7 @@ #![feature(naked_functions)] #![no_std] +extern crate aligned; pub extern crate cortex_m_semihosting as semihosting; extern crate volatile_register; diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index 4756027..0ca1a80 100644 --- a/src/peripheral/mod.rs +++ b/src/peripheral/mod.rs @@ -51,6 +51,7 @@ pub const TPIU: Peripheral<Tpiu> = unsafe { Peripheral::new(0xE004_0000) }; // TODO stand-alone registers: ICTR, ACTLR and STIR /// A peripheral +#[derive(Debug)] pub struct Peripheral<T> where T: 'static, @@ -149,6 +150,13 @@ pub struct Dwt { pub lsr: RO<u32>, } +impl Dwt { + /// Enables the cycle counter + pub fn enable_cycle_counter(&self) { + unsafe { self.ctrl.modify(|r| r | 1) } + } +} + /// Comparator #[repr(C)] pub struct Comparator { @@ -425,6 +433,7 @@ pub struct Scb { } /// FPU access mode +#[derive(Clone, Copy, Debug)] pub enum FpuAccessMode { /// FPU is not accessible Disabled, @@ -434,17 +443,19 @@ pub enum FpuAccessMode { Privileged, } -const SCB_CPACR_FPU_MASK: u32 = 0x00f00000; +const SCB_CPACR_FPU_MASK: u32 = 0x00f00000; const SCB_CPACR_FPU_ENABLE: u32 = 0x00500000; -const SCB_CPACR_FPU_USER: u32 = 0x00a00000; +const SCB_CPACR_FPU_USER: u32 = 0x00a00000; impl Scb { /// Gets FPU access mode pub fn fpu_access_mode(&self) -> FpuAccessMode { let cpacr = self.cpacr.read(); - if cpacr & (SCB_CPACR_FPU_ENABLE | SCB_CPACR_FPU_USER) != 0 { + if cpacr & SCB_CPACR_FPU_MASK == + SCB_CPACR_FPU_ENABLE | SCB_CPACR_FPU_USER + { FpuAccessMode::Enabled - } else if cpacr & SCB_CPACR_FPU_ENABLE != 0 { + } else if cpacr & SCB_CPACR_FPU_MASK == SCB_CPACR_FPU_ENABLE { FpuAccessMode::Privileged } else { FpuAccessMode::Disabled @@ -456,10 +467,10 @@ impl Scb { let mut cpacr = self.cpacr.read() & !SCB_CPACR_FPU_MASK; match mode { FpuAccessMode::Disabled => (), - FpuAccessMode::Privileged => - cpacr |= SCB_CPACR_FPU_ENABLE, - FpuAccessMode::Enabled => - cpacr |= SCB_CPACR_FPU_ENABLE | SCB_CPACR_FPU_USER, + FpuAccessMode::Privileged => cpacr |= SCB_CPACR_FPU_ENABLE, + FpuAccessMode::Enabled => { + cpacr |= SCB_CPACR_FPU_ENABLE | SCB_CPACR_FPU_USER + } } unsafe { self.cpacr.write(cpacr) } } @@ -489,22 +500,23 @@ pub struct Syst { } /// SysTick clock source +#[derive(Clone, Copy, Debug)] pub enum SystClkSource { /// Core-provided clock Core, /// External reference clock - External + External, } -const SYST_COUNTER_MASK: u32 = 0x00ffffff; +const SYST_COUNTER_MASK: u32 = 0x00ffffff; -const SYST_CSR_ENABLE: u32 = 1 << 0; -const SYST_CSR_TICKINT: u32 = 1 << 1; +const SYST_CSR_ENABLE: u32 = 1 << 0; +const SYST_CSR_TICKINT: u32 = 1 << 1; const SYST_CSR_CLKSOURCE: u32 = 1 << 2; const SYST_CSR_COUNTFLAG: u32 = 1 << 16; -const SYST_CALIB_SKEW: u32 = 1 << 30; -const SYST_CALIB_NOREF: u32 = 1 << 31; +const SYST_CALIB_SKEW: u32 = 1 << 30; +const SYST_CALIB_NOREF: u32 = 1 << 31; impl Syst { /// Checks if counter is enabled @@ -542,17 +554,19 @@ impl Syst { let clk_source_bit = self.csr.read() & SYST_CSR_CLKSOURCE != 0; match clk_source_bit { false => SystClkSource::External, - true => SystClkSource::Core + true => SystClkSource::Core, } } /// Sets clock source pub fn set_clock_source(&self, clk_source: SystClkSource) { match clk_source { - SystClkSource::External => - unsafe { self.csr.modify(|v| v & !SYST_CSR_CLKSOURCE) }, - SystClkSource::Core => - unsafe { self.csr.modify(|v| v | SYST_CSR_CLKSOURCE) } + SystClkSource::External => unsafe { + self.csr.modify(|v| v & !SYST_CSR_CLKSOURCE) + }, + SystClkSource::Core => unsafe { + self.csr.modify(|v| v | SYST_CSR_CLKSOURCE) + }, } } @@ -586,7 +600,8 @@ impl Syst { unsafe { self.cvr.write(0) } } - /// Returns the reload value with which the counter would wrap once per 10 ms + /// Returns the reload value with which the counter would wrap once per 10 + /// ms /// /// Returns `0` if the value is not known (e.g. because the clock can /// change dynamically). @@ -596,8 +611,9 @@ impl Syst { /// Checks if the calibration value is precise /// - /// Returns `false` if using the reload value returned by `get_ticks_per_10ms()` - /// may result in a period significantly deviating from 10 ms. + /// Returns `false` if using the reload value returned by + /// `get_ticks_per_10ms()` may result in a period significantly deviating + /// from 10 ms. pub fn is_precise(&self) -> bool { self.calib.read() & SYST_CALIB_SKEW == 0 } diff --git a/src/register/apsr.rs b/src/register/apsr.rs index 338c684..d966de0 100644 --- a/src/register/apsr.rs +++ b/src/register/apsr.rs @@ -1,6 +1,7 @@ //! Application Program Status Register /// Application Program Status Register +#[derive(Clone, Copy, Debug)] pub struct Apsr { bits: u32, } diff --git a/src/register/control.rs b/src/register/control.rs index 62ebff6..d5cb8ec 100644 --- a/src/register/control.rs +++ b/src/register/control.rs @@ -1,6 +1,7 @@ //! Control register /// Control register +#[derive(Clone, Copy, Debug)] pub struct Control { bits: u32, } @@ -40,7 +41,7 @@ impl Control { } /// Thread mode privilege level -#[derive(Clone, Copy, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Npriv { /// Privileged Privileged, @@ -61,7 +62,7 @@ impl Npriv { } /// Currently active stack pointer -#[derive(Clone, Copy, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Spsel { /// MSP is the current stack pointer Msp, @@ -82,7 +83,7 @@ impl Spsel { } /// Whether context floating-point is currently active -#[derive(Clone, Copy, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Fpca { /// Floating-point context active. Active, diff --git a/src/register/faultmask.rs b/src/register/faultmask.rs index 5a06b37..7a0d06c 100644 --- a/src/register/faultmask.rs +++ b/src/register/faultmask.rs @@ -1,7 +1,7 @@ //! Fault Mask Register /// All exceptions are ... -#[derive(Clone, Copy, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Faultmask { /// Active Active, diff --git a/src/register/primask.rs b/src/register/primask.rs index 1e24b73..313693f 100644 --- a/src/register/primask.rs +++ b/src/register/primask.rs @@ -1,7 +1,7 @@ //! Priority mask register /// All exceptions with configurable priority are ... -#[derive(Clone, Copy, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Primask { /// Active Active, |