Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[test] Better hardware unit tests using simple on-device logic analyzer #96

Merged
merged 5 commits into from
Jul 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,54 +198,55 @@ can easily configure them for you specific needs.
<td align="center">FT245</td>
</tr><tr>
<td align="center">FT6X06</td>
<td align="center">GPIO-SAMPLER</td>
<td align="center">HCLAx</td>
<td align="center">HD44780</td>
<td align="center">HMC58x</td>
<td align="center">HMC6343</td>
<td align="center">HX711</td>
</tr><tr>
<td align="center">HX711</td>
<td align="center">I2C-EEPROM</td>
<td align="center">ITG3200</td>
<td align="center">L3GD20</td>
<td align="center">LAWICEL</td>
<td align="center">LIS302DL</td>
<td align="center">LIS3DSH</td>
</tr><tr>
<td align="center">LIS3DSH</td>
<td align="center">LIS3MDL</td>
<td align="center">LM75</td>
<td align="center">LP503X</td>
<td align="center">LSM303A</td>
<td align="center">LSM6DS33</td>
<td align="center">LTC2984</td>
</tr><tr>
<td align="center">LTC2984</td>
<td align="center">MAX6966</td>
<td align="center">MAX7219</td>
<td align="center">MCP23X17</td>
<td align="center">MCP2515</td>
<td align="center">NOKIA5110</td>
<td align="center">NRF24</td>
</tr><tr>
<td align="center">NRF24</td>
<td align="center">TFT-DISPLAY</td>
<td align="center">PAT9125EL</td>
<td align="center">PCA8574</td>
<td align="center">PCA9535</td>
<td align="center">PCA9548A</td>
<td align="center">PCA9685</td>
</tr><tr>
<td align="center">PCA9685</td>
<td align="center">SIEMENS-S65</td>
<td align="center">SIEMENS-S75</td>
<td align="center">SK6812</td>
<td align="center">SK9822</td>
<td align="center">SSD1306</td>
<td align="center">SX1276</td>
</tr><tr>
<td align="center">SX1276</td>
<td align="center">TCS3414</td>
<td align="center">TCS3472</td>
<td align="center">TLC594X</td>
<td align="center">TMP102</td>
<td align="center">TMP175</td>
<td align="center">VL53L0</td>
</tr><tr>
<td align="center">VL53L0</td>
<td align="center">VL6180</td>
<td align="center">WS2812</td>
</tr>
Expand Down
126 changes: 126 additions & 0 deletions src/modm/driver/gpio/gpio_sampler.cpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* 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/.
*/
// ----------------------------------------------------------------------------

#include "gpio_sampler.hpp"
#include <modm/architecture/interface/interrupt.hpp>
#include <modm/platform.hpp>
#include <modm/debug/logger.hpp>

using IrqHandler = void(*)();
%% if vectors_location == "rom"
modm_fastdata
IrqHandler exti_vectors[{{ extis | length }}] = {nullptr};

%% for vector in extis
MODM_ISR({{ vector }})
{
exti_vectors[{{ loop.index0 }}]();
}
%% endfor
%% endif

const IRQn_Type
irq_map[{{ extis | length }}] =
{
%% for vector in extis
{{ vector }}_IRQn,
%% endfor
};

namespace modm
{

modm_fastdata
void *GpioSampler::context{nullptr};

void
GpioSampler::reset(Interrupt vector)
{
const size_t index = int(vector);
NVIC_DisableIRQ(irq_map[index]);
NVIC_SetPriority(irq_map[index], 0);
}

void
GpioSampler::setHandler(Interrupt vector, IrqHandler handler)
{
const size_t index = int(vector);
if (index >= {{ extis | length }}) return;
%% if vectors_location == "ram"
NVIC_SetVector(irq_map[index], (uint32_t) handler);
%% else
exti_vectors[index] = handler;
%% endif
NVIC_EnableIRQ(irq_map[index]);
}

GpioSampler::Channel::Channel()
{}
void
GpioSampler::Channel::allocate(size_t max_samples)
{
max_count = max_samples + 1;
data = new Type[max_samples + 1];
}
GpioSampler::Channel::~Channel()
{
delete[] data;
}

void
GpioSampler::Channel::reset()
{
count = 0;
}

void
GpioSampler::Channel::dump() const
{
for (size_t ii=0; ii<count; ii++) {
Type d = diff(ii);
MODM_LOG_DEBUG.printf("%3u %9ld %6ld (%ldus)\n", ii, (*this)[ii], d, int32_t(int64_t(d * 1000000) / modm::platform::fcpu));
}
MODM_LOG_DEBUG << modm::endl;
}

void
GpioSampler::Channel::add(Type time)
{
if (count < max_count) data[count++] = time;
}

const GpioSampler::Type&
GpioSampler::Channel::operator[](size_t index) const
{
return data[(index < count) ? index : (count - 1)];
}

bool
GpioSampler::Channel::read(size_t index) const
{
return ((*this)[index] > 0);
}

GpioSampler::Type
GpioSampler::Channel::diff(size_t index) const
{
if (index == 0) return 0;
Type s0 = (*this)[index - 1];
Type s1 = (*this)[index];
// abs
uint32_t t0 = (s0 > 0) ? s0 : -s0;
uint32_t t1 = (s1 > 0) ? s1 : -s1;
// Fix overflow issues
if (t1 < t0) t1 |= (1ul << 31);
return t1 - t0;
}

} // namespace modm::platform
111 changes: 111 additions & 0 deletions src/modm/driver/gpio/gpio_sampler.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* 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_TEST_LOGIC_ANALYZER_SAMPLER_HPP
#define MODM_TEST_LOGIC_ANALYZER_SAMPLER_HPP

