From c148bf8bf8c43274bac5f7aa2ff77f5d1468e9bc Mon Sep 17 00:00:00 2001 From: Jeff McBride Date: Wed, 1 Sep 2021 18:14:05 -0700 Subject: [PATCH] Add support for SAMG family - Basic clock configuration - Gpio driver - BSP for samg55_xplained_pro - simply blinky example for that board --- .github/workflows/linux.yml | 6 +- README.md | 27 ++- examples/samg55_xplained_pro/blink/main.cpp | 13 ++ .../samg55_xplained_pro/blink/project.xml | 9 + ext/microchip/module.lb | 21 +- ext/microchip/sam | 2 +- ext/modm-devices | 2 +- repo.lb | 1 + src/modm/board/samg55_xplained_pro/board.hpp | 55 +++++ src/modm/board/samg55_xplained_pro/board.xml | 14 ++ src/modm/board/samg55_xplained_pro/module.lb | 28 +++ src/modm/platform/clock/samg/clockgen.cpp | 121 ++++++++++ src/modm/platform/clock/samg/clockgen.hpp | 112 ++++++++++ .../platform/clock/samg/clockgen_impl.hpp | 79 +++++++ src/modm/platform/clock/samg/module.lb | 30 +++ src/modm/platform/clock/systick/module.lb | 3 +- src/modm/platform/core/sam/module.lb | 3 +- .../platform/core/sam/startup_platform.c.in | 2 + src/modm/platform/gpio/sam/config.hpp.in | 2 + src/modm/platform/gpio/sam/enable.cpp.in | 38 ++++ src/modm/platform/gpio/sam/module.lb | 33 ++- .../platform/gpio/sam/{pin.hpp => pin.hpp.in} | 211 ++++++++++++++++-- src/modm/platform/gpio/sam/unused.hpp | 11 - test/modm/mock/module.lb | 5 +- 24 files changed, 777 insertions(+), 51 deletions(-) create mode 100644 examples/samg55_xplained_pro/blink/main.cpp create mode 100644 examples/samg55_xplained_pro/blink/project.xml create mode 100644 src/modm/board/samg55_xplained_pro/board.hpp create mode 100644 src/modm/board/samg55_xplained_pro/board.xml create mode 100644 src/modm/board/samg55_xplained_pro/module.lb create mode 100644 src/modm/platform/clock/samg/clockgen.cpp create mode 100644 src/modm/platform/clock/samg/clockgen.hpp create mode 100644 src/modm/platform/clock/samg/clockgen_impl.hpp create mode 100644 src/modm/platform/clock/samg/module.lb create mode 100644 src/modm/platform/gpio/sam/enable.cpp.in rename src/modm/platform/gpio/sam/{pin.hpp => pin.hpp.in} (65%) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 6722d31c22..57659fb3dc 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -68,10 +68,14 @@ jobs: if: always() run: | (cd examples && ../tools/scripts/examples_compile.py generic) - - name: Examples SAM Devices + - name: Examples SAMD Devices if: always() run: | (cd examples && ../tools/scripts/examples_compile.py samd) + - name: Examples SAMG Devices + if: always() + run: | + (cd examples && ../tools/scripts/examples_compile.py samg55_xplained_pro) - name: Execute Python Scripts if: always() run: | diff --git a/README.md b/README.md index 81fca96f93..bb861836d7 100644 --- a/README.md +++ b/README.md @@ -74,10 +74,10 @@ git clone --recurse-submodules --jobs 8 https://github.com/modm-io/modm.git ## Microcontrollers -modm can create a HAL for 3172 devices of these vendors: +modm can create a HAL for 3175 devices of these vendors: - STMicroelectronics STM32: 2621 devices. -- Microchip SAM: 163 devices. +- Microchip SAM: 166 devices. - Microchip AVR: 388 devices. Here is a table with all device families and the peripheral drivers they support: @@ -96,7 +96,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. STM32 -SAM +SAM AT Peripheral @@ -113,6 +113,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. L1 L4 D21 +G 90 Mega Tiny @@ -132,6 +133,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ○ ○ +○ ✅ ✅ @@ -149,6 +151,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✗ ✅ ✗ +✗ ○ ○ ✗ @@ -167,6 +170,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ○ ✅ ○ +✗ ○ ○ ○ @@ -186,6 +190,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ○ ✗ +✗ ○ ✗ @@ -206,6 +211,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✗ ✗ ✗ +✗ Ethernet ✗ @@ -224,6 +230,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✗ ✗ ✗ +✗ External Interrupt ✅ @@ -242,6 +249,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ✅ +✅ External Memory ✗ @@ -260,6 +268,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✗ ✗ ✗ +✗ GPIO ✅ @@ -278,6 +287,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ✅ +✅ I2C ✅ @@ -293,6 +303,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ○ +○ ✅ ✅ ✅ @@ -314,6 +325,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✗ ✗ ✗ +✗ Random Generator ✗ @@ -332,6 +344,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✗ ✗ ✗ +✗ SPI ✅ @@ -347,6 +360,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ○ +○ ✅ ✅ ✅ @@ -368,6 +382,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✗ ✗ ✗ +✗ Timer ✅ @@ -386,6 +401,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ○ ○ ○ +○ UART ✅ @@ -401,6 +417,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ✅ +○ ✅ ✅ ○ @@ -422,6 +439,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✗ ✗ ✗ +✗ USB ✅ @@ -440,6 +458,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✗ ✗ ✗ +✗ @@ -523,9 +542,11 @@ We have out-of-box support for many development boards including documentation. Raspberry Pi SAMD21-MINI +SAMG55-XPLAINED-PRO STM32-F4VE STM32F030-DEMO Smart Response XE + diff --git a/examples/samg55_xplained_pro/blink/main.cpp b/examples/samg55_xplained_pro/blink/main.cpp new file mode 100644 index 0000000000..c6f374328c --- /dev/null +++ b/examples/samg55_xplained_pro/blink/main.cpp @@ -0,0 +1,13 @@ +#include "modm/board.hpp" + +using namespace modm::platform; + +int main() { + Board::initialize(); + + while(true) { + Board::Led::toggle(); + modm::delay_ms(500); + } + +} \ No newline at end of file diff --git a/examples/samg55_xplained_pro/blink/project.xml b/examples/samg55_xplained_pro/blink/project.xml new file mode 100644 index 0000000000..15d63cd482 --- /dev/null +++ b/examples/samg55_xplained_pro/blink/project.xml @@ -0,0 +1,9 @@ + + modm:samg55-xplained-pro + + + + + modm:build:scons + + \ No newline at end of file diff --git a/ext/microchip/module.lb b/ext/microchip/module.lb index 026f0a9cf9..a875db6af9 100644 --- a/ext/microchip/module.lb +++ b/ext/microchip/module.lb @@ -28,26 +28,35 @@ def prepare(module, options): pp = {} def validate(env): device = env[":target"] - name = device.partname.split("-")[0] + # 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"]]), + ] - define = "__{}__".format(name.upper()) + device_define = None family_file = None + device_header = None for famfile in Path(localpath("sam")).glob("**/sam.h"): content = famfile.read_text(encoding="utf-8", errors="replace") match = re.findall(r"defined\((?P__SAM.*?__)\)", content) - if match is not None and define in match: - family_file = famfile.relative_to(localpath(".")) + for n in names: + define = "__{}__".format(n.upper()) + if match is not None and define in match: + family_file = famfile.relative_to(localpath(".")) + device_header = "{}.h".format(n) + device_define = define + if family_file is None: raise ValidateException("No device define found for '{}'!".format(device.partname)) family_folder = family_file.parent - device_header = "{}.h".format(name) global pp pp = { - "define": define, + "define": device_define, "folder": family_folder, "device_header": device_header, } diff --git a/ext/microchip/sam b/ext/microchip/sam index bc94859670..4876890d97 160000 --- a/ext/microchip/sam +++ b/ext/microchip/sam @@ -1 +1 @@ -Subproject commit bc94859670c5e1a4076502345b24269ece59befd +Subproject commit 4876890d97a22deb9273bb91169e74364b66ee3b diff --git a/ext/modm-devices b/ext/modm-devices index 2b667e16f0..56f0ccaa90 160000 --- a/ext/modm-devices +++ b/ext/modm-devices @@ -1 +1 @@ -Subproject commit 2b667e16f0e519bfeb3412596b7dd0495a227c72 +Subproject commit 56f0ccaa902ad695958b01ce470fd008c0a3b18b diff --git a/repo.lb b/repo.lb index f66fd674e2..c9850f5a3d 100644 --- a/repo.lb +++ b/repo.lb @@ -87,6 +87,7 @@ class DevicesCache(dict): "stm32l0", "stm32l1", "stm32l4", "at90", "attiny", "atmega", "samd21", + "samg55", "hosted"] device_file_names = [dfn for dfn in device_file_names if any(s in dfn for s in supported)] # These files are ignored due to various issues diff --git a/src/modm/board/samg55_xplained_pro/board.hpp b/src/modm/board/samg55_xplained_pro/board.hpp new file mode 100644 index 0000000000..631644627f --- /dev/null +++ b/src/modm/board/samg55_xplained_pro/board.hpp @@ -0,0 +1,55 @@ +/* + * 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 + +/// @ingroup modm_board_samg55_xplained_pro +namespace Board +{ +using namespace modm::literals; +using namespace modm::platform; + +struct SystemClock +{ + static constexpr uint32_t PllAMult = 3662; + static constexpr uint32_t Frequency = PllAMult * SlowClkFreqHz; + static bool inline + enable() + { + ClockGen::setFlashLatency(); + ClockGen::updateCoreFrequency(); + ClockGen::enableExternal32Khz(false); + ClockGen::enablePllA(); + ClockGen::selectMasterClk(); + return true; + } +}; + +using Led = GpioA6; +using Button = GpioA2; + +inline void +initialize() +{ + // Turn off the watchdog + WDT->WDT_MR = (WDT_MR_WDDIS_Msk); + + SystemClock::enable(); + SysTickTimer::initialize(); + + Led::setOutput(modm::Gpio::Low); + + Button::setInput(); +} + +} // namespace Board + diff --git a/src/modm/board/samg55_xplained_pro/board.xml b/src/modm/board/samg55_xplained_pro/board.xml new file mode 100644 index 0000000000..a53d64f16a --- /dev/null +++ b/src/modm/board/samg55_xplained_pro/board.xml @@ -0,0 +1,14 @@ + + + + ../../../../repo.lb + + + + + + + + modm:board:samg55-xplained-pro + + diff --git a/src/modm/board/samg55_xplained_pro/module.lb b/src/modm/board/samg55_xplained_pro/module.lb new file mode 100644 index 0000000000..3b293c4151 --- /dev/null +++ b/src/modm/board/samg55_xplained_pro/module.lb @@ -0,0 +1,28 @@ +#!/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 init(module): + module.name = ":board:samg55-xplained-pro" + module.description = "Microchip SAMG55 Xplained Pro" + +def prepare(module, options): + if not options[":target"].partname == "samg55j19a-au": + return False + + module.depends(":platform:clockgen", ":platform:gpio", ":platform:core"); + return True + +def build(env): + env.outbasepath = "modm/src/modm/board" + env.copy('board.hpp') + + # TODO: openocd config? \ No newline at end of file diff --git a/src/modm/platform/clock/samg/clockgen.cpp b/src/modm/platform/clock/samg/clockgen.cpp new file mode 100644 index 0000000000..0cd1020221 --- /dev/null +++ b/src/modm/platform/clock/samg/clockgen.cpp @@ -0,0 +1,121 @@ +/** + * 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 "clockgen.hpp" +#include + + +// CMSIS Core compliance +uint32_t modm_fastdata SystemCoreClock(8000000); +modm_weak void SystemCoreClockUpdate() { /* Nothing to update */ } + +namespace modm::platform +{ +uint16_t modm_fastdata delay_fcpu_MHz = computeDelayMhz(8'000'000); +uint16_t modm_fastdata delay_ns_per_loop = computeDelayNsPerLoop(8'000'000); + + +static uint16_t divFromMasterClkPrescaler(MasterClkPrescaler pres) { + switch(pres) { + case MasterClkPrescaler::CLK_1: + return 1; + case MasterClkPrescaler::CLK_2: + return 2; + case MasterClkPrescaler::CLK_4: + return 4; + case MasterClkPrescaler::CLK_8: + return 8; + case MasterClkPrescaler::CLK_16: + return 16; + case MasterClkPrescaler::CLK_32: + return 32; + case MasterClkPrescaler::CLK_64: + return 64; + case MasterClkPrescaler::CLK_3: + return 3; + default: + return 1; + } +} + +void ClockGen::enableExternal32Khz(bool crystal_bypass) { + if(crystal_bypass) { + SUPC->SUPC_MR |= SUPC_MR_OSCBYPASS; + } + + SUPC->SUPC_CR = SUPC_CR_KEY_PASSWD | SUPC_CR_XTALSEL; +} + + + + +uint32_t ClockGen::masterClkFrequency() { + uint32_t mckr = PMC->PMC_MCKR; + MasterClkSource src = (MasterClkSource)((mckr & PMC_MCKR_CSS_Msk) >> PMC_MCKR_CSS_Pos); + MasterClkPrescaler pres = (MasterClkPrescaler)((mckr & PMC_MCKR_PRES_Msk) >> PMC_MCKR_PRES_Pos); + uint32_t div = divFromMasterClkPrescaler(pres); + switch(src) { + case MasterClkSource::SLOW_CLK: + return SlowClkFreqHz / div; + case MasterClkSource::MAIN_CLK: + return mainClkFrequency() / div; + case MasterClkSource::PLLA_CLK: + return pllAFrequency() / div; + case MasterClkSource::PLLB_CLK: + return pllBFrequency() / div; + default: + return 0; + } +} + +uint32_t ClockGen::mainClkFrequency() { + // NOTE: This does not support external main clk. To add this, we'll need + // a way for user supplied input frequency. For now, this assumes we're + // using the internal RC for main clock. + uint32_t moscrcf = (PMC->CKGR_MOR & CKGR_MOR_MOSCRCF_Msk) >> CKGR_MOR_MOSCRCF_Pos; + if(moscrcf == (uint32_t)MainInternalFreq::Rc8Mhz) { + return 8'000'000; + } else if(moscrcf == (uint32_t)MainInternalFreq::Rc16Mhz) { + return 16'000'000; + } else if(moscrcf == (uint32_t)MainInternalFreq::Rc24Mhz) { + return 24'000'000; + } else { + return 0; + } +} + +uint32_t ClockGen::pllAFrequency() { + uint32_t mul = ((PMC->CKGR_PLLAR & CKGR_PLLAR_MULA_Msk) >> CKGR_PLLAR_MULA_Pos) + 1; + uint32_t freq = SlowClkFreqHz * mul; + if(PMC->PMC_MCKR & PMC_MCKR_PLLADIV2) { + freq /= 2; + } + return freq; +} + +uint32_t ClockGen::pllBFrequency() { + uint32_t mul = ((PMC->CKGR_PLLBR & CKGR_PLLBR_MULB_Msk) >> CKGR_PLLBR_MULB_Pos) + 1; + uint32_t freq = SlowClkFreqHz * mul; + if(PMC->PMC_MCKR & PMC_MCKR_PLLBDIV2) { + freq /= 2; + } + return freq; +} + + +void ClockGen::setMainInternalFreq(MainInternalFreq freq) { + PMC->CKGR_MOR = + (PMC->CKGR_MOR & ~(CKGR_MOR_MOSCRCF_Msk)) | + CKGR_MOR_MOSCRCF((uint32_t)freq) | + CKGR_MOR_KEY_PASSWD; +} + +} // namespace modm::platform \ No newline at end of file diff --git a/src/modm/platform/clock/samg/clockgen.hpp b/src/modm/platform/clock/samg/clockgen.hpp new file mode 100644 index 0000000000..652754ca78 --- /dev/null +++ b/src/modm/platform/clock/samg/clockgen.hpp @@ -0,0 +1,112 @@ +/** + * 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 "../device.hpp" +#include + +extern uint32_t SystemCoreClock; + +namespace modm::platform { + +enum class 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, + PLLB_CLK = PMC_MCKR_CSS_PLLB_CLK_Val +}; + +enum class MasterClkPrescaler : uint32_t { + CLK_1 = 0, + CLK_2, + CLK_4, + CLK_8, + CLK_16, + CLK_32, + CLK_64, + CLK_3 +}; + +enum class MainInternalFreq : uint32_t { + Rc8Mhz = 0, + Rc16Mhz, + Rc24Mhz +}; + +static constexpr uint32_t SlowClkFreqHz = 32'768; + +/** + * Clock Generator Controller for SAMG5x devices + * + * This class provides control of the clock generation for the CPU and + * peripherals. + * + * There are two source clocks: a "slow" 32kHz clock which can be sourced from + * an external oscillator/crystal or internal RC oscillator, and a "main" clock which + * can be sourced from the 8/16/24MHz selectable internal RC oscillator, or from + * an external oscillator/crystal. + * + * In addition, there are two PLLs (PLLA, and PLLB), which run off of the slow + * clock. PLLB is used to provide a 48MHz USB clock (external slow crystal is + * required for USB), and PLLA can (optionally) be used to provide a faster + * clock for the CPU and peripherals. + * + * This class does not allow full control of all possible clock configurations. + * + * @author Jeff McBride + * @ingroup modm_platform_clockgen + */ +class ClockGen { +public: + /** + * Select external source for slow clock + * + * \param crystal_bypass + * Set true to enable use of external oscillator input on XIN, instead + * of crystal. + */ + static void enableExternal32Khz(bool crystal_bypass = false); + + template + static void enablePllA(uint32_t wait_cycles = 50); + + template + static void enablePllB(uint32_t wait_cycles = 50); + + template + static void selectMasterClk(); + + static void setMainInternalFreq(MainInternalFreq freq); + + template + static void setFlashLatency(); + + template + static void updateCoreFrequency(); + + /** Returns the configured frequency of the main clock */ + static uint32_t mainClkFrequency(); + + /** Returns the configured frequency of the master clock */ + static uint32_t masterClkFrequency(); + + /** Returns the configured frequency of PLL A output */ + static uint32_t pllAFrequency(); + + /** Returns the configured frequency of PLL B output */ + static uint32_t pllBFrequency(); +}; + +} // namespace modm::platform + +#include "clockgen_impl.hpp" \ No newline at end of file diff --git a/src/modm/platform/clock/samg/clockgen_impl.hpp b/src/modm/platform/clock/samg/clockgen_impl.hpp new file mode 100644 index 0000000000..9a62556b28 --- /dev/null +++ b/src/modm/platform/clock/samg/clockgen_impl.hpp @@ -0,0 +1,79 @@ +/* + * 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/. + */ +// ---------------------------------------------------------------------------- + +namespace modm::platform { + +template +void ClockGen::selectMasterClk() { + // Datasheet says when selecting PLL as source, write PRES first, otherwise + // write CSS first. + if(src == MasterClkSource::PLLA_CLK || src == MasterClkSource::PLLB_CLK) { + PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_PRES_Msk) | PMC_MCKR_PRES((uint32_t)pres); + while(!(PMC->PMC_SR & PMC_SR_MCKRDY)) {} + PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS((uint32_t)src); + while(!(PMC->PMC_SR & PMC_SR_MCKRDY)) {} + } else { + PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS((uint32_t)src); + 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)) {} + } +} + +template +void ClockGen::enablePllA(uint32_t wait_cycles) { + static_assert(multiplier > 8 && multiplier < 7502, "Valid PLL MUL range is 9-7501"); + PMC->CKGR_PLLAR = + CKGR_PLLAR_MULA(multiplier-1) | + CKGR_PLLAR_PLLACOUNT(wait_cycles) | + CKGR_PLLAR_PLLAEN(1); + // Wait for lock bit + while(!(PMC->PMC_SR & PMC_SR_LOCKA)) {} +} + +template +void ClockGen::enablePllB(uint32_t wait_cycles) { + static_assert(multiplier > 8 && multiplier < 7502, "Valid PLL MUL range is 9-7501"); + PMC->CKGR_PLLBR = + CKGR_PLLBR_MULB(multiplier-1) | + CKGR_PLLBR_PLLBCOUNT(wait_cycles) | + CKGR_PLLBR_PLLBEN(1); + // Wait for lock bit + while(!(PMC->PMC_SR & PMC_SR_LOCKB)) {} +} + +template +void ClockGen::updateCoreFrequency() { + SystemCoreClock = Core_Hz; + delay_fcpu_MHz = computeDelayMhz(Core_Hz); + delay_ns_per_loop = computeDelayNsPerLoop(Core_Hz); +} + +template +void ClockGen::setFlashLatency() { + uint32_t fws = 0; + if(Core_Hz <= 20'000'000) { + fws = 0; + } else if(Core_Hz <= 40'000'000) { + fws = 1; + } else if (Core_Hz <= 60'000'000) { + fws = 2; + } else if (Core_Hz <= 80'000'000) { + fws = 3; + } else if (Core_Hz <= 100'000'000) { + fws = 4; + } else { + fws = 5; + } + EFC->EEFC_FMR = (EFC->EEFC_FMR & ~EEFC_FMR_FWS_Msk) | EEFC_FMR_FWS(fws); +} + +} // namespace modm::platform diff --git a/src/modm/platform/clock/samg/module.lb b/src/modm/platform/clock/samg/module.lb new file mode 100644 index 0000000000..8ba8046ee3 --- /dev/null +++ b/src/modm/platform/clock/samg/module.lb @@ -0,0 +1,30 @@ +#!/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 init(module): + module.name = ":platform:clockgen" + module.description = "Clock Generator (CKGR)" + +def prepare(module, options): + if not options[":target"].has_driver("pmc:samg*"): + return False + + module.depends(":cmsis:device", ":platform:clock") + return True + +def build(env): + device = env[":target"] + + env.outbasepath = "modm/src/modm/platform/clock" + env.copy("clockgen.hpp") + env.copy("clockgen_impl.hpp") + env.copy("clockgen.cpp") diff --git a/src/modm/platform/clock/systick/module.lb b/src/modm/platform/clock/systick/module.lb index 131edd9e56..e57815ce4a 100644 --- a/src/modm/platform/clock/systick/module.lb +++ b/src/modm/platform/clock/systick/module.lb @@ -41,7 +41,8 @@ def build(env): # SysTick clock prescaler is dynamically chosen as /1 or /8 div = 8 # SAMD: Prescaler not implemented - if target.platform == "sam": div = 1; + if target.platform == "sam" and target.family in ["d"]: + div = 1 # H742/43: Prescaler not implemented in revY elif target.family == "h7" and target.name in ["42", "43"] and target.revision == "y": div = 1 diff --git a/src/modm/platform/core/sam/module.lb b/src/modm/platform/core/sam/module.lb index 4149c519a7..4951a92f9e 100644 --- a/src/modm/platform/core/sam/module.lb +++ b/src/modm/platform/core/sam/module.lb @@ -35,7 +35,8 @@ def build(env): # delay code that must be tuned for each family # (cycles per loop, setup cost in loops, us shift) tuning = { - "d": (3, 4, 5), # CM0 tested on D21 in RAM + "d": (3, 4, 5), # CM0 tested on D21 in RAM + "g": (6, 4, 5) # G55 }.get(target.family, (4, 4, 5)) env.substitutions.update({ diff --git a/src/modm/platform/core/sam/startup_platform.c.in b/src/modm/platform/core/sam/startup_platform.c.in index 6bd7663bfb..5c99d8d829 100644 --- a/src/modm/platform/core/sam/startup_platform.c.in +++ b/src/modm/platform/core/sam/startup_platform.c.in @@ -48,6 +48,8 @@ __modm_initialize_platform(void) %% elif target.family == "l" %% if target.series == "21" %% endif + +%% elif target.family == "g" %% endif } diff --git a/src/modm/platform/gpio/sam/config.hpp.in b/src/modm/platform/gpio/sam/config.hpp.in index ca55ca328e..0e4cd32505 100644 --- a/src/modm/platform/gpio/sam/config.hpp.in +++ b/src/modm/platform/gpio/sam/config.hpp.in @@ -106,11 +106,13 @@ struct {{ gpio["port"] ~ gpio["pin"] }} }; %% endfor +%% if gpio["signal"] | length > 0 using Signals = std::tuple< %% for signal in gpio["signal"] {{ signal["full_name"] }}{{ ">;" if loop.last else ","}} %% endfor +%% endif }; } // namespace gpio using Gpio{{ gpio["port"] ~ gpio["pin"] }} = Gpio; diff --git a/src/modm/platform/gpio/sam/enable.cpp.in b/src/modm/platform/gpio/sam/enable.cpp.in new file mode 100644 index 0000000000..5ddf333994 --- /dev/null +++ b/src/modm/platform/gpio/sam/enable.cpp.in @@ -0,0 +1,38 @@ +/* + * 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 "../device.hpp" +#include + +void +modm_gpio_enable(void) +{ + +%% if target["family"] in ["g"] + + PMC->PMC_PCER0 = +%% for port in options["enable_ports"] +%% if not loop.last + (1<PIO_OWER = 0xFFFFFFFF; +%% endfor + +%%endif +} + +MODM_HARDWARE_INIT_ORDER(modm_gpio_enable, 80); diff --git a/src/modm/platform/gpio/sam/module.lb b/src/modm/platform/gpio/sam/module.lb index 35d5650f0f..9289c0a04b 100644 --- a/src/modm/platform/gpio/sam/module.lb +++ b/src/modm/platform/gpio/sam/module.lb @@ -15,6 +15,19 @@ import copy from collections import defaultdict, OrderedDict +def port_ranges(gpios): + ports = {p: (32, 0) for p in set(p["port"] for p in gpios)} + for gpio in gpios: + pin = int(gpio["pin"]) + pmin, pmax = ports[gpio["port"]] + ports[gpio["port"]] = (min(pin, pmin), max(pin, pmax)) + + ports = [{"name": k.upper(), "start": v[0], "width": v[1] - v[0] + 1} for k,v in ports.items()] + ports.sort(key=lambda p: p["name"]) + return ports + +bprops = {} + def init(module): module.name = ":platform:gpio" module.description = "General Purpose I/O (GPIO)" @@ -24,6 +37,16 @@ def prepare(module, options): if not device.has_driver("gpio:sam*"): return False + bprops["ranges"] = port_ranges(device.get_driver("gpio")["gpio"]) + bprops["ports"] = OrderedDict([(k, i) for i, k in enumerate([p["name"] for p in bprops["ranges"]])]) + + module.add_set_option( + EnumerationOption( + name="enable_ports", + description="Enable clock for these GPIO ports during startup", + enumeration=list(bprops["ports"].keys())), + default=list(bprops["ports"].keys())) + module.depends( ":architecture:gpio", ":cmsis:device", @@ -42,12 +65,16 @@ def build(env): peripherals = OrderedDict() for gpio in driver["gpio"]: signals = [] - for signal in gpio["signal"]: + for signal in gpio.get("signal", []): signal.update({ "peripheral": signal["driver"].capitalize(), "function": signal["function"].capitalize(), "name": signal["name"].capitalize(), }) + # SAMG devices have X1 peripheral connection, which is "extra function", + # but it is not configured by the normal alternate IO function. Skip these. + if signal['function'] == 'X1': + continue # This is a hack for L variant devices, which have a Ac without instance *and* # a Ac with instance 1. Why??? if (device.identifier.get("variant") == "l" @@ -80,5 +107,7 @@ def build(env): bprops["gpios"] = driver["gpio"] env.template("config.hpp.in") env.copy("connector.hpp") - env.copy("pin.hpp") + if len(env["enable_ports"]): + env.template("enable.cpp.in") + env.template("pin.hpp.in") env.copy("unused.hpp") diff --git a/src/modm/platform/gpio/sam/pin.hpp b/src/modm/platform/gpio/sam/pin.hpp.in similarity index 65% rename from src/modm/platform/gpio/sam/pin.hpp rename to src/modm/platform/gpio/sam/pin.hpp.in index cd9af440a7..73fad6d9f1 100644 --- a/src/modm/platform/gpio/sam/pin.hpp +++ b/src/modm/platform/gpio/sam/pin.hpp.in @@ -28,24 +28,6 @@ enum class InputType PullDown = 0x2, ///< pull-down on input }; -enum class OutputType -{ - PushPull = 0x0, ///< push-pull on output - OpenDrain = 0x1, ///< open-drain on output -}; - -enum class OutputSpeed -{ - Low = 0, - Medium = 0x1, - High = 0x2, - VeryHigh = 0x3, ///< 30 pF (80 MHz Output max speed on 15 pF) - MHz2 = Low, - MHz25 = Medium, - MHz50 = High, - MHz100 = VeryHigh, -}; - enum class PeripheralPin { BitBang, @@ -104,6 +86,176 @@ struct OneOfSignals static constexpr bool value = ((std::is_same_v) | ...); }; +%% if target["family"] in ["g"] + +template +struct PinMuxMixin +{ + inline static void set(uint8_t){}; +}; + +template +struct PinMuxMixin +{ + inline static void + set(uint8_t cfg) + { + uint8_t bit0 = cfg & 1; + uint8_t bit1 = (cfg & 2) >> 1; + if constexpr (PinConfig::port == PortName::A) + { + PIOA->PIO_ABCDSR[0] = (PIOA->PIO_ABCDSR[0] & ~(1<PIO_ABCDSR[1] = (PIOA->PIO_ABCDSR[1] & ~(1<PIO_ABCDSR[0] = (PIOB->PIO_ABCDSR[0] & ~(1<PIO_ABCDSR[1] = (PIOB->PIO_ABCDSR[1] & ~(1<::set(cfg); + } +}; + +template +class GpioSet : protected PinMuxMixin +{ +protected: + using PinMux = PinMuxMixin; + + static constexpr uint32_t + mask(PortName port) + { + return (((PinConfigs::port == port) ? 1u << PinConfigs::pin : 0u) | ...); + } + + template + inline static constexpr volatile uint32_t* + getPortReg(size_t offset) + { + if constexpr (port == PortName::A) { + return (uint32_t*)((uint8_t*)PIOA + offset); + } + if constexpr (port == PortName::B) { + return (uint32_t*)((uint8_t*)PIOB + offset); + } + } + + inline static constexpr void + setPortReg(size_t offset) + { + if constexpr (mask(PortName::A) != 0) { *getPortReg(offset) = mask(PortName::A); } + if constexpr (mask(PortName::B) != 0) { *getPortReg(offset) = mask(PortName::B); } + } + + template + inline static constexpr uint32_t + readPortReg(size_t offset) + { + if constexpr (port == PortName::A) + { + static_assert(mask(PortName::A) != 0, + "Trying to read port which is not in the GpioSet!"); + return *getPortReg(offset) & mask(PortName::A); + } else if constexpr (port == PortName::B) + { + static_assert(mask(PortName::B) != 0, + "Trying to read port which is not in the GpioSet!"); + return *getPortReg(offset) & mask(PortName::B); + } + } + +public: + inline static void + setOutput() + { + setPortReg(PIO_OER_OFFSET); + } + + inline static void + setOutput(bool status) + { + setOutput(); + set(status); + } + + static void + setInput() + { + setPortReg(PIO_ODR_OFFSET); // Disable output driver + } + + static void + setInput(InputType type) + { + configure(type); + setInput(); + } + + static void + configure(InputType type) + { + if(type == InputType::Floating) { + setPortReg(PIO_PPDDR_OFFSET); // disable pull down + setPortReg(PIO_PUDR_OFFSET); // disable pull up + } else if (type == InputType::PullDown) { + setPortReg(PIO_PPDER_OFFSET); // Enable pull down + setPortReg(PIO_PUDR_OFFSET); // disable pull up + } else { + setPortReg(PIO_PPDDR_OFFSET); // Disable pull down + setPortReg(PIO_PUER_OFFSET); // Enable pull up + } + } + + static void + setAnalogInput() + {} + + static void + set() + { + setPortReg(PIO_SODR_OFFSET); + } + + static void + set(bool status) + { + if (status) + set(); + else + reset(); + } + + static void + reset() + { + setPortReg(PIO_CODR_OFFSET); + } + + static void + toggle() + { + if constexpr (mask(PortName::A) != 0) { + volatile uint32_t *reg = getPortReg(PIO_ODSR_OFFSET); + uint32_t tmp = *reg; + tmp ^= mask(PortName::A); + *reg = tmp; + } + if constexpr (mask(PortName::B) != 0) { + volatile uint32_t *reg = getPortReg(PIO_ODSR_OFFSET); + uint32_t tmp = *reg; + tmp ^= mask(PortName::B); + *reg = tmp; + } + } + + static void + disconnect() + { + setInput(InputType::Floating); + } +}; + +%% else + template struct PinCfgMixin { @@ -189,10 +341,6 @@ class GpioSet : protected PinCfgMixin set(status); } - static void setOutput(OutputType, OutputSpeed) { setOutput(); } - - static void configure(OutputType, OutputSpeed = OutputSpeed::MHz50) {} - static void setInput() { @@ -250,6 +398,8 @@ class GpioSet : protected PinCfgMixin setInput(InputType::Floating); } }; +%% endif + template class Gpio : public GpioSet, public ::modm::GpioIO @@ -259,7 +409,6 @@ class Gpio : public GpioSet, public ::modm::GpioIO public: // For backwards compability with bitbang API using InputType = ::modm::platform::InputType; - using OutputType = ::modm::platform::OutputType; template struct As; @@ -273,7 +422,11 @@ class Gpio : public GpioSet, public ::modm::GpioIO inline static bool read() { +%% if target["family"] in ["g"] + return Base::readPortReg(PIO_PDSR_OFFSET); +%% else return Base::readPortReg(PORT_IN_OFFSET); +%% endif } inline static void @@ -332,6 +485,16 @@ struct Gpio::As : public Gpio inline static void connect() { +%% if target["family"] in ["g"] + Pio* PIOBase; + if constexpr (PinConfig::port == PortName::A) { + PIOBase = PIOA; + } else { + PIOBase = PIOB; + } + PIOBase->PIO_PDR = (1<::As : public Gpio PORT->Group[uint32_t(PinConfig::port)].PMUX[PinConfig::pin >> 1].bit.PMUXE = uint32_t(PinSignal::function); } +%% endif } + }; }; diff --git a/src/modm/platform/gpio/sam/unused.hpp b/src/modm/platform/gpio/sam/unused.hpp index 64011c5c55..47660cb182 100644 --- a/src/modm/platform/gpio/sam/unused.hpp +++ b/src/modm/platform/gpio/sam/unused.hpp @@ -34,13 +34,6 @@ class GpioUnused : public ::modm::GpioIO static constexpr uint8_t pin = uint8_t(-1); static constexpr uint32_t mask = 0; -protected: - /// @cond - static void setAlternateFunction(uint8_t) {} - static void setAnalogInput() {} - /// @endcond - -public: // GpioOutput // start documentation inherited static void setOutput() {} @@ -51,16 +44,12 @@ class GpioUnused : public ::modm::GpioIO static void toggle() {} static bool isSet() { return false; } // stop documentation inherited - static void configure(OutputType, OutputSpeed = OutputSpeed::MHz50) {} - static void setOutput(OutputType, OutputSpeed = OutputSpeed::MHz50) {} // GpioInput // start documentation inherited static void setInput() {} static bool read() { return false; } // end documentation inherited - static void configure(InputType) {} - static void setInput(InputType) {} // GpioIO // start documentation inherited diff --git a/test/modm/mock/module.lb b/test/modm/mock/module.lb index adc64c8ee9..b5a7fc55ca 100644 --- a/test/modm/mock/module.lb +++ b/test/modm/mock/module.lb @@ -99,6 +99,10 @@ class LogicAnalyzer(Module): if not core or "m0" in core["type"] or "m7" in core["type"]: return False + # gpio_sampler only supports stm32 + if options[":target"].identifier["platform"] != "stm32": + return False + module.depends(":stdc++", ":driver:gpio_sampler", ":debug") return True @@ -106,7 +110,6 @@ class LogicAnalyzer(Module): env.outbasepath = "modm-test/src/modm-test/mock" env.copy("logic_analyzer.hpp") - def init(module): module.name = ":mock"