4

I am using an STM8 microcontroller. Since the vector table is in flash memory, I need to write to flash memory in order to create and use the vector table.

Since addresses of vector tables are fixed, my code writes the same values to the same memory addresses of the flash memory on every startup.

Since there is no flash memory erase call in the code, I think that after the first write process it won't do anything anymore.

Would it reduce the life of the flash memory if I write the same values to the same flash memory addresses on every startup/reset, without erasing?

ocrdu
  • 8,705
  • 21
  • 30
  • 42
  • 1
    The flash controller should take care of it for you. So you better worry about overall flashing cycles, or not worry at all. In my career, I have never reached the point where I could not flash an MCU because it's flash "wearied out" – Eugene Sh. Jun 02 '22 at 18:10
  • Thank you. So on the back plan it doesn't write anything at all I guess. I was worried because in internet it says a couple thousand times for flash lifespan. And if I reset my device for some amount of times during the day as it is right now and if it still writes things, I would reach that point in a couple of years. – Günkut Ağabeyoğlu Jun 02 '22 at 18:16
  • 4
    @EugeneSh., a microcontroller's internal flash memory has no intelligent flash controller nor automatic wear-leveling, to my knowledge. It just erases the whole thing and then writes in order from beginning to end, according to your programmer instructions during erase and upload, and the addresses specified by your linker script. – Gabriel Staples Jun 02 '22 at 21:12
  • 3
    @EugeneSh. I know lots of RL anecdotes about people having written flash/eeprom wear-out bugs, it's real. Personally I once managed to permanently damage flash in a similar manner by using the wrong prescaler clock calculation in a flash driver. Also, there's nothing in the average MCU which will take care of it for you, the responsibility lies on the programmer. – Lundin Jun 03 '22 at 06:52
  • 8
    Why in the world does your application write to the vector table "_in every startup_"? For a start without an erase cycle it will not work. The ROM vector table would normally be statically determined by the toolchain linker and start-up code. You are worrying about a problem you shouldn't have. If you think you do, then you are doing something ill-advised and unnecessary. – Clifford Jun 03 '22 at 07:49
  • you can have the vectors in the table point to ram and then just change them in ram...very common thing to do...there should be no real use case for changing the vectors in flash. – old_timer Nov 20 '22 at 02:23

5 Answers5

9

The physics, electronics, and programming of how flash memory really works

English ambiguity:

From this chat, I learned that English has some ambiguity. "Write 7 to byte #1", and "write byte #1 to 7", can mean the exact same thing in English despite having seemingly opposite directions. I am a programmer and think in code sometimes, so If you have a write operation to write a decimal 7 to address 0x01, I would write it like this in code:

write(0x01, 7);  // write(address, value)

In my wording below I would describe that as: "write byte 1 to 7", although more-clear English might say "write a 7 to byte 1".

So, when I say "write a binary 0b11111111 (all bits at HIGH voltage) to 0b00000000 (all bits at LOW voltage)", I mean this:

// some address already has a `0b11111111` in it, meaning you previously did
// this:
erase(address); // erase 4 KiB starting at address
// Now you are going to try to write this same address to a `0b00000000`
// instead, like this:
write8bits(address, 0b00000000);  // write(address, value)

Another wording might be: "try to change or program a byte which contains binary 0b11111111 (all bits at HIGH voltage) to 0b00000000 (all bits at LOW voltage)".

As you read my descriptions below, just remember I'm thinking in code, and speaking kind of like I write code.

Quick answers and summary:

Since addresses of vector tables are fixed my code writes same values to same memory address of the flash on every startup.

Study what I wrote below. No new damage occurs after the initial erase operation and first write operation.

My question is would it reduce the life of flash if I write to same flash memory address on every startup/reset without erasing?

If you're writing the same exact thing to the same exact flash memory every time, with no erase cycles in between, no new damage occurs after the first write. Only the first write would actually discharge any bits (capacitors), thereby causing damage just that one time.

Here's the big take-away: write (frequently called "program" in datasheets) can only discharge. It has such a precise granularity that it can discharge single bits. Erase can only charge, and it has a horrible granularity. It can only charge sectors (ex: 4 KiB of flash memory in the Winbond W25Q128JV chip below), blocks (ex: 16 sectors, or 64 KiB in the Winbond W25Q128JV chip), or the whole chip (ex: 16 MiB in the Winbond W25Q128JV chip) at once. Sectors are generally many kilobytes. A "write" operation can never charge bits, only an "erase" operation can.

