diff --git a/lib/ex_atom_vm.ex b/lib/ex_atom_vm.ex index 325aa79..49ae81a 100644 --- a/lib/ex_atom_vm.ex +++ b/lib/ex_atom_vm.ex @@ -1,18 +1,4 @@ defmodule ExAtomVM do - @moduledoc """ - Documentation for ExAtomVM. - """ - - @doc """ - Hello world. - - ## Examples - - iex> ExAtomVM.hello() - :world - - """ - def hello do - :world - end + @moduledoc "README.md" + |> File.read!() end diff --git a/lib/mix/tasks/check.ex b/lib/mix/tasks/check.ex index 6fb839b..a6bb005 100644 --- a/lib/mix/tasks/check.ex +++ b/lib/mix/tasks/check.ex @@ -1,4 +1,17 @@ defmodule Mix.Tasks.Atomvm.Check do + use Mix.Task + @shortdoc "Check application code for use of unsupported instructions" + + @moduledoc """ + Verifies that the functions and modules used are either part of the application source (or deps) or supported by AtomVM. + + The check will catch the use of any standard Elixir modules or functions used in the application that are not included in exavmlib. + + > #### Info {: .info} + > + > Note. The `Mix.Tasks.Atomvm.Packbeam` task depends on this one, so users will likely never need to use it directly. + """ + alias Mix.Project def run(args) do @@ -201,6 +214,7 @@ defmodule Mix.Tasks.Atomvm.Check do you must remove the parentheses: map.field """) end + IO.puts("error: following missing instructions are used:") print_list(missing_instructions) IO.puts("") diff --git a/lib/mix/tasks/esp32_flash.ex b/lib/mix/tasks/esp32_flash.ex index 4920f36..6f8c2b6 100644 --- a/lib/mix/tasks/esp32_flash.ex +++ b/lib/mix/tasks/esp32_flash.ex @@ -1,5 +1,62 @@ defmodule Mix.Tasks.Atomvm.Esp32.Flash do use Mix.Task + + @shortdoc "Flash the application to an ESP32 micro-controller" + + @moduledoc """ + Flashes the application to an ESP32 micro-controller. + + > #### Important {: .warning} + > + > Before running this task, you must flash the AtomVM virtual machine to the target device. + > + > This tasks depends on `esptool` and can be installed using package managers: + > - linux (debian): apt install esptool + > - macos: brew install esptool + > - or follow these [installation instructions](https://docs.espressif.com/projects/esptool/en/latest/esp32/installation.html#installation) when not available through a package manager. + + ## Usage example + + Within your AtomVM mix project run + + ` + $ mix atomvm.esp32.flash + ` + + Or with optional flags (which will override the config in mix.exs) + + ` + $ mix atomvm.esp32.flash --port /dev/tty.usbserial-0001 + ` + + Or detect the port automatically with + + ` + $ mix atomvm.esp32.flash --port auto + ` + + ## Configuration + + ExAtomVM can be configured from the mix.ex file and supports the following settings for the + `atomvm.esp32.flash` task. + + * `:flash_offset` - The start address of the flash to write the application to in hexademical format, + defaults to `0x250000`. + + * `:chip` - Chip type, defaults to `auto`. + + * `:port` - The port to which device is connected on the host computer, defaults to `/dev/ttyUSB0`. + + * `:baud` - The BAUD rate used when flashing to device, defaults to `115200`. + + ## Command line options + + Properties in the mix.exs file may be over-ridden on the command line using long-style flags (prefixed by --) by the same name + as the [supported properties](#module-configuration) + + For example, you can use the `--port` option to specify or override the port property. + """ + alias Mix.Project alias Mix.Tasks.Atomvm.Packbeam diff --git a/lib/mix/tasks/packbeam.ex b/lib/mix/tasks/packbeam.ex index 6dd4406..6cc4dca 100644 --- a/lib/mix/tasks/packbeam.ex +++ b/lib/mix/tasks/packbeam.ex @@ -1,5 +1,38 @@ defmodule Mix.Tasks.Atomvm.Packbeam do use Mix.Task + + @shortdoc "Bundle the application into an AVM file" + + @moduledoc """ + Bundle an application into an AVM file that can be flashed to a micro-controller and (or directly on a unix host) executed by the AtomVM virtual machine. + + > #### Info {: .info} + > + > Normally using this task manually is not required, it is called automatically by `atomvm.esp32.flash`, `atomvm.stm32.flash` and `atomvm.pico.flash`. + + ## Usage example + + Within your AtomVM mix project run + + ` + $ mix atomvm.packbeam + ` + + ## Configuration + + ExAtomVM can be configured from the mix.ex file and supports the following settings for the + `atomvm.packbeam` task. + + * `:start` - The name of the module containing the start/0 entrypoint function. Only to be used to override the `:start` options defined in the the projects `mix.exs` This would not normally be needed, unless the user had an alternate mode of operation e.g like a client/server app that normally builds the client, but when building the server uses a different start module. + + ## Command line options + + Properties in the mix.exs file may be over-ridden on the command line using long-style flags (prefixed by --) by the same name + as the [supported properties](#module-configuration) + + For example, you can use the `--start` option to specify or override the `start` property. + """ + alias ExAtomVM.PackBEAM alias Mix.Project alias Mix.Tasks.Atomvm.Check diff --git a/lib/mix/tasks/pico_flash.ex b/lib/mix/tasks/pico_flash.ex index 7d65fbd..e477ce4 100644 --- a/lib/mix/tasks/pico_flash.ex +++ b/lib/mix/tasks/pico_flash.ex @@ -1,5 +1,53 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do use Mix.Task + + @shortdoc "Flash the application to a pico micro-controller" + + @moduledoc """ + Flashes the application to a Raspberry Pi RP2 + + You can build with all boards supported by Raspberry Pi pico SDK, including Pico, Pico-W and Pico2. + AtomVM also works with clones such as RP2040 Zero. + + > #### Important {: .warning} + > + > Before running this task, you must flash the AtomVM virtual machine to the target device. + > + > The pico provided partition is expected to be mounted. MacOS and most Linux desktop environments will do this automatically, for some it will need to be setup by the user. + + ## Usage example + + Within your AtomVM mix project run + + ` + $ mix atomvm.pico.flash + ` + + Or with optional flags (which will override the config in mix.exs) + + ` + $ mix atomvm.pico.flash --picotool /some/path + ` + + ## Configuration + + ExAtomVM can be configured from the mix.ex file and supports the following settings for the + `atomvm.pico.flash` task. + + * `:pico_path` - The full path to the pico mount point, defaults to `"/run/media/${USER}/RPI-RP2"` on linux; `"/Volumes/RPI-RP2"` on darwin (Mac) + + * `:pico_reset` - The full path to the pico device to reset if required, default `"/dev/ttyACM*"` on linux; `"/dev/cu.usbmodem14*"` on darwin (Mac) + + * `:picotool` - The full path to picotool executable (currently optional), default `undefined` + + ## Command line options + + Properties in the mix.exs file may be over-ridden on the command line using long-style flags (prefixed by --) by the same name + as the [supported properties](#module-configuration) + + For example, you can use the `--picotool` option to specify or override the `picotool` property. + """ + alias Mix.Project alias Mix.Tasks.Atomvm.Uf2create @@ -9,12 +57,38 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do with {:atomvm, {:ok, avm_config}} <- {:atomvm, Keyword.fetch(config, :atomvm)}, {:args, {:ok, options}} <- {:args, parse_args(args)}, {:uf2, :ok} <- {:uf2, Uf2create.run(args)} do - pico_path = - Map.get(options, :pico_path, Keyword.get(avm_config, :pico_path, System.get_env("ATOMVM_PICO_MOUNT_PATH", get_default_mount()))) + pico_path = + Map.get( + options, + :pico_path, + Keyword.get( + avm_config, + :pico_path, + System.get_env("ATOMVM_PICO_MOUNT_PATH", get_default_mount()) + ) + ) + pico_reset = - Map.get(options, :pico_reset, Keyword.get(avm_config, :pico_reset, System.get_env("ATOMVM_PICO_RESET_DEV", get_reset_base()))) + Map.get( + options, + :pico_reset, + Keyword.get( + avm_config, + :pico_reset, + System.get_env("ATOMVM_PICO_RESET_DEV", get_reset_base()) + ) + ) + picotool = - Map.get(options, :picotool, Keyword.get(avm_config, :picotool, System.get_env("ATOMVM_PICOTOOL_PATH", "#{:os.find_executable(~c"picotool")}"))) + Map.get( + options, + :picotool, + Keyword.get( + avm_config, + :picotool, + System.get_env("ATOMVM_PICOTOOL_PATH", "#{:os.find_executable(~c"picotool")}") + ) + ) do_flash(pico_path, pico_reset, picotool) else @@ -70,13 +144,16 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do case Map.get(filestat, :type) do :directory -> :ok + _ -> IO.puts("Object found at #{mount} is not a directory") exit({:shutdown, 1}) end + {:error, :enoent} -> Process.sleep(1000) wait_for_mount(mount, count + 1) + error -> IO.puts("unexpected error: #{error} while checking pico mount path.") exit({:shutdown, 1}) @@ -94,10 +171,12 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do case Map.get(info, :type) do :directory -> :ok + _ -> IO.puts("error: object at pico mount path not a directory. Abort!") exit({:shutdown, 1}) end + _ -> IO.puts("error: Pico not mounted. Abort!") exit({:shutdown, 1}) @@ -123,6 +202,7 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do case Path.wildcard(resetdev) do [] -> false + [device | _t] -> case File.stat(device) do {:ok, info} -> @@ -130,9 +210,11 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do :device -> {true, device} _ -> false end + _ -> false end + _ -> false end @@ -146,25 +228,40 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do {"", 0} -> # Pause to let the device settle Process.sleep(200) + error -> case picotool do false -> - IO.puts("Error: #{error}\nUnable to locate 'picotool', close the serial monitor before flashing, or install picotool for automatic disconnect and BOOTSEL mode.") + IO.puts( + "Error: #{error}\nUnable to locate 'picotool', close the serial monitor before flashing, or install picotool for automatic disconnect and BOOTSEL mode." + ) + exit({:shutdown, 1}) + _ -> - IO.puts("Warning: #{error}\nFor faster flashing remember to disconnect serial monitor first.") + IO.puts( + "Warning: #{error}\nFor faster flashing remember to disconnect serial monitor first." + ) + reset_args = ["reboot", "-f", "-u"] - IO.puts("Disconnecting serial monitor with `picotool #{:lists.join(" ", reset_args)}` in 5 seconds...") + + IO.puts( + "Disconnecting serial monitor with `picotool #{:lists.join(" ", reset_args)}` in 5 seconds..." + ) + Process.sleep(5000) + case :string.trim(System.cmd(picotool, reset_args)) do {status, 0} -> case status do "The device was asked to reboot into BOOTSEL mode." -> :ok + pt_error -> IO.puts("Failed to prepare pico for flashing: #{pt_error}") exit({:shutdown, 1}) end + _ -> IO.puts("Failed to prepare pico for flashing: #{error}") end @@ -176,6 +273,7 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do case needs_reset(pico_reset) do false -> :ok + {true, reset_port} -> do_reset(reset_port, picotool) IO.puts("Waiting for the device at path #{pico_path} to settle and mount...") @@ -183,7 +281,14 @@ defmodule Mix.Tasks.Atomvm.Pico.Flash do end check_pico_mount(pico_path) - _bytes = File.copy!("#{Project.config()[:app]}.uf2", "#{pico_path}/#{Project.config()[:app]}.uf2", :infinity) + + _bytes = + File.copy!( + "#{Project.config()[:app]}.uf2", + "#{pico_path}/#{Project.config()[:app]}.uf2", + :infinity + ) + IO.puts("Successfully loaded #{Project.config()[:app]} to the pico device at #{pico_path}.") end end diff --git a/lib/mix/tasks/stm32_flash.ex b/lib/mix/tasks/stm32_flash.ex index a798ff3..273cd65 100644 --- a/lib/mix/tasks/stm32_flash.ex +++ b/lib/mix/tasks/stm32_flash.ex @@ -1,5 +1,50 @@ defmodule Mix.Tasks.Atomvm.Stm32.Flash do use Mix.Task + + @shortdoc "Flash the application to a stm32 micro-controller" + + @moduledoc """ + Flashes the application to an stm32 micro-controller. + + > #### Important {: .warning} + > + > Before running this task, you must flash the AtomVM virtual machine to the target device. + > + > This tasks depends on a host installation of STM32 tooling, see [STM32 Build Instructions ](https://www.atomvm.net/doc/main/build-instructions.html#building-for-stm32) + + ## Usage example + + Within your AtomVM mix project run + + ` + $ mix atomvm.stm32.flash + ` + + Or with optional flags (which will override the config in mix.exs) + + ` + $ mix atomvm.stm32.flash --stflash_path /some/path + ` + + ## Configuration + + ExAtomVM can be configured from the mix.ex file and supports the following settings for the + `atomvm.stm32.flash` task. + + * `:flash_offset` - The start address of the flash to write the application to in hexademical format, + defaults to `0x8080000`. + + * `:stflash_path` - The full path to the st-flash utility, if not in users PATH, default `undefined` + + + ## Command line options + + Properties in the mix.exs file may be over-ridden on the command line using long-style flags (prefixed by --) by the same name + as the [supported properties](#module-configuration) + + For example, you can use the `--stflash_path` option to specify or override the `stflash_path` property. + """ + alias Mix.Project alias Mix.Tasks.Atomvm.Packbeam @@ -10,7 +55,6 @@ defmodule Mix.Tasks.Atomvm.Stm32.Flash do {:args, {:ok, options}} <- {:args, parse_args(args)}, {:pack, {:ok, _}} <- {:pack, Packbeam.run(args)}, stflash_path <- System.get_env("ATOMVM_MIX_PLUGIN_STFLASH", <<"">>) do - flash_offset = Map.get(options, :flash_offset, Keyword.get(avm_config, :flash_offset, 0x8080000)) diff --git a/lib/mix/tasks/uf2create.ex b/lib/mix/tasks/uf2create.ex index afce430..f0f42ef 100644 --- a/lib/mix/tasks/uf2create.ex +++ b/lib/mix/tasks/uf2create.ex @@ -1,5 +1,45 @@ defmodule Mix.Tasks.Atomvm.Uf2create do use Mix.Task + + @shortdoc "Create uf2 files appropriate for pico devices from a packed .avm application file" + + @moduledoc """ + Create uf2 files appropriate for pico devices from a packed .avm application file, + if the packed file does not exist the `atomvm.packbeam` task will be used to create the file (after compilation if necessary). + + > #### Info {: .info} + > + > Normally using this task manually is not required, it is called automatically by `atomvm.pico.flash` if a uf2 file has not already been created. + + ## Usage example + + Within your AtomVM mix project run + + ` + $ mix atomvm.uf2create + ` + + Or with optional flags (which will override the config in mix.exs) + + ` + $ mix atomvm.uf2create --app_start /some/path + ` + + ## Configuration + + ExAtomVM can be configured from the mix.ex file and supports the following settings for the + `atomvm.uf2create` task. + + * `:app_start` - The flash address ,in hexademical format, to place the application, default `0x10180000` + + ## Command line options + + Properties in the mix.exs file may be over-ridden on the command line using long-style flags (prefixed by --) by the same name + as the [supported properties](#module-configuration) + + For example, you can use the `--app_start` option to specify or override the `app_start` property. + """ + alias Mix.Project alias Mix.Tasks.Atomvm.Packbeam # require :uf2tool @@ -11,9 +51,22 @@ defmodule Mix.Tasks.Atomvm.Uf2create do {:args, {:ok, options}} <- {:args, parse_args(args)}, {:pack, {:ok, _}} <- {:pack, Packbeam.run(args)} do app_start = - parse_addr(Keyword.get(avm_config, :app_start, Map.get(options, :app_start, System.get_env("ATOMVM_PICO_APP_START", "0x10180000")))) + parse_addr( + Keyword.get( + avm_config, + :app_start, + Map.get(options, :app_start, System.get_env("ATOMVM_PICO_APP_START", "0x10180000")) + ) + ) + family_id = - validate_fam(Keyword.get(avm_config, :family_id, Map.get(options, :family_id, System.get_env("ATOMVM_PICO_UF2_FAMILY", "rp2040")))) + validate_fam( + Keyword.get( + avm_config, + :family_id, + Map.get(options, :family_id, System.get_env("ATOMVM_PICO_UF2_FAMILY", "rp2040")) + ) + ) :ok = :uf2tool.uf2create("#{config[:app]}.uf2", family_id, app_start, "#{config[:app]}.avm") IO.puts("Created #{config[:app]}.uf2") @@ -69,18 +122,42 @@ defmodule Mix.Tasks.Atomvm.Uf2create do defp validate_fam(family) do case family do - "rp2040" -> :rp2040 - ":rp2040" -> :rp2040 - :rp2040 -> :rp2040 - "rp2035" -> :data - ":rp2035" -> :data - :rp2035 -> :data - "data" -> :data - ":data" -> :data - :data -> :data - "universal" -> :universal - ":universal" -> :universal - :universal -> :universal + "rp2040" -> + :rp2040 + + ":rp2040" -> + :rp2040 + + :rp2040 -> + :rp2040 + + "rp2035" -> + :data + + ":rp2035" -> + :data + + :rp2035 -> + :data + + "data" -> + :data + + ":data" -> + :data + + :data -> + :data + + "universal" -> + :universal + + ":universal" -> + :universal + + :universal -> + :universal + unsupported -> IO.puts("Unsupported 'family_id' #{unsupported}") exit({:shutdown, 1}) diff --git a/mix.exs b/mix.exs index 141bf37..8e1a96e 100644 --- a/mix.exs +++ b/mix.exs @@ -7,7 +7,18 @@ defmodule ExAtomVM.MixProject do version: "0.1.0", elixir: "~> 1.8", start_permanent: Mix.env() == :prod, - deps: deps() + deps: deps(), + + # Docs + name: "ExAtomVM", + source_url: "https://github.com/atomvm/exatomvm", + homepage_url: "https://www.atomvm.net/", + docs: [ + # The main page in the docs + main: "ExAtomVM", + # logo: "path/to/logo.png", + extras: ["README.md"] + ] ] end @@ -21,7 +32,8 @@ defmodule ExAtomVM.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - {:uf2tool, "1.1.0"} + {:uf2tool, "1.1.0"}, + {:ex_doc, "~> 0.20", only: :dev, runtime: false} # {:dep_from_hexpm, "~> 0.3.0"}, # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} ]