#include <stdint.h>
#include <stddef.h>
#include <modm/platform/device.hpp>

namespace modm
{

class GpioSampler
{
public:
using Type = int32_t;

class Channel
{
friend class GpioSampler;
size_t max_count = 0;
volatile size_t count = 0;
Type *data = nullptr;
Channel();
void allocate(size_t max_samples);
void add(Type time);
void reset();

public:
~Channel();

inline size_t max() const { return max_count; }
inline size_t size() const { return count; }

void dump() const;

Type diff(size_t index) const;
bool read(size_t index) const;

const Type& operator[](size_t index) const;

inline const Type* begin() const { return data; }
inline const Type* end() const { return &data[count]; }
};

template<size_t channels>
class Handle
{
friend class GpioSampler;
using CleanupHandler = void(*)();
using StartHandler = void(*)(Handle<channels> &);

const CleanupHandler cleanup;
const StartHandler start;
Channel data[channels];

Handle(size_t max_samples, StartHandler start, CleanupHandler cleanup);
void set_start_time(const Type *start);
public:
static constexpr size_t Channels = channels;

public:
~Handle();

void
restart();

const Channel&
operator[](size_t channel) const;
};

template< class... Gpios >
static auto Create(size_t max_samples);

protected:
static void *context;

enum class
Interrupt : uint8_t
{
%% for vector in extis
{{ vector | capitalize }} = {{ loop.index0 }},
%% endfor
};

template< size_t channels, size_t pin_count, uint8_t pin, class Gpio, class... Gpios >
static void sampleGpio(Channel *data, Type time);
template< size_t channels, size_t pin_count, uint8_t pin >
static void sampleGpio(Channel *, Type) {}

static void reset(Interrupt vector);
static void setHandler(Interrupt vector, void(*handler)());
static inline Type getTime() {
return Type(DWT->CYCCNT & ~(1ul << 31));
}
};

} // namespace modm

#include "gpio_sampler_impl.hpp"

#endif // MODM_TEST_LOGIC_ANALYZER_SAMPLER_HPP
58 changes: 58 additions & 0 deletions src/modm/driver/gpio/gpio_sampler.lb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/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/.

from collections import OrderedDict

def init(module):
module.parent = ":driver"
module.name = "gpio_sampler"


def prepare(module, options):
if options[":target"].identifier["platform"] != "stm32":
return False

core = options[":target"].get_driver("core:cortex-m*")
# Cortex-M0 doesn't have the DWT->CYCCNT and Cortex-M7 support is broken
if not core or "m0" in core["type"] or "m7" in core["type"]:
return False

module.depends(
":platform:gpio",
":platform:core",
":architecture:interrupt")
return True


def build(env):
exti_vectors = [v["name"] for v in env[":target"].get_driver("core")["vector"] if "EXTI" in v["name"]]
# These are all exti possible vectors: 0, 0_1, 1, 15_10, 2, 2_3, 2_TSC, 3, 4, 4_15, 9_5
extimap = {
"0": [0], "1": [1], "2": [2], "3": [3], "4": [4],
"0_1": [0,1],
"2_TSC": [2],
"2_3": [2,3],
"4_15": [4,5,6,7,8,9,10,11,12,13,14,15],
"9_5": [5,6,7,8,9],
"15_10": [10,11,12,13,14,15],
}
extis = OrderedDict()
for v in sorted(exti_vectors):
extis[v] = extimap[v[4:]]

env.substitutions = {
"extis": extis,
"vectors_location": env.get(":platform:core:vector_table_location", "rom")
}
env.outbasepath = "modm/src/modm/driver"
env.template("gpio_sampler.cpp.in")
env.template("gpio_sampler.hpp.in")
env.template("gpio_sampler_impl.hpp.in")
Loading