Here are some useful quotes of mine from the comments below this answer. Remember too: whether 0b11111111 represents all bits at HIGH voltage or at LOW voltage is rather arbitrary. The Winbond W25Q128JV chip below says that all bits at HIGH voltage (freshly erased) will be represented as 0b11111111, but in most of my answer I use the opposite convention, so just pay attention to that too as I talk:

Here is my exact quote:

But, if you try to write a binary 0b11111111 (all bits at LOW voltage) to 0b00000000 (all bits at HIGH) voltage, nothing happens! Writing can NOT charge the cells, only an erase operation can! You end up with 0b11111111 still. No damage to your cells occurred. You got a no-op.

That quote is correct as-written.

But, whether a charged cell is a 0 or a 1 is rather arbitrary, and is up to the manufacturer of the flash memory [although, I now realize that erasing to a 0b11111111 is probably more common than erasing to a 0b00000000]. So, if your flash memory uses an opposite convention, then this would also be correct as-written:

If you try to write a binary 0b00000000 (all bits at LOW voltage) to 0b11111111 (all bits at HIGH) voltage, nothing happens! Writing can NOT charge the cells, only an erase operation can! You end up with 0b00000000 still. No damage to your cells occurred. You got a no-op.

That is also correct as-written.

Details:

My question is would it reduce the life of flash if I write to same flash memory address on every startup/reset without erasing?

Let me add some insight here.

My crude understanding below gets the general principles, usage, & behaviors across, but if you really want to learn more about the physics of it, you might start by studying this article here: https://en.wikipedia.org/wiki/Flash_memory.

Here is my understanding of how flash memory really works:

  1. Erase: Flash memory is memory stored in microscopic capacitors. An "erase" charges all cells to a HIGH voltage, which, depending on your mcu, may be considered a binary zero (0) or a binary one (1). For the rest of my analysis below, however, let's assume a bit which is charged to a HIGH voltage is considered a binary zero (0). An "erase" cycle damages the atomic structure of the capacitor cell wall as electrons blast through it in the charging process. Erase cycles are done on entire flash memory "pages", or large chunks of memory usually many kilobytes in size, all at once. An erase is an expensive operation, both in terms of energy used and time, since a bunch of charge pumps must pump up the tiny mcu voltage to a high voltage which can charge all cells of an entire flash memory page at once in one large flood of energy to charge the capacitor bank.
  2. Write: A "write" can be done on a granular word (ex: 4 bytes) or byte level (granularity depends on the mcu), rather than on a large flash memory page level which is usually kilobytes. It is fast since all it has to do is short a single bit or several bits to ground to discharge the capacitors to change a HIGH voltage (0) to a LOW voltage (1). "Writing" a 1 to a bit simply discharges that bit from HIGH (0) to LOW (1). It only changes anything if the bit was "erased" previously to charge it to a HIGH (0) in the first place. Discharging a bit from HIGH to LOW is also a destructive process since it blasts electrons through the capacitor wall as they discharge since you shorted that cell to ground, removing matter at an atomic level as the electrons blast through. "Writing" a bit to a 0 (HIGH voltage) does absolutely nothing. It is a no-op (no operation). It is the erase cycle that sets that bit to a 0 (HIGH). It's like this:
    1. Writing a flash 0 (HIGH) --> 1 (LOW) = a charged bit is discharged by shorting it to ground; this is a molecularly destrutive operation.
    2. Writing a flash 0 (HIGH) --> 0 (HIGH) = nothing happens (writing to HIGH is a no-op); the bit is already charged, and writing cannot charge a bit; only erase can. The bit remains HIGH (0).
    3. Writing a flash 1 (LOW) --> 0 (HIGH) = nothing happens (writing to HIGH is a no-op); writing cannot charge a bit; only erase can. The bit remains LOW (1).
    4. Writing a flash 1 (LOW) --> 1 (LOW) = the bit is shorted to ground to discharge it to LOW (1), but since it was already discharged, nothing happens. There are no electrons wanting to move, so this is a non-descructive operation, as the "damage" already occurred previously when this bit was discharged from a 0 (HIGH) to a 1 (LOW).

Make sense?

