diff --git a/test/Makefile b/test/Makefile
index 76e8abec8b..40ed8f7b69 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -63,6 +63,12 @@ run-nucleo-l432:
$(call run-test,nucleo-l432,size)
+compile-nucleo-g474:
+ $(call compile-test,nucleo-g474,size)
+run-nucleo-g474:
+ $(call run-test,nucleo-g474,size)
+
+
compile-nucleo-f103_A:
$(call compile-test,nucleo-f103_A,size)
run-nucleo-f103_A:
diff --git a/test/config/nucleo-g474.xml b/test/config/nucleo-g474.xml
new file mode 100644
index 0000000000..0361617c4b
--- /dev/null
+++ b/test/config/nucleo-g474.xml
@@ -0,0 +1,12 @@
+
+
+ modm:nucleo-g474re
+
+
+
+
+
+ modm:platform:heap
+ modm-test:test:**
+
+
diff --git a/test/modm/platform/fdcan/fdcan_test.cpp b/test/modm/platform/fdcan/fdcan_test.cpp
new file mode 100644
index 0000000000..883a9b6a77
--- /dev/null
+++ b/test/modm/platform/fdcan/fdcan_test.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2021, 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 "fdcan_test.hpp"
+
+#include
+#include
+
+using namespace modm::platform;
+
+void
+FdcanTest::setUp()
+{
+ Fdcan1::initialize(9, Fdcan1::Mode::TestInternalLoopback);
+
+ // receive all extended messages
+ Fdcan1::setExtendedFilter(0, Fdcan1::FilterConfig::Fifo1,
+ modm::can::ExtendedIdentifier(0),
+ modm::can::ExtendedMask(0));
+}
+
+void
+FdcanTest::testSendReceive()
+{
+ modm::can::Message message{0x12345678, 7};
+ constexpr std::string_view data = "\xDE\xAD\xBE\xEF\x12\x34\x56";
+ std::copy(std::begin(data), std::begin(data) + 7, message.data);
+
+ TEST_ASSERT_FALSE(Fdcan1::isMessageAvailable());
+ TEST_ASSERT_TRUE(Fdcan1::sendMessage(message));
+
+ modm::delay_ms(1);
+
+ modm::can::Message receivedMessage;
+ TEST_ASSERT_TRUE(Fdcan1::getMessage(receivedMessage));
+ TEST_ASSERT_EQUALS(receivedMessage.getIdentifier(), 0x12345678u);
+ TEST_ASSERT_EQUALS(receivedMessage.getLength(), 7);
+ TEST_ASSERT_TRUE(receivedMessage.isExtended());
+ TEST_ASSERT_FALSE(receivedMessage.isRemoteTransmitRequest());
+ TEST_ASSERT_TRUE(std::equal(std::begin(data), std::begin(data) + 7, message.data));
+}
+
+void
+FdcanTest::testFilters()
+{
+ Fdcan1::setStandardFilter(27, Fdcan1::FilterConfig::Fifo0,
+ modm::can::StandardIdentifier(0x108),
+ modm::can::StandardMask(0x1F8));
+
+ modm::can::Message message{0x188, 0};
+ message.setExtended(false);
+ Fdcan1::sendMessage(message);
+ modm::delay_ms(1);
+ TEST_ASSERT_FALSE(Fdcan1::isMessageAvailable());
+
+ message.setIdentifier(0xF09);
+ Fdcan1::sendMessage(message);
+ modm::delay_ms(1);
+ TEST_ASSERT_TRUE(Fdcan1::isMessageAvailable());
+ TEST_ASSERT_TRUE(Fdcan1::getMessage(message));
+ TEST_ASSERT_FALSE(message.isExtended());
+}
+
+void
+FdcanTest::testBuffers()
+{
+ modm::can::Message message{0x4711, 0};
+ // send 8 messages, exceeds internal peripheral queue size
+ for (uint_fast8_t i = 0; i <= 8; ++i) {
+ message.setLength(i);
+ for (uint_fast8_t dataIndex = 0; dataIndex < i; ++dataIndex) {
+ message.data[dataIndex] = i;
+ }
+ Fdcan1::sendMessage(message);
+ }
+
+ modm::delay_ms(10);
+
+ // try to receive same messages
+ modm::can::Message receivedMessage;
+ for (uint_fast8_t i = 0; i <= 8; ++i) {
+ TEST_ASSERT_TRUE(Fdcan1::getMessage(receivedMessage));
+
+ TEST_ASSERT_EQUALS(receivedMessage.getIdentifier(), 0x4711u);
+ TEST_ASSERT_EQUALS(receivedMessage.getLength(), i);
+
+ for (uint_fast8_t dataIndex = 0; dataIndex < i; ++dataIndex) {
+ TEST_ASSERT_EQUALS(receivedMessage.data[dataIndex], i);
+ }
+ }
+ TEST_ASSERT_FALSE(Fdcan1::isMessageAvailable());
+ TEST_ASSERT_FALSE(Fdcan1::getMessage(message));
+}
diff --git a/test/modm/platform/fdcan/fdcan_test.hpp b/test/modm/platform/fdcan/fdcan_test.hpp
new file mode 100644
index 0000000000..b952af6e9c
--- /dev/null
+++ b/test/modm/platform/fdcan/fdcan_test.hpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2021, 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
+
+/// @ingroup modm_test_test_platform_fdcan
+class FdcanTest : public unittest::TestSuite
+{
+public:
+ void
+ setUp() override;
+
+ void
+ testSendReceive();
+
+ void
+ testFilters();
+
+ void
+ testBuffers();
+};
diff --git a/test/modm/platform/fdcan/module.lb b/test/modm/platform/fdcan/module.lb
new file mode 100644
index 0000000000..c7c8934fe5
--- /dev/null
+++ b/test/modm/platform/fdcan/module.lb
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021, 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 = ":test:platform:fdcan"
+
+def prepare(module, options):
+ target = options[":target"]
+
+ identifier = target.identifier
+ if identifier.platform != "stm32" or identifier.family != "g4":
+ return False
+
+ module.depends(":architecture:delay", ":platform:can:1")
+ return True
+
+def build(env):
+# env.substitutions = properties
+ env.outbasepath = "modm-test/src/modm-test/platform/fdcan_test"
+ env.copy("fdcan_test.hpp")
+ env.copy("fdcan_test.cpp")