Skip to content

Commit f3e2efe

Browse files
committed
Merge branch 'develop'
2 parents ba71c71 + c147bc2 commit f3e2efe

File tree

5 files changed

+136
-68
lines changed

5 files changed

+136
-68
lines changed

library.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"url": "https://github.com/m5stack/M5Utility.git"
1212
},
1313
"dependencies": [],
14-
"version": "0.0.7",
14+
"version": "0.0.8",
1515
"frameworks": [
1616
"arduino"
1717
],

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=M5Utility
2-
version=0.0.7
2+
version=0.0.8
33
author=M5Stack
44
maintainer=M5Stack
55
sentence=Library for other M5 libraries and products

src/m5_utility/lfsr.hpp

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include <bitset>
1414
#include <climits>
15+
#include "misc.hpp"
1516

1617
namespace m5 {
1718
namespace utility {
@@ -25,7 +26,7 @@ namespace utility {
2526
*/
2627
template <uint32_t N, uint32_t... Taps>
2728
class FibonacciLFSR_Right {
28-
static_assert(N > 0, "N must be > 0");
29+
static_assert(N >= 1, "N must be >= 1");
2930
static_assert(N <= 64, "N must be <= 64");
3031
static_assert(sizeof...(Taps) >= 1, "At least one tap required");
3132

@@ -52,14 +53,12 @@ class FibonacciLFSR_Right {
5253
}
5354

5455
public:
55-
using state_type_t = std::bitset<N>; //!< State type
56+
using storage_t = typename uint_least_for_bits<N>::type; //!< uint8/16/32/64_t
57+
using state_type_t = std::bitset<N>; //!< State type
5658

5759
///@name Constructor
5860
///@{
59-
explicit FibonacciLFSR_Right(const uint64_t seed) noexcept : _state{seed}
60-
{
61-
}
62-
explicit FibonacciLFSR_Right(const state_type_t s) noexcept : _state{s}
61+
explicit FibonacciLFSR_Right(const storage_t seed) noexcept : _state{seed}
6362
{
6463
}
6564
///@}
@@ -73,20 +72,21 @@ class FibonacciLFSR_Right {
7372
//! @brief Gets the state value
7473
template <typename UL = unsigned long,
7574
typename std::enable_if<(sizeof(UL) * CHAR_BIT >= 64), std::nullptr_t>::type = nullptr>
76-
inline uint64_t value() const
75+
inline storage_t value() const
7776
{
78-
return static_cast<uint64_t>(_state.to_ulong());
77+
return static_cast<storage_t>(_state.to_ulong());
7978
}
8079
template <typename UL = unsigned long,
8180
typename std::enable_if<(sizeof(UL) * CHAR_BIT < 64), std::nullptr_t>::type = nullptr>
82-
inline uint64_t value() const
81+
inline storage_t value() const
8382
{
84-
return _state.to_ullong();
83+
return static_cast<storage_t>(_state.to_ullong());
8584
}
8685

8786
//! @brief Shift 1 step (Right). Returns output bit (LSB before shift)
8887
bool step() noexcept
8988
{
89+
// x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
9090
const bool out = _state.test(0); // LSB (output)
9191
const bool fb = taps_xor<Taps...>(_state); // feedback
9292
_state >>= 1; // Shift to right
@@ -141,7 +141,7 @@ class FibonacciLFSR_Right {
141141
*/
142142
template <uint32_t N, uint32_t... Taps>
143143
class FibonacciLFSR_Left {
144-
static_assert(N > 0, "N must be > 0");
144+
static_assert(N >= 1, "N must be >= 1");
145145
static_assert(N <= 64, "N must be <= 64");
146146
static_assert(sizeof...(Taps) >= 1, "At least one tap required");
147147

@@ -157,29 +157,24 @@ class FibonacciLFSR_Left {
157157
static_assert(all_valid<Taps...>::value, "Taps out of range");
158158

159159
protected:
160-
// XOR of taps; bit index is N - Ts (Ts is exponent)
161160
template <uint32_t... Ts>
162161
static bool taps_xor(const std::bitset<N>& s)
163162
{
164163
bool r{};
165164
using swallow = int[];
166-
//(void)swallow{0, (r ^= s.test(N - Ts), 0)...}; // Swallow idiom
167165
(void)swallow{0, (r ^= s.test(Ts - 1), 0)...}; // Swallow idiom
168-
//(void)swallow{0, (r ^= s.test((Ts == N) ? (N - 1) : (N - Ts)), 0)...}; return r;
169166
return r;
170167
}
171168

172169
public:
173-
using state_type_t = std::bitset<N>;
170+
using storage_t = typename uint_least_for_bits<N>::type; //!< uint8/16/32/64_t
171+
using state_type_t = std::bitset<N>; //!< State type
174172

175173
/// @name Constructor
176174
/// @{
177175
explicit FibonacciLFSR_Left(const uint64_t seed) noexcept : _state{seed}
178176
{
179177
}
180-
explicit FibonacciLFSR_Left(const state_type_t s) noexcept : _state{s}
181-
{
182-
}
183178
/// @}
184179

185180
//! @brief Gets the state
@@ -191,15 +186,15 @@ class FibonacciLFSR_Left {
191186
//! @brief Gets the state value
192187
template <typename UL = unsigned long,
193188
typename std::enable_if<(sizeof(UL) * CHAR_BIT >= 64), std::nullptr_t>::type = nullptr>
194-
inline uint64_t value() const
189+
inline storage_t value() const
195190
{
196-
return static_cast<uint64_t>(_state.to_ulong());
191+
return static_cast<storage_t>(_state.to_ulong());
197192
}
198193
template <typename UL = unsigned long,
199194
typename std::enable_if<(sizeof(UL) * CHAR_BIT < 64), std::nullptr_t>::type = nullptr>
200-
inline uint64_t value() const
195+
inline storage_t value() const
201196
{
202-
return _state.to_ullong();
197+
return static_cast<storage_t>(_state.to_ullong());
203198
}
204199

205200
//! @brief Shift 1 step (Left). Returns output bit (MSB before shift)

src/m5_utility/misc.hpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define M5_UTILITY_MISC_HPP
1212

1313
#include <cstdint>
14+
#include <type_traits>
1415

1516
namespace m5 {
1617
namespace utility {
@@ -53,6 +54,47 @@ inline uint16_t reverseBitOrder(const uint16_t u16)
5354
#endif
5455
}
5556

57+
/*!
58+
@class uint_least_for_bits
59+
@brief Gets the smallest unsigned integer type that can store N bit
60+
@tparam N Number of bits
61+
@note using foo_t = uint_least_for_bits<52>::type; // foo_t == uint64_t
62+
*/
63+
template <uint32_t N>
64+
struct uint_least_for_bits {
65+
static_assert(N >= 1, "N must be >= 1");
66+
#ifdef __SIZEOF_INT128__
67+
static_assert(N <= 128, "N must be <= 128");
68+
using type = //
69+
typename std::conditional< //
70+
(N <= 8), uint8_t, //
71+
typename std::conditional< //
72+
(N <= 16), uint16_t, //
73+
typename std::conditional< //
74+
(N <= 32), uint32_t, //
75+
typename std::conditional< //
76+
(N <= 64), uint64_t, //
77+
__uint128_t //
78+
>::type //
79+
>::type //
80+
>::type //
81+
>::type;
82+
#else
83+
static_assert(N <= 64, "N must be <= 64m");
84+
using type = //
85+
typename std::conditional< //
86+
(N <= 8), uint8_t, //
87+
typename std::conditional< //
88+
(N <= 16), uint16_t, //
89+
typename std::conditional< //
90+
(N <= 32), uint32_t, //
91+
uint64_t //
92+
>::type //
93+
>::type //
94+
>::type;
95+
#endif
96+
};
97+
5698
} // namespace utility
5799
} // namespace m5
58100
#endif

test/lfsr.cpp

Lines changed: 75 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,74 @@
1010
#include <gtest/gtest.h>
1111
#include <M5Utility.hpp>
1212
#include <M5Unified.hpp>
13+
#include <random>
1314

1415
using namespace m5::utility;
1516

1617
namespace {
18+
auto rng = std::default_random_engine{};
19+
20+
// x^16 + x^14 + x^13 + x^11 + 1
21+
uint32_t prng_successor32(uint32_t x, uint32_t n)
22+
{
23+
while (n--) {
24+
// 0 16(32-16) 14(32-18) 13(32-19) 11(32-11)
25+
x = (x >> 1) | (((x >> 16) ^ (x >> 18) ^ (x >> 19) ^ (x >> 21)) << 31);
26+
}
27+
return x;
28+
}
29+
30+
uint16_t prng_successor16(uint16_t x, uint32_t n)
31+
{
32+
while (n--) {
33+
// 0(16-16) 14(16-2) 13(16-3) 11(16-5)
34+
x = (x >> 1) | (((x >> 0) ^ (x >> 2) ^ (x >> 3) ^ (x >> 5)) << 15);
35+
}
36+
return x;
37+
}
38+
39+
} // namespace
40+
41+
TEST(Utility, FibonacciLFSR16)
42+
{
43+
{
44+
using LFSR16 = FibonacciLFSR_Right<16, 16, 14, 13, 11>;
45+
46+
constexpr uint16_t seed{0xACE1U};
47+
LFSR16 lfsr{seed};
48+
// uint16_t reg = seed;
49+
// uint16_t bit;
50+
uint16_t p16 = seed;
51+
for (int i = 0; i < 65536; ++i) {
52+
p16 = prng_successor16(p16, 1);
53+
#if 0
54+
bool out = (reg >> 1);
55+
bit = (reg & 0x0001) ^ ((reg & 0x0004) >> 2) ^ ((reg & 0x0008) >> 3) ^ ((reg & 0x0020) >> 5);
56+
reg = (reg >> 1) | (bit << 15);
57+
#endif
58+
lfsr.step();
59+
EXPECT_EQ(lfsr.value(), p16) << i;
60+
}
61+
}
1762
}
1863

19-
using FLL = FibonacciLFSR_Left<8, 4, 1>; // Tapbit // bit 4,7
64+
TEST(Utility, FibonacciLFSR32)
65+
{
66+
{
67+
using LFSR32 = FibonacciLFSR_Right<32, 16, 14, 13, 11>;
68+
69+
constexpr uint32_t seed{0xACE1U};
70+
LFSR32 lfsr{seed};
71+
uint32_t p32 = seed;
72+
for (int i = 0; i < 65536; ++i) {
73+
p32 = prng_successor32(p32, 1);
74+
lfsr.step();
75+
EXPECT_EQ(lfsr.value(), p32) << i;
76+
}
77+
}
78+
}
79+
80+
using FLL = FibonacciLFSR_Left<8, 8, 5>;
2081
class DruagaLFSR : public FLL {
2182
public:
2283
using Base = FLL;
@@ -25,14 +86,13 @@ class DruagaLFSR : public FLL {
2586

2687
bool step() noexcept
2788
{
28-
const bool out = this->_state.test(7); // MSB
29-
const bool fb = !Base::taps_xor_all(this->_state);
30-
this->_state <<= 1; // shift
31-
this->_state.set(0, fb); // Insert into LSB
32-
return out;
33-
}
89+
const bool fb = !Base::taps_xor_all(this->_state); // NOT tap
90+
this->_state <<= 1; // shift
91+
this->_state.set(0, fb); // Insert into LSB
92+
return fb;
93+
};
3494

35-
uint64_t step(uint32_t nbits) noexcept
95+
uint64_t step(const uint32_t nbits) noexcept
3696
{
3797
uint64_t v{};
3898
for (uint32_t i = 0; i < nbits; ++i) v |= (uint64_t)step() << i;
@@ -65,35 +125,6 @@ class DruagaLFSR : public FLL {
65125
}
66126
};
67127

68-
TEST(Utility, FibonacciLFSR)
69-
{
70-
using LFSR16 = FibonacciLFSR_Right<16, 16, 14, 13, 11>;
71-
72-
LFSR16 l(0xACE1U);
73-
uint16_t reg = 0xACE1;
74-
uint16_t bit;
75-
uint16_t first{};
76-
77-
for (int i = 0; i < 65536; ++i) {
78-
// bit 0, 2,3,5
79-
// x^16 + x^(16-2) + x^(16-3) + x^(16-5) + 1
80-
// x^16 + x^14 + x^13 + x^11 + 1
81-
bit = (reg & 0x0001) ^ ((reg & 0x0004) >> 2) ^ ((reg & 0x0008) >> 3) ^ ((reg & 0x0020) >> 5);
82-
reg = (reg >> 1) | (bit << 15);
83-
84-
l.step();
85-
EXPECT_EQ(l.value(), reg);
86-
// M5_LOGI("[%3d]:%04X:%04X", i, reg, (uint16_t)l.value());
87-
88-
if (i == 0) {
89-
first = reg;
90-
}
91-
if (i == 65535) {
92-
EXPECT_EQ(reg, first);
93-
}
94-
}
95-
}
96-
97128
TEST(Utility, DruagaLFSR)
98129
{
99130
{ // 1 cycle
@@ -106,45 +137,45 @@ TEST(Utility, DruagaLFSR)
106137
EXPECT_EQ(d.next64(), 0xFFFFFFFFFFFFFFFFull);
107138
}
108139

109-
{ // 217 cycle
140+
if (1) { // 217 cycle
110141
DruagaLFSR d(0);
111142
std::vector<uint8_t> a1, a2;
112143

113144
for (int i = 0; i < 217; ++i) {
114145
d.step();
115146
a1.push_back((uint8_t)d.value());
116-
// M5_LOGI("[%3d]:%u", i, (uint8_t)d.value());
147+
// M5_LOGI("[%3d]:%u", i, (uint8_t)d.value());
117148
}
118149
for (int i = 0; i < 217; ++i) {
119150
d.step();
120151
a2.push_back((uint8_t)d.value());
121-
// M5_LOGI("[%3d]:%u", i, (uint8_t)d.value());
152+
// M5_LOGI("[%3d]:%u", i, (uint8_t)d.value());
122153
}
123154
// m5::utility::log::dump(a1.data(), a1.size(), false);
124155
// m5::utility::log::dump(a2.data(), a2.size(), false);
125156
EXPECT_EQ(a1, a2);
126157
}
127158

128-
{ // 31 cycle
159+
if (1) { // 31 cycle
129160
DruagaLFSR d(6);
130161
std::vector<uint8_t> a1, a2;
131162

132163
for (int i = 0; i < 31; ++i) {
133164
d.step();
134165
a1.push_back((uint8_t)d.value());
135-
// M5_LOGI("[%3d]:%u", i, (uint8_t)d.value());
166+
// M5_LOGI("[%3d]:%u", i, (uint8_t)d.value());
136167
}
137168
for (int i = 0; i < 31; ++i) {
138169
d.step();
139170
a2.push_back((uint8_t)d.value());
140-
// M5_LOGI("[%3d]:%u", i, (uint8_t)d.value());
171+
// M5_LOGI("[%3d]:%u", i, (uint8_t)d.value());
141172
}
142173
// m5::utility::log::dump(a1.data(), a1.size(), false);
143174
// m5::utility::log::dump(a2.data(), a2.size(), false);
144175
EXPECT_EQ(a1, a2);
145176
}
146177

147-
{ // 7 cycle
178+
if (1) { // 7 cycle
148179
DruagaLFSR d(26);
149180
std::vector<uint8_t> a1, a2;
150181

0 commit comments

Comments
 (0)