initial commit

This commit is contained in:
2022-10-23 23:45:43 -07:00
commit e190fa5193
6450 changed files with 8626944 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
[unstable]
weak-dep-features = true
[target.thumbv7em-none-eabihf]
# uncomment this to make `cargo run` execute programs on QEMU
# runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
# runner = "probe-run --chip AMA3B1KK-KBR"
runner = "../../tools/objcopy-bootload-svl.sh"
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# uncomment ONE of these three option to make `cargo run` start a GDB session
# which option to pick depends on your system
# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
# runner = "gdb-multiarch -q -x openocd.gdb"
# runner = "gdb -q -x openocd.gdb"
rustflags = [
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
"-C", "link-arg=--nmagic",
# LLD (shipped with the Rust toolchain) is used as the default linker
"-C", "link-arg=-Tlink.x",
# if you run into problems with LLD switch to the GNU linker by commenting out
# this line
# "-C", "linker=arm-none-eabi-ld",
# if you need to link to pre-compiled C libraries provided by a C toolchain
# use GCC as the linker by commenting out both lines above and then
# uncommenting the three lines below
# "-C", "linker=arm-none-eabi-gcc",
# "-C", "link-arg=-Wl,-Tmemory.x",
# "-C", "link-arg=-nostartfiles",
]
[build]
# Pick ONE of these compilation targets
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
#target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
# target = "thumbv8m.base-none-eabi" # Cortex-M23
# target = "thumbv8m.main-none-eabi" # Cortex-M33 (no FPU)
# target = "thumbv8m.main-none-eabihf" # Cortex-M33 (with FPU)
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "flashdev"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
panic-halt = "0.2.0"
cortex-m-rt = { version = "0.7.0"}
cortex-m = "0.7.3"
cty = "0.2.2"
[dependencies.ambiq-hal]
path = "../../ambiq-hal"
features = [ "ambiq-sdk", "sparkfun-redboard-nano", "rt"]
[dev-dependencies]
rtt-target = { version = "*", features = [ "cortex-m" ] }
ufmt = "*"
+31
View File
@@ -0,0 +1,31 @@
//! This build script copies the `memory.x` file from the crate root into
//! a directory where the linker can always find it at build time.
//! For many projects this is optional, as the linker always searches the
//! project root directory -- wherever `Cargo.toml` is. However, if you
//! are using a workspace or have a more complicated build setup, this
//! build script becomes required. Additionally, by requesting that
//! Cargo re-run the build script whenever `memory.x` is changed,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
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());
// By default, Cargo will re-run a build script whenever
// any file in the project changes. By specifying `memory.x`
// here, we ensure the build script is only re-run when
// `memory.x` is changed.
println!("cargo:rerun-if-changed=memory.x");
}
+120
View File
@@ -0,0 +1,120 @@
ENTRY(Reset_Handler)
MEMORY
{
/* FLASH (rx) : ORIGIN = 0x0000C000, LENGTH = (960K - (0xc000)) */
/* RAM (rwx) : ORIGIN = 0x10000000, LENGTH = 384K */
FLASH (rx) : ORIGIN = 0x00010000, LENGTH = (960K - (0x10000))
RAM_NVIC (rwx) : ORIGIN = 0x10000000, LENGTH = 0x100
RAM (rwx) : ORIGIN = (0x10000000 + 0x100), LENGTH = (384K - 0x100)
}
/* SECTIONS */
/* { */
/* .text : */
/* { */
/* . = ALIGN(4); */
/* _stext = .; */
/* KEEP(*(.vector_table.interrupts)) */
/* *(.text) */
/* *(.text*) */
/* } */
/* SECTIONS */
/* { */
/* .text : */
/* { */
/* . = ALIGN(4); */
/* _stext = .; */
/* KEEP(*(.isr_vector)) */
/* KEEP(*(.ble_patch)) */
/* *(.text) */
/* *(.text*) */
/* KEEP(*(.init)) */
/* KEEP(*(.fini)) */
/* *crtbegin.o(.ctors) */
/* *crtbegin?.o(.ctors) */
/* *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) */
/* *(SORT(.ctors.*)) */
/* *(.ctors) */
/* *crtbegin.o(.dtors) */
/* *crtbegin?.o(.dtors) */
/* *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) */
/* *(SORT(.dtors.*)) */
/* *(.dtors) */
/* . = ALIGN(4); */
/* *(.rodata) */
/* *(.rodata*) */
/* KEEP(*(.eh_frame*)) */
/* . = ALIGN(4); */
/* } > FLASH */
/* .ARM.extab : */
/* { */
/* *(.ARM.extab* .gnu.linkonce.armextab.*) */
/* } > FLASH */
/* __exidx_start = .; */
/* .ARM.exidx : */
/* { */
/* *(.ARM.exidx* .gnu.linkonce.armexidx.*) */
/* } > FLASH */
/* __exidx_end = .; */
/* __etext = ALIGN(8); */
/* .data : AT (__etext) */
/* { */
/* __data_start__ = .; */
/* *(.data*) */
/* . = ALIGN(8); */
/* PROVIDE_HIDDEN (__preinit_array_start = .); */
/* KEEP(*(.preinit_array)) */
/* PROVIDE_HIDDEN (__preinit_array_end = .); */
/* . = ALIGN(8); */
/* PROVIDE_HIDDEN (__init_array_start = .); */
/* KEEP(*(SORT(.init_array.*))) */
/* KEEP(*(.init_array)) */
/* PROVIDE_HIDDEN (__init_array_end = .); */
/* . = ALIGN(8); */
/* PROVIDE_HIDDEN (__fini_array_start = .); */
/* KEEP(*(SORT(.fini_array.*))) */
/* KEEP(*(.fini_array)) */
/* PROVIDE_HIDDEN (__fini_array_end = .); */
/* KEEP(*(.jcr*)) */
/* . = ALIGN(8); */
/* __data_end__ = .; */
/* } > RAM */
/* .uninitialized (NOLOAD): */
/* { */
/* . = ALIGN(32); */
/* __uninitialized_start = .; */
/* *(.uninitialized) */
/* KEEP(*(.keep.uninitialized)) */
/* . = ALIGN(32); */
/* __uninitialized_end = .; */
/* } > RAM */
/* .bss : */
/* { */
/* . = ALIGN(8); */
/* _sbss = .; */
/* __bss_start__ = .; */
/* *(.bss) */
/* *(.bss*) */
/* *(COMMON) */
/* . = ALIGN(8); */
/* _ebss = .; */
/* __bss_end__ = .; */
/* } > RAM */
/* .heap (NOLOAD): */
/* { */
/* . = ALIGN(4); */
/* __end__ = .; */
/* PROVIDE( end = . ); */
/* _sheap = .; */
/* . = ORIGIN(RAM) + LENGTH(RAM) - 0x400 -8; */
/* __HeapLimit = .; */
/* } >RAM */
/* .stack_dummy (NOLOAD): */
/* { */
/* . = ALIGN(8); */
/* *(.stack*) */
/* } > RAM */
/* __StackTop = ORIGIN(RAM) + LENGTH(RAM)-8; */
/* __StackLimit = __StackTop - 0x400; */
/* PROVIDE(__stack = __StackTop); */
/* PROVIDE(_sstack = __StackTop); */
/* } */
+29
View File
@@ -0,0 +1,29 @@
use cty::*;
#[repr(C)]
struct SECTOR_INFO {
SectorSize: u32, // Sector Size in bytes
SectorStartAddr: u32, // Start address of the sector area (relative to the "BaseAddr" of the flash)
}
#[repr(C)]
pub struct FlashDevice {
AlgoVer: u16, // Algo version number
Name: [u8; 128], // Flash device name. NEVER change the size of this array!
Type: u16, // Flash device type
BaseAddr: u32, // Flash base address
TotalSize: u32, // Total flash device size in Bytes (256 KB)
PageSize: u32, // Page Size (number of bytes that will be passed to ProgramPage(). MinAlig is 8 byte
Reserved: u32, // Reserved, should be 0
ErasedVal: u8, // Flash erased value
TimeoutProg: u32, // Program page timeout in ms
TimeoutErase: u32, // Erase sector timeout in ms
SectorInfo: [SECTOR_INFO; 4], // Flash sector layout definition. May be adapted up to 512 entries
}
static FlashDevice: FlashDevice = FlashDevice {
AlgoVer: 0,
Name:
};
+20
View File
@@ -0,0 +1,20 @@
#![no_std]
#![no_main]
#![allow(non_snake_case)]
use panic_halt as _;
pub mod flashdev;
use ambiq_hal as hal;
use cortex_m_rt::entry;
use cortex_m;
#[entry]
fn main() -> ! {
// println!("Hello, world!");
loop {
cortex_m::asm::nop();
}
}
+33
View File
@@ -0,0 +1,33 @@
#! /usr/bin/env bash
set -ep
TARGET=${1}
BIN="${TARGET}.bin"
JLINK="${TARGET}.jlink"
BOOTLOADER="$(dirname ${0})/svl/svl.py"
echo "size:"
arm-none-eabi-size "${TARGET}"
echo "objcopy.."
arm-none-eabi-objcopy -S -O binary "${TARGET}" "${BIN}"
echo "writing JLink script to ${JLINK}.."
cat > ${JLINK} <<EOF
r
sleep 10
wreg MSP, 0x10000100
loadbin ${BIN}, 0x10000
w4 0x40000004 0x1B
qc
EOF
echo "flashing using JLinkExe.."
JLinkExe -device AMA3B1KK-KBR -autoconnect 1 -if swd -speed 4000 -NoGui -ExitOnError -CommandFile ${JLINK}
echo "running probe-run.."
probe-run --chip AMA3B1KK-KBR $@
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "probe"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.47"
env_logger = "0.9.0"
log = "0.4.14"
probe-rs = "0.11.0"
[patch.crates-io]
probe-rs = { path = "../../../embedded/probe-rs/probe-rs" }
+121
View File
@@ -0,0 +1,121 @@
#[macro_use]
extern crate log;
use env_logger::Env;
use probe_rs::{Probe, Core, Session, CoreRegisterAddress, MemoryInterface};
use probe_rs::flashing::{
Format,
download_file_with_options,
DownloadOptions,
};
use std::path::Path;
use std::thread::sleep_ms;
use std::time::Duration;
fn main() -> anyhow::Result<()> {
env_logger::Builder::from_env(Env::default().default_filter_or("probe=debug,probe_rs=debug,probe_rs::probe::jlink::swd=info")).init();
info!("Connecting to Apollo3 MCU through first attached probe..");
let mut session = Session::auto_attach("AMA3B1KK-KBR")?;
{
info!("Select a core");
let mut core = session.core(0)?;
info!("Trying to halt and continue core..");
core.halt(Duration::from_millis(100))?;
print_core_status(&mut core);
assert!(core.core_halted().unwrap());
info!("Resuming core..");
core.run()?;
print_core_status(&mut core);
assert!(!core.core_halted().unwrap());
info!("Try reset_and_halt");
core.reset_and_halt(Duration::from_millis(500))?;
print_core_status(&mut core);
assert!(core.core_halted().unwrap());
info!("Write and readback test RAM");
// Try and write some sample data
let data = (0..1024).collect::<Vec<u32>>();
assert_eq!(data.len(), 1024);
core.write_32(0x1000_5000, &data)?;
// Reading back and verifying.
let mut read_back = vec![0u32; 1024];
core.read_32(0x1000_5000, &mut read_back)?;
assert_eq!(data, read_back);
// info!("Write and readback test FLASH");
// // Can we write to flash?... no obviously not.
// core.write_32(0x10_00c0, &[1u32, 2, 4])?;
}
// Should now be ready for flashing.
// let mut mm = session.memory_map();
let elf = "/home/gauteh/dev/ambiq-rs/boards/redboard-nano/target/thumbv7em-none-eabihf/debug/examples/rtt";
flash_test(&mut session, elf)?;
{
let mut core = session.core(0)?;
info!("Trying to resume (reset)..");
core.reset()?;
print_core_status(&mut core);
assert!(!core.core_halted().unwrap());
}
Ok(())
}
pub fn flash(session: &mut Session, elf: &str) -> anyhow::Result<()> {
warn!("Trying to flash: {}", elf);
let mut opts = DownloadOptions::default();
opts.verify = true;
download_file_with_options(
session,
Path::new(elf),
Format::Elf,
opts)?;
warn!("Done flashing: {}", elf);
Ok(())
}
pub fn flash_test(session: &mut Session, elf: &str) -> anyhow::Result<()> {
warn!("Trying flash_test: {}", elf);
let mut opts = DownloadOptions::default();
let mut loader = session.target().flash_loader();
loader.add_data(0x10_000, &[1, 2, 3, 4])?;
loader.commit(session, opts)?;
let mut core = session.core(0)?;
let mut data = vec![0u8; 16];
core.read_8(0x10_000, &mut data)?;
info!("read back: {:?}", data);
warn!("Done flashing: {}", elf);
Ok(())
}
pub fn print_core_status(core: &mut Core) -> anyhow::Result<()> {
let running = !core.core_halted().unwrap();
let pc = core.read_core_reg(CoreRegisterAddress(15))?;
info!("Core running: {}", running);
info!("PC: {:#08x}", pc);
Ok(())
}
+19
View File
@@ -0,0 +1,19 @@
#! /usr/bin/env bash
set -ep
TARGET=${1}
BIN="${TARGET}.bin"
BOOTLOADER="$(dirname ${0})/svl/svl.py"
echo "size:"
arm-none-eabi-size "${TARGET}"
echo "objcopy.."
arm-none-eabi-objcopy -S -O binary "${TARGET}" "${BIN}"
echo "flashing /dev/ttyUSB0.."
python3 "${BOOTLOADER}" -f "${BIN}" /dev/ttyUSB0 -v
+2
View File
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
+164
View File
@@ -0,0 +1,164 @@
on:
push:
name: generate executables
jobs:
windows:
name: windows
runs-on: windows-latest
steps:
- name: repo
uses: actions/checkout@v2.3.1
with:
path: svl
fetch-depth: 0
- name: python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: git
run: |
git config --global user.email "apollo3-uploader-builder@sparkfun.com"
git config --global user.name "apollo3-uploader-builder"
git config --global pull.ff only
cd svl
git status
git pull
cd ${GITHUB_WORKSPACE}
- name: install
run: |
python --version
python -m pip install --upgrade pip setuptools wheel
pip install -r svl/requirements.txt
- name: build
run: |
pyinstaller --onefile svl/svl.py
- name: copy
run: |
Remove-Item -Recurse -Force svl\dist\windows
mkdir svl\dist\windows
Move-Item -Path dist\svl.exe -Destination svl\dist\windows\svl.exe
- name: commit
run: |
cd svl
git add dist\windows\*
git commit -m "generated windows executable"
git push
cd ${GITHUB_WORKSPACE}
linux:
name: linux
needs: windows
runs-on: ubuntu-16.04
steps:
- name: repo
uses: actions/checkout@v2.3.1
with:
path: svl
fetch-depth: 0
- name: python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: git
run: |
git config --global user.email "apollo3-uploader-builder@sparkfun.com"
git config --global user.name "apollo3-uploader-builder"
git config --global pull.ff only
cd svl
git pull
cd ${GITHUB_WORKSPACE}
- name: install
run: |
python --version
python -m pip install --upgrade pip setuptools wheel
pip install -r svl/requirements.txt
- name: build
run: |
pyinstaller --onefile svl/svl.py
- name: copy
run: |
rm -rf ./svl/dist/linux
mkdir -p ./svl/dist/linux
mv ./dist/svl ./svl/dist/linux/svl
- name: permit
run: |
chmod +x ./svl/dist/linux/svl
- name: commit
run: |
cd ./svl
git add ./dist/linux/*
git commit -m "generated linux executable"
git push
cd ${GITHUB_WORKSPACE}
macosx:
name: macosx
needs: linux
runs-on: macos-latest
steps:
- name: repo
uses: actions/checkout@v2.3.1
with:
path: svl
fetch-depth: 0
- name: python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: git
run: |
git config --global user.email "apollo3-uploader-builder@sparkfun.com"
git config --global user.name "apollo3-uploader-builder"
git config --global pull.ff only
cd svl
git pull
cd ${GITHUB_WORKSPACE}
- name: install
run: |
python --version
python -m pip install --upgrade pip setuptools wheel
pip install -r svl/requirements.txt
- name: build
run: |
echo "Build disabled until github environment supports Big Sur"
# pyinstaller --onefile svl/svl.py
- name: copy
run: |
echo "Build disabled until github environment supports Big Sur"
# rm -rf ./svl/dist/macosx
# mkdir -p ./svl/dist/macosx
# mv ./dist/svl ./svl/dist/macosx/svl
- name: permit
run: |
echo "Build disabled until github environment supports Big Sur"
# chmod +x ./svl/dist/macosx/svl
- name: commit
run: |
echo "Build disabled until github environment supports Big Sur"
# cd ./svl
# git add ./dist/macosx/*
# git commit -m "generated macosx executable"
# git push
# cd ${GITHUB_WORKSPACE}
+134
View File
@@ -0,0 +1,134 @@
# intermediate blobs
*intermediate_*OTA_blob.bin
# macosx
.DS_Store
# gcc build artifacts
*.o
*.d
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
+108
View File
@@ -0,0 +1,108 @@
ENTRY(Reset_Handler)
MEMORY
{
FLASH (rx) : ORIGIN = 0x00010000, LENGTH = 0x000F0000
RAM_NVIC (rwx) : ORIGIN = 0x10000000, LENGTH = 0x100
RAM (rwx) : ORIGIN = (0x10000000 + 0x100), LENGTH = (384K - (0x100))
}
SECTIONS
{
.text :
{
. = ALIGN(4);
_stext = .;
KEEP(*(.isr_vector))
KEEP(*(.ble_patch))
*(.text)
*(.text*)
KEEP(*(.init))
KEEP(*(.fini))
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
. = ALIGN(4);
*(.rodata)
*(.rodata*)
KEEP(*(.eh_frame*))
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
__etext = ALIGN(8);
.data : AT (__etext)
{
__data_start__ = .;
*(.data*)
. = ALIGN(8);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(8);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
KEEP(*(.jcr*))
. = ALIGN(8);
__data_end__ = .;
} > RAM
.uninitialized (NOLOAD):
{
. = ALIGN(32);
__uninitialized_start = .;
*(.uninitialized)
KEEP(*(.keep.uninitialized))
. = ALIGN(32);
__uninitialized_end = .;
} > RAM
.bss :
{
. = ALIGN(8);
_sbss = .;
__bss_start__ = .;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(8);
_ebss = .;
__bss_end__ = .;
} > RAM
.heap (NOLOAD):
{
. = ALIGN(4);
__end__ = .;
PROVIDE( end = . );
_sheap = .;
. = ORIGIN(RAM) + LENGTH(RAM) - 0x400 -8;
__HeapLimit = .;
} >RAM
.stack_dummy (NOLOAD):
{
. = ALIGN(8);
*(.stack*)
} > RAM
__StackTop = ORIGIN(RAM) + LENGTH(RAM)-8;
__StackLimit = __StackTop - 0x400;
PROVIDE(__stack = __StackTop);
PROVIDE(_sstack = __StackTop);
}
+31
View File
@@ -0,0 +1,31 @@
![generate executables](https://github.com/sparkfun/Apollo3_Uploader_SVL/workflows/generate%20executables/badge.svg)
# Apollo3 Uploader - SparkFun Variable Loader (SVL)
The ```svl``` bootloader is the default bootloader on Artemis module--based boards. This repo contains both the Python programs that are used to send code to a board and the source code for the program that receives code on an Artemis module.
The bootloader for the Artemis can be upgraded via Arduino using [these instructions](https://learn.sparkfun.com/tutorials/designing-with-the-sparkfun-artemis/all#troubleshooting). It can also be upgraded using the [Artemis Firmware Upload GUI](https://github.com/sparkfun/Artemis-Firmware-Upload-GUI).
Artemis SVL Versions:
* v5 - Speed and reliability increases
* v4 - Added peripheral deinitialization to deliver the Apollo3 closer to the initial reset state.
* v3 - Initial version
# Usage
* prerequisites
* board has the SparkFun Variable Loader installed
* source code is located in ```bootlaoder/src```
* precompiled binaries and a makefile are located in ```bootloader/gcc```
(the artemis module target is suitable for all current SparkFun products)
* Python is installed on your computer
* your program is compiled + linked to support the SVL (binary offset ```0x10000```)
* use the SVL loader script to upload to the board
```python svl.py [flags] port```
* ```-b``` **baud rate**: 115200, 921600, or other desired common baud rate (57600, 115200, 230400, 460800, 921600)
* ```-f``` **filepath**: path to the binary image of your program to upload
* ```port``` **serial port**: the serial port to connect over (*/dev/\** on \*nix or *COMX* on windows)
* ```-v``` **verbose**: turn on verbose output
+405
View File
@@ -0,0 +1,405 @@
#******************************************************************************
#
# Makefile - Rules for building the libraries, examples and docs.
#
# Copyright (c) 2019, Ambiq Micro
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# Third party software included in this distribution is subject to the
# additional license terms as defined in the /docs/licenses directory.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# This is part of revision 2.1.0 of the AmbiqSuite Development Package.
#
#******************************************************************************
#******************************************************************************
#
# This is an example makefile for SparkFun Apollo3 boards as used in the
# AmbiqSuite SDK.
#
# Recommended usage
# make
# make bootload_svl (uses the SparkFun Variable Loader to upload code)
# make bootload_asb (uses the Ambiq Secure Bootlaoder to upload code)
# make clean
#
# Filepaths
# You can relocate this makefile easily by providing the path to the root of
# the AmbiqSuite SDK. If that path is not specified then this file will
# assume that it is located in
# <AmbiqSDKRoot>/boards/<your_board>/examples/<your_example>/gcc
# and use relative paths
#
# User Configuration
# You must also specify which COM_PORT to use if you want to use the
# 'bootlaoder' targets.
# Windows example: COM_PORT=COM4
# *nix example: COM_PORT=/dev/usbserialxxxx
#
# Python vs. Executable
# For simplicity the upload tools are called as Python scripts by default.
# Make sure PYTHON is set to the appropriate command to run Python3 from the
# command line.
#
#******************************************************************************
#******************************************************************************
#
# User Options
#
#******************************************************************************
# You can override these values on the command line e.g. make bootload COM_PORT=/dev/cu***
# COM_PORT is the serial port to use for uploading. For example COM#### on Windows or /dev/cu.usbserial-#### on *nix
COM_PORT ?=
# ASB_UPLOAD_BAUD is the baud rate setting of the Ambiq Secue Bootloader (ASB) as it is configured on the Apollo3. Defautls to 115200 if unset
ASB_UPLOAD_BAUD ?=
# SVL_UPLOAD_BAUD is the baud rate setting of the SparkFun Variable Loader (SVL). Defaults to 921600 if unset
SVL_UPLOAD_BAUD ?=
# PYTHON3 should evaluate to a call to the Python3 executable on your machine
PYTHON3 ?=
# *Optionally* specify absolute paths to the SDK and the BSP
# You can do this on the command line - e.g. make bootload SDKPATH=~/$AMBIQ_SDK_ROOT_PATH
# Make sure to use / instead of \ when on Windows
SDKPATH ?=# Set as the path to the SDK root if not located at ../../../../..
COMMONPATH ?=# Set as the path to the BSP common folder if not located at ../../../../common
BOARDPATH ?=# Set as the path to the board if not located at ../../..
PROJECTPATH ?=# Set as the path to the project if not located at ..
BOARD ?=# If using a SparkFun board you can simply provide the name e.g. redboard_artemis_atp
### Project Settings
TARGET := svl
COMPILERNAME := gcc
PROJECT := $(TARGET)_gcc
#******************************************************************************
#
# Warning Messages
#
#******************************************************************************
ifeq ($(BOARD),)
$(warning warning: no BOARD specified, will fall back to BOARDPATH for arbitrary bsp locations)
else
BOARDPATH=../../../../$(BOARD)
$(warning Using BOARD=$(BOARD) at $(BOARDPATH))
endif
ifeq ($(COM_PORT),)
COM_PORT=COM4
$(warning warning: you have not defined COM_PORT. Assuming it is COM4)
endif
ifeq ($(PYTHON3),)
PYTHON3=python3
$(warning warning: you have not defined PYTHON3. assuming it is accessible by 'python3')
endif
ifeq ($(ASB_UPLOAD_BAUD),)
ASB_UPLOAD_BAUD=115200
$(warning defaulting to 115200 baud for ASB)
endif
ifeq ($(SVL_UPLOAD_BAUD),)
SVL_UPLOAD_BAUD=921600
$(warning defaulting to 921600 baud for SVL)
endif
ifeq ($(SDKPATH),)
SDKPATH =../../../../..
$(warning warning: you have not defined SDKPATH so will continue assuming that the SDK root is at $(SDKPATH))
else
# When the SDKPATH is given export it
export SDKPATH
endif
ifeq ($(COMMONPATH),)
COMMONPATH =../../../../common
$(warning warning: you have not defined COMMONPATH so will continue assuming that the COMMON root is at $(COMMONPATH))
else
# When the COMMONPATH is given export it
export COMMONPATH
endif
ifeq ($(BOARDPATH),)
$(error Error: BOARDPATH must be provided)
else
# Ensure that boardpath does not include a trailing '/'
ifeq ($(notdir $(BOARDPATH)),)
override BOARDPATH:=$(patsubst %/, %,$(BOARDPATH))
$(warning BOARDPATH had a trivial 'notdir' so we tried changing it to: $(BOARDPATH))
endif
BOARD=$(notdir $(BOARDPATH))
# When the BOARDPATH is given export it
export BOARDPATH
endif
ifeq ($(PROJECTPATH),)
PROJECTPATH =..
$(warning warning: you have not defined PROJECTPATH so will continue assuming that the PROJECT root is at $(PROJECTPATH))
else
# When the PROJECTPATH is given export it
export PROJECTPATH
endif
CONFIG := $(PROJECTPATH)/gcc/$(BOARD)/bin
$(warning CONFIG=$(CONFIG))
#******************************************************************************
#
# User Defines / Includes / Sources / Libraries
#
#******************************************************************************
# Global Defines
DEFINES= -DPART_$(PART)
DEFINES+= -DAM_CUSTOM_BDADDR
DEFINES+= -DAM_PACKAGE_BGA
DEFINES+= -DWSF_TRACE_ENABLED
DEFINES+= -DAM_DEBUG_PRINTF
DEFINES+= -DAM_PART_APOLLO3
DEFINES+=
# Includes (Add paths to where example header files are located)
INCLUDES=
INCLUDES+= -I$(PROJECTPATH)/src
INCLUDES+= -I$(BOARDPATH)/bsp
INCLUDES+= -I$(SDKPATH)
INCLUDES+= -I$(SDKPATH)/utils
INCLUDES+= -I$(SDKPATH)/devices
INCLUDES+= -I$(SDKPATH)/mcu/apollo3
INCLUDES+= -I$(SDKPATH)/CMSIS/AmbiqMicro/Include
INCLUDES+= -I$(SDKPATH)/CMSIS/ARM/Include
INCLUDES+= -I$(PROJECTPATH)/src/svl_packet
INCLUDES+= -I$(PROJECTPATH)/src/svl_ringbuf
INCLUDES+= -I$(PROJECTPATH)/src/svl_uart
INCLUDES+= -I$(PROJECTPATH)/src/svl_utils
INCLUDES+=
# Compilation Units (Add all the .c files you need to compile)
SRC=
SRC+= main.c
SRC+= am_util_delay.c
SRC+= am_util_stdio.c
SRC+= startup_gcc.c
SRC+= svl_packet.c
SRC+= svl_ringbuf.c
SRC+= svl_uart.c
SRC+= svl_utils.c
SRC+=
# VPATH (Add paths to where your source files are located)
VPATH=
VPATH+= $(PROJECTPATH)/src
VPATH+= $(SDKPATH)/utils
VPATH+= $(COMMONPATH)/tools_sfe/templates
VPATH+= $(PROJECTPATH)/src
VPATH+= $(PROJECTPATH)/src/svl_packet
VPATH+= $(PROJECTPATH)/src/svl_ringbuf
VPATH+= $(PROJECTPATH)/src/svl_uart
VPATH+= $(PROJECTPATH)/src/svl_utils
VPATH+=
# LIBS (Precompiled libraries to include in the linker step)
LIBS=
LIBS+= $(BOARDPATH)/bsp/gcc/bin/libam_bsp.a
LIBS+= $(SDKPATH)/mcu/apollo3/hal/gcc/bin/libam_hal.a
LIBS+=
#******************************************************************************
#
# Warning Messages
#
#******************************************************************************
### Bootloader Tools
ASB_UPLOADER=$(PYTHON3) $(COMMONPATH)/tools_sfe/asb/asb.py
SVL_UPLOADER=$(PYTHON3) $(COMMONPATH)/tools_sfe/svl/svl.py
SHELL:=/bin/bash
#### Setup ####
TOOLCHAIN ?= arm-none-eabi
PART = apollo3
CPU = cortex-m4
FPU = fpv4-sp-d16
# Default to FPU hardware calling convention. However, some customers and/or
# applications may need the software calling convention.
#FABI = softfp
FABI = hard
STARTUP_FILE := ./startup_$(COMPILERNAME).c
#### Required Executables ####
CC = $(TOOLCHAIN)-gcc
GCC = $(TOOLCHAIN)-gcc
CPP = $(TOOLCHAIN)-cpp
CXX = $(TOOLCHAIN)-g++
LD = $(TOOLCHAIN)-ld
CP = $(TOOLCHAIN)-objcopy
OD = $(TOOLCHAIN)-objdump
RD = $(TOOLCHAIN)-readelf
AR = $(TOOLCHAIN)-ar
SIZE = $(TOOLCHAIN)-size
RM = $(shell which rm 2>/dev/null)
EXECUTABLES = CC LD CP OD AR RD SIZE GCC CXX
K := $(foreach exec,$(EXECUTABLES),\
$(if $(shell which $($(exec)) 2>/dev/null),,\
$(info $(exec) not found on PATH ($($(exec))).)$(exec)))
$(if $(strip $(value K)),$(info Required Program(s) $(strip $(value K)) not found))
ifneq ($(strip $(value K)),)
all clean:
$(info Tools $(TOOLCHAIN)-$(COMPILERNAME) not installed.)
$(RM) -rf bin
else
#******************************************************************************
#
# Machinery
#
#******************************************************************************
XSRC = $(filter %.cpp,$(SRC))
ZSRC = $(filter %.cc,$(SRC))
CSRC = $(filter %.c,$(SRC))
ASRC = $(filter %.s,$(SRC))
OBJS = $(XSRC:%.cpp=$(CONFIG)/%.o)
OBJS+= $(ZSRC:%.cc=$(CONFIG)/%.o)
OBJS+= $(CSRC:%.c=$(CONFIG)/%.o)
OBJS+= $(ASRC:%.s=$(CONFIG)/%.o)
DEPS = $(XSRC:%.cpp=$(CONFIG)/%.d)
DEPS+= $(ZSRC:%.cc=$(CONFIG)/%.d)
DEPS+= $(CSRC:%.c=$(CONFIG)/%.d)
DEPS+= $(ASRC:%.s=$(CONFIG)/%.d)
CSTD = -std=c99
CFLAGS = -mthumb -mcpu=$(CPU) -mfpu=$(FPU) -mfloat-abi=$(FABI)
CFLAGS+= -ffunction-sections -fdata-sections
CFLAGS+= -MMD -MP $(CSTD) -Wall -g
CFLAGS+= -O0
CFLAGS+= $(DEFINES)
CFLAGS+= $(INCLUDES)
CFLAGS+=
XSTD = -std=gnu++11
XFLAGS = $(CFLAGS)
XFLAGS+= -fno-exceptions
LFLAGS = -mthumb -mcpu=$(CPU) -mfpu=$(FPU) -mfloat-abi=$(FABI)
LFLAGS+= -nostartfiles -static
LFLAGS+= -Wl,--gc-sections,--entry,Reset_Handler,-Map,$(CONFIG)/$(TARGET).map
LFLAGS+= -Wl,--start-group -lm -lc -lgcc $(LIBS) -Wl,--end-group
LFLAGS+=
# Additional user specified CFLAGS
CFLAGS+=$(EXTRA_CFLAGS)
CPFLAGS = -Obinary
ODFLAGS = -S
#******************************************************************************
#
# Targets / Rules
#
#******************************************************************************
all: asb
asb: directories $(CONFIG)/$(TARGET).bin
directories:
@mkdir -p $(CONFIG)
$(CONFIG)/%.o: %.cpp $(CONFIG)/%.d
@echo " Compiling $(COMPILERNAME) $<" ;\
$(CXX) -c $(XSTD) $(XFLAGS) $< -o $@
$(CONFIG)/%.o: %.cc $(CONFIG)/%.d
@echo " Compiling $(COMPILERNAME) $<" ;\
$(CXX) -c $(XSTD) $(XFLAGS) $< -o $@
$(CONFIG)/%.o: %.c $(CONFIG)/%.d
@echo " Compiling $(COMPILERNAME) $<" ;\
$(CC) -c $(CFLAGS) $< -o $@
$(CONFIG)/%.o: %.s $(CONFIG)/%.d
@echo " Assembling $(COMPILERNAME) $<" ;\
$(CC) -c $(CFLAGS) $< -o $@
$(CONFIG)/$(TARGET).axf: LINKER_FILE = $(COMMONPATH)/tools_sfe/templates/asb_linker.ld
$(CONFIG)/$(TARGET).axf: $(OBJS) $(LIBS)
@echo " Linking $(COMPILERNAME) $@ with script $(LINKER_FILE)";\
$(CC) -Wl,-T,$(LINKER_FILE) -o $@ $(OBJS) $(LFLAGS)
$(CONFIG)/$(TARGET).bin: $(CONFIG)/$(TARGET).axf
@echo " Copying $(COMPILERNAME) $@..." ;\
$(CP) $(CPFLAGS) $< $@ ;\
$(OD) $(ODFLAGS) $< > $(CONFIG)/$(TARGET).lst
bootload_asb: directories $(CONFIG)/$(TARGET).bin
$(AMBIQ_BIN2BOARD) --bin $(CONFIG)/$(TARGET).bin --load-address-blob 0x20000 --magic-num 0xCB -o $(CONFIG)/$(TARGET) --version 0x0 --load-address-wired 0xC000 -i 6 --options 0x1 -b $(ASB_UPLOAD_BAUD) -port $(COM_PORT) -r 2 -v
bootload_svl: directories $(CONFIG)/$(TARGET)_svl.bin
$(ARTEMIS_SVL) $(COM_PORT) -f $(CONFIG)/$(TARGET)_svl.bin -b $(SVL_UPLOAD_BAUD) -v
bootload: bootload_svl
clean:
@echo "Cleaning..." ;\
$(RM) -f $(OBJS) $(DEPS) \
$(CONFIG)/$(TARGET).bin $(CONFIG)/$(TARGET).axf \
$(CONFIG)/$(TARGET).lst $(CONFIG)/$(TARGET).map \
$(CONFIG)/$(TARGET)_svl.bin $(CONFIG)/$(TARGET)_svl.axf \
$(CONFIG)/$(TARGET)_svl.lst $(CONFIG)/$(TARGET)_svl.map \
$(CONFIG)/$(TARGET)_asb.bin $(CONFIG)/$(TARGET)_asb.axf \
$(CONFIG)/$(TARGET)_asb.lst $(CONFIG)/$(TARGET)_asb.map
$(CONFIG)/%.d: ;
$(SDKPATH)/mcu/apollo3/hal/gcc/bin/libam_hal.a:
$(MAKE) -C $(SDKPATH)/mcu/apollo3/hal/gcc
$(SDKPATH)/third_party/uecc/gcc/bin/lib_uecc.a:
$(MAKE) -C $(SDKPATH)/third_party/uecc
$(BOARDPATH)/bsp/gcc/bin/libam_bsp.a:
$(MAKE) -C $(BOARDPATH)/bsp/gcc
# Automatically include any generated dependencies
-include $(DEPS)
endif
.PHONY: all clean directories bootload bootload_asb bootload_svl
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+763
View File
@@ -0,0 +1,763 @@
//*****************************************************************************
//
//! @file main.c
//!
//! @brief A variable-baud rate bootloader for Apollo3 / Artemis module
//!
//! Purpose:
//
//*****************************************************************************
/*
Copyright (c) 2020 SparkFun Electronics
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
Authors:
Owen Lyke, Nathan Seidle
Modified: Juy 22 2019
*/
//*****************************************************************************
//
// Includes
//
//*****************************************************************************
#include "am_mcu_apollo.h"
#include "am_bsp.h"
#include "am_util.h"
#include "stdint.h"
#include "svl_ringbuf.h"
#include "svl_packet.h"
#include "svl_uart.h"
#include "svl_utils.h"
//*****************************************************************************
//
// Defines
//
//*****************************************************************************
#define SVL_VERSION_NUMBER 0x05
// ****************************************
//
// Bootloader Options
//
// ****************************************
#define BL_UART_BUF_LEN (2048 + 512) // must be larger than maximum frame transmission length for guaranteed performance
#define BL_UART_INST 0 // which UART peripheral to use for BL data
#define BL_RX_PAD 49 // RX pad for BL_UART_INST
#define BL_TX_PAD 48 // TX pad for BL_UART_INST
#define USERCODE_OFFSET (0xC000 + 0x4000) // location in flash to begin storing user's code (Linker script needs to be adjusted to offset user's flash to this address)
#define FRAME_BUFFER_SIZE 512 // maximum number of 4-byte words that can be transmitted in a single frame packet
// ****************************************
//
// Debug Options
//
// ****************************************
//#define DEBUG 1 // uncomment to enable debug output
#ifdef DEBUG
#define DEBUG_BAUD_RATE 921600 // debug output baud rate
#define DEBUG_UART_INST 1 // debug UART peripheral instance (should not be the same as BL_UART_INST)
#define DEBUG_RX_PAD 25 // RX pad for
#define DEBUG_TX_PAD 24
#define DEBUG_UART_BUF_LEN 256
#define DEBUG_PRINT_APP 1 // undefine to not print app pages
#define APP_PRINT_NUM_PAGE 1
#undef APP_PRINT_PRETTY // define APP_PRINT_PRETTY for the alternate app data print format
uint8_t debug_buffer[DEBUG_UART_BUF_LEN] = {0};
#endif // DEBUG
// ****************************************
//
// Bootloader Commands
//
// ****************************************
#define CMD_VERSION (0x01)
#define CMD_BLMODE (0x02)
#define CMD_NEXT (0x03)
#define CMD_FRAME (0x04)
#define CMD_RETRY (0x05)
#define CMD_DONE (0x06)
//*****************************************************************************
//
// Macros
//
//*****************************************************************************
#define UART_GPIO_PINCONFIG_INNER(INST, TXRX, PAD) \
{ \
.uFuncSel = AM_HAL_PIN_##PAD##_UART##INST##TXRX, .eDriveStrength = AM_HAL_GPIO_PIN_DRIVESTRENGTH_2MA \
}
#define UART_GPIO_PINCONFIG(INST, TXRX, PAD) UART_GPIO_PINCONFIG_INNER(INST, TXRX, PAD)
//*****************************************************************************
//
// Forward Declarations
//
//*****************************************************************************
void setup(void);
bool detect_baud_rate(uint32_t *baud);
void start_uart_bl(uint32_t baud);
void enter_bootload(void);
uint8_t handle_frame_packet(svl_packet_t *packet, uint32_t *p_frame_address, uint16_t *p_last_page_erased);
void app_start(void);
void debug_printf(char *fmt, ...);
//*****************************************************************************
//
// Globals
//
//*****************************************************************************
art_svl_ringbuf_t bl_rx_ringbuf = {
.buf = NULL,
.len = 0,
.r_offset = 0,
.w_offset = 0,
};
void *hUART_bl = NULL; // pointer to handle for bootloader UART
void *hUART_debug = NULL; // pointer to handle for debug UART
#define BL_BAUD_SAMPLES (5)
volatile uint8_t bl_baud_ticks_index = 0x00;
volatile uint32_t bl_baud_ticks[BL_BAUD_SAMPLES] = {0};
//*****************************************************************************
//
// Main
//
//*****************************************************************************
int main(void)
{
bool baud_valid = false;
uint32_t bl_baud = 0x00;
uint8_t bl_buffer[BL_UART_BUF_LEN] = {0};
#define PLLEN_VER 1
uint8_t packet_ver_buf[PLLEN_VER] = {SVL_VERSION_NUMBER};
svl_packet_t svl_packet_version = {CMD_VERSION, packet_ver_buf, PLLEN_VER, PLLEN_VER};
svl_packet_t svl_packet_blmode = {CMD_BLMODE, NULL, 0, 0};
art_svl_ringbuf_init(&bl_rx_ringbuf, bl_buffer, BL_UART_BUF_LEN);
setup();
debug_printf("\n\nArtemis SVL Bootloader - DEBUG\n\n");
baud_valid = detect_baud_rate(&bl_baud); // Detects the baud rate. Returns true if a valid baud rate was found
if (baud_valid == false)
{
app_start(); // w/o valid baud rate jump t the app
}
start_uart_bl(bl_baud); // This will create a 23 us wide low 'blip' on the TX line (until possibly fixed)
am_util_delay_us(200); // At the minimum baud rate of 115200 one byte (10 bits with start/stop) takes 10/115200 or 87 us. 87+23 = 100, double to be safe
debug_printf("phase:\tconfirm bootloading entry\n");
debug_printf("\tsending Artemis SVL version packet\n");
svl_packet_send(&svl_packet_version); // when baud rate is determined send the version packet
debug_printf("\twaiting for bootloader confirmation\n");
if (svl_packet_wait(&svl_packet_blmode) != 0)
{ // wait for the bootloader to confirm bootloader mode entry
debug_printf("\tno confirmation received\n");
app_start(); // break to app
}
debug_printf("\tentering bootloader\n\n");
enter_bootload(); // Now we are locked in
am_util_delay_ms(10);
am_hal_reset_control(AM_HAL_RESET_CONTROL_SWPOI, 0); //Cause a system Power On Init to release as much of the stack as possible
debug_printf("ERROR - runoff");
while (1)
{ // Loop forever while sleeping.
am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP); // Go to Deep Sleep.
}
}
//*****************************************************************************
//
// Function definitions below
//
//*****************************************************************************
#ifdef DEBUG
void start_uart_debug(void)
{
const am_hal_gpio_pincfg_t debug_uart_tx_pinconfig = UART_GPIO_PINCONFIG(DEBUG_UART_INST, TX, DEBUG_TX_PAD);
const am_hal_gpio_pincfg_t debug_uart_rx_pinconfig = UART_GPIO_PINCONFIG(DEBUG_UART_INST, RX, DEBUG_RX_PAD);
const am_hal_uart_config_t debug_uart_config = {
// Standard UART settings: 115200-8-N-1
.ui32BaudRate = DEBUG_BAUD_RATE,
.ui32DataBits = AM_HAL_UART_DATA_BITS_8,
.ui32Parity = AM_HAL_UART_PARITY_NONE,
.ui32StopBits = AM_HAL_UART_ONE_STOP_BIT,
.ui32FlowControl = AM_HAL_UART_FLOW_CTRL_NONE,
// Set TX and RX FIFOs to interrupt at half-full.
.ui32FifoLevels = (AM_HAL_UART_TX_FIFO_1_2 |
AM_HAL_UART_RX_FIFO_1_2),
// Buffers
.pui8TxBuffer = NULL,
.ui32TxBufferSize = 0,
.pui8RxBuffer = NULL,
.ui32RxBufferSize = 0,
};
// Initialize the printf interface for UART output.
am_hal_uart_initialize(DEBUG_UART_INST, &hUART_debug);
am_hal_uart_power_control(hUART_debug, AM_HAL_SYSCTRL_WAKE, false);
am_hal_uart_configure(hUART_debug, &debug_uart_config);
// Disable that pesky FIFO
UARTn(DEBUG_UART_INST)->LCRH_b.FEN = 0;
// Enable the UART pins.
am_hal_gpio_pinconfig(DEBUG_TX_PAD, debug_uart_tx_pinconfig);
am_hal_gpio_pinconfig(DEBUG_RX_PAD, debug_uart_rx_pinconfig);
// Enable interrupts.
NVIC_EnableIRQ((IRQn_Type)(UART0_IRQn + DEBUG_UART_INST));
am_hal_uart_interrupt_enable(hUART_debug, (AM_HAL_UART_INT_RX));
}
void stop_uart_debug(void)
{
// Deinitialize the UART printf interface.
am_hal_uart_power_control(hUART_debug, AM_HAL_SYSCTRL_DEEPSLEEP, false);
am_hal_uart_deinitialize(hUART_debug);
// Re-enable that pesky FIFO
UARTn(DEBUG_UART_INST)->LCRH_b.FEN = 1;
// Disable the UART pins.
am_hal_gpio_pinconfig(DEBUG_TX_PAD, g_AM_HAL_GPIO_DISABLE);
am_hal_gpio_pinconfig(DEBUG_RX_PAD, g_AM_HAL_GPIO_DISABLE);
// Disable interrupts.
NVIC_DisableIRQ((IRQn_Type)(UART0_IRQn + DEBUG_UART_INST));
am_hal_uart_interrupt_disable(hUART_debug, (AM_HAL_UART_INT_RX));
}
#endif // DEBUG
//*****************************************************************************
//
// Setup
//
//*****************************************************************************
void setup(void)
{
// Set the clock frequency.
am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0);
// Set the default cache configuration
am_hal_cachectrl_config(&am_hal_cachectrl_defaults);
am_hal_cachectrl_enable();
// Configure the stimer
am_hal_stimer_int_enable(AM_HAL_STIMER_INT_OVERFLOW);
NVIC_EnableIRQ(STIMER_IRQn);
am_hal_stimer_config(AM_HAL_STIMER_CFG_CLEAR | AM_HAL_STIMER_CFG_FREEZE);
am_hal_stimer_config(AM_HAL_STIMER_HFRC_3MHZ);
#ifdef DEBUG
start_uart_debug();
#endif
// Enable interrupts.
am_hal_interrupt_master_enable();
}
//*****************************************************************************
//
// Un-set-up
//
//*****************************************************************************
void unsetup(void)
{
disable_burst_mode();
// Deconfigure the stimer
am_hal_stimer_int_disable(AM_HAL_STIMER_INT_OVERFLOW);
NVIC_DisableIRQ(STIMER_IRQn);
am_hal_stimer_config(AM_HAL_STIMER_CFG_CLEAR | AM_HAL_STIMER_CFG_FREEZE);
am_hal_stimer_config(AM_HAL_STIMER_NO_CLK);
#ifdef DEBUG
stop_uart_debug();
#endif
// Disable interrupts.
am_hal_interrupt_master_disable();
}
// ****************************************
//
// Baud Rate Detect Phase
//
// ****************************************
bool detect_baud_rate(uint32_t *baud)
{
uint32_t bl_entry_timeout_ms = 200;
uint32_t bl_entry_timeout_start = millis();
bool baud_is_valid = false;
bool timed_out = true;
debug_printf("phase:\tdetect baud rate\n");
enable_burst_mode();
am_hal_gpio_pinconfig(BL_RX_PAD, g_AM_HAL_GPIO_INPUT_PULLUP);
ap3_gpio_enable_interrupts(BL_RX_PAD, AM_HAL_GPIO_PIN_INTDIR_LO2HI);
am_hal_gpio_interrupt_clear(AM_HAL_GPIO_BIT(BL_RX_PAD));
am_hal_gpio_interrupt_enable(AM_HAL_GPIO_BIT(BL_RX_PAD));
NVIC_EnableIRQ(GPIO_IRQn);
while ((millis() - bl_entry_timeout_start) < bl_entry_timeout_ms)
{
// try to detect baud rate
// debug_printf("\ttime (ms):\t%d\n", millis());
if (bl_baud_ticks_index == BL_BAUD_SAMPLES)
{
// compute differences between samples
for (uint8_t indi = 0; indi < (BL_BAUD_SAMPLES - 1); indi++)
{
bl_baud_ticks[indi] = bl_baud_ticks[indi + 1] - bl_baud_ticks[indi];
}
float mean = 0.0;
for (uint8_t indi = 0; indi < (BL_BAUD_SAMPLES - 1); indi++)
{
mean += bl_baud_ticks[indi];
}
mean /= (BL_BAUD_SAMPLES - 1);
if (mean < 3)
{
// invalid
}
else if ((mean >= 4) && (mean <= 8))
{
*baud = 921600;
baud_is_valid = true;
}
else if ((mean >= 10) && (mean <= 14))
{
*baud = 460800;
baud_is_valid = true;
}
else if ((mean >= 25) && (mean <= 30))
{
*baud = 230400;
baud_is_valid = true;
}
else if ((mean >= 45) && (mean <= 55))
{
*baud = 115200;
baud_is_valid = true;
}
else if ((mean >= 91) && (mean <= 111))
{
*baud = 57600;
baud_is_valid = true;
}
else
{
// invalid
}
if (baud_is_valid)
{
timed_out = false;
}
break; // exit the timeout loop
}
}
am_hal_gpio_interrupt_disable(AM_HAL_GPIO_BIT(BL_RX_PAD));
am_hal_gpio_interrupt_clear(AM_HAL_GPIO_BIT(BL_RX_PAD));
NVIC_DisableIRQ(GPIO_IRQn);
disable_burst_mode();
#ifdef DEBUG
// show differences for debugging purposes
debug_printf("\ttiming differences: { ");
for (uint8_t indi = 0; indi < (BL_BAUD_SAMPLES - 1); indi++)
{
debug_printf("%d", bl_baud_ticks[indi]);
if (indi < (BL_BAUD_SAMPLES - 2))
{
debug_printf(", ");
}
}
debug_printf("}\n");
#endif // DEBUG
if (!baud_is_valid)
{
debug_printf("\tbaud rate not detected.\n\t\trising edges:\t%d\n\t\ttimed out:\t%d\n\n", bl_baud_ticks_index, timed_out);
}
else
{
debug_printf("\tdetected valid baud rate:\t%d\n\n", *baud);
}
return baud_is_valid;
}
//*****************************************************************************
//
// Start BL UART at desired baud
//
//*****************************************************************************
void start_uart_bl(uint32_t baud)
{
const am_hal_gpio_pincfg_t bl_uart_tx_pinconfig = UART_GPIO_PINCONFIG(BL_UART_INST, TX, BL_TX_PAD);
const am_hal_gpio_pincfg_t bl_uart_rx_pinconfig = UART_GPIO_PINCONFIG(BL_UART_INST, RX, BL_RX_PAD);
am_hal_uart_config_t bl_uart_config =
{
// Standard UART settings: 115200-8-N-1
.ui32BaudRate = baud,
.ui32DataBits = AM_HAL_UART_DATA_BITS_8,
.ui32Parity = AM_HAL_UART_PARITY_NONE,
.ui32StopBits = AM_HAL_UART_ONE_STOP_BIT,
.ui32FlowControl = AM_HAL_UART_FLOW_CTRL_NONE,
// Set TX and RX FIFOs to interrupt at half-full.
.ui32FifoLevels = (AM_HAL_UART_TX_FIFO_1_2 |
AM_HAL_UART_RX_FIFO_1_2),
// Buffers
.pui8TxBuffer = NULL,
.ui32TxBufferSize = 0,
.pui8RxBuffer = NULL,
.ui32RxBufferSize = 0,
};
// Initialize the printf interface for UART output.
am_hal_uart_initialize(BL_UART_INST, &hUART_bl);
am_hal_uart_power_control(hUART_bl, AM_HAL_SYSCTRL_WAKE, false);
am_hal_uart_configure(hUART_bl, &bl_uart_config);
// Disable that pesky FIFO
UARTn(BL_UART_INST)->LCRH_b.FEN = 0;
// Enable the UART pins.
am_hal_gpio_pinconfig(BL_TX_PAD, bl_uart_tx_pinconfig);
am_hal_gpio_pinconfig(BL_RX_PAD, bl_uart_rx_pinconfig);
// Enable interrupts.
NVIC_EnableIRQ((IRQn_Type)(UART0_IRQn + BL_UART_INST));
am_hal_uart_interrupt_enable(hUART_bl, (AM_HAL_UART_INT_RX));
// Provide SVL Packet interfaces
svl_packet_link_read_fn(art_svl_ringbuf_read, &bl_rx_ringbuf);
svl_packet_link_avail_fn(art_svl_ringbuf_available, &bl_rx_ringbuf);
svl_packet_link_millis_fn(millis);
svl_packet_link_write_fn(svl_uart_write_byte, hUART_bl);
}
// ****************************************
//
// Bootload phase
//
// ****************************************
void enter_bootload(void)
{
enable_burst_mode();
bool done = false;
uint32_t frame_address = 0;
uint16_t last_page_erased = 0;
uint8_t retransmit = 0;
static uint32_t frame_buffer[FRAME_BUFFER_SIZE];
svl_packet_t svl_packet_incoming_frame = {CMD_FRAME, (uint8_t *)frame_buffer, sizeof(frame_buffer) / sizeof(uint8_t), sizeof(frame_buffer) / sizeof(uint8_t)};
svl_packet_t svl_packet_retry = {CMD_RETRY, NULL, 0, 0};
svl_packet_t svl_packet_next = {CMD_NEXT, NULL, 0, 0};
debug_printf("phase:\tbootload\n");
while (!done)
{
if (retransmit != 0)
{
debug_printf("\trequesting retransmission\n");
svl_packet_send((svl_packet_t *)&svl_packet_retry); // Ask to retransmit
}
else
{
debug_printf("\trequesting next app frame\n");
svl_packet_send((svl_packet_t *)&svl_packet_next); // Ask for the next frame packet
}
retransmit = 0;
uint8_t stat = svl_packet_wait(&svl_packet_incoming_frame);
if (stat != 0)
{ // wait for either a frame or the done command
debug_printf("\t\terror receiving packet (%d)\n", stat);
retransmit = 1;
am_util_delay_us(177000); //Worst case: wait 177ms for 2048 byte transfer at 115200bps to complete
//Flush the buffers to remove any inbound or outbound garbage
bl_rx_ringbuf.r_offset = 0;
bl_rx_ringbuf.w_offset = 0;
continue;
}
// debug_printf("Successfully received incoming frame packet (todo: add extra details in debug)\n", stat);
if (svl_packet_incoming_frame.cmd == CMD_FRAME)
{
debug_printf("\t\treceived an app frame\n");
if (handle_frame_packet(&svl_packet_incoming_frame, &frame_address, &last_page_erased) != 0)
{
// debug_printf("\t\t\tbootload error - packet could not be handled\n");
retransmit = 1;
continue;
}
}
else if (svl_packet_incoming_frame.cmd == CMD_DONE)
{
debug_printf("\t\treceived done signal!\n\n");
done = true;
}
else
{
debug_printf("bootload error - unknown command\n");
retransmit = 1;
continue;
}
}
// finish bootloading
}
// ****************************************
//
// Handle a frame packet
//
// ****************************************
uint8_t handle_frame_packet(svl_packet_t *packet, uint32_t *p_frame_address, uint16_t *p_last_page_erased)
{
// debug_printf("\t\thandling frame\n");
uint32_t num_words = (packet->pl_len / 4);
debug_printf("\t\tframe_address = 0x%08X, num_words = %d\n", *(p_frame_address), num_words);
// Check payload length is multiple of words
if ((packet->pl_len % 4))
{
debug_printf("Error: frame packet not integer multiple of words (4 bytes per word)\n");
return 1;
}
int32_t i32ReturnCode = 0;
uint32_t offset_address = (*(p_frame_address) + USERCODE_OFFSET);
if ((*p_last_page_erased) < AM_HAL_FLASH_ADDR2PAGE(offset_address))
{ // Prevent erasing partially-filled pages
// debug_printf("Erasing instance %d, page %d\n\r", AM_HAL_FLASH_ADDR2INST( offset_address ), AM_HAL_FLASH_ADDR2PAGE(offset_address) );
//Erase the 8k page for this address
i32ReturnCode = am_hal_flash_page_erase(AM_HAL_FLASH_PROGRAM_KEY, AM_HAL_FLASH_ADDR2INST(offset_address), AM_HAL_FLASH_ADDR2PAGE(offset_address));
*(p_last_page_erased) = AM_HAL_FLASH_ADDR2PAGE(offset_address);
if (i32ReturnCode)
{
debug_printf("FLASH_MASS_ERASE i32ReturnCode = 0x%x.\n\r", i32ReturnCode);
}
}
//Record the array
//debug_printf("Recording %d words (%d bytes) to memory\n", num_words, 4 * num_words);
i32ReturnCode = am_hal_flash_program_main(AM_HAL_FLASH_PROGRAM_KEY, (uint32_t *)packet->pl, (uint32_t *)(*(p_frame_address) + USERCODE_OFFSET), num_words);
if (i32ReturnCode)
{
debug_printf("FLASH_WRITE error = 0x%x.\n\r", i32ReturnCode);
return 1;
}
*(p_frame_address) += num_words * 4;
// debug_printf("Array recorded to flash\n");
return 0;
}
// ****************************************
//
// Jump to the application
//
// ****************************************
void app_start(void)
{
// debug_printf("\n\t-- app start --\n");
// #ifdef DEBUG
// #ifdef DEBUG_PRINT_APP
// uint32_t start_address = USERCODE_OFFSET; // Print a section of flash
// debug_printf("Printing page starting at offset 0x%04X\n", start_address);
// #ifdef APP_PRINT_PRETTY
// for (uint16_t x = 0; x < 512*APP_PRINT_NUM_PAGE; x++){
// if (x % 8 == 0){
// debug_printf("\nAdr: 0x%04X", start_address + (x * 4));
// }
// debug_printf(" 0x%08X", *(uint32_t *)(start_address + (x * 4)));
// }
// debug_printf("\n");
// #else
// for (uint16_t x = 0; x < 512*APP_PRINT_NUM_PAGE; x++){
// if (x % 4 == 0){
// debug_printf("\n");
// }
// uint32_t wor = *(uint32_t *)(start_address + (x * 4));
// debug_printf("%02x%02x %02x%02x", (wor & 0x000000FF), (wor & 0x0000FF00) >> 8, (wor & 0x00FF0000) >> 16, (wor & 0xFF000000) >> 24 );
// if( (x%4) != 3 ){
// debug_printf(" ");
// }
// }
// debug_printf("\n");
// #endif // APP_PRINT_PRETTY
// #endif // DEBUG_PRINT_APP
// #endif // DEBUG
void *entryPoint = (void *)(*((uint32_t *)(USERCODE_OFFSET + 4)));
debug_printf("\nJump to App at 0x%08X\n\n", (uint32_t)entryPoint);
am_util_delay_ms(10); // Wait for prints to complete
unsetup(); // Undoes configuration to provide users with a clean slate
goto *entryPoint; // Jump to start of user code
}
// ****************************************
//
// Debug printf function
//
// ****************************************
void debug_printf(char *fmt, ...)
{
#ifdef DEBUG
char debug_buffer[DEBUG_UART_BUF_LEN];
va_list args;
va_start(args, fmt);
vsnprintf(debug_buffer, DEBUG_UART_BUF_LEN, (const char *)fmt, args);
va_end(args);
svl_uart_print(hUART_debug, debug_buffer);
#endif //DEBUG
}
//*****************************************************************************
//
// UART interrupt handlers
//
//*****************************************************************************
void am_uart_isr(void)
{
// Service the FIFOs as necessary, and clear the interrupts.
#if BL_UART_INST == 0
uint32_t ui32Status, ui32Idle;
am_hal_uart_interrupt_status_get(hUART_bl, &ui32Status, true);
am_hal_uart_interrupt_clear(hUART_bl, ui32Status);
am_hal_uart_interrupt_service(hUART_bl, ui32Status, &ui32Idle);
if (ui32Status & AM_HAL_UART_INT_RX)
{
uint8_t c = 0x00;
if (svl_uart_read(hUART_bl, (char *)&c, 1) != 0)
{
art_svl_ringbuf_write(&bl_rx_ringbuf, c);
}
}
#else
#ifdef DEBUG
am_hal_uart_interrupt_status_get(hUART_debug, &ui32Status, true);
am_hal_uart_interrupt_clear(hUART_debug, ui32Status);
am_hal_uart_interrupt_service(hUART_debug, ui32Status, &ui32Idle);
#endif // DEBUG
#endif // BL_UART_INST == 0
}
void am_uart1_isr(void)
{
// Service the FIFOs as necessary, and clear the interrupts.
#if BL_UART_INST == 1
uint32_t ui32Status, ui32Idle;
am_hal_uart_interrupt_status_get(hUART_bl, &ui32Status, true);
am_hal_uart_interrupt_clear(hUART_bl, ui32Status);
am_hal_uart_interrupt_service(hUART_bl, ui32Status, &ui32Idle);
if (ui32Status & AM_HAL_UART_INT_RX)
{
uint8_t c = 0x00;
if (read(hUART_bl, &c, 1) != 0)
{
art_svl_ringbuf_write(&bl_rx_ringbuf, c);
}
}
#else
#ifdef DEBUG
uint32_t ui32Status, ui32Idle;
am_hal_uart_interrupt_status_get(hUART_debug, &ui32Status, true);
am_hal_uart_interrupt_clear(hUART_debug, ui32Status);
am_hal_uart_interrupt_service(hUART_debug, ui32Status, &ui32Idle);
#endif // DEBUG
#endif // BL_UART_INST == 0
}
//*****************************************************************************
//
// GPIO interrupt handler
//
//*****************************************************************************
void am_gpio_isr(void)
{
am_hal_gpio_interrupt_clear(AM_HAL_GPIO_BIT(BL_RX_PAD));
if (bl_baud_ticks_index < BL_BAUD_SAMPLES)
{
bl_baud_ticks[bl_baud_ticks_index++] = CTIMER->STTMR;
}
}
//*****************************************************************************
//
// STimer interrupt handler
//
//*****************************************************************************
void am_stimer_isr(void)
{
am_hal_stimer_int_clear(AM_HAL_STIMER_INT_OVERFLOW);
ap3_stimer_overflows += 1;
// At the fastest rate (3MHz) the 64 bits of the stimer
// along with this overflow counter can keep track of
// the time for ~ 195,000 years without wrapping to 0
}
@@ -0,0 +1,240 @@
#include "svl_packet.h"
void *read_param = NULL;
void *write_param = NULL;
void *avail_param = NULL;
svl_packet_read_byte_fn_t read_fn = NULL;
svl_packet_write_byte_fn_t write_fn = NULL;
svl_packet_avail_bytes_fn_t avail_fn = NULL;
svl_packet_millis_fn_t millis_fn = NULL;
uint8_t CRCL, CRCH;
uint16_t CRC_Table[8 * 32] = {
0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202};
//Update CRC with given byte
inline __attribute__((always_inline)) void updateCRC(uint8_t num)
{
uint16_t tableAddr = (num ^ CRCH);
CRCH = (CRC_Table[tableAddr] >> 8) ^ CRCL;
CRCL = (CRC_Table[tableAddr] & 0x00FF);
}
inline __attribute__((always_inline)) size_t svl_packet_read_byte(uint8_t *c)
{
size_t retval = 0x00;
if (read_fn != NULL)
{
retval = read_fn(read_param, c);
}
return retval;
}
inline __attribute__((always_inline)) size_t svl_packet_write_byte(uint8_t c)
{
size_t retval = 0x00;
if (write_fn != NULL)
{
retval = write_fn(write_param, c);
}
return retval;
}
inline __attribute__((always_inline)) size_t svl_packet_avail_bytes(void)
{
size_t retval = 0x00;
if (avail_fn != NULL)
{
retval = avail_fn(avail_param);
}
return retval;
}
inline __attribute__((always_inline)) size_t svl_packet_millis(void)
{
size_t retval = 0x00;
if (millis_fn != NULL)
{
retval = millis_fn();
}
return retval;
}
void svl_packet_link_read_fn(svl_packet_read_byte_fn_t fn, void *param)
{
read_param = param;
read_fn = fn;
}
void svl_packet_link_write_fn(svl_packet_write_byte_fn_t fn, void *param)
{
write_param = param;
write_fn = fn;
}
void svl_packet_link_avail_fn(svl_packet_avail_bytes_fn_t fn, void *param)
{
avail_param = param;
avail_fn = fn;
}
void svl_packet_link_millis_fn(svl_packet_millis_fn_t fn)
{
millis_fn = fn;
}
void svl_packet_send(svl_packet_t *packet)
{
CRCL = 0;
CRCH = 0;
updateCRC(packet->cmd); //Add this byte to CRC
for (uint32_t x = 0; x < packet->pl_len; x++)
{
updateCRC(*(packet->pl + x)); //Add this byte to CRC
}
svl_packet_write_byte(((packet->pl_len + 3) >> 8)); // len high byte (including command and CRC bytes)
svl_packet_write_byte(((packet->pl_len + 3) & 0xFF)); // len low byte (including command and CRC bytes)
svl_packet_write_byte((packet->cmd)); // command byte
if ((packet->pl != NULL) && (packet->pl_len != 0))
{
for (uint16_t indi = 0; indi < packet->pl_len; indi++)
{ // payload
svl_packet_write_byte(*(packet->pl + indi));
}
}
svl_packet_write_byte(CRCH); // CRC H
svl_packet_write_byte(CRCL); // CRC L
}
uint8_t svl_packet_wait(svl_packet_t *packet)
{
// wait for 2 bytes (the length bytes)
// wait for length bytes to come in
// make sure that 'length' bytes are enough to satisfy the desired payload length
if (packet == NULL)
{
return (SVL_PACKET_ERR);
}
const uint8_t num_bytes_length = 2;
if (svl_packet_wait_bytes(num_bytes_length))
{
return (SVL_PACKET_ERR_TIMEOUT | SVL_PACKET_LEN);
}
uint16_t len = svl_packet_get_uint16_t();
if (len == 0)
{
return (SVL_PACKET_ERR_ZLP);
}
if ((len - 3) > packet->max_pl_len)
{
return (SVL_PACKET_ERR_MEM | SVL_PACKET_PL);
}
//Wait for entire packet to come in
if (svl_packet_wait_bytes(len))
return (SVL_PACKET_ERR_TIMEOUT | SVL_PACKET_PL);
uint8_t incoming;
CRCL = 0;
CRCH = 0;
//Get command byte
svl_packet_read_byte(&incoming);
packet->cmd = incoming;
updateCRC(incoming); //Add this byte to CRC
packet->pl_len = (len - 3);
//Now read the data coming in
if ((packet->pl != NULL) && (packet->max_pl_len != 0))
{
for (uint32_t x = 0; x < packet->pl_len; x++)
{
svl_packet_read_byte(&incoming);
updateCRC(incoming); //Add this byte to CRC
*(packet->pl + x) = incoming; //Fill payload with data
}
}
uint16_t crc = svl_packet_get_uint16_t(); //Read final two bytes into CRC
uint16_t check = ((uint16_t)CRCH << 8) | CRCL;
if (crc != check)
{
return (SVL_PACKET_ERR_CRC);
}
return (SVL_PACKET_OK);
}
uint16_t svl_packet_get_uint16_t(void)
{
uint8_t h = 0x00;
uint8_t l = 0x00;
svl_packet_read_byte(&h);
svl_packet_read_byte(&l);
return (((uint16_t)h << 8) | (l & 0xFF));
}
uint8_t svl_packet_wait_bytes(uint32_t num)
{
uint32_t timeout_ms = 500;
uint32_t start = svl_packet_millis();
uint32_t avail = 0;
while ((svl_packet_millis() - start) < timeout_ms)
{
avail = svl_packet_avail_bytes();
if (avail >= num)
{
return 0;
}
}
// debug_printf("only got %d bytes...\n",avail);
return 1;
}
@@ -0,0 +1,47 @@
#ifndef _SVL_PACKET_H_
#define _SVL_PACKET_H_
#include "stdint.h"
#include "stdlib.h"
#include "stdbool.h"
typedef struct _svl_packet_t
{ // An SVL3 packet consists of 5+N bytes. N is the length of the data payload, and there are 5 bytes that are always transmitted
// len // 2 - length of the remainder of the packet (pllen + 3) (note, this is automatically calculated)
uint8_t cmd; // 1 - The command
uint8_t *pl; // N - The payload (pointer)
uint16_t pl_len; // - Length of the payload in bytes (note, this is not transmitted across the line, just used internally)
// crc // 2 - CRC16 on the command and the payload. poly = 0x8005, nothing extra or fancy. Byte order MSB first, bit order MSB first
uint16_t max_pl_len; // - This is the number of bytes pointed to by 'pl'
} svl_packet_t;
enum
{
SVL_PACKET_OK = 0x00,
SVL_PACKET_ERR = 0x01, // general error
SVL_PACKET_ERR_TIMEOUT = 0x02, // timeout
SVL_PACKET_ERR_ZLP = 0x04, // zero length packet
SVL_PACKET_ERR_MEM = 0x08, // not enough space to receive packet
SVL_PACKET_ERR_CRC = 0x10, // crc mismatch
SVL_PACKET_LEN = 0x80, // flag indicating 'len' header
SVL_PACKET_PL = 0x40, // flag indicating payload
};
typedef size_t (*svl_packet_read_byte_fn_t)(void *, uint8_t *);
typedef size_t (*svl_packet_write_byte_fn_t)(void *, uint8_t);
typedef size_t (*svl_packet_avail_bytes_fn_t)(void *);
typedef size_t (*svl_packet_millis_fn_t)(void);
void svl_packet_link_read_fn(svl_packet_read_byte_fn_t fn, void *param);
void svl_packet_link_write_fn(svl_packet_write_byte_fn_t fn, void *param);
void svl_packet_link_avail_fn(svl_packet_avail_bytes_fn_t fn, void *param);
void svl_packet_link_millis_fn(svl_packet_millis_fn_t fn);
void svl_packet_send(svl_packet_t *packet);
uint8_t svl_packet_wait(svl_packet_t *packet);
uint16_t svl_packet_get_uint16_t(void);
uint8_t svl_packet_wait_bytes(uint32_t num);
#endif // _SVL_PACKET_H_
@@ -0,0 +1,69 @@
#include "svl_ringbuf.h"
size_t art_svl_ringbuf_init( void* vrb, uint8_t* buf, size_t len ){
if( vrb == NULL ){ return 0; }
art_svl_ringbuf_t* rb = (art_svl_ringbuf_t*)vrb;
rb->buf = buf;
rb->len = len;
rb->r_offset = 0;
rb->w_offset = 0;
return rb->len;
}
size_t art_svl_ringbuf_available( void* vrb ){
if( vrb == NULL ){ return 0; }
art_svl_ringbuf_t* rb = (art_svl_ringbuf_t*)vrb;
size_t avail = 0x00;
if((rb->w_offset) >= (rb->r_offset)){
avail = rb->w_offset - rb->r_offset;
}else{
avail = rb->len - (rb->r_offset - rb->w_offset);
}
return avail;
}
size_t art_svl_ringbuf_bytes_free( void* vrb ){
if( vrb == NULL ){ return 0; }
art_svl_ringbuf_t* rb = (art_svl_ringbuf_t*)vrb;
size_t friegh = 0x00;
if((rb->w_offset) >= (rb->r_offset)){
friegh = rb->len - rb->w_offset + rb->r_offset -1;
}else{
friegh = rb->r_offset - rb->w_offset - 1;
}
return friegh;
}
size_t art_svl_ringbuf_write( void* vrb, uint8_t c ){
if( vrb == NULL ){ return 0; }
art_svl_ringbuf_t* rb = (art_svl_ringbuf_t*)vrb;
if(art_svl_ringbuf_bytes_free(rb) > 0){
*(rb->buf + rb->w_offset) = c;
rb->w_offset++;
if(rb->w_offset >= rb->len){
rb->w_offset = 0;
}
return 1;
}
return 0;
}
size_t art_svl_ringbuf_read( void* vrb, uint8_t* c ){
if( vrb == NULL ){ return 0; }
art_svl_ringbuf_t* rb = (art_svl_ringbuf_t*)vrb;
if(art_svl_ringbuf_available(rb) > 0){
*c = *(rb->buf + rb->r_offset);
rb->r_offset++;
if(rb->r_offset >= rb->len){
rb->r_offset = 0;
}
return 1;
}
return 0;
}
@@ -0,0 +1,20 @@
#ifndef _SVL_RINGBUF_H_
#define _SVL_RINGBUF_H_
#include "stdio.h"
typedef struct _art_svl_ringbuf_t {
uint8_t* buf;
size_t len;
volatile size_t r_offset;
volatile size_t w_offset;
}art_svl_ringbuf_t;
size_t art_svl_ringbuf_init ( void* rb, uint8_t* buf, size_t len );
size_t art_svl_ringbuf_available ( void* rb );
size_t art_svl_ringbuf_bytes_free ( void* rb );
size_t art_svl_ringbuf_write ( void* rb, uint8_t c );
size_t art_svl_ringbuf_read ( void* rb, uint8_t* c );
#endif // _SVL_RINGBUF_H_
@@ -0,0 +1,70 @@
#include "svl_uart.h"
//*****************************************************************************
//
// UART read buffer
//
//*****************************************************************************
size_t svl_uart_read(void *pHandle, char* buf, size_t len){
uint32_t ui32BytesRead = 0x00;
am_hal_uart_transfer_t sRead = {
.ui32Direction = AM_HAL_UART_READ,
.pui8Data = (uint8_t*)buf,
.ui32NumBytes = len,
.ui32TimeoutMs = 0,
.pui32BytesTransferred = &ui32BytesRead,
};
am_hal_uart_transfer(pHandle, &sRead);
return ui32BytesRead;
}
//*****************************************************************************
//
// UART write buffer
//
//*****************************************************************************
size_t svl_uart_write(void *pHandle, char* buf, size_t len){
uint32_t ui32BytesWritten = 0;
const am_hal_uart_transfer_t sUartWrite =
{
.ui32Direction = AM_HAL_UART_WRITE,
.pui8Data = (uint8_t*) buf,
.ui32NumBytes = len,
.ui32TimeoutMs = AM_HAL_UART_WAIT_FOREVER,
.pui32BytesTransferred = &ui32BytesWritten,
};
am_hal_uart_transfer(pHandle, &sUartWrite);
return ui32BytesWritten;
}
//*****************************************************************************
//
// UART write byte
//
//*****************************************************************************
size_t svl_uart_write_byte(void *pHandle, uint8_t c){
return svl_uart_write(pHandle, (char*)&c, 1);
}
//*****************************************************************************
//
// UART send string
//
//*****************************************************************************
size_t svl_uart_print(void *pHandle, char* str){
uint32_t ui32StrLen = 0;
while (str[ui32StrLen] != 0){ ui32StrLen++; } // Measure the length of the string.
return svl_uart_write( pHandle, str, ui32StrLen);
// uint16_t indi = 0;
// while((*(debug_buffer+indi)!='\0') && (indi < DEBUG_UART_BUF_LEN)){
// svl_uart_write(hUART_debug, debug_buffer+indi, 1);
// indi++;
// }
}
@@ -0,0 +1,15 @@
#ifndef _SVL_UART_H_
#define _SVL_UART_H_
#include "am_mcu_apollo.h"
#include "am_bsp.h"
#include "am_util.h"
size_t svl_uart_read (void *pHandle, char* buf, size_t len);
size_t svl_uart_write (void *pHandle, char* buf, size_t len);
size_t svl_uart_write_byte (void *pHandle, uint8_t c);
size_t svl_uart_print (void *pHandle, char* str);
#endif // _SVL_UART_H_
@@ -0,0 +1,119 @@
#include "svl_utils.h"
#define AP3_STIMER_FREQ_HZ (3000000)
#define AP3_STIMER_FREQ_KHZ (AP3_STIMER_FREQ_HZ / 1000)
#define AP3_STIMER_FREQ_MHZ (AP3_STIMER_FREQ_HZ / 1000000)
volatile uint32_t ap3_stimer_overflows = 0x00;
uint64_t ticks = 0;
void _fill_ticks(void)
{
ticks = ap3_stimer_overflows;
ticks <<= 32;
ticks |= (am_hal_stimer_counter_get() & 0xFFFFFFFF);
}
size_t millis(void){
_fill_ticks();
return (uint32_t)(ticks / AP3_STIMER_FREQ_KHZ);
}
//*****************************************************************************
//
// Burst mode
//
//*****************************************************************************
bool enable_burst_mode(void)
{
// Check that the Burst Feature is available.
am_hal_burst_avail_e eBurstModeAvailable;
if (AM_HAL_STATUS_SUCCESS != am_hal_burst_mode_initialize(&eBurstModeAvailable))
{
return (false);
}
// Put the MCU into "Burst" mode.
am_hal_burst_mode_e eBurstMode;
if (AM_HAL_STATUS_SUCCESS != am_hal_burst_mode_enable(&eBurstMode))
{
return (false);
}
return (true);
}
//Turns main processor from 96MHz to 48MHz
//Returns false if disable fails
bool disable_burst_mode(void)
{
am_hal_burst_mode_e eBurstMode;
if (AM_HAL_STATUS_SUCCESS == am_hal_burst_mode_disable(&eBurstMode))
{
if (AM_HAL_NORMAL_MODE != eBurstMode)
{
return (false);
}
}
else
{
return (false);
}
return (true);
}
//*****************************************************************************
// Local defines. Copied from am_hal_gpio.c
//*****************************************************************************
//
// Generally define GPIO PADREG and GPIOCFG bitfields
//
#define PADREG_FLD_76_S 6
#define PADREG_FLD_FNSEL_S 3
#define PADREG_FLD_DRVSTR_S 2
#define PADREG_FLD_INPEN_S 1
#define PADREG_FLD_PULLUP_S 0
#define GPIOCFG_FLD_INTD_S 3
#define GPIOCFG_FLD_OUTCFG_S 1
#define GPIOCFG_FLD_INCFG_S 0
uint32_t ap3_gpio_enable_interrupts(uint32_t ui32Pin, uint32_t eIntDir){
uint32_t ui32Padreg, ui32AltPadCfg, ui32GPCfg;
bool bClearEnable = false;
ui32GPCfg = ui32Padreg = ui32AltPadCfg = 0;
ui32GPCfg |= (((eIntDir >> 0) & 0x1) << GPIOCFG_FLD_INTD_S) | (((eIntDir >> 1) & 0x1) << GPIOCFG_FLD_INCFG_S);
uint32_t ui32GPCfgAddr;
uint32_t ui32GPCfgClearMask;
uint32_t ui32GPCfgShft;
ui32GPCfgShft = ((ui32Pin & 0x7) << 2);
ui32GPCfgAddr = AM_REGADDR(GPIO, CFGA) + ((ui32Pin >> 1) & ~0x3);
ui32GPCfgClearMask = ~((uint32_t)0xF << ui32GPCfgShft);
ui32GPCfg <<= ui32GPCfgShft;
AM_CRITICAL_BEGIN
if (bClearEnable)
{
am_hal_gpio_output_tristate_disable(ui32Pin);
}
GPIO->PADKEY = GPIO_PADKEY_PADKEY_Key;
// Here's where the magic happens
AM_REGVAL(ui32GPCfgAddr) = (AM_REGVAL(ui32GPCfgAddr) & ui32GPCfgClearMask) | ui32GPCfg;
GPIO->PADKEY = 0;
AM_CRITICAL_END
return AM_HAL_STATUS_SUCCESS;
}
@@ -0,0 +1,18 @@
#ifndef _SVL_UTILS_H_
#define _SVL_UTILS_H_
#include "am_mcu_apollo.h"
#include "am_bsp.h"
#include "am_util.h"
#include "stdint.h"
void _fill_ticks(void);
size_t millis(void);
bool enable_burst_mode(void);
bool disable_burst_mode(void);
uint32_t ap3_gpio_enable_interrupts(uint32_t ui32Pin, uint32_t eIntDir);
extern volatile uint32_t ap3_stimer_overflows;
#endif // _SVL_UTILS_H_
Vendored Executable
BIN
View File
Binary file not shown.
Vendored Executable
BIN
View File
Binary file not shown.
Vendored Executable
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
###### Requirements without Version Specifiers ######`
pyinstaller == 3.6
pyserial
+421
View File
@@ -0,0 +1,421 @@
#!/usr/bin/env python
# SparkFun Variable Loader
# Variable baud rate bootloader for Artemis Apollo3 modules
# Immediately upon reset the Artemis module will search for the timing character
# to auto-detect the baud rate. If a valid baud rate is found the Artemis will
# respond with the bootloader version packet
# If the computer receives a well-formatted version number packet at the desired
# baud rate it will send a command to begin bootloading. The Artemis shall then
# respond with the a command asking for the next frame.
# The host will then send a frame packet. If the CRC is OK the Artemis will write
# that to memory and request the next frame. If the CRC fails the Artemis will
# discard that data and send a request to re-send the previous frame.
# This cycle repeats until the Artemis receives a done command in place of the
# requested frame data command.
# The initial baud rate determination must occur within some small timeout. Once
# baud rate detection has completed all additional communication will have a
# universal timeout value. Once the Artemis has begun requesting data it may no
# no longer exit the bootloader. If the host detects a timeout at any point it
# will stop bootloading.
# Notes about PySerial timeout:
# The timeout operates on whole functions - that is to say that a call to
# ser.read(10) will return after ser.timeout, just as will ser.read(1) (assuming
# that the necessary bytes were not found)
# If there are no incoming bytes (on the line or in the buffer) then two calls to
# ser.read(n) will time out after 2*ser.timeout
# Incoming UART data is buffered behind the scenes, probably by the OS.
# ***********************************************************************************
#
# Imports
#
# ***********************************************************************************
import argparse
import serial
import serial.tools.list_ports as list_ports
import sys
import time
import math
import os.path
from sys import exit
SCRIPT_VERSION_MAJOR = "1"
SCRIPT_VERSION_MINOR = "7"
# ***********************************************************************************
#
# Commands
#
# ***********************************************************************************
SVL_CMD_VER = 0x01 # version
SVL_CMD_BL = 0x02 # enter bootload mode
SVL_CMD_NEXT = 0x03 # request next chunk
SVL_CMD_FRAME = 0x04 # indicate app data frame
SVL_CMD_RETRY = 0x05 # request re-send frame
SVL_CMD_DONE = 0x06 # finished - all data sent
barWidthInCharacters = 50 # Width of progress bar, ie [###### % complete
crcTable = (
0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202)
# ***********************************************************************************
#
# Compute CRC on a byte array
#
# ***********************************************************************************
def get_crc16(data):
# Table and code ported from Artemis SVL bootloader
crc = 0x0000
data = bytearray(data)
for ch in data:
tableAddr = ch ^ (crc >> 8)
CRCH = (crcTable[tableAddr] >> 8) ^ (crc & 0xFF)
CRCL = crcTable[tableAddr] & 0x00FF
crc = CRCH << 8 | CRCL
return crc
# ***********************************************************************************
#
# Wait for a packet
#
# ***********************************************************************************
def wait_for_packet(ser):
packet = {'len': 0, 'cmd': 0, 'data': 0, 'crc': 1, 'timeout': 1}
n = ser.read(2) # get the number of bytes
if(len(n) < 2):
return packet
packet['len'] = int.from_bytes(n, byteorder='big', signed=False) #
payload = ser.read(packet['len'])
if(len(payload) != packet['len']):
return packet
# all bytes received, so timeout is not true
packet['timeout'] = 0
# cmd is the first byte of the payload
packet['cmd'] = payload[0]
# the data is the part of the payload that is not cmd or crc
packet['data'] = payload[1:packet['len']-2]
# performing the crc on the whole payload should return 0
packet['crc'] = get_crc16(payload)
return packet
# ***********************************************************************************
#
# Send a packet
#
# ***********************************************************************************
def send_packet(ser, cmd, data):
data = bytearray(data)
num_bytes = 3 + len(data)
payload = bytearray(cmd.to_bytes(1, 'big'))
payload.extend(data)
crc = get_crc16(payload)
payload.extend(bytearray(crc.to_bytes(2, 'big')))
ser.write(num_bytes.to_bytes(2, 'big'))
ser.write(bytes(payload))
# ***********************************************************************************
#
# Setup: signal baud rate, get version, and command BL enter
#
# ***********************************************************************************
def phase_setup(ser):
baud_detect_byte = b'U'
verboseprint('\nPhase:\tSetup')
# Handle the serial startup blip
ser.reset_input_buffer()
verboseprint('\tCleared startup blip')
ser.write(baud_detect_byte) # send the baud detection character
packet = wait_for_packet(ser)
if(packet['timeout'] or packet['crc']):
return False # failed to enter bootloader
twopartprint('\t', 'Got SVL Bootloader Version: ' +
str(int.from_bytes(packet['data'], 'big')))
verboseprint('\tSending \'enter bootloader\' command')
send_packet(ser, SVL_CMD_BL, b'')
return True
# Now enter the bootload phase
# ***********************************************************************************
#
# Bootloader phase (Artemis is locked in)
#
# ***********************************************************************************
def phase_bootload(ser):
startTime = time.time()
frame_size = 512*4
resend_max = 4
resend_count = 0
verboseprint('\nPhase:\tBootload')
with open(args.binfile, mode='rb') as binfile:
application = binfile.read()
total_len = len(application)
total_frames = math.ceil(total_len/frame_size)
curr_frame = 0
progressChars = 0
if (not args.verbose):
print("[", end='')
verboseprint('\thave ' + str(total_len) +
' bytes to send in ' + str(total_frames) + ' frames')
bl_done = False
bl_succeeded = True
while((bl_done == False) and (bl_succeeded == True)):
# wait for indication by Artemis
packet = wait_for_packet(ser)
if(packet['timeout'] or packet['crc']):
verboseprint('\n\tError receiving packet')
verboseprint(packet)
verboseprint('\n')
bl_succeeded = False
bl_done = True
if(packet['cmd'] == SVL_CMD_NEXT):
# verboseprint('\tgot frame request')
curr_frame += 1
resend_count = 0
elif(packet['cmd'] == SVL_CMD_RETRY):
verboseprint('\t\tRetrying...')
resend_count += 1
if(resend_count >= resend_max):
bl_succeeded = False
bl_done = True
else:
print('Timeout or unknown error')
bl_succeeded = False
bl_done = True
if(curr_frame <= total_frames):
frame_data = application[(
(curr_frame-1)*frame_size):((curr_frame-1+1)*frame_size)]
if(args.verbose):
verboseprint('\tSending frame #'+str(curr_frame) +
', length: '+str(len(frame_data)))
else:
percentComplete = curr_frame * 100 / total_frames
percentCompleteInChars = math.ceil(
percentComplete / 100 * barWidthInCharacters)
while(progressChars < percentCompleteInChars):
progressChars = progressChars + 1
print('#', end='', flush=True)
if (percentComplete == 100):
print("]", end='')
send_packet(ser, SVL_CMD_FRAME, frame_data)
else:
send_packet(ser, SVL_CMD_DONE, b'')
bl_done = True
if(bl_succeeded == True):
twopartprint('\n\t', 'Upload complete')
endTime = time.time()
bps = total_len / (endTime - startTime)
verboseprint('\n\tNominal bootload bps: ' + str(round(bps, 2)))
else:
twopartprint('\n\t', 'Upload failed')
return bl_succeeded
# ***********************************************************************************
#
# Help if serial port could not be opened
#
# ***********************************************************************************
def phase_serial_port_help():
devices = list_ports.comports()
# First check to see if user has the given port open
for dev in devices:
if(dev.device.upper() == args.port.upper()):
print(dev.device + " is currently open. Please close any other terminal programs that may be using " +
dev.device + " and try again.")
exit()
# otherwise, give user a list of possible com ports
print(args.port.upper() +
" not found but we detected the following serial ports:")
for dev in devices:
if 'CH340' in dev.description:
print(
dev.description + ": Likely an Arduino or derivative. Try " + dev.device + ".")
elif 'FTDI' in dev.description:
print(
dev.description + ": Likely an Arduino or derivative. Try " + dev.device + ".")
elif 'USB Serial Device' in dev.description:
print(
dev.description + ": Possibly an Arduino or derivative.")
else:
print(dev.description)
# ***********************************************************************************
#
# Main function
#
# ***********************************************************************************
def main():
try:
num_tries = 3
print('\n\nArtemis SVL Bootloader')
verboseprint("Script version " + SCRIPT_VERSION_MAJOR +
"." + SCRIPT_VERSION_MINOR)
if not os.path.exists(args.binfile):
print("Bin file {} does not exist.".format(args.binfile))
exit()
bl_success = False
entered_bootloader = False
for _ in range(num_tries):
with serial.Serial(args.port, args.baud, timeout=args.timeout) as ser:
# startup time for Artemis bootloader (experimentally determined - 0.095 sec min delay)
t_su = 0.15
time.sleep(t_su) # Allow Artemis to come out of reset
# Perform baud rate negotiation
entered_bootloader = phase_setup(ser)
if(entered_bootloader == True):
bl_success = phase_bootload(ser)
if(bl_success == True): # Bootload
#print("Bootload complete!")
break
else:
verboseprint("Failed to enter bootload phase")
if(bl_success == True):
break
if(entered_bootloader == False):
print(
"Target failed to enter bootload mode. Verify the right COM port is selected and that your board has the SVL bootloader.")
except serial.SerialException:
phase_serial_port_help()
exit()
# ******************************************************************************
#
# Main program flow
#
# ******************************************************************************
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='SparkFun Serial Bootloader for Artemis')
parser.add_argument('port', help='Serial COMx Port')
parser.add_argument('-b', dest='baud', default=115200, type=int,
help='Baud Rate (default is 115200)')
parser.add_argument('-f', dest='binfile', default='',
help='Binary file to program into the target device')
parser.add_argument("-v", "--verbose", default=0, help="Enable verbose output",
action="store_true")
parser.add_argument("-t", "--timeout", default=0.50, help="Communication timeout in seconds (default 0.5)",
type=float)
if len(sys.argv) < 2:
print("No port selected. Detected Serial Ports:")
devices = list_ports.comports()
for dev in devices:
print(dev.description)
args = parser.parse_args()
# Create print function for verbose output if caller deems it: https://stackoverflow.com/questions/5980042/how-to-implement-the-verbose-or-v-option-into-a-script
if args.verbose:
def verboseprint(*args):
# Print each argument separately so caller doesn't need to
# stuff everything to be printed into a single string
for arg in args:
print(arg, end='', flush=True),
print()
else:
verboseprint = lambda *a: None # do-nothing function
def twopartprint(verbosestr, printstr):
if args.verbose:
print(verbosestr, end='')
print(printstr)
main()