diff --git a/.github/workflows/compile-all.yml b/.github/workflows/compile-all.yml index 226a3b2d2d..ff89e15407 100644 --- a/.github/workflows/compile-all.yml +++ b/.github/workflows/compile-all.yml @@ -31,7 +31,7 @@ jobs: name: avr-compile-all path: test/all/log - sam-compile-all: + sam-d0x-d1x-d2x-compile-all: if: github.event.label.name == 'ci:hal' runs-on: ubuntu-20.04 container: @@ -47,9 +47,61 @@ jobs: - name: Update lbuild run: | pip3 install --upgrade --upgrade-strategy=eager modm - - name: Compile HAL for all SAM + - name: Compile HAL for SAMD0x, SAMD1x, SAMD2x run: | - (cd test/all && python3 run_all.py sam --quick-remaining) + (cd test/all && python3 run_all.py samd0 samd1 samd2 --quick-remaining) + - name: Upload log artifacts + if: always() + uses: actions/upload-artifact@v2 + with: + name: sam-compile-all + path: test/all/log + + sam-x5x-compile-all: + if: github.event.label.name == 'ci:hal' + runs-on: ubuntu-20.04 + container: + image: ghcr.io/modm-ext/modm-build-cortex-m:2022-09-27 + steps: + - name: Check out repository + uses: actions/checkout@v3 + with: + submodules: 'recursive' + - name: Fix Git permission/ownership problem + run: | + git config --global --add safe.directory /__w/modm/modm + - name: Update lbuild + run: | + pip3 install --upgrade --upgrade-strategy=eager modm + - name: Compile HAL for SAMD5x, SAME5x, SAMG5x + run: | + (cd test/all && python3 run_all.py samd5 same5 samg5 --quick-remaining) + - name: Upload log artifacts + if: always() + uses: actions/upload-artifact@v2 + with: + name: sam-compile-all + path: test/all/log + + sam-x7x-compile-all: + if: github.event.label.name == 'ci:hal' + runs-on: ubuntu-20.04 + container: + image: ghcr.io/modm-ext/modm-build-cortex-m:2022-09-27 + steps: + - name: Check out repository + uses: actions/checkout@v3 + with: + submodules: 'recursive' + - name: Fix Git permission/ownership problem + run: | + git config --global --add safe.directory /__w/modm/modm + - name: Update lbuild + run: | + pip3 install --upgrade --upgrade-strategy=eager modm + - name: Compile HAL for SAME7x, SAMS7x, SAMV7x + run: | + (cd test/all && python3 run_all.py same7 sams7 samv7 --quick-remaining) - name: Upload log artifacts if: always() uses: actions/upload-artifact@v2 diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index c476fff2c3..8bdb2c584b 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -92,15 +92,19 @@ jobs: - name: Examples SAMD Devices if: always() run: | - (cd examples && ../tools/scripts/examples_compile.py samd) + (cd examples && ../tools/scripts/examples_compile.py samd samd21_xplained_pro) - name: Examples SAMG Devices if: always() run: | (cd examples && ../tools/scripts/examples_compile.py samg55_xplained_pro) + - name: Examples SAME5x Devices + if: always() + run: | + (cd examples && ../tools/scripts/examples_compile.py same54_xplained_pro) - name: Examples SAMV Devices if: always() run: | - (cd examples && ../tools/scripts/examples_compile.py samv) + (cd examples && ../tools/scripts/examples_compile.py samv samv71_xplained_ultra) - name: Examples RP20 Devices if: always() run: | diff --git a/README.md b/README.md index f12b1d9c64..d549a05b27 100644 --- a/README.md +++ b/README.md @@ -80,10 +80,10 @@ git clone --recurse-submodules --jobs 8 https://github.com/modm-io/modm.git ## Microcontrollers -modm can create a HAL for 3304 devices of these vendors: +modm can create a HAL for 3534 devices of these vendors: - STMicroelectronics STM32: 2729 devices. -- Microchip SAM: 186 devices. +- Microchip SAM: 416 devices. - Microchip AVR: 388 devices. - Raspberry Pi: 1 device. @@ -103,7 +103,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. STM32 -SAM +SAM RP AT @@ -121,9 +121,10 @@ Please [discover modm's peripheral drivers for your specific device][discover]. L1 L4 L5 -D21 -G55 -V70 +D1x
D2x
DAx +D5x
E5x +E7x
S7x
V7x +G5x 20 90 Mega @@ -144,8 +145,9 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ○ -✅ ○ +○ +✅ ✅ ○ ✅ @@ -166,8 +168,9 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ✕ -✕ ○ +○ +✕ ✕ ○ ○ @@ -188,8 +191,9 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ○ ○ -✕ ○ +○ +✕ ✕ ○ ○ @@ -210,10 +214,11 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ○ -✕ +○ ○ ✕ ✕ +✕ ○ ✕ @@ -232,8 +237,9 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ○ -✕ ○ +○ +✕ ✅ ✕ ✕ @@ -254,7 +260,8 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✕ ✕ ✕ -✕ +○ +○ ✕ ✕ ✕ @@ -278,6 +285,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ○ ○ +○ ✅ ✅ ✅ @@ -300,6 +308,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✕ ✕ ○ +✕ ○ ✕ ✕ @@ -326,6 +335,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ✅ +✅ I2C ✅ @@ -344,6 +354,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ○ ○ ○ +○ ✅ ✅ ✅ @@ -366,6 +377,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ○ ○ ○ +○ ✕ ✕ ✕ @@ -386,8 +398,9 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ✕ -✕ ○ +○ +✕ ✕ ✕ ✕ @@ -408,8 +421,9 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ○ -✅ ○ +○ +✅ ✅ ✅ ✅ @@ -433,6 +447,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ✅ +✅ ✕ ✕ ✕ @@ -452,9 +467,10 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ○ -✅ ○ ○ +✅ +○ ○ ○ ○ @@ -475,7 +491,8 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ✅ -○ +✅ +✅ ✅ ✅ ✅ @@ -502,6 +519,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✕ ✕ ✕ +✕ USB ✅ @@ -521,6 +539,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ○ ✅ +✅ ✕ ✕ ✕ @@ -613,11 +632,15 @@ We have out-of-box support for many development boards including documentation. Raspberry Pi Pico SAMD21-MINI +SAMD21-XPLAINED-PRO +SAME54-XPLAINED-PRO SAMG55-XPLAINED-PRO + +SAMV71-XPLAINED-ULTRA Smart Response XE STM32-F4VE - STM32F030-DEMO + THINGPLUS-RP2040 diff --git a/examples/samd/interrupt/main.cpp b/examples/samd/interrupt/main.cpp index 0e3a9aceb3..c35747a826 100644 --- a/examples/samd/interrupt/main.cpp +++ b/examples/samd/interrupt/main.cpp @@ -27,7 +27,7 @@ int main() { Board::initialize(); - ExternalInterrupt::initialize(); + ExternalInterrupt::initialize(Board::SystemClock::ClockGen32kHz); ExtInt<3>::initialize(&isr); ExtInt<3>::connect(); while (1) diff --git a/examples/samd21_xplained_pro/blink/main.cpp b/examples/samd21_xplained_pro/blink/main.cpp new file mode 100644 index 0000000000..90716c8993 --- /dev/null +++ b/examples/samd21_xplained_pro/blink/main.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016-2017, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include + +using namespace Board; + +int +main() +{ + Board::initialize(); + + // Use the logging streams to print some messages. + // Change MODM_LOG_LEVEL above to enable or disable these messages + MODM_LOG_DEBUG << "debug" << modm::endl; + MODM_LOG_INFO << "info" << modm::endl; + MODM_LOG_WARNING << "warning" << modm::endl; + MODM_LOG_ERROR << "error" << modm::endl; + + uint32_t counter(0); + + while (true) + { + Led0::toggle(); + modm::delay(Button::read() ? 500ms : 100ms); + + MODM_LOG_INFO << "loop: " << counter++ << modm::endl; + } + + return 0; +} diff --git a/examples/samd21_xplained_pro/blink/project.xml b/examples/samd21_xplained_pro/blink/project.xml new file mode 100644 index 0000000000..2c9638f352 --- /dev/null +++ b/examples/samd21_xplained_pro/blink/project.xml @@ -0,0 +1,9 @@ + + modm:samd21-xplained-pro + + + + + modm:build:scons + + diff --git a/examples/samd21_xplained_pro/usbserial/main.cpp b/examples/samd21_xplained_pro/usbserial/main.cpp new file mode 100644 index 0000000000..2bf24ef589 --- /dev/null +++ b/examples/samd21_xplained_pro/usbserial/main.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016-2017, Niklas Hauser + * Copyright (c) 2022, Christopher Durand + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include +#include + +using namespace Board; + +modm::IODeviceWrapper usb_io_device; +modm::IOStream usb_stream(usb_io_device); + +int +main() +{ + Board::initialize(); + Board::initializeUsbFs(); + + tusb_init(); + + usb_stream << "Hello from USB" << modm::endl; + + uint32_t counter(0); + + modm::PeriodicTimer timer{500ms}; + while (true) + { + tud_task(); + if (timer.execute()) { + Led0::toggle(); + usb_stream << "loop: " << counter++ << modm::endl; + } + } + + return 0; +} diff --git a/examples/samd21_xplained_pro/usbserial/project.xml b/examples/samd21_xplained_pro/usbserial/project.xml new file mode 100644 index 0000000000..8ab1e6a5a9 --- /dev/null +++ b/examples/samd21_xplained_pro/usbserial/project.xml @@ -0,0 +1,12 @@ + + modm:samd21-xplained-pro + + + + + + modm:tinyusb + modm:build:scons + modm:processing:timer + + diff --git a/examples/same54_xplained_pro/blink/main.cpp b/examples/same54_xplained_pro/blink/main.cpp new file mode 100644 index 0000000000..90716c8993 --- /dev/null +++ b/examples/same54_xplained_pro/blink/main.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016-2017, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include + +using namespace Board; + +int +main() +{ + Board::initialize(); + + // Use the logging streams to print some messages. + // Change MODM_LOG_LEVEL above to enable or disable these messages + MODM_LOG_DEBUG << "debug" << modm::endl; + MODM_LOG_INFO << "info" << modm::endl; + MODM_LOG_WARNING << "warning" << modm::endl; + MODM_LOG_ERROR << "error" << modm::endl; + + uint32_t counter(0); + + while (true) + { + Led0::toggle(); + modm::delay(Button::read() ? 500ms : 100ms); + + MODM_LOG_INFO << "loop: " << counter++ << modm::endl; + } + + return 0; +} diff --git a/examples/same54_xplained_pro/blink/project.xml b/examples/same54_xplained_pro/blink/project.xml new file mode 100644 index 0000000000..fa74596a8f --- /dev/null +++ b/examples/same54_xplained_pro/blink/project.xml @@ -0,0 +1,10 @@ + + modm:same54-xplained-pro + + + + + modm:build:scons + modm:platform:uart:1 + + diff --git a/examples/same54_xplained_pro/usbserial/main.cpp b/examples/same54_xplained_pro/usbserial/main.cpp new file mode 100644 index 0000000000..2bf24ef589 --- /dev/null +++ b/examples/same54_xplained_pro/usbserial/main.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016-2017, Niklas Hauser + * Copyright (c) 2022, Christopher Durand + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include +#include + +using namespace Board; + +modm::IODeviceWrapper usb_io_device; +modm::IOStream usb_stream(usb_io_device); + +int +main() +{ + Board::initialize(); + Board::initializeUsbFs(); + + tusb_init(); + + usb_stream << "Hello from USB" << modm::endl; + + uint32_t counter(0); + + modm::PeriodicTimer timer{500ms}; + while (true) + { + tud_task(); + if (timer.execute()) { + Led0::toggle(); + usb_stream << "loop: " << counter++ << modm::endl; + } + } + + return 0; +} diff --git a/examples/same54_xplained_pro/usbserial/project.xml b/examples/same54_xplained_pro/usbserial/project.xml new file mode 100644 index 0000000000..a43ae490d0 --- /dev/null +++ b/examples/same54_xplained_pro/usbserial/project.xml @@ -0,0 +1,12 @@ + + modm:same54-xplained-pro + + + + + + modm:tinyusb + modm:build:scons + modm:processing:timer + + diff --git a/examples/samv71_xplained_ultra/blink/main.cpp b/examples/samv71_xplained_ultra/blink/main.cpp new file mode 100644 index 0000000000..63d68f8de1 --- /dev/null +++ b/examples/samv71_xplained_ultra/blink/main.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016-2017, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include + +using namespace Board; + +int +main() +{ + Board::initialize(); + + // Use the logging streams to print some messages. + // Change MODM_LOG_LEVEL above to enable or disable these messages + MODM_LOG_DEBUG << "debug" << modm::endl; + MODM_LOG_INFO << "info" << modm::endl; + MODM_LOG_WARNING << "warning" << modm::endl; + MODM_LOG_ERROR << "error" << modm::endl; + + uint32_t counter(0); + + while (true) + { + Led0::toggle(); + Led1::toggle(); + modm::delay(ButtonSW0::read() ? 500ms : 100ms); + + MODM_LOG_INFO << "loop: " << counter++ << modm::endl; + } + + return 0; +} diff --git a/examples/samv71_xplained_ultra/blink/project.xml b/examples/samv71_xplained_ultra/blink/project.xml new file mode 100644 index 0000000000..44ac35294a --- /dev/null +++ b/examples/samv71_xplained_ultra/blink/project.xml @@ -0,0 +1,9 @@ + + modm:samv71-xplained-ultra + + + + + modm:build:scons + + diff --git a/ext/hathach/module.lb b/ext/hathach/module.lb index 085eaf4bf0..f0273488c2 100644 --- a/ext/hathach/module.lb +++ b/ext/hathach/module.lb @@ -101,11 +101,12 @@ def build(env): env.copy("tinyusb/src/portable/st/synopsys", "portable/st/synopsys/") elif target.platform == "sam": - if target.family == "g": + if target.family == "g5x": tusb_config["CFG_TUSB_MCU"] = "OPT_MCU_SAMG" env.copy("tinyusb/src/portable/microchip/samg/", "portable/microchip/samg/") else: - tusb_config["CFG_TUSB_MCU"] = "OPT_MCU_SAM{}{}".format(target.family.upper(), target.series.upper()) + series = "e5x" if target.series.startswith("e5") else target.series + tusb_config["CFG_TUSB_MCU"] = "OPT_MCU_SAM{}".format(series.upper()) env.copy("tinyusb/src/portable/microchip/samd/", "portable/microchip/samd/") elif target.platform == "rp": @@ -179,8 +180,10 @@ def build(env): if target.platform == "stm32": irq_data = env.query(":platform:usb:irqs") irqs = irq_data["port_irqs"][speed] - elif target.platform == "sam" and target.family in ["g"]: + elif target.platform == "sam" and target.family in ["g5x"]: irqs = ["UDP"] + elif target.platform == "sam" and target.family in ["d5x/e5x"]: + irqs = ["USB_OTHER", "USB_SOF_HSOF", "USB_TRCPT0", "USB_TRCPT1"] elif target.platform == "rp" and target.family in ["20"]: irqs = ["USBCTRL_IRQ"] else: diff --git a/ext/hathach/tusb_port.cpp.in b/ext/hathach/tusb_port.cpp.in index 37f4554039..29006dfc79 100644 --- a/ext/hathach/tusb_port.cpp.in +++ b/ext/hathach/tusb_port.cpp.in @@ -66,7 +66,7 @@ tusb_get_device_serial(uint16_t *serial_str) ((uint32_t *) UID_BASE), ((uint32_t *) UID_BASE) + 1, ((uint32_t *) UID_BASE) + 2, ((uint32_t *) UID_BASE) + 3 %% elif target.platform in ["sam"] - %% if "samd51" in target.string + %% if "samd51" in target.string or "same5" in target.string (uint32_t *)0x008061FC, (uint32_t *)0x00806010, (uint32_t *)0x00806014, (uint32_t *)0x00806018 %% else diff --git a/ext/microchip/module.lb b/ext/microchip/module.lb index a875db6af9..f5522900c3 100644 --- a/ext/microchip/module.lb +++ b/ext/microchip/module.lb @@ -23,16 +23,53 @@ def prepare(module, options): return False module.depends(":cmsis:core") + + module.add_query( + EnvironmentQuery(name="clock-map", factory=clock_map)) + return True pp = {} + +def clock_map_gclk(env, header): + clock_pattern = re.compile(r"^#define\s+(?P(MCLK|PM))_(?P(AHB|APB[A-E]))MASK_(?P([A-Z0-9])+(_2X)?(_[A-Z]+)?)_Pos(\s+)(?P([0-9]+))") + peripheral_pattern = re.compile(r"^(?P[A-Za-z]+([0-9]+[A-Za-z]+)?(_2X)?(_[A-Z]+)?)(?P[0-9]?)$") + clock_map = {} + with open(header, "r") as clock_header: + for line in clock_header: + m = clock_pattern.match(line) + if m: + m2 = peripheral_pattern.match(m.group("per")) + (peripheral, instance) = (m2.group("name"), m2.group("instance")) + if (peripheral, instance) in clock_map: + clock_map[(peripheral, instance)].append((m.group("clk_per"), m.group("clk"), m.group("pos"))) + else: + clock_map[(peripheral, instance)] = [(m.group("clk_per"), m.group("clk"), m.group("pos"))] + return clock_map + +def clock_map(env): + folder = Path(localpath(pp["folder"])) / "component" + clock_file = None + if (folder / "mclk.h").exists(): + clock_file = folder / "mclk.h" + elif (folder / "mclk_100.h").exists(): + clock_file = folder / "mclk_100.h" + elif (folder / "pm.h").exists(): + clock_file = folder / "pm.h" + elif (folder / "pm_100.h").exists(): + clock_file = folder / "pm_100.h" + + if clock_file is not None: + return clock_map_gclk(env, clock_file) + return {} + def validate(env): device = env[":target"] # Some families use the variant in header defines, some do not (e.g. SAMG) names = [ - "".join([device.identifier[f] for f in ["platform", "family", "series", "pin", "flash", "variant"]]), - "".join([device.identifier[f] for f in ["platform", "family", "series", "pin", "flash"]]), + "".join([device.identifier[f] for f in ["platform", "series", "pin", "flash", "variant"]]), + "".join([device.identifier[f] for f in ["platform", "series", "pin", "flash"]]), ] device_define = None @@ -44,6 +81,10 @@ def validate(env): for n in names: define = "__{}__".format(n.upper()) if match is not None and define in match: + # In case of multiple matches the most specific header is selected. + # The one with the matching variant suffix will have the longest name. + if device_define is not None and len(device_define) > len(define): + continue family_file = famfile.relative_to(localpath(".")) device_header = "{}.h".format(n) device_define = define diff --git a/ext/microchip/sam b/ext/microchip/sam index 718fa0f98c..f903c3f55c 160000 --- a/ext/microchip/sam +++ b/ext/microchip/sam @@ -1 +1 @@ -Subproject commit 718fa0f98cbc7d35becddabc3612d73d03929cf3 +Subproject commit f903c3f55c1273ce1d121ee860e4fe7d2c6033bb diff --git a/ext/modm-devices b/ext/modm-devices index 05fca29f63..2714f2321f 160000 --- a/ext/modm-devices +++ b/ext/modm-devices @@ -1 +1 @@ -Subproject commit 05fca29f6398f847a99215ed0ab07d54f541cc3a +Subproject commit 2714f2321f89bece09f0b339909ebe37e133858d diff --git a/repo.lb b/repo.lb index 570f35afce..87fc2b561c 100644 --- a/repo.lb +++ b/repo.lb @@ -86,7 +86,9 @@ class DevicesCache(dict): "stm32h7", "stm32l0", "stm32l1", "stm32l4", "stm32l5", "at90", "attiny", "atmega", - "samd21", "samg55", "samv70", + "samd21", "samg55", + "same70", "sams70", "samv70", "samv71", + "samd51", "same51", "same53", "same54", "rp2040", "hosted"] device_file_names = [dfn for dfn in device_file_names if any(s in dfn for s in supported)] diff --git a/src/modm/board/feather_m0/board.hpp b/src/modm/board/feather_m0/board.hpp index 2b18fd3551..19860e77eb 100644 --- a/src/modm/board/feather_m0/board.hpp +++ b/src/modm/board/feather_m0/board.hpp @@ -2,6 +2,7 @@ * Copyright (c) 2016-2017, Sascha Schade * Copyright (c) 2017-2018, Niklas Hauser * Copyright (c) 2020, Erik Henriksson + * Copyright (c) 2022, Christopher Durand * * This file is part of the modm project. * @@ -60,42 +61,39 @@ using RadioCs = GpioA06; // This is the red LED by the USB jack. using Led = D13; -/// samd21g18a running at 48MHz generated from the external 32.768 KHz crystal +/// samd21g18a running at 48MHz generated from the external 32.768 kHz crystal struct SystemClock { - static constexpr uint32_t Frequency = 48_MHz; - static constexpr uint32_t Usb = 48_MHz; - // static constexpr uint32_t Ahb = Frequency; - // static constexpr uint32_t Apba = Frequency; - // static constexpr uint32_t Apbb = Frequency; - // static constexpr uint32_t Apbc = Frequency; - - // static constexpr uint32_t Adc = Apb2; - - // static constexpr uint32_t SercomSlow = Apb2; - // static constexpr uint32_t Sercom0 = Apb2; - // static constexpr uint32_t Sercom1 = Apb2; - // static constexpr uint32_t Sercom2 = Apb2; - // static constexpr uint32_t Sercom3 = Apb2; - // static constexpr uint32_t Sercom4 = Apb2; - // static constexpr uint32_t Sercom5 = Apb2; - - // static constexpr uint32_t Apb1Timer = Apb1 * 2; - // static constexpr uint32_t Apb2Timer = Apb2 * 1; - // static constexpr uint32_t Timer1 = Apb2Timer; - // static constexpr uint32_t Timer2 = Apb1Timer; - // static constexpr uint32_t Timer3 = Apb1Timer; - // static constexpr uint32_t Timer4 = Apb1Timer; + static constexpr uint32_t Dfll48m = 48_MHz; + static constexpr uint32_t Xosc32k = 32768_Hz; + + static constexpr uint32_t Frequency = Dfll48m; + static constexpr uint32_t Usb = Dfll48m; + + static constexpr uint32_t Sercom0 = Frequency; + static constexpr uint32_t SercomSlow = Xosc32k; + + static constexpr auto ClockGen32kHz = ClockGenerator::Generator2; static bool inline enable() { + // Configure GCLK generator 2 with external 32k crystal source + GenericClockController::enableExternalCrystal32k(Xosc32StartupTime::Start_500ms); + GenericClockController::enableGenerator(); + + // generate 48 MHz from 32768 Hz crystal reference + GenericClockController::connect(ClockGen32kHz); + GenericClockController::enableDfll48mClosedLoop(); + GenericClockController::setFlashLatency(); - GenericClockController::initExternalCrystal(); - GenericClockController::initDFLL48MHz(); - GenericClockController::initOsc8MHz(); - GenericClockController::setSystemClock(ClockSource::DFLL48M); + GenericClockController::setSystemClock(); GenericClockController::updateCoreFrequency(); + + GenericClockController::connect(ClockGenerator::System); + GenericClockController::connect(ClockGen32kHz); + + GenericClockController::connect(ClockGenerator::System); return true; } }; diff --git a/src/modm/board/samd21_mini/board.hpp b/src/modm/board/samd21_mini/board.hpp index 89e81f8fce..5ef27846eb 100644 --- a/src/modm/board/samd21_mini/board.hpp +++ b/src/modm/board/samd21_mini/board.hpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2016-2017, Sascha Schade * Copyright (c) 2017-2018, Niklas Hauser + * Copyright (c) 2022, Christopher Durand * * This file is part of the modm project. * @@ -25,39 +26,30 @@ using namespace modm::literals; struct SystemClock { - static constexpr uint32_t Frequency = 48_MHz; - // static constexpr uint32_t Ahb = Frequency; - // static constexpr uint32_t Apba = Frequency; - // static constexpr uint32_t Apbb = Frequency; - // static constexpr uint32_t Apbc = Frequency; - - static constexpr uint32_t Usb = 48_MHz; - // static constexpr uint32_t Adc = Apb2; - - // static constexpr uint32_t SercomSlow = Apb2; - // static constexpr uint32_t Sercom0 = Apb2; - // static constexpr uint32_t Sercom1 = Apb2; - // static constexpr uint32_t Sercom2 = Apb2; - // static constexpr uint32_t Sercom3 = Apb2; - // static constexpr uint32_t Sercom4 = Apb2; - // static constexpr uint32_t Sercom5 = Apb2; - - // static constexpr uint32_t Apb1Timer = Apb1 * 2; - // static constexpr uint32_t Apb2Timer = Apb2 * 1; - // static constexpr uint32_t Timer1 = Apb2Timer; - // static constexpr uint32_t Timer2 = Apb1Timer; - // static constexpr uint32_t Timer3 = Apb1Timer; - // static constexpr uint32_t Timer4 = Apb1Timer; + static constexpr uint32_t Dfll48m = 48_MHz; + static constexpr uint32_t Xosc32k = 32768_Hz; + + static constexpr uint32_t Frequency = Dfll48m; + static constexpr uint32_t Usb = Dfll48m; + + static constexpr auto ClockGen32kHz = ClockGenerator::Generator2; static bool inline enable() { + // Configure GCLK generator 2 with external 32k crystal source + GenericClockController::enableExternalCrystal32k(Xosc32StartupTime::Start_500ms); + GenericClockController::enableGenerator(); + + // generate 48 MHz from 32768 Hz crystal reference + GenericClockController::connect(ClockGen32kHz); + GenericClockController::enableDfll48mClosedLoop(); + GenericClockController::setFlashLatency(); - GenericClockController::initExternalCrystal(); - GenericClockController::initDFLL48MHz(); - GenericClockController::initOsc8MHz(); - GenericClockController::setSystemClock(ClockSource::DFLL48M); + GenericClockController::setSystemClock(); GenericClockController::updateCoreFrequency(); + + GenericClockController::connect(ClockGenerator::System); return true; } }; @@ -103,10 +95,10 @@ initialize() } inline void -initializeUsbFs(uint8_t priority=3) +initializeUsbFs(uint8_t priority = 3) { - Usb::initialize(priority); - Usb::connect(); + modm::platform::Usb::initialize(priority); + modm::platform::Usb::connect(); } /// @} diff --git a/src/modm/board/samd21_xplained_pro/board.hpp b/src/modm/board/samd21_xplained_pro/board.hpp new file mode 100644 index 0000000000..4b4ce82b84 --- /dev/null +++ b/src/modm/board/samd21_xplained_pro/board.hpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2022, Christopher Durand + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- +#pragma once + +#include +#include + +#define MODM_BOARD_HAS_LOGGER + +namespace Board +{ +/// @ingroup modm_board_samd21_xplained_pro +/// @{ +using namespace modm::literals; +using namespace modm::platform; + + +struct SystemClock +{ + static constexpr uint32_t Dfll48m = 48_MHz; + static constexpr uint32_t Xosc32k = 32768_Hz; + + static constexpr uint32_t Frequency = Dfll48m; + static constexpr uint32_t Usb = Dfll48m; + + static constexpr auto ClockGen32kHz = ClockGenerator::Generator2; + + static constexpr uint32_t Sercom3 = Frequency; + static constexpr uint32_t SercomSlow = Xosc32k; + + static bool inline + enable() + { + // Configure GCLK generator 2 with external 32k crystal source + GenericClockController::enableExternalCrystal32k(Xosc32StartupTime::Start_500ms); + GenericClockController::enableGenerator(); + + // generate 48 MHz from 32768 Hz crystal reference + GenericClockController::connect(ClockGen32kHz); + GenericClockController::enableDfll48mClosedLoop(); + + GenericClockController::setFlashLatency(); + GenericClockController::setSystemClock(); + GenericClockController::updateCoreFrequency(); + + GenericClockController::connect(ClockGenerator::System); + + GenericClockController::connect(ClockGenerator::System); + GenericClockController::connect(ClockGen32kHz); + + return true; + } +}; + +using Led0 = GpioB30; +using Button = GpioA15; + +// No SoftwareGpioPort yet for SAM +struct Leds +{ + static constexpr std::size_t width{1}; + + static void setOutput() + { + Led0::setOutput(); + } + + static void write(uint32_t value) + { + Led0::set(value & 1); + } + + static void toggle() + { + Led0::toggle(); + } +}; + +struct Debug +{ + using Uart = Uart3; + using UartTx = GpioA22; + using UartRx = GpioA23; +}; + +using LoggerDevice = modm::IODeviceWrapper; + +inline void +initialize() +{ + SystemClock::enable(); + SysTickTimer::initialize(); + + Debug::Uart::initialize(); + Debug::Uart::connect(); + + Led0::setOutput(modm::Gpio::Low); + + Button::setInput(Button::InputType::PullUp); +} + +inline void initializeUsbFs() +{ + modm::platform::Usb::initialize(); + modm::platform::Usb::connect(); +} +/// @} + +} // namespace Board + diff --git a/src/modm/board/samd21_xplained_pro/board.xml b/src/modm/board/samd21_xplained_pro/board.xml new file mode 100644 index 0000000000..507a259706 --- /dev/null +++ b/src/modm/board/samd21_xplained_pro/board.xml @@ -0,0 +1,14 @@ + + + + ../../../../repo.lb + + + + + + + + modm:board:samd21-xplained-pro + + diff --git a/src/modm/board/samd21_xplained_pro/module.lb b/src/modm/board/samd21_xplained_pro/module.lb new file mode 100644 index 0000000000..359aebb225 --- /dev/null +++ b/src/modm/board/samd21_xplained_pro/module.lb @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2021, Jeff McBride +# Copyright (c) 2021-2022, Christopher Durand +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":board:samd21-xplained-pro" + module.description = "Microchip SAMD21 Xplained Pro" + +def prepare(module, options): + if not options[":target"].partname == "samd21j18a-au": + return False + + module.depends(":debug", ":platform:gclk", ":platform:gpio", ":platform:core", ":platform:usb", ":platform:uart:3") + return True + +def build(env): + env.outbasepath = "modm/src/modm/board" + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } + env.template("../board.cpp.in", "board.cpp") + env.copy('board.hpp') + + env.outbasepath = "modm/openocd/modm/board/" + env.collect(":build:openocd.source", "board/atmel_samd21_xplained_pro.cfg"); diff --git a/src/modm/board/same54_xplained_pro/board.hpp b/src/modm/board/same54_xplained_pro/board.hpp new file mode 100644 index 0000000000..a242acb4e8 --- /dev/null +++ b/src/modm/board/same54_xplained_pro/board.hpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021-2022, Christopher Durand + * Copyright (c) 2021, Jeff McBride + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- +#pragma once + +#include +#include + +#define MODM_BOARD_HAS_LOGGER + +namespace Board +{ +/// @ingroup modm_board_same54_xplained_pro +/// @{ +using namespace modm::literals; +using namespace modm::platform; + + +struct SystemClock +{ + static constexpr uint32_t Frequency = 120_MHz; + + static constexpr uint32_t Clock48MHz = 48_MHz; + static constexpr auto Generator48MHz = ClockGenerator::Generator1; + + static constexpr uint32_t Clock32k = 32768; + static constexpr auto Generator32k = ClockGenerator::Generator2; + + static constexpr uint32_t Usb = Clock48MHz; + + static constexpr uint32_t Sercom2 = Frequency; + static constexpr uint32_t SercomSlow = Clock32k; + + static bool inline + enable() + { + GenericClockController::enableExternalCrystal<12_MHz>(Xosc::Xosc1); + GenericClockController::enableDpll, 120_MHz>(); + + GenericClockController::setFlashLatency(); + GenericClockController::updateCoreFrequency(); + GenericClockController::setSystemClock(); + + GenericClockController::enableExternalCrystal32k(Xosc32StartupTime::Start_500ms); + GenericClockController::enableGenerator(); + + // generate 48 MHz clock from external 32768 Hz crystal reference + GenericClockController::connect(Generator32k); + GenericClockController::enableDfll48mClosedLoop(); + + GenericClockController::enableGenerator(); + GenericClockController::connect(Generator48MHz); + + GenericClockController::connect(ClockGenerator::System); + GenericClockController::connect(Generator32k); + + return true; + } +}; + +using Led0 = GpioC18; +using Button = GpioB31; + +// No SoftwareGpioPort yet for SAM +struct Leds +{ + static constexpr std::size_t width{1}; + + static void setOutput() + { + Led0::setOutput(); + } + + static void write(uint32_t value) + { + Led0::set(value & 1); + } +}; + +struct Debug +{ + using Uart = Uart2; + using UartTx = GpioB25; + using UartRx = GpioB24; +}; + +using LoggerDevice = modm::IODeviceWrapper; + +inline void +initialize() +{ + SystemClock::enable(); + SysTickTimer::initialize(); + + Debug::Uart::initialize(); + Debug::Uart::connect(); + + Led0::setOutput(modm::Gpio::Low); + + Button::setInput(InputType::PullUp); +} + +inline void initializeUsbFs() +{ + modm::platform::Usb::initialize(); + modm::platform::Usb::connect(); +} +/// @} + +} // namespace Board + diff --git a/src/modm/board/same54_xplained_pro/board.xml b/src/modm/board/same54_xplained_pro/board.xml new file mode 100644 index 0000000000..9f074a31ee --- /dev/null +++ b/src/modm/board/same54_xplained_pro/board.xml @@ -0,0 +1,14 @@ + + + + ../../../../repo.lb + + + + + + + + modm:board:same54-xplained-pro + + diff --git a/src/modm/board/same54_xplained_pro/module.lb b/src/modm/board/same54_xplained_pro/module.lb new file mode 100644 index 0000000000..5bb733767c --- /dev/null +++ b/src/modm/board/same54_xplained_pro/module.lb @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2021, Jeff McBride +# Copyright (c) 2021-2022, Christopher Durand +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":board:same54-xplained-pro" + module.description = "Microchip SAME54 Xplained Pro" + +def prepare(module, options): + if not options[":target"].partname == "same54p20a-au": + return False + + module.depends(":debug", ":platform:gclk", ":platform:gpio", ":platform:core", ":platform:usb", ":platform:uart:2") + return True + +def build(env): + env.outbasepath = "modm/src/modm/board" + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } + env.template("../board.cpp.in", "board.cpp") + env.copy('board.hpp') + + env.outbasepath = "modm/openocd/modm/board/" + env.copy(repopath("tools/openocd/modm/atmel_same54_xplained_pro.cfg"), "atmel_same54_xplained_pro.cfg") + env.collect(":build:openocd.source", "modm/board/atmel_same54_xplained_pro.cfg") diff --git a/src/modm/board/samg55_xplained_pro/board.hpp b/src/modm/board/samg55_xplained_pro/board.hpp index 55be67fed6..1d9c0628e0 100644 --- a/src/modm/board/samg55_xplained_pro/board.hpp +++ b/src/modm/board/samg55_xplained_pro/board.hpp @@ -39,6 +39,8 @@ struct SystemClock static constexpr uint32_t Pck6 = Mck; static constexpr uint32_t Pck7 = Mck; + static constexpr uint32_t Usart7 = Mck; + static bool inline enable() { @@ -62,7 +64,7 @@ struct SystemClock using Led = GpioA6; using Button = GpioA2; -using DebugUart = Uart7; +using DebugUart = Usart7; using TxPin = GpioA28; using RxPin = GpioA27; diff --git a/src/modm/board/samg55_xplained_pro/module.lb b/src/modm/board/samg55_xplained_pro/module.lb index b3302f866c..3aec8017fb 100644 --- a/src/modm/board/samg55_xplained_pro/module.lb +++ b/src/modm/board/samg55_xplained_pro/module.lb @@ -20,7 +20,7 @@ def prepare(module, options): module.depends( ":platform:clockgen", - ":platform:uart:7", + ":platform:usart:7", ":platform:gpio", ":platform:core", ":platform:usb", @@ -31,4 +31,4 @@ def build(env): env.outbasepath = "modm/src/modm/board" env.copy('board.hpp') - # TODO: openocd config? \ No newline at end of file + # TODO: openocd config? diff --git a/src/modm/board/samv71_xplained_ultra/board.hpp b/src/modm/board/samv71_xplained_ultra/board.hpp new file mode 100644 index 0000000000..9ae9e57e10 --- /dev/null +++ b/src/modm/board/samv71_xplained_ultra/board.hpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2022, Christopher Durand + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- +#pragma once + +#include +#include +#include + +#define MODM_BOARD_HAS_LOGGER + +namespace Board +{ +/// @ingroup modm_board_samv71_xplained_ultra +/// @{ +using namespace modm::literals; +using namespace modm::platform; + +struct SystemClock +{ + // 300MHz system clock generated by PLLA from internal Rc 12MHz clock + static constexpr uint32_t PllAMult = 25; + static constexpr uint32_t Frequency = 300_MHz; + static constexpr uint32_t Mck = Frequency / 2; // 150 MHz max. + static constexpr uint32_t Usart1 = Mck; +// static constexpr uint32_t Usb = 48_MHz; + + static bool inline + enable() + { + ClockGen::setFlashLatency(); // Flash runs off MCK + + ClockGen::enableMainInternal(MainInternalFreq::Rc12Mhz); + ClockGen::selectMainClockSource(MainClockSource::Internal); + ClockGen::enablePllA(); + ClockGen::selectMasterClk(); + ClockGen::updateCoreFrequency(); + + return true; + } +}; + +using Led0 = GpioA23; +using Led1 = GpioC9; +using ButtonSW0 = GpioA9; + +// No SoftwareGpioPort yet for SAM +struct Leds +{ + static constexpr std::size_t width{2}; + + static void setOutput() + { + Led0::setOutput(); + Led1::setOutput(); + } + + static void write(uint32_t value) + { + Led0::set(value & 1); + Led1::set(value & 2); + } +}; + +struct Debug +{ + using Uart = Usart1; + using UartTx = GpioB4; + using UartRx = GpioA21; +}; + +using LoggerDevice = modm::IODeviceWrapper; + +inline void +initialize() +{ + // Turn off the watchdog + WDT->WDT_MR = WDT_MR_WDDIS_Msk; + + SystemClock::enable(); + SysTickTimer::initialize(); + + // Disable JTAG TDI function on debug UART TX pin + MATRIX->CCFG_SYSIO |= CCFG_SYSIO_SYSIO4; + + Debug::Uart::initialize(); + Debug::Uart::connect(); + + Leds::setOutput(); + ButtonSW0::setInput(InputType::PullUp); +} + +/* +// TODO: usb +inline void initializeUsbFs() +{ + //SystemClock::enableUsb(); + //modm::platform::Usb::initialize(); +} +*/ +/// @} + +} // namespace Board + diff --git a/src/modm/board/samv71_xplained_ultra/board.xml b/src/modm/board/samv71_xplained_ultra/board.xml new file mode 100644 index 0000000000..b13402668a --- /dev/null +++ b/src/modm/board/samv71_xplained_ultra/board.xml @@ -0,0 +1,14 @@ + + + + ../../../../repo.lb + + + + + + + + modm:board:samv71-xplained-ultra + + diff --git a/src/modm/board/samv71_xplained_ultra/module.lb b/src/modm/board/samv71_xplained_ultra/module.lb new file mode 100644 index 0000000000..1b08b54603 --- /dev/null +++ b/src/modm/board/samv71_xplained_ultra/module.lb @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2021, Jeff McBride +# Copyright (c) 2022, Christopher Durand +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":board:samv71-xplained-ultra" + module.description = "Microchip SAMV71 Xplained Ultra" + +def prepare(module, options): + if not options[":target"].partname == "samv71q21b-aab": + return False + + module.depends(":debug", ":platform:clockgen", ":platform:gpio", ":platform:core", ":platform:usart:1") #, ":platform:usb") + return True + +def build(env): + env.outbasepath = "modm/src/modm/board" + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } + env.template("../board.cpp.in", "board.cpp") + env.copy('board.hpp') + env.copy(repopath("tools/openocd/modm/atmel_samv71_xplained_ultra.cfg"), "atmel_samv71_xplained_ultra.cfg") + env.collect(":build:openocd.source", "modm/src/modm/board/atmel_samv71_xplained_ultra.cfg") diff --git a/src/modm/platform/clock/sam/gclk.cpp.in b/src/modm/platform/clock/sam/gclk.cpp.in index b2e3890a41..0b7e1759f0 100644 --- a/src/modm/platform/clock/sam/gclk.cpp.in +++ b/src/modm/platform/clock/sam/gclk.cpp.in @@ -1,6 +1,7 @@ /* * Copyright (c) 2020-2021, Niklas Hauser * Copyright (c) 2020, Erik Henriksson + * Copyright (c) 2022, Christopher Durand * * This file is part of the modm project. * @@ -23,100 +24,53 @@ namespace modm::platform constinit uint16_t modm_fastdata delay_fcpu_MHz(computeDelayMhz(GenericClockController::BootFrequency)); constinit uint16_t modm_fastdata delay_ns_per_loop(computeDelayNsPerLoop(GenericClockController::BootFrequency)); +%% if target.family == "d1x/d2x/dax" bool -GenericClockController::initOsc8MHz(uint32_t waitCycles) +GenericClockController::configureOsc8m(Osc8mPrescaler prescaler, uint32_t waitCycles) { - SYSCTRL->OSC8M.bit.PRESC = 0x0; + SYSCTRL->OSC8M.bit.PRESC = static_cast(prescaler); SYSCTRL->OSC8M.bit.ONDEMAND = true; SYSCTRL->OSC8M.bit.RUNSTDBY = false; SYSCTRL->OSC8M.bit.ENABLE = true; while (!SYSCTRL->PCLKSR.bit.OSC8MRDY && --waitCycles); return waitCycles; } +%% endif +%% if target.family == "d1x/d2x/dax" +/// Enable DFLL48M in open-loop mode bool -GenericClockController::initExternalCrystal(uint32_t waitCycles) +GenericClockController::enableDfll48m(uint32_t waitCycles) { - // Enable external crystal. - SYSCTRL->XOSC32K.reg = - SYSCTRL_XOSC32K_STARTUP( 0x6u ) | - SYSCTRL_XOSC32K_XTALEN | - SYSCTRL_XOSC32K_RUNSTDBY | - SYSCTRL_XOSC32K_EN32K; - // separate call, as described in chapter 15.6.3 - SYSCTRL->XOSC32K.bit.ENABLE = 1; - while (!SYSCTRL->PCLKSR.bit.XOSC32KRDY and --waitCycles) ; - - // Write Generic Clock Generator configuration - GCLK->GENCTRL.reg = - GCLK_GENCTRL_ID(uint32_t(ClockGenerator::ExternalCrystal32K)) | - GCLK_GENCTRL_SRC_XOSC32K | - GCLK_GENCTRL_IDC | - GCLK_GENCTRL_GENEN; - // Wait for synchronization. - while (GCLK->STATUS.bit.SYNCBUSY and --waitCycles) ; - - return waitCycles; -} - -bool -GenericClockController::initDFLL48MHz(uint32_t waitCycles) -{ - // Put ExternalCrystal as source for the PLL - GCLK->CLKCTRL.reg = - GCLK_CLKCTRL_ID(uint32_t(ClockMux::DFLL48M)) | - GCLK_CLKCTRL_GEN(uint32_t(ClockGenerator::ExternalCrystal32K)) | - GCLK_CLKCTRL_CLKEN; - // Wait for synchronization. - while (GCLK->STATUS.bit.SYNCBUSY and --waitCycles) ; - - // Errata 1.2.1: Disable the OnDemand mode + // Errata 1.2.1: Disable OnDemand mode SYSCTRL->DFLLCTRL.bit.ONDEMAND = 0; - // Wait for synchronization. - while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles) ; + // Wait for synchronization + while (!SYSCTRL->PCLKSR.bit.DFLLRDY && waitCycles > 0) --waitCycles; - SYSCTRL->DFLLMUL.reg = - SYSCTRL_DFLLMUL_CSTEP( 31 ) | - SYSCTRL_DFLLMUL_FSTEP( 511 ) | - SYSCTRL_DFLLMUL_MUL(48_MHz / 32'768_Hz); - // Wait for synchronization. - while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles) ; + // TODO: is returning the right thing to do? + if (waitCycles == 0) + return false; + + // read calibration data from "NVM Software Calibration Area" (128 bits from address 0x806020) + // DFLL coarse value is in bits 63:58 + const auto calibrationData = *reinterpret_cast(0x806020 + 4); + const auto dfllCoarseCalibration = (calibrationData >> (58 - 32)) & 0b11'1111; + SYSCTRL->DFLLVAL.reg = (dfllCoarseCalibration << SYSCTRL_DFLLVAL_COARSE_Pos) + | (512 << SYSCTRL_DFLLVAL_FINE_Pos); // Write full configuration to DFLL control register - SYSCTRL->DFLLCTRL.reg |= - SYSCTRL_DFLLCTRL_MODE | // Enable the closed loop mode + SYSCTRL->DFLLCTRL.reg = (SYSCTRL->DFLLCTRL.reg | SYSCTRL_DFLLCTRL_WAITLOCK | // No output until DFLL is locked. - SYSCTRL_DFLLCTRL_QLDIS ; // Disable Quick lock - // Wait for synchronization. - while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles) ; + SYSCTRL_DFLLCTRL_QLDIS) // Disable quick lock + // Disable closed-loop and USB clock recovery mode + & ~(SYSCTRL_DFLLCTRL_MODE | SYSCTRL_DFLLCTRL_USBCRM); + while (!SYSCTRL->PCLKSR.bit.DFLLRDY); - // Enable the DFLL SYSCTRL->DFLLCTRL.bit.ENABLE = true; - // Wait for locks flags - while ( - not (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKC) or - not (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKF)); - // Wait for synchronization. - while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles) ; - - return waitCycles; -} + while (!SYSCTRL->PCLKSR.bit.DFLLRDY && waitCycles > 0) --waitCycles; -bool -GenericClockController::setSystemClock(ClockSource source, uint32_t waitCycles) -{ - GCLK->GENDIV.reg = - GCLK_GENDIV_ID(uint32_t(ClockGenerator::System)) | - GCLK_GENDIV_DIV(0u); - GCLK->GENCTRL.reg = - GCLK_GENCTRL_ID(uint32_t(ClockGenerator::System)) | - GCLK_GENCTRL_SRC(uint32_t(source)) | - GCLK_GENCTRL_IDC | - GCLK_GENCTRL_GENEN; - // Wait for synchronization. - while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) ; - - return waitCycles; + return waitCycles > 0; } +%% endif } diff --git a/src/modm/platform/clock/sam/gclk.hpp b/src/modm/platform/clock/sam/gclk.hpp deleted file mode 100644 index 84d25abfa7..0000000000 --- a/src/modm/platform/clock/sam/gclk.hpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2019, Ethan Slattery - * Copyright (c) 2020, Erik Henriksson - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#pragma once - -#include -#include "../device.hpp" -#include - -namespace modm::platform -{ -/// @ingroup modm_platform_gclk -/// @{ - -enum class -ClockSource : uint32_t -{ - OSC8M = GCLK_GENCTRL_SRC_OSC8M_Val, - DFLL48M = GCLK_GENCTRL_SRC_DFLL48M_Val -}; - -enum class -ClockGenerator : uint32_t -{ - System = 0, - ExternalCrystal32K = 1, - ULP32K = 2, - Internal8M = 3, -}; - -enum class -ClockPeripheral : uint32_t -{ - Dfll48 = GCLK_CLKCTRL_ID_DFLL48_Val, - Fdpll = GCLK_CLKCTRL_ID_FDPLL_Val, - Fdpll32K = GCLK_CLKCTRL_ID_FDPLL32K_Val, - Wdt = GCLK_CLKCTRL_ID_WDT_Val, - Rtc = GCLK_CLKCTRL_ID_RTC_Val, - Eic = GCLK_CLKCTRL_ID_EIC_Val, - Usb = GCLK_CLKCTRL_ID_USB_Val, - Evsys0 = GCLK_CLKCTRL_ID_EVSYS_0_Val, - Evsys1 = GCLK_CLKCTRL_ID_EVSYS_1_Val, - Evsys2 = GCLK_CLKCTRL_ID_EVSYS_2_Val, - Evsys3 = GCLK_CLKCTRL_ID_EVSYS_3_Val, - Evsys4 = GCLK_CLKCTRL_ID_EVSYS_4_Val, - Evsys5 = GCLK_CLKCTRL_ID_EVSYS_5_Val, - Evsys6 = GCLK_CLKCTRL_ID_EVSYS_6_Val, - Evsys7 = GCLK_CLKCTRL_ID_EVSYS_7_Val, - Evsys8 = GCLK_CLKCTRL_ID_EVSYS_8_Val, - Evsys9 = GCLK_CLKCTRL_ID_EVSYS_9_Val, - Evsys10 = GCLK_CLKCTRL_ID_EVSYS_10_Val, - Evsys11 = GCLK_CLKCTRL_ID_EVSYS_11_Val, - SercomXSlow = GCLK_CLKCTRL_ID_SERCOMX_SLOW_Val, - Sercom0 = GCLK_CLKCTRL_ID_SERCOM0_CORE_Val, - Sercom1 = GCLK_CLKCTRL_ID_SERCOM1_CORE_Val, - Sercom2 = GCLK_CLKCTRL_ID_SERCOM2_CORE_Val, - Sercom3 = GCLK_CLKCTRL_ID_SERCOM3_CORE_Val, - Sercom4 = GCLK_CLKCTRL_ID_SERCOM4_CORE_Val, - Sercom5 = GCLK_CLKCTRL_ID_SERCOM5_CORE_Val, - Tcc0 = GCLK_CLKCTRL_ID_TCC0_TCC1_Val, - Tcc1 = GCLK_CLKCTRL_ID_TCC0_TCC1_Val, - Tcc2 = GCLK_CLKCTRL_ID_TCC2_TC3_Val, - Tc3 = GCLK_CLKCTRL_ID_TCC2_TC3_Val, - Tc4 = GCLK_CLKCTRL_ID_TC4_TC5_Val, - Tc5 = GCLK_CLKCTRL_ID_TC4_TC5_Val, - Tc6 = GCLK_CLKCTRL_ID_TC6_TC7_Val, - Tc7 = GCLK_CLKCTRL_ID_TC6_TC7_Val, - Adc = GCLK_CLKCTRL_ID_ADC_Val, - AcDig = GCLK_CLKCTRL_ID_AC_DIG_Val, - AcAna = GCLK_CLKCTRL_ID_AC_ANA_Val, - Dac = GCLK_CLKCTRL_ID_DAC_Val, - Ptc = GCLK_CLKCTRL_ID_PTC_Val, - I2s0 = GCLK_CLKCTRL_ID_I2S_0_Val, - I2s1 = GCLK_CLKCTRL_ID_I2S_1_Val -}; -/// @} - -/** - * Clock management - * - * \ingroup modm_platform_gclk - */ -class GenericClockController -{ -public: - static constexpr uint32_t BootFrequency = 1'000'000; - - template< uint32_t Core_Hz> - static uint32_t - setFlashLatency(); - - template< uint32_t Core_Hz > - static void - updateCoreFrequency(); - - static bool - initOsc8MHz(uint32_t waitCycles = 2048); - - static bool - initExternalCrystal(uint32_t waitCycles = 2048); - - static bool - initDFLL48MHz(uint32_t waitCycles = 2048); - - static bool - setSystemClock( - ClockSource source = ClockSource::OSC8M, - uint32_t waitCycles = 2048); - - template< ClockPeripheral peripheral > - static void - connect(ClockGenerator clockGen); - -private: - - enum class - ClockMux : uint32_t - { - DFLL48M = 0, - }; -}; - -} - -#include "gclk_impl.hpp" diff --git a/src/modm/platform/clock/sam/gclk.hpp.in b/src/modm/platform/clock/sam/gclk.hpp.in new file mode 100644 index 0000000000..397b0acc0e --- /dev/null +++ b/src/modm/platform/clock/sam/gclk.hpp.in @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2019, Ethan Slattery + * Copyright (c) 2020, Erik Henriksson + * Copyright (c) 2022, Christopher Durand + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include "../device.hpp" +#include +#include +#include + +namespace modm::platform +{ +/// @ingroup modm_platform_gclk +/// @{ + +enum class +ClockSource : uint32_t +{ +%% for source in clock_sources + {{ source.name }} = {{ source.value }}{% if not loop.last %},{% endif %} +%% endfor +}; + +enum class +ClockGenerator : uint32_t +{ + System = 0, + Main = 0, +%% for i in range(0, generator_count) + Generator{{i}} = {{i}}{% if not loop.last %},{% endif %} +%% endfor +}; + +struct GeneratorConfiguration +{ + ClockSource source{}; + + /** + * The clock will be divided by the selected divider. + * Normal integer division and division by 2^(N+1) is supported. + * The registers will be automatically configured in the appropriate mode + * for division by powers of 2. + * + * @warning Not all generator instances support the full 16 bit divider value range. + * This is currently not checked yet at compile-time. Only the maximum limits + * are verified. + */ + uint32_t divider = 1; + + bool gpioOutputEnabled = false; +}; + +enum class +ClockPeripheral : uint32_t +{ +%% for clock in peripheral_clocks +%% set instance=clock.get("instance", "") +%% set name=clock.get("name", "") +%% if clock["peripheral"] in ["sysctrl", "oscctrl"] +%% set peripheral="" +%% else +%% set peripheral=clock["peripheral"] +%% endif +%% if clock["peripheral"] == "sercom" and name == "core" +%% set name="" +%% endif +%% set id=peripheral.capitalize() + instance + name.capitalize() + {{ id }} = {{clock["value"]}}{% if not loop.last %},{% endif %} +%% endfor +}; + +%% if has_dpll + +struct DpllSource +{ + enum class DpllReference + { + // values correspond to REFCLOCK field in DPLLCTRLB +%% if target.family == "d5x/e5x" + Gclk = 0, + Xosc32 = 1, + Xosc0 = 2, + Xosc1 = 3 +%% elif target.family == "d1x/d2x/dax" + Xosc32 = 0, + Xosc = 1, + Gclk = 2 +%% endif + }; + + DpllReference reference{}; + frequency_t frequency{}; + + constexpr bool isXoscSource() const + { +%% if target.family == "d5x/e5x" + return (reference == DpllReference::Xosc0) + || (reference == DpllReference::Xosc1); +%% elif target.family == "d1x/d2x/dax" + return reference == DpllReference::Xosc; +%% endif + } +}; + +template +constexpr DpllSource GclkSource = DpllSource{DpllSource::DpllReference::Gclk, freq}; + +template +constexpr DpllSource Xosc32Source = DpllSource{DpllSource::DpllReference::Xosc32, freq}; + +%% if target.family == "d5x/e5x" +template +constexpr DpllSource Xosc0Source = DpllSource{DpllSource::DpllReference::Xosc0, freq}; + +template +constexpr DpllSource Xosc1Source = DpllSource{DpllSource::DpllReference::Xosc1, freq}; +%% elif target.family == "d1x/d2x/dax" +template +constexpr DpllSource XoscSource = DpllSource{DpllSource::DpllReference::Xosc, freq}; +%% endif + +struct DpllConfig +{ +%% if target.family == "d5x/e5x" + static constexpr uint16_t MultiplierFractionalBits{5}; + static constexpr uint16_t MultiplierIntegerBits{13}; + static constexpr unsigned MaxReference{kHz(3200)}; + static constexpr unsigned MinOutput{MHz(96)}; + static constexpr unsigned MaxOutput{MHz(200)}; +%% elif target.family == "d1x/d2x/dax" + static constexpr uint16_t MultiplierFractionalBits{4}; + static constexpr uint16_t MultiplierIntegerBits{12}; + static constexpr unsigned MaxReference{MHz(2)}; + static constexpr unsigned MinOutput{MHz(48)}; + static constexpr unsigned MaxOutput{MHz(96)}; +%% endif + static constexpr unsigned MinReference{kHz(32)}; + // divider operation: f_out = f_in / (2 * (DIV + 1)) + static constexpr uint16_t XoscDividerBits{11}; + + uint16_t integerMultiplier{}; + uint16_t fractionalMultiplier{}; + uint16_t xoscDivider{}; +}; + +%% if target.family == "d5x/e5x" +enum class DpllInstance +{ + Dpll0 = 0, + Dpll1 = 1 +}; +%% endif +%% endif + +%% if target.family == "d5x/e5x" +enum class Xosc +{ + Xosc0 = 0, + Xosc1 = 1 +}; +%% endif + +enum class XoscStartupTime +{ + Start_31us = 0x0u, + Start_61us = 0x1u, + Start_122us = 0x2u, + Start_244us = 0x3u, + Start_488us = 0x4u, + Start_977us = 0x5u, + Start_1953us = 0x6u, + Start_3906us = 0x7u, + Start_7813us = 0x8u, + Start_15625us = 0x9u, + Start_31250us = 0xAu, + Start_61250us = 0xBu, + Start_125000us = 0xCu, + Start_250000us = 0xDu, + Start_500000us = 0xEu, + Start_1000000us = 0xFu +}; + +enum class Xosc32StartupTime +{ +%% if target.family == "d1x/d2x/dax" + Start_132us = 0x0u, + Start_1ms = 0x1u, + Start_63ms = 0x2u, + Start_125ms = 0x3u, + Start_500ms = 0x4u, + Start_1000ms = 0x5u, + Start_2000ms = 0x6u, + Start_4000ms = 0x7u +%% else + Start_63ms = 0x0u, + Start_125ms = 0x1u, + Start_500ms = 0x2u, + Start_1000ms = 0x3u, + Start_2000ms = 0x4u, + Start_4000ms = 0x5u, + Start_8000ms = 0x6u +%% endif +}; + +%% if target.family == "d1x/d2x/dax" + enum class Osc8mPrescaler + { + Prescaler1 = 0x0, + Prescaler2 = 0x1, + Prescaler4 = 0x2, + Prescaler8 = 0x3 + }; +%% endif + +/// @} + +/** + * Clock management + * + * @ingroup modm_platform_gclk + */ +class GenericClockController +{ +public: + static constexpr uint32_t BootFrequency = {{ boot_frequency }}; + + template< uint32_t Core_Hz, uint16_t Vdd_mV=3300 > + static uint32_t + setFlashLatency(); + + template< uint32_t Core_Hz > + static void + updateCoreFrequency(); + +%% if target.family == "d1x/d2x/dax" + /// Configure frequency of the internal oscillator + static bool + configureOsc8m(Osc8mPrescaler prescaler, uint32_t waitCycles = 10'000); + + /// Enable DFLL48M in open-loop mode + static bool + enableDfll48m(uint32_t waitCycles = 10'000); +%% endif + + /// Enable DFLL48M in closed-loop mode + /// @warning The reference clock on GCLK channel 0 must be available prior to calling this function + template + static bool + enableDfll48mClosedLoop(uint32_t waitCycles = 10'000); + + /// Enable PLL with automatically computed coefficients + /// If the specified tolerance is exceeded, compilation will fail. + template + static bool +%% if target.family == "d5x/e5x" + enableDpll(DpllInstance instance = DpllInstance::Dpll0); +%% else + enableDpll(); +%% endif + + /// Enable PLL with manual configuration + template + static bool +%% if target.family == "d5x/e5x" + enableDpll(DpllInstance instance = DpllInstance::Dpll0); +%% else + enableDpll(); +%% endif + + static inline void +%% if target.family == "d5x/e5x" + disableDpll(DpllInstance instance = DpllInstance::Dpll0); +%% else + disableDpll(); +%% endif + + template + static bool +%% if target.family == "d5x/e5x" + enableExternalCrystal(Xosc clock, uint32_t waitCycles = (1000u << unsigned(startupTime))); +%% else + enableExternalCrystal(uint32_t waitCycles = (1000u << unsigned(startupTime))); +%% endif + + static inline bool +%% if target.family == "d5x/e5x" + enableExternalClock(Xosc clock, uint32_t waitCycles = 10'000); +%% else + enableExternalClock(uint32_t waitCycles = 10'000); +%% endif + + static inline bool + enableExternalCrystal32k(Xosc32StartupTime time); + + static inline bool + enableExternalClock32k(); + + template + static void + connect(ClockGenerator clockGen); + + template + static void + enableGenerator(); + + template + static void + enableGenerator(); + + template + static void + disableGenerator(); + + /** + * Convenience function to configure "Main" clock generator source + * with a divider of 1. + * + * Equivalent to: + * @code + * const auto config = GeneratorConfiguration{ .source = SOURCE, .divider = 1 }; + * GenericClockController::enableGenerator(); + * @endcode + */ + template + static bool + setSystemClock(); + +private: + static inline void + sync(); + + static inline void + sync(ClockGenerator clockGen); +}; + +#if defined(__DOXYGEN__) +/// Peripheral AHB/APB bus clock control +/// @ingroup modm_platform_gclk +template +struct PeripheralClock +{ + /// Enable peripheral clock + static void enable(); + /// Disable peripheral clock + static void disable(); +}; +#else +template +struct PeripheralClock; +#endif + +} + +#include "gclk_impl.hpp" diff --git a/src/modm/platform/clock/sam/gclk_impl.hpp.in b/src/modm/platform/clock/sam/gclk_impl.hpp.in index 32485f9bbc..6a2a198802 100644 --- a/src/modm/platform/clock/sam/gclk_impl.hpp.in +++ b/src/modm/platform/clock/sam/gclk_impl.hpp.in @@ -2,6 +2,7 @@ * Copyright (c) 2019, Ethan Slattery * Copyright (c) 2020, Erik Henriksson * Copyright (c) 2021, Niklas Hauser + * Copyright (c) 2022, Christopher Durand * * This file is part of the modm project. * @@ -12,12 +13,41 @@ // ---------------------------------------------------------------------------- #include -#include +#include +#include +#include +#include namespace modm::platform { extern "C" uint32_t SystemCoreClock; +%% for peripheral, clocks in clock_map.items() +template<> +%% set name=peripheral[0] +%% set instance=peripheral[1] +%% if instance +struct PeripheralClock> +%% else +struct PeripheralClock +%% endif +{ + static inline void enable() + { +%% for clock in clocks + {{clock[0]}}->{{clock[1]}}MASK.reg |= (1u << {{clock[2]}}); +%% endfor + } + + static inline void disable() + { +%% for clock in clocks + {{clock[0]}}->{{clock[1]}}MASK.reg &= ~(1u << {{clock[2]}}); +%% endfor + } +}; +%% endfor + template< uint32_t Core_Hz > void GenericClockController::updateCoreFrequency() @@ -27,28 +57,577 @@ GenericClockController::updateCoreFrequency() delay_ns_per_loop = computeDelayNsPerLoop(Core_Hz); } -template< uint32_t Core_Hz > +template< uint32_t Core_Hz, uint16_t Vdd_mV=3300 > uint32_t GenericClockController::setFlashLatency() { +%% if target.family == "d5x/e5x": +{% raw %} + // TODO: move to device files + constexpr std::array, 6> waitStates0{{ + {22_MHz, 0}, { 44_MHz, 1}, { 67_MHz, 2}, + {89_MHz, 3}, {111_MHz, 4}, {120_MHz, 5} + }}; + constexpr std::array, 6> waitStates1{{ + { 24_MHz, 0}, { 51_MHz, 1}, { 77_MHz, 2}, + {101_MHz, 3}, {119_MHz, 4}, {120_MHz, 5} + }}; +{% endraw %} + constexpr auto waitStates = (Vdd_mV >= 2700) ? waitStates1 : waitStates0; + constexpr auto rws = std::lower_bound(std::begin(waitStates), std::end(waitStates), Core_Hz, + [](auto w1, auto w2) { + return w1.first < w2; + })->second; + NVMCTRL->CTRLA.bit.RWS = rws; + NVMCTRL->CTRLA.bit.AUTOWS = 0; +%% else // See table 41.11 (NVM Characteristics) in the datasheet if constexpr (Core_Hz > 24_MHz) { NVMCTRL->CTRLB.bit.RWS = NVMCTRL_CTRLB_RWS_HALF_Val; } else { NVMCTRL->CTRLB.bit.RWS = NVMCTRL_CTRLB_RWS_SINGLE_Val; } +%% endif return Core_Hz; } +template +bool +GenericClockController::enableDfll48mClosedLoop(uint32_t waitCycles) +{ + static_assert(reference > 732_Hz, "DFLL48 reference frequency must be larger than 732 Hz"); + static_assert(reference < 43_kHz, "DFLL48 reference frequency must be less than 33 kHz"); + constexpr auto multiplier = uint16_t(std::round(48_MHz / double(reference))); + +%% if target.family == "d1x/d2x/dax" + // Errata 1.2.1: Disable OnDemand mode + SYSCTRL->DFLLCTRL.bit.ONDEMAND = 0; + // Wait for synchronization + while (!SYSCTRL->PCLKSR.bit.DFLLRDY && waitCycles > 0) --waitCycles; + + // TODO: is returning the right thing to do? + if (waitCycles == 0) + return false; + + // read DFLL coarse value calibration from "NVM Software Calibration Area" (0x806020, 128 bits) + // DFLL coarse calibration value is in bits 63:58 + const auto calibrationData = *reinterpret_cast(0x806020 + 4); + const auto dfllCoarseCalibration = (calibrationData >> (58 - 32)) & 0b11'1111; + SYSCTRL->DFLLVAL.reg = (dfllCoarseCalibration << SYSCTRL_DFLLVAL_COARSE_Pos); + while (!SYSCTRL->PCLKSR.bit.DFLLRDY); + + // Skip coarse lock phase with DFLLVAL.COARSE pre-programmed (17.6.7.1.2 in datasheet) + SYSCTRL->DFLLCTRL.bit.BPLCKC = 1; + while (!SYSCTRL->PCLKSR.bit.DFLLRDY); + + // Set multiplier and adjustment step sizes + // TODO: evaluate step size values + SYSCTRL->DFLLMUL.reg = + SYSCTRL_DFLLMUL_CSTEP(8) | + SYSCTRL_DFLLMUL_FSTEP(8) | + SYSCTRL_DFLLMUL_MUL(multiplier); + while (!SYSCTRL->PCLKSR.bit.DFLLRDY); + + // Write full configuration to DFLL control register + SYSCTRL->DFLLCTRL.reg = (SYSCTRL->DFLLCTRL.reg | + SYSCTRL_DFLLCTRL_MODE | // Enable closed-loop mode + SYSCTRL_DFLLCTRL_WAITLOCK | // No output until DFLL is locked. + SYSCTRL_DFLLCTRL_QLDIS) // Disable quick lock + // Disable USB clock recovery mode + & ~(SYSCTRL_DFLLCTRL_USBCRM); + + SYSCTRL->DFLLCTRL.bit.ENABLE = true; + while (!SYSCTRL->PCLKSR.bit.DFLLRDY && waitCycles > 0) --waitCycles; + // wait for PLL lock + while (!SYSCTRL->PCLKSR.bit.DFLLLCKC && waitCycles > 0) --waitCycles; + while (!SYSCTRL->PCLKSR.bit.DFLLLCKF && waitCycles > 0) --waitCycles; +%% else + // Set multiplier and adjustment step sizes + // TODO: evaluate step size values + OSCCTRL->DFLLMUL.reg = + OSCCTRL_DFLLMUL_CSTEP(8) | + OSCCTRL_DFLLMUL_FSTEP(8) | + OSCCTRL_DFLLMUL_MUL(multiplier); + while (OSCCTRL->DFLLSYNC.bit.DFLLMUL); + + OSCCTRL->DFLLCTRLB.reg = + OSCCTRL_DFLLCTRLB_MODE | // Enable closed-loop mode + OSCCTRL_DFLLCTRLB_QLDIS; // Disable quick lock + while (OSCCTRL->DFLLSYNC.bit.DFLLCTRLB); + + OSCCTRL->DFLLCTRLA.bit.ONDEMAND = 0; + OSCCTRL->DFLLCTRLA.bit.ENABLE = 1; + while (OSCCTRL->DFLLSYNC.bit.ENABLE); + + // wait for PLL lock + while (!OSCCTRL->STATUS.bit.DFLLLCKC && waitCycles > 0) --waitCycles; + while (!OSCCTRL->STATUS.bit.DFLLLCKF && waitCycles > 0) --waitCycles; + while (!OSCCTRL->STATUS.bit.DFLLRDY && waitCycles > 0) --waitCycles; +%% endif + + return waitCycles > 0; +} + +%% if target.family == "d1x/d2x/dax" template< ClockPeripheral peripheral > void GenericClockController::connect(ClockGenerator clockGen) { + GCLK->CLKCTRL.bit.ID = uint8_t(peripheral); + GCLK->CLKCTRL.bit.CLKEN = 0; + while (GCLK->CLKCTRL.bit.CLKEN); + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(uint32_t(clockGen)) | - GCLK_CLKCTRL_ID(uint32_t(peripheral)); + GCLK_CLKCTRL_ID(uint8_t(peripheral)); + sync(); +} + +void +GenericClockController::sync() +{ + while (GCLK->STATUS.bit.SYNCBUSY); +} + +void +GenericClockController::sync(ClockGenerator) +{ + sync(); +} + +%% elif target.family == "d5x/e5x" +void +GenericClockController::sync() +{ + constexpr auto mask = GCLK_SYNCBUSY_GENCTRL_Msk | GCLK_SYNCBUSY_SWRST; + while (GCLK->SYNCBUSY.reg & mask); +} + +void +GenericClockController::sync(ClockGenerator clockGen) +{ + const auto bit = static_cast(clockGen) << GCLK_SYNCBUSY_GENCTRL_Pos; + while (GCLK->SYNCBUSY.reg & bit); +} + +template< ClockPeripheral peripheral > +void +GenericClockController::connect(ClockGenerator clockGen) +{ + auto& reg = GCLK->PCHCTRL[static_cast(peripheral)].reg; + reg = 0; + while (reg != 0); + reg = GCLK_PCHCTRL_CHEN | (static_cast(clockGen) << GCLK_PCHCTRL_GEN_Pos); + sync(clockGen); +} +%% endif + +template +bool +%% if target.family == "d5x/e5x" +GenericClockController::enableExternalCrystal(Xosc clock, uint32_t waitCycles) +%% else +GenericClockController::enableExternalCrystal(uint32_t waitCycles) +%% endif +{ +%% if target.family == "d5x/e5x" +%% set reg="OSCCTRL->XOSCCTRL[int(clock)]" +%% set prefix="OSCCTRL_XOSCCTRL" +%% set status="OSCCTRL->STATUS.reg & (OSCCTRL_STATUS_XOSCRDY0 << int(clock))" + PeripheralClock::enable(); +%% elif target.family == "d1x/d2x/dax" +%% set reg="SYSCTRL->XOSC" +%% set prefix="SYSCTRL_XOSC" +%% set status="SYSCTRL->PCLKSR.bit.XOSCRDY" + PeripheralClock::enable(); +%% endif + {{reg}}.bit.STARTUP = uint32_t(startupTime); + {{reg}}.bit.XTALEN = 1; + // Automatic gain control can only be enabled when the crystal is running +%% if target.family == "d5x/e5x" + {{reg}}.bit.ENALC = 0; + if constexpr (frequency <= 8'000'000) { + {{reg}}.bit.IMULT = 0x3; + {{reg}}.bit.IPTAT = 0x2; + } else if constexpr (frequency <= 16'000'000) { + {{reg}}.bit.IMULT = 0x4; + {{reg}}.bit.IPTAT = 0x3; + } else if constexpr (frequency <= 24'000'000) { + {{reg}}.bit.IMULT = 0x5; + {{reg}}.bit.IPTAT = 0x3; + } else { + {{reg}}.bit.IMULT = 0x6; + {{reg}}.bit.IPTAT = 0x3; + } +%% else + {{reg}}.bit.AMPGC = 0; + if constexpr (frequency <= 2'000'000) { + {{reg}}.bit.GAIN = 0x0; + } else if constexpr (frequency <= 4'000'000) { + {{reg}}.bit.GAIN = 0x1; + } else if constexpr (frequency <= 8'000'000) { + {{reg}}.bit.GAIN = 0x2; + } else if constexpr (frequency <= 16'000'000) { + {{reg}}.bit.GAIN = 0x3; + } else { + {{reg}}.bit.GAIN = 0x4; + } +%% endif + + // force oscillator start even if no clock sink is using it yet + {{reg}}.bit.ONDEMAND = 0; + + {{reg}}.bit.ENABLE = 1; + while(!({{status}}) && --waitCycles); + // Enable automatic gain control on success +%% if target.family == "d5x/e5x" + {{reg}}.bit.ENALC = waitCycles > 0; +%% else + {{reg}}.bit.AMPGC = waitCycles > 0; +%% endif + return waitCycles; +} + +bool +%% if target.family == "d5x/e5x" +GenericClockController::enableExternalClock(Xosc clock, uint32_t waitCycles) +%% else +GenericClockController::enableExternalClock(uint32_t waitCycles) +%% endif +{ +%% if target.family == "d5x/e5x" +%% set reg="OSCCTRL->XOSCCTRL[int(clock)]" +%% set status="OSCCTRL->STATUS.reg & (OSCCTRL_STATUS_XOSCRDY0 << int(clock))" + PeripheralClock::enable(); +%% elif target.family == "d1x/d2x/dax" +%% set reg="SYSCTRL->XOSC" +%% set status="SYSCTRL->PCLKSR.bit.XOSCRDY" + PeripheralClock::enable(); +%% endif + // force oscillator start even if no clock sink is using it yet + {{reg}}.bit.ONDEMAND = 0; + + {{reg}}.bit.STARTUP = 0; + {{reg}}.bit.XTALEN = 0; + {{reg}}.bit.ENABLE = 1; + while(!({{status}}) && --waitCycles); + return waitCycles; +} + +bool +GenericClockController::enableExternalCrystal32k(Xosc32StartupTime time) +{ +%% if target.family == "d1x/d2x/dax" +%% set reg="SYSCTRL->XOSC32K" +%% set status="SYSCTRL->PCLKSR.bit.XOSC32KRDY" + PeripheralClock::enable(); +%% else +%% set reg="OSC32KCTRL->XOSC32K" +%% set status="OSC32KCTRL->STATUS.bit.XOSC32KRDY" + PeripheralClock::enable(); +%% endif + // force oscillator start even if no clock sink is using it yet + {{reg}}.bit.ONDEMAND = 0; +%% if target.family == "d1x/d2x/dax" + // disable non-functional automatic gain control, see errata 1.1.1 + {{reg}}.bit.AAMPEN = 0; +%% endif + {{reg}}.bit.EN1K = 1; + {{reg}}.bit.EN32K = 1; + {{reg}}.bit.RUNSTDBY = 1; + {{reg}}.bit.XTALEN = 1; + {{reg}}.bit.STARTUP = static_cast(time); + {{reg}}.bit.ENABLE = 1; + while(!({{status}})); + return true; +} + +bool +GenericClockController::enableExternalClock32k() +{ +%% if target.family == "d1x/d2x/dax" +%% set reg="SYSCTRL->XOSC32K" +%% set status="SYSCTRL->PCLKSR.bit.XOSC32KRDY" + PeripheralClock::enable(); +%% else +%% set reg="OSC32KCTRL->XOSC32K" +%% set status="OSC32KCTRL->STATUS.bit.XOSC32KRDY" + PeripheralClock::enable(); +%% endif + // force oscillator start even if no clock sink is using it yet + {{reg}}.bit.ONDEMAND = 0; + + {{reg}}.bit.EN1K = 1; + {{reg}}.bit.EN32K = 1; + {{reg}}.bit.RUNSTDBY = 1; + {{reg}}.bit.XTALEN = 0; + {{reg}}.bit.STARTUP = 0; + {{reg}}.bit.ENABLE = 1; + while(!({{status}})); + return true; +} + +%% if has_dpll + +/// @cond +namespace detail +{ + +struct DpllConfigCalculation : DpllConfig +{ + double bestOutputFrequency{}; +}; + +consteval DpllConfigCalculation +findDpllConfig(double inputClock, double target, bool fractional) +{ + uint32_t maxMultiplier = (1u << DpllConfig::MultiplierIntegerBits); + if (fractional) { + maxMultiplier = (1u << (DpllConfig::MultiplierIntegerBits + DpllConfig::MultiplierFractionalBits)); + target *= (1 << DpllConfig::MultiplierFractionalBits); + } + // f_pll = f_reference * (1 + N_int + N_frac/(2^frac_bits)) + const auto idealMultiplier = (target / inputClock) - 1; + const uint32_t multplier = std::min(maxMultiplier, std::round(idealMultiplier)); + if (fractional) { + const auto output = inputClock * (multplier + 1) / (1u << DpllConfig::MultiplierFractionalBits); + return DpllConfigCalculation { + {.integerMultiplier = uint16_t(multplier >> DpllConfig::MultiplierFractionalBits), + .fractionalMultiplier = uint16_t(multplier & ((1u << DpllConfig::MultiplierFractionalBits) - 1)), + .xoscDivider = 0}, + output + }; + } else { + const auto output = inputClock * (multplier + 1); + return DpllConfigCalculation { + {.integerMultiplier = uint16_t(multplier), + .fractionalMultiplier = 0, + .xoscDivider = 0}, + output + }; + } } +consteval DpllConfigCalculation +calculateDpllConfigXosc(double xoscClock, double target, bool fractional) +{ + DpllConfigCalculation bestConfig; + bestConfig.bestOutputFrequency = std::numeric_limits::max(); + auto minError = std::numeric_limits::max(); + + for (uint32_t div = 0; div < (1u << DpllConfig::XoscDividerBits); ++div) { + // Xosc divider divides by / (2*(div + 1)) + const double reference = xoscClock / (2*(div + 1)); + if (reference < DpllConfig::MinReference || reference > DpllConfig::MaxReference) continue; + auto result = findDpllConfig(reference, target, fractional); + const auto output = result.bestOutputFrequency; + result.xoscDivider = div; + const double error = std::abs(target - output); + if (error < minError && output >= DpllConfig::MinOutput && output <= DpllConfig::MaxOutput) { + bestConfig = result; + minError = error; + } + } + return bestConfig; } +consteval DpllConfigCalculation +findDpllConfigXosc(double xoscClock, double target, uint16_t tolerance_ppm [[maybe_unused]]) +{ + return calculateDpllConfigXosc(xoscClock, target, false); + + // TODO: fractional mode is disabled for now, it was causing an unstable PLL for some parameters + // it can still be used with manually specified coefficients + + // For reducing PLL jitter first try to find a suitable configuration in PLL integer mode. + // In case no solution is found fractional mode is used. + /*DpllConfigCalculation bestConfig{calculateDpllConfigXosc(xoscClock, target, false)}; + const auto output = bestConfig.bestOutputFrequency; + const double error = std::abs(target - output); + + // treat errors below tolerance as exact match + if ((error / target) < (tolerance_ppm * double(1e-6))) { + return bestConfig; + } + + return calculateDpllConfigXosc(xoscClock, target, true);*/ +} + +} +/// @endcond + +template +bool +%% if target.family == "d5x/e5x" +GenericClockController::enableDpll(DpllInstance instance) +%% else +GenericClockController::enableDpll() +%% endif +{ + constexpr auto config = [&]() { + if constexpr (source.isXoscSource()) { + return detail::findDpllConfigXosc(source.frequency, output, tolerance_ppm); + } else { + const auto resultInt = detail::findDpllConfig(source.frequency, output, false); + // TODO: fractional mode is disabled for now, because of PLL instability issues + //if (std::abs((resultInt.bestOutputFrequency / output) - 1) < (tolerance_ppm* double(1e-6))) { + return resultInt; + //} + //return detail::findDpllConfig(source.frequency, output, true); + } + }(); + + static_assert(std::abs((config.bestOutputFrequency / output) - 1) <= tolerance_ppm, "DPLL clock tolerance exceeded"); +%% if target.family == "d5x/e5x" + return enableDpll(instance); +%% else + return enableDpll(); +%% endif +} + +/// Enable PLL with manual configuration +template +bool +%% if target.family == "d5x/e5x" +GenericClockController::enableDpll(DpllInstance instance) +%% else +GenericClockController::enableDpll() +%% endif +{ + constexpr auto reference = source.isXoscSource() ? + double(source.frequency) / (2 * (config.xoscDivider + 1)) : + double(source.frequency); + + static_assert(reference >= DpllConfig::MinReference, "DPLL reference frequency is too low"); + static_assert(reference <= DpllConfig::MaxReference, "DPLL reference frequency is too high"); + + static_assert(config.integerMultiplier < (1 << DpllConfig::MultiplierIntegerBits), + "DPLL integer multplier out of range"); + static_assert(config.fractionalMultiplier < (1 << DpllConfig::MultiplierFractionalBits), + "DPLL fractional multplier out of range"); + + constexpr auto intFactor = config.integerMultiplier; + constexpr auto fracFactor = config.fractionalMultiplier / (1 << DpllConfig::MultiplierFractionalBits); + constexpr auto output = reference * (1 + intFactor + fracFactor); + + static_assert(output >= DpllConfig::MinOutput, "DPLL output frequency is too low"); + static_assert(output <= DpllConfig::MaxOutput, "DPLL output frequency is too high"); + +%% if target.family == "d1x/d2x/dax" +%% set dpll_peripheral="SYSCTRL" +%% else +%% set dpll_peripheral="OSCCTRL" +%% endif + +%% if target.family == "d5x/e5x" +%% set instance="Dpll[int(instance)]." +%% else +%% set instance="" +%% endif + PeripheralClock::enable(); + {{dpll_peripheral}}->{{instance}}DPLLRATIO.bit.LDR = config.integerMultiplier; + {{dpll_peripheral}}->{{instance}}DPLLRATIO.bit.LDRFRAC = config.fractionalMultiplier; + {{dpll_peripheral}}->{{instance}}DPLLCTRLB.bit.DIV = config.xoscDivider; + {{dpll_peripheral}}->{{instance}}DPLLCTRLB.bit.REFCLK = static_cast(source.reference); +%% if target.family != "d1x/d2x/dax" + while({{dpll_peripheral}}->{{instance}}DPLLSYNCBUSY.bit.DPLLRATIO); +%% endif + // force oscillator start even if no clock sink is using it yet + {{dpll_peripheral}}->{{instance}}DPLLCTRLA.bit.ONDEMAND = 0; + + {{dpll_peripheral}}->{{instance}}DPLLCTRLA.bit.ENABLE = 1; +%% if target.family == "d1x/d2x/dax" + while(!{{dpll_peripheral}}->{{instance}}DPLLSTATUS.bit.ENABLE); +%% else + while({{dpll_peripheral}}->{{instance}}DPLLSYNCBUSY.bit.ENABLE); +%% endif + uint32_t counter{50000}; + while (counter && !{{dpll_peripheral}}->{{instance}}DPLLSTATUS.bit.CLKRDY) --counter; + while (counter && !{{dpll_peripheral}}->{{instance}}DPLLSTATUS.bit.LOCK) --counter; + return bool(counter); +} + +void +%% if target.family == "d5x/e5x" +GenericClockController::disableDpll(DpllInstance instance) +%% else +GenericClockController::disableDpll() +%% endif +{ + {{dpll_peripheral}}->{{instance}}DPLLCTRLA.bit.ENABLE = 0; +%% if target.family == "d1x/d2x/dax" + while({{dpll_peripheral}}->{{instance}}DPLLSTATUS.bit.ENABLE); +%% else + while({{dpll_peripheral}}->{{instance}}DPLLSYNCBUSY.bit.ENABLE); +%% endif +} +%% endif + +template +void +GenericClockController::enableGenerator() +{ + constexpr bool powerOf2 = std::has_single_bit(config.divider) && (config.divider != 1); + constexpr auto dividerReg = powerOf2 ? std::countr_zero(config.divider) : config.divider; + // TODO: check individual divider limits for each generator instance + static_assert(config.divider != 0 && dividerReg <= std::numeric_limits::max(), "Divider out of range"); + +%% if target.family == "d1x/d2x/dax" + constexpr auto gen = static_cast(clockGen); + GCLK->GENDIV.reg = + GCLK_GENDIV_ID(gen) | + GCLK_GENDIV_DIV(dividerReg); + GCLK->GENCTRL.reg = + GCLK_GENCTRL_ID(gen) | + GCLK_GENCTRL_SRC(static_cast(config.source)) | + (powerOf2 ? GCLK_GENCTRL_DIVSEL : 0u) | + (config.gpioOutputEnabled ? GCLK_GENCTRL_OE : 0u) | + GCLK_GENCTRL_IDC | + GCLK_GENCTRL_GENEN; +%% else + GCLK->GENCTRL[static_cast(clockGen)].reg = + GCLK_GENCTRL_DIV(dividerReg) | + (powerOf2 ? GCLK_GENCTRL_DIVSEL : 0u) | + (config.gpioOutputEnabled ? GCLK_GENCTRL_OE : 0u) | + GCLK_GENCTRL_IDC | + GCLK_GENCTRL_GENEN | + GCLK_GENCTRL_SRC(static_cast(config.source)); +%% endif + sync(clockGen); +} + +template +void +GenericClockController::enableGenerator() +{ + constexpr auto config = GeneratorConfiguration { + .source = source, + .divider = 1, + .gpioOutputEnabled = false + }; + GenericClockController::enableGenerator(); +} + +template +void +GenericClockController::disableGenerator() +{ +%% if target.family == "d1x/d2x/dax" + GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(static_cast(clockGen)); +%% else + GCLK->GENCTRL[static_cast(clockGen)].bit.GENEN = false; +%% endif +} + +template +bool +GenericClockController::setSystemClock() +{ + constexpr auto config = GeneratorConfiguration{ .source = source, .divider = 1 }; + GenericClockController::enableGenerator(); + sync(ClockGenerator::Main); + return true; +} + +} diff --git a/src/modm/platform/clock/sam/module.lb b/src/modm/platform/clock/sam/module.lb index f68cb6aa3f..a710bf3923 100644 --- a/src/modm/platform/clock/sam/module.lb +++ b/src/modm/platform/clock/sam/module.lb @@ -3,6 +3,7 @@ # # Copyright (c) 2016-2018, Niklas Hauser # Copyright (c) 2017, Fabian Greif +# Copyright (c) 2022, Christopher Durand # # This file is part of the modm project. # @@ -19,11 +20,42 @@ def prepare(module, options): if not options[":target"].has_driver("gclk:sam"): return False - module.depends(":cmsis:device", ":architecture:delay") + module.depends(":cmsis:device", ":architecture:delay", ":platform:clock") return True def build(env): + target = env[":target"].identifier + driver = env[":target"].get_driver("gclk") + + if target.family not in ["d1x/d2x/dax", "d5x/e5x"]: + raise RuntimeError("Unsupported device") + + if target.family == "d5x/e5x": + boot_frequency = "48'000'000" + else: # d1x/d2x/dax + boot_frequency = "1'000'000" + + clock_map = env.query(":cmsis:device:clock-map") + if target.series == "d21": + # Variant "l" devices have one comparator with instance number "1" and one without. + # To be compatible with the gpio data, instance number "0" is used for the first. + if target.variant == "l": + ac0 = clock_map[('AC', '')] + del clock_map[('AC', '')] + clock_map[('AC', '0')] = ac0 + elif ('AC', '1') in clock_map: + del clock_map[('AC', '1')] + + env.substitutions = { + "target": target, + "boot_frequency": boot_frequency, + "clock_map": clock_map, + "peripheral_clocks": driver["clock"], + "clock_sources": driver["source"], + "generator_count": int(driver["generators"][0]), + "has_dpll": True # True for all supported devices for now + } env.outbasepath = "modm/src/modm/platform/clock" - env.copy("gclk.hpp") + env.template("gclk.hpp.in") env.template("gclk.cpp.in") env.template("gclk_impl.hpp.in") diff --git a/src/modm/platform/clock/sam_pmc/clockgen.cpp.in b/src/modm/platform/clock/sam_pmc/clockgen.cpp.in index 38b272c5c7..de949ac750 100644 --- a/src/modm/platform/clock/sam_pmc/clockgen.cpp.in +++ b/src/modm/platform/clock/sam_pmc/clockgen.cpp.in @@ -70,10 +70,10 @@ ClockGen::masterClkFrequency() return mainClkFrequency() / div; case MasterClkSource::PLLA_CLK: return pllAFrequency() / div; -%% if target.family in ["g"] +%% if target.family == "g5x" case MasterClkSource::PLLB_CLK: return pllBFrequency() / div; -%% elif target.family in ["v"] +%% elif target.family == "e7x/s7x/v7x" case MasterClkSource::UPLL_CLK: return 240'000'000; // UPLLDIV2 %% endif @@ -112,10 +112,10 @@ uint32_t ClockGen::pllAFrequency() { uint32_t mul = ((PMC->CKGR_PLLAR & CKGR_PLLAR_MULA_Msk) >> CKGR_PLLAR_MULA_Pos) + 1; -%% if target.family in ["g"] +%% if target.family == "g5x" uint32_t freq = SlowClkFreqHz * mul; if(PMC->PMC_MCKR & PMC_MCKR_PLLADIV2) freq /= 2; -%% elif target.family in ["v"] +%% elif target.family == "e7x/s7x/v7x" uint32_t freq = mainClkFrequency() * mul; const auto diva = ((PMC->CKGR_PLLAR & CKGR_PLLAR_DIVA_Msk) >> CKGR_PLLAR_DIVA_Pos); if (diva == 0) return 0; // PLLA is disabled @@ -124,7 +124,7 @@ ClockGen::pllAFrequency() return freq; } -%% if target.family in ["g"] +%% if target.family == "g5x" uint32_t ClockGen::pllBFrequency() { diff --git a/src/modm/platform/clock/sam_pmc/clockgen.hpp.in b/src/modm/platform/clock/sam_pmc/clockgen.hpp.in index 37479970c5..a8f4657a95 100644 --- a/src/modm/platform/clock/sam_pmc/clockgen.hpp.in +++ b/src/modm/platform/clock/sam_pmc/clockgen.hpp.in @@ -36,9 +36,9 @@ MasterClkSource : uint32_t SLOW_CLK = PMC_MCKR_CSS_SLOW_CLK_Val, MAIN_CLK = PMC_MCKR_CSS_MAIN_CLK_Val, PLLA_CLK = PMC_MCKR_CSS_PLLA_CLK_Val, -%% if target.family in ["g"] +%% if target.family == "g5x" PLLB_CLK = PMC_MCKR_CSS_PLLB_CLK_Val, -%% elif target.family in ["v"] +%% elif target.family == "e7x/s7x/v7x" UPLL_CLK = PMC_MCKR_CSS_UPLL_CLK_Val, %% endif }; @@ -60,7 +60,7 @@ enum class MasterClkDivider : uint8_t { Div1 = 0, -%% if target.family in ["v"] +%% if target.family == "e7x/s7x/v7x" Div2 = PMC_MCKR_MDIV_PCK_DIV2_Val, Div4 = PMC_MCKR_MDIV_PCK_DIV4_Val, Div3 = PMC_MCKR_MDIV_PCK_DIV3_Val, @@ -123,7 +123,7 @@ ClockPeripheral : uint32_t Tc10 = ID_TC3_CHANNEL1, Tc11 = ID_TC3_CHANNEL2, #endif -%% if target.family in ["g"] +%% if target.family == "g5x" Pdmic0 = ID_PDMIC0, Pdmic1 = ID_PDMIC1, Mem2Mem = ID_MEM2MEM, @@ -139,7 +139,7 @@ ClockPeripheral : uint32_t Flexcom5 = ID_FLEXCOM5, Flexcom6 = ID_FLEXCOM6, Flexcom7 = ID_FLEXCOM7, -%% elif target.family in ["v"] +%% elif target.family == "e7x/s7x/v7x" Xdmac = ID_XDMAC, #ifdef ID_UART0 Uart0 = ID_UART0, @@ -199,20 +199,24 @@ ClockPeripheral : uint32_t Ssc = ID_SSC, Afec0 = ID_AFEC0, Afec1 = ID_AFEC1, +#ifdef ID_DACC Dacc = ID_DACC, +#endif Pwm0 = ID_PWM0, Pwm1 = ID_PWM1, Icm = ID_ICM, Acc = ID_ACC, Usb = ID_USBHS, +#ifdef ID_MLB Mlb = ID_MLB, +#endif Aes = ID_AES, Trng = ID_TRNG, Isi = ID_ISI, %% endif }; -%% if target.family in ["v"] +%% if target.family == "e7x/s7x/v7x" enum class UtmiRefClk : uint32_t { @@ -273,13 +277,13 @@ public: static void enablePllA(uint32_t wait_cycles = 50); -%% if target.family in ["g"] +%% if target.family == "g5x" template static void enablePllB(uint32_t wait_cycles = 50); %% endif -%% if target.family in ["v"] +%% if target.family == "e7x/s7x/v7x" template static void enableUPll(uint32_t wait_cycles = 50); @@ -310,7 +314,7 @@ public: static uint32_t pllAFrequency(); -%% if target.family in ["g"] +%% if target.family == "g5x" /** Returns the configured frequency of PLL B output */ static uint32_t pllBFrequency(); diff --git a/src/modm/platform/clock/sam_pmc/clockgen_impl.hpp.in b/src/modm/platform/clock/sam_pmc/clockgen_impl.hpp.in index 97f84d5cfa..e3b05ddee7 100644 --- a/src/modm/platform/clock/sam_pmc/clockgen_impl.hpp.in +++ b/src/modm/platform/clock/sam_pmc/clockgen_impl.hpp.in @@ -49,11 +49,11 @@ ClockGen::selectMasterClk() { // Datasheet says when selecting PLL as source, write PRES first, otherwise // write CSS first. -%% if target.family in ["g"] +%% if target.family == "g5x" static_assert(divider == MasterClkDivider::Div1, "Divider for master clock is not supported"); constexpr bool isPll = (src == MasterClkSource::PLLA_CLK || src == MasterClkSource::PLLB_CLK); -%% elif target.family in ["v"] +%% elif target.family == "e7x/s7x/v7x" constexpr bool isPll = (src == MasterClkSource::PLLA_CLK || src == MasterClkSource::UPLL_CLK); %% endif @@ -61,7 +61,7 @@ ClockGen::selectMasterClk() if constexpr (isPll) { PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_PRES_Msk) | PMC_MCKR_PRES((uint32_t)pres); while(!(PMC->PMC_SR & PMC_SR_MCKRDY)) {} -%% if target.family in ["v"] +%% if target.family == "e7x/s7x/v7x" PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_MDIV_Msk) | PMC_MCKR_MDIV(uint32_t(divider)); while(!(PMC->PMC_SR & PMC_SR_MCKRDY)) {} %% endif @@ -72,7 +72,7 @@ ClockGen::selectMasterClk() while(!(PMC->PMC_SR & PMC_SR_MCKRDY)) {} PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_PRES_Msk) | PMC_MCKR_PRES((uint32_t)pres); while(!(PMC->PMC_SR & PMC_SR_MCKRDY)) {} -%% if target.family in ["v"] +%% if target.family == "e7x/s7x/v7x" PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_MDIV_Msk) | PMC_MCKR_MDIV(uint32_t(divider)); while(!(PMC->PMC_SR & PMC_SR_MCKRDY)) {} %% endif @@ -83,10 +83,10 @@ template void ClockGen::enablePllA(uint32_t wait_cycles) { -%% if target.family in ["g"] +%% if target.family == "g5x" static_assert(9 <= multiplier && multiplier <= 7501, "Valid PLL MUL range is 9-7501"); static_assert(divider == 1, "PLL divider not supported"); -%% elif target.family in ["v"] +%% elif target.family == "e7x/s7x/v7x" static_assert(1 <= multiplier && multiplier <= 62, "Valid PLL MUL range is 1-62"); static_assert(1 <= divider && divider <= 255, "Valid PLL DIV range is 1-255"); %% endif @@ -94,9 +94,9 @@ ClockGen::enablePllA(uint32_t wait_cycles) PMC->CKGR_PLLAR = CKGR_PLLAR_MULA(multiplier-1) | CKGR_PLLAR_PLLACOUNT(wait_cycles) | -%% if target.family in ["g"] +%% if target.family == "g5x" CKGR_PLLAR_PLLAEN(1); -%% elif target.family in ["v"] +%% elif target.family == "e7x/s7x/v7x" CKGR_PLLAR_ONE | CKGR_PLLAR_DIVA(divider); %% endif @@ -104,7 +104,7 @@ ClockGen::enablePllA(uint32_t wait_cycles) while(!(PMC->PMC_SR & PMC_SR_LOCKA)) {} } -%% if target.family in ["g"] +%% if target.family == "g5x" template void ClockGen::enablePllB(uint32_t wait_cycles) @@ -119,7 +119,7 @@ ClockGen::enablePllB(uint32_t wait_cycles) } %% endif -%% if target.family in ["v"] +%% if target.family == "e7x/s7x/v7x" template void ClockGen::enableUPll(uint32_t wait_cycles) diff --git a/src/modm/platform/clock/sam_pmc/module.lb b/src/modm/platform/clock/sam_pmc/module.lb index 5ea13eabac..1ebc680e78 100644 --- a/src/modm/platform/clock/sam_pmc/module.lb +++ b/src/modm/platform/clock/sam_pmc/module.lb @@ -15,8 +15,7 @@ def init(module): module.description = "Clock Generator (CKGR)" def prepare(module, options): - if not (options[":target"].has_driver("pmc:samg*") - or options[":target"].has_driver("pmc:samv*")): + if not options[":target"].has_driver("pmc:sam*"): return False module.depends(":cmsis:device", ":platform:clock") diff --git a/src/modm/platform/clock/systick/module.lb b/src/modm/platform/clock/systick/module.lb index cb0681e145..e1b7d5b908 100644 --- a/src/modm/platform/clock/systick/module.lb +++ b/src/modm/platform/clock/systick/module.lb @@ -40,8 +40,13 @@ def build(env): # SysTick clock prescaler is dynamically chosen as /1 or /8 div = 8 - # SAMD: Prescaler not implemented - if target.platform == "sam" and target.family in ["d"]: + # SAMD5x/E5x: Prescaler not implemented + if target.platform == "sam" and target.family == "d5x/e5x": + div = 1 + # systick clock is too fast to run at 4 Hz, increase frequency + freq = 8 + # SAMD2x: Prescaler not implemented + elif target.platform == "sam" and target.family == "d1x/d2x/dax": div = 1 # H742/43: Prescaler not implemented in revY elif target.family == "h7" and target.name in ["42", "43", "50", "53"] and target.revision == "y": diff --git a/src/modm/platform/core/sam/module.lb b/src/modm/platform/core/sam/module.lb index ac910d949a..98257adf99 100644 --- a/src/modm/platform/core/sam/module.lb +++ b/src/modm/platform/core/sam/module.lb @@ -35,9 +35,10 @@ def build(env): # delay code that must be tuned for each family # (cycles per loop, setup cost in loops, max cpu frequency) tuning = { - "d": (3, 4, 48), # CM0 tested on D21 in RAM - "g": (6, 4, 120), # G55 - "v": (4, 4, 300), # V70 + "d1x/d2x/dax": (3, 4, 48), # CM0 tested on D21 in RAM + "d5x/e5x": (4, 4, 120), # CM4 tested on E54 in RAM + "g5x": (6, 4, 120), # G55 + "e7x/s7x/v7x": (4, 4, 300), # CM7 tested on V71 }[target.family] # us_shift is an optimization to limit error via fractional math diff --git a/src/modm/platform/core/sam/startup_platform.c.in b/src/modm/platform/core/sam/startup_platform.c.in index 5c99d8d829..5c6c5fd1d3 100644 --- a/src/modm/platform/core/sam/startup_platform.c.in +++ b/src/modm/platform/core/sam/startup_platform.c.in @@ -24,8 +24,7 @@ void __modm_initialize_platform(void) { -%% if target.family == "d" -%% if target.series == "21" +%% if target.series == "d21" // Overwriting the default value of the NVMCTRL.CTRLB.MANW bit (errata reference 13134) NVMCTRL->CTRLB.bit.MANW = 1; @@ -41,15 +40,5 @@ __modm_initialize_platform(void) DMAC->QOSCTRL.bit.DQOS = 2; DMAC->QOSCTRL.bit.FQOS = 2; DMAC->QOSCTRL.bit.WRBQOS = 2; - -%% elif target.series == "21" -%% endif - -%% elif target.family == "l" -%% if target.series == "21" %% endif - -%% elif target.family == "g" -%% endif - } diff --git a/src/modm/platform/extint/sam/extint.hpp b/src/modm/platform/extint/sam/extint.hpp index 9f6a365298..01977727de 100644 --- a/src/modm/platform/extint/sam/extint.hpp +++ b/src/modm/platform/extint/sam/extint.hpp @@ -52,10 +52,10 @@ class ExternalInterrupt * @param clockGen * The clock generator to use for the peripheral. If any interrupts are to * be used to akeup the CPU from standby mode, make sure this clock is - * actually running in standby. Defaults to external 32.768kHz crystal osc. + * actually running in standby. */ static void - initialize(ClockGenerator clockGen = ClockGenerator::ExternalCrystal32K, + initialize(ClockGenerator clockGen, int priority = (1ul << __NVIC_PRIO_BITS) - 1ul); protected: diff --git a/src/modm/platform/extint/sam/module.lb b/src/modm/platform/extint/sam/module.lb index ab3272c3de..8433841392 100644 --- a/src/modm/platform/extint/sam/module.lb +++ b/src/modm/platform/extint/sam/module.lb @@ -15,7 +15,7 @@ def init(module): module.description = "External Interrupt" def prepare(module, options): - if not options[":target"].partname.startswith("samd"): + if not options[":target"].partname.startswith("samd2"): return False module.depends(":cmsis:device", ":platform:gclk", ":platform:gpio") diff --git a/src/modm/platform/gpio/sam/config.hpp.in b/src/modm/platform/gpio/sam/config.hpp.in index d8c878f2a1..7e14305cd5 100644 --- a/src/modm/platform/gpio/sam/config.hpp.in +++ b/src/modm/platform/gpio/sam/config.hpp.in @@ -37,7 +37,7 @@ struct Peripherals struct {{ name }} { %% for signal, index_list in peripheral["signals"].items() -%% if not index_list or name == "Adc" +%% if not index_list or (name in ["Adc", "Afec"] and not target["family"] == "d5x/e5x") struct {{ signal }} {}; %% else template @@ -62,7 +62,7 @@ enum class PortName %% if "instances" in peripheral %% for instance in peripheral["instances"] %% for signal, index_list in peripheral["signals"].items() -%% if index_list +%% if index_list and name != "Afec" %% for index in index_list template<> template<> struct Peripherals::{{ name }}<{{ instance }}>::{{ signal }}<{{ index }}> {}; @@ -73,7 +73,7 @@ struct Peripherals::{{ name }}<{{ instance }}>::{{ signal }} {}; %% endif %% endfor %% endfor -%% elif name != "Adc" +%% elif name not in ["Adc", "Afec"] or target["family"] == "d5x/e5x" %% for signal, index_list in peripheral["signals"].items() %% for index in index_list template<> @@ -103,12 +103,12 @@ struct {{ gpio["port"] ~ gpio["pin"] }} %% else using peripheral = Peripherals::{{ signal["peripheral"] }}; %% endif - %% if "index" in signal and signal["peripheral"] != "Adc" + %% if "index" in signal and (signal["peripheral"] not in ["Adc", "Afec"] or target["family"] == "d5x/e5x") using signal = peripheral::{{ signal["name"] }}<{{ signal["index"] }}>; %% else using signal = peripheral::{{ signal["name"] }}; %% endif - %% if signal["peripheral"] == "Adc" and "index" in signal + %% if signal["peripheral"] in ["Adc", "Afec"] and "index" in signal static constexpr int32_t AdcChannel = {{ signal["index"] }}; %% else static constexpr int32_t AdcChannel = -1; diff --git a/src/modm/platform/gpio/sam/enable.cpp.in b/src/modm/platform/gpio/sam/enable.cpp.in index efa607f865..f73a33a91d 100644 --- a/src/modm/platform/gpio/sam/enable.cpp.in +++ b/src/modm/platform/gpio/sam/enable.cpp.in @@ -16,8 +16,7 @@ void modm_gpio_enable(void) { -%% if target["family"] in ["g", "v"] - +%% if target["family"] in ["g5x", "e7x/s7x/v7x"] PMC->PMC_PCER0 = %% for port in options.enable_ports (1<) | ...); }; -%% if target["family"] in ["g", "v"] +%% if target["family"] in ["g5x", "e7x/s7x/v7x"] template struct PinMuxMixin @@ -379,7 +379,6 @@ public: setInput(InputType type) { configure(type); - setInput(); } static void @@ -473,7 +472,7 @@ public: inline static bool read() { -%% if target["family"] in ["g", "v"] +%% if target["family"] in ["g5x", "e7x/s7x/v7x"] return Base::template readPortReg(PIO_PDSR_OFFSET); %% else return Base::template readPortReg(PORT_IN_OFFSET); @@ -483,7 +482,7 @@ public: inline static bool isSet() { -%% if target["family"] in ["g", "v"] +%% if target["family"] in ["g5x", "e7x/s7x/v7x"] return Base::template readPortReg(PIO_ODSR_OFFSET); %% else return Base::template readPortReg(PORT_OUT_OFFSET); @@ -547,7 +546,7 @@ struct Gpio::As : public Gpio connect() { -%% if target["family"] in ["g", "v"] +%% if target["family"] in ["g5x", "e7x/s7x/v7x"] // X1 denotes an "extra function", such as an ADC pin, which is not // enabled by the PIO ABCD register. static_assert(PinSignal::function != PinFunction::X1, diff --git a/src/modm/platform/uart/cortex/itm.cpp.in b/src/modm/platform/uart/cortex/itm.cpp.in index a089112a9f..1ca1f0f89f 100644 --- a/src/modm/platform/uart/cortex/itm.cpp.in +++ b/src/modm/platform/uart/cortex/itm.cpp.in @@ -134,6 +134,12 @@ Itm::discardTransmitBuffer() %% endif } +%% if target.platform == "sam" +// Some SAM headers define a PORT macro +#pragma push_macro("PORT") +#undef PORT +%% endif + bool Itm::write_itm(uint32_t data, uint8_t size) { @@ -163,6 +169,10 @@ Itm::write_itm(uint32_t data, uint8_t size) return true; } +%% if target.platform == "sam" +#pragma pop_macro("PORT") +%% endif + void Itm::update() { diff --git a/src/modm/platform/uart/sam/module.lb b/src/modm/platform/uart/sam-sercom/module.lb similarity index 100% rename from src/modm/platform/uart/sam/module.lb rename to src/modm/platform/uart/sam-sercom/module.lb diff --git a/src/modm/platform/uart/sam-sercom/uart.cpp.in b/src/modm/platform/uart/sam-sercom/uart.cpp.in new file mode 100644 index 0000000000..1c527e4535 --- /dev/null +++ b/src/modm/platform/uart/sam-sercom/uart.cpp.in @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2020, Erik Henriksson + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +%% set name = "Uart" ~ id +%% set hal = "UartHal" ~ id + +#include "../device.hpp" +#include "uart_hal_{{ id }}.hpp" +#include "uart_{{ id }}.hpp" + +namespace modm::platform +{ + +void +{{ name }}::writeBlocking(uint8_t data) +{ + while(!{{ hal }}::isTransmitRegisterEmpty()); + {{ hal }}::write(data); +} + +void +{{ name }}::writeBlocking(const uint8_t *data, std::size_t length) +{ + while (length-- != 0) { + writeBlocking(*data++); + } +} + +void +{{ name }}::flushWriteBuffer() +{ + return; +} + +bool +{{ name }}::write(uint8_t data) +{ + if({{ hal }}::isTransmitRegisterEmpty()) { + {{ hal }}::write(data); + return true; + } else { + return false; + } +} + +std::size_t +{{ name }}::write(const uint8_t *data, std::size_t length) +{ + uint32_t i = 0; + for (; i < length; ++i) + { + if (!write(*data++)) { + return i; + } + } + return i; +} + +bool +{{ name }}::isWriteFinished() +{ + return {{ hal }}::isTransmitRegisterEmpty(); +} + +std::size_t +{{ name }}::discardTransmitBuffer() +{ + return 0; +} + +bool +{{ name }}::read(uint8_t &data) +{ + if({{ hal }}::isReceiveRegisterNotEmpty()) { + {{ hal }}::read(data); + return true; + } else { + return false; + } +} + +std::size_t +{{ name }}::read(uint8_t *data, std::size_t length) +{ + (void)length; // avoid compiler warning + if(read(*data)) { + return 1; + } else { + return 0; + } +} + +std::size_t +{{ name }}::discardReceiveBuffer() +{ + return 0; +} + +} // namespace modm::platform diff --git a/src/modm/platform/uart/sam-sercom/uart.hpp.in b/src/modm/platform/uart/sam-sercom/uart.hpp.in new file mode 100644 index 0000000000..d0170e5b52 --- /dev/null +++ b/src/modm/platform/uart/sam-sercom/uart.hpp.in @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2020, Erik Henriksson + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +%% set hal = "UartHal" ~ id +%% set name = "Uart" ~ id + +#pragma once + +#include +#include "uart_base.hpp" +#include "uart_hal_{{ id }}.hpp" +#include + +namespace modm::platform +{ + +/** + * Universal asynchronous receiver transmitter ({{ "Uart" | upper ~ id }}) + * + * @author Erik Henriksson + * @ingroup modm_platform_uart modm_platform_uart_{{id}} + */ +class {{ name }} : public UartBase, public ::modm::Uart +{ + + // Separate because that makes GCC print the template args. + template + struct ValidateNotSame { + static_assert( + !std::is_same_v, + "Rx and Tx cannot use the same signal!"); + }; + + template + static void + configurePadMapping() + { + constexpr auto rxIndex = detail::get_pad_index_v; + constexpr auto txIndex = detail::get_pad_index_v; + + PeripheralClock>::enable(); + + if constexpr (rxIndex == 0) { + {{ hal }}::setRxPinout(RxPinout::RxPad0); + } else if constexpr (rxIndex == 1) { + {{ hal }}::setRxPinout(RxPinout::RxPad1); + } else if constexpr (rxIndex == 2) { + {{ hal }}::setRxPinout(RxPinout::RxPad2); + } else if constexpr (rxIndex == 3) { + {{ hal }}::setRxPinout(RxPinout::RxPad3); + } else { + // assert is always false + static_assert(!std::is_same_v, "Invalid rx signal"); + } + + if constexpr (txIndex == 0) { + {{ hal }}::setTxPinout(TxPinout::TxPad0_XckPad1); +%% if target.family != "d5x/e5x" + } else if constexpr (txIndex == 2) { + {{ hal }}::setTxPinout(TxPinout::TxPad2_XckPad3); +%% endif + } else { + // assert is always false + static_assert(!std::is_same_v, "Invalid tx signal"); + } + } + +public: + template< class... Pins > + static void + connect() + { + using RxPin = GetPin_t; + using TxPin = GetPin_t; + static_assert( + !std::is_same_v, + "Rx and Tx cannot use the same pin!"); + using Sercom = Peripherals::Sercom<{{ id | int }}>; + using RxConnector = typename RxPin::template Connector, Sercom::Pad<1>, Sercom::Pad<2>, Sercom::Pad<3>>; + using TxConnector = typename TxPin::template Connector, Sercom::Pad<2>>; + ValidateNotSame {}; + + configurePadMapping(); + RxConnector::connect(); + TxConnector::connect(); + } + + template< class SystemClock, baudrate_t baudrate, percent_t tolerance=pct(1) > + static void + initialize(Parity parity = Parity::Disabled) + { + {{ hal }}::initialize(parity); + {{ hal }}::setTransmitterEnable(true); + {{ hal }}::setReceiverEnable(true); + } + + static void + writeBlocking(uint8_t data); + + static void + writeBlocking(const uint8_t *data, std::size_t length); + + static void + flushWriteBuffer(); + + static bool + write(uint8_t data); + + static std::size_t + write(const uint8_t *data, std::size_t length); + + static bool + isWriteFinished(); + + static std::size_t + discardTransmitBuffer(); + + static bool + read(uint8_t &data); + + static std::size_t + read(uint8_t *buffer, std::size_t length); + + static std::size_t + discardReceiveBuffer(); +}; + +} // namespace modm::platform diff --git a/src/modm/platform/uart/sam-sercom/uart_base.hpp.in b/src/modm/platform/uart/sam-sercom/uart_base.hpp.in new file mode 100644 index 0000000000..03aaf96da3 --- /dev/null +++ b/src/modm/platform/uart/sam-sercom/uart_base.hpp.in @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020, Erik Henriksson + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include "../device.hpp" +#include +#include +#include + +/// @cond +namespace modm::platform::detail +{ + template + struct get_pad_index; + + template typename T, int index> + struct get_pad_index> + { + static constexpr int value{index}; + }; + + template + static constexpr auto get_pad_index_v = get_pad_index::value; +} +/// @endcond + +namespace modm::platform +{ +/** + * Base class for the UART classes + * + * Provides some common enum that do not depend on the specific UART. + * + * @ingroup modm_platform_uart + */ +class UartBase +{ +public: + enum class Parity : uint32_t + { + Even = 0, + Odd = 1, + Disabled = 2, + }; + + enum class RxPinout : uint8_t + { + RxPad0 = 0, + RxPad1 = 1, + RxPad2 = 2, + RxPad3 = 3 + }; + + enum class TxPinout : uint8_t + { + TxPad0_XckPad1 = 0, +%% if target.family != "d5x/e5x" + TxPad2_XckPad3 = 1, +%% endif + TxPad0_RtsTePad2_CtsPad3 = 2, +%% if target.family in ["d5x/e5x", "l1x", "l2x"] + TxPad0_XckPad1_RtsTePad2 = 3 +%% endif + }; +}; + +} // namespace modm::platform diff --git a/src/modm/platform/uart/sam/uart_hal.hpp.in b/src/modm/platform/uart/sam-sercom/uart_hal.hpp.in similarity index 92% rename from src/modm/platform/uart/sam/uart_hal.hpp.in rename to src/modm/platform/uart/sam-sercom/uart_hal.hpp.in index dce1143e6b..833a4f6430 100644 --- a/src/modm/platform/uart/sam/uart_hal.hpp.in +++ b/src/modm/platform/uart/sam-sercom/uart_hal.hpp.in @@ -108,6 +108,14 @@ public: /// Returns true if data can be written static inline bool isTransmitRegisterEmpty(); + + /// Configure mapping between rx pin functions and IO pads + static inline void + setRxPinout(RxPinout rxPinout); + + /// Configure mapping between tx pin functions and IO pads + static inline void + setTxPinout(TxPinout txPinout); }; } // namespace modm::platform diff --git a/src/modm/platform/uart/sam/uart_hal_impl.hpp.in b/src/modm/platform/uart/sam-sercom/uart_hal_impl.hpp.in similarity index 86% rename from src/modm/platform/uart/sam/uart_hal_impl.hpp.in rename to src/modm/platform/uart/sam-sercom/uart_hal_impl.hpp.in index 8cfbddcb59..8947a40bee 100644 --- a/src/modm/platform/uart/sam/uart_hal_impl.hpp.in +++ b/src/modm/platform/uart/sam-sercom/uart_hal_impl.hpp.in @@ -55,18 +55,13 @@ void {{ name }}::initialize(Parity parity) { // Enable peripheral clock in power manager. - PM->APBCMASK.bit.{{ peripheral }}_ = true; - GenericClockController::connect(ClockGenerator::System); - while (GCLK->STATUS.bit.SYNCBUSY); + PeripheralClock>::enable(); // Reset USART reset(); // Set clock mode internal {{ peripheral }}->USART.CTRLA.bit.MODE = 0x1; // Set asynchronous mode {{ peripheral }}->USART.CTRLA.bit.CMODE = 0x0; - // Set rx/tx pins - {{ peripheral }}->USART.CTRLA.bit.RXPO = 0x3; // Pad3 - {{ peripheral }}->USART.CTRLA.bit.TXPO = 0x1; // Pad2; // Set character size to 8 bits {{ peripheral }}->USART.CTRLB.bit.CHSIZE = 0x0; // Set Data order to LSB first @@ -77,11 +72,11 @@ void {{ peripheral }}->USART.CTRLB.bit.SBMODE = 0x0; // Oversampling 8x or 16x - constexpr uint32_t scalar = (baudrate * 16l > SystemClock::Frequency) ? 8 : 16; + constexpr uint32_t scalar = (baudrate * 16l > SystemClock::{{ sercom | capitalize }}) ? 8 : 16; {{ peripheral }}->USART.CTRLA.bit.SAMPR = (scalar == 16) ? 0x1 : 0x3; // Prescaler 13 bit integer, 3 bit fractional constexpr auto result = Prescaler::from_range( - SystemClock::Frequency/(scalar/8), baudrate, 1, (1ul << 16) - 1ul); + SystemClock::{{ sercom | capitalize }}/(scalar/8), baudrate, 1, (1ul << 16) - 1ul); assertBaudrateInTolerance< result.frequency, baudrate, tolerance >(); {{ peripheral }}->USART.BAUD.FRAC.BAUD = result.prescaler >> 3; {{ peripheral }}->USART.BAUD.FRAC.FP = result.prescaler & 0b111; @@ -141,4 +136,16 @@ bool return {{ peripheral }}->USART.INTFLAG.bit.DRE; } +void +{{ name }}::setRxPinout(RxPinout rxPinout) +{ + {{ peripheral }}->USART.CTRLA.bit.RXPO = static_cast(rxPinout); +} + +void +{{ name }}::setTxPinout(TxPinout txPinout) +{ + {{ peripheral }}->USART.CTRLA.bit.TXPO = static_cast(txPinout); +} + } // namespace modm::platform diff --git a/src/modm/platform/uart/sam/uart.cpp.in b/src/modm/platform/uart/sam/uart.cpp.in index 1c527e4535..24c45a1f07 100644 --- a/src/modm/platform/uart/sam/uart.cpp.in +++ b/src/modm/platform/uart/sam/uart.cpp.in @@ -1,5 +1,6 @@ /* - * Copyright (c) 2020, Erik Henriksson + * Copyright (c) 2021, Jeff McBride + * Copyright (c) 2022, Christopher Durand * * This file is part of the modm project. * @@ -9,55 +10,92 @@ */ // ---------------------------------------------------------------------------- -%% set name = "Uart" ~ id -%% set hal = "UartHal" ~ id +#include "{{ type }}_{{ id }}.hpp" -#include "../device.hpp" -#include "uart_hal_{{ id }}.hpp" -#include "uart_{{ id }}.hpp" +#include -namespace modm::platform -{ +%% set name="{}{}".format(type.capitalize(), id) +%% set reg=name.upper() +%% if type == "usart" and target.family == "e7x/s7x/v7x" and target.variant == "b" +%% set reg_suffix="_USART" +%% else +%% set reg_suffix="" +%% endif -void -{{ name }}::writeBlocking(uint8_t data) +namespace { - while(!{{ hal }}::isTransmitRegisterEmpty()); - {{ hal }}::write(data); + static modm::atomic::Queue rxBuffer; + static modm::atomic::Queue txBuffer; } -void -{{ name }}::writeBlocking(const uint8_t *data, std::size_t length) +MODM_ISR({{ peripheral | upper }}{{ id }}) { - while (length-- != 0) { - writeBlocking(*data++); + using namespace modm::platform; + if({{ name }}::isReceiveReady()) { + uint8_t data = (uint8_t){{ name }}::Regs()->{{ prefix }}_RHR; + {{ name }}::read(data); + rxBuffer.push(data); + } + + if({{ name }}::isTransmitReady()) { + if(txBuffer.isEmpty()) { + {{ name }}::Regs()->{{ prefix }}_IDR = {{ prefix }}_IDR_TXRDY_Msk; + } else { + {{ name }}::Regs()->{{ prefix }}_THR = txBuffer.get(); + txBuffer.pop(); + } } } -void -{{ name }}::flushWriteBuffer() +namespace modm::platform { - return; + +bool +{{ name }}::read(uint8_t &dataOut) { + if(rxBuffer.isEmpty()) { + return false; + } else { + dataOut = rxBuffer.get(); + rxBuffer.pop(); + return true; + } +} + +std::size_t +{{ name }}::read(uint8_t *data, std::size_t length) { + uint32_t i = 0; + for(; i < length; i++) { + if(rxBuffer.isEmpty()) { + return i; + } else { + data[i] = rxBuffer.get(); + rxBuffer.pop(); + } + } + return i; } bool {{ name }}::write(uint8_t data) { - if({{ hal }}::isTransmitRegisterEmpty()) { - {{ hal }}::write(data); - return true; + if(txBuffer.isEmpty() && isTransmitReady()) { + Regs()->{{ prefix }}_THR = data; } else { - return false; + if(!txBuffer.push(data)) { + return false; + } + // Enable tx interrupt + Regs()->{{ prefix }}_IER = {{ prefix }}_IER_TXRDY_Msk; } + return true; } std::size_t {{ name }}::write(const uint8_t *data, std::size_t length) { uint32_t i = 0; - for (; i < length; ++i) - { - if (!write(*data++)) { + for(; i < length; i++) { + if(!write(data[i])) { return i; } } @@ -67,41 +105,34 @@ std::size_t bool {{ name }}::isWriteFinished() { - return {{ hal }}::isTransmitRegisterEmpty(); + return txBuffer.isEmpty() && isTransmitReady(); } -std::size_t -{{ name }}::discardTransmitBuffer() +void +{{ name }}::flushWriteBuffer() { - return 0; + while(!isWriteFinished()); } -bool -{{ name }}::read(uint8_t &data) +void +{{ name }}::setParity(Parity parity) { - if({{ hal }}::isReceiveRegisterNotEmpty()) { - {{ hal }}::read(data); - return true; - } else { - return false; - } + Regs()->{{ prefix }}_MR = (Regs()->{{ prefix }}_MR & ~{{ prefix }}_MR{{reg_suffix}}_PAR_Msk) | (uint32_t)parity; } -std::size_t -{{ name }}::read(uint8_t *data, std::size_t length) +%% if type == "usart" +void +{{ name }}::setWordLength(WordLength length) { - (void)length; // avoid compiler warning - if(read(*data)) { - return 1; + if(length == WordLength::Bit9) { + Regs()->{{ prefix }}_MR |= {{ prefix }}_MR{{reg_suffix}}_MODE9_Msk; } else { - return 0; + Regs()->{{ prefix }}_MR &= ~{{ prefix }}_MR{{reg_suffix}}_MODE9_Msk; + Regs()->{{ prefix }}_MR = + (Regs()->{{ prefix }}_MR & ~{{ prefix }}_MR_CHRL_Msk) + | {{ prefix }}_MR_CHRL((uint32_t)length); } } +%% endif -std::size_t -{{ name }}::discardReceiveBuffer() -{ - return 0; -} - -} // namespace modm::platform +} // namespace modm::platform diff --git a/src/modm/platform/uart/sam/uart.hpp.in b/src/modm/platform/uart/sam/uart.hpp.in index 56e4a3f424..0d157f1014 100644 --- a/src/modm/platform/uart/sam/uart.hpp.in +++ b/src/modm/platform/uart/sam/uart.hpp.in @@ -1,5 +1,6 @@ /* - * Copyright (c) 2020, Erik Henriksson + * Copyright (c) 2021, Jeff McBride + * Copyright (c) 2022, Christopher Durand * * This file is part of the modm project. * @@ -9,37 +10,50 @@ */ // ---------------------------------------------------------------------------- -%% set hal = "UartHal" ~ id -%% set name = "Uart" ~ id - #pragma once +#include "{{ type }}_base.hpp" #include -#include "uart_base.hpp" -#include "uart_hal_{{ id }}.hpp" -#include +#include +#include +#include + +%% set name="{}{}".format(type.capitalize(), id) +%% set reg=name.upper() +%% if type == "usart" and target.family == "e7x/s7x/v7x" and target.variant == "b" +%% set reg_suffix="_USART" +%% else +%% set reg_suffix="" +%% endif namespace modm::platform { /** - * Universal asynchronous receiver transmitter ({{ "Uart" | upper ~ id }}) +%% if type == "usart" + * Universal synchronous asynchronous receiver transmitter (USART{{ id }}) +%% else + * Universal asynchronous receiver transmitter (UART{{ id }}) +%% endif * - * @author Erik Henriksson - * @ingroup modm_platform_uart modm_platform_uart_{{id}} + * @author Jeff McBride + * @author Christopher Durand + * @ingroup modm_platform_{{type}} modm_platform_{{type}}_{{id}} */ -class {{ name }} : public UartBase, public ::modm::Uart +class {{ name }} : public {{ type | capitalize }}Base, public modm::Uart { - - // Separate because that makes GCC print the template args. - template - struct ValidateNotSame { - static_assert( - !std::is_same_v, - "Rx and Tx cannot use the same signal!"); - }; - +private: + // prevent name collision between ::Uart* from SAM header with modm::Uart +%% if type == "uart" + static auto* Regs() { return reinterpret_cast<::Uart*>{{ reg }}; } +%% else + static auto* Regs() { return {{ reg }}; } +%% endif + friend void ::{{ peripheral | upper }}{{ id }}_IRQHandler(); public: + static constexpr size_t RxBufferSize = {{ options["buffer.rx"] }}; + static constexpr size_t TxBufferSize = {{ options["buffer.tx"] }}; + template< class... Pins > static void connect() @@ -49,52 +63,89 @@ public: static_assert( !std::is_same_v, "Rx and Tx cannot use the same pin!"); - using Sercom = Peripherals::Sercom<{{ id | int }}>; - using RxConnector = typename RxPin::template Connector, Sercom::Pad<1>, Sercom::Pad<2>, Sercom::Pad<3>>; - using TxConnector = typename TxPin::template Connector, Sercom::Pad<2>>; - ValidateNotSame {}; + using Peripheral = Peripherals::{{ peripheral }}<{{ id | int }}>; + %% if peripheral == "Flexcom" + using RxConnector = typename RxPin::template Connector; + using TxConnector = typename TxPin::template Connector; + %% elif type == "usart" + using RxConnector = typename RxPin::template Connector>; + using TxConnector = typename TxPin::template Connector>; + %% elif type == "uart" + using RxConnector = typename RxPin::template Connector>; + using TxConnector = typename TxPin::template Connector>; + %% endif RxConnector::connect(); TxConnector::connect(); } + template< class SystemClock, baudrate_t baudrate, percent_t tolerance=pct(1) > - static void - initialize(Parity parity = Parity::Disabled) + static inline void + initialize( + Parity parity=Parity::Disabled, +%% if type == "usart" + WordLength length=WordLength::Bit8, +%% endif + uint8_t irq_priority = 5) { - {{ hal }}::initialize(parity); - {{ hal }}::setTransmitterEnable(true); - {{ hal }}::setReceiverEnable(true); + ClockGen::enable(); + +%% if peripheral == "Flexcom" + FLEXCOM{{ id }}->FLEXCOM_MR = FLEXCOM_MR_OPMODE_USART; +%% endif + constexpr auto result = Prescaler::from_function( + SystemClock::{{type | capitalize}}{{id}}, + baudrate, + 1, + 65535, +%% if type == "usart" + [](uint32_t x) { return x * 8; } +%% else + [](uint32_t x) { return x * 16; } +%% endif + ); + + Regs()->{{ prefix }}_BRGR = result.index; + +%% if type == "usart" + // Use 8x oversampling (this affects baud rate generation) + Regs()->{{ prefix }}_MR = {{ prefix }}_MR{{reg_suffix}}_OVER; +%% endif + + setParity(parity); +%% if type == "usart" + setWordLength(length); +%% endif + Regs()->{{ prefix }}_CR = {{ prefix }}_CR_RXEN_Msk | {{ prefix }}_CR_TXEN_Msk; + // Enable rx interrupt + Regs()->{{ prefix }}_IER = {{ prefix }}_IER_RXRDY_Msk; + + // Enable the IRQ + NVIC_SetPriority({{ peripheral | upper }}{{ id }}_IRQn, irq_priority); + NVIC_EnableIRQ({{ peripheral | upper }}{{ id }}_IRQn); } - static void - writeBlocking(uint8_t data); + static bool read(uint8_t &dataOut); - static void - writeBlocking(const uint8_t *data, std::size_t length); + static std::size_t read(uint8_t *data, std::size_t length); - static void - flushWriteBuffer(); + static bool write(uint8_t data); - static bool - write(uint8_t data); + static std::size_t write(const uint8_t *data, std::size_t length); - static std::size_t - write(const uint8_t *data, std::size_t length); + static bool isWriteFinished(); - static bool - isWriteFinished(); + static void flushWriteBuffer(); - static std::size_t - discardTransmitBuffer(); + static void setParity(Parity parity); - static bool - read(uint8_t &data); +%% if type == "usart" + static void setWordLength(WordLength length); +%% endif - static std::size_t - read(uint8_t *buffer, std::size_t length); + static inline bool isTransmitReady() { return Regs()->{{ prefix }}_{{ sr }} & {{ prefix }}_{{ sr }}_TXRDY_Msk; } - static std::size_t - discardReceiveBuffer(); + static inline bool isReceiveReady() { return Regs()->{{ prefix }}_{{ sr }} & {{ prefix }}_{{ sr }}_RXRDY_Msk; } }; -} // namespace modm::platform +} // namespace modm::platform diff --git a/src/modm/platform/uart/samg/module.lb b/src/modm/platform/uart/sam/uart/module.lb similarity index 69% rename from src/modm/platform/uart/samg/module.lb rename to src/modm/platform/uart/sam/uart/module.lb index 21747d916c..6443f5f231 100644 --- a/src/modm/platform/uart/samg/module.lb +++ b/src/modm/platform/uart/sam/uart/module.lb @@ -10,7 +10,17 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # ----------------------------------------------------------------------------- -props = {} +def _get_properties(env, instance=None): + device = env[":target"] + return { + "type" : "uart", + "peripheral" : "Uart", + "id" : instance, + "prefix" : "UART", + "sr" : "SR", + "target" : device.identifier + } + class Instance(Module): def __init__(self, driver, instance): @@ -40,24 +50,21 @@ class Instance(Module): return True def build(self, env): - device = env[":target"].identifier - global props - props["id"] = self.instance - - env.substitutions = props + env.substitutions = _get_properties(env, self.instance) env.outbasepath = "modm/src/modm/platform/uart" - env.template("uart.hpp.in", "uart_{}.hpp".format(self.instance)) - env.template("uart.cpp.in", "uart_{}.cpp".format(self.instance)) + env.template("../uart.hpp.in", "uart_{}.hpp".format(self.instance)) + env.template("../uart.cpp.in", "uart_{}.cpp".format(self.instance)) def init(module): module.name = ":platform:uart" - module.description = "Universal Synchronous Asynchronous Receiver Transmitter (UART)" + module.description = "Universal Asynchronous Receiver Transmitter (UART)" + def prepare(module, options): device = options[":target"] - if not (device.has_driver("usart:samg*")): + if (not device.has_driver("uart:sam*")) or device.has_driver("sercom:sam"): return False module.depends( @@ -67,17 +74,15 @@ def prepare(module, options): ":platform:gpio", ":platform:clockgen") - global props - drivers = options[":target"].get_all_drivers("usart") + drivers = options[":target"].get_all_drivers("uart") for driver in drivers: for instance in driver["instance"]: module.add_submodule(Instance(driver, instance)) - props["target"] = device.identifier return True + def build(env): - global props - env.substitutions = props + env.substitutions = _get_properties(env) env.outbasepath = "modm/src/modm/platform/uart" - env.copy("uart_base.hpp") + env.template("../uart_base.hpp.in", "uart_base.hpp") diff --git a/src/modm/platform/uart/sam/uart_base.hpp.in b/src/modm/platform/uart/sam/uart_base.hpp.in index 3d0e0eb723..ad5f4123e9 100644 --- a/src/modm/platform/uart/sam/uart_base.hpp.in +++ b/src/modm/platform/uart/sam/uart_base.hpp.in @@ -1,5 +1,6 @@ /* - * Copyright (c) 2020, Erik Henriksson + * Copyright (c) 2021, Jeff McBride + * Copyright (c) 2022, Christopher Durand * * This file is part of the modm project. * @@ -11,31 +12,52 @@ #pragma once -#include #include "../device.hpp" -#include -#include -#include - namespace modm::platform { -/** - * Base class for the UART classes - * - * Provides some common enum that do not depend on the specific UART. - * - * @ingroup modm_platform_uart - */ -class UartBase + +/// @ingroup modm_platform_uart +class {{type | capitalize}}Base { public: enum class Parity : uint32_t { - Even = 0, - Odd = 1, - Disabled = 2, +%% if type == "usart" +%% if target.family == "e7x/s7x/v7x" and target.variant == "b" + Disabled = US_MR_USART_PAR_NO_Val, + Even = US_MR_USART_PAR_EVEN_Val, + Odd = US_MR_USART_PAR_ODD_Val, + Space = US_MR_USART_PAR_SPACE_Val, + Mark = US_MR_USART_PAR_MARK_Val, + MultiDrop = US_MR_USART_PAR_MULTIDROP_Val +%% else + Disabled = US_MR_PAR_NO, + Even = US_MR_PAR_EVEN, + Odd = US_MR_PAR_ODD, + Space = US_MR_PAR_SPACE, + Mark = US_MR_PAR_MARK, + MultiDrop = US_MR_PAR_MULTIDROP +%% endif +%% else + Disabled = UART_MR_PAR_NO, + Even = UART_MR_PAR_EVEN, + Odd = UART_MR_PAR_ODD, + Space = UART_MR_PAR_SPACE, + Mark = UART_MR_PAR_MARK +%% endif + }; + +%% if type == "usart" + enum class WordLength : uint32_t + { + Bit5 = 0, + Bit6, + Bit7, + Bit8, + Bit9 }; +%% endif }; -} // namespace modm::platform +} // namespace modm::platform diff --git a/src/modm/platform/uart/sam/usart/module.lb b/src/modm/platform/uart/sam/usart/module.lb new file mode 100644 index 0000000000..d9768d5861 --- /dev/null +++ b/src/modm/platform/uart/sam/usart/module.lb @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2021, Jeff McBride +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def _get_properties(env, instance=None): + device = env[":target"] + peripheral = "Flexcom" if device.has_driver("flexcom") else "Usart" + return { + "type" : "usart", + "peripheral" : peripheral, + "id" : instance, + "prefix" : "US", + "sr" : "CSR", + "target" : device.identifier + } + + +class Instance(Module): + def __init__(self, driver, instance): + self.driver = driver + self.instance = int(instance) + + def init(self, module): + module.name = str(self.instance) + module.description = "Instance {}".format(self.instance) + + def prepare(self, module, options): + module.depends(":platform:usart") + + module.add_option( + NumericOption( + name="buffer.tx", + description="Size of transmit buffer", + minimum=1, maximum=2 ** 16 - 2, + default=64)) + module.add_option( + NumericOption( + name="buffer.rx", + description="Size of receive buffer", + minimum=1, maximum=2 ** 16 - 2, + default=64)) + + return True + + def build(self, env): + env.substitutions = _get_properties(env, self.instance) + env.outbasepath = "modm/src/modm/platform/usart" + + env.template("../uart.hpp.in", "usart_{}.hpp".format(self.instance)) + env.template("../uart.cpp.in", "usart_{}.cpp".format(self.instance)) + + +def init(module): + module.name = ":platform:usart" + module.description = "Universal Synchronous Asynchronous Receiver Transmitter (USART)" + + +def prepare(module, options): + device = options[":target"] + if (not device.has_driver("usart:sam*")) or device.has_driver("sercom:sam"): + return False + + module.depends( + ":architecture:uart", + ":math:algorithm", + ":cmsis:device", + ":platform:gpio", + ":platform:clockgen") + + drivers = options[":target"].get_all_drivers("usart") + for driver in drivers: + for instance in driver["instance"]: + module.add_submodule(Instance(driver, instance)) + + return True + + +def build(env): + env.substitutions = _get_properties(env) + env.outbasepath = "modm/src/modm/platform/usart" + env.template("../uart_base.hpp.in", "usart_base.hpp") diff --git a/src/modm/platform/uart/samg/uart.cpp.in b/src/modm/platform/uart/samg/uart.cpp.in deleted file mode 100644 index d3462c082d..0000000000 --- a/src/modm/platform/uart/samg/uart.cpp.in +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2021, Jeff McBride - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#include "uart_{{ id }}.hpp" - -#include - -namespace -{ - static modm::atomic::Queue rxBuffer; - static modm::atomic::Queue txBuffer; -} - -MODM_ISR(FLEXCOM{{ id }}) -{ - using namespace modm::platform; - if(Uart{{ id }}::isReceiveReady()) { - uint8_t data = (uint8_t)USART{{ id }}->US_RHR; - Uart{{ id }}::read(data); - rxBuffer.push(data); - } - - if(Uart{{ id }}::isTransmitReady()) { - if(txBuffer.isEmpty()) { - USART{{ id }}->US_IDR = US_IDR_TXRDY; - } else { - USART{{ id }}->US_THR = txBuffer.get(); - txBuffer.pop(); - } - } -} - -namespace modm::platform -{ - -bool -Uart{{ id }}::read(uint8_t &dataOut) { - if(rxBuffer.isEmpty()) { - return false; - } else { - dataOut = rxBuffer.get(); - rxBuffer.pop(); - return true; - } -} - -std::size_t -Uart{{ id }}::read(uint8_t *data, std::size_t length) { - uint32_t i = 0; - for(; i < length; i++) { - if(rxBuffer.isEmpty()) { - return i; - } else { - data[i] = rxBuffer.get(); - rxBuffer.pop(); - } - } - return i; -} - -bool -Uart{{ id }}::write(uint8_t data) -{ - if(txBuffer.isEmpty() && isTransmitReady()) { - USART{{ id }}->US_THR = data; - } else { - if(!txBuffer.push(data)) { - return false; - } - // Enable tx interrupt - USART{{ id }}->US_IER = US_IER_TXRDY; - } - return true; -} - -std::size_t -Uart{{ id }}::write(const uint8_t *data, std::size_t length) -{ - uint32_t i = 0; - for(; i < length; i++) { - if(!write(data[i])) { - return i; - } - } - return i; -} - -bool -Uart{{ id }}::isWriteFinished() -{ - return txBuffer.isEmpty() && isTransmitReady(); -} - -void -Uart{{ id }}::flushWriteBuffer() -{ - while(!isWriteFinished()); -} - -void -Uart{{ id }}::setParity(Parity parity) -{ - USART{{ id }}->US_MR = (USART{{ id }}->US_MR & ~US_MR_PAR_Msk) | (uint32_t)parity; -} - -void -Uart{{ id }}::setWordLength(WordLength length) -{ - if(length == WordLength::Bit9) { - USART{{ id }}->US_MR |= US_MR_MODE9; - } else { - USART{{ id }}->US_MR &= ~US_MR_MODE9; - USART{{ id }}->US_MR = - (USART{{ id }}->US_MR & ~US_MR_CHRL_Msk) - | US_MR_CHRL((uint32_t)length); - } -} - -} // namespace modm::platform \ No newline at end of file diff --git a/src/modm/platform/uart/samg/uart.hpp.in b/src/modm/platform/uart/samg/uart.hpp.in deleted file mode 100644 index 93b394f596..0000000000 --- a/src/modm/platform/uart/samg/uart.hpp.in +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2021, Jeff McBride - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#pragma once - -#include "uart_base.hpp" -#include -#include -#include -#include - -namespace modm::platform -{ - -/** - * Universal synchronous asynchronous receiver transmitter (USART{{ id }}) - * - * @author Jeff McBride - * @ingroup modm_platform_uart modm_platform_uart_{{id}} - */ -class Uart{{ id }} : public UartBase, public modm::Uart -{ -public: - static constexpr size_t RxBufferSize = {{ options["buffer.rx"] }}; - static constexpr size_t TxBufferSize = {{ options["buffer.tx"] }}; - - template< class... Pins > - static void - connect() - { - using RxPin = GetPin_t; - using TxPin = GetPin_t; - static_assert( - !std::is_same_v, - "Rx and Tx cannot use the same pin!"); - using Flexcom = Peripherals::Flexcom<{{ id | int }}>; - using RxConnector = typename RxPin::template Connector; - using TxConnector = typename TxPin::template Connector; - RxConnector::connect(); - TxConnector::connect(); - } - - - template< class SystemClock, baudrate_t baudrate, percent_t tolerance=pct(1) > - static inline void - initialize( - Parity parity=Parity::Disabled, - WordLength length=WordLength::Bit8, - uint8_t irq_priority = 5) - { - ClockGen::enable(); - FLEXCOM{{ id }}->FLEXCOM_MR = FLEXCOM_MR_OPMODE_USART; - constexpr auto result = Prescaler::from_function( - SystemClock::Mck, - baudrate, - 1, - 65535, - [](uint32_t x) { return x * 8; } - ); - - USART{{ id }}->US_BRGR = result.index; - - // Use 8x oversampling (this affects baud rate generation) - USART{{ id }}->US_MR = US_MR_OVER; - - setParity(parity); - setWordLength(length); - - USART{{ id }}->US_CR = US_CR_RXEN | US_CR_TXEN; - - // Enable the IRQ - - NVIC_SetPriority(FLEXCOM{{ id }}_IRQn, irq_priority); - NVIC_EnableIRQ(FLEXCOM{{ id }}_IRQn); - } - - static bool read(uint8_t &dataOut); - - static std::size_t read(uint8_t *data, std::size_t length); - - static bool write(uint8_t data); - - static std::size_t write(const uint8_t *data, std::size_t length); - - static bool isWriteFinished(); - - static void flushWriteBuffer(); - - static void setParity(Parity parity); - - static void setWordLength(WordLength length); - - static inline bool isTransmitReady() { return USART{{ id }}->US_CSR & US_CSR_TXRDY; } - - static inline bool isReceiveReady() { return USART{{ id }}->US_CSR & US_CSR_RXRDY; } -}; - -} // namespace modm::platform diff --git a/src/modm/platform/uart/samg/uart_base.hpp b/src/modm/platform/uart/samg/uart_base.hpp deleted file mode 100644 index 3ec44a304c..0000000000 --- a/src/modm/platform/uart/samg/uart_base.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2021, Jeff McBride - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- -#pragma once - -#include "../device.hpp" - -namespace modm::platform -{ - -/// @ingroup modm_platform_uart -class UartBase -{ -public: - enum class Parity : uint32_t - { - Disabled = US_MR_PAR_NO, - Even = US_MR_PAR_EVEN, - Odd = US_MR_PAR_ODD, - Space = US_MR_PAR_SPACE, - Mark = US_MR_PAR_MARK, - MultiDrop = US_MR_PAR_MULTIDROP - }; - - enum class WordLength : uint32_t - { - Bit5 = 0, - Bit6, - Bit7, - Bit8, - Bit9 - }; -}; - -} // namespace modm::platform diff --git a/src/modm/platform/usb/sam/usb.hpp.in b/src/modm/platform/usb/sam/usb.hpp.in index 1201e1eb46..5db79081a8 100644 --- a/src/modm/platform/usb/sam/usb.hpp.in +++ b/src/modm/platform/usb/sam/usb.hpp.in @@ -28,7 +28,7 @@ public: static void initialize(uint8_t priority=3) { -%% if target["family"] in ["g"] +%% if target["family"] == "g5x" // only the USB device mode is supported for now static_assert(modm::Tolerance::isValueInTolerance(48_MHz, SystemClock::Usb, 0.25_pct), "The USB clock frequency must be within 0.25\% of 48MHz!"); @@ -42,18 +42,23 @@ public: ClockGen::enable(); NVIC_SetPriority(UDP_IRQn, priority); %% else - static_assert(SystemClock::Frequency == 48_MHz, "Usb must have a 48MHz clock!"); - PM->APBBMASK.reg |= PM_APBBMASK_USB; - PM->AHBMASK.reg |= PM_AHBMASK_USB; - GenericClockController::connect(ClockGenerator::System); + static_assert(SystemClock::Usb == 48_MHz, "Usb must have a 48MHz clock!"); + PeripheralClock::enable(); +%% if target["family"] == "d5x/e5x" + NVIC_SetPriority(USB_0_IRQn, priority); + NVIC_SetPriority(USB_1_IRQn, priority); + NVIC_SetPriority(USB_2_IRQn, priority); + NVIC_SetPriority(USB_3_IRQn, priority); +%% else NVIC_SetPriority(USB_IRQn, priority); +%% endif %% endif } // SAMG does not provide a connect method for USB. It's not defined in the SVD, // and the USB pins are fixed. Instead, calling Usb::initialize() switches the // IO pin function to the USB controller. -%% if target["family"] not in ["g"] +%% if target["family"] != "g5x" template static void connect() diff --git a/tools/openocd/modm/atmel_same54_xplained_pro.cfg b/tools/openocd/modm/atmel_same54_xplained_pro.cfg new file mode 100644 index 0000000000..3b3adccc7f --- /dev/null +++ b/tools/openocd/modm/atmel_same54_xplained_pro.cfg @@ -0,0 +1,12 @@ +# +# Atmel SAME54 Xplained Pro evaluation kit. +# https://www.microchip.com/en-us/development-tool/ATSAME54-XPRO +# + +source [find interface/cmsis-dap.cfg] + +# chip name +set CHIPNAME ATSAME54P20 + +source [find target/atsame5x.cfg] + diff --git a/tools/openocd/modm/atmel_samv71_xplained_ultra.cfg b/tools/openocd/modm/atmel_samv71_xplained_ultra.cfg new file mode 100644 index 0000000000..cb02483dc0 --- /dev/null +++ b/tools/openocd/modm/atmel_samv71_xplained_ultra.cfg @@ -0,0 +1,14 @@ +# +# Atmel SAMV71 Xplained Ultra evaluation kit. +# http://www.atmel.com/tools/ATSAMV71-XULT.aspx +# +# To connect using the EDBG chip on the dev kit over USB, you will +# first need to source [find interface/cmsis-dap.cfg] +# however, since this board also has a SWD+ETM connector, we don't +# automatically source that file here. + +source [find interface/cmsis-dap.cfg] + +set CHIPNAME samv71 + +source [find target/atsamv.cfg] diff --git a/tools/scripts/generate_hal_matrix.py b/tools/scripts/generate_hal_matrix.py index 498316d42d..d31d889993 100755 --- a/tools/scripts/generate_hal_matrix.py +++ b/tools/scripts/generate_hal_matrix.py @@ -41,7 +41,7 @@ def hal_get_modules(): short_id.naming_schema = "{platform}-{family}" elif target.platform == "sam": - short_id.naming_schema = "{platform}{family}{series}" + short_id.naming_schema = "{platform}{series}" short_id.set("platform", target.platform) # invalidate caches minimal_targets[short_id.string].append(target) @@ -106,6 +106,7 @@ def hal_get_modules(): "flash": "Internal Flash", "timer": "Timer", "i2c": "I2C", + "usart": "UART" } mname = remap.get(mname, mname.upper()) modules.add(mname) @@ -202,7 +203,11 @@ def hal_create_table(targets, platforms, common_table=False): {% endfor %} Peripheral {% for fam in families -%} +{% if fam[0] == "sam" -%} +{{ fam[1] | upper | replace("X", "x") | replace("/", "
") }} +{% else -%} {{ fam[1] | capitalize }} +{% endif -%} {% endfor %} {%- for per in pers | sort %} {{ per }} diff --git a/tools/scripts/generate_module_docs.py b/tools/scripts/generate_module_docs.py index d90a3631f6..c346bb4e47 100755 --- a/tools/scripts/generate_module_docs.py +++ b/tools/scripts/generate_module_docs.py @@ -160,7 +160,7 @@ def replace(text, key, content): return re.sub(r"# {0}.*?# /{0}".format(key), "# {0}\n{1}\n# /{0}".format(key, content), text, flags=re.DOTALL | re.MULTILINE) def url_name(name): - for c in ":., ({": name = name.replace(c, "-"); + for c in ":.,/ ({": name = name.replace(c, "-"); for c in "})": name = name.replace(c, ""); return name