| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- #pragma once
- // Diagnostic-fill harness, Pattern A (byte/row ordering) - see the
- // gxepd2-diag-fill-spec memory. Instead of the uniform 0xA5 fill, it fills
- // the 1bpp plane buffer with a positional TAG, records the exact wire-byte
- // stream (trace.h recording), and inverts the tag to recover the driver's
- // buffer->wire row permutation (identity / reversed / line_order /
- // unknown). Host-only, no hardware.
- //
- // Scope: 1bpp byte-preserving writeImage path (mono, and the per-plane
- // bursts of a BW+accent panel). Packed panels that re-pack 1bpp planes into
- // 2bpp/4bpp codes in writeImage do NOT preserve byte identity, so Pattern A
- // there needs the native (writeNative) path or address-sequence analysis -
- // a documented follow-on, see the spec memory.
- //
- // Two fills:
- // ROW_TAG: each row r -> byte[0]=(r>>8)&0xFF, byte[1]=r&0xFF, rest 0.
- // Group the plane burst into rows of bpr; [hi,lo] of each group
- // recovers that wire-row's source row -> the permutation.
- // COL_TAG: byte[c]=c&0xFF across every row -> each wire row should read
- // 0,1,2,... confirming within-row order is identity (catches a
- // byte-stride/offset bug - the shape of the 583 smear).
- #include <cstdint>
- #include <cstdio>
- #include <cstring>
- #include <vector>
- #include "spy/Arduino.h"
- #include "spy/SPI.h"
- #include "trace.h"
- template<typename Driver> struct DiagPeek : Driver {
- using Driver::Driver;
- int busy_level() const { return this->_busy_level; }
- };
- // Pull the plane-sized data bursts out of the recorded (is_cmd,byte) stream:
- // maximal runs of consecutive data bytes whose length == plane_bytes.
- inline std::vector<std::vector<uint8_t>> extract_plane_bursts(uint32_t plane_bytes) {
- std::vector<std::vector<uint8_t>> bursts;
- std::vector<uint8_t> run;
- auto flush = [&]() {
- if (run.size() == plane_bytes) {
- // Skip all-uniform bursts (e.g. a writeScreenBuffer prime) - they
- // carry no tag, only the tagged writeImage burst is of interest.
- bool uniform = true;
- for (uint8_t v : run)
- if (v != run[0]) {
- uniform = false;
- break;
- }
- if (!uniform)
- bursts.push_back(run);
- }
- run.clear();
- };
- for (const auto &ev : Trace::record) {
- if (ev.first) { // command byte -> ends any data run
- flush();
- } else {
- run.push_back(ev.second);
- }
- }
- flush();
- return bursts;
- }
- // true if most data bytes are 0xFF rather than 0x00 - the driver inverted
- // the plane (our tag is mostly 0x00 bytes), so decode with ^0xFF.
- inline bool burst_is_inverted(const std::vector<uint8_t> &b) {
- uint32_t zeros = 0, ones = 0;
- for (uint8_t v : b) {
- if (v == 0x00)
- zeros++;
- else if (v == 0xFF)
- ones++;
- }
- return ones > zeros;
- }
- template<typename Driver> void analyze_row_tag(const char *name) {
- const uint16_t W = Driver::WIDTH, H = Driver::HEIGHT;
- const uint32_t plane_bytes = (uint32_t) W * H / 8;
- const uint32_t bpr = (uint32_t) W / 8;
- if (bpr < 2) {
- printf(" [row-tag] skipped: bpr<2 (needs 2-byte row tag)\n");
- return;
- }
- auto *black = new uint8_t[plane_bytes]();
- auto *color = new uint8_t[plane_bytes]();
- for (uint16_t r = 0; r < H; r++) {
- black[r * bpr + 0] = color[r * bpr + 0] = (r >> 8) & 0xFF;
- black[r * bpr + 1] = color[r * bpr + 1] = r & 0xFF;
- }
- DiagPeek<Driver> d(10, 11, 12, 13);
- Trace::set_dc_pin(11);
- Trace::last_busy_release_value = d.busy_level() == HIGH ? LOW : HIGH;
- d.selectSPI(SPI, SPISettings());
- d.init(0);
- d.writeImage(black, color, 0, 0, W, H); // prime: clears _initial_write (its screen-buffer prime is untracked)
- Trace::start_record();
- d.writeImage(black, color, 0, 0, W, H); // tagged write, now with no priming burst
- Trace::stop_record();
- delete[] black;
- delete[] color;
- auto bursts = extract_plane_bursts(plane_bytes);
- printf(" [row-tag] %s %ux%u: bpr=%u, plane_bytes=%u, plane bursts found=%zu\n", name, W, H, (unsigned) bpr,
- (unsigned) plane_bytes, bursts.size());
- for (size_t p = 0; p < bursts.size(); p++) {
- const auto &data = bursts[p];
- bool inv = burst_is_inverted(data);
- uint8_t x = inv ? 0xFF : 0x00;
- std::vector<uint32_t> recovered(H);
- for (uint16_t g = 0; g < H; g++)
- recovered[g] = (uint32_t) ((data[g * bpr] ^ x) << 8) | (data[g * bpr + 1] ^ x);
- bool identity = true, reversed = true, interleave = true;
- for (uint16_t g = 0; g < H; g++) {
- if (recovered[g] != g)
- identity = false;
- if (recovered[g] != (uint32_t) (H - 1 - g))
- reversed = false;
- uint32_t expect = (g % 2 == 0) ? (g / 2) : (H - 1 - (g / 2));
- if (recovered[g] != expect)
- interleave = false;
- }
- const char *verdict = identity ? "SEQUENTIAL (identity)"
- : reversed ? "REVERSED rows"
- : interleave ? "ROW_INTERLEAVE (top/bottom pairs)"
- : "UNKNOWN permutation";
- printf(" plane %zu: invert=%s -> %s\n", p, inv ? "yes" : "no", verdict);
- printf(" first rows:");
- for (uint16_t g = 0; g < 6 && g < H; g++)
- printf(" %u", recovered[g]);
- printf(" | last rows:");
- for (uint16_t g = (H > 6 ? H - 6 : 0); g < H; g++)
- printf(" %u", recovered[g]);
- printf("\n");
- }
- }
- template<typename Driver> void analyze_col_tag(const char *name) {
- const uint16_t W = Driver::WIDTH, H = Driver::HEIGHT;
- const uint32_t plane_bytes = (uint32_t) W * H / 8;
- const uint32_t bpr = (uint32_t) W / 8;
- auto *black = new uint8_t[plane_bytes]();
- auto *color = new uint8_t[plane_bytes]();
- for (uint16_t r = 0; r < H; r++)
- for (uint32_t c = 0; c < bpr; c++)
- black[r * bpr + c] = color[r * bpr + c] = c & 0xFF;
- DiagPeek<Driver> d(10, 11, 12, 13);
- Trace::set_dc_pin(11);
- Trace::last_busy_release_value = d.busy_level() == HIGH ? LOW : HIGH;
- d.selectSPI(SPI, SPISettings());
- d.init(0);
- d.writeImage(black, color, 0, 0, W, H); // prime (untracked)
- Trace::start_record();
- d.writeImage(black, color, 0, 0, W, H); // tagged write
- Trace::stop_record();
- delete[] black;
- delete[] color;
- auto bursts = extract_plane_bursts(plane_bytes);
- for (size_t p = 0; p < bursts.size(); p++) {
- const auto &data = bursts[p];
- bool inv = burst_is_inverted(data);
- uint8_t x = inv ? 0xFF : 0x00;
- // Within-row order is identity iff every row reads 0,1,2,...,bpr-1 (mod 256).
- bool ok = true;
- uint16_t bad_row = 0, bad_col = 0;
- for (uint16_t g = 0; g < H && ok; g++)
- for (uint32_t c = 0; c < bpr; c++)
- if ((uint8_t) (data[g * bpr + c] ^ x) != (uint8_t) (c & 0xFF)) {
- ok = false;
- bad_row = g;
- bad_col = c;
- break;
- }
- if (ok)
- printf(" plane %zu: within-row order OK (0..bpr-1 each row)\n", p);
- else
- printf(" plane %zu: within-row MISMATCH at row %u col %u\n", p, bad_row, bad_col);
- }
- }
- template<typename Driver> void run_diag_harness(const char *name) {
- printf("=== %s: DIAGNOSTIC FILL (Pattern A: row/byte ordering) ===\n", name);
- analyze_row_tag<Driver>(name);
- analyze_col_tag<Driver>(name);
- }
|