Skip to content

Commit f8247d2

Browse files
authored
Merge pull request #92 from CURocketEngineering/fix/eraseSectorTiming
Fix/erase sector timing
2 parents 7a4a582 + 8586f56 commit f8247d2

7 files changed

Lines changed: 282 additions & 25 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
.DS_Store
88
build/
99
*.csv
10+
.codex

AGENTS.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Avionics is our modular C++ (Arduino-core compatible) library used for all of ou
3131
## Hard Rules
3232

3333
1. Never include Arduino headers directly (`<Arduino.h>`, etc.) from Avionics code.
34-
2. Always include `hal/ArduinoHAL.h` for Arduino-facing APIs.
34+
2. Always include `ArduinoHAL.h` from `hal/` for Arduino-facing APIs.
3535
3. Keep host-native tests buildable; avoid adding dependencies that only exist on embedded targets.
3636
4. Maintain `include/` and `src/` parity for non-header-only modules.
3737
5. Keep changes compatible with the repo's C++ standard (`-std=c++11` in `platformio.ini`).
@@ -94,6 +94,7 @@ python -m http.server --directory build/doxygen 8000
9494
- Deploy target: `gh-pages` branch
9595
- Published folder: `build/doxygen`
9696
- If code changes affect public behavior/API, update doc comments and relevant Markdown docs in the same change.
97+
- Don't use a return type of `void` in doc comments; just omit the `@return` line if there is no return value.
9798

9899
## Data-Driven Tests
99100

@@ -105,8 +106,9 @@ python -m http.server --directory build/doxygen 8000
105106

106107
1. Prefer small, focused patches.
107108
2. If behavior changes, update or add tests in `test/` to cover it.
108-
3. Preserve public API names unless the task explicitly asks for a breaking change.
109-
4. Keep docs in sync when changing module behavior:
109+
3. If you are patching a bug, add a regression test that fails without the fix and passes with it.
110+
4. Preserve public API names unless the task explicitly asks for a breaking change.
111+
5. Keep docs in sync when changing module behavior:
110112
- top-level `README.md` for high-level behavior,
111113
- `docs/*.md` for detailed formats/protocols.
112114

include/data_handling/DataSaverSPI.h

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "data_handling/DataSaver.h"
77
#include <array>
88
#include <cstdlib>
9+
#include <limits>
910

1011

