diff --git a/mkdocs/contributor_guide/arm_cortex_m_bringup.md b/mkdocs/contributor_guide/arm_cortex_m_bringup.md index f5a6358a..8ec98fd1 100644 --- a/mkdocs/contributor_guide/arm_cortex_m_bringup.md +++ b/mkdocs/contributor_guide/arm_cortex_m_bringup.md @@ -1,57 +1,208 @@ # 🧠 ARM Cortex-M Series Platform Bringup -To fully bring up any ARM Cortex-M series micro-controller for libhal, the -following list of things need to be implemented: +To fully bring up an ARM Cortex-M series microcontroller for libhal, several +critical elements need to be implemented: -1. Linker scripts 1. Conan profiles -1. CI & Deployment setup +2. Linker scripts +3. Continuous Integration & Deployment +4. Platform Constants +5. Core APIs -To make drivers possible to be implemented for the various peripherals within the micro-controller, then APIs for the following are required. +To ensure peripheral drivers operate without conflicts, such as clashing power +control implementations, the following APIs must be implemented: 1. Power control 2. Pin Multiplexing 3. Clock tree 4. Direct Memory Access (DMA) -Understand that, like all libhal tutorials, everything is relative and some company may have made a chip that is not very reasonable and doesn't fit the typical scheme laid out here. In such cases, you'll need to understand the basics and use good judgement to make your system work. +While not all devices support these features, and many drivers can function +without them, implementing them ensures comprehensive peripheral support. For +instance, on the LPC40xx series, GPIOs are enabled by default, and simple tasks +like blinking an LED don't require knowledge of the system's clock speed or DMA. + +However, accommodating all features enables support for all potential +peripheral drivers. + +Keep in mind that, as with all libhal tutorials, adaptability is key. Some +chips may deviate from standard architectures; in such cases, a fundamental +understanding and sound judgment are essential to ensure your system functions +effectively. ## Getting Started -libhal provides a template repo for ARM Cortex M series platforms. To get -started, simply go to the -[`libhal-__platform__`](https://github.com/libhal/libhal-__platform__) and press the button "Use this template" and then "Create new repository". +libhal provides a template repository for ARM Cortex-M series platforms. Begin +by visiting +[`libhal-__platform__`](https://github.com/libhal/libhal-__platform__), +clicking "Use this template," and then "Create new repository." -The name of the project should be `libhal-`. This allows -the github action -[update_name.yml](https://github.com/libhal/libhal-__platform__/blob/main/.github/workflows/update_name.yml), create a pull request that updates the names -and files within the repo from `__platform__` to the name that you choose. -Merge this pull request and continue on to the next phase. +Your project should be named `libhal-`. This standard +naming triggers the GitHub action +[update_name.yml](https://github.com/libhal/libhal-__platform__/blob/main/.github/workflows/update_name.yml) +to create a pull request that updates names and files within the repo from +`__platform__` to your chosen name. Merge this pull request to proceed to the +next phase. !!! note - If there is demand for platform names that do not have `libhal-` as a - prefix, we can update `update_name.yml` to allow it. But for now, we just - support prefixed package names. + If there is demand for platform names without the `libhal-` prefix, we can + modify `update_name.yml` to accommodate this. Currently, we support only + prefixed package names. + +## Conan Profiles + +Libhal leverages Conan's robust profile system to specify the architecture and +operating system for which an application is built. If you are new to libhal, +refer to the "Getting Started" guide for details on building applications using +compiler and platform profiles. + +A microcontroller family consists of microcontrollers with nearly identical +designs but variations in memory, storage, and peripherals. Since these +variations share a common architecture, drivers can typically operate across +the family. + +Creating a Conan profile begins with understanding the device family. This +determines the number of profiles needed. For example, the RP2040, which is a +single-device family, would have one profile simply named `rp2040`. However, a +chip family with 15 variations would require a distinct profile for each +variant. + +### Components of a Typical Profile + +Below is an example of a Conan profile for the `rp2040` microcontroller: + +```plaintext +[settings] +build_type=MinSizeRel +os=baremetal +arch=cortex-m0plus +libc=custom + +[options] +*:platform=rp2040 +``` + +#### Explanation of Settings + +- **`[settings]`**: This section configures the build environment for your code. +- **`build_type`**: Defaults to `MinSizeRel`, optimizing for the smallest + binary size, which is crucial for embedded applications. +- **`os`**: Always set to `baremetal` for ARM Cortex-M processors, indicating a + direct hardware operation without a traditional operating system. +- **`arch`**: Specifies the processor architecture. It is essential to consult + the user manual to identify the correct architecture as variations within the + same family may exist. For instance, the LPC4078 uses a Cortex-M4F (with + floating point unit), unlike the LPC4072, which uses a Cortex-M4 (without + floating point unit). Supported architectures include `cortex-m0`, + `cortex-m0plus`, `cortex-m1`, `cortex-m3`, `cortex-m4`, `cortex-m4f`, + `cortex-m7`, `cortex-m7f`, `cortex-m7d`, `cortex-m23`, `cortex-m33`, + `cortex-m33f`, `cortex-m33p`, `cortex-m35p`, `cortex-m55`, and `cortex-m85`. +- **`libc`**: Should be set to `custom` to accommodate picolibc, which is + preferable over the default newlib-nano used by the GNU ARM toolchain. -## Conan profiles +#### Options Section -TBD +- **`[options]`**: This section defines library options for the build process. + It facilitates the selection of specific package options within + `conanfile.py`. +- **`*:platform=rp2040`**: This setting universally applies the `rp2040` + platform option across all packages, using `*` as a wildcard to ensure it + affects all compiled packages. + +#### Customizing Your Profile + +To tailor this profile for different platforms, replace `rp2040` with the +relevant platform name and adjust `arch` to match the specific CPU architecture +of your device. This customized approach ensures that the build settings are +perfectly aligned with the hardware specifications of the microcontroller you +are working with. + +### Using Profile Templates + +Conan has the ability to use Jinja templates in its profiles allowing for the composition and expansion of profiles. So if you have a ton of devices in the same family with nearly identical bits of information, you can make a template for them. Check out the following examples: + +=== "libhal-lpc40" + + Notice how the only thing that changes between these are the architecture + and the platform. + + `libhal-lpc40/conan/v2/lpc40`: + + ``` + [settings] + build_type=MinSizeRel + os=baremetal + arch={{ arch }} + libc=custom + + [options] + *:platform={{ platform }} + ``` + + `libhal-lpc40/conan/v2/lpc4078`: + + ``` + {% set platform = "lpc4078" %} + {% set arch = "cortex-m4f" %} + {% include "lpc40" %} + ``` + + `libhal-lpc40/conan/v2/lpc4072`: + + ``` + {% set platform = "lpc4072" %} + {% set arch = "cortex-m4" %} + {% include "lpc40" %} + ``` + +=== "libhal-stm32f1" + + The only difference between the chips with respect to the settings and options is just the platform name so the template only needs to be used for the platform variable. + + `libhal-stm32f1/conan/v2/stm32f103`: + ``` + [settings] + build_type=MinSizeRel + os=baremetal + arch=cortex-m3 + libc=custom + + [options] + *:platform={{ platform }} + ``` + + `libhal-stm32f1/conan/v2/stm32f103c8`: + ``` + {% set platform = "stm32f103c8" %} + {% include "stm32f1" %} + ``` + + `libhal-stm32f1/conan/v2/stm32f103vc`: + ``` + {% set platform = "stm32f103vc" %} + {% include "stm32f1" %} + ``` + +--- + +### Directory structure + +In order to allow changes into the future, it is advised to put your profiles +in a `v1` or `vN` directory. This way, if there is a significant change between +the profiles into the future, code can still use the original profiles they +used before. ## Linker Scripts -Linker scripts define the memory layout of an embedded system. They specify how -different types of data are organized within the binary. This includes code, -initialized data, uninitialized data, read-only data, and thread local storage. -Additionally, they delineate the memory regions available for the application, -ensuring optimal memory usage and system stability. +Linker scripts play a crucial role in defining the memory layout of embedded systems. They are used to organize different types of data within the binary, such as code, initialized data, uninitialized data, read-only data, and thread local storage. These scripts also outline the memory regions available for the application. + +### Standard Linker Script Template -The processor library `libhal-armcortex` provides standard linker script -templates that can be used in your platform labeled +The `libhal-armcortex` library provides standardized linker script templates, which can be easily adapted for specific platforms. An example template is available at: [`libhal-armcortex/linker_scripts/libhal-armcortex/standard.ld`](https://github.com/libhal/libhal-armcortex/blob/main/linker_scripts/libhal-armcortex/standard.ld). -To use this you only need to provide the following within your linker scripts -directory: +To utilize these templates, include the following definitions in your linker scripts directory: ```ld __flash = 0x00000000; @@ -63,23 +214,17 @@ __stack_size = 1K; INCLUDE "libhal-armcortex/standard.ld" ``` -The above is the linker script file for the [`lpc4072`](https://github.com/libhal/libhal-lpc40/blob/main/linker_scripts/libhal-lpc40/lpc4072.ld) as an example. +The above configuration is an example from the [`lpc4072`](https://github.com/libhal/libhal-lpc40/blob/main/linker_scripts/libhal-lpc40/lpc4072.ld) script. -You simply need to specify: +### Customizing Linker Scripts -- Where the flash memory is located in the device's address space -- How large the flash is -- Where the main ram is located in the device's address space -- How large the ram is -- The minimum stack size before the build should fail +You need to specify: -The minimum stack size is kind of arbitrary so use `1k` as the standard for -libhal. If the ram is really small choose what seems reasonable for the device. -At some point we may determine that a better value is necessary here. The -minimum stack size is relative to the ram size. For example, if you have 15.5kB -of static memory, that only leaves 500 bytes available for the program's stack. +- The location and size of the flash memory within the device's address space. +- The location and size of the main RAM. +- The minimum stack size before the build should fail, typically set to `1K` for libhal. Adjust this based on available RAM. -You'll need a linker script for every variation of the chip available in the chip's family that has a different flash and ram size. +Each variation within a chip family, such as those in the LPC40xx series, requires its own linker script due to differences in flash and RAM sizes: For example the chips in the LPC40xx series are: @@ -89,10 +234,7 @@ For example the chips in the LPC40xx series are: - [lpc4078](https://github.com/libhal/libhal-lpc40/blob/main/linker_scripts/libhal-lpc40/lpc4078.ld) - [lpc4088](https://github.com/libhal/libhal-lpc40/blob/main/linker_scripts/libhal-lpc40/lpc4088.ld) -And each of them requires their own linker script because the flash and ram -is different for each. - -The stm32f10x series has: +Similarly, the STM32F10x series has distinct linker scripts for each variant based on flash and RAM requirements, such as: - [stm32f10xx4.ld](https://github.com/libhal/libhal-stm32f1/blob/main/linker_scripts/libhal-stm32f1/stm32f10xx4.ld) - [stm32f10xx6.ld](https://github.com/libhal/libhal-stm32f1/blob/main/linker_scripts/libhal-stm32f1/stm32f10xx6.ld) @@ -106,50 +248,63 @@ The stm32f10x series has: The last digit of the chip name defines its flash and ram requirements and thus, each is given its own linker script. -!!! note "Supporting multi flash multi ram MCUs" +### Handling Multiple Flash and RAM Configurations + +Currently, `libhal-armcortex` supports MCUs with single flash and RAM +configurations. For support for multi-flash and multi-RAM devices, consider +contributing or following the development on this GitHub issue: +[Add multi flash & multi ram linker scripts](https://github.com/libhal/libhal-armcortex/issues/16). +In order to communicate to the build system what your linker scripts are and +where to find them, we must add to the conan package's `cpp_info.exelinkflags` +array. This property describes to conan what link flags should be added if a +package depends on this package. See the code below. - libhal-armcortex's linker script only supports single flash, single ram - MCUs. We are still deciding how we want to handle devices with these memory - layouts into the future. If this is a necessary feature for your platform then add a thumbs up emoji reaction to this GitHub issue: [Add multi flash & multi ram linker scripts](https://github.com/libhal/libhal-armcortex/issues/16) +### Integrating Linker Scripts into Build Systems -In order to communicate to the build system what your linker scripts are and where to find them, we must add to the conan package's `cpp_info.exelinkflags` array. This property describes to conan what link flags should be added if a package depends on this package. See the code below. +To properly integrate your linker scripts with the Conan build system, add the appropriate link flags to the `cpp_info.exelinkflags` array in your package. This setup ensures that the linker scripts are correctly recognized and used during the build process. ```python +def package_info(self): + self.cpp_info.set_property("cmake_target_name", "libhal::__platform__") + self.cpp_info.libs = ["libhal-__platform__"] + + if self.settings.os == "baremetal" and self._use_linker_script: + self.add_linker_scripts_to_link_flags() + def add_linker_scripts_to_link_flags(self): platform = str(self.options.platform) - # This attribute defines the list of linker flags for this package self.cpp_info.exelinkflags = [ - # -L is the linker script equivalent of -I in GCC and it tells GCC - # flag where to find linker scripts. "-L" + os.path.join(self.package_folder, "linker_scripts"), - # -T provides a path to the a linker script. GCC will search all -L - # directories passed to it and its own internal linker paths. "-T" + os.path.join("libhal-__platform__", platform + ".ld"), ] +``` -def package_info(self): - self.cpp_info.set_property("cmake_target_name", "libhal::__platform__") - self.cpp_info.libs = ["libhal-__platform__"] +This section of the documentation provides clear guidelines on using and +customizing linker scripts within the libhal framework, ensuring that +developers can effectively manage memory layouts for their ARM Cortex-M +microcontrollers. - # We only want to apply the linker scripts when the OS is baremetal. - # Otherwise the linker script flags will be injected into the host test - # build causing them to fail. - if self.settings.os == "baremetal" and self._use_linker_script: - self.add_linker_scripts_to_link_flags() -``` +### Implementing Linker Scripts for Your Platform -### Implementing linker scripts for your platform +#### 1. Download and Review the User Manual -#### 1. Download and open user manual +Begin by downloading and reviewing the user manual for your microcontroller. +The datasheet may provide some information, but the user manual will typically +contain a comprehensive memory map. Search for the section labeled "memory map" +to find detailed information about the sizes of the device's flash and RAM. -The data sheet may also have the information as well, but the user manual will likely have the whole memory map as well. Search for the area labelled "memory map" and you should find the sizes for all of the different device's flash and -ram sizes are. +#### 2. Determine the Naming Scheme -#### 2. Determine the naming scheme +Device naming schemes vary. Some, like the `lpc4078`, have straightforward +names, while others, like the `stm32f10x`, incorporate coded symbols. Establish +a clear naming strategy for your linker scripts. Each profile should correspond +to exactly one linker script, although a single linker script can apply to +multiple profiles if the hardware characteristics are identical. -Some devices have clear cut names for each device without any sort of pattern or coded symbols, like lpc4078. Some are like stm32f10x where a few of the numbers are not necessary it would require less files to just use a few files and ignore the characters are not needed. Every profile needs to map to exactly one linker script, but a linker script can map to many profiles. +#### 3. Populate the Linker Script -#### 3. Fill out the information in this file +Fill out the linker script with the specific memory addresses and sizes for +your device: ```ld __flash = ???; @@ -161,23 +316,24 @@ __stack_size = 1K; INCLUDE "libhal-armcortex/standard.ld" ``` -#### 4. Repeat #3 +#### 4. Repeat for All Variants -Until all files are written. +Continue this process for each device variant within the chip family, ensuring +that all have appropriate linker scripts reflecting their specific memory +configurations. #### 5. Update `add_linker_scripts_to_link_flags()` in `conanfile.py` -In order for your package to be able to tell the - -If there is just one profile for each linker script and they are named the same (ignoring the .ld extension) then you can keep the default `add_linker_scripts_to_link_flags()` function and change nothing. You may skip this step. +Modify the `add_linker_scripts_to_link_flags()` function in your `conanfile.py` +to correctly link the appropriate scripts based on the platform: -If you are from the st family or some other MCU that likes to have a lot of family members see below: +For devices like the STM32 family, where multiple variants exist, implement a +dynamic approach to link the correct script: ```python def add_linker_scripts_to_link_flags(self): linker_script_name = list(str(self.options.platform)) - # Replace the MCU number and pin count number with 'x' (don't care) - # to map to the linker script + # Replace unneeded characters with 'x' to denote a generic script linker_script_name[8] = 'x' linker_script_name[9] = 'x' linker_script_name = "".join(linker_script_name) @@ -188,162 +344,278 @@ def add_linker_scripts_to_link_flags(self): ] ``` -The pattern with stm32f10x is that there are two additional letters that define -the chip size and feature set. These are not important to the linker script -so we replace those with the typical hardware "x" denoting it as a "don't care" -symbol. We can simply take the full `stm32f103c8` and transform it to -`stm32f10xx8` and use that to construct the path to the linker script. If your -names do not map so easily, then its advisable to use a python `dict` to map -each profile to its linker script path and pass that to the -`cpp_info.exelinkflags`. - -### 6. Done! Now to test +This adjustment allows you to use a single script for similar variants by +replacing specific parts of the chip identifier with a 'don't care' symbol +('x'). -Remember to replace `YOUR_PROFILE` with your actual profile name in each -command. +#### 6. Testing -To test, try to build your package using: +Once all scripts are in place, it's time to test: +To build the package, run: ```bash conan create . -pr YOUR_PROFILE -pr arm-gcc-12.3 --version=latest ``` -!!! Note - - We use `--version=latest` because the demos will either use the latest - compatible and cached version of the library or they will use `latest`. - They use the semver pattern `[^1.0.0 || latest]`. - -Then try and build your demos: - +To build your demos, use: ```bash VERBOSE=1 conan build demos -pr YOUR_PROFILE -pr arm-gcc-12.3 ``` On Windows: - ```bash $env:VERBOSE=1 conan build demos -pr YOUR_PROFILE -pr arm-gcc-12.3 ``` -The `VERBOSE=1` environment variable is needed to get CMake to print the full -command string to stdout. If you are on Windows or using a terminal that cannot -handle CMake's multi-thread output add this to the end of the command -`tools.build:jobs=1` to make the build single threaded. - -Take the output and search for your `-Tyour_linker_script.ld` command argument. If it is there then you were successful. To ensure that the binary also fits the linker scripts's addresses use the following command: +Ensure verbose output is enabled to check the `-Tyour_linker_script.ld` command +argument during the build process. Verify the binary fits the addresses +specified in the linker script with: ```bash arm-none-eabi-readelf -S demos/build/YOUR_PROFILE/MinSizeRel/blinker.elf ``` -!!! note "if the above command fails" +Confirm the `.init` section aligns with the flash address and `.data` with the +RAM address. If these match, your implementation is successful. + +!!! error "Troubleshooting" - If the above command does not work, on linux and mac, run this command: + If commands do not execute as expected, particularly on Linux or macOS, + source your environment variables with: ```bash source demos/build/YOUR_PROFILE/MinSizeRel/generators/conanbuild.sh ``` - This will add all of the conan build environment variables to your shell. +## Continuous Integration & Deployment -Confirm that: +Continuous Integration (CI) ensures that code in the main branch of any libhal +library—or code intended for the main branch—builds successfully and passes all +tests. This process is crucial for maintaining code quality and functionality +over time. -1. `.init` address is equal the flash address -2. `.data` address is equal to the ram ram. +!!! warning -If so, then you are finished. + The CI system is currently optimized for use within the libhal + organization. Efforts are underway to enhance its usability for other + organizations without requiring a fork or clone of the `libhal/ci` + repository. -## CI & Deployment setup +### Branch & Pull Request Checks -TBD +The `libhal-__platform__` includes a pre-configured +[`ci.yml`](https://github.com/libhal/libhal-__platform__/blob/main/.github/workflows/ci.yml) +GitHub Action script, which provides an overview of our automated testing +approach: -## Peripheral Constants +```yaml +on: + workflow_dispatch: + pull_request: + push: + branches: + - main + schedule: + - cron: "0 12 * * 0" +``` -TBD +Key Features: + +- **Scheduled Tests:** The CI system automatically tests the main branch daily + to ensure ongoing compatibility and to detect any issues caused by changes in + other packages or the infrastructure. +- **Pull Request Tests:** All pull requests undergo CI tests to ensure that new + contributions do not introduce bugs or compatibility issues. +- **Manual Trigger:** The `workflow_dispatch` event allows for manual CI runs + without needing to push updates or create pull requests. + +```yaml +jobs: + library_checks: + uses: libhal/ci/.github/workflows/library_check.yml@5.x.y + secrets: inherit + + deploy_cortex-m4f_check: + uses: libhal/ci/.github/workflows/deploy.yml@5.x.y + with: + arch: cortex-m4f + os: baremetal + compiler: gcc + compiler_version: 12.3 + compiler_package: arm-gnu-toolchain + secrets: inherit + + deploy_cortex-m4_check: + uses: libhal/ci/.github/workflows/deploy.yml@5.x.y + with: + arch: cortex-m4 + os: baremetal + compiler: gcc + compiler_version: 12.3 + compiler_package: arm-gnu-toolchain + secrets: inherit + + demo_check_profile1: + uses: libhal/ci/.github/workflows/demo_builder.yml@5.x.y + with: + compiler_profile_url: https://github.com/libhal/arm-gnu-toolchain.git + compiler_profile: v1/arm-gcc-12.3 + platform_profile_url: https://github.com/libhal/libhal-__platform__.git + platform_profile: v1/profile1 + secrets: inherit + + demo_check_profile2: + uses: libhal/ci/.github/workflows/demo_builder.yml@5.x.y + with: + compiler_profile_url: https://github.com/libhal/arm-gnu-toolchain.git + compiler_profile: v1/arm-gcc-12.3 + platform_profile_url: https://github.com/libhal/libhal-__platform__.git + platform_profile: v1/profile2 + secrets: inherit +``` -## Power control +Key Checks: -The library should provide APIs for powering on and off peripherals on the -device. +- **Library Checks:** Ensures packaging in host mode, conducts host side tests, + verifies API documentation (Doxygen comments), and checks code formatting. +- **Deployment Checks:** Uses `deploy.yml` to simulate the deployment process + for all `build_type`s such as `Debug`, `MinSizeRel`, and `Release`, without a + specific `version` input for a dry run. +- **Demo Application Checks:** Ensures demo applications remain functional + after changes using `demo_builder.yml`. This script should specify the paths + to compiler and platform profiles, using these to download and build the + applications. -The typical libhal APIs for power are: +Each `ci.yml` configuration should include these checks. If a package does not +include demos, the demo check can be omitted, though it is generally +recommended to include demos to demonstrate the library's capabilities. -```C++ -#pragma once +## Platform Constants -#include "constants.hpp" +Now we've reached the point where we can start modifying the C++ source code. The first area to start with is defining the `peripheral` and `irq` enumeration +class constants. These outline the set of peripherals and interrupt requests that can be used on the platform. +Here's a guide section for "Peripheral Constants" that you can use in your documentation. This section explains how to map peripheral identifiers to their respective power and clock control registers, tailored specifically for an API like the one you're designing for libhal. + +### Peripheral Constants + +In the libhal ecosystem, peripheral constants play a crucial role in the power and clock management APIs. These constants uniquely identify each peripheral and correspond directly to control bits in the power and clock registers. This design ensures efficient and straightforward management of peripheral power states and clock frequencies. + +#### Defining Peripheral Constants + +Peripheral constants are defined in an enumeration where each constant corresponds to a specific bit in a device's power or clock enable registers. This method allows direct manipulation of these registers using bit operations, which are both fast and memory-efficient. + +```C++ namespace hal::your_platform { -/** - * @brief Power on the peripheral - * - */ -void power_on(peripheral p_peripheral); - -/** - * @brief Check if the peripheral is powered on - * - * @return true - peripheral is on - * @return false - peripheral is off - */ -[[nodiscard]] bool is_on(peripheral p_peripheral); - -/** - * @brief Power off peripheral - * - */ -void power_off(peripheral p_peripheral); -} // namespace hal::your_platform + /// List of each peripheral and their power on id number for this platform + enum class peripheral : std::uint8_t + { + // Examples + gpio = 0, + uart0 = 1, + spi1 = 2, + // More peripherals follow... + max, // Placeholder for the count of peripherals + }; +} ``` -The `peripheral` type is an `enum class` with each peripheral represented. Here is an example from `libhal-lpc40`. +#### Mapping to Power Registers + +1. **Locate Power Registers:** First, consult the power management section of + your microcontroller's user manual. Identify the registers responsible for + powering peripherals. These are often labeled as power control registers or + clock enable registers. + +2. **Understand Register Layout:** Registers typically control multiple + peripherals. Each bit in a register corresponds to the power state of one + peripheral. For instance, bit 0 might control the power for the GPIO + interface, bit 1 for the UART0, and so on. + +3. **Designing the Enumeration:** Define each peripheral in the enum class such + that the value of the enum matches the bit position in the power register. + For a microcontroller with two 32-bit power registers: + - Peripherals controlled by the first register will have IDs 0 to 31. + - Peripherals controlled by the second register will have IDs 32 to 63. + +4. **Bitwise Operations:** With each peripheral ID corresponding directly to a + bit position, you can toggle power by applying bitwise operations. For + example, to power on a peripheral, the operation would be: + + ```C++ + power_register |= (1 << static_cast(peripheral::uart0)); + ``` + + To power it off: + + ```C++ + power_register &= ~(1 << static_cast(peripheral::uart0)); + ``` + +#### Example Usage + +Consider a scenario where the ADC peripheral is mapped to bit 12 in the power control register. By defining the `adc` constant as 12 in the enum, you enable straightforward manipulation: + +- **Power On:** `power_register |= (1 << static_cast(peripheral::adc));` +- **Check Power State:** `bool isPowered = power_register & (1 << static_cast(peripheral::adc));` +- **Power Off:** `power_register &= ~(1 << static_cast(peripheral::adc));` + +#### Benefits + +This mapping strategy ensures that your power and clock management API is both +efficient and easy to use. It reduces the overhead of calculating bit masks and +positions dynamically, leading to faster execution and cleaner code. + +This guide section aims to clarify the process of defining and using peripheral +constants within the libhal framework, providing a structured approach to +managing device resources effectively. + +!!! note + + Your microcontroller may use multiple bits or have a more complicated + scheme to power control. If that is the case, then it is up to you to + determine what is the best scheme for powering on peripheral on the device + that driver and potentially users can utilize. + +### IRQ Constants + +**IRQ** stands for **Interrupt Request Number**. Every MCU supports at least +the default range of interrupts, numbered from -16 to -1. Additional interrupts +are unique to each microcontroller. ```C++ -enum class peripheral : std::uint8_t +// The enum class type must always be `std::int16_t`, representing the +// maximum number of IRQs a Cortex-M processor can support. This type is also +// used for the input parameter that specifies the IRQ number. +enum class irq : std::int16_t { - lcd = 0, + watchdog_timer = 0, // The first IRQ must always be zero timer0 = 1, timer1 = 2, - uart0 = 3, - uart1 = 4, - pwm0 = 5, - pwm1 = 6, - i2c0 = 7, - uart4 = 8, - rtc = 9, - ssp1 = 10, - emc = 11, - adc = 12, - can1 = 13, - can2 = 14, - gpio = 15, - spifi = 16, - motor_control_pwm = 17, - quadrature_encoder = 18, - i2c1 = 19, - ssp2 = 20, - ssp0 = 21, - timer2 = 22, - timer3 = 23, - uart2 = 24, - uart3 = 25, - i2c2 = 26, - i2s = 27, - sdcard = 28, - gpdma = 29, - ethernet = 30, - usb = 31, - cpu, // always on - dac, // always on + uart0 = 5, + uart1 = 6, + pwm1 = 9, + i2c0 = 10, + i2c1 = 11, + i2c2 = 12, + reserved0 = 13, // Fill gaps with reserved IRQs + spi0 = 14, + spi1 = 15, + pll0 = 16, + rtc = 17, + // ... Add the rest... + max, // The final entry must ALWAYS be "max" }; ``` -Some devices will always be powered on and do not require power controls. The `peripheral` type can be used in other parts of the code for example -the clock tree, so always powered peripherals can also show up here in the list. +When referencing your user manual, look for the term **NVIC**, which stands for +**Nested Vector Interrupt Controller**. This is how ARM refers to their +interrupt controller. The NVIC section in the manual typically includes IRQ +numbers for each peripheral. Integrate these numbers into the enum class, +assigning them as corresponding values. Additionally, you may encounter +**ISER**, or **Interrupt Set-Enable Register**, which is the ARM designation +for the register controlling interrupt enabling. -The behavior for always powered peripherals is to simply do nothing when passed their peripheral id. +## Implementing the core APIs -The API should do what is described in the comments. -The peripheral enumeration should exist in a `constants.hpp` header. -The header should +~~incomplete section~~.