Estimating Program Size - Page 1
Forum section: electronics / general

author: admiralk
author_rank: Regular Contributor
timestamp: June 18, 2021, 11:37:43 pm
content: I never put much thought into it before, and never had to, until now. I am not really sure what parts of a program go into the different memories.I have been wanting to do something with a display for a while, other than printing text, and got interested in The Game of Life. I do not feel like relearning OGL and doing this on a PC right now, so I thought why not get a reasonably sized display and use a microcontroller.ThisI would consider reasonable sized and at the top of my budget. It also means I need to maintain two arrays at 320 x 240. Which means my trusty AVRs are not going to work, as far as I can guess. I need to initialize the grid array randomly, I think I can do that in hardware. And then whatever I need to do to control the display, I have yet to figure out what is involved in that.I have aEK-TM4C123GXL, ARM board, but its memory does not look much bigger than a large AVR, unless I am missing something and why I am asking this question. So, what do I need to calculate for what type of memory?

author: mariush
author_rank: Super Contributor
timestamp: June 18, 2021, 11:48:22 pm
content: Well, you have a 320x240 pixel display, but you probably don't have to track every pixel.Your game of life probably has some sprites / icons / figures for cells , you won't have 320x240 cells on the display. So let's say you'll have 8x8 pixel cells and you'll have maybe 240 x 240 "game area" on screen, leaving a vertical column for score, instructions, various crap.So with a 240x240 pixel "game area" and 8x8 cells you'll have 30x30 cells and now you only need an array of 900 cells ... but you can store the state of each cell as a bit in a byte ... so now you only need 113 bytes to store all the information

author: jpanhalt
author_rank: Super Contributor
timestamp: June 18, 2021, 11:49:44 pm
content: Do you really have to map both X and Y, or can X (or Y) be calculated?

author: admiralk
author_rank: Regular Contributor
timestamp: June 19, 2021, 12:18:34 am
content: Unless I am missing something, I do need to modify every dot/pixel.https://web.stanford.edu/class/sts145/Library/life.pdfThe second array is to track the neighbors.Here is the console version -https://youtu.be/QgwDMoGdP_AJust to add the inspiration for this project:

author: amyk
author_rank: Super Contributor
timestamp: June 19, 2021, 12:35:52 am
content: Find a display that has internal RAM and readback ability.

author: SiliconWizard
author_rank: Super Contributor
timestamp: June 19, 2021, 12:59:19 am
content: Even if you want to "track" individual pixels in RAM, 320x240 pixels is only 9600 bytes if all you need is an on/off state (1 pixel per bit). You can always use colors to display them, but that can be handled when you write data to the display.

author: sleemanj
author_rank: Super Contributor
timestamp: June 19, 2021, 01:00:18 am
content: You only need mono right, so 1 bit per cell (pixel), 9600 bytes for a single generation.Some displays have the ability to let you read their framebuffer as well as write it, so there is the possibility you could get away with one generation in memory, and one on the framebuffer (generation n in memory, update it into the framebuffer on the display, read back the framebuffer into generation n+1).But even with that you don't obviously have the RAM on an ATMega328, as it only has 2k, you can't even store 1 playfield.But for your application you could use an external SRAM as the data store, either a parallel addressed and read one, or an SPI one. 256kbit SRAM of either variety can be had for a couple of bucks, large enough to hold both generations. Obviously this will be slower (especially an SPI one) but that won't matter for a Conway's Game Of Life.

author: ejeffrey
author_rank: Super Contributor
timestamp: June 19, 2021, 03:12:04 am
content: Quote from: sleemanj on June 19, 2021, 01:00:18 amIf you are happy with update in place you only need one extra row plus two pixels of auxillary space.

author: Siwastaja
author_rank: Super Contributor
timestamp: June 19, 2021, 12:58:57 pm
content: Full framebuffer (or two, see double buffering) in the CPUs address space makes a lot of sense development-wise even if you sometimes could work without. Just pick the MCU suitable for the task. Even with 8 bpp which would enable color/gray scale shades and/or ease addressing, 320x200 is just 64000 bytes. Many MCUs in Cortex-M4 series offer good performance and enough memory with affordable price, while not being overkill. If unsure, you can start with some Cortex-M7 models running at over 400MHz with half a megabyte of RAM and downgrade later. Look if you could use DMA to update the display.

