firmware-base/vendor/sming/Sming/samples/Basic_ProgMem/app/TestProgmem.cpp
2026-01-28 16:42:43 +01:00

228 lines
5.9 KiB
C++

/*
* test_progmem.h
*
* @author: 18 Sep 2018 - mikee47 <mike@sillyhouse.net>
*/
#include "TestProgmem.h"
#include <Platform/Timers.h>
#include "FlashData.h"
#include <FlashString/Array.hpp>
#include <FlashString/Vector.hpp>
#include <cinttypes>
#ifndef __INT32
#define __INT32
#endif
// Note: contains nulls which won't display, but will be stored
#define DEMO_TEST_TEXT "This is a flash string -\0Second -\0Third -\0Fourth."
DEFINE_FSTR_LOCAL(demoFSTR1, DEMO_TEST_TEXT);
DEFINE_FSTR_LOCAL(demoFSTR2, DEMO_TEST_TEXT);
DEFINE_PSTR_LOCAL(demoPSTR1, DEMO_TEST_TEXT);
static const char demoText[] = DEMO_TEST_TEXT;
// Write a string literal (stored in flash, naturally)
#define _FPUTS(_s) out.write(_F(_s), sizeof(_s) - 1)
/*
* Sum every character in a buffer - a standard operation for speed testing to ensure optimisation
* doesn't mess things up
*/
static unsigned sumBuffer(volatile const char* buffer, size_t length)
{
unsigned sum = 0;
for(unsigned i = 0; i < length; ++i)
sum += buffer[i];
return sum;
}
void testPSTR(Print& out)
{
//
out.println(_F("Simple PSTR example using stack\n"));
//
out.println(F("Simple PSTR example using heap (String object)\n"));
// Note that characters after first nul won't be shown ...
out.print("> demoPSTR1 (print char*): ");
{
LOAD_PSTR(s, demoPSTR1)
out << '"' << s << '"' << endl;
}
// ... now they will: note buf will be aligned up to next dword boundary though
out.print("> demoPSTR1 (write): ");
LOAD_PSTR(buf, demoPSTR1);
out.print('"');
out.write(buf, sizeof(buf) - 1);
out.println('"');
m_printHex("hex", buf, sizeof(buf)); // show the actual data
// PSR defined in another module - we don't know its size! so have to guess,
// and of course nuls don't get included
out.print("> externalPSTR1 (print char*): ");
char buf2[100];
strncpy_P(buf2, externalPSTR1, sizeof(buf2));
buf2[sizeof(buf2) - 1] = '\0';
out << '"' << buf2 << '"' << endl;
//
out.print("> PSTR_ARRAY: ");
PSTR_ARRAY(psarr, DEMO_TEST_TEXT);
out.print('"');
out.write(psarr, sizeof(__pstr__psarr) - 1);
out.println('"');
m_printHex("hex", psarr, sizeof(__pstr__psarr));
//
out.println("< testPSTR() end\n");
}
void testFSTR(Print& out)
{
// Demonstrate direct reading of a flashstring
char buffer[1024];
auto len = demoFSTR1.readFlash(0, buffer, sizeof(buffer));
out.print("demoFSTR1.read(): ");
out.write(buffer, len);
out.println();
// Implicit conversion of FlashString to String; entire content (including nuls) loaded
out.print("> demoFSTR1 (print String): ");
out.print('"');
out.print(demoFSTR1);
out.println('"');
// Load the FlashString into a stack buffer
out.print("> demoFSTR1 (print char*): ");
LOAD_FSTR(test, demoFSTR1);
out.print('"');
out.print(test);
out.println('"');
out.print("> demoFSTR1 (write): ");
out.print('"');
out.write(test, demoFSTR1.length());
out.println('"');
// FlashString defined in another module
out.print("> externalFSTR1 (print): ");
out.print('"');
out.print(externalFSTR1);
out.println('"');
// Define a FlashString and load it into a stack buffer in one operation (non-reusable!)
out.print("> FSTR_ARRAY: ");
FSTR_ARRAY(fsarr, DEMO_TEST_TEXT);
out.print('"');
out.write(fsarr, sizeof(fsarr) - 1);
out.println('"');
// Example of array or custom data usage
out.print("> demoArray1 : ");
static constexpr const struct {
FSTR::ObjectBase object;
char data[5];
} demoArray1 PROGMEM = {{5}, {1, 2, 3, 4, 5}};
auto& arr = demoArray1.object.as<FSTR::Array<uint8_t>>();
out << arr << endl;
// Test equality operators
#define TEST(_test) out.printf(_F("%s: %s\n"), (_test) ? _F("PASS") : _F("FAIL"), _F(#_test));
TEST(demoFSTR1 == demoFSTR2)
{
LOAD_PSTR(s, demoPSTR1);
TEST(demoFSTR1 != s)
}
TEST(String(demoFSTR1) == demoFSTR2)
TEST(demoFSTR1 == String(demoFSTR2))
#undef TEST
// FSTR table
DEFINE_FSTR_LOCAL(fstr1, "Test string #1");
DEFINE_FSTR_LOCAL(fstr2, "Test string #2");
DEFINE_FSTR_VECTOR_LOCAL(table, FlashString, &fstr1, &fstr2);
// Table entries may be accessed directly as they are word-aligned
out.println(_F("FSTR tables -"));
out << _F(" fstr1 = '") << table[0] << endl;
out << _F(" fstr1.length() = ") << table[0].length() << endl;
out << _F(" entries = ") << table.length() << endl;
out.println("< testFSTR() end\r\n");
}
/*
* Run a load of iterations for PSTR/FSTR options to illustrate relative performance.
*/
void testSpeed(Print& out)
{
const unsigned iterations = 200;
out.printf("Speed tests, %u iterations, times in microseconds\n", iterations);
ElapseTimer timer;
uint32_t baseline, elapsed;
_FPUTS("Baseline test, read string in RAM...");
unsigned sum = 0;
timer.start();
for(unsigned i = 0; i < iterations; ++i) {
sum += sumBuffer(demoText, sizeof(demoText));
}
baseline = timer.elapsedTime();
out << "Elapsed: " << baseline << ", sum " << sum << endl;
#define END() \
elapsed = timer.elapsedTime(); \
out << "Elapsed: " << elapsed << " (baseline + " << elapsed - baseline << "), sum " << sum << endl;
_FPUTS("Load PSTR into stack buffer...");
sum = 0;
timer.start();
for(unsigned i = 0; i < iterations; ++i) {
LOAD_PSTR(buf, demoPSTR1);
sum += sumBuffer(buf, sizeof(buf));
}
END()
_FPUTS("Load PSTR into String...");
sum = 0;
timer.start();
for(unsigned i = 0; i < iterations; ++i) {
String s(demoFSTR1.data());
sum += sumBuffer(s.c_str(), s.length() + 1);
}
END()
_FPUTS("Load FlashString into stack buffer...");
sum = 0;
timer.start();
for(unsigned i = 0; i < iterations; ++i) {
LOAD_FSTR(buf, demoFSTR1);
sum += sumBuffer(buf, sizeof(buf));
}
END()
_FPUTS("Load FlashString into String...");
sum = 0;
timer.start();
for(unsigned i = 0; i < iterations; ++i) {
String s(demoFSTR1);
sum += sumBuffer(s.c_str(), s.length() + 1);
}
END()
}
void testProgmem(Print& out)
{
testPSTR(out);
testFSTR(out);
testSpeed(out);
}