Skip to content

Commit 6c9b089

Browse files
committed
Initial commit
0 parents  commit 6c9b089

File tree

8 files changed

+265
-0
lines changed

8 files changed

+265
-0
lines changed

.github/FUNDING.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
github: [probe-rs]

.github/workflows/ci.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
on:
2+
push:
3+
branches: [master, staging, trying]
4+
pull_request:
5+
6+
name: Run CI
7+
8+
env:
9+
CARGO_TERM_COLOR: always
10+
11+
jobs:
12+
build:
13+
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- uses: actions/checkout@v3
18+
- name: Install stable toolchain
19+
uses: dtolnay/rust-toolchain@stable
20+
with:
21+
target: thumbv7em-none-eabi
22+
- name: Cache Dependencies
23+
uses: Swatinem/[email protected]
24+
- name: Install Dependencies
25+
run: |
26+
sudo apt update
27+
cargo install cargo-binutils
28+
rustup component add llvm-tools-preview
29+
- name: Check
30+
run: cargo check --target thumbv7em-none-eabi
31+
- name: Clippy
32+
run: cargo clippy --target thumbv7em-none-eabi
33+
- name: Format
34+
run: cargo fmt

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/target
2+
/Cargo.lock

Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "flash-algorithm"
3+
version = "0.1.0"
4+
edition = "2021"
5+
readme = "README.md"
6+
keywords = ["no-std", "embedded", "flashing"]
7+
license = "MIT OR Apache-2.0"
8+
repository = "https://github.com/probe-rs/flash-algorithm"
9+
description = "A crate to write CMSIS-DAP flash algorithms for flashing embedded targets."
10+
11+
[dependencies]

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# flash-algorithm
2+
3+
A crate to write CMSIS-DAP flash algorithms for flashing embedded targets.
4+
This crate is an abstrction over https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/flashAlgorithm.html which takes care of proper placement of functions in the respective ELF sections and linking properly.
5+
6+
[![crates.io](https://img.shields.io/crates/v/flash-algorithm)](https://crates.io/crates/flash-algorithm) [![documentation](https://docs.rs/flash-algorithm/badge.svg)](https://docs.rs/flash-algorithm) [![Actions Status](https://img.shields.io/github/actions/workflow/status/probe-rs/flash-algorithm/ci.yml?branch=master)](https://github.com/probe-rs/flash-algorithm/actions) [![chat](https://img.shields.io/badge/chat-probe--rs%3Amatrix.org-brightgreen)](https://matrix.to/#/#probe-rs:matrix.org)
7+
8+
To write a flash algorithm, follow the instructions in https://github.com/probe-rs/flash-algorithm-template.
9+
10+
# License
11+
12+
This thingy is licensed under either of
13+
14+
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
15+
http://www.apache.org/licenses/LICENSE-2.0)
16+
17+
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
18+
19+
at your option.

build.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use std::{env, fs::File, io::Write, path::PathBuf};
2+
3+
fn main() {
4+
// Put the linker script somewhere the linker can find it
5+
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
6+
File::create(out.join("memory.x"))
7+
.unwrap()
8+
.write_all(include_bytes!("memory.x"))
9+
.unwrap();
10+
println!("cargo:rustc-link-search={}", out.display());
11+
12+
println!("cargo:rerun-if-changed=build.rs");
13+
println!("cargo:rerun-if-changed=memory.x");
14+
}

memory.x

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
SECTIONS {
2+
. = DEFINED(ALGO_PLACEMENT_START_ADDRESS) ? ALGO_PLACEMENT_START_ADDRESS : 0x0;
3+
4+
/*
5+
* The PrgCode output section name comes from the CMSIS-Pack flash algorithms
6+
* templates and armlink. It is used here because several tools that work
7+
* with these flash algorithms expect this section name.
8+
*
9+
* All input sections are combined into PrgCode because RWPI using R9 is not
10+
* currently stable in Rust, thus having separate PrgData sections that the
11+
* debug host might locate at a different offset from PrgCode is not safe.
12+
*/
13+
PrgCode : {
14+
KEEP(*(.entry))
15+
KEEP(*(.entry.*))
16+
17+
*(.text)
18+
*(.text.*)
19+
20+
*(.rodata)
21+
*(.rodata.*)
22+
23+
*(.data)
24+
*(.data.*)
25+
26+
*(.sdata)
27+
*(.sdata.*)
28+
29+
*(.bss)
30+
*(.bss.*)
31+
32+
*(.uninit)
33+
*(.uninit.*)
34+
35+
. = ALIGN(4);
36+
}
37+
38+
/DISCARD/ : {
39+
/* Unused exception related info that only wastes space */
40+
*(.ARM.exidx);
41+
*(.ARM.exidx.*);
42+
*(.ARM.extab.*);
43+
}
44+
}

src/lib.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#![no_std]
2+
#![no_main]
3+
#![macro_use]
4+
5+
#[cfg(not(test))]
6+
#[panic_handler]
7+
fn panic(_info: &core::panic::PanicInfo) -> ! {
8+
unsafe {
9+
core::arch::asm!("udf #0");
10+
core::hint::unreachable_unchecked();
11+
}
12+
}
13+
14+
pub const FUNCTION_ERASE: u32 = 1;
15+
pub const FUNCTION_PROGRAM: u32 = 2;
16+
pub const FUNCTION_VERIFY: u32 = 3;
17+
18+
pub type ErrorCode = core::num::NonZeroU32;
19+
20+
pub trait FlashAlgorithm: Sized + 'static {
21+
/// Initialize the flash algorithm.
22+
///
23+
/// It can happen that the flash algorithm does not need any specific initialization
24+
/// for the function to be executed or no initialization at all. It is up to the implementor
25+
/// to decide this.
26+
///
27+
/// # Arguments
28+
///
29+
/// * `address` - The start address of the flash region to program.
30+
/// * `clock` - The clock speed in Hertz for programming the device.
31+
/// * `function` - The function for which this initialization is for.
32+
fn new(address: u32, clock: u32, function: Function) -> Result<Self, ErrorCode>;
33+
34+
/// Erase entire chip. Will only be called after [`FlashAlgorithm::new()`] with [`Function::Erase`].
35+
fn erase_all(&mut self) -> Result<(), ErrorCode>;
36+
37+
/// Erase sector. Will only be called after [`FlashAlgorithm::new()`] with [`Function::Erase`].
38+
///
39+
/// # Arguments
40+
///
41+
/// * `address` - The start address of the flash sector to erase.
42+
fn erase_sector(&mut self, address: u32) -> Result<(), ErrorCode>;
43+
44+
/// Program bytes. Will only be called after [`FlashAlgorithm::new()`] with [`Function::Program`].
45+
///
46+
/// # Arguments
47+
///
48+
/// * `address` - The start address of the flash page to program.
49+
/// * `size` - Specifies the size of the data buffer.
50+
/// * `data` - The data to be written to the page.
51+
fn program_page(&mut self, address: u32, size: u32, data: *const u8) -> Result<(), ErrorCode>;
52+
}
53+
54+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
55+
pub enum Function {
56+
Erase = 1,
57+
Program = 2,
58+
Verify = 3,
59+
}
60+
61+
/// A macro to define a new flash algoritm.
62+
///
63+
/// It takes care of placing the functions in the correct linker sections
64+
/// and checking the flash algorithm initialization status.
65+
#[macro_export]
66+
macro_rules! algorithm {
67+
($type:ty) => {
68+
static mut _IS_INIT: bool = false;
69+
static mut _ALGO_INSTANCE: MaybeUninit<$type> = MaybeUninit::uninit();
70+
71+
#[no_mangle]
72+
#[link_section = ".entry"]
73+
pub unsafe extern "C" fn Init(addr: u32, clock: u32, function: u32) -> u32 {
74+
if _IS_INIT {
75+
UnInit();
76+
}
77+
_IS_INIT = true;
78+
let function = match function {
79+
1 => $crate::Function::Erase,
80+
2 => $crate::Function::Program,
81+
3 => $crate::Function::Verify,
82+
_ => panic!("This branch can only be reached if the host library sent an unknown function code.")
83+
};
84+
match <$type as FlashAlgorithm>::new(addr, clock, function) {
85+
Ok(inst) => {
86+
_ALGO_INSTANCE.as_mut_ptr().write(inst);
87+
_IS_INIT = true;
88+
0
89+
}
90+
Err(e) => e.get(),
91+
}
92+
}
93+
#[no_mangle]
94+
#[link_section = ".entry"]
95+
pub unsafe extern "C" fn UnInit() -> u32 {
96+
if !_IS_INIT {
97+
return 1;
98+
}
99+
_ALGO_INSTANCE.as_mut_ptr().drop_in_place();
100+
_IS_INIT = false;
101+
0
102+
}
103+
#[no_mangle]
104+
#[link_section = ".entry"]
105+
pub unsafe extern "C" fn EraseChip() -> u32 {
106+
if !_IS_INIT {
107+
return 1;
108+
}
109+
let this = &mut *_ALGO_INSTANCE.as_mut_ptr();
110+
match <$type as FlashAlgorithm>::erase_all(this) {
111+
Ok(()) => 0,
112+
Err(e) => e.get(),
113+
}
114+
}
115+
#[no_mangle]
116+
#[link_section = ".entry"]
117+
pub unsafe extern "C" fn EraseSector(addr: u32) -> u32 {
118+
if !_IS_INIT {
119+
return 1;
120+
}
121+
let this = &mut *_ALGO_INSTANCE.as_mut_ptr();
122+
match <$type as FlashAlgorithm>::erase_sector(this, addr) {
123+
Ok(()) => 0,
124+
Err(e) => e.get(),
125+
}
126+
}
127+
#[no_mangle]
128+
#[link_section = ".entry"]
129+
pub unsafe extern "C" fn ProgramPage(addr: u32, size: u32, data: *const u8) -> u32 {
130+
if !_IS_INIT {
131+
return 1;
132+
}
133+
let this = &mut *_ALGO_INSTANCE.as_mut_ptr();
134+
match <$type as FlashAlgorithm>::program_page(this, addr, size, data) {
135+
Ok(()) => 0,
136+
Err(e) => e.get(),
137+
}
138+
}
139+
};
140+
}

0 commit comments

Comments
 (0)