author: admiralk
author_rank: Regular Contributor
timestamp: June 19, 2021, 04:52:24 pm
content: As near as I can tell, the display gets 8 bits at a time, in parallel. The memory on the display is just for characters. It sounds like my TI board will have enough space, from what you all have said. I need to figure out how to add the processor to CCS since it is only set up for MSP430s right now. If that is still too small, thenthis Nucleo boardshould do it? But then I have to set up a whole new tool chain.

author: westfw
author_rank: Super Contributor
timestamp: June 20, 2021, 01:59:05 am
content: QuoteWell, at 256k of flash and 32k of RAM, it's pretty much equal to the largest 8bit AVR that you can get, with twice the RAM. And you can get significantly larger ARM chips pretty easily, and don't need to deal with bank switching issues...Do you know about the "nm" tool, which will tell you the size of individual functions and data structures, nicely sorted and categorized as to which memory area they are in.

author: admiralk
author_rank: Regular Contributor
timestamp: June 20, 2021, 04:53:49 am
content: Since I have no idea what you are talking about, I guess the answer is no.

author: jpanhalt
author_rank: Super Contributor
timestamp: June 20, 2021, 10:17:26 am
content: Quote from: admiralk on June 19, 2021, 04:52:24 pmYour full comment is consistent with my experience with GLCDs; although, that experience is not very extensive. That is, you can use a serial (e.g., SPI) interface to write. Some may allow reading, but they seem to be rare. Most commonly, if one wants to read you need a parallel interface.In terms of reading, you read a full byte.* That is, one column 8-bits high, which is often called a page. So, a 132x64/65 display (e.g., an ST7567 driver) will be 8 pages high and 132 columns wide. *I have heard claims of a display, driver, and MCU that could address individual pixels but have never used one.With that preface, I am not sure what you mean by just reading characters? Do you mean it returns an ASCII value for a character rather than the bit pattern? Wouldn't that sort of hobble the usefulness of a GLCD? If it actually returns a full byte, as described above, then addressing pixels is just a matter of shifts and/or bit manipulation.Please explain what you meant by reading just characters.John

author: admiralk
author_rank: Regular Contributor
timestamp: June 20, 2021, 03:08:17 pm
content: As I understand it, you set a block, just like any character display.https://www.buydisplay.com/download/ic/RA8835.pdf] https://www.buydisplay.com/download/ic/RA8835.pdf, page 38. Then just reference that part of memory to print it. I could be completely wrong, but I think the courser moves one dot at a time. So I send 8 bits and advance the courser each bit. The draw picture example uses a large 8 bit array, and since it is monochrome, I assume the 8 bits represent 8 dots and not a shade for each.Hopefully when I start CCS back up it will recognize my board so I can at least declare the arrays and see if it accepts them or puke them up. 320 does not fit in an 8 bit number and that seems to be my breaking point.

author: jpanhalt
author_rank: Super Contributor
timestamp: June 20, 2021, 03:30:53 pm
content: Quote from: admiralk on June 20, 2021, 03:08:17 pmFor most displays when writing, the cursor advances one column per write. A column is one pixel/bit wide and probably 8 bits/pixels high. When reading, it is possible to turn off that autoadvance feature. On the displays I use, it is caller RMW mode (read-modify-write).QuoteThis is going to get very confused. How do you send "1-bit." Do you IOR with the read? Can you use the equivalent of a "bsf <destination>" instruction? I suspect not.Unfortunately, the link you post is blocked on my PC. What controller(s) does your display use. I will look of the appropriate datasheet.EDIT: Never mind. I think I found how to get around the block.

author: Jan Audio
author_rank: Frequent Contributor
timestamp: June 20, 2021, 04:20:53 pm
content: I am intrested in this topic.How about SRAM ?, maybe you wont get 60 fps.Just intrested, i want it also.

