diff --git a/.circleci/config.yml b/.circleci/config.yml
index 25330c979a..5bfd6c3a3a 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -51,6 +51,10 @@ jobs:
name: Generic Examples
command: |
(cd examples && ../tools/scripts/examples_compile.py generic)
+ - run:
+ name: Examples SAM Devices
+ command: |
+ (cd examples && ../tools/scripts/examples_compile.py samd)
- run:
name: Execute Python Scripts
command: |
@@ -157,6 +161,23 @@ jobs:
path: test/all/log
destination: log
+ samd-compile-all:
+ docker:
+ - image: modm/modm-build:latest
+ steps:
+ - checkout
+ - run:
+ name: Checkout code and update modm tools
+ command: |
+ (git submodule sync && git submodule update --init --jobs 8) & pip3 install --upgrade --upgrade-strategy=eager modm & wait
+ - run:
+ name: Compile HAL for all SAMD
+ command: |
+ (cd test/all && python3 run_all.py samd)
+ - store_artifacts:
+ path: test/all/log
+ destination: log
+
stm32f0-compile-all:
docker:
- image: modm/modm-build:latest
@@ -437,6 +458,12 @@ workflows:
filters:
branches:
ignore: /^develop.*/
+ - samd-compile-all:
+ requires:
+ - unittests-linux-generic
+ filters:
+ branches:
+ ignore: /^develop.*/
- stm32f0-compile-all:
requires:
- unittests-linux-generic
@@ -514,6 +541,7 @@ workflows:
ignore: /^develop.*/
requires:
- avr-compile-all
+ - samd-compile-all
- stm32f0-compile-all
- stm32f1-compile-all
- stm32f2-compile-all
diff --git a/.gitmodules b/.gitmodules
index d604e01df7..4693242c2b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -25,3 +25,6 @@
[submodule "ext/adamgreen/crashcatcher"]
path = ext/adamgreen/crashcatcher
url = https://github.com/modm-ext/CrashCatcher-partial.git
+[submodule "ext/microchip/sam"]
+ path = ext/microchip/sam
+ url = https://github.com/modm-io/cmsis-header-sam.git
diff --git a/README.md b/README.md
index 5d31921f71..c4eca83206 100644
--- a/README.md
+++ b/README.md
@@ -70,7 +70,8 @@ git clone --recurse-submodules https://github.com/modm-io/modm.git
## Targets
-modm can generate code for 530 AVR and 1959
+modm can generate code for 530 AVR,
+163 SAM and 1959
STM32 devices, however, there are different levels of support and testing.
@@ -81,6 +82,7 @@ STM32 devices, however, there are different levels of support and testing.
| STM32F2 | ★★★★ | STM32F3 | ★★★★★ | STM32F4 | ★★★★★ |
| STM32F7 | ★★★★ | STM32L1 | ★★★★ | STM32L4 | ★★★★ |
| STM32L4+ | ★★★★ | STM32G0 | ★★★★ | STM32G4 | ★★★★ |
+| SAMD21 | ★ | | | | |
@@ -130,31 +132,33 @@ documentation.
DISCO-L152RC |
DISCO-L476VG |
+FEATHER-M0 |
MEGA-2560-PRO |
MINI-F401 |
MINI-F411 |
-NUCLEO-F031K6 |
+NUCLEO-F031K6 |
NUCLEO-F042K6 |
NUCLEO-F103RB |
NUCLEO-F303K8 |
-NUCLEO-F303RE |
+NUCLEO-F303RE |
NUCLEO-F401RE |
NUCLEO-F411RE |
NUCLEO-F429ZI |
-NUCLEO-F446RE |
+NUCLEO-F446RE |
NUCLEO-F746ZG |
NUCLEO-G071RB |
NUCLEO-G474RE |
-NUCLEO-L152RE |
+NUCLEO-L152RE |
NUCLEO-L432KC |
NUCLEO-L476RG |
OLIMEXINO-STM32 |
-STM32F030F4P6-DEMO |
+SAMD21-MINI |
+STM32F030F4P6-DEMO |
diff --git a/docs/src/guide/installation.md b/docs/src/guide/installation.md
index b66c58be6f..aa75820db1 100644
--- a/docs/src/guide/installation.md
+++ b/docs/src/guide/installation.md
@@ -49,6 +49,10 @@ well:
brew install arm-gcc-bin
brew install openocd --HEAD
+To program Microchip SAM devices via the bootloader, install the `bossac` tool:
+
+ brew cask install bossa
+
We recommend the use of a graphical frontend for GDB called [gdbgui][]:
pip3 install gdbgui
diff --git a/examples/samd/blink/main.cpp b/examples/samd/blink/main.cpp
new file mode 100644
index 0000000000..7472a0a5ec
--- /dev/null
+++ b/examples/samd/blink/main.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019, Ethan Slattery
+ *
+ * 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;
+using namespace std::chrono_literals;
+
+int
+main()
+{
+ Board::initialize();
+ LedRx::set();
+
+ while (1)
+ {
+ LedTx::toggle();
+ LedRx::toggle();
+ modm::delay(500ms);
+
+#ifdef MODM_BOARD_HAS_LOGGER
+ static uint32_t counter(0);
+ MODM_LOG_INFO << "Loop counter: " << (counter++) << modm::endl;
+#endif
+ }
+
+ return 0;
+}
diff --git a/examples/samd/blink/project.xml b/examples/samd/blink/project.xml
new file mode 100644
index 0000000000..0c80983a8b
--- /dev/null
+++ b/examples/samd/blink/project.xml
@@ -0,0 +1,9 @@
+
+ modm:samd21-mini
+
+
+
+
+ modm:build:scons
+
+
diff --git a/ext/microchip/device.hpp.in b/ext/microchip/device.hpp.in
new file mode 100644
index 0000000000..5106855867
--- /dev/null
+++ b/ext/microchip/device.hpp.in
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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/.
+ */
+// ----------------------------------------------------------------------------
+
+#ifndef MODM_DEVICE_HPP
+#define MODM_DEVICE_HPP
+
+#define DONT_USE_CMSIS_INIT 1
+%% for define in defines
+#define {{ define }} 1
+%% endfor
+
+#include
+// Defines for example the modm_always_inline macro
+#include
+
+// Include external device headers:
+%% for header in headers
+#include <{{ header }}>
+%% endfor
+
+#endif // MODM_DEVICE_HPP
diff --git a/ext/microchip/module.lb b/ext/microchip/module.lb
new file mode 100644
index 0000000000..026f0a9cf9
--- /dev/null
+++ b/ext/microchip/module.lb
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2019, 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/.
+# -----------------------------------------------------------------------------
+
+import re
+from pathlib import Path
+
+# -----------------------------------------------------------------------------
+def init(module):
+ module.name = ":cmsis:device"
+
+def prepare(module, options):
+ device = options[":target"]
+ if device.identifier["platform"] != "sam":
+ return False
+
+ module.depends(":cmsis:core")
+ return True
+
+pp = {}
+def validate(env):
+ device = env[":target"]
+ name = device.partname.split("-")[0]
+
+
+ define = "__{}__".format(name.upper())
+ family_file = 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("."))
+
+ 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,
+ "folder": family_folder,
+ "device_header": device_header,
+ }
+
+def build(env):
+ global pp
+ env.collect(":build:path.include", "modm/ext")
+ env.collect(":build:path.include", "modm/ext/cmsis/device")
+
+ env.outbasepath = "modm/ext/cmsis/device"
+ files = ["sam.h", "component-version.h", "pio", "instance", "component", pp["device_header"]]
+ for file in files:
+ env.copy(localpath(pp["folder"], file), file)
+
+ env.substitutions = {
+ "headers": [pp["device_header"]],
+ "defines": [pp["define"]],
+ }
+ env.outbasepath = "modm/src/modm/platform"
+ env.template("device.hpp.in")
diff --git a/ext/microchip/sam b/ext/microchip/sam
new file mode 160000
index 0000000000..9f08123fc9
--- /dev/null
+++ b/ext/microchip/sam
@@ -0,0 +1 @@
+Subproject commit 9f08123fc9c504cfe99928fb8f125e149c815e80
diff --git a/repo.lb b/repo.lb
index db99cf8aa3..4810f89dfd 100644
--- a/repo.lb
+++ b/repo.lb
@@ -85,6 +85,7 @@ class DevicesCache(dict):
"stm32g0", "stm32g4",
"stm32l1", "stm32l4",
"at90", "attiny", "atmega",
+ "samd21",
"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
new file mode 100644
index 0000000000..277da20516
--- /dev/null
+++ b/src/modm/board/feather_m0/board.hpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2016-2017, Sascha Schade
+ * Copyright (c) 2017-2018, 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/.
+ */
+// ----------------------------------------------------------------------------
+
+#pragma once
+
+#include
+#include
+
+using namespace modm::platform;
+
+
+/// @ingroup modm_board_feather_m0
+namespace Board
+{
+ using namespace modm::literals;
+
+/// 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 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 bool inline
+ enable()
+ {
+ // GenericClockController::enableExternalCrystal(Frequency);
+
+ // switch system clock to PLL output
+ // GenericClockController::enableSystemClock(ClockControl::SystemClockSource::Pll);
+
+ // update frequencies for busy-wait delay functions
+ // GenericClockController::updateCoreFrequency();
+
+ return true;
+ }
+};
+
+// User LED (inverted, because connected to 3V3)
+// using LedRed = GpioOutputA17;
+// using Leds = SoftwareGpioPort< LedRed >;
+
+// using Button = GpioUnused;
+
+inline void
+initialize()
+{
+ SystemClock::enable();
+ SysTickTimer::initialize();
+
+ // LedGreen::setOutput(modm::Gpio::Low);
+}
+
+} // Board namespace
diff --git a/src/modm/board/feather_m0/board.xml b/src/modm/board/feather_m0/board.xml
new file mode 100644
index 0000000000..c816c6f149
--- /dev/null
+++ b/src/modm/board/feather_m0/board.xml
@@ -0,0 +1,14 @@
+
+
+
+ ../../../../repo.lb
+
+
+
+
+
+
+
+ modm:board:feather-m0
+
+
diff --git a/src/modm/board/feather_m0/module.lb b/src/modm/board/feather_m0/module.lb
new file mode 100644
index 0000000000..ad1fa5d59c
--- /dev/null
+++ b/src/modm/board/feather_m0/module.lb
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2016-2018, Niklas Hauser
+# Copyright (c) 2017, Fabian Greif
+#
+# 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:feather-m0"
+ module.description = """\
+# Adafruit Feather-M0
+
+At the Feather M0's heart is an ATSAMD21G18 ARM Cortex M0 processor,
+clocked at 48 MHz and at 3.3V logic, the same one used in the new Arduino Zero.
+This chip has a whopping 256K of FLASH (8x more than the Atmega328 or 32u4)
+and 32K of RAM (16x as much)! This chip comes with built in USB so it has
+USB-to-Serial program & debug capability built in with no need for an FTDI-like chip.
+
+https://www.adafruit.com/product/2772
+"""
+
+def prepare(module, options):
+ if not options[":target"].partname.startswith("samd21g18a"):
+ return False
+
+ module.depends(":platform:gclk", ":platform:core", ":platform:clock")
+ return True
+
+def build(env):
+ env.outbasepath = "modm/src/modm/board"
+ # env.substitutions = {"board_has_logger": False}
+ # env.template("../board.cpp.in", "board.cpp")
+ env.copy('.')
+
+ # env.outbasepath = "modm/openocd/modm/board/"
+ # env.copy(repopath("tools/openocd/modm/stm32f103_blue_pill.cfg"), "stm32f103_blue_pill.cfg")
+ # env.collect(":build:openocd.source", "modm/board/stm32f103_blue_pill.cfg");
diff --git a/src/modm/board/samd21_mini/board.hpp b/src/modm/board/samd21_mini/board.hpp
new file mode 100644
index 0000000000..901662728d
--- /dev/null
+++ b/src/modm/board/samd21_mini/board.hpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016-2017, Sascha Schade
+ * Copyright (c) 2017-2018, 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/.
+ */
+// ----------------------------------------------------------------------------
+
+#pragma once
+
+#include
+#include
+
+using namespace modm::platform;
+
+
+/// @ingroup modm_board_samd21_mini
+namespace Board
+{
+using namespace modm::literals;
+
+struct SystemClock
+{
+ static constexpr uint32_t Frequency = 1_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 bool inline
+ enable()
+ {
+ // GenericClockController::enableExternalCrystal(Frequency);
+
+ // switch system clock to PLL output
+ // GenericClockController::enableSystemClock(ClockControl::SystemClockSource::Pll);
+
+ // update frequencies for busy-wait delay functions
+ // GenericClockController::updateCoreFrequency();
+
+ return true;
+ }
+};
+
+// User LED (inverted, because connected to 3V3)
+using LedD13 = GpioInverted;
+using LedTx = GpioInverted;
+using LedRx = GpioInverted;
+// using Leds = SoftwareGpioPort< LedRed >;
+
+using Button = GpioUnused;
+
+inline void
+initialize()
+{
+ SystemClock::enable();
+ SysTickTimer::initialize();
+
+ LedTx::setOutput(modm::Gpio::Low);
+ LedRx::setOutput(modm::Gpio::Low);
+}
+
+} // Board namespace
diff --git a/src/modm/board/samd21_mini/board.xml b/src/modm/board/samd21_mini/board.xml
new file mode 100644
index 0000000000..d57567cf23
--- /dev/null
+++ b/src/modm/board/samd21_mini/board.xml
@@ -0,0 +1,15 @@
+
+
+
+ ../../../../repo.lb
+
+
+
+
+
+
+
+
+ modm:board:samd21-mini
+
+
diff --git a/src/modm/board/samd21_mini/module.lb b/src/modm/board/samd21_mini/module.lb
new file mode 100644
index 0000000000..f6d243640c
--- /dev/null
+++ b/src/modm/board/samd21_mini/module.lb
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2016-2018, Niklas Hauser
+# Copyright (c) 2017, Fabian Greif
+#
+# 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-mini"
+ module.description = """
+# RobotDyn SAMD21 M0 MINI
+
+The SAMD21 MINI board is a small breakout board for the ATSAMD21G18 ARM
+Cortex-M0 processor, clocked at 48 MHz and at 3.3V logic.
+
+See: https://robotdyn.com/samd21-m0-mini.html
+
+It can be bought for little cost from well known Chinese online stores.
+"""
+
+def prepare(module, options):
+ if not options[":target"].partname.startswith("samd21g18a"):
+ return False
+
+ module.depends(":platform:gclk", ":platform:core", ":platform:clock",
+ ":platform:gpio")
+ return True
+
+def build(env):
+ env.outbasepath = "modm/src/modm/board"
+ # env.substitutions = {"board_has_logger": False}
+ # env.template("../board.cpp.in", "board.cpp")
+ env.copy('.')
+
+ # env.outbasepath = "modm/openocd/modm/board/"
+ # env.copy(repopath("tools/openocd/modm/stm32f103_blue_pill.cfg"), "stm32f103_blue_pill.cfg")
+ # env.collect(":build:openocd.source", "modm/board/stm32f103_blue_pill.cfg");
diff --git a/src/modm/platform/clock/sam/gclk.cpp.in b/src/modm/platform/clock/sam/gclk.cpp.in
new file mode 100644
index 0000000000..c53ba5b92d
--- /dev/null
+++ b/src/modm/platform/clock/sam/gclk.cpp.in
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013-2014, Kevin Läufer
+ * Copyright (c) 2014-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 "../device.hpp"
+#include "gclk.hpp"
+
+namespace modm::platform
+{
+uint16_t modm_fastdata delay_fcpu_MHz(1);
+uint16_t modm_fastdata delay_ns_per_loop({{ loops * 1000 }});
+}
diff --git a/src/modm/platform/clock/sam/gclk.hpp b/src/modm/platform/clock/sam/gclk.hpp
new file mode 100644
index 0000000000..82e57430bb
--- /dev/null
+++ b/src/modm/platform/clock/sam/gclk.hpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019, Ethan Slattery
+ *
+ * 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"
+
+namespace modm::platform
+{
+
+/**
+ * Clock management
+ *
+ * \ingroup modm_platform_gclk
+ */
+class GenericClockController
+{
+public:
+ template< uint32_t Core_Hz >
+ void
+ updateCoreFrequency();
+};
+
+}
+
+#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
new file mode 100644
index 0000000000..d1cfc9dc00
--- /dev/null
+++ b/src/modm/platform/clock/sam/gclk_impl.hpp.in
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019, Ethan Slattery
+ *
+ * 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
+
+namespace modm::platform
+{
+/// @cond
+extern uint16_t delay_fcpu_MHz;
+extern uint16_t delay_ns_per_loop;
+/// @endcond
+
+template< uint32_t Core_Hz >
+void
+GenericClockController::updateCoreFrequency()
+{
+ delay_fcpu_MHz = Core_Hz / 1'000'000;
+ delay_ns_per_loop = ::round({{loops}}000.f / (Core_Hz / 1'000'000));
+}
+
+}
+
diff --git a/src/modm/platform/clock/sam/module.lb b/src/modm/platform/clock/sam/module.lb
new file mode 100644
index 0000000000..a4d1fadf54
--- /dev/null
+++ b/src/modm/platform/clock/sam/module.lb
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2016-2018, Niklas Hauser
+# Copyright (c) 2017, Fabian Greif
+#
+# 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:gclk"
+ module.description = "Generic Clock Controller (GCLK)"
+
+def prepare(module, options):
+ if not options[":target"].has_driver("gclk:sam"):
+ return False
+
+ module.depends(":cmsis:device")
+ return True
+
+def build(env):
+ device = env[":target"]
+ core = device.get_driver("core")["type"]
+
+ if "m0" in core:
+ loops = 4
+ elif "m7" in core:
+ loops = 1
+ else:
+ loops = 3
+
+ env.substitutions = {"loops": loops}
+ env.outbasepath = "modm/src/modm/platform/clock"
+ env.copy("gclk.hpp")
+ env.template("gclk.cpp.in")
+ env.template("gclk_impl.hpp.in")
diff --git a/src/modm/platform/core/sam/linkerscript/sam_ram.ld.in b/src/modm/platform/core/sam/linkerscript/sam_ram.ld.in
new file mode 100644
index 0000000000..8b1c33a64e
--- /dev/null
+++ b/src/modm/platform/core/sam/linkerscript/sam_ram.ld.in
@@ -0,0 +1,50 @@
+%% import "../../cortex/linker.macros" as linker with context
+{{ linker.copyright() }}
+
+{{ linker.prefix() }}
+
+%% if vector_table_location == "ram"
+/* Round up the number of vector table entries to the nearest power-of-two and multiply by 8. */
+VECTOR_TABLE_ALIGNMENT = (1 << LOG2CEIL({{ number_of_interrupts + 16 }})) * 8;
+/* compute the vector table offset from start of RAM */
+VECTOR_TABLE_OFFSET = ALIGN(TOTAL_STACK_SIZE, VECTOR_TABLE_ALIGNMENT);
+%% else
+VECTOR_TABLE_OFFSET = TOTAL_STACK_SIZE;
+%% endif
+
+SECTIONS
+{
+{{ linker.section_rom_start("FLASH") }}
+
+{{ linker.section_vector_rom("FLASH") }}
+
+{{ linker.section_stack("RAM", "VECTOR_TABLE_OFFSET - TOTAL_STACK_SIZE") }}
+
+{{ linker.section_vector_ram("RAM") }}
+
+{{ linker.section_rom("FLASH") }}
+
+{{ linker.section("FLASH", "fastcode") }}
+
+{{ linker.section_ram("RAM", "FLASH") }}
+
+{{ linker.section("RAM AT >FLASH", "fastdata") }}
+
+{{ linker.section_heap("RAM", "heap1") }}
+
+{{ linkerscript_sections | indent(first=True) }}
+
+ /* TABLES! TABLES! ALL THE TABLES YOU COULD EVER WANT! TABLES! */
+{{ linker.section_table_zero("FLASH") }}
+
+{{ linker.section_table_copy("FLASH") }}
+
+{{ linker.section_table_extern("FLASH") }}
+
+%% set heap_table = [{"name": "heap1", "prop": "0x000f"}]
+{{ linker.section_table_heap("FLASH", heap_table) }}
+
+{{ linker.section_rom_end("FLASH") }}
+
+{{ linker.section_debug() }}
+}
diff --git a/src/modm/platform/core/sam/module.lb b/src/modm/platform/core/sam/module.lb
new file mode 100644
index 0000000000..e19ea01ecf
--- /dev/null
+++ b/src/modm/platform/core/sam/module.lb
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2019, Ethan Slattery
+#
+# 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:core"
+ module.description = """
+# Microchip SAM core module
+
+Provides SAM specific linkerscripts and startup code.
+"""
+
+def prepare(module, options):
+ if options[":target"].identifier.platform != "sam":
+ return False
+
+ module.depends(":platform:cortex-m")
+ return True
+
+
+def build(env):
+ env.substitutions = {"target": env[":target"].identifier}
+ env.outbasepath = "modm/src/modm/platform/core"
+ # startup helper code
+ env.template("startup_platform.c.in")
+
+
+def post_build(env):
+ env.substitutions = env.query("::cortex-m:linkerscript")
+ env.outbasepath = "modm/link"
+ env.template("linkerscript/sam_ram.ld.in", "linkerscript.ld")
diff --git a/src/modm/platform/core/sam/startup_platform.c.in b/src/modm/platform/core/sam/startup_platform.c.in
new file mode 100644
index 0000000000..6bd7663bfb
--- /dev/null
+++ b/src/modm/platform/core/sam/startup_platform.c.in
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019, Ethan Slattery
+ *
+ * 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"
+
+/**
+ * This code should _only_ enable internal memories and nothing else.
+ * Since this is the first code executed after a reset, you do not
+ * have access to _any_ data stored in RAM, since it has not yet been
+ * initialized.
+ * In the worst case you won't even have access to the stack, if the
+ * memory containing the stack is not physically enabled yet.
+ * In that case, consider using inline assembly to manage stack access
+ * manually, until the memory is enabled.
+ */
+void
+__modm_initialize_platform(void)
+{
+%% if target.family == "d"
+%% if target.series == "21"
+
+ // Overwriting the default value of the NVMCTRL.CTRLB.MANW bit (errata reference 13134)
+ NVMCTRL->CTRLB.bit.MANW = 1;
+
+ // Change default QOS values to have the best performance and correct USB behaviour
+ SBMATRIX->SFR[SBMATRIX_SLAVE_HMCRAMC0].reg = 2;
+
+#ifdef USB
+ USB->DEVICE.QOSCTRL.bit.CQOS = 2;
+ USB->DEVICE.QOSCTRL.bit.DQOS = 2;
+#endif
+
+ 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
+%% endif
+
+}
diff --git a/src/modm/platform/gpio/common/inverted.hpp b/src/modm/platform/gpio/common/inverted.hpp
index 96fb642356..3bcf0dfbde 100644
--- a/src/modm/platform/gpio/common/inverted.hpp
+++ b/src/modm/platform/gpio/common/inverted.hpp
@@ -44,7 +44,7 @@ class GpioInverted : public Pin
using Input = GpioInverted;
using IO = GpioInverted;
using Type = typename Pin::Type;
- static constexpr bool isInverted = Pin::isInverted ^ true;
+ static constexpr bool isInverted = not Pin::isInverted;
public:
using Pin::setOutput;
diff --git a/src/modm/platform/gpio/sam/base.hpp.in b/src/modm/platform/gpio/sam/base.hpp.in
new file mode 100644
index 0000000000..a2873316fd
--- /dev/null
+++ b/src/modm/platform/gpio/sam/base.hpp.in
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020, 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/.
+ */
+// ----------------------------------------------------------------------------
+
+#pragma once
+
+#include "../device.hpp"
+#include
+#include
+#include
+
+namespace modm::platform
+{
+
+/// @ingroup modm_platform_gpio
+struct Gpio
+{
+ enum class
+ InputType
+ {
+ Floating = 0x0, ///< floating on input
+ PullUp = 0x1, ///< pull-up on input
+ 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
+ InputTrigger
+ {
+ RisingEdge,
+ FallingEdge,
+ BothEdges,
+ };
+
+ /// The Port a Gpio Pin is connected to.
+ enum class
+ Port
+ {
+%% for port in ports
+ {{ port | upper }} = {{ port | modm.ord }},
+%% endfor
+ };
+
+ /// @cond
+ enum class
+ Signal
+ {
+ BitBang,
+ };
+ /// @endcond
+
+protected:
+ /// @cond
+ /// I/O Direction Mode values for this specific pin.
+ enum class
+ Mode
+ {
+ Input = 0x0,
+ Output = 0x1,
+ AlternateFunction = 0x2,
+ Analog = 0x3,
+ Mask = 0x3,
+ };
+
+ static constexpr uint32_t
+ i(Mode mode) { return uint32_t(mode); }
+ // Enum Class To Integer helper functions.
+ static constexpr uint32_t
+ i(InputType pull) { return uint32_t(pull); }
+ static constexpr uint32_t
+ i(OutputType out) { return uint32_t(out); }
+ static constexpr uint32_t
+ i(OutputSpeed speed) { return uint32_t(speed); }
+ /// @endcond
+};
+
+} // namespace modm::platform
diff --git a/src/modm/platform/gpio/sam/module.lb b/src/modm/platform/gpio/sam/module.lb
new file mode 100644
index 0000000000..70c53fa480
--- /dev/null
+++ b/src/modm/platform/gpio/sam/module.lb
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2016-2018, Niklas Hauser
+# Copyright (c) 2017, Fabian Greif
+#
+# 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/.
+# -----------------------------------------------------------------------------
+
+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)"
+
+def prepare(module, options):
+ device = options[":target"]
+ if not device.has_driver("gpio:sam*"):
+ return False
+
+ module.depends(
+ ":architecture:gpio",
+ ":cmsis:device",
+ ":math:utils")
+ return True
+
+def build(env):
+ device = env[":target"]
+ driver = device.get_driver("gpio")
+ bprops["ranges"] = port_ranges(driver["gpio"])
+ bprops["ports"] = OrderedDict([(k, i) for i, k in enumerate([p["name"] for p in bprops["ranges"]])])
+
+ properties = {"target": device.identifier}
+ properties.update(bprops)
+
+ env.substitutions = properties
+ env.outbasepath = "modm/src/modm/platform/gpio"
+
+ for gpio in driver["gpio"]:
+ po, pi = gpio["port"], gpio["pin"]
+ properties["gpio"] = gpio
+ env.template("pin.hpp.in", "gpio_{}{}.hpp".format(po.upper(), pi))
+
+ # env.template("port.hpp.in")
+ # env.template("software_port.hpp.in")
+ env.template("set.hpp.in")
+ env.template("base.hpp.in")
+ env.template("unused.hpp.in")
+
+ env.copy("../common/inverted.hpp", "inverted.hpp")
+ env.copy("../common/connector.hpp", "connector.hpp")
+ env.template("../common/connector_detail.hpp.in", "connector_detail.hpp")
+
+ # FIXME: Move to modm:platform:core!
+ env.outbasepath = "modm/src/modm/platform/core"
+ env.template("peripherals.hpp.in")
diff --git a/src/modm/platform/gpio/sam/peripherals.hpp.in b/src/modm/platform/gpio/sam/peripherals.hpp.in
new file mode 100644
index 0000000000..f87f1b776f
--- /dev/null
+++ b/src/modm/platform/gpio/sam/peripherals.hpp.in
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019, 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/.
+ */
+// ----------------------------------------------------------------------------
+
+#pragma once
+
+namespace modm::platform
+{
+
+enum class
+Peripheral
+{
+ BitBang,
+};
+
+}
diff --git a/src/modm/platform/gpio/sam/pin.hpp.in b/src/modm/platform/gpio/sam/pin.hpp.in
new file mode 100644
index 0000000000..44e38a0644
--- /dev/null
+++ b/src/modm/platform/gpio/sam/pin.hpp.in
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2017-2018, 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/.
+ */
+// ----------------------------------------------------------------------------
+
+%% set port = gpio.port | upper
+%% set reg = "GPIO" ~ port
+%% set pin = gpio.pin
+%% set pnr = gpio.port | modm.ord
+
+#pragma once
+
+#include "../device.hpp"
+#include "base.hpp"
+#include "set.hpp"
+
+namespace modm::platform
+{
+
+/// @cond
+class Gpio{{ port ~ pin }};
+using GpioOutput{{ port ~ pin }} = Gpio{{ port ~ pin }};
+using GpioInput{{ port ~ pin }} = Gpio{{ port ~ pin }};
+/// @endcond
+
+/// IO class for Pin {{port ~ pin}}
+/// @ingroup modm_platform_gpio
+class Gpio{{ port ~ pin }} : public Gpio, public ::modm::GpioIO
+{
+ template
+ friend class GpioSet;
+ using PinSet = GpioSet;
+ friend class Adc;
+ friend class Adc1; friend class Adc2;
+ friend class Adc3; friend class Adc4;
+public:
+ using Output = Gpio{{ port ~ pin }};
+ using Input = Gpio{{ port ~ pin }};
+ using IO = Gpio{{ port ~ pin }};
+ using Type = Gpio{{ port ~ pin }};
+ static constexpr bool isInverted = false;
+ static constexpr Port port = Port::{{port}}; ///< Port name
+ static constexpr uint8_t pin = {{pin|int}}; ///< Pin number
+
+protected:
+ /// Bitmask for registers that contain a 1bit value for every pin.
+ static constexpr uint32_t mask = 1ul << pin;
+ /// Bitmask for registers that contain a 2bit value for every pin.
+ // static constexpr uint32_t mask2 = 0x3 << (pin * 2);
+ /// Port Number.
+ static constexpr uint8_t port_nr = uint8_t(port);
+ /// Alternate Function register id. 0 for pin 0-7. 1 for pin 8-15.
+ // static constexpr uint8_t af_id = pin / 8;
+ /// Alternate Function offset.
+ // static constexpr uint8_t af_offset = (pin * 4) % 32;
+ /// Alternate Function register mask.
+ // static constexpr uint32_t af_mask = 0xf << af_offset;
+
+public:
+ /// @cond
+ inline static void setAlternateFunction(uint8_t) {
+ // {{reg}}->AFR[af_id] = ({{reg}}->AFR[af_id] & ~af_mask) | ((af & 0xf) << af_offset);
+ // {{reg}}->MODER = ({{reg}}->MODER & ~mask2) | (i(Mode::AlternateFunction) << (pin * 2));
+ }
+
+ /// Enable Analog Mode which is needed to use this pin as an ADC input.
+ inline static void setAnalogInput() { PinSet::setAnalogInput(); }
+ /// @endcond
+
+public:
+ // GpioOutput
+ // start documentation inherited
+ inline static void setOutput() { PinSet::setOutput(); }
+ inline static void setOutput(bool status) { PinSet::setOutput(status); }
+ inline static void set() { PinSet::set(); }
+ inline static void set(bool status) { PinSet::set(status); }
+ inline static void reset() { PinSet::reset(); }
+ inline static void toggle() {
+ if (isSet()) { reset(); }
+ else { set(); }
+ }
+ inline static bool isSet() { return (REG_PORT_OUT{{pnr}} & mask); }
+ // stop documentation inherited
+ inline static void configure(OutputType type, OutputSpeed speed = OutputSpeed::MHz50) { PinSet::configure(type, speed); }
+ inline static void setOutput(OutputType type, OutputSpeed speed = OutputSpeed::MHz50) { PinSet::setOutput(type, speed); }
+ // GpioInput
+ // start documentation inherited
+ inline static void setInput() { PinSet::setInput(); }
+ inline static bool read() { return (REG_PORT_IN{{pnr}} & mask); }
+ // end documentation inherited
+ inline static void configure(InputType type) { PinSet::configure(type); }
+ inline static void setInput(InputType type) { PinSet::setInput(type); }
+ // GpioIO
+ // start documentation inherited
+ inline static Direction getDirection() {
+ if (REG_PORT_DIR{{pnr}} & mask)
+ return Direction::In;
+ return Direction::Out;
+ }
+ // end documentation inherited
+ inline static void disconnect() {
+ setInput(InputType::Floating);
+ }
+};
+
+} // namespace modm::platform
diff --git a/src/modm/platform/gpio/sam/set.hpp.in b/src/modm/platform/gpio/sam/set.hpp.in
new file mode 100644
index 0000000000..bc1d89bab7
--- /dev/null
+++ b/src/modm/platform/gpio/sam/set.hpp.in
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2018, 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/.
+ */
+// ----------------------------------------------------------------------------
+
+#ifndef MODM_STM32_GPIO_SET_HPP
+#define MODM_STM32_GPIO_SET_HPP
+
+#include "../device.hpp"
+#include "base.hpp"
+
+namespace modm
+{
+
+namespace platform
+{
+
+/// @ingroup modm_platform_gpio
+template< class... Gpios >
+class GpioSet : public Gpio
+{
+protected:
+ static constexpr uint32_t inverteds[{{ ports | length }}] = {
+%% for port in ports
+ (((Gpios::port == Port::{{port}} and Gpios::isInverted) ? Gpios::mask : 0) | ...),
+%% endfor
+ };
+ static constexpr uint32_t inverted(uint8_t id) { return inverteds[id]; }
+
+ static constexpr uint32_t masks[{{ ports | length }}] = {
+%% for port in ports
+ (((Gpios::port == Port::{{port}}) ? Gpios::mask : 0) | ...),
+%% endfor
+ };
+ static constexpr uint32_t mask(uint8_t id) { return masks[id]; }
+ static constexpr uint8_t numberOfPorts() {
+ uint8_t r{0};
+ for (const auto &m: masks) r += (m) ? 1 : 0;
+ return r;
+ }
+public:
+ static constexpr uint8_t width = sizeof...(Gpios);
+ static constexpr uint8_t number_of_ports = numberOfPorts();
+public:
+ static void setOutput()
+ {
+ %% for port, id in ports.items()
+ if constexpr (mask({{id}})) REG_PORT_DIRSET{{id}} = mask({{id}});
+ %% endfor
+ }
+
+ static void setOutput(bool status)
+ {
+ set(status);
+ setOutput();
+ }
+
+ static void setOutput(OutputType type, OutputSpeed speed = OutputSpeed::MHz50)
+ {
+ configure(type, speed);
+ setOutput();
+ }
+
+ static void configure(OutputType , OutputSpeed = OutputSpeed::MHz50)
+ {
+ %% for port, id in ports.items()
+ if constexpr (mask({{id}})) {
+ // GPIO{{port}}->OSPEEDR = (GPIO{{port}}->OSPEEDR & ~mask2({{id}})) | (i(speed) * mask2({{id}}, 0b01));
+ // GPIO{{port}}->OTYPER = (GPIO{{port}}->OTYPER & ~mask({{id}})) | (i(type) ? mask({{id}}) : 0);
+ }
+ %% endfor
+ }
+
+ static void setInput()
+ {
+ %% for port, id in ports.items()
+ if constexpr (mask({{id}})) {
+ REG_PORT_DIRCLR{{id}} = mask({{id}});
+ }
+ %% endfor
+ }
+
+ static void setInput(InputType type)
+ {
+ configure(type);
+ setInput();
+ }
+
+ static void setAnalogInput()
+ {
+ %% for port, id in ports.items()
+ // if constexpr (mask({{id}})) GPIO{{port}}->MODER |= mask2({{id}}, i(Mode::Analog));
+ %% endfor
+ }
+
+ static void configure(InputType)
+ {
+ %% for port, id in ports.items()
+ if constexpr (mask({{id}})) {
+ // GPIO{{port}}->PUPDR = (GPIO{{port}}->PUPDR & ~mask2({{id}})) | (i(type) * mask2({{id}}, 0b01));
+ }
+ %% endfor
+ }
+
+ static void set()
+ {
+%% for port, id in ports.items()
+ if constexpr (mask({{id}})) REG_PORT_OUTSET{{id}} = mask({{id}});
+%% endfor
+ }
+
+ static void set(bool status)
+ {
+ if (status) set();
+ else reset();
+ }
+
+ static void reset()
+ {
+%% for port, id in ports.items()
+ if constexpr (mask({{id}})) REG_PORT_OUTCLR{{id}} = mask({{id}});
+%% endfor
+ }
+
+ static void toggle()
+ {
+%% for port, id in ports.items()
+ if constexpr (mask({{id}})) REG_PORT_OUTTGL{{id}} = mask({{id}});
+%% endfor
+ }
+
+ static void disconnect()
+ {
+ // (Gpios::disconnect(), ...);
+ }
+};
+
+} // namespace platform
+
+} // namespace modm
+
+#endif // MODM_STM32_GPIO_SET_HPP
diff --git a/src/modm/platform/gpio/sam/unused.hpp.in b/src/modm/platform/gpio/sam/unused.hpp.in
new file mode 100644
index 0000000000..1be9a4d1af
--- /dev/null
+++ b/src/modm/platform/gpio/sam/unused.hpp.in
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020, 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/.
+ */
+// ----------------------------------------------------------------------------
+
+#pragma once
+
+#include "base.hpp"
+#include
+
+namespace modm::platform
+{
+
+/**
+ * @author Niklas Hauser
+ * @ingroup modm_platform_gpio
+ */
+class GpioUnused : public Gpio, public ::modm::GpioIO
+{
+public:
+ using Output = GpioUnused;
+ using Input = GpioUnused;
+ using IO = GpioUnused;
+ using Type = GpioUnused;
+ static constexpr bool isInverted = false;
+ static constexpr Port port = Port(-1);
+ 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() {}
+ static void setOutput(bool) {}
+ static void set() {}
+ static void set(bool) {}
+ static void reset() {}
+ 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) {}
+ // External Interrupts
+ static void enableExternalInterrupt() {}
+ static void disableExternalInterrupt() {}
+ static void enableExternalInterruptVector(const uint32_t) {}
+ static void disableExternalInterruptVector() {}
+ static void setInputTrigger(const InputTrigger) {}
+ static bool getExternalInterruptFlag() { return false; }
+ /// Reset the interrupt flag in the interrupt routine.
+ static void acknowledgeExternalInterruptFlag() {}
+
+ // GpioIO
+ // start documentation inherited
+ static Direction getDirection() { return Direction::Special; }
+ // end documentation inherited
+ static void lock() {}
+ static void disconnect() {}
+};
+
+} // namespace modm::platform
diff --git a/test/all/stm32.cpp b/test/all/cortex-m.cpp
similarity index 100%
rename from test/all/stm32.cpp
rename to test/all/cortex-m.cpp
diff --git a/test/all/stm32.xml b/test/all/cortex-m.xml
similarity index 100%
rename from test/all/stm32.xml
rename to test/all/cortex-m.xml
diff --git a/test/all/ignored.txt b/test/all/ignored.txt
index 44ac365e5a..7a0b65ef99 100644
--- a/test/all/ignored.txt
+++ b/test/all/ignored.txt
@@ -98,3 +98,8 @@ stm32l151qch6
stm32l151zct6
stm32l152qch6
stm32l152zct6
+# FIXME: Missing cmsis-header for these ones
+samd21e15c-uf
+samd21e15c-uu
+samd21e16c-uf
+samd21e16c-uu
diff --git a/test/all/run_all.py b/test/all/run_all.py
index 54a695b781..31e9841da8 100644
--- a/test/all/run_all.py
+++ b/test/all/run_all.py
@@ -84,9 +84,9 @@ def run(self):
if self.device.startswith("at"):
lbuild_command = ["-c", "avr.xml"]
shutil.copyfile("avr.cpp", os.path.join(tempdir, "main.cpp"))
- elif self.device.startswith("stm32"):
- lbuild_command = ["-c", "stm32.xml", "-D:::main_stack_size=512"]
- shutil.copyfile("stm32.cpp", os.path.join(tempdir, "main.cpp"))
+ else:
+ lbuild_command = ["-c", "cortex-m.xml", "-D:::main_stack_size=512"]
+ shutil.copyfile("cortex-m.cpp", os.path.join(tempdir, "main.cpp"))
if self.cache_dir:
lbuild_command.append("-D:::cache_dir={}".format(self.cache_dir))
diff --git a/test/module.lb b/test/module.lb
index f444a99eb2..865bb72787 100644
--- a/test/module.lb
+++ b/test/module.lb
@@ -22,6 +22,7 @@ def prepare(module, options):
def build(env):
env.collect("modm:build:path.include", "modm-test/src")
+ core = env[":target"].get_driver("core")["type"]
+ core = next(t for t in ("avr", "cortex-m", "hosted") if core.startswith(t))
env.outbasepath = "."
- env.copy("runner/{}.cpp".format(env[":target"].identifier.platform),
- "main.cpp")
+ env.copy("runner/{}.cpp".format(core), "main.cpp")
diff --git a/test/runner/stm32.cpp b/test/runner/cortex-m.cpp
similarity index 100%
rename from test/runner/stm32.cpp
rename to test/runner/cortex-m.cpp
diff --git a/tools/build_script_generator/cmake/cmake_scripts/configure-gcc.cmake.in b/tools/build_script_generator/cmake/cmake_scripts/configure-gcc.cmake.in
index 564ff4a78a..23224fbb26 100644
--- a/tools/build_script_generator/cmake/cmake_scripts/configure-gcc.cmake.in
+++ b/tools/build_script_generator/cmake/cmake_scripts/configure-gcc.cmake.in
@@ -30,10 +30,9 @@ if(WIN32)
%% if platform == "hosted"
elseif(APPLE)
# Using homebrew gcc on macOS
- find_program(GCC_VERSION NAMES "gcc-9" "gcc-8" "gcc-7")
- string(LENGTH ${GCC_VERSION} GCC_VERSION_LENGTH)
- math(EXPR GCC_VERSION_LENGTH "${GCC_VERSION_LENGTH}-2")
- string(SUBSTRING ${GCC_VERSION} ${GCC_VERSION_LENGTH} -1 GCC_VERSION)
+ find_program(GCC_VERSION NAMES "gcc-10" "gcc-9" "gcc-8" "gcc-7")
+ string(REGEX MATCH "gcc-[0-9]+" GCC_VERSION ${GCC_VERSION})
+ string(SUBSTRING ${GCC_VERSION} 3 -1 GCC_VERSION)
set(TOOL_EXECUTABLE_SUFFIX "${GCC_VERSION}")
%% endif
else()
diff --git a/tools/build_script_generator/module.lb b/tools/build_script_generator/module.lb
index c722247900..db780748a8 100644
--- a/tools/build_script_generator/module.lb
+++ b/tools/build_script_generator/module.lb
@@ -117,6 +117,11 @@ def prepare(module, options):
PathCollector(name="path.openocd",
description="Search path for OpenOCD configuration files"))
+ if platform == "sam":
+ module.add_collector(
+ StringCollector(name="bossac.options",
+ description="Additional BOSSAc options"))
+
elif platform in ["avr"]:
module.add_collector(
StringCollector(name="default.avrdude.programmer",
@@ -171,6 +176,8 @@ def build(env):
if is_cortex_m:
tools.update({"bmp", "openocd", "crashdebug", "gdb", "backend",
"log", "build_id", "size"})
+ if platform in ["sam"]:
+ tools.update({"bossac"})
elif platform in ["avr"]:
tools.update({"avrdude"})
diff --git a/tools/build_script_generator/scons/module.lb b/tools/build_script_generator/scons/module.lb
index c0486ea4a5..a98038aeb3 100644
--- a/tools/build_script_generator/scons/module.lb
+++ b/tools/build_script_generator/scons/module.lb
@@ -104,6 +104,8 @@ def build(env):
if device["core"].startswith("cortex-m"):
tools.update({"compiler_arm_none_eabi_gcc", "size", "log_itm", "artifact",
"openocd", "openocd_remote", "bmp", "crashdebug", "dfu"})
+ if device["platform"] in ["sam"]:
+ tools.update({"bossac"})
elif device["core"].startswith("avr"):
tools.update({"compiler_avr_gcc", "size", "avrdude"})
else: # hosted
@@ -185,6 +187,9 @@ def post_build(env):
})
if subs["platform"] == "avr":
subs.update(env.query("::avrdude_options"))
+ if subs["platform"] == "sam":
+ subs.update({"bossac_offset": env.get(":platform:cortex-m:linkerscript.flash_offset", None),
+ "bossac_options": " ".join(env.collector_values(":build:bossac.options"))})
# Set these substitutions for all templates
env.substitutions = subs
diff --git a/tools/build_script_generator/scons/resources/SConscript.in b/tools/build_script_generator/scons/resources/SConscript.in
index 77d3000de9..f6d29755cc 100644
--- a/tools/build_script_generator/scons/resources/SConscript.in
+++ b/tools/build_script_generator/scons/resources/SConscript.in
@@ -17,7 +17,7 @@ env["BUILDPATH"] = join(env["CONFIG_BUILD_BASE"], profile)
env["BASEPATH"] = abspath(".")
%% if family == "darwin"
# Using homebrew gcc on macOS instead of clang
-env["COMPILERSUFFIX"] = env.Detect(["gcc-9", "gcc-8", "gcc-7"])[3:]
+env["COMPILERSUFFIX"] = env.Detect(["gcc-10", "gcc-9", "gcc-8", "gcc-7"])[3:]
%% endif
%% endif
@@ -108,13 +108,21 @@ env["CONFIG_AVRDUDE_PORT"] = "{{ avrdude_port }}"
%% if avrdude_options
env["CONFIG_AVRDUDE_OPTIONS"] = "{{ avrdude_options }}"
%% endif
-%% if avrdude_baudrate > 0
+%% if avrdude_baudrate
env["CONFIG_AVRDUDE_BAUDRATE"] = "{{ avrdude_baudrate }}"
%% endif
%% elif core.startswith("cortex-m")
env.Append(MODM_OPENOCD_CONFIGFILES="$BASEPATH/openocd.cfg")
env.Append(MODM_OPENOCD_GDBINIT="$BASEPATH/openocd_gdbinit")
env.Append(MODM_GDBINIT="$BASEPATH/gdbinit")
+%% if platform == "sam"
+%% if bossac_offset
+env.Append(MODM_BOSSAC_OFFSET={{ bossac_offset }})
+%% endif
+%% if bossac_options
+env.Append(MODM_BOSSAC_OPTIONS="{{ bossac_options }}")
+%% endif
+%% endif
%% endif
# XPCC generator tool path
diff --git a/tools/build_script_generator/scons/resources/build_target.py.in b/tools/build_script_generator/scons/resources/build_target.py.in
index 30d673b10d..6d302224d7 100644
--- a/tools/build_script_generator/scons/resources/build_target.py.in
+++ b/tools/build_script_generator/scons/resources/build_target.py.in
@@ -40,23 +40,32 @@ def build_target(env, sources):
env.Alias("artifact", env.CacheArtifact(program))
%% set artifact = ', "artifact"' if upload_with_artifact else ''
- env.Alias("program", [env.ProgramOpenOcd(program){{ artifact }}])
+ env.Alias("program-openocd", [env.ProgramOpenOcd(program){{ artifact }}])
env.Alias("program-remote", [env.ProgramGdbRemote(program){{ artifact }}])
env.Alias("program-bmp", [env.ProgramBMP(program){{ artifact }}])
env.Alias('program-dfu', [env.ProgramDFU(env.Bin(program)){{ artifact }}])
+ %% if platform in ["sam"]
+ env.Alias("program-bossac", [env.ProgramBossac(env.Bin(program)){{ artifact }}])
+ %% endif
- env.Alias("debug", env.DebugOpenOcd(program))
+ env.Alias("debug-openocd", env.DebugOpenOcd(program))
env.Alias("debug-remote", env.DebugGdbRemote(program))
env.Alias("debug-bmp", env.DebugBMP(program))
env.Alias("debug-coredump", env.DebugCoredump(program))
- env.Alias("reset", env.ResetOpenOcd())
+ env.Alias("reset-openocd", env.ResetOpenOcd())
env.Alias("reset-bmp", env.ResetBMP())
env.Alias("reset-remote", env.ResetGdbRemote())
+ # Default to OpenOCD
+ env.Alias("program", "program-openocd")
+ env.Alias("reset", "reset-openocd")
+ env.Alias("debug", "debug-openocd")
+
%% elif core.startswith("avr")
- env.Alias("program", env.ProgramAvrdude(program))
+ env.Alias("program-avrdude", env.ProgramAvrdude(program))
env.Alias("program-fuses", env.ProgramAvrdudeFuses(program))
+ env.Alias("program", "program-avrdude")
%% endif
env.Alias("all", ["build", "size"])
diff --git a/tools/build_script_generator/scons/site_tools/avrdude.py b/tools/build_script_generator/scons/site_tools/avrdude.py
index a480be0406..ef76f3fd2e 100644
--- a/tools/build_script_generator/scons/site_tools/avrdude.py
+++ b/tools/build_script_generator/scons/site_tools/avrdude.py
@@ -24,7 +24,7 @@ def call_avrdude(env, source, fuses=None):
fuses=fuses)
# -----------------------------------------------------------------------------
-def avrdude_flash(env, source, alias="avrdude_program"):
+def avrdude_flash(env, source, alias="avrdude_flash"):
def call_program(target, source, env):
call_avrdude(env, source[0])
diff --git a/tools/build_script_generator/scons/site_tools/bossac.py b/tools/build_script_generator/scons/site_tools/bossac.py
new file mode 100644
index 0000000000..8ec99b25e9
--- /dev/null
+++ b/tools/build_script_generator/scons/site_tools/bossac.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, 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/.
+# -----------------------------------------------------------------------------
+
+import platform
+from SCons.Script import *
+from modm_tools import bossac
+
+# -----------------------------------------------------------------------------
+def bossac_program(env, source, alias="bossac_program"):
+ def call_program(target, source, env):
+ offset = env.get("MODM_BOSSAC_OFFSET", 0)
+ if offset < 0x1000: # Is 4kB the smallest SAM-BA bootloader?
+ raise ValueError("Refusing to overwrite bootloader!\n"
+ " Hint: Set `:platform:cortex-m:linkerscript.flash_offset` to bootloader size!")
+ else:
+ bossac.program(source=source[0], offset=offset,
+ port=ARGUMENTS.get("port", "auto"),
+ options=env.get("MODM_BOSSAC_OPTIONS"))
+
+ action = Action(call_program, cmdstr="$PROGRAM_BOSSAC_STR")
+ return env.AlwaysBuild(env.Alias(alias, source, action))
+
+# -----------------------------------------------------------------------------
+def generate(env, **kw):
+ # build messages
+ if ARGUMENTS.get("verbose") != "1":
+ env["PROGRAM_BOSSAC_STR"] = \
+ "{0}.-------------- {1}$SOURCE\n" \
+ "{0}'----BOSSAc---> {2}$CONFIG_DEVICE_NAME{3}" \
+ .format("\033[;0;32m", "\033[;0;33m", "\033[;1;33m", "\033[;0;0m")
+
+ env.AddMethod(bossac_program, "ProgramBossac")
+
+def exists(env):
+ return env.Detect("bossac")
diff --git a/tools/modm_tools/bossac.py b/tools/modm_tools/bossac.py
new file mode 100644
index 0000000000..2b22e67a11
--- /dev/null
+++ b/tools/modm_tools/bossac.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2020, 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/.
+# -----------------------------------------------------------------------------
+
+"""
+### BOSSA
+
+This tool simply wraps the `bossac` command line tool to guess the serial port.
+
+```sh
+python3 modm/modm_tools/bossac.py -p auto --offset=0x2000 -e \\
+ path/to/project.bin
+```
+
+(\* *only SAM targets*)
+"""
+
+import os
+import subprocess
+if __name__ == "__main__":
+ import sys
+ sys.path.append(os.path.dirname(os.path.dirname(__file__)))
+
+from modm_tools import utils
+from elftools.elf.elffile import ELFFile, NoteSection
+
+# -----------------------------------------------------------------------------
+def program(source, offset=None, port=None, erase=False, options=None):
+ command = ["bossac", "-b", "-R"]
+
+ # Attempt to find a serial port automatically
+ if port == "auto":
+ port = utils.guess_serial_port("bossac")
+
+ if port is not None:
+ command.append("--port={}".format(port))
+
+ if offset is not None:
+ command.append("--offset={}".format(offset))
+
+ if erase:
+ command.append("-e")
+
+ command.extend(utils.listify(options))
+ command.extend(["-v", "-w", str(source)])
+
+ command = " ".join(command)
+ # print(command)
+ subprocess.call(command, cwd=os.getcwd(), shell=True)
+
+
+# -----------------------------------------------------------------------------
+if __name__ == "__main__":
+ import argparse
+
+ parser = argparse.ArgumentParser(description='Program ELF file via BOSSA')
+ parser.add_argument(
+ dest="source",
+ metavar="BIN")
+ parser.add_argument(
+ "-p",
+ dest="port",
+ default="auto")
+ parser.add_argument(
+ "--offset",
+ dest="offset")
+ parser.add_argument(
+ "-e",
+ dest="erase",
+ default=False)
+
+ args = parser.parse_args()
+ program(args.source, offset=args.offset, port=args.port, erase=args.erase)
diff --git a/tools/scripts/docs_modm_io_generator.py b/tools/scripts/docs_modm_io_generator.py
index 3b8545cc9e..7a186f5e5e 100755
--- a/tools/scripts/docs_modm_io_generator.py
+++ b/tools/scripts/docs_modm_io_generator.py
@@ -59,6 +59,9 @@ def get_targets():
elif target.platform == "hosted":
short_id.naming_schema = "{platform}"
+ elif target.platform == "sam":
+ short_id.naming_schema = "{platform}{family}{series}"
+
short_id.set("platform", target.platform) # invalidate caches
minimal_targets[short_id.string].append(target)
@@ -93,7 +96,7 @@ def main():
# test list
device_list = ["hosted-linux", "atmega328p-au", "stm32f103c8t6", "stm32g474cet6"]
elif args.test2:
- device_list = ["hosted-linux", "atmega328p-pu", "stm32f103zgt7", "stm32g474vet7"]
+ device_list = ["hosted-linux", "atmega328p-pu", "stm32f103zgt7", "stm32g474vet7", "samd21g18a-uu"]
else:
device_list = get_targets()
diff --git a/tools/scripts/synchronize_docs.py b/tools/scripts/synchronize_docs.py
index 7106ec1044..29e8efb066 100755
--- a/tools/scripts/synchronize_docs.py
+++ b/tools/scripts/synchronize_docs.py
@@ -124,6 +124,7 @@ def get_lbuild(root, target=None):
targets -= ignored_devices
avr_count = len([t for t in targets if t.startswith("at")])
stm_count = len([t for t in targets if t.startswith("stm32")])
+sam_count = len([t for t in targets if t.startswith("sam")])
# get the author count
from authors import author_handles
@@ -141,6 +142,7 @@ def get_lbuild(root, target=None):
readme = readme_path.read_text()
readme = replace(readme, "authorcount", author_count - 7)
readme = replace(readme, "avrcount", avr_count)
+readme = replace(readme, "samcount", sam_count)
readme = replace(readme, "stmcount", stm_count)
readme = replace(readme, "bsptable", bsp_table)
readme = replace(readme, "drivertable", driver_table)