Update quickstart to rp2 blinky
Change-Id: If2776ca18cf100b132a23c10f3d99c1840256441
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/quickstart/bazel/+/228453
Lint: Lint 🤖 <android-build-ayeaye@system.gserviceaccount.com>
Reviewed-by: Armando Montanez <amontanez@google.com>
Commit-Queue: Taylor Cramer <cramertj@google.com>
diff --git a/.bazelignore b/.bazelignore
index 912eacc..882c16b 100644
--- a/.bazelignore
+++ b/.bazelignore
@@ -1 +1,12 @@
+.cipd
+.presubmit
+.environment
+environment
+node_modules
+out
+bazel-bin
+bazel-out
+bazel-pigweed
+bazel-testlogs
+outbazel
third_party
diff --git a/.bazelrc b/.bazelrc
index 293ee0d..aec79d0 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -1,4 +1,4 @@
-# Copyright 2023 The Pigweed Authors
+# Copyright 2024 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
@@ -11,35 +11,165 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
-#
-# TODO: https://pwbug.dev/258836641#comment4: Enabling bzlmod breaks the build.
-common --noenable_bzlmod
+# Standard Pigweed flags
+# ======================
+# All Pigweed projects are expected to set these flags. They mostly pre-adopt
+# future Bazel settings.
+#
+# The source of truth for these flags is pw_build/pigweed.bazelrc in the main
+# Pigweed repo.
+#
# Do not attempt to configure an autodetected (local) toolchain. We vendor all
# our toolchains, and CI VMs may not have any local toolchain to detect.
common --repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
-# Don't propagate flags or defines to the exec config. This will become the
-# default one day (https://github.com/bazelbuild/bazel/issues/22457) and will
-# improve cache hit rates between builds targeting different platforms.
-common --experimental_exclude_defines_from_exec_config
-common --experimental_exclude_starlark_flags_from_exec_config
-
# Required for new toolchain resolution API.
build --incompatible_enable_cc_toolchain_resolution
+# Expose exec toolchains for Python.
+build --@rules_python//python/config_settings:exec_tools_toolchain=enabled
+
+# Don't propagate flags or defines to the exec config. This will become the
+# default one day (https://github.com/bazelbuild/bazel/issues/22457) and will
+# improve cache hit rates between builds targeting different platforms. This is
+# especially impactful for large host tools like protoc, which will have its
+# cache invalidated when your host C++ config changes.
+common --experimental_exclude_defines_from_exec_config
+common --experimental_exclude_starlark_flags_from_exec_config
+
+# Don't automatically create __init__.py files.
+#
+# This prevents spurious package name collisions at import time, and should be
+# the default (https://github.com/bazelbuild/bazel/issues/7386). It's
+# particularly helpful for Pigweed, because we have many potential package name
+# collisions due to a profusion of stuttering paths like
+# pw_transfer/py/pw_transfer.
+common --incompatible_default_to_explicit_init_py
+
+# Don't inherit system PATH. Improves hermeticity and cache hit rates. Should
+# be true by default one day (https://github.com/bazelbuild/bazel/issues/7026).
+common --incompatible_strict_action_env
+
+# Expose exec toolchains for Python. We use these toolchains in some rule
+# implementations (git grep for
+# "@rules_python//python:exec_tools_toolchain_type").
+build --@rules_python//python/config_settings:exec_tools_toolchain=enabled
+
+# C++ toolchain configuration
+# ===========================
+
+# Ignore all warnings in third-party code.
+common --per_file_copt=external/.*@-w
+common --host_per_file_copt=external/.*@-w
+
+# Picotool needs to build with exceptions and RTTI enabled.
+common --per_file_copt=external.*picotool.*@-fexceptions,-frtti
+common --host_per_file_copt=external.*picotool.*@-fexceptions,-frtti
+
+# Keep debugging symbols, but don't send them when flashing.
+build --strip=never
+
+build --@pico-sdk//bazel/config:PICO_STDIO_USB=True
+build --@pico-sdk//bazel/config:PICO_STDIO_UART=True
+
+# Sanitizer configs
+# =================
+common:asan --@pigweed//pw_toolchain/host_clang:asan
+common:tsan --@pigweed//pw_toolchain/host_clang:tsan
+common:ubsan --@pigweed//pw_toolchain/host_clang:ubsan
+
+# Presubmit
+# =========
# Default targets to build when running:
# bazel build --config=presubmit
build:presubmit -- \
//... \
- //src:echo.elf
+ //apps/blinky:blinky \
+ //apps/blinky:rp2040_blinky.elf \
+ //apps/blinky:rp2040_console \
+ //apps/blinky:simulator_blinky \
+ //apps/blinky:simulator_console \
+ //tools:console \
-# Use the remote cache. This will only work for users who have permission to access it.
+# UX settings
+# ===========
+# Error output settings.
+common --verbose_failures
+test --test_output=errors
+
+# Suppress the DEBUG: log messages from bazel. We get spammy DEBUG:
+# messages from rules_python.
+#
+# TODO: https://github.com/bazelbuild/rules_python/issues/1818 - Re-enable DEBUG
+# messages once rules_python stops spamming us.
+common --ui_event_filters=-debug
+
+# Remote cache
+# ============
+# Use the remote cache. This will only work for users who have permission to
+# access it (including the CI system!).
common:remote_cache --remote_cache=grpcs://remotebuildexecution.googleapis.com
common:remote_cache --google_default_credentials=true
common:remote_cache --remote_instance_name=projects/pigweed-rbe-open/instances/default-instance
common:remote_cache --remote_upload_local_results=false
+# Platform configuration
+# ======================
+common --custom_malloc=//targets:malloc
+build --@pigweed//pw_build:default_module_config=//system:module_config
+
+# Host platform default backends.
+common --@pigweed//pw_log:backend=@pigweed//pw_log_string
+common --@pigweed//pw_log:backend_impl=@pigweed//pw_log_string:impl
+common --@pigweed//pw_log_string:handler_backend=@pigweed//pw_system:log_backend
+common --@pigweed//pw_sys_io:backend=@pigweed//pw_sys_io_stdio
+common --@pigweed//pw_system:io_backend=@pigweed//pw_system:socket_target_io
+
+# RP2040 platform configuration
+build:rp2040 --platforms=//targets/rp2:rp2040
+build:rp2040 --//system:system=//targets/rp2:system
+build:rp2040 --@pigweed//pw_assert:assert_backend=@pigweed//pw_assert_trap
+build:rp2040 --@pigweed//pw_assert:assert_backend_impl=@pigweed//pw_assert_trap:impl
+build:rp2040 --@pigweed//pw_assert:check_backend=@pigweed//pw_assert_trap
+build:rp2040 --@pigweed//pw_assert:check_backend_impl=@pigweed//pw_assert_trap:impl
+build:rp2040 --@pigweed//pw_cpu_exception:entry_backend=@pigweed//pw_cpu_exception_cortex_m:cpu_exception
+build:rp2040 --@pigweed//pw_cpu_exception:entry_backend_impl=@pigweed//pw_cpu_exception_cortex_m:cpu_exception_impl
+build:rp2040 --@pigweed//pw_cpu_exception:handler_backend=@pigweed//pw_cpu_exception:basic_handler
+build:rp2040 --@pigweed//pw_cpu_exception:support_backend=@pigweed//pw_cpu_exception_cortex_m:support
+build:rp2040 --@pigweed//pw_interrupt:backend=@pigweed//pw_interrupt_cortex_m:context
+build:rp2040 --@pigweed//pw_log:backend=@pigweed//pw_log_tokenized
+build:rp2040 --@pigweed//pw_log:backend_impl=@pigweed//pw_log_tokenized:impl
+build:rp2040 --@pigweed//pw_log_tokenized:handler_backend=@pigweed//pw_system:log_backend
+build:rp2040 --@pigweed//pw_sync:binary_semaphore_backend=@pigweed//pw_sync_freertos:binary_semaphore
+build:rp2040 --@pigweed//pw_sync:interrupt_spin_lock_backend=@pigweed//pw_sync_freertos:interrupt_spin_lock
+build:rp2040 --@pigweed//pw_sync:mutex_backend=@pigweed//pw_sync_freertos:mutex
+build:rp2040 --@pigweed//pw_sync:thread_notification_backend=@pigweed//pw_sync_freertos:thread_notification
+build:rp2040 --@pigweed//pw_sync:timed_thread_notification_backend=@pigweed//pw_sync_freertos:timed_thread_notification
+build:rp2040 --@pigweed//pw_sys_io:backend=@pigweed//pw_sys_io_rp2040
+build:rp2040 --@pigweed//pw_system:device_handler_backend=@pigweed//targets/rp2040:device_handler
+build:rp2040 --@pigweed//pw_system:extra_platform_libs=//targets/rp2:extra_platform_libs
+build:rp2040 --@pigweed//pw_system:io_backend=@pigweed//pw_system:sys_io_target_io
+build:rp2040 --@pigweed//pw_thread_freertos:config_override=//targets/rp2:thread_config_overrides
+build:rp2040 --@pigweed//pw_thread:id_backend=@pigweed//pw_thread_freertos:id
+build:rp2040 --@pigweed//pw_thread:iteration_backend=@pigweed//pw_thread_freertos:thread_iteration
+build:rp2040 --@pigweed//pw_thread:sleep_backend=@pigweed//pw_thread_freertos:sleep
+build:rp2040 --@pigweed//pw_thread:thread_backend=@pigweed//pw_thread_freertos:thread
+build:rp2040 --@pigweed//pw_thread:test_thread_context_backend=@pigweed//pw_thread_freertos:test_thread_context
+build:rp2040 --@pigweed//pw_unit_test:config_override=//targets/rp2:64k_unit_tests
+build:rp2040 --@pigweed//pw_unit_test:main=//targets/rp2:unit_test_rpc_main
+build:rp2040 --@freertos//:freertos_config=//targets/rp2:freertos_config
+build:rp2040 --@pico-sdk//bazel/config:PICO_STDIO_USB=True
+build:rp2040 --@pico-sdk//bazel/config:PICO_STDIO_UART=True
+build:rp2040 --@pico-sdk//bazel/config:PICO_CLIB=llvm_libc
+build:rp2040 --@pico-sdk//bazel/config:PICO_TOOLCHAIN=clang
+build:rp2040 --@pigweed//pw_toolchain:cortex-m_toolchain_kind=clang
+test:rp2040 --run_under=@pigweed//targets/rp2040/py:unit_test_client
+
+# RP2350 is the same as rp2040 but with a different --platforms setting.
+build:rp2350 --config=rp2040
+build:rp2350 --platforms=//targets/rp2:rp2350
+
# User bazelrc file; see
# https://bazel.build/configure/best-practices#bazelrc-file
#
diff --git a/.bazelversion b/.bazelversion
index d6139c1..e44dad1 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-8.0.0-pre.20240618.2
+0ddcfd327ffd012d348deeae08ec0836409706ad
diff --git a/.buildifier.json b/.buildifier.json
new file mode 100644
index 0000000..d0ffbbe
--- /dev/null
+++ b/.buildifier.json
@@ -0,0 +1,8 @@
+{
+ "type": "auto",
+ "warningsList": [
+ "load",
+ "native-build",
+ "unsorted-dict-items"
+ ]
+}
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..7f63e9b
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,8 @@
+BasedOnStyle: Google
+BinPackArguments: false
+BinPackParameters: false
+DerivePointerAlignment: false
+PointerAlignment: Left
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+LineEnding: LF
diff --git a/.clangd.shared b/.clangd.shared
new file mode 100644
index 0000000..ef516ad
--- /dev/null
+++ b/.clangd.shared
@@ -0,0 +1,2 @@
+CompileFlags:
+ Add: -ferror-limit=0
diff --git a/.github/workflows/postsubmit.yaml b/.github/workflows/postsubmit.yaml
index 0edaf94..d7f571c 100644
--- a/.github/workflows/postsubmit.yaml
+++ b/.github/workflows/postsubmit.yaml
@@ -22,7 +22,15 @@
disk-cache: ${{ github.workflow }}
# Share repository cache between workflows.
repository-cache: true
- - name: Bazel Build
- run: bazel build ...
- - name: Bazel Test
- run: bazel test ...
+ - name: Presubmit
+ run: bazel build --config=presubmit
+ - name: RP2040
+ run: bazel build --config=rp2040 //...
+ - name: Test
+ run: bazel test //...
+ - name: ASAN
+ run: bazel test --config=asan //...
+ - name: TSAN
+ run: bazel test --config=tsan //...
+ - name: UBSAN
+ run: bazel test --config=ubsan //...
diff --git a/.github/workflows/presubmit.yaml b/.github/workflows/presubmit.yaml
index e9aa5c0..28e6d7e 100644
--- a/.github/workflows/presubmit.yaml
+++ b/.github/workflows/presubmit.yaml
@@ -23,7 +23,15 @@
disk-cache: ${{ github.workflow }}
# Share repository cache between workflows.
repository-cache: true
- - name: Bazel Build
- run: bazel build ...
- - name: Bazel Test
- run: bazel test ...
+ - name: Presubmit
+ run: bazel build --config=presubmit
+ - name: RP2040
+ run: bazel build --config=rp2040 //...
+ - name: Test
+ run: bazel test //...
+ - name: ASAN
+ run: bazel test --config=asan //...
+ - name: TSAN
+ run: bazel test --config=tsan //...
+ - name: UBSAN
+ run: bazel test --config=ubsan //...
diff --git a/.gitignore b/.gitignore
index 13fa984..2cd1c37 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,64 @@
+# Build
+out/
+.presubmit/
+compile_commands.json
user.bazelrc
-bazel-*
+/bazel-*
-# Editor files
-*.swo
+# Ignore until https://github.com/bazelbuild/bazel/issues/20369 is fixed.
+MODULE.bazel.lock
+
+# Environment
+.environment/
+environment/
+build_overrides/pigweed_environment.gni
+
+# Editors
+.pw_ide/
+.vscode/settings.json
+.vscode/tasks.json
+.vscode/pw_user_*.json
+.vscode/*.bak.json
+.idea/
+.project
+.cproject
+.clangd
+.clangd/
*.swp
+*.swo
+.#*
+
+# Python
+python*-env/
+.python*-env/
+venv/
+*.pyc
+*.egg/
+*.eggs/
+*.egg-info/
+.cache/
+.mypy_cache/
+__pycache__/
+
+# Mac
+.DS_Store
+
+# GDB
+.gdb_history
+
+# Git
+*.orig
+*.BACKUP.*
+*.BASE.*
+*.LOCAL.*
+*.REMOTE.*
+*_BACKUP_*.txt
+*_BASE_*.txt
+*_LOCAL_*.txt
+*_REMOTE_*.txt
+
+# Other
+logfile.txt
+pw_console-*logs.txt
+.pw_console.user.yaml
+factory-logs-*.txt
diff --git a/.pw_console.yaml b/.pw_console.yaml
new file mode 100644
index 0000000..4870700
--- /dev/null
+++ b/.pw_console.yaml
@@ -0,0 +1,99 @@
+# This is a pw_console config file that defines a default window layout.
+# For more info on what can be added to this file see:
+# https://pigweed.dev/pw_console/py/pw_console/docs/user_guide.html#configuration
+config_title: pw_console
+
+# Window layout
+windows:
+ # Left split with tabbed views.
+ Split 1 tabbed:
+ Python Repl:
+ hidden: False
+ # Right split with stacked views.
+ Split 2 stacked:
+ Device Logs:
+ hidden: False
+ Host Logs:
+ hidden: False
+
+window_column_split_method: vertical
+# window_column_split_method: horizontal
+
+# Default colors:
+ui_theme: dark
+code_theme: pigweed-code
+swap_light_and_dark: False
+
+# A few other choices:
+# ui_theme: high-contrast-dark
+# ui_theme: nord
+# ui_theme: synthwave84
+# code_theme: gruvbox-dark
+# code_theme: pigweed-code-light
+# code_theme: synthwave84
+
+# Log display options:
+spaces_between_columns: 2
+hide_date_from_log_time: True
+recolor_log_lines_to_match_level: False
+
+column_order:
+ # Column name
+ - time
+ - level
+ - timestamp
+ - module
+ # Hidden:
+ # - source_name
+
+column_order_omit_unspecified_columns: True
+
+snippets:
+ Echo RPC:
+ code: |
+ device.rpcs.pw.rpc.EchoService.Echo(msg='hello world')
+ description: |
+ Send a string to the device and receive a response with the same
+ message back.
+
+ >>> device.rpcs.pw.rpc.EchoService.Echo(msg='hello world')
+ (Status.OK, pw.rpc.EchoMessage(msg='hello world'))
+
+ Reboot to bootloader:
+ code: |
+ device.rpcs.board.Board.Reboot(reboot_type=1)
+ description: |
+ Sent a reboot request to the device. The console must be
+ relaunched to reconnect.
+
+ Get onboard temp sensor value:
+ code: |
+ device.rpcs.board.Board.OnboardTemp()
+ description: |
+ Get the current value of the onboard temperature sensor.
+
+ ```pycon
+ >>> device.rpcs.board.Board.OnboardTemp()
+ (Status.OK, board.OnboardTempResponse(temp=24.797744750976562))
+ ```
+
+ Log onboard temp sensor values every 1000ms:
+ code: |
+ call = device.rpcs.board.Board.OnboardTempStream.invoke(
+ request_args=dict(sample_interval_ms=1000),
+ on_next=lambda _, message: LOG.info(message)
+ )
+ description: |
+ Start logging with:
+
+ ```pycon
+ >>> call = device.rpcs.board.Board.OnboardTempStream.invoke(request_args=dict(sample_interval_ms=1000), on_next=lambda _, message: LOG.info(message))
+ ```
+
+ Log messages will appear in the 'Host Logs' window.
+ To stop the streaming RPC run `call.cancel()`:
+
+ ```pycon
+ >>> call.cancel()
+ True
+ ```
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..bf6ac5c
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,9 @@
+{
+ "recommendations": [
+ "pigweed.pigweed"
+ ],
+ "unwantedRecommendations": [
+ "ms-vscode.cpptools",
+ "ms-vscode.cpptools-extension-pack"
+ ]
+}
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..b13fcec
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,7 @@
+# This is the list of Pigweed authors for copyright purposes.
+#
+# This does not necessarily list everyone who has contributed code, since in
+# some cases, their employer may be the copyright holder. To see the full list
+# of contributors, see the revision history in source control.
+
+Google LLC
diff --git a/BUILD.bazel b/BUILD.bazel
index 1767ea4..6ddb18f 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -1,4 +1,4 @@
-# Copyright 2023 The Pigweed Authors
+# Copyright 2024 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
@@ -11,11 +11,42 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
-load("@rules_python//python:pip.bzl", "compile_pip_requirements")
-# Run bazel run //:pip_requirements.update to regenerate requirements_lock.txt.
-compile_pip_requirements(
- name = "pip_requirements",
- requirements_in = "requirements.in",
- requirements_txt = "requirements_lock.txt",
+load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
+load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands")
+load("@pigweed//pw_build:compatibility.bzl", "incompatible_with_mcu")
+
+package(default_visibility = ["//visibility:public"])
+
+copy_file(
+ name = "copy_clangd",
+ src = "@pigweed//pw_toolchain/host_clang:clangd",
+ out = "clangd",
+ allow_symlink = True,
+)
+
+refresh_compile_commands(
+ name = "refresh_compile_commands",
+ out_dir = ".compile_commands",
+ target_compatible_with = incompatible_with_mcu(),
+ target_groups = {
+ "host_simulator": [
+ "//apps/blinky:simulator_blinky",
+ "//modules/blinky:blinky_test",
+ ],
+ "rp2040": [
+ "//apps/blinky:rp2040_blinky.elf",
+ [
+ "//modules/blinky:blinky_test",
+ "--config=rp2040",
+ ],
+ ],
+ },
+)
+
+filegroup(
+ name = "pw_console_config",
+ srcs = [
+ ".pw_console.yaml",
+ ],
)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..677be08
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,29 @@
+# Contributing to Pigweed
+
+Pigweed lets anyone contribute to the project, regardless of their employer.
+The Pigweed project reviews and encourages well-tested, high-quality
+contributions from anyone who wants to contribute to Pigweed.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement (CLA).
+
+To see any Contributor License Agreements on file or to sign a CLA, go to
+<https://cla.developers.google.com/>.
+
+For more information about the Google CLA, see
+[Contributor License Agreements](https://cla.developers.google.com/about).
+
+## Contributing changes and submitting code reviews
+
+All changes require review, including changes by project members.
+
+For detailed instructions on how to contribute changes,
+see [the Gerrit docs](https://gerrit-review.googlesource.com/Documentation/intro-user.html).
+
+## Community guidelines
+
+This project observes the following community guidelines:
+
+ * [Google's Open Source Community Guidelines](https://opensource.google/conduct/)
diff --git a/MODULE.bazel b/MODULE.bazel
new file mode 100644
index 0000000..22367e6
--- /dev/null
+++ b/MODULE.bazel
@@ -0,0 +1,81 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+module(
+ name = "quickstart",
+)
+
+bazel_dep(name = "bazel_skylib", version = "1.7.1")
+bazel_dep(name = "freertos", version = "10.5.1.bcr.2")
+bazel_dep(name = "nanopb", repo_name = "com_github_nanopb_nanopb")
+bazel_dep(name = "pico-sdk", version = "2.0.0")
+bazel_dep(name = "pigweed")
+bazel_dep(name = "platforms", version = "0.0.10")
+bazel_dep(name = "pw_toolchain")
+bazel_dep(name = "rules_cc")
+bazel_dep(name = "rules_python", version = "0.34.0")
+
+bazel_dep(name = "hedron_compile_commands", dev_dependency = True)
+
+# Module overrides
+# ================
+# TODO: https://pwbug.dev/349880767 - Point this back to the upstream repo once
+# this PR is merged.
+archive_override(
+ module_name = "hedron_compile_commands",
+ strip_prefix = "bazel-compile-commands-extractor-163521345aa6366fd1ed801b989b668b5c806f69",
+ urls = ["https://github.com/chadnorvell/bazel-compile-commands-extractor/archive/163521345aa6366fd1ed801b989b668b5c806f69.tar.gz"],
+)
+
+# TODO: https://pwbug.dev/354274498 - nanopb is not yet in the BCR.
+git_override(
+ module_name = "nanopb",
+ commit = "7c6c581bc6f7406a4f01c3b9853251ff0a68458b",
+ remote = "https://github.com/nanopb/nanopb.git",
+)
+
+git_override(
+ module_name = "pigweed",
+ # ROLL: Warning: this entry is automatically updated.
+ # ROLL: Last updated 2024-08-07.
+ # ROLL: By https://cr-buildbucket.appspot.com/build/8740243540358493793.
+ commit = "146fd4b5c55fb45c390515b9d5faa2f5abd00be2",
+ remote = "https://pigweed.googlesource.com/pigweed/pigweed",
+)
+
+git_override(
+ module_name = "pw_toolchain",
+ # ROLL: Warning: this entry is automatically updated.
+ # ROLL: Last updated 2024-08-07.
+ # ROLL: By https://cr-buildbucket.appspot.com/build/8740243540358493793.
+ commit = "146fd4b5c55fb45c390515b9d5faa2f5abd00be2",
+ remote = "https://pigweed.googlesource.com/pigweed/pigweed",
+ strip_prefix = "pw_toolchain_bazel",
+)
+
+# TODO: https://pwbug.dev/258836641 - Pre-release version needed for the Pico
+# SDK. Remove this once rules_cc 0.10.0 is released and the Pico SDK
+# MODULE.bazel declares its dependency on it.
+archive_override(
+ module_name = "rules_cc",
+ integrity = "sha256-NddP6xi6LzsIHT8bMSVJ2NtoURbN+l3xpjvmIgB6aSg=",
+ strip_prefix = "rules_cc-1acf5213b6170f1f0133e273cb85ede0e732048f",
+ urls = [
+ "https://github.com/bazelbuild/rules_cc/archive/1acf5213b6170f1f0133e273cb85ede0e732048f.zip",
+ ],
+)
+
+http_archive = use_repo_rule(
+ "@bazel_tools//tools/build_defs/repo:http.bzl",
+ "http_archive",
+)
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..528c611
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,7 @@
+# Remove the `*` from this file and add specific owners to this file and to
+# subdirectories where appropriate to enforce OWNERS approval in this
+# repository.
+#
+# Syntax for OWNERS files can be found at:
+# https://pigweed-review.googlesource.com/plugins/code-owners/Documentation/backend-find-owners.html#syntax
+*
diff --git a/README.md b/README.md
index 7da4f53..21acbde 100644
--- a/README.md
+++ b/README.md
@@ -1,59 +1,67 @@
# Pigweed: minimal Bazel example
This repository contains a minimal example of a Bazel-based Pigweed project.
-It's an echo application for the STM32F429 Discovery Board.
+It is a LED-blinking service (featuring RPC control!) for the
+[Raspberry Pi Pico](https://www.raspberrypi.com/products/raspberry-pi-pico/).
+It can also be run on any computer using the included simulator.
-## Cloning
+## Getting the code
```
-git clone --recursive https://pigweed.googlesource.com/pigweed/quickstart/bazel
+git clone https://pigweed.googlesource.com/pigweed/quickstart/bazel pw_bazel_quickstart
+cd pw_bazel_quickstart
```
-If you already cloned but forgot to include `--recursive`, run `git submodule
-update --init` to pull all submodules.
+## Dependencies
-TODO: b/300695111 - Don't require submodules for this example.
+The only dependency that must be installed is Bazelisk.
-## Building
+Bazelisk is a launcher for the Bazel build system that allows for easy
+management of multiple Bazel versions.
-We'll assume you already have Bazel on your system. If you don't, the
-recommended way to get it is through
-[Bazelisk](https://github.com/bazelbuild/bazelisk/blob/master/README.md).
+[Instructions for installing Bazelisk can be found here.](https://github.com/bazelbuild/bazelisk/blob/master/README.md)
-To build the entire project (including building the application for both the
-host and the STM32 Discovery Board), run
+## Running on the simulator
+
+To run the simulator, type:
+`bazelisk run //apps/blinky:simulator_blinky`
+Then, in a new console, connect to the simulator using:
+`bazelisk run //apps/blinky:simulator_console`
+
+## Running on hardware
+
+To start, connect a Raspberry Pi Pico, Pico 2, or debug probe via USB.
+
+To run on the Raspberry Pi Pico, type:
+`bazelisk run //apps/blinky:flash_rp2040`
+Then, in a new console, connect to the device using:
+`bazelisk run //apps/blinky:rp2040_console`
+
+## Controlling the LED
+
+Once connected with a console, RPCs can be sent to control the LED.
+Try running:
```
-bazel build //...
+device.set_led(True)
+device.set_led(False)
+device.toggle_led()
+device.blink(blink_count=3)
```
-To run the application locally on your machine, run,
+## Running unit tests on the host device
-```
-bazel run //src:echo
-```
+`bazelisk test //...` will run the unit tests defined in this project,
+such as the ones in `modules/blinky/blinky_test.cc`.
-## Flashing
+## Running unit tests on hardware
-To flash the firmware to a STM32F429 Discovery Board connected to your machine,
-run,
+`bazelisk run @pigweed//targets/rp2040/py:unit_test_server` in one console followed by
+`bazelisk test //... --config=rp2040` will also allow running the unit tests on-device.
-```
-bazel run //tools:flash
-```
+## Next steps
-Note that you _don't need to build the firmware first_: Bazel knows that the
-firmware images are needed to flash the board, and will build them for you. And
-if you edit the source of the firmware or any of its dependencies, it will get
-rebuilt when you flash.
-
-## Communicating
-
-Run,
-
-```
-bazel run //tools:miniterm -- /dev/ttyACM0 --filter=debug
-```
-
-to communicate with the board. When you transmit a character, you should get
-the same character back!
+Try poking around the codebase for inspiration about how Pigweed projects can be
+organized. Most of the relevant code in this quickstart (including RPC
+definitions) is inside `modules/blinky`, with some client-side Python code in
+`tools/console.py`.
diff --git a/WORKSPACE b/WORKSPACE
deleted file mode 100644
index 38523cb..0000000
--- a/WORKSPACE
+++ /dev/null
@@ -1,143 +0,0 @@
-# Copyright 2023 The Pigweed Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-
-load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
-load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
-
-# Load Pigweed's own dependencies that we'll need.
-#
-# TODO: b/274148833 - Don't require users to spell these out.
-http_archive(
- name = "platforms",
- sha256 = "8150406605389ececb6da07cbcb509d5637a3ab9a24bc69b1101531367d89d74",
- urls = [
- "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz",
- "https://github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz",
- ],
-)
-
-http_archive(
- name = "bazel_skylib", # 2022-09-01
- sha256 = "4756ab3ec46d94d99e5ed685d2d24aece484015e45af303eb3a11cab3cdc2e71",
- strip_prefix = "bazel-skylib-1.3.0",
- urls = ["https://github.com/bazelbuild/bazel-skylib/archive/refs/tags/1.3.0.zip"],
-)
-
-http_archive(
- name = "rules_proto",
- sha256 = "dc3fb206a2cb3441b485eb1e423165b231235a1ea9b031b4433cf7bc1fa460dd",
- strip_prefix = "rules_proto-5.3.0-21.7",
- urls = [
- "https://github.com/bazelbuild/rules_proto/archive/refs/tags/5.3.0-21.7.tar.gz",
- ],
-)
-
-http_archive(
- name = "rules_python",
- sha256 = "0a8003b044294d7840ac7d9d73eef05d6ceb682d7516781a4ec62eeb34702578",
- strip_prefix = "rules_python-0.24.0",
- url = "https://github.com/bazelbuild/rules_python/releases/download/0.24.0/rules_python-0.24.0.tar.gz",
-)
-
-load("@rules_python//python:repositories.bzl", "py_repositories")
-
-py_repositories()
-
-http_archive(
- name = "com_google_protobuf",
- sha256 = "c6003e1d2e7fefa78a3039f19f383b4f3a61e81be8c19356f85b6461998ad3db",
- strip_prefix = "protobuf-3.17.3",
- url = "https://github.com/protocolbuffers/protobuf/archive/v3.17.3.tar.gz",
-)
-
-load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
-
-protobuf_deps()
-
-# TODO(b/311746469): Switch back to a released version when possible.
-git_repository(
- name = "rules_fuzzing",
- commit = "67ba0264c46c173a75825f2ae0a0b4b9b17c5e59",
- remote = "https://github.com/bazelbuild/rules_fuzzing",
-)
-
-load("@rules_fuzzing//fuzzing:repositories.bzl", "rules_fuzzing_dependencies")
-
-rules_fuzzing_dependencies()
-
-load("@rules_fuzzing//fuzzing:init.bzl", "rules_fuzzing_init")
-
-rules_fuzzing_init()
-
-git_repository(
- name = "pigweed",
- # ROLL: Warning: this entry is automatically updated.
- # ROLL: Last updated 2024-08-07.
- # ROLL: By https://cr-buildbucket.appspot.com/build/8740220870553192513.
- commit = "04109d522e8b54b140d5d6dca2aa2171e4b5e22d",
- remote = "https://pigweed.googlesource.com/pigweed/pigweed.git",
-)
-
-git_repository(
- name = "pw_toolchain",
- # ROLL: Warning: this entry is automatically updated.
- # ROLL: Last updated 2024-08-07.
- # ROLL: By https://cr-buildbucket.appspot.com/build/8740220870553192513.
- commit = "04109d522e8b54b140d5d6dca2aa2171e4b5e22d",
- remote = "https://pigweed.googlesource.com/pigweed/pigweed.git",
- strip_prefix = "pw_toolchain_bazel",
-)
-
-# Get ready to grab CIPD dependencies. For this minimal example, the only
-# dependencies will be the toolchains and OpenOCD (used for flashing).
-load(
- "@pigweed//pw_env_setup/bazel/cipd_setup:cipd_rules.bzl",
- "cipd_client_repository",
- "cipd_repository",
-)
-
-cipd_client_repository()
-
-
-load("@pigweed//pw_toolchain:register_toolchains.bzl", "register_pigweed_cxx_toolchains")
-
-register_pigweed_cxx_toolchains()
-
-# Get the OpenOCD binary (we'll use it for flashing).
-cipd_repository(
- name = "openocd",
- path = "infra/3pp/tools/openocd/${os}-${arch}",
- tag = "version:2@0.11.0-3",
-)
-
-load("@rules_python//python:repositories.bzl", "python_register_toolchains")
-
-# Set up the Python interpreter and PyPI dependencies we'll need.
-python_register_toolchains(
- name = "python3",
- python_version = "3.11",
-)
-
-load("@python3//:defs.bzl", "interpreter")
-load("@rules_python//python:pip.bzl", "pip_parse")
-
-pip_parse(
- name = "pypi",
- python_interpreter_target = interpreter,
- requirements_lock = "//:requirements_lock.txt",
-)
-
-load("@pypi//:requirements.bzl", "install_deps")
-
-install_deps()
diff --git a/apps/blinky/BUILD.bazel b/apps/blinky/BUILD.bazel
new file mode 100644
index 0000000..0cec3ed
--- /dev/null
+++ b/apps/blinky/BUILD.bazel
@@ -0,0 +1,99 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+load("@pigweed//targets/host_device_simulator:transition.bzl", "host_device_simulator_binary")
+load("@pigweed//targets/rp2040:flash.bzl", "flash_rp2040")
+load("//targets/rp2:binary.bzl", "rp2040_binary", "rp2350_binary")
+load("//tools:tools.bzl", "device_console", "host_console")
+
+package(default_visibility = ["//visibility:public"])
+
+cc_binary(
+ name = "blinky",
+ srcs = ["main.cc"],
+ deps = [
+ "//modules/blinky:service",
+ "//system",
+ "@pigweed//pw_log",
+ "@pigweed//pw_system:async",
+
+ # These should be provided by pw_system:async.
+ "@pigweed//pw_assert:assert_backend_impl",
+ "@pigweed//pw_assert:check_backend_impl",
+ "@pigweed//pw_log:backend_impl",
+ "@pigweed//pw_system:extra_platform_libs",
+ ],
+)
+
+# Create an rp2040 flashable ELF
+rp2040_binary(
+ name = "rp2040_blinky.elf",
+ binary = ":blinky",
+)
+
+# Create an rp2350 flashable ELF
+rp2350_binary(
+ name = "rp2350_blinky.elf",
+ binary = ":blinky",
+)
+
+# Create a host binary using the Pigweed upstream pw_system host_device_simulator.
+host_device_simulator_binary(
+ name = "simulator_blinky",
+ binary = ":blinky",
+)
+
+host_console(
+ name = "simulator_console",
+ binary = ":simulator_blinky",
+)
+
+host_console(
+ name = "simulator_webconsole",
+ binary = ":simulator_blinky",
+ extra_args = ["--browser"],
+)
+
+device_console(
+ name = "rp2040_console",
+ binary = ":rp2040_blinky.elf",
+)
+
+device_console(
+ name = "rp2040_webconsole",
+ binary = ":rp2040_blinky.elf",
+ extra_args = ["--browser"],
+)
+
+device_console(
+ name = "rp2350_console",
+ binary = ":rp2350_blinky.elf",
+)
+
+device_console(
+ name = "rp2350_webconsole",
+ binary = ":rp2350_blinky.elf",
+ extra_args = ["--browser"],
+)
+
+flash_rp2040(
+ name = "flash_rp2040",
+ rp2040_binary = "rp2040_blinky.elf",
+)
+
+# Note: Despite the name, the rule works for the 2350.
+flash_rp2040(
+ name = "flash_rp2350",
+ rp2040_binary = "rp2350_blinky.elf",
+)
diff --git a/apps/blinky/main.cc b/apps/blinky/main.cc
new file mode 100644
index 0000000..3032d43
--- /dev/null
+++ b/apps/blinky/main.cc
@@ -0,0 +1,34 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#define PW_LOG_MODULE_NAME "MAIN"
+
+#include "modules/blinky/service.h"
+#include "pw_log/log.h"
+#include "pw_system/system.h"
+#include "system/system.h"
+
+int main() {
+ demo::system::Init();
+ auto& rpc_server = pw::System().rpc_server();
+ auto& monochrome_led = demo::system::MonochromeLed();
+
+ static demo::BlinkyService blinky_service;
+ blinky_service.Init(
+ pw::System().dispatcher(), pw::System().allocator(), monochrome_led);
+ rpc_server.RegisterService(blinky_service);
+
+ PW_LOG_INFO("Started blinky app; waiting for RPCs...");
+ demo::system::Start();
+ PW_UNREACHABLE;
+}
diff --git a/echo.bzl b/echo.bzl
deleted file mode 100644
index 4151508..0000000
--- a/echo.bzl
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright 2023 The Pigweed Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-"""Build rules and transitions for firmware."""
-
-def _stm32_transition_impl(settings, attr):
- # buildifier: disable=unused-variable
- _ignore = attr
- return {
- "//command_line_option:platforms": "//targets:stm32",
- "@pigweed//pw_boot:backend": "@pigweed//pw_boot_cortex_m",
- "@pigweed//pw_assert:backend": "@pigweed//pw_assert_basic",
- "@pigweed//pw_assert:backend_impl": "@pigweed//pw_assert_basic:impl",
- "@pigweed//pw_assert:check_backend": "@pigweed//pw_assert_basic",
- "@pigweed//pw_assert:check_backend_impl": "@pigweed//pw_assert_basic:impl",
- "@pigweed//pw_log:backend": "@pigweed//pw_log_basic",
- "@pigweed//pw_log:backend_impl": "@pigweed//pw_log_basic:impl",
- "@pigweed//pw_sys_io:backend": "@pigweed//pw_sys_io_baremetal_stm32f429",
- }
-
-_stm32_transition = transition(
- implementation = _stm32_transition_impl,
- inputs = [],
- outputs = [
- "//command_line_option:platforms",
- "@pigweed//pw_boot:backend",
- "@pigweed//pw_assert:backend",
- "@pigweed//pw_assert:backend_impl",
- "@pigweed//pw_assert:check_backend",
- "@pigweed//pw_assert:check_backend_impl",
- "@pigweed//pw_log:backend",
- "@pigweed//pw_log:backend_impl",
- "@pigweed//pw_sys_io:backend",
- ],
-)
-
-def _binary_impl(ctx):
- out = ctx.actions.declare_file(ctx.label.name)
- ctx.actions.symlink(output = out, target_file = ctx.executable.binary)
- return [DefaultInfo(files = depset([out]), executable = out)]
-
-# TODO(tpudlik): Replace this with platform_data when it is available.
-stm32_cc_binary = rule(
- _binary_impl,
- attrs = {
- "binary": attr.label(
- doc = "cc_binary to build for stm32",
- cfg = _stm32_transition,
- executable = True,
- mandatory = True,
- ),
- "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
- },
-)
diff --git a/modules/blinky/BUILD.bazel b/modules/blinky/BUILD.bazel
new file mode 100644
index 0000000..967b44d
--- /dev/null
+++ b/modules/blinky/BUILD.bazel
@@ -0,0 +1,97 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+load("@pigweed//pw_build:pigweed.bzl", "pw_cc_test")
+load(
+ "@pigweed//pw_protobuf_compiler:pw_proto_library.bzl",
+ "nanopb_proto_library",
+ "nanopb_rpc_proto_library",
+)
+load("@rules_python//python:proto.bzl", "py_proto_library")
+
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+ name = "blinky",
+ srcs = ["blinky.cc"],
+ hdrs = ["blinky.h"],
+ implementation_deps = [
+ "@pigweed//pw_log",
+ "@pigweed//pw_preprocessor",
+ ],
+ deps = [
+ "//modules/timer_future",
+ "//system",
+ "@pigweed//pw_async2:coro",
+ "@pigweed//pw_async2:coro_or_else_task",
+ "@pigweed//pw_async2:dispatcher",
+ "@pigweed//pw_chrono:system_clock",
+ "@pigweed//pw_function",
+ "@pigweed//pw_sync:interrupt_spin_lock",
+ "@pigweed//pw_sync:lock_annotations",
+ "@pigweed//pw_system:async",
+ ],
+)
+
+pw_cc_test(
+ name = "blinky_test",
+ srcs = ["blinky_test.cc"],
+ deps = [
+ ":blinky",
+ "@pigweed//pw_allocator:testing",
+ "@pigweed//pw_async2:dispatcher",
+ "@pigweed//pw_digital_io:digital_io_mock",
+ "@pigweed//pw_thread:sleep",
+ "@pigweed//pw_unit_test",
+ ],
+)
+
+cc_library(
+ name = "service",
+ srcs = ["service.cc"],
+ hdrs = ["service.h"],
+ deps = [
+ ":blinky",
+ ":nanopb_rpc",
+ "@pigweed//pw_allocator:allocator",
+ "@pigweed//pw_assert:check",
+ "@pigweed//pw_async2:dispatcher",
+ ],
+)
+
+proto_library(
+ name = "proto",
+ srcs = ["blinky.proto"],
+ import_prefix = "blinky_pb",
+ strip_import_prefix = "/modules/blinky",
+ deps = [
+ "@pigweed//pw_protobuf:common_proto",
+ ],
+)
+
+nanopb_proto_library(
+ name = "nanopb",
+ deps = [":proto"],
+)
+
+nanopb_rpc_proto_library(
+ name = "nanopb_rpc",
+ nanopb_proto_library_deps = [":nanopb"],
+ deps = [":proto"],
+)
+
+py_proto_library(
+ name = "py_pb2",
+ deps = [":proto"],
+)
diff --git a/modules/blinky/blinky.cc b/modules/blinky/blinky.cc
new file mode 100644
index 0000000..72954c7
--- /dev/null
+++ b/modules/blinky/blinky.cc
@@ -0,0 +1,142 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#define PW_LOG_MODULE_NAME "BLINKY"
+#include "blinky.h"
+
+#include <mutex>
+
+#include "modules/blinky/blinky.h"
+#include "pw_async2/coro.h"
+#include "pw_log/log.h"
+
+namespace demo {
+
+using ::pw::Allocator;
+using ::pw::OkStatus;
+using ::pw::Status;
+using ::pw::async2::Coro;
+using ::pw::async2::CoroContext;
+using ::pw::async2::Dispatcher;
+using ::pw::digital_io::DigitalInOut;
+using ::pw::digital_io::State;
+
+namespace {
+
+bool IsOn(DigitalInOut& io) {
+ auto result = io.GetState();
+ if (!result.ok()) {
+ return false;
+ }
+ return *result == State::kActive;
+}
+
+void TurnOn(DigitalInOut& io) { io.SetState(State::kActive).IgnoreError(); }
+
+void TurnOff(DigitalInOut& io) { io.SetState(State::kInactive).IgnoreError(); }
+
+void ToggleIo(DigitalInOut& io) {
+ io.SetState(IsOn(io) ? State::kInactive : State::kActive).IgnoreError();
+}
+
+} // namespace
+
+Coro<Status> Blinky::BlinkLoop(CoroContext&,
+ uint32_t blink_count,
+ pw::chrono::SystemClock::duration interval) {
+ for (uint32_t blinked = 0; blinked < blink_count || blink_count == 0;
+ ++blinked) {
+ {
+ PW_LOG_INFO("LED blinking: OFF");
+ std::lock_guard lock(lock_);
+ TurnOff(*monochrome_led_);
+ }
+ co_await timer_.WaitFor(interval);
+ {
+ PW_LOG_INFO("LED blinking: ON");
+ std::lock_guard lock(lock_);
+ TurnOn(*monochrome_led_);
+ }
+ co_await timer_.WaitFor(interval);
+ }
+ {
+ std::lock_guard lock(lock_);
+ TurnOff(*monochrome_led_);
+ }
+ PW_LOG_INFO("Stopped blinking");
+ co_return OkStatus();
+}
+
+Blinky::Blinky()
+ : blink_task_(Coro<Status>::Empty(), [](Status) {
+ PW_LOG_ERROR("Failed to allocate blink loop coroutine.");
+ }) {}
+
+void Blinky::Init(Dispatcher& dispatcher,
+ Allocator& allocator,
+ pw::digital_io::DigitalInOut& monochrome_led) {
+ dispatcher_ = &dispatcher;
+ allocator_ = &allocator;
+
+ std::lock_guard lock(lock_);
+ monochrome_led_ = &monochrome_led;
+ monochrome_led_->Enable().IgnoreError();
+ TurnOff(*monochrome_led_);
+}
+
+Blinky::~Blinky() { blink_task_.Deregister(); }
+
+void Blinky::Toggle() {
+ blink_task_.Deregister();
+ PW_LOG_INFO("Toggling LED");
+ std::lock_guard lock(lock_);
+ ToggleIo(*monochrome_led_);
+}
+
+void Blinky::SetLed(bool on) {
+ blink_task_.Deregister();
+ std::lock_guard lock(lock_);
+ if (on) {
+ PW_LOG_INFO("Setting LED on");
+ TurnOn(*monochrome_led_);
+ } else {
+ PW_LOG_INFO("Setting LED off");
+ TurnOff(*monochrome_led_);
+ }
+}
+
+pw::Status Blinky::Blink(uint32_t blink_count, uint32_t interval_ms) {
+ if (blink_count == 0) {
+ PW_LOG_INFO("Blinking forever at a %ums interval", interval_ms);
+ } else {
+ PW_LOG_INFO(
+ "Blinking %u times at a %ums interval", blink_count, interval_ms);
+ }
+
+ pw::chrono::SystemClock::duration interval =
+ pw::chrono::SystemClock::for_at_least(
+ std::chrono::milliseconds(interval_ms));
+
+ blink_task_.Deregister();
+ CoroContext coro_cx(*allocator_);
+ blink_task_.SetCoro(BlinkLoop(coro_cx, blink_count, interval));
+ dispatcher_->Post(blink_task_);
+ return OkStatus();
+}
+
+bool Blinky::IsIdle() const {
+ std::lock_guard lock(lock_);
+ return !blink_task_.IsRegistered();
+}
+
+} // namespace demo
diff --git a/modules/blinky/blinky.h b/modules/blinky/blinky.h
new file mode 100644
index 0000000..33d0a97
--- /dev/null
+++ b/modules/blinky/blinky.h
@@ -0,0 +1,79 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <chrono>
+
+#include "modules/timer_future/timer_future.h"
+#include "pw_allocator/allocator.h"
+#include "pw_async2/coro_or_else_task.h"
+#include "pw_async2/dispatcher.h"
+#include "pw_chrono/system_clock.h"
+#include "pw_digital_io/digital_io.h"
+#include "pw_status/status.h"
+#include "pw_sync/interrupt_spin_lock.h"
+#include "pw_sync/lock_annotations.h"
+
+namespace demo {
+
+/// Simple component that blinks the on-board LED.
+class Blinky final {
+ public:
+ static constexpr uint32_t kDefaultIntervalMs = 1000;
+ static constexpr pw::chrono::SystemClock::duration kDefaultInterval =
+ pw::chrono::SystemClock::for_at_least(
+ std::chrono::milliseconds(kDefaultIntervalMs));
+
+ Blinky();
+ ~Blinky();
+
+ /// Injects this object's dependencies.
+ ///
+ /// This method MUST be called before using any other method.
+ void Init(pw::async2::Dispatcher& dispatcher,
+ pw::Allocator& allocator,
+ pw::digital_io::DigitalInOut& monochrome_led);
+
+ /// Turns the LED on if it is off, and off if it is on.
+ void Toggle() PW_LOCKS_EXCLUDED(lock_);
+
+ /// Sets the state of the LED.
+ void SetLed(bool on) PW_LOCKS_EXCLUDED(lock_);
+
+ /// Queues a sequence of call backs to blink the configured number of times.
+ ///
+ /// @param blink_count The number of times to blink the LED.
+ /// @param interval_ms The duration of a blink, in milliseconds.
+ pw::Status Blink(uint32_t blink_count, uint32_t interval_ms)
+ PW_LOCKS_EXCLUDED(lock_);
+
+ /// Returns whether this instance is currently blinking or not.
+ bool IsIdle() const PW_LOCKS_EXCLUDED(lock_);
+
+ private:
+ /// Creates a blinking coroutine.
+ pw::async2::Coro<pw::Status> BlinkLoop(
+ pw::async2::CoroContext&,
+ uint32_t blink_count,
+ pw::chrono::SystemClock::duration interval) PW_LOCKS_EXCLUDED(lock_);
+
+ pw::async2::Dispatcher* dispatcher_;
+ pw::Allocator* allocator_;
+ mutable pw::sync::InterruptSpinLock lock_;
+ pw::digital_io::DigitalInOut* monochrome_led_ PW_GUARDED_BY(lock_) = nullptr;
+ AsyncTimer timer_;
+ mutable pw::async2::CoroOrElseTask blink_task_;
+};
+
+} // namespace demo
diff --git a/modules/blinky/blinky.proto b/modules/blinky/blinky.proto
new file mode 100644
index 0000000..3d0eced
--- /dev/null
+++ b/modules/blinky/blinky.proto
@@ -0,0 +1,51 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+syntax = "proto3";
+
+package blinky;
+
+import "pw_protobuf_protos/common.proto";
+
+service Blinky {
+ // Toggles the LED on or off.
+ // If a previous `Blink` request is still running, stops it before toggling.
+ rpc ToggleLed(pw.protobuf.Empty) returns (pw.protobuf.Empty);
+
+ // Sets the LED on or off.
+ rpc SetLed(SetLedRequest) returns (pw.protobuf.Empty);
+
+ // Continuously blinks the board LED a specified number of times.
+ rpc Blink(BlinkRequest) returns (pw.protobuf.Empty);
+
+ // Returns true or false if LED blinking is idle.
+ rpc IsIdle(pw.protobuf.Empty) returns (BlinkIdleResponse);
+}
+
+message SetLedRequest {
+ bool on = 1;
+}
+
+message BlinkIdleResponse {
+ bool is_idle = 1;
+}
+
+message BlinkRequest {
+ // The interval at which to blink or pulse the LED, in milliseconds.
+ uint32 interval_ms = 1;
+
+ // The number of times to blink the LED.
+ // If unset, blinks forever.
+ // If 0, stops blinking.
+ optional uint32 blink_count = 2;
+}
diff --git a/modules/blinky/blinky_test.cc b/modules/blinky/blinky_test.cc
new file mode 100644
index 0000000..66038bf
--- /dev/null
+++ b/modules/blinky/blinky_test.cc
@@ -0,0 +1,168 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "modules/blinky/blinky.h"
+
+#include "pw_allocator/testing.h"
+#include "pw_async2/dispatcher.h"
+#include "pw_digital_io/digital_io_mock.h"
+#include "pw_thread/sleep.h"
+#include "pw_unit_test/framework.h"
+
+namespace demo {
+
+using AllocatorForTest = ::pw::allocator::test::AllocatorForTest<256>;
+using ::pw::async2::Dispatcher;
+
+// Test fixtures.
+
+class BlinkyTest : public ::testing::Test {
+ protected:
+ using Event = ::pw::digital_io::DigitalInOutMockImpl::Event;
+ using State = ::pw::digital_io::State;
+
+ static constexpr uint32_t kIntervalMs = 10;
+ static constexpr pw::chrono::SystemClock::duration kInterval =
+ pw::chrono::SystemClock::for_at_least(
+ std::chrono::milliseconds(kIntervalMs));
+
+ // TODO(b/352327457): Ideally this would use simulated time, but no
+ // simulated system timer exists yet. For now, relax the constraint by
+ // checking that the LED was in the right state for _at least_ the expected
+ // number of intervals. On some platforms, the fake LED is implemented using
+ // threads, and may sleep a bit longer.
+ BlinkyTest() : clock_(pw::chrono::VirtualSystemClock::RealClock()) {}
+
+ pw::InlineDeque<Event>::iterator FirstActive() {
+ pw::InlineDeque<Event>& events = monochrome_led_.events();
+ pw::InlineDeque<Event>::iterator event = events.begin();
+ while (event != events.end()) {
+ if (event->state == State::kActive) {
+ break;
+ }
+ ++event;
+ }
+ return event;
+ }
+
+ uint32_t ToMs(pw::chrono::SystemClock::duration interval) {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(interval)
+ .count();
+ }
+
+ static constexpr const size_t kEventCapacity = 256;
+
+ AllocatorForTest allocator_;
+ Dispatcher dispatcher_;
+ pw::chrono::VirtualSystemClock& clock_;
+ pw::digital_io::DigitalInOutMock<kEventCapacity> monochrome_led_;
+};
+
+// Unit tests.
+
+TEST_F(BlinkyTest, Toggle) {
+ Blinky blinky;
+ blinky.Init(dispatcher_, allocator_, monochrome_led_);
+
+ auto start = clock_.now();
+ blinky.Toggle();
+ pw::this_thread::sleep_for(kInterval * 1);
+ blinky.Toggle();
+ pw::this_thread::sleep_for(kInterval * 2);
+ blinky.Toggle();
+ pw::this_thread::sleep_for(kInterval * 3);
+ blinky.Toggle();
+
+ auto event = FirstActive();
+ ASSERT_NE(event, monochrome_led_.events().end());
+ EXPECT_EQ(event->state, State::kActive);
+ EXPECT_GE(ToMs(event->timestamp - start), kIntervalMs * 0);
+ start = event->timestamp;
+
+ ASSERT_NE(++event, monochrome_led_.events().end());
+ EXPECT_EQ(event->state, State::kInactive);
+ EXPECT_GE(ToMs(event->timestamp - start), kIntervalMs * 1);
+ start = event->timestamp;
+
+ ASSERT_NE(++event, monochrome_led_.events().end());
+ EXPECT_EQ(event->state, State::kActive);
+ EXPECT_GE(ToMs(event->timestamp - start), kIntervalMs * 2);
+ start = event->timestamp;
+
+ ASSERT_NE(++event, monochrome_led_.events().end());
+ EXPECT_EQ(event->state, State::kInactive);
+ EXPECT_GE(ToMs(event->timestamp - start), kIntervalMs * 3);
+}
+
+TEST_F(BlinkyTest, Blink) {
+ Blinky blinky;
+ blinky.Init(dispatcher_, allocator_, monochrome_led_);
+
+ auto start = clock_.now();
+ EXPECT_EQ(blinky.Blink(1, kIntervalMs), pw::OkStatus());
+ while (!blinky.IsIdle()) {
+ dispatcher_.RunUntilStalled().IgnorePoll();
+ pw::this_thread::sleep_for(kInterval);
+ }
+
+ auto event = FirstActive();
+ ASSERT_NE(event, monochrome_led_.events().end());
+ EXPECT_EQ(event->state, State::kActive);
+ EXPECT_GE(ToMs(event->timestamp - start), kIntervalMs);
+ start = event->timestamp;
+
+ ASSERT_NE(++event, monochrome_led_.events().end());
+ EXPECT_EQ(event->state, State::kInactive);
+ EXPECT_GE(ToMs(event->timestamp - start), kIntervalMs);
+}
+
+TEST_F(BlinkyTest, BlinkMany) {
+ Blinky blinky;
+ blinky.Init(dispatcher_, allocator_, monochrome_led_);
+
+ auto start = clock_.now();
+ EXPECT_EQ(blinky.Blink(100, kIntervalMs), pw::OkStatus());
+ while (!blinky.IsIdle()) {
+ dispatcher_.RunUntilStalled().IgnorePoll();
+ pw::this_thread::sleep_for(kInterval);
+ }
+
+ // Every "on" and "off" is recorded.
+ EXPECT_GE(monochrome_led_.events().size(), 200);
+ EXPECT_GE(ToMs(clock_.now() - start), kIntervalMs * 200);
+}
+
+TEST_F(BlinkyTest, BlinkSlow) {
+ Blinky blinky;
+ blinky.Init(dispatcher_, allocator_, monochrome_led_);
+
+ auto start = clock_.now();
+ EXPECT_EQ(blinky.Blink(1, kIntervalMs * 32), pw::OkStatus());
+ while (!blinky.IsIdle()) {
+ dispatcher_.RunUntilStalled().IgnorePoll();
+ pw::this_thread::sleep_for(kInterval);
+ }
+
+ auto event = FirstActive();
+ ASSERT_NE(event, monochrome_led_.events().end());
+ EXPECT_EQ(event->state, State::kActive);
+ EXPECT_GE(ToMs(event->timestamp - start), kIntervalMs * 32);
+ start = event->timestamp;
+
+ ASSERT_NE(++event, monochrome_led_.events().end());
+ EXPECT_EQ(event->state, State::kInactive);
+ EXPECT_GE(ToMs(event->timestamp - start), kIntervalMs * 32);
+}
+
+} // namespace demo
diff --git a/modules/blinky/service.cc b/modules/blinky/service.cc
new file mode 100644
index 0000000..f795ca0
--- /dev/null
+++ b/modules/blinky/service.cc
@@ -0,0 +1,56 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#define PW_LOG_MODULE_NAME "BLINKY"
+
+#include "modules/blinky/service.h"
+
+#include "pw_assert/check.h"
+
+namespace demo {
+
+void BlinkyService::Init(pw::async2::Dispatcher& dispatcher,
+ pw::Allocator& allocator,
+ pw::digital_io::DigitalInOut& monochrome_led) {
+ blinky_.Init(dispatcher, allocator, monochrome_led);
+ // Start binking once every 1000ms.
+ PW_CHECK_OK(blinky_.Blink(/*blink_count=*/0, /*interval_ms=*/1000));
+}
+
+pw::Status BlinkyService::ToggleLed(const pw_protobuf_Empty&,
+ pw_protobuf_Empty&) {
+ blinky_.Toggle();
+ return pw::OkStatus();
+}
+
+pw::Status BlinkyService::SetLed(const blinky_SetLedRequest& request,
+ pw_protobuf_Empty&) {
+ blinky_.SetLed(request.on);
+ return pw::OkStatus();
+}
+
+pw::Status BlinkyService::IsIdle(const pw_protobuf_Empty&,
+ blinky_BlinkIdleResponse& response) {
+ response.is_idle = blinky_.IsIdle();
+ return pw::OkStatus();
+}
+
+pw::Status BlinkyService::Blink(const blinky_BlinkRequest& request,
+ pw_protobuf_Empty&) {
+ uint32_t interval_ms = request.interval_ms == 0 ? Blinky::kDefaultIntervalMs
+ : request.interval_ms;
+ uint32_t blink_count = request.has_blink_count ? request.blink_count : 0;
+ return blinky_.Blink(blink_count, interval_ms);
+}
+
+} // namespace demo
diff --git a/modules/blinky/service.h b/modules/blinky/service.h
new file mode 100644
index 0000000..c1d2266
--- /dev/null
+++ b/modules/blinky/service.h
@@ -0,0 +1,43 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "modules/blinky/blinky.h"
+#include "modules/blinky/blinky_pb/blinky.rpc.pb.h"
+#include "pw_allocator/allocator.h"
+#include "pw_async2/dispatcher.h"
+
+namespace demo {
+
+class BlinkyService final
+ : public ::blinky::pw_rpc::nanopb::Blinky::Service<BlinkyService> {
+ public:
+ void Init(pw::async2::Dispatcher& dispatcher,
+ pw::Allocator& allocator,
+ pw::digital_io::DigitalInOut& monochrome_led);
+
+ pw::Status ToggleLed(const pw_protobuf_Empty&, pw_protobuf_Empty&);
+
+ pw::Status SetLed(const blinky_SetLedRequest& request, pw_protobuf_Empty&);
+
+ pw::Status Blink(const blinky_BlinkRequest& request, pw_protobuf_Empty&);
+
+ pw::Status IsIdle(const pw_protobuf_Empty&,
+ blinky_BlinkIdleResponse& response);
+
+ private:
+ Blinky blinky_;
+};
+
+} // namespace demo
diff --git a/tools/miniterm.py b/modules/timer_future/BUILD.bazel
similarity index 60%
rename from tools/miniterm.py
rename to modules/timer_future/BUILD.bazel
index 90487a6..ce6f0a4 100644
--- a/tools/miniterm.py
+++ b/modules/timer_future/BUILD.bazel
@@ -1,4 +1,4 @@
-# Copyright 2023 The Pigweed Authors
+# Copyright 2024 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
@@ -11,9 +11,16 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
-"""Spin up a miniterm for talking to the board."""
-from serial.tools import miniterm
+package(default_visibility = ["//visibility:public"])
-if __name__ == "__main__":
- miniterm.main(default_baudrate=115200)
+cc_library(
+ name = "timer_future",
+ srcs = ["timer_future.cc"],
+ hdrs = ["timer_future.h"],
+ deps = [
+ "@pigweed//pw_async2:dispatcher",
+ "@pigweed//pw_chrono:system_clock",
+ "@pigweed//pw_chrono:system_timer",
+ ],
+)
diff --git a/modules/timer_future/timer_future.cc b/modules/timer_future/timer_future.cc
new file mode 100644
index 0000000..6d08972
--- /dev/null
+++ b/modules/timer_future/timer_future.cc
@@ -0,0 +1,53 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "modules/timer_future/timer_future.h"
+
+namespace demo {
+
+using ::pw::async2::Context;
+using ::pw::async2::Pending;
+using ::pw::async2::Poll;
+using ::pw::async2::Ready;
+using ::pw::async2::WaitReason;
+using ::pw::chrono::SystemClock;
+using ::pw::chrono::SystemTimer;
+
+AsyncTimer::AsyncTimer()
+ : waker_(),
+ deadline_(),
+ timer_([this](SystemClock::time_point) { std::move(waker_).Wake(); }) {}
+
+TimerFuture AsyncTimer::WaitUntil(SystemClock::time_point deadline) {
+ timer_.Cancel();
+ waker_.Clear();
+ deadline_ = deadline;
+ timer_.InvokeAt(deadline);
+ return TimerFuture(*this);
+}
+
+TimerFuture AsyncTimer::WaitFor(SystemClock::duration duration) {
+ return WaitUntil(SystemClock::now() + duration);
+}
+
+Poll<> TimerFuture::Pend(Context& cx) {
+ async_timer_.waker_ = cx.GetWaker(WaitReason::Unspecified());
+ if (SystemClock::now() < async_timer_.deadline_) {
+ return Pending();
+ }
+ async_timer_.waker_.Clear();
+ return Ready();
+}
+
+} // namespace demo
\ No newline at end of file
diff --git a/modules/timer_future/timer_future.h b/modules/timer_future/timer_future.h
new file mode 100644
index 0000000..d622455
--- /dev/null
+++ b/modules/timer_future/timer_future.h
@@ -0,0 +1,57 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_async2/dispatcher.h"
+#include "pw_chrono/system_clock.h"
+#include "pw_chrono/system_timer.h"
+
+namespace demo {
+
+class TimerFuture;
+
+class AsyncTimer {
+ public:
+ // AsyncTimer is not movable due to the timer keeping a reference to the
+ // waker.
+ AsyncTimer();
+ AsyncTimer(const AsyncTimer&) = delete;
+ AsyncTimer& operator=(const AsyncTimer&) = delete;
+ AsyncTimer(AsyncTimer&&) = delete;
+ AsyncTimer& operator=(AsyncTimer&&) = delete;
+ ~AsyncTimer() { timer_.Cancel(); }
+
+ TimerFuture WaitUntil(pw::chrono::SystemClock::time_point deadline);
+ TimerFuture WaitFor(pw::chrono::SystemClock::duration duration);
+
+ private:
+ friend class TimerFuture;
+
+ pw::async2::Waker waker_;
+ pw::chrono::SystemClock::time_point deadline_;
+ pw::chrono::SystemTimer timer_;
+};
+
+class TimerFuture {
+ public:
+ pw::async2::Poll<> Pend(pw::async2::Context& cx);
+
+ private:
+ friend class AsyncTimer;
+
+ TimerFuture(AsyncTimer& async_timer) : async_timer_(async_timer) {}
+ AsyncTimer& async_timer_;
+};
+
+} // namespace demo
\ No newline at end of file
diff --git a/pigweed.json b/pigweed.json
index ea9b21d..1778b2a 100644
--- a/pigweed.json
+++ b/pigweed.json
@@ -5,8 +5,35 @@
"upload_local_results": true,
"programs": {
"default": [
- ["build", "--config=presubmit"],
- ["test", "//..."]
+ [
+ "build",
+ "--config=presubmit"
+ ],
+ [
+ "build",
+ "--config=rp2040",
+ "//..."
+ ],
+ [
+ "test",
+ "//..."
+ ],
+ [
+ "test",
+ "--config=asan",
+ "//..."
+ ],
+ [
+ "test",
+ "--config=tsan",
+ "//...",
+ "--runs_per_test=10"
+ ],
+ [
+ "test",
+ "--config=ubsan",
+ "//..."
+ ]
]
}
}
diff --git a/requirements.in b/requirements.in
deleted file mode 100644
index 120041f..0000000
--- a/requirements.in
+++ /dev/null
@@ -1 +0,0 @@
-pyserial ~= 3.5
diff --git a/requirements_lock.txt b/requirements_lock.txt
deleted file mode 100644
index a7b06a3..0000000
--- a/requirements_lock.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-#
-# This file is autogenerated by pip-compile with Python 3.11
-# by the following command:
-#
-# bazel run //:pip_requirements.update
-#
-pyserial==3.5 \
- --hash=sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb \
- --hash=sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0
- # via -r requirements.in
diff --git a/src/BUILD.bazel b/src/BUILD.bazel
deleted file mode 100644
index 8ec1218..0000000
--- a/src/BUILD.bazel
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright 2023 The Pigweed Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-load("//:echo.bzl", "stm32_cc_binary")
-
-load(
- "@pigweed//pw_build:pigweed.bzl",
- "pw_cc_test",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-cc_binary(
- name = "echo",
- srcs = ["echo.cc"],
- malloc = select({
- "@platforms//cpu:armv7e-m": "@pigweed//pw_malloc",
- "//conditions:default": "@bazel_tools//tools/cpp:malloc",
- }),
- deps = [
- "@pigweed//pw_assert:backend_impl",
- "@pigweed//pw_assert:check_backend_impl",
- "@pigweed//pw_assert:assert_backend_impl",
- "@pigweed//pw_boot",
- "@pigweed//pw_sys_io",
- ] + select({
- "@platforms//cpu:armv7e-m": [
- "@pigweed//pw_toolchain/arm_gcc:arm_none_eabi_gcc_support",
- "@pigweed//targets/stm32f429i_disc1:basic_linker_script",
- "@pigweed//targets/stm32f429i_disc1:pre_init",
- ],
- "//conditions:default": [],
- }),
-)
-
-stm32_cc_binary(
- name = "echo.elf",
- binary = ":echo",
-)
-
-cc_library(
- name = "multiply",
- hdrs = ["multiply.h"],
- srcs = ["multiply.cc"],
-)
-
-# Note: Must use pw_cc_test instead of cc_test to enable on-device execution.
-# See http://pigweed.dev/bazel#pw_cc_test for more details.
-pw_cc_test(
- name = "multiply_test",
- srcs = ["multiply_test.cc"],
- deps = [
- ":multiply",
- ]
-)
-
-stm32_cc_binary(
- name = "multiply_test.elf",
- binary = ":multiply_test",
- testonly = True,
-)
diff --git a/src/echo.cc b/src/echo.cc
deleted file mode 100644
index a99fa4f..0000000
--- a/src/echo.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2023 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-#include <cstddef>
-
-#include "pw_sys_io/sys_io.h"
-
-
-int main() {
- while (true) {
- std::byte data;
- pw::sys_io::ReadByte(&data).IgnoreError();
- pw::sys_io::WriteByte(data).IgnoreError();
- }
- return 0;
-}
diff --git a/src/multiply.h b/src/multiply.h
deleted file mode 100644
index 3a7115a..0000000
--- a/src/multiply.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2024 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-// Demonstration function; multiplies a and b, returns result.
-int Multiply(int a, int b);
diff --git a/src/multiply_test.cc b/src/multiply_test.cc
deleted file mode 100644
index 81a548b..0000000
--- a/src/multiply_test.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2024 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-// Note: Bazel/monorepo style include path, not Pigweed style.
-#include "src/multiply.h"
-
-#include "pw_unit_test/framework.h"
-
-namespace {
-
-TEST(Multiply, TwoTimesThreeIsSix) {
- ASSERT_EQ(Multiply(2, 3), 6);
-}
-
-} // namespace
diff --git a/system/BUILD.bazel b/system/BUILD.bazel
new file mode 100644
index 0000000..d005a35
--- /dev/null
+++ b/system/BUILD.bazel
@@ -0,0 +1,49 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+load("@pigweed//pw_build:compatibility.bzl", "host_backend_alias")
+
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+ name = "module_config",
+ defines = [
+ # TODO: https://pwbug.dev/352389854 - Move this to per-platform config
+ # when platform flags are implemented.
+ #
+ # Allow us to capture two 64bit pointers in a pw::function.
+ "PW_FUNCTION_INLINE_CALLABLE_SIZE=16UL",
+ "PW_ASSERT_BASIC_ACTION=PW_ASSERT_BASIC_ACTION_EXIT",
+ ],
+)
+
+label_flag(
+ name = "system",
+ build_setting_default = ":unspecified_backend",
+)
+
+host_backend_alias(
+ name = "unspecified_backend",
+ backend = "//targets/host:system",
+)
+
+cc_library(
+ name = "headers",
+ hdrs = [
+ "system.h",
+ ],
+ deps = [
+ "@pigweed//pw_digital_io",
+ ],
+)
diff --git a/system/system.h b/system/system.h
new file mode 100644
index 0000000..7d91287
--- /dev/null
+++ b/system/system.h
@@ -0,0 +1,31 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_digital_io/digital_io.h"
+
+// The functions in this file return specific implementations of singleton types
+// provided by the system.
+
+namespace demo::system {
+
+/// Initializes the system. This must be called before anything else in `main`.
+void Init();
+
+/// Starts the main system scheduler. This function never returns.
+[[noreturn]] void Start();
+
+pw::digital_io::DigitalInOut& MonochromeLed();
+
+} // namespace demo::system
diff --git a/targets/BUILD.bazel b/targets/BUILD.bazel
index 5a5ce5b..e83b707 100644
--- a/targets/BUILD.bazel
+++ b/targets/BUILD.bazel
@@ -1,4 +1,4 @@
-# Copyright 2023 The Pigweed Authors
+# Copyright 2024 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
@@ -12,15 +12,14 @@
# License for the specific language governing permissions and limitations under
# the License.
-platform(
- name = "stm32",
- constraint_values = [
- "@pigweed//pw_malloc_freelist:backend",
- "@platforms//cpu:armv7e-m",
- "@platforms//os:none",
- "@pigweed//pw_sys_io_baremetal_stm32f429:compatible",
- # Target the cortex-m4. Bazel selects the right toolchain based on
- # this.
- "@pw_toolchain//constraints/arm_mcpu:cortex-m4+nofp",
- ],
+package(default_visibility = ["//visibility:public"])
+
+# TODO: https://github.com/bazelbuild/bazel/issues/22457 - Move this into the
+# platform once it no longer propagates to the exec configuration.
+alias(
+ name = "malloc",
+ actual = select({
+ "@pico-sdk//bazel/constraint:rp2040": "@pico-sdk//src/rp2_common/pico_malloc",
+ "//conditions:default": "@bazel_tools//tools/cpp:malloc",
+ }),
)
diff --git a/targets/host/BUILD.bazel b/targets/host/BUILD.bazel
new file mode 100644
index 0000000..e0910b6
--- /dev/null
+++ b/targets/host/BUILD.bazel
@@ -0,0 +1,36 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+load("@pigweed//pw_build:compatibility.bzl", "incompatible_with_mcu")
+
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+ name = "system",
+ srcs = [
+ "led.cc",
+ "system.cc",
+ ],
+ implementation_deps = [
+ "@pigweed//pw_channel",
+ "@pigweed//pw_channel:stream_channel",
+ "@pigweed//pw_digital_io",
+ "@pigweed//pw_digital_io:digital_io_mock",
+ "@pigweed//pw_multibuf:simple_allocator",
+ "@pigweed//pw_system:async",
+ "@pigweed//pw_system:io",
+ ],
+ target_compatible_with = incompatible_with_mcu(),
+ deps = ["//system:headers"],
+)
diff --git a/src/multiply.cc b/targets/host/led.cc
similarity index 65%
rename from src/multiply.cc
rename to targets/host/led.cc
index b1f6871..c8f4773 100644
--- a/src/multiply.cc
+++ b/targets/host/led.cc
@@ -12,6 +12,15 @@
// License for the specific language governing permissions and limitations under
// the License.
-int Multiply(int a, int b) {
- return a * b;
+#include "pw_digital_io/digital_io_mock.h"
+#include "system/system.h"
+
+namespace demo::system {
+
+pw::digital_io::DigitalInOut& MonochromeLed() {
+ static constexpr size_t kCapacity = 256;
+ static pw::digital_io::DigitalInOutMock<kCapacity> digital_io_mock;
+ return digital_io_mock;
}
+
+} // namespace demo::system
diff --git a/targets/host/system.cc b/targets/host/system.cc
new file mode 100644
index 0000000..8a221af
--- /dev/null
+++ b/targets/host/system.cc
@@ -0,0 +1,99 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "system/system.h"
+
+#include <signal.h>
+#include <stdio.h>
+
+#include "pw_assert/check.h"
+#include "pw_channel/stream_channel.h"
+#include "pw_digital_io/digital_io.h"
+#include "pw_multibuf/simple_allocator.h"
+#include "pw_system/io.h"
+#include "pw_system/system.h"
+#include "pw_thread_stl/options.h"
+
+using ::pw::channel::StreamChannel;
+using ::pw::digital_io::DigitalIn;
+using ::pw::digital_io::State;
+
+extern "C" {
+
+void CtrlCSignalHandler(int /* ignored */) {
+ printf("\nCtrl-C received; simulator exiting immediately...\n");
+ // Skipping the C++ destructors since we want to exit immediately.
+ _exit(0);
+}
+
+} // extern "C"
+
+void InstallCtrlCSignalHandler() {
+ // Catch Ctrl-C to force a 0 exit code (success) to avoid signaling an error
+ // for intentional exits. For example, VSCode shows an alarming dialog on
+ // non-zero exit, which is confusing for users intentionally quitting.
+ signal(SIGINT, CtrlCSignalHandler);
+}
+
+namespace {
+class VirtualInput : public DigitalIn {
+ public:
+ VirtualInput(State state) : state_(state) {}
+
+ private:
+ pw::Status DoEnable(bool) override { return pw::OkStatus(); }
+ pw::Result<State> DoGetState() override { return state_; }
+
+ State state_;
+};
+
+VirtualInput io_sw_a(State::kInactive);
+VirtualInput io_sw_b(State::kInactive);
+VirtualInput io_sw_x(State::kInactive);
+VirtualInput io_sw_y(State::kInactive);
+
+} // namespace
+
+namespace demo::system {
+
+void Init() {}
+
+void Start() {
+ InstallCtrlCSignalHandler();
+ printf("==========================================\n");
+ printf("=== Pigweed Quickstart: Host Simulator ===\n");
+ printf("==========================================\n");
+ printf("Simulator is now running. To connect with a console,\n");
+ printf("either run one in a new terminal:\n");
+ printf("\n");
+ printf(" $ bazelisk run //blinky:simulator_console\n");
+ printf("\n");
+ printf("one from VSCode under the 'Bazel Build Targets' explorer tab.\n");
+ printf("\n");
+ printf("Press Ctrl-C to exit\n");
+
+ static std::byte channel_buffer[16384];
+ static pw::multibuf::SimpleAllocator multibuf_alloc(channel_buffer,
+ pw::System().allocator());
+ static pw::NoDestructor<StreamChannel> channel(multibuf_alloc,
+ pw::system::GetReader(),
+ pw::thread::stl::Options(),
+ pw::system::GetWriter(),
+ pw::thread::stl::Options());
+
+ pw::SystemStart(*channel);
+ PW_UNREACHABLE;
+}
+
+} // namespace demo::system
diff --git a/targets/rp2/BUILD.bazel b/targets/rp2/BUILD.bazel
new file mode 100644
index 0000000..3cc2ea9
--- /dev/null
+++ b/targets/rp2/BUILD.bazel
@@ -0,0 +1,134 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+package(default_visibility = ["//visibility:public"])
+
+# This is an incomplete platform, do NOT try to pass this
+# as a --platforms flag value. Use :rp2040 or :rp2350.
+platform(
+ name = "rp2_common",
+ constraint_values = [
+ "@freertos//:disable_task_statics",
+ "@pigweed//pw_build/constraints/rtos:freertos",
+ "@pigweed//pw_build/constraints/chipset:rp2040", # TODO: https://pwbug.dev/343487589 - Use Pico SDK constraints.
+ "@pigweed//pw_cpu_exception:enabled",
+ "@pigweed//pw_interrupt_cortex_m:backend",
+ "@platforms//os:none",
+ ],
+)
+
+platform(
+ name = "rp2040",
+ constraint_values = [
+ "@freertos//:port_ARM_CM0",
+ "@pico-sdk//bazel/constraint:rp2040",
+ # For toolchain selection.
+ "@platforms//cpu:armv6-m",
+ "@pw_toolchain//constraints/arm_mcpu:cortex-m0",
+ ],
+ parents = [":rp2_common"],
+)
+
+platform(
+ name = "rp2350",
+ constraint_values = [
+ "@freertos//:port_ARM_CM33_NTZ",
+ "@pico-sdk//bazel/constraint:rp2350",
+ # For toolchain selection.
+ "@platforms//cpu:armv8-m",
+ "@pw_toolchain//constraints/arm_mcpu:cortex-m33",
+ ],
+ parents = [":rp2_common"],
+)
+
+cc_library(
+ name = "extra_platform_libs",
+ deps = [
+ "@pico-sdk//src/rp2_common/pico_stdlib:pico_stdlib",
+ "@pigweed//pw_tokenizer:linker_script",
+ ] + select({
+ "@rules_cc//cc/compiler:clang": [
+ "@pigweed//pw_libcxx",
+ ],
+ "//conditions:default": [],
+ }),
+ alwayslink = 1,
+)
+
+cc_library(
+ name = "freertos_config",
+ hdrs = [
+ "config/FreeRTOSConfig.h",
+ ],
+ includes = ["config"],
+ deps = ["@pigweed//third_party/freertos:config_assert"],
+)
+
+cc_library(
+ name = "thread_config_overrides",
+ defines = [
+ "PW_THREAD_FREERTOS_CONFIG_JOINING_ENABLED=1",
+ ],
+)
+
+cc_library(
+ name = "system",
+ srcs = [
+ "led.cc",
+ "system.cc",
+ ],
+ implementation_deps = [
+ "//system:headers",
+ "@pico-sdk//src/rp2_common/cmsis:cmsis_core",
+ "@pico-sdk//src/rp2_common/hardware_adc",
+ "@pico-sdk//src/rp2_common/hardware_exception:hardware_exception",
+ "@pico-sdk//src/rp2_common/pico_stdlib:pico_stdlib",
+ "@pigweed//pw_channel",
+ "@pigweed//pw_channel:rp2_stdio_channel",
+ "@pigweed//pw_cpu_exception:entry_backend_impl",
+ "@pigweed//pw_digital_io_rp2040",
+ "@pigweed//pw_multibuf:simple_allocator",
+ "@pigweed//pw_system:async",
+ "@pigweed//third_party/freertos:support",
+ ],
+ deps = ["//system:headers"],
+ alwayslink = 1,
+)
+
+cc_library(
+ name = "unit_test_rpc_main",
+ testonly = True,
+ srcs = ["unit_test_rpc_main.cc"],
+ deps = [
+ "//system",
+ "@pigweed//pw_log",
+ "@pigweed//pw_system:async",
+ "@pigweed//pw_unit_test:rpc_service",
+
+ # These should be provided by pw_system:async.
+ "@pigweed//pw_assert:assert_backend_impl",
+ "@pigweed//pw_assert:check_backend_impl",
+ "@pigweed//pw_log:backend_impl",
+ "@pigweed//pw_system:extra_platform_libs",
+ ],
+)
+
+# Several tests store `TestWorker` thread contexts in their unit test object.
+# Since these thread stacks are currently 32k, the test objects may be large.
+cc_library(
+ name = "64k_unit_tests",
+ defines = [
+ "PW_UNIT_TEST_CONFIG_MEMORY_POOL_SIZE=65536",
+ ],
+)
diff --git a/targets/rp2/binary.bzl b/targets/rp2/binary.bzl
new file mode 100644
index 0000000..46c146f
--- /dev/null
+++ b/targets/rp2/binary.bzl
@@ -0,0 +1,103 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+"""Project-specific bazel transitions for rp2xxx-series chips.
+
+TODO: b/301334234 - Use platform-based flags and retire these transitions.
+"""
+
+load("@pigweed//pw_build:merge_flags.bzl", "merge_flags_for_transition_impl", "merge_flags_for_transition_outputs")
+load("@pigweed//targets/rp2040:transition.bzl", "RP2_SYSTEM_FLAGS")
+
+_COMMON_FLAGS = merge_flags_for_transition_impl(
+ base = RP2_SYSTEM_FLAGS,
+ override = {
+ "//system:system": "//targets/rp2:system",
+ "@freertos//:freertos_config": "//targets/rp2:freertos_config",
+ "@pico-sdk//bazel/config:PICO_CLIB": "llvm_libc",
+ "@pico-sdk//bazel/config:PICO_TOOLCHAIN": "clang",
+ "@pigweed//pw_build:default_module_config": "//system:module_config",
+ "@pigweed//pw_system:extra_platform_libs": "//targets/rp2:extra_platform_libs",
+ "@pigweed//pw_system:io_backend": "@pigweed//pw_system:sys_io_target_io",
+ "@pigweed//pw_toolchain:cortex-m_toolchain_kind": "clang",
+ "@pigweed//pw_unit_test:config_override": "//targets/rp2:64k_unit_tests",
+ },
+)
+
+_RP2040_FLAGS = {
+ "//command_line_option:platforms": "//targets/rp2:rp2040",
+}
+
+_RP2350_FLAGS = {
+ "//command_line_option:platforms": "//targets/rp2:rp2350",
+}
+
+def _rp2_transition(device_specific_flags):
+ def _rp2_transition_impl(settings, attr):
+ # buildifier: disable=unused-variable
+ _ignore = settings, attr
+ return merge_flags_for_transition_impl(
+ base = _COMMON_FLAGS,
+ override = device_specific_flags,
+ )
+
+ return transition(
+ implementation = _rp2_transition_impl,
+ inputs = [],
+ outputs = merge_flags_for_transition_outputs(
+ base = _COMMON_FLAGS,
+ override = device_specific_flags,
+ ),
+ )
+
+def _rp2_binary_impl(ctx):
+ out = ctx.actions.declare_file(ctx.label.name)
+ ctx.actions.symlink(output = out, is_executable = True, target_file = ctx.executable.binary)
+ return [DefaultInfo(files = depset([out]), executable = out)]
+
+rp2040_binary = rule(
+ _rp2_binary_impl,
+ attrs = {
+ "binary": attr.label(
+ doc = "cc_binary to build for the rp2040",
+ cfg = _rp2_transition(_RP2040_FLAGS),
+ executable = True,
+ mandatory = True,
+ ),
+ "_allowlist_function_transition": attr.label(
+ default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
+ ),
+ },
+ doc = "Builds the specified binary for the rp2040 platform",
+ # This target is for rp2040 and can't be run on host.
+ executable = False,
+)
+
+rp2350_binary = rule(
+ _rp2_binary_impl,
+ attrs = {
+ "binary": attr.label(
+ doc = "cc_binary to build for the rp2040",
+ cfg = _rp2_transition(_RP2350_FLAGS),
+ executable = True,
+ mandatory = True,
+ ),
+ "_allowlist_function_transition": attr.label(
+ default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
+ ),
+ },
+ doc = "Builds the specified binary for the rp2350 platform",
+ # This target is for rp2350 and can't be run on host.
+ executable = False,
+)
diff --git a/targets/rp2/config/FreeRTOSConfig.h b/targets/rp2/config/FreeRTOSConfig.h
new file mode 100644
index 0000000..65a5433
--- /dev/null
+++ b/targets/rp2/config/FreeRTOSConfig.h
@@ -0,0 +1,113 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <stdint.h>
+
+// Disable formatting to make it easier to compare with other config files.
+// clang-format off
+
+extern uint32_t SystemCoreClock;
+
+#define vPortSVCHandler SVC_Handler
+#define xPortPendSVHandler PendSV_Handler
+#define xPortSysTickHandler SysTick_Handler
+
+#if __ARM_FP
+#define configENABLE_FPU 1
+#else
+#define configENABLE_FPU 0
+#endif // __ARM_FP
+
+// TODO: Set up the MPU.
+#define configENABLE_MPU 0
+#define configENABLE_TRUSTZONE 0
+#define configRUN_FREERTOS_SECURE_ONLY 1
+
+#define configUSE_PREEMPTION 1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configUSE_TICKLESS_IDLE 0
+#define configCPU_CLOCK_HZ (SystemCoreClock)
+#define configTICK_RATE_HZ ((TickType_t)1000)
+#define configMAX_PRIORITIES 5
+#define configMINIMAL_STACK_SIZE ((uint32_t)(256))
+#define configMAX_TASK_NAME_LEN 16
+#define configUSE_16_BIT_TICKS 0
+#define configIDLE_SHOULD_YIELD 1
+#define configUSE_TASK_NOTIFICATIONS 1
+#define configTASK_NOTIFICATION_ARRAY_ENTRIES 3
+#define configUSE_MUTEXES 1
+#define configUSE_RECURSIVE_MUTEXES 0
+#define configUSE_COUNTING_SEMAPHORES 1
+#define configQUEUE_REGISTRY_SIZE 10
+#define configUSE_QUEUE_SETS 0
+#define configUSE_TIME_SLICING 1
+#define configUSE_NEWLIB_REENTRANT 0
+#define configENABLE_BACKWARD_COMPATIBILITY 0
+#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5
+#define configSTACK_DEPTH_TYPE uint32_t
+#define configMESSAGE_BUFFER_LENGTH_TYPE size_t
+
+#define configSUPPORT_STATIC_ALLOCATION 1
+#define configSUPPORT_DYNAMIC_ALLOCATION 0
+#define configTOTAL_HEAP_SIZE ((size_t)(1 * 1024))
+#define configAPPLICATION_ALLOCATED_HEAP 1
+
+#define configUSE_IDLE_HOOK 0
+#define configUSE_TICK_HOOK 0
+#define configCHECK_FOR_STACK_OVERFLOW 0
+#define configUSE_MALLOC_FAILED_HOOK 0
+#define configUSE_DAEMON_TASK_STARTUP_HOOK 0
+
+#define configGENERATE_RUN_TIME_STATS 0
+#define configUSE_TRACE_FACILITY 0
+#define configUSE_STATS_FORMATTING_FUNCTIONS 0
+
+#define configUSE_CO_ROUTINES 0
+#define configMAX_CO_ROUTINE_PRIORITIES 1
+
+#define configUSE_TIMERS 1
+#define configTIMER_TASK_PRIORITY 3
+#define configTIMER_QUEUE_LENGTH 10
+#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE
+
+/* __NVIC_PRIO_BITS in CMSIS */
+#define configPRIO_BITS 4
+
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY ((1U << (configPRIO_BITS)) - 1)
+#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 2
+#define configKERNEL_INTERRUPT_PRIORITY (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
+#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
+
+// Instead of defining configASSERT(), include a header that provides a
+// definition that redirects to pw_assert.
+#include "pw_third_party/freertos/config_assert.h"
+
+#define INCLUDE_vTaskPrioritySet 1
+#define INCLUDE_uxTaskPriorityGet 1
+#define INCLUDE_vTaskDelete 1
+#define INCLUDE_vTaskSuspend 1
+#define INCLUDE_xResumeFromISR 1
+#define INCLUDE_vTaskDelayUntil 1
+#define INCLUDE_vTaskDelay 1
+#define INCLUDE_xTaskGetSchedulerState 1
+#define INCLUDE_xTaskGetCurrentTaskHandle 1
+#define INCLUDE_uxTaskGetStackHighWaterMark 0
+#define INCLUDE_xTaskGetIdleTaskHandle 0
+#define INCLUDE_eTaskGetState 0
+#define INCLUDE_xEventGroupSetBitFromISR 1
+#define INCLUDE_xTimerPendFunctionCall 0
+#define INCLUDE_xTaskAbortDelay 0
+#define INCLUDE_xTaskGetHandle 0
+#define INCLUDE_xTaskResumeFromISR 1
diff --git a/targets/rp2/led.cc b/targets/rp2/led.cc
new file mode 100644
index 0000000..a22bcd5
--- /dev/null
+++ b/targets/rp2/led.cc
@@ -0,0 +1,31 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pico/stdlib.h"
+#include "pw_digital_io_rp2040/digital_io.h"
+#include "system/system.h"
+
+namespace demo::system {
+
+static constexpr pw::digital_io::Rp2040Config kDefaultLedConfig = {
+ .pin = PICO_DEFAULT_LED_PIN,
+ .polarity = pw::digital_io::Polarity::kActiveHigh,
+};
+
+pw::digital_io::DigitalInOut& MonochromeLed() {
+ static ::pw::digital_io::Rp2040DigitalInOut led_sio(kDefaultLedConfig);
+ return led_sio;
+}
+
+} // namespace demo::system
diff --git a/targets/rp2/system.cc b/targets/rp2/system.cc
new file mode 100644
index 0000000..472ab2b
--- /dev/null
+++ b/targets/rp2/system.cc
@@ -0,0 +1,56 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "system/system.h"
+
+#include "hardware/adc.h"
+#include "hardware/exception.h"
+#include "pico/stdlib.h"
+#include "pw_channel/rp2_stdio_channel.h"
+#include "pw_cpu_exception/entry.h"
+#include "pw_digital_io_rp2040/digital_io.h"
+#include "pw_multibuf/simple_allocator.h"
+#include "pw_system/system.h"
+#if defined(PICO_RP2040) && PICO_RP2040
+#include "system_RP2040.h"
+#endif // defined(PICO_RP2040) && PICO_RP2040
+#if defined(PICO_RP2350) && PICO_RP2350
+#include "system_RP2350.h"
+#endif // defined(PICO_RP2350) && PICO_RP2350
+
+using pw::digital_io::Rp2040DigitalIn;
+
+namespace demo::system {
+
+void Init() {
+ // PICO_SDK inits.
+ SystemInit();
+ stdio_init_all();
+ setup_default_uart();
+ stdio_usb_init();
+ adc_init();
+
+ // Install the CPU exception handler.
+ exception_set_exclusive_handler(HARDFAULT_EXCEPTION, pw_cpu_exception_Entry);
+}
+
+void Start() {
+ static std::byte channel_buffer[2048];
+ static pw::multibuf::SimpleAllocator multibuf_alloc(channel_buffer,
+ pw::System().allocator());
+ pw::SystemStart(pw::channel::Rp2StdioChannelInit(multibuf_alloc));
+ PW_UNREACHABLE;
+}
+
+} // namespace demo::system
diff --git a/targets/rp2/unit_test_rpc_main.cc b/targets/rp2/unit_test_rpc_main.cc
new file mode 100644
index 0000000..ad0d057
--- /dev/null
+++ b/targets/rp2/unit_test_rpc_main.cc
@@ -0,0 +1,35 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_log/log.h"
+#include "pw_system/system.h"
+#include "pw_unit_test/unit_test_service.h"
+#include "system/system.h"
+
+namespace {
+
+pw::unit_test::UnitTestService unit_test_service;
+
+} // namespace
+
+int main() {
+ demo::system::Init();
+ auto& rpc_server = pw::System().rpc_server();
+
+ rpc_server.RegisterService(unit_test_service);
+
+ PW_LOG_INFO("Started test_runner app; waiting for RPCs...");
+ demo::system::Start();
+ PW_UNREACHABLE;
+}
diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel
index 6d048f7..89cabc4 100644
--- a/tools/BUILD.bazel
+++ b/tools/BUILD.bazel
@@ -11,28 +11,18 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
+
load("@rules_python//python:defs.bzl", "py_binary")
package(default_visibility = ["//visibility:public"])
py_binary(
- name = "flash",
- srcs = ["flash.py"],
- data = [
- "//src:echo.elf",
- "@openocd//:bin/openocd",
- "@pigweed//targets/stm32f429i_disc1/py/stm32f429i_disc1_utils:openocd_stm32f4xx.cfg",
- ],
+ name = "console",
+ srcs = ["console.py"],
deps = [
- "@pypi_pyserial//:pkg",
- "@rules_python//python/runfiles",
+ "//modules/blinky:py_pb2",
+ "@pigweed//pw_protobuf:common_py_pb2",
+ "@pigweed//pw_rpc:echo_py_pb2",
+ "@pigweed//pw_system/py:pw_system_lib",
],
)
-
-py_binary(
- name = "miniterm",
- srcs = ["miniterm.py"],
- deps = [
- "@pypi_pyserial//:pkg",
- ],
-)
diff --git a/tools/console.py b/tools/console.py
new file mode 100644
index 0000000..2e8e895
--- /dev/null
+++ b/tools/console.py
@@ -0,0 +1,100 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Wraps pw_system's console to inject quickstart's RPC proto."""
+
+import argparse
+import sys
+
+import logging
+
+import pw_cli
+from pw_system.device import Device as PwSystemDevice
+from pw_system.device_connection import (
+ add_device_args,
+ DeviceConnection,
+ create_device_serial_or_socket_connection,
+)
+import pw_system.console
+from blinky_pb import blinky_pb2
+
+
+_LOG = logging.getLogger(__file__)
+
+
+# Quickstart-specific device classes, new functions can be added here.
+# similar to ones on the parent pw_system.device.Device class:
+# https://cs.opensource.google/pigweed/pigweed/+/main:pw_system/py/pw_system/device.py?q=%22def%20run_tests(%22
+class Device(PwSystemDevice):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ def toggle_led(self):
+ """Toggles the onboard (non-RGB) LED."""
+ self.rpcs.blinky.Blinky.ToggleLed()
+
+ def set_led(self, on: bool):
+ """Sets the onboard (non-RGB) LED."""
+ self.rpcs.blinky.Blinky.SetLed(on=on)
+
+ def blink(self, interval_ms=1000, blink_count=None):
+ """Sets the onboard (non-RGB) LED to blink on and off."""
+ self.rpcs.blinky.Blinky.Blink(
+ interval_ms=interval_ms, blink_count=blink_count
+ )
+
+
+def get_device_connection(
+ setup_logging: bool = True,
+ log_level: int = logging.DEBUG,
+) -> DeviceConnection:
+ if setup_logging:
+ pw_cli.log.install(level=log_level)
+
+ parser = argparse.ArgumentParser(
+ prog='quickstart',
+ description=__doc__,
+ )
+ parser = add_device_args(parser)
+ args, _remaning_args = parser.parse_known_args()
+
+ compiled_protos = [blinky_pb2]
+
+ device_context = create_device_serial_or_socket_connection(
+ device=args.device,
+ baudrate=args.baudrate,
+ token_databases=args.token_databases,
+ compiled_protos=compiled_protos,
+ socket_addr=args.socket_addr,
+ ticks_per_second=args.ticks_per_second,
+ serial_debug=args.serial_debug,
+ rpc_logging=args.rpc_logging,
+ hdlc_encoding=args.hdlc_encoding,
+ channel_id=args.channel_id,
+ # Device tracing is not hooked up yet for Pigweed Sense.
+ device_tracing=False,
+ device_class=Device,
+ )
+
+ return device_context
+
+
+def main() -> int:
+ return pw_system.console.main(
+ compiled_protos=[blinky_pb2],
+ device_connection=get_device_connection(),
+ )
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/tools/flash.py b/tools/flash.py
deleted file mode 100644
index 69a6a8b..0000000
--- a/tools/flash.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# Copyright 2023 The Pigweed Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-"""Flash the echo binary to a connected STM32 Discovery Board.
-
-Usage:
- bazel run //tools:flash
-"""
-
-import subprocess
-
-from rules_python.python.runfiles import runfiles
-from serial.tools import list_ports
-
-_BINARY_PATH = "__main__/src/echo.elf"
-_OPENOCD_PATH = "openocd/bin/openocd"
-_OPENOCD_CONFIG_PATH = "pigweed/targets/stm32f429i_disc1/py/stm32f429i_disc1_utils/openocd_stm32f4xx.cfg"
-
-# Vendor ID and model ID for the STM32 Discovery Board.
-_ST_VENDOR_ID = 0x0483
-_DISCOVERY_MODEL_ID = 0x374B
-
-
-def get_board_serial() -> str:
- for dev in list_ports.comports():
- if dev.vid == _ST_VENDOR_ID and dev.pid == _DISCOVERY_MODEL_ID:
- return dev.serial_number
-
- raise IOError("Failed to detect connected board")
-
-
-def flash(board_serial):
- r = runfiles.Create()
- openocd = r.Rlocation(_OPENOCD_PATH)
- binary = r.Rlocation(_BINARY_PATH)
- openocd_cfg = r.Rlocation(_OPENOCD_CONFIG_PATH)
-
- print(f"binary Rlocation is: {binary}")
- print(f"openocd Rlocation is: {openocd}")
- print(f"openocd config Rlocation is: {openocd_cfg}")
-
- assert binary is not None
- assert openocd_cfg is not None
-
- # Variables referred to by the OpenOCD config.
- env = {
- "PW_STLINK_SERIAL": board_serial,
- "PW_GDB_PORT": "disabled",
- }
-
- subprocess.check_call(
- [
- openocd,
- "-f",
- f"{openocd_cfg}",
- "-c",
- f"program {binary} reset exit",
- ],
- env=env,
- )
-
-
-if __name__ == "__main__":
- flash(get_board_serial())
diff --git a/tools/tools.bzl b/tools/tools.bzl
new file mode 100644
index 0000000..f8a35a4
--- /dev/null
+++ b/tools/tools.bzl
@@ -0,0 +1,67 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+load("@bazel_skylib//rules:native_binary.bzl", "native_binary")
+
+def host_console(name, binary, extra_args = []):
+ """Create a host binary console run target.
+
+ Args:
+ name: target name
+ binary: target binary the console is for
+ extra_args: additional arguments added to the console invocation
+ """
+ native_binary(
+ name = name,
+ src = "//tools:console",
+ args = [
+ # This arg lets us skip manual port selection.
+ "--socket",
+ "default",
+ "--config-file",
+ "$(rootpath //:pw_console_config)",
+ ] + extra_args,
+ data = [
+ binary,
+ "//:pw_console_config",
+ ],
+ )
+
+def device_console(name, binary, extra_args = []):
+ """Create a device binary console run target.
+
+ Makes running a console for a binary easy, and ensures the associated binary is
+ up to date (but does not flash the device).
+
+ Args:
+ name: target name
+ binary: target binary the console is for
+ extra_args: additional arguments added to the console invocation
+ """
+ native_binary(
+ name = name,
+ src = "//tools:console",
+ args = [
+ "-b",
+ "115200",
+ "--token-databases",
+ "$(rootpath " + binary + ")",
+ "--config-file",
+ "$(rootpath //:pw_console_config)",
+ ] + extra_args,
+ data = [
+ binary,
+ "//:pw_console_config",
+ ],
+ )