1112
constexpr uint32_t kMetadataStartAddress = 0x000000; // Start writing metadata at the beginning of flash
@@ -60,7 +61,8 @@ class DataSaverSPI : public IDataSaver {
6061
*
6162
* @param dataPoint The data point to save
6263
* @param name An 8-bit “identifier” for the data point (could be a sensor ID)
63-
* @return int 0 on success; non-zero on error
64+
* @return int 0 on success, 1 when writes are blocked by post-launch state,
65+
* and -1 on write/buffer error.
6466
*/
6567
int saveDataPoint(const DataPoint& dataPoint, uint8_t name) override;
6668

@@ -69,12 +71,16 @@ class DataSaverSPI : public IDataSaver {
6971
* @param timestamp_ms Timestamp in milliseconds to record.
7072
* @note When to use: emitted internally when gaps exceed
7173
* timestampInterval_ms_, or explicitly in tests.
74+
* @return int 0 on success, 1 when writes are blocked by post-launch state,
75+
* and -1 on non-post-launch write/buffer error.
7276
*/
7377
int saveTimestamp(uint32_t timestamp_ms);
7478

7579
/**
7680
* @brief Initialize the flash chip and metadata.
7781
* @note When to use: call during setup before any saveDataPoint usage.
82+
* @return true when flash is initialized and writable for logging.
83+
* @return false when initialization fails or chip is already in post-launch mode.
7884
*/
7985
virtual bool begin() override;
8086

@@ -116,6 +122,7 @@ class DataSaverSPI : public IDataSaver {
116122
*
117123
* The rocket may not be recovered for several hours, this prevents the cool launch data
118124
* from being overwitten with boring laying-on-the-ground data.
125+
* @param launchTimestamp_ms Timestamp at which launch was detected.
119126
*/
120127
void launchDetected(uint32_t launchTimestamp_ms);
121128

@@ -146,6 +153,7 @@ class DataSaverSPI : public IDataSaver {
146153

147154
/**
148155
* @brief Returns the last timestamp that was actually written to flash.
156+
* @return Last persisted timestamp in milliseconds.
149157
*/
150158
uint32_t getLastTimestamp() const {
151159
return lastTimestamp_ms_;
@@ -154,15 +162,24 @@ class DataSaverSPI : public IDataSaver {
154162
/**
155163
* @brief Returns the last DataPoint that was written (not necessarily
156164
* including timestamp, just the data chunk).
165+
* @return Copy of the last cached data point.
157166
*/
158167
DataPoint getLastDataPoint() const {
159168
return lastDataPoint_;
160169
}
161170

171+
/**
172+
* @brief Returns the launch-protected address computed during launch detection.
173+
* @return Address in flash used as post-launch protection boundary.
174+
*/
162175
uint32_t getLaunchWriteAddress() const {
163176
return launchWriteAddress_;
164177
}
165178

179+
/**
180+
* @brief Returns the next flash address where a full page will be written.
181+
* @return Next write address in flash.
182+
*/
166183
uint32_t getNextWriteAddress() const {
167184
return nextWriteAddress_;
168185
}
@@ -178,6 +195,7 @@ class DataSaverSPI : public IDataSaver {
178195
/**
179196
* @brief Returns whether the flash chip is in post-launch mode
180197
* without updating the post-launch mode flag or reading from flash.
198+
* @return true when in post-launch mode; otherwise false.
181199
*/
182200
bool quickGetPostLaunchMode() {
183201
return this->postLaunchMode_;
@@ -211,11 +229,18 @@ class DataSaverSPI : public IDataSaver {
211229
/**
212230
* @brief Helper to write a block of bytes to flash at the current
213231
* write address and advance that pointer.
232+
* @param data Pointer to bytes to write.
233+
* @param length Number of bytes to write.
234+
* @return true on successful write and pointer advance; otherwise false.
214235
*/
215236
bool writeToFlash(const uint8_t* data, size_t length);
216237

217238
/**
218239
* @brief Helper to read a block of bytes from flash (updates read pointer externally).
240+
* @param readAddress Input/output flash address. Advanced by `length` on success.
241+
* @param buffer Output byte buffer.
242+
* @param length Number of bytes to read.
243+
* @return true on successful read and pointer advance; otherwise false.
219244
*/
220245
bool readFromFlash(uint32_t& readAddress, uint8_t* buffer, size_t length);
221246

@@ -228,6 +253,7 @@ class DataSaverSPI : public IDataSaver {
228253
/**
229254
* @brief Returns the current buffer index
230255
* Useful for testing
256+
* @return Number of bytes currently buffered.
231257
*/
232258
size_t getBufferIndex() const {
233259
return bufferIndex_;
@@ -236,20 +262,89 @@ class DataSaverSPI : public IDataSaver {
236262
/**
237263
* @brief Returns the current buffer flush count
238264
* Useful for testing
265+
* @return Number of successful page flushes.
239266
*/
240267
uint32_t getBufferFlushes() const {
241268
return bufferFlushes_;
242269
}
243270

271+
/**
272+
* @brief Returns whether writes have been stopped by post-launch protection.
273+
* @return true if chip-full post-launch protection latch is set; otherwise false.
274+
*/
244275
bool getIsChipFullDueToPostLaunchProtection() const {
245276
return isChipFullDueToPostLaunchProtection_;
246277
}
247278

279+
/**
280+
* @brief Returns whether startup detected existing post-launch mode metadata.
281+
* @return true if rebooted in post-launch mode; otherwise false.
282+
*/
248283
bool getRebootedInPostLaunchMode() const {
249284
return rebootedInPostLaunchMode_;
250285
}
251286

287+
#ifdef UNIT_TEST
288+
/**
289+
* @brief Test-only helper to override internal post-launch state.
290+
* Resets in-memory state first so the injected state is
291+
* self-consistent and does not depend on prior test writes.
292+
* @param nextWriteAddress_in Next write address to inject.
293+
* @param launchWriteAddress_in Launch-protected address to inject.
294+
* @param postLaunchMode_in Post-launch mode flag to inject.
295+
*/
296+
void setPostLaunchStateForTest(uint32_t nextWriteAddress_in,
297+
uint32_t launchWriteAddress_in,
298+
bool postLaunchMode_in) {
299+
clearInternalState();
300+
nextWriteAddress_ = nextWriteAddress_in;
301+
launchWriteAddress_ = launchWriteAddress_in;
302+
postLaunchMode_ = postLaunchMode_in;
303+
isChipFullDueToPostLaunchProtection_ = false;
304+
}
305+
#endif
306+
252307
private:
308+
/**
309+
* @brief Normalizes an address into the data region when crossing flash end.
310+
* @param address Candidate flash address.
311+
* @return `address` when in range, otherwise `kDataStartAddress`.
312+
*/
313+
uint32_t normalizeDataAddress(uint32_t address) const;
314+
315+
/**
316+
* @brief Checks if a sector is protected by post-launch rules.
317+
* @param sectorNumber Sector index to evaluate.
318+
* @return true when sector contains `launchWriteAddress_` and post-launch mode is active.
319+
* @return false otherwise.
320+
*/
321+
bool isProtectedLaunchSector(uint32_t sectorNumber) const;
322+
323+
/**
324+
* @brief Outcome of attempting to erase a sector before writing into it.
325+
*/
326+
enum class SectorEraseResult {
327+
kErased,
328+
kProtectedSectorLatched,
329+
kFlashEraseFailed
330+
};
331+
332+
/**
333+
* @brief Erases a sector for writing and latches post-launch protection if blocked.
334+
* @param sectorNumber Sector index to erase.
335+
* @return SectorEraseResult::kErased on successful erase.
336+
* @return SectorEraseResult::kProtectedSectorLatched when sector is
337+
* post-launch protected (and chip-full is latched).
338+
* @return SectorEraseResult::kFlashEraseFailed when flash erase fails.
339+
*/
340+
SectorEraseResult eraseSectorForWriteAndLatchOnProtection(uint32_t sectorNumber);
341+
342+
/**
343+
* @brief Checks if the upcoming write window would violate post-launch protection.
344+
* @return true when writing must stop and chip-full protection is latched.
345+
* @return false when write may proceed.
346+
*/
347+
bool shouldStopForPostLaunchWindow();
253348

254349
/**
255350
* @brief Flushes the buffer to flash.
@@ -272,10 +367,20 @@ class DataSaverSPI : public IDataSaver {
272367

273368
// Overloaded functions to add data to the buffer from a Record_t or TimestampRecord_t
274369
// More efficient than callling addDataToBuffer with each part of the record
370+
/**
371+
* @brief Adds a 5-byte record payload to the page buffer.
372+
* @param record Pointer to packed record bytes.
373+
* @return int 0 on success; -1 on flush/buffer error.
374+
*/
275375
int addRecordToBuffer(Record_t * record) {
276376
return addDataToBuffer(reinterpret_cast<const uint8_t*>(record), 5);
277377
}
278378

379+
/**
380+
* @brief Adds a 5-byte timestamp record payload to the page buffer.
381+
* @param record Pointer to packed timestamp record bytes.
382+
* @return int 0 on success; -1 on flush/buffer error.
383+
*/
279384
int addRecordToBuffer(TimestampRecord_t * record) {
280385
return addDataToBuffer(reinterpret_cast<const uint8_t*>(record), 5);
281386
}
@@ -287,6 +392,10 @@ class DataSaverSPI : public IDataSaver {
287392
// If the flight computer boots and is already in post launch mode, do not write to flash.
288393
// Calling clearPostLaunchMode() will allow writing to flash again after a reboot.
289394
bool rebootedInPostLaunchMode_ = false;
395+
396+
// Tracks which sector has already been pre-erased for the next boundary write.
397+
// UINT32_MAX means "no prepared sector".
398+
uint32_t preparedSectorNumber_ = std::numeric_limits<uint32_t>::max();
290399
};
291400

292401
#endif // DATA_SAVER_SPI_H

0 commit comments

Comments
 (0)