Skip to content
Open
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
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,8 @@ list(APPEND SOURCE_FILES
components/stopwatch/StopWatchController.cpp
components/alarm/AlarmController.cpp
components/fs/FS.cpp
components/rng/PCG.cpp

drivers/Cst816s.cpp
FreeRTOS/port.c
FreeRTOS/port_cmsis_systick.c
Expand Down Expand Up @@ -662,6 +664,7 @@ set(INCLUDE_FILES
components/timer/Timer.h
components/stopwatch/StopWatchController.h
components/alarm/AlarmController.h
components/rng/PCG.h
drivers/Cst816s.h
FreeRTOS/portmacro.h
FreeRTOS/portmacro_cmsis.h
Expand Down
86 changes: 86 additions & 0 deletions src/components/rng/PCG.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#include "components/rng/PCG.h"

Pinetime::Controllers::RNG::State Pinetime::Controllers::RNG::Seed(Pinetime::Controllers::RNG::rng_uint s,
Pinetime::Controllers::RNG::rng_uint i) {
rng.state = 0u;
rng.inc = i | 1u;
Generate();
rng.state += s;
Generate();
return rng;
}

Pinetime::Controllers::RNG::State Pinetime::Controllers::RNG::Seed() {
using namespace Pinetime::Controllers;
Pinetime::Controllers::RNG::State new_rng;
new_rng.state = (RNG::rng_uint) Generate() << (sizeof(new_rng.state) * 4) ^ (RNG::rng_uint) Generate();
new_rng.inc = (RNG::rng_uint) Generate() << (sizeof(new_rng.inc) * 4) ^ (RNG::rng_uint) Generate();
return new_rng;
}

// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
// Website: https://www.pcg-random.org/download.html
// See: https://www.apache.org/licenses/GPL-compatibility.html
/*
uint64_t oldstate = rng.state;
// Advance internal state
rng.state = oldstate * 6364136223846793005ULL + (rng.inc | 1);
// Calculate output function (XSH RR), uses old state for max ILP
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
uint32_t rot = oldstate >> 59u;
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
*/
Pinetime::Controllers::RNG::rng_out Pinetime::Controllers::RNG::Generate() {
using namespace Pinetime::Controllers;
// See magic numbers in https://github.com/imneme/pcg-cpp/blob/master/include/pcg_random.hpp
constexpr uint32_t tbits = sizeof(rng.state) * 8;
constexpr uint32_t sbits = sizeof(rng) * 8;
constexpr uint32_t obits = sizeof(RNG::rng_out) * 8;

constexpr rng_uint multiplier = tbits >= 64 ? 1442695040888963407ULL : tbits >= 32 ? 747796405U : tbits >= 16 ? 12829U : 141u;

constexpr uint32_t opbits = sbits - 5 >= 64 ? 5u : // 128 bits -> 5
sbits - 4 >= 32 ? 4u
: // 64 bits -> 4
sbits - 3 >= 16 ? 3u
: // 32 bits -> 3
sbits - 2 >= 8 ? 2u
: // 16 bits -> 2
sbits - 1 >= 1 ? 1u
: // 8 bits -> 1
0u;

auto oldstate = rng.state;
rng.state = oldstate * multiplier + (rng.inc | 1);
// 64 bits of state, 32 output (64 - 5 = 59, 32 - 5 = 27 and floor((5+32)/2) = 18)
// 2^5 = 32*
// output = rotate<s3,s2,s1>(state ^ (state >> s1)) >> s2, state >> s3)
// 32 bits of state, 16 output (32 - 4 = 28, 16 - 4 = 12, and floor((4 + 16)/2) = 10)
// 2^4 = 16*
constexpr uint32_t s3 = tbits - opbits;
constexpr uint32_t s2 = obits - opbits;
constexpr uint32_t s1 = (obits + 5) / 2;

constexpr RNG::rng_out mask = (1 << opbits) - 1;
RNG::rng_out xorshifted = ((oldstate >> s1) ^ oldstate) >> s2;
rng_out rot = oldstate >> s3;
// rotate
return (xorshifted >> rot) | (xorshifted << ((-rot) & mask));
};

// Lemire's Method (slight rewrite) [0, range)
Pinetime::Controllers::RNG::rng_out Pinetime::Controllers::RNG::GenerateBounded(Pinetime::Controllers::RNG::rng_out range) {
using namespace Pinetime::Controllers;
rng_uint m;
RNG::rng_out t = (-range) % range;
RNG::rng_out l;

do {
RNG::rng_out x = Generate();
m = RNG::rng_uint(x) * RNG::rng_uint(range);
l = RNG::rng_out(m);
} while (l < t);

return m >> (sizeof(RNG::rng_out) * 8);
};
44 changes: 44 additions & 0 deletions src/components/rng/PCG.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once
#include <cstdint>
#include <FreeRTOS.h>
#include <timers.h>
#include "components/motion/MotionController.h"

namespace Pinetime {
namespace Controllers {
struct RNG {
using rng_uint = uint32_t;
using rng_uint2 = uint64_t;
using rng_out = uint16_t;

struct pcg_random_t {
rng_uint state = {};
rng_uint inc = {};
};

using State = pcg_random_t;
State rng = {};

State Seed(rng_uint s, rng_uint i);
// Generate another RNG struct with data generated via this one
State Seed();
// Produces an unsigned result within the full range of the data type
rng_out Generate();
// Produces an unsigned result within [0, range)
rng_out GenerateBounded(rng_out range);

RNG() : rng() {};

RNG& operator=(const State& pcg_state) {
rng = pcg_state;
return *this;
};

RNG(State pcg_state) {
rng = pcg_state;
};

~RNG() = default;
};
}
}
2 changes: 2 additions & 0 deletions src/displayapp/Controllers.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace Pinetime {
class Timer;
class MusicService;
class NavigationService;
class RNG;
}

namespace System {
Expand Down Expand Up @@ -53,6 +54,7 @@ namespace Pinetime {
Pinetime::Components::LittleVgl& lvgl;
Pinetime::Controllers::MusicService* musicService;
Pinetime::Controllers::NavigationService* navigationService;
Pinetime::Controllers::RNG* prngController;
};
}
}
1 change: 1 addition & 0 deletions src/displayapp/DisplayApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,7 @@ void DisplayApp::PushMessageToSystemTask(Pinetime::System::Messages message) {
void DisplayApp::Register(Pinetime::System::SystemTask* systemTask) {
this->systemTask = systemTask;
this->controllers.systemTask = systemTask;
this->controllers.prngController = &(systemTask->prngController);
}

void DisplayApp::Register(Pinetime::Controllers::SimpleWeatherService* weatherService) {
Expand Down
18 changes: 6 additions & 12 deletions src/displayapp/screens/Dice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,10 @@ namespace {

Dice::Dice(Controllers::MotionController& motionController,
Controllers::MotorController& motorController,
Controllers::Settings& settingsController)
Controllers::Settings& settingsController,
Controllers::RNG& prngController)
: motorController {motorController}, motionController {motionController}, settingsController {settingsController} {
std::seed_seq sseq {static_cast<uint32_t>(xTaskGetTickCount()),
static_cast<uint32_t>(motionController.X()),
static_cast<uint32_t>(motionController.Y()),
static_cast<uint32_t>(motionController.Z())};
gen.seed(sseq);
rng = prngController.Seed();

lv_obj_t* nCounterLabel = MakeLabel(&jetbrains_mono_bold_20,
LV_COLOR_WHITE,
Expand Down Expand Up @@ -79,8 +76,7 @@ Dice::Dice(Controllers::MotionController& motionController,
lv_obj_align(dCounter.GetObject(), dCounterLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
dCounter.SetValue(6);

std::uniform_int_distribution<> distrib(0, resultColors.size() - 1);
currentColorIndex = distrib(gen);
currentColorIndex = rng.GenerateBounded(resultColors.size());

resultTotalLabel = MakeLabel(&jetbrains_mono_42,
resultColors[currentColorIndex],
Expand Down Expand Up @@ -157,12 +153,10 @@ void Dice::Refresh() {
void Dice::Roll() {
uint8_t resultIndividual;
uint16_t resultTotal = 0;
std::uniform_int_distribution<> distrib(1, dCounter.GetValue());

lv_label_set_text(resultIndividualLabel, "");

if (nCounter.GetValue() == 1) {
resultTotal = distrib(gen);
resultTotal = rng.GenerateBounded(dCounter.GetValue()) + 1;
if (dCounter.GetValue() == 2) {
switch (resultTotal) {
case 1:
Expand All @@ -175,7 +169,7 @@ void Dice::Roll() {
}
} else {
for (uint8_t i = 0; i < nCounter.GetValue(); i++) {
resultIndividual = distrib(gen);
resultIndividual = rng.GenerateBounded(dCounter.GetValue()) + 1;
resultTotal += resultIndividual;
lv_label_ins_text(resultIndividualLabel, LV_LABEL_POS_LAST, std::to_string(resultIndividual).c_str());
if (i < (nCounter.GetValue() - 1)) {
Expand Down
13 changes: 9 additions & 4 deletions src/displayapp/screens/Dice.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
#include "displayapp/screens/Screen.h"
#include "displayapp/widgets/Counter.h"
#include "displayapp/Controllers.h"
#include "components/rng/PCG.h"
#include "Symbols.h"

#include <array>
#include <random>
#include <cstdint>

namespace Pinetime {
namespace Applications {
Expand All @@ -16,7 +17,8 @@ namespace Pinetime {
public:
Dice(Controllers::MotionController& motionController,
Controllers::MotorController& motorController,
Controllers::Settings& settingsController);
Controllers::Settings& settingsController,
Controllers::RNG& prngController);
~Dice() override;
void Roll();
void Refresh() override;
Expand All @@ -29,7 +31,7 @@ namespace Pinetime {
lv_task_t* refreshTask;
bool enableShakeForDice = false;

std::mt19937 gen;
Controllers::RNG rng;

std::array<lv_color_t, 3> resultColors = {LV_COLOR_YELLOW, LV_COLOR_MAGENTA, LV_COLOR_AQUA};
uint8_t currentColorIndex;
Expand All @@ -54,7 +56,10 @@ namespace Pinetime {
static constexpr const char* icon = Screens::Symbols::dice;

static Screens::Screen* Create(AppControllers& controllers) {
return new Screens::Dice(controllers.motionController, controllers.motorController, controllers.settingsController);
return new Screens::Dice(controllers.motionController,
controllers.motorController,
controllers.settingsController,
*controllers.prngController);
};

static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) {
Expand Down
7 changes: 4 additions & 3 deletions src/displayapp/screens/Twos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

using namespace Pinetime::Applications::Screens;

Twos::Twos() {
Twos::Twos(Pinetime::Controllers::RNG& prngController) {
rng = prngController.Seed();

struct colorPair {
lv_color_t bg;
Expand Down Expand Up @@ -86,9 +87,9 @@ bool Twos::placeNewTile() {
return false; // game lost
}

int random = rand() % nEmpty;
int random = rng.GenerateBounded(nEmpty);

if ((rand() % 100) < 90) {
if (rng.GenerateBounded(100) < 90) {
grid[emptyCells[random] / nCols][emptyCells[random] % nCols].value = 2;
} else {
grid[emptyCells[random] / nCols][emptyCells[random] % nCols].value = 4;
Expand Down
9 changes: 6 additions & 3 deletions src/displayapp/screens/Twos.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "displayapp/screens/Screen.h"
#include "displayapp/Controllers.h"

#include "displayapp/screens/Dice.h"

namespace Pinetime {
namespace Applications {
struct TwosTile {
Expand All @@ -14,7 +16,7 @@ namespace Pinetime {
namespace Screens {
class Twos : public Screen {
public:
Twos();
Twos(Controllers::RNG& prngController);
~Twos() override;

bool OnTouchEvent(TouchEvents event) override;
Expand All @@ -29,6 +31,7 @@ namespace Pinetime {
static constexpr int nRows = 4;
static constexpr int nCells = nCols * nRows;
TwosTile grid[nRows][nCols];
Controllers::RNG rng;
unsigned int score = 0;
void updateGridDisplay();
bool tryMerge(int newRow, int newCol, int oldRow, int oldCol);
Expand All @@ -42,8 +45,8 @@ namespace Pinetime {
static constexpr Apps app = Apps::Twos;
static constexpr const char* icon = "2";

static Screens::Screen* Create(AppControllers& /*controllers*/) {
return new Screens::Twos();
static Screens::Screen* Create(AppControllers& controllers) {
return new Screens::Twos(*controllers.prngController);
};

static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) {
Expand Down
7 changes: 6 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "components/heartrate/HeartRateController.h"
#include "components/stopwatch/StopWatchController.h"
#include "components/fs/FS.h"
#include "components/rng/PCG.h"
#include "drivers/Spi.h"
#include "drivers/SpiMaster.h"
#include "drivers/SpiNorFlash.h"
Expand Down Expand Up @@ -361,8 +362,12 @@ int main() {
}

systemTask.Start();

nimble_port_init();
// ble_ll functions should probably be called after ble_ll_init which is called from nimble_port_init
Pinetime::Controllers::RNG::State prngBleInit;
ble_ll_rand_data_get((uint8_t*) &prngBleInit, sizeof(prngBleInit));
prngBleInit.state ^= (decltype(prngBleInit.state)) xTaskGetTickCount();
systemTask.prngController.rng = prngBleInit;

vTaskStartScheduler();

Expand Down
3 changes: 2 additions & 1 deletion src/systemtask/SystemTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi,
spiNorFlash,
heartRateController,
motionController,
fs) {
fs),
prngController {} {
}

void SystemTask::Start() {
Expand Down
5 changes: 5 additions & 0 deletions src/systemtask/SystemTask.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "components/stopwatch/StopWatchController.h"
#include "components/alarm/AlarmController.h"
#include "components/fs/FS.h"
#include "components/rng/PCG.h"
#include "touchhandler/TouchHandler.h"
#include "buttonhandler/ButtonHandler.h"
#include "buttonhandler/ButtonActions.h"
Expand Down Expand Up @@ -128,6 +129,10 @@ namespace Pinetime {
Pinetime::Controllers::ButtonHandler& buttonHandler;
Pinetime::Controllers::NimbleController nimbleController;

public:
Pinetime::Controllers::RNG prngController;

private:
static void Process(void* instance);
void Work();
bool isBleDiscoveryTimerRunning = false;
Expand Down
Loading