So, if you try to write a binary byte 0b00000000 (all bits at HIGH voltage) to 0b11111111 (all bits at LOW voltage) then it discharges all bits by shorting them to ground, bringing their voltage from HIGH to LOW and you end up with 0b11111111. This damages the capacitor cells. If you try to write that byte to 0b11111111 again, it shorts all of their cells to ground again, but nothing happens since the cells were already discharged! No new damage occurs.

But, if you try to write a binary 0b11111111 (all bits at LOW voltage) to 0b00000000 (all bits at HIGH) voltage, nothing happens! Writing can NOT charge the cells, only an erase operation can! You end up with 0b11111111 still. No damage to your cells occurred. You got a no-op.

Therefore, damage occurs when you discharge a bit, and when you charge a bit. Charging a bit is done through an erase cycle, so you can just count how many times you erased as an estimate of the number of "damage cycles" you've imposed upon the flash memory capacitor cells.

  • Standard flash memory (erasable in groups of large "pages") is generally rated up to 10,000 "write-to-1 (LOW; via discharge) / erase-to-0 (HIGH, via charge)" cycles. Refer to your datasheet for your exact number.
  • EEPROM (erasable at a very granular byte or word level) flash memory is generally more-robust and can handle 100,000 write/erase cycles. Refer to your datasheet for your exact number.

Now, with this knowledge, is your "writing" really doing anything at all without an erase first?

Are you really causing any damage? If you cause it during the first write, will subsequent writes cause more damage?

You should have the tools to know those answers now.

Real-life example: Winbond W25Q128JV 16 MiB flash memory chip ($5 on a breakout board from Adafruit)

I just consulted the datasheet for the Winbond W25Q128JV 16 MiB flash memory chip, and the terms it uses are "Page Program" (pg 36 as listed at the bottom of a PDF page), and "Sector Erase", "Block Erase", or "Chip Erase". The smallest quantity of bytes you can "program" at once is 1 byte, and the smallest quantity of bytes you can erase at once is 4 KiB. So, their terms are "program" and "erase". I simply chose to say "write" and "erase". Their erased state is all 1s. (0b11111111 in each byte). Therefore, "programming" can only discharge bits from 1 (a HIGH voltage level) to 0 (a LOW voltage level), and "erasing" can only charge bits from 0 (a LOW voltage level) to 1 (a HIGH voltage level). "Programming" can never change a bit that is a 0 back to a 1, no matter how hard you try. :)

Here is the "block diagram", or flash memory layout, of the Winbond W25Q128JV, from pg 10 of its datasheet. You can see in it that this particular chip has 256 blocks (numbered 0 to 255), where each block is 16 sectors (numbered 0 to 15), and each sector is 4 KiB. So, that makes the whole chip 4 KiB x 16 x 256 = 16384 KiB. Divide that by 1024 and you get 16 MiB. The smallest granularity you can program ("write") to this chip is 1 byte via the "Page Program" (0x02) instruction on p36, and the smallest granularity you can erase is 1 sector of 4 KiB via the "Sector Erase" (0x20) instruction on p38.

enter image description here

So, if you have 4 bytes that are erased to 0xFFFFFFFF (all 1s), and then you "program" them to 0xDEADBEEF (0b 11011110 10101101 10111110 11101111), they will now contain 0xDEADBEEF (0b 11011110 10101101 10111110 11101111). If you then "program" them to 0xBEEFDEAD (0b 10111110 11101111 11011110 10101101), however, those 4 bytes will NOT contain that. Rather, they will change to 0x9EAD9EAD (0b 10011110 10101101 10011110 10101101), as shown here:

// look for all of the attempted changes from 0 to 1. 
// Programming can't do that. Only erasing can. So, they
// remain 0s. 

// 1. Starting point after "erasing" is 0xffffffff.
// 2. Attempt to program to 0xdeadbeef, and you get it!
// 3. Attempt to then program to 0xbeefdead withOUT erasing again first, 
//    and you do NOT get it! 
// 4. You end up with 0x9ead9ead instead.

11111111 11111111 11111111 11111111  ffffffff // 1.  
11011110 10101101 10111110 11101111  deadbeef // 2. 
10111110 11101111 11011110 10101101  beefdead // 3. 
-------- -------- -------- --------  --------
10011110 10101101 10011110 10101101  9ead9ead // 4.

The code in C to attempt the above might look like this:

uint32_t addr = 0;

erase(addr); // erase 4 KiB starting at address 
             // `addr` to `0xffffffff` (all binary 1s)
