I'd say you're dreaming. The main problem will be the limited RAM.
In 2004, Eric Beiderman managed to get a kernel booting with 2.5MB of RAM, with a lot of functionality removed.
However, that was on x86, and you're talking about ARM. So I tried to build the smallest possible ARM kernel, for the 'versatile' platform (one of the simplest). I turned off all configurable options, including the ones that you're looking for (USB, WiFi, SPI, I2C), to see how small it would get. Now, I'm just referring to the kernel here, and this does not include any userspace components.
The good news: it will fit in your flash. The resulting zImage is 383204 bytes.
The bad news: with 256kB of RAM, it won't be able to boot:
$ size obj/vmlinux
text data bss dec hex filename
734580 51360 14944 800884 c3874 obj/vmlinux
The .text segment is bigger than your available RAM, so the kernel can't decompress, let alone allocate memory to boot, let alone run anything useful.
One workaround would be to use the execute-in-place support (CONFIG_XIP), if your system supports that (ie, it can fetch instructions directly from Flash). However, that means your kernel needs to fit uncompressed in flash, and 734kB > 700kB. Also, the .data and .bss sections total 66kB, leaving abut 190kB for everything else (ie, all dynamically-allocated data structures in the kernel).
That's just the kernel. Without the drivers you need, or any userspace.
So, yes, you're going to need a bit more RAM.
Some types of non-volatile memory device use error-correcting logic which adds an extra few bits to each programmable chunk (e.g. 5 bits per 16, 6 per 32, 7 per 64, 8 per 128, etc.) Generally the error correction code is chosen so that all bits blank is a valid representation; in some cases, but not all, it may also be chosen such that all bits programmed is also a valid combination. For simplicity, I'll assume a code with which guards each group of 4 bits with 3 guard bits. Compute the three guard bits guard bit as being the xor of either data bits 0+1+3, 0+2+3, or 1+2+3. I'll also assume that a blank word is zero.
The 16 possible code values are thus
3210 ABC 3210 ABC 3210 ABC 3210 ABC
0000 000 0100 011 1000 111 1100 100
0001 110 0101 101 1001 001 1100 010
0010 101 0110 110 1010 010 1110 001
0011 011 0111 000 1011 100 1111 111
When a memory nybble is read, the system can see what bits ABC should be according to the table. If one of the seven bits in the word is misread, the combination of ABC bits that don't match the computed value will indicate which bit was wrong.
Suppose a memory system used the 16-bit code shown above, and one wanted to overwrite a byte value of 1110 (ECC bits 001) with a value of 1000 (ECC bits should be 111). The net effect would be that the system would write 1000 with ECC bits of 001. When the data is read back, the system would see that for a value of 1000, the ECC bits should be 111 but are instead 001. The fact that bits A and B are wrong means bit 0 of the data was wrong and should be flipped; the system would thus read the value as 1001 (whose ECC is correctly 001).
In most cases, there should be enough flexibility in the design of an error-correcting code to permit both all-bits-clear and all-bits-set to be regarded as valid combinations. Some systems do not do so, however. If an error-correcting code would require an all-bits-programmed word to have two or more of the ECC bits blank, then an attempt to obliterate a word which has those bits programmed would likely visibly fail; attempts to program many other values would likely yield a state which was only one bit error away from failure rather than two.
I really wish memory designers would allow for data to be obliterated even if they don't allow most other overwriting patterns. Especially with NAND flash, it would make some operations a lot easier.
Best Answer
The short answer is to declare your variable with the
const
keyword. If your MCU actually remembers the value of yourconst
variable (i.e. your sine computation actually works), it pretty much has to be stored in flash memory, otherwise it would be lost at the first reboot after programming.The long answer has to do with linker script. These scripts are MCU-dependant and tell the linker where to put what. Usually, this script is provided by the IDE, but you can write your own. On my STM32F4 setup, my linker script starts with a such a statement:
It says that the flash starts at address
0x08000000
and the RAM at address0x20000000
. These addresses can be found in the datasheet where the memory map is described. The rest of the script can get involved, but at some point something along these lines will be present:This says that all
.text
sections (that's how the compiler calls code section) and.rodata
sections (that's how the compiler callsconst
variables) are to be put in the flash section.As suggested above, the
.map
file is the primary way you can check what the linker puts where. You tell the linker to generate it using this option:You can search for your symbole inside this map file, see at which address it has been stored and check that against the memory map specified in the MCU datasheet.