Skip to content

Commit

Permalink
Replace uf2 creation code with upstream uf2tool
Browse files Browse the repository at this point in the history
Replace `atomvm_uf2create_provider` uf2 creation code with upstream standalone
`uf2tool` from https://github.com/pguyot/uf2tool.

Signed-off-by: Winford <winford@object.stream>
  • Loading branch information
UncleGrumpy committed Sep 24, 2024
1 parent 2eb0eec commit 6be6585
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 84 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Using the `-s init` option is still supported but deprecated. Use the `--application` (or `-a`) option to generate OTP applications using AtomVM.
- Replace `atomvm_uf2create_provider` uf2 creation code with [upstream `uf2tool`](https://github.com/pguyot/uf2tool)

## [0.7.3] (2023.11.25)

Expand Down
45 changes: 32 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ file for this task. Use `packbeam` as the key for any properties defined for th
| Key | Type | Description |
|-----|------|-------------|
| `force` | `boolean()` | Always force a rebuild of the AVM file, even if up to date |
| `prune` | `boolean()` | Prune unecessary BEAM files from generated AVM |
| `prune` | `boolean()` | Prune unnecessary BEAM files from generated AVM |
| `start` | `atom()` | The start module |
| `remove_lines` | `boolean()` | Remove line number information from generated AVM files. |
| `list` | `boolean()` | List the AVM file contents when generating AVM files. |
Expand Down Expand Up @@ -205,7 +205,7 @@ For example, a module that implements the OTP `application` behavior might look

(assume `myapp_sup` is also a part of your OTP application).

And the application configuration file (e.g., `myapp.app.src`) should include the application mdoule (`myapp_app`) under it's `mod` entry:
And the application configuration file (e.g., `myapp.app.src`) should include the application module (`myapp_app`) under it's `mod` entry:

{
application, myapp, [
Expand Down Expand Up @@ -297,7 +297,7 @@ The following table enumerates the properties that may be defined in your projec
| Key | Type | Description |
|-----|------|-------------|
| `esptool` | `string()` | Path to the `esptool.py` tool, if not already in user's `PATH` |
| `chip` | `string()` | ESP32 chipt type |
| `chip` | `string()` | ESP32 chip type |
| `port` | `string()` | Device port on which the ESP32 can be located |
| `baud` | `integer()` | Device BAUD rate |
| `offset` | `string()` | Offset into which to write AtomVM application |
Expand Down Expand Up @@ -343,7 +343,7 @@ You may use the `stm32_flash` task to flash the generated AtomVM packbeam applic
-s, --stflash Path to st-flash
-o, --offset Offset (default 0x8080000)

The `stm32_flash` will use the `st-flash` tool from the open source (bsd-3 liscensed) [stlink](https://github.com/stlink-org/stlink) suite of stm32 utilites to flash the STM32 device. This tool is available on [github](https://github.com/stlink-org/stlink), and in many package managers.
The `stm32_flash` will use the `st-flash` tool from the open source (bsd-3 licensed) [stlink](https://github.com/stlink-org/stlink) suite of stm32 utilities to flash the STM32 device. This tool is available on [github](https://github.com/stlink-org/stlink), and in many package managers.

By default, the `stm32_flash` task will assume the `st-flash` command is available on the user's executable path. Alternatively, you may specify the full path to the `st-flash` command via the `-s` (or `--stflash`) option

Expand Down Expand Up @@ -452,11 +452,13 @@ Example:

Alternatively, the following environment variables may be used to control the above settings:

* `ATOMVM_REBAR3_PLUGIN_PICO_MOUNT_PATH`
* `ATOMVM_REBAR3_PLUGIN_PICO_RESET_DEV`
* `ATOMVM_REBAR3_PLUGIN_PICO_MOUNT_PATH` | `ATOMVM_PICO_MOUNT_PATH`
* `ATOMVM_REBAR3_PLUGIN_PICO_RESET_DEV` | `ATOMVM_PICO_RESET_DEV`

Any setting specified on the command line take precedence over entries in `rebar.config`, which in turn take precedence over environment variable settings, which in turn take precedence over the default values specified above.

> Note the setting `ATOMVM_REBAR3_PLUGIN_PICO_MOUNT_PATH` and `ATOMVM_REBAR3_PLUGIN_PICO_RESET_PATH` take precedence, but `ATOMVM_PICO_MOUNT_PATH` and `ATOMVM_PICO_RESET_DEV` are also honoured as a fallback, so that the same environment setting can be shared with the [ExAtomVM](https://github.com/atomvm/exatomvm) `mix` plugin.
The `pico_flash` task depends on the `uf2create` task (which in turn depends on the `packbeam` task), so the so the application will be packed and re-formatted if any changes have been made to dependencies.

### The `uf2create` task
Expand All @@ -467,35 +469,52 @@ The `uf2create` task is used to generated an uf2 binary suitable for running on

Use this plugin to create Raspberry Pico uf2 files from an AtomVM packbeam file.

Usage: rebar3 atomvm uf2create [-o <output>] [-s <start>] [-i <input>]
Usage: rebar3 atomvm uf2create [-f <family_id>] [-o <output>]
[-s <start>] [-i <input>]

-o, --output Output path/name
-s, --start Start address for the uf2 binary (default 0x10180000)
-i, --input Input avm file to convert to uf2
-f, --family_id Flavor of uf2 file to create (default rp2040)
-o, --output Output path/name
-s, --start Start address for the uf2 binary (default 0x10180000)
-i, --input Input avm file to convert to uf2

It should not be necessary to use this tool before using `pico_flash`, unless you have built a custom VM that requires changing the start address of the uf2 binary. If the application has not been compiled, or packed with packbeam, these steps will be run first using the default settings for `packbeam`.

The following table enumerates the valid `family_id` options:

| Key | Description |
|-------------|-------------|
| `rp2040` | Original Raspberry Pi Pico and Pico-W |
| `rp2035` | Raspberry Pi Pico 2 |
| `data` | Raspberry Pi Pico 2 |
| `universal` | Universal format for both `rp2040` and `rp2035` |

> Note the convenience of universal uf2 binaries comes with the expense of being twice the size, as both versions are included in the universal uf2.
The following table enumerates the properties that may be defined in your project's `rebar.config` file for this task. Use `uf2create` as the key for any properties defined for this task.

| Key | Type | Description |
|-----|------|-------------|
| `start` | `string()` | Start address for the uf2 binary |
| `family_id` | `string()` | The family\_id or flavor of uf2 to build |

Example:

{atomvm_rebar3_plugin, [{uf2create, [{start, "0x10180000"}]}]}.
{atomvm_rebar3_plugin, [{uf2create, [{app_id, "0x10180000"},{family_id,universal}]}]}.

Alternatively, the following environment variables may be used to control the above settings:

* `ATOMVM_REBAR3_PLUGIN_UF2CREATE_START`
* `ATOMVM_REBAR3_PLUGIN_UF2CREATE_START` | `ATOMVM_PICO_APP_START`
* `ATOMVM_REBAR3_PLUGIN_UF2_FAMILY` | `ATOMVM_PICO_UF2_FAMILY`

> Note the settings `ATOMVM_REBAR3_PLUGIN_UF2CREATE_START` and `ATOMVM_REBAR3_PLUGIN_UF2_FAMILY` take precedence, but `ATOMVM_PICO_APP_START` and`ATOMVM_PICO_UF2_FAMILY` will also be used, if set, so that the same environment setting can be shared with the [ExAtomVM](https://github.com/atomvm/exatomvm) `mix` plugin.
Any setting specified on the command line take precedence over entries in `rebar.config`, which in turn take precedence over environment variable settings, which in turn take precedence over the default values specified above.

The `uf2create` task depends on the `packbeam` task, so the packbeam file will get automatically built if any changes have been made to its dependencies.

### The `version` task

use the `version` task to print the current verison of the [`atomvm_rebar3_plugin`](https://atomvm.github.io/atomvm_rebar3_plugin) to the console.
use the `version` task to print the current version of the [`atomvm_rebar3_plugin`](https://atomvm.github.io/atomvm_rebar3_plugin) to the console.

shell$ rebar3 atomvm version
0.7.3
Expand Down
5 changes: 4 additions & 1 deletion rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
%%
{erl_opts, [debug_info]}.

{deps, [{atomvm_packbeam, "0.7.2"}]}.
{deps, [
{atomvm_packbeam, "0.7.2"},
{uf2tool, {git, "https://github.com/pguyot/uf2tool.git", {tag, "v1.1.0"}}}
]}.

{ex_doc, [
{source_url, <<"https://github.com/atomvm/atomvm_rebar3_plugin">>},
Expand Down
6 changes: 4 additions & 2 deletions src/atomvm_pico_flash_provider.erl
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ get_reset_base() ->
"/dev/cu.usbmodem14*";
_Other ->
""
end.
end,
os:getenv("ATOMVM_PICO_RESET_DEV", Base).

%% @private
get_default_mount() ->
Expand All @@ -144,7 +145,8 @@ get_default_mount() ->
"/Volumes/RPI-RP2";
_Other ->
""
end.
end,
os:getenv("ATOMVM_PICO_MOUNT_PATH", Default).

%% @private
wait_for_mount(Mount, Count) when Count < 30 ->
Expand Down
94 changes: 26 additions & 68 deletions src/atomvm_uf2create_provider.erl
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
%
% This file is part of AtomVM.
%
% Copyright 2022 Paul Guyot <pguyot@kallisys.net>
%
% Adapted for atomvm_rebar3_plugin:
% Copyright 2023 Winford (UncleGrumpy) <winford@object.stream>
% Copyright 2023-24 Winford (UncleGrumpy) <winford@object.stream>
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -32,13 +29,15 @@
-define(PROVIDER, uf2create).
-define(DEPS, [packbeam]).
-define(OPTS, [
{family_id, $f, "family_id", string, "Flavor of uf2 file to create (default rp2040)"},
{output, $o, "output", string, "Output path/name"},
{start, $s, "start", string, "Start address for the uf2 binary (default 0x10180000)"},
{input, $i, "input", string, "Input avm file to convert to uf2"}
]).

-define(DEFAULT_OPTS, #{
start => "0x10180000"
start => os:getenv("ATOMVM_PICO_APP_START", "0x10180000"),
family_id => os:getenv("ATOMVM_PICO_UF2_FAMILY", rp2040)
}).

%%
Expand Down Expand Up @@ -75,11 +74,11 @@ do(State) ->
rebar_api:debug("Effective opts for ~p: ~p", [?PROVIDER, Opts]),
OutFile = get_out_file(State),
TargetAVM = get_avm_file(State),
ok = do_uf2create(
maps:get(output, Opts, OutFile),
parse_addr(maps:get(start, Opts)),
maps:get(input, Opts, TargetAVM)
),
Output = maps:get(output, Opts, OutFile),
StartAddrStr = parse_addr(maps:get(start, Opts)),
Image = maps:get(input, Opts, TargetAVM),
Uf2Flavor = validate_flavor(maps:get(family_id, Opts, maps:get(family_id, ?DEFAULT_OPTS))),
ok = uf2tool:uf2create(Output, Uf2Flavor, StartAddrStr, Image),
{ok, State}
catch
C:E:S ->
Expand Down Expand Up @@ -115,6 +114,10 @@ env_opts() ->
start => os:getenv(
"ATOMVM_REBAR3_PLUGIN_UF2CREATE_START",
maps:get(start, ?DEFAULT_OPTS)
),
family_id => os:getenv(
"ATOMVM_REBAR3_PLUGIN_UF2_FAMILY",
maps:get(family_id, ?DEFAULT_OPTS)
)
}.

Expand Down Expand Up @@ -142,62 +145,17 @@ parse_addr("16#" ++ AddrHex) ->
parse_addr(AddrDec) ->
list_to_integer(AddrDec).

%%% UF2 defines
-define(UF2_MAGIC_START0, 16#0A324655).
-define(UF2_MAGIC_START1, 16#9E5D5157).
-define(UF2_MAGIC_END, 16#0AB16F30).

-define(UF2_FLAG_FAMILY_ID_PRESENT, 16#00002000).

%%% Pico defines
-define(UF2_PICO_FLAGS, ?UF2_FLAG_FAMILY_ID_PRESENT).
-define(UF2_PICO_PAGE_SIZE, 256).
-define(UF2_PICO_FAMILY_ID, 16#E48BFF56).

%%%

do_uf2create(OutputPath, StartAddr, ImagePath) ->
{ok, ImageBin} = file:read_file(ImagePath),
BlocksCount0 = byte_size(ImageBin) div ?UF2_PICO_PAGE_SIZE,
BlocksCount =
BlocksCount0 +
if
byte_size(ImageBin) rem ?UF2_PICO_PAGE_SIZE =:= 0 -> 0;
true -> 1
end,
OutputBin = uf2create0(0, BlocksCount, StartAddr, ImageBin, []),
ok = file:write_file(OutputPath, OutputBin).

%% @private
uf2create0(_BlockIndex, _BlocksCount, _BaseAddr, <<>>, Acc) ->
lists:reverse(Acc);
uf2create0(BlockIndex, BlocksCount, BaseAddr, ImageBin, Acc) ->
{PageBin, Tail} =
if
byte_size(ImageBin) >= ?UF2_PICO_PAGE_SIZE ->
split_binary(ImageBin, ?UF2_PICO_PAGE_SIZE);
true ->
{ImageBin, <<>>}
end,
PaddedData = pad_binary(PageBin, 476),
Block = [
<<
?UF2_MAGIC_START0:32/little,
?UF2_MAGIC_START1:32/little,
?UF2_PICO_FLAGS:32/little,
BaseAddr:32/little,
?UF2_PICO_PAGE_SIZE:32/little,
BlockIndex:32/little,
BlocksCount:32/little,
?UF2_PICO_FAMILY_ID:32/little
>>,
PaddedData,
<<?UF2_MAGIC_END:32/little>>
],
uf2create0(BlockIndex + 1, BlocksCount, BaseAddr + ?UF2_PICO_PAGE_SIZE, Tail, [Block | Acc]).

%% @private
pad_binary(Bin, Len) ->
PadCount = Len - byte_size(Bin),
Pad = binary:copy(<<0>>, PadCount),
[Bin, Pad].
validate_flavor(Flavor) ->
case Flavor of
rp2040 ->
rp2040;
rp2035 ->
data;
data ->
data;
universal ->
universal;
Family ->
rebar_api:error("An error occurred in the ~p task. Invalid family_id ~p~n", [?PROVIDER, Family])
end.

0 comments on commit 6be6585

Please sign in to comment.