program32bits(addr, 0xdeadbeef); // addr now contains `0xdeadbeef`
program32bits(addr, 0xbeefdead); // addr now contains `0x9ead9ead`
Gabriel Staples
  • 1,525
  • 4
  • 18
  • 26
  • 1
    Whilst it probably does not matter, and is certainly off-topic, the _physics_ of flash memory is not as you describe. A bit is composed of a floating gate transistor rather than _capacitor_. Capicitors are a feature of DRAM and SRAM _volatile_ memory technologies. – Clifford Jun 03 '22 at 08:15
  • 3
    Yes. But your answer is correct either way - in case the erased value is 1 then you can only program 0, so it works the same as you describe. – Lundin Jun 03 '22 at 07:45
  • @Clifford, that's great information. MOSFET gates are known for their very high capacitance, which means one can call the gate itself a micro "capacitor", no? And, being a transistor allows them to be individually and quickly discharged through the source to ground connection (I'm assuming N-channel here), thereby providing a means to quickly and individually discharge any given bit, which again, sounds like it stores its charge in the gate capacitance. – Gabriel Staples Jun 03 '22 at 15:18
  • @Lundin, when you say "default" value, are you referring to the "charged" HIGH voltage value after an "erase" operation? – Gabriel Staples Jun 03 '22 at 07:34
  • 1
    Erasing is not performed by "discharging" to ground; it requires a large reverse voltage to "free" the trapped electrons. It is a different electronic phenomena than capacitance. – Clifford Jun 03 '22 at 21:15
  • 2
    _"To erase a NOR flash cell (resetting it to the "1" state), a large voltage of the opposite polarity is applied between the CG and source terminal, pulling the electrons off the FG through quantum tunneling."_ (https://en.wikipedia.org/wiki/Flash_memory). – Clifford Jun 03 '22 at 21:21
  • @Clifford, you meant to say "writing"`, not "erasing", right? Erasing is the process of putting electrons in (I called this "charging"), and writing is the process of letting them come out (I called this "discharging to ground")...I think. – Gabriel Staples Jun 03 '22 at 21:17
  • "You end up with 0b11111111 still." did you mean to say "You end up with 0b00000000 still."? – Maple Nov 19 '22 at 02:51
  • @Maple, no. I double-checked and my wording is correct as-is, for the convention I'm using. If you try to write all bits from LOW (`0b11111111` in the convention I'm using) to HIGH (`0b00000000` in the convention I'm using), then _nothing happens_, as only an _erase_ operation, _not_ a _write_ operation, can charge bits back to HIGH voltage levels. So, you end up with all bits still being in a LOW voltage state (`0b11111111` still, in the convention I'm using). – Gabriel Staples Nov 19 '22 at 03:37
  • **Here's the big take-away:** _write_ can only _discharge_. It has such a precise granularity that it can discharge single bits. _Erase_ can only _charge_, and it has a horrible granularity. It can only charge _pages_, which are generally many kilobytes at once. _A write operation can never charge bits._ – Gabriel Staples Nov 19 '22 at 03:41
  • @GabrielStaples but that is NOT the wording that you actually use: "if you try to write a binary 0b11111111 to 0b00000000 ... You end up with 0b11111111" – Maple Nov 19 '22 at 19:41
  • @Maple, here is my _exact_ quote: "But, if you try to write a binary `0b11111111` (all bits at LOW voltage) to `0b00000000` (all bits at HIGH) voltage, **nothing happens!** Writing can NOT charge the cells, only an _erase_ operation can! You end up with `0b11111111` still. No damage to your cells occurred. You got a no-op. " That quote is correct as-written. – Gabriel Staples Nov 19 '22 at 20:02
  • @Maple, But, whether a charged cell is a `0` or a `1` is rather arbitrary, and is up to the manufacturer of the flash memory. So, if your flash memory uses an opposite convention, then this would also be correct as-written: "if you try to write a binary `0b00000000` (all bits at LOW voltage) to `0b11111111` (all bits at HIGH) voltage, **nothing happens!** Writing can NOT charge the cells, only an _erase_ operation can! You end up with `0b00000000` still. No damage to your cells occurred. You got a no-op." That is also correct as-written. – Gabriel Staples Nov 19 '22 at 20:04
  • @Maple, I added these new quotes to my answer. I hope this helps clarify things. I've reviewed my wording again, and it is correct as-written. – Gabriel Staples Nov 19 '22 at 20:08
  • @GabrielStaples "write A _to_ B" means you have B and trying to replace it with A. If you want to say the opposite, correct wording would be "rewrite A with B", or "write B instead of A" or anything else less confusing. I am not correcting your description of memory operation, I am just saying that the wording you use is not precise. – Maple Nov 19 '22 at 20:20
  • @Maple, I just consulted [the datasheet for the Winbond W25Q128JV 16 MiB flash memory chip](https://www.winbond.com/hq/support/documentation/levelOne.jsp?__locale=en&DocNo=DA00-W25Q128JV), and the terms it uses are "Page Program" (pg 36 as listed at the bottom of a PDF page), and "Sector Erase", "Block Erase", or "Chip Erase". The smallest quantity of bytes you can "program" at once is 1 byte, and the smallest quantity of bytes you can erase at once is 4 KiB. So, their terms are "program" and "erase". I simply chose to say "write" and "erase". Their erased state is all `1`s. (`0b11111111`). – Gabriel Staples Nov 20 '22 at 07:03
  • @Maple, is saying "program" less confusing than saying "write"? I don't understand your confusion. Feel free to move this to a chat if you want to help me understand your perspective about terms not being precise. I don't understand what you mean. – Gabriel Staples Nov 20 '22 at 07:09
  • Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/140757/discussion-between-maple-and-gabriel-staples). – Maple Nov 20 '22 at 18:16
6

Let's address the x-y problem.

Why do you need to write to the vector table? For me that's huge red flag, foreseeing getting bricked devices with RMA labels on them. Because the power brown-out during writing, flash erasing is slow and costs energy.

Typically the vector table is part of the program, written during programming, and doesn't change. Because the functions it refers to won't move around.

If you need to remap stuff, why not use the vector table remapping function?

Jeroen3
  • 21,976
  • 36
  • 73
  • I think an interesting question to ask first would be if OP means "__Interrupt__ Vector Table". Because "vector table" can mean many different things, for example some graphic coordinates. Having said that, writing same values at same address on every startup still makes no sense. – Maple Nov 19 '22 at 03:00
3

My question is would it reduce the life of flash if I write to same flash memory address on every startup/reset without erasing?

In general, writing exactly the same value should not reduce the lifespan. But as has been observed empirically (see comments below) things on the physical side are not always as clear as they should be.

It also depends on the implementation of the write routine, i.e. on your "operating system". It might determine that the write is unnecessary and won't perform it or it might always erase/write.

A rule of thumb value for FLASH erase/write is 50 k. (The manufacturer only guarantees 100 erase/write cycles on this data sheet).

On the manufacturer's page I did not find any mentioning of integrated wear-leveling, neither for FLASH memory nor for EEPROM, see https://www.st.com/en/microcontrollers-microprocessors/stm8-8-bit-mcus.html

  • Program memory: 8-Kbyte Flash memory; data retention 20 years at 55 °C after 100 cycles
  • RAM: 1 Kbyte
  • Data memory: 128-byte true data EEPROM; endurance up to 100 k write/erase cycles

The following page stresses that you should use wear-leveling techniques: https://embedded-lab.com/blog/continuing-stm8-microcontroller-expedition/11/

Though flash memories are primarily intended for storing application codes, it is still possible to use them just like EEPROMs using In-Application Programming (IAP). However, it is important to check that by mistake, we don’t write in those areas where application code reside. IAP can also be used for upgrading application firmware Over-The-Air (OTA).

Avoid frequent writes/erases to increase memory life cycles. If needed to write/erase data frequently use wear-leveling technique.

Franck
  • 139
  • 3
  • I believe this answer to be incorrect. If you're writing the same exact thing to the same exact flash memory every time, with no erase cycles in between, _no new damage occurs after the first write._ Only the first write would actually discharge any bits (capacitors), thereby causing damage just that one time. See my full answer on [how flash memory works here](https://stackoverflow.com/a/72482314/4561887). – Gabriel Staples Jun 02 '22 at 21:18
  • 1
    @GabrielStaples I can say for sure that this is not _necessarily_ true. When attempting to change the contents of flash in an NEC 78Kos MCU I found that writing to the same location multiple times caused adjacent bits to change. Not 'damage' as such, but not good for data integrity. – Bruce Abbott Jun 07 '22 at 08:07
  • @BruceAbbott Amazingly this is similar to the pattern for a row hammer attack (https://en.wikipedia.org/wiki/Row_hammer) although it usually refers to DRAM. – Franck Jun 07 '22 at 09:18
2

It's not really clear to me why you need to write to the vector table each and every reset. Assuming some manner of bootloader with vector indirection, then:

Before writing to any form of flash, memcmp the already present data/code there with what you were about to write. If it is identical, then don't do a thing.

Writing to an address without making sure is has been erased first is certainly wrong. Generally erased flash has the value of "all ones" 0xFF (though some parts use "all zeroes" as the default state, see Why do most of the non-volatile memories have logical 1 as the default state?). Assuming 1 is the default state, the process of writing involves turning ones into zeroes and this is what wears the flash. But it also means that when writing, you can only write zeroes. Which is why writing multiple times to the same cell without erasing it doesn't make sense.

Example:

  • Erase some 16 bit flash chunk, we get value 0xFFFF.
  • We write the value 0xF0F0, we get 0xF0F0.
  • We write the value 0xFAFA, we get 0xF0F0 still because you can't turn zeroes into ones without erasing. So this is a severe bug.

Also a general note is that MCU manufacturers specify number of erase cycles (as well as data retention) in the full temperature range, typically the industrial temperature range -40° to +85° (or wider yet for automotive). But if your product spends most of it's life far from the extreme ranges in this interval, it will work for many more cycles than specified.

Lundin
  • 17,577
  • 1
  • 24
  • 67
  • 2
    Not sure about STM8, but STM32 flash controller will not allow you to program a _word_ that is not fully erased, so you cannot change a bit from 1 to 0 if any other bit if the word is already zero. This can catch you out if you intend to exploit this common feature if flash memory. – Clifford Jun 03 '22 at 08:01
  • 1
    I think you missed my point - it was not about erase state or erase blocks; but never mind, it is not important. The elephant in the room w.r.t. this question is perhaps why the vector table in ROM is "written" in any event? Looks like an X-Y problem. – Clifford Jun 03 '22 at 09:00
  • @Clifford Yeah of course, my example is simplified for some generic MCU. Both STM8 and STM32 will require far larger chunks to be erased. (Also iirc some STM32 do default to "all zeroes" though most of them are "all ones". Been ages since I worked with STM8 but I _think_ it had "all ones" too.) – Lundin Jun 03 '22 at 08:33
  • 1
    @Clifford, I think I see what you're saying in your original comment: it's simply that if you attempt a write on a word, STM8 or STM32 will first ensure all bits of the word about to be written to are already all `1`s. If so, it proceeds with the write. If not, it means that word isn't erased, so it does not proceed with the write. Note: they may actually use a bitfield, extra bit per word, or some flag registers or something instead of actually reading all bits in the word-to-be-written first, but the end result remains the same: you're saying it won't even try to write if it isn't erased. – Gabriel Staples Jun 10 '22 at 05:19
0

Let's be clear now, and define exactly the meaning of a "program/erase cycle". It's hard to find consensus on this topic.

It seems agreed upon that when a sector of a flash is erased (let's use an Infineon S25FL, part 4K sectors of 256 byte pages), this uses up a hit point on all 32768 bits of the sector regardless of whether they were already 1's. Thus there's 100K minus one program/erase cycles remaining for each of the bits. Or is that even wrong, and only the bits that were fallen and resurrected lose a hit point?

It's been said by a few (hard to find even this said) that when one writes to an empty freshly erased page, knocking down some bits, this uses up one of the hit points for the bit(s) knocked down - and even less frequently, it's said the ones still standing also lose a hit point because there's really three states:

(E) ERASED (-hit 1 point for all bits)

(1) Written, but not knocked down. Can be written again, knocking it down (-hit point for all bits). In other words, an erased page of FF's will still consume a cycle even if FF's are rewritten to the page.

(0) Written, knocked down (-hit point). Can go from (E) to 0, and use just two hit points for those bits.

Thus, it seems I could use up three hit points for every bit if (E)->1->0 is done, or just two hit points if (E)->0 is done.

(E)->1->0 is the same hit point damage as (E)->0.

Rewriting the 1, after it's left the erased state, doesn't do anything ... (E)->1->1->1->1 is either one or two hit points, depending on whether there's an (E) state separate from a (1) state.

Obviously rewriting a 0 to a 0 doesn't do anything.