author: westfw
author_rank: Super Contributor
timestamp: June 20, 2021, 10:48:42 pm
content: Quote"nm" is one of a set of utilities that comes as part of gcc compilers (and many others too, i believe. It essentially operates on .elf format files, so it will probably work on any tool set that produces .elf files.) It analyzes the symbol information that is present in such files (so it will NOT work on .bin or .hex files.)It will have a prefix, like "avr-nm" or "arm-none-eabi-nm"The most useful form for program size analysis is "nm -C -S --size-sort myfile.elf", which will list each symbol (possibly de-mangled from C++ names ("-C") and their size ("-S"), and sort the output so that the biggest things are at the end.Here's some output from a recent AVR/Arduino program:Code:0000249c 00000046 T sprintf_P00001026 00000048 t refreshCache(unsigned long)000002d4 0000004a t micros00002424 0000004e T atol00000382 0000004e t twi_transmit00001222 00000050 t refreshInProgress()000014e2 00000050 t String::reserve(unsigned int)00001556 00000056 t String::String(char const*) [clone .part.2]000015ac 00000056 t String::operator=(char const*)00000180 0000005a t Print::write(unsigned char const*, unsigned int)00000820 0000005a t TwoWire::write(unsigned char const*, unsigned int)000008d8 0000005a t DS3232RTC::writeRTC(unsigned char, unsigned char*, unsigned char) [clone .constprop.19]00000b84 0000005c t setTime(unsigned long)0000087a 0000005e t TwoWire::write(unsigned char)000028c2 00000060 T fputc00000272 00000062 t pinMode0000031e 00000064 t delay00001612 00000070 t twi_stop00001272 00000078 t setNextRefresh(unsigned long)00001090 00000078 t DS3232RTC::readRTC(unsigned char, unsigned char*, unsigned char) [clone .constprop.28]00000ae2 00000080 t Stream::timedRead() [clone .constprop.6]00001790 00000094 T __vector_16000001da 00000098 t digitalWrite000013f6 000000b2 t setNextCheckTime()00000982 000000b8 t SoftwareSerial::write(unsigned char)00002922 000000bc T __ultoa_invert00001152 000000d0 t DS3232RTC::get()00001a62 000000f0 T __vector_300001a62 000000f0 T __vector_400001a62 000000f0 T __vector_5000012ea 0000010c t setNextRefreshDate()00000be0 0000010e t now()00001682 0000010e t printTime(String, unsigned long)00002c8e 00000112 T free00002b56 00000138 T malloc00001b52 0000013c t global constructors keyed to 65535_0_sketch_jun20a.ino.cpp.o.246300002da0 0000017a T realloc00000cee 00000180 t makeTime(tmElements_t const&)0000044e 0000019e t twi_readFrom.constprop.3800000e6e 000001b8 t breakTime(unsigned long, tmElements_t&)000005ec 000001e0 t TwoWire::endTransmission(unsigned char) [clone .constprop.35]00001824 0000023e T __vector_24000024e2 000003b4 T vfprintf00001c8e 0000075e T mainThe output format is "<value> <size> <type> <symbol>", where "<type>" is "t" or "T" for code ("text"), "b"/"B" for uninitialized data ("bss"), and "d"/"D" for initialized data. Uppercase means a global symbol, lowercase means local.In the above sample output, one might wonder "why is free/malloc/realloc here; I didn't use them", or "gee, printf really is pretty big!", or "fputc? In Arduino code?" (None of the "big" symbols in my example are data, but in a display example you might notice "do I really need such large and multiple fonts?")

author: admiralk
author_rank: Regular Contributor
timestamp: June 22, 2021, 01:05:56 am
content: That looks interesting. Thanks for explaining.

author: JPortici
author_rank: Super Contributor
timestamp: June 22, 2021, 03:40:45 pm
content: Quote from: admiralk on June 18, 2021, 11:37:43 pmFYI, if you look at the LVGL simulator, they set up a display emulator, you just write the memory and it will display the data

author: admiralk
author_rank: Regular Contributor
timestamp: June 22, 2021, 10:20:10 pm
content: I might look into that later. For now, I found out that my TI board is not going to cut it. I did find an ST board withthis beaston it, though. Hopefully I can find another datasheet, since this one looks a little light at only 200 pages.

author: Renate
author_rank: Super Contributor
timestamp: June 23, 2021, 11:49:31 am
content: The AVR gcc puts .note.gnu.avr.deviceinfo in the ELFThat lists the AVR model and all the sizes of flash, RAM, EEPROM.I have my own little program:Code:C:\> elfview volume.elf /mCode: 6798Data: 0Zero: 1044AVR: atmega32u4Flash: 6798 / 32768 = 20.7%RAM: 1044 / 2560 = 40.8%

author: SiliconWizard
author_rank: Super Contributor
timestamp: June 23, 2021, 04:06:42 pm
content: One simple thing you can do to get a per-section usage of memory is to pass the '--print-memory-usage' option to the linker (if you're using GCC/binutils).When invoking the linker through GCC, this can be done with the following option: '-Wl,--print-memory-usage'.Another way is to use 'size' from binutils on the .elf file:size xxx.elfI usually use both in my makefiles.