1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! The FM25V02, a 256-kilobit ferroelectric memory on a serial interface: the parameter store of a
//! flight controller, read and written a word at a time behind a chip select.
//!
//! Written against Cypress document 001-84494 Rev. \*G, "FM25V02, 256-Kbit (32 K × 8) Serial (SPI) F-RAM",
//! May 2, 2017. Every section and table below is that document's. It is the first chip behind a chip
//! select, and the select is the interface's business: this chip sees `select`, words, and `deselect`,
//! which is the whole of what a chip on such a bus sees.
//!
//! The part is simpler than the serial flash and EEPROM it replaces, and the datasheet says why on its
//! first page: `NoDelay`™ writes, "no page register is needed and any number of sequential writes may be
//! performed". There is no busy flag to poll and no page to wrap. What there is, is a command state
//! machine, a write-enable latch with three ways to clear it, and a device ID a driver recognises before
//! it trusts a byte. The array and the status register are its whole account of itself; a test asks
//! [`Fm25v02::stored`] what landed rather than the chip what it heard.
//!
//! | Register or section | Behaviour | Source | Test |
//! |---|---|---|---|
//! | Opcodes | `WREN 06h`, `WRDI 04h`, `RDSR 05h`, `WRSR 01h`, `READ 03h`, `FSTRD 0Bh`, `WRITE 02h`, `SLEEP B9h`, `RDID 9Fh`; the first byte after the select falls | Table 1 | `an_unknown_opcode_is_ignored_until_the_select_rises` |
//! | Device ID | Nine bytes: six of `7Fh`, then `C2h`, then `22h 00h` — family, density, sub, revision. Read-only, and what a driver compares before it trusts the chip | Table 6, Figure 16 | `the_device_id_is_nine_bytes_the_driver_recognises` |
//! | `WREN` | "The FM25V02 will power up with writes disabled. The WREN command must be issued before any write operation." Sets the latch; `WEL` reads set | WREN, p. 6 | `a_write_without_the_latch_is_ignored` |
//! | `WEL` clearing | "The WEL bit will be automatically cleared on the rising edge of CS following a WRDI, a WRSR, or a WRITE operation." The operation, not a byte landing: a `WRITE` the block protection refused clears it too. A `RDSR` leaves it standing, and a `WRSR` cannot set or clear it: "Attempting to write the WEL bit in the Status Register has no effect" | WREN, p. 6; Table 3 | `the_latch_clears_on_the_rising_edge_after_a_write_a_wrdi_or_a_wrsr`, `a_refused_write_still_clears_the_latch` |
//! | Status register | `WPEN` bit 7, `BP1` bit 3, `BP0` bit 2, `WEL` bit 1; bits 0 and 4 to 6 "are non-writable and always return '0' upon read". Bit 0 is the busy bit other parts have: "the F-RAM writes in real-time and is never busy" | Table 2, Table 3 | `the_status_register_writes_only_its_three_bits` |
//! | `RDSR` | "Will return one byte with the contents of the Status Register" | RDSR, p. 8 | `the_status_register_repeats_while_clocked` |
//! | Block protection | `BP1:BP0` — `00` none, `01` `6000h` to `7FFFh`, `10` `4000h` to `7FFFh`, `11` all. Nonvolatile | Table 4, Table 3 | `each_block_protect_range_refuses_writes` |
//! | `WRITE` | Two address bytes carry `A14` to `A0`, "the upper bit of the two-byte address is ignored"; bytes land as they arrive; "if the last address of 7FFFh is reached, the counter will roll over to 0000h"; the rising edge of `CS` ends it | Write Operation, p. 8 | `a_read_answers_what_was_written`, `the_address_rolls_over_at_the_top`, `the_upper_address_bit_is_ignored` |
//! | A write reaching protection | "The automatic address increment stops and all the subsequent data bytes received for write will be ignored by the device" | Write Operation, note, p. 8 | `a_burst_write_stops_at_a_protected_block` |
//! | `READ` | Two address bytes then data on every following eight clocks, "the SI input is ignored during read data bytes", incrementing and rolling over the same way | Read Operation, p. 9 | `a_read_answers_what_was_written`, `mosi_is_ignored_during_read_data` |
//! | `FSTRD` | `READ` with one dummy byte between the address and the data, "provided for code compatibility with serial flash devices" | Fast Read Operation, p. 9 | `a_fast_read_skips_one_dummy_byte` |
//! | `SLEEP` | Entered "when the SLEEP opcode B9h is clocked in and a rising edge of CS is applied"; asleep, "the SCK and SI pins are ignored and SO will be HI-Z"; the next falling edge of `CS` wakes it, and it does not "respond to an opcode within the wakeup period", `tREC`, 400 µs at most | Sleep Mode, p. 11; AC table | `sleep_is_entered_on_the_rising_edge_and_answers_nothing`, `a_command_inside_the_wake_up_time_is_ignored` |
//! | Power-up | "Not accessible for a tPU time after power-up", 250 µs from `VDD(min)` to the first `CS` low | Power Up to First Access, p. 6; AC table | `nothing_answers_before_the_power_up_time` |
//!
//! # What is not, and why
//!
//! | Departure | Reason |
//! |---|---|
//! | The `WP` pin | Not wired; nothing on the board drives it. Treated as high, so `WPEN` has nothing to act on and the status register is writable whenever `WEL` is; the rows of Table 5 that need `WP` low are unreachable |
//! | The `HOLD` pin | Not wired. "The HOLD pin can be used to interrupt a serial operation without aborting it"; with nothing to pull it, no operation pauses |
//! | What `RDSR` and `RDID` answer past their last byte | The datasheet shows one byte and nine and says nothing after. The status byte repeats and the ID answers zero past the ninth, and this row is here so a reader knows those are choices |
//! | An opcode Table 1 does not list | Not specified. Ignored until `CS` rises |
//! | The 40 MHz ceiling on `SCK` | The seam carries words, not a clock, so the chip cannot see the rate; a controller run faster than the part allows is the controller's to refuse |
//! | A sixteen-bit frame | The chip is eight bits wide and the controller may frame sixteen; the low byte is taken and the departure is the controller's, which frames what the firmware asked |
//! | Endurance and retention | 10¹⁴ cycles and 151 years. A counter would be honest and would never be read |
//! | What the memory holds at power-up | The datasheet is silent; the array is nonvolatile and holds whatever it held. It starts at zero here, and carrying contents across runs is its own change |
use cratePeer;
use crateDeviceContext;
use crateVirtualNanos;
/// Thirty-two kilobytes, "256-Kbit (32 K × 8)".
pub const SIZE: usize = 32 * 1024;
/// The address bits the part decodes, `A14` to `A0`; the upper bit of the two address bytes is ignored.
const ADDRESS_MASK: u16 = 0x7FFF;
/// Table 1.
const WREN: u8 = 0x06;
const WRDI: u8 = 0x04;
const RDSR: u8 = 0x05;
const WRSR: u8 = 0x01;
const READ: u8 = 0x03;
const FSTRD: u8 = 0x0B;
const WRITE: u8 = 0x02;
const SLEEP: u8 = 0xB9;
const RDID: u8 = 0x9F;
/// Table 6: six bytes of the JEDEC continuation code, the Cypress (Ramtron) identifier in bank 7, then the
/// family, density, sub and revision codes of this part.
pub const DEVICE_ID: = ;
/// Table 2.
const WEL: u8 = 1 << 1;
const BP0: u8 = 1 << 2;
const BP1: u8 = 1 << 3;
const WPEN: u8 = 1 << 7;
/// The bits a `WRSR` lands: bits 0 and 4 to 6 are fixed and `WEL` is the latch's, Table 3.
const WRITABLE: u8 = WPEN | BP1 | BP0;
/// `tPU`, power-up to the first `CS` low, AC table.
pub const POWER_UP: VirtualNanos = VirtualNanos;
/// `tREC`, recovery from sleep mode, AC table.
pub const RECOVERY: VirtualNanos = VirtualNanos;
/// Where a select cycle is, from the opcode to the rising edge that ends it.
/// One FM25V02.
///
/// Its state is the array, the status register, and the latch's pending clear; where it is in a command
/// is the phase, which every falling edge of the select resets. Time enters only at the select: whether
/// the part is ready to answer is decided when `CS` falls, which is when the datasheet decides it too.