How do I reduce program size to bare minimum?

Description

I am trying to get LVGL into a Bootloader with limited program size (~240 KB or less). Without LVGL, I was getting a program size of around 131 KB. Now I am asked to add an improved appearance with LVGL. Output is read-only – no user input, and the simplicity of what I need is rendering a single (multi-line) string on a 16-bpp color touch-screen application. Adding LVGL and simply calling lv_init() and initializing and registering a display, with default lv_conf.h I was getting a new program size of 436 KB. All I need to do is display ASCII text on a black (or fixed-color) background. So in lv_conf.h I found the various LV_USE_… macros and set them all to 0 except for LV_USE_LABEL. I also set sub-macros LV_LABEL_TEXT_SELECTION and LV_LABEL_LONG_TXT_HINT to 0.

This helped a great deal. Now I am down to 267 KB. I already have my compiler putting each function into its own section, and having my linker remove unused sections. Normally it is very clever at finding functions – even in a deep call tree – that end up not getting called and removing them. So I normally feel safe including complex libraries, and these two features normally cause the linker to keep ONLY the library features that I actually call (plus the functions they call, of course).

A cursory examination of the output of

nm --print-size --size-sort --reverse-sort   <.elf file>

on the resulting .elf file is showing my program still contains these (showing only the largest several LVGL functions).

9d0072a8 0000106c T lv_draw_sw_blend_basic
9d008314 00000e34 T lv_obj_get_scrollbar_area
9d009d68 00000b94 T lv_obj_refr_size
9d00a8fc 00000a9c t refr_area_part
9d00c878 00000960 T lv_draw_label
9d00e334 000007bc T lv_draw_sw_img_decoded
9d00ffa0 000006bc T _lv_disp_refr_timer
9d01065c 000006a8 t lv_obj_event
9d012500 000005c4 T lv_draw_sw_letter
9d013bd8 0000056c t lv_obj_draw
9d0146a8 0000054c t refr_obj
9d01511c 00000510 T lv_obj_refr_pos
9d01562c 000004f0 t scroll_area_into_view
9d015ff8 000004a4 t lv_obj_set_state
9d017208 0000042c T _lv_txt_get_next_line
9d017a44 000003f0 T lv_draw_img
9d017e34 000003e0 T lv_draw_sw_line
9d0185c4 00000380 T _lv_obj_style_state_compare
9d019018 00000368 T lv_obj_init_draw_rect_dsc
9d019380 00000360 t trans_anim_cb
9d019a3c 0000035c t draw_bg_img

What confuses me is that I am not doing anything with images or image decoding, and yet

lv_draw_img

and

draw_bg_img

which then forces things like ‘lv_draw_sw_img_decoded’ and other things about image decoding to be pulled into the program (1.9 KB), which I am not going to be using.

Another example: if I am not using scrolling of any type, why are

lv_obj_get_scrollbar_area()  (3.5 KB)

and

scroll_area_into_view()  (1.2 KB)

being included?

Notes:

  1. I have worked with LVGL 6.0.2 extensively in another application, so I have no worries about getting it functioning. I’ve had time to study the flow of control and some of its inner parts that get rendering to take place, and so my worries are NOT about getting it working, but ONLY about reducing program size. Reason: If I can’t get the whole program under about 240 KB (which means LVGL needs to fit inside about 109 KB including the [single] font I am using), then I won’t be able to use LVGL at all.
  2. All I need to do is display un-editable text (one size) on a black background (labels).
  3. There is no user input needed.
  4. I’m not using anything that would need to be scrolled, so everything about scroll bars (e.g. lv_obj_get_scrollbar_area(), which itself has a 3.5 KB footprint) is not going to be needed.
  5. I’m not using any transformations or functions that should need any Trigonometry functions. (I found transforms can be removed by setting LV_DRAW_COMPLEX to 0.)
  6. I’m not using any animations (which means several things can be eliminated).
  7. LV_USE_FS_FATFS, LV_USE_FS_STDIO, LV_USE_FS_POSIX, LV_USE_FS_WIN32 are all defined as 0.
  8. I have already removed my original text-rendering functions from the program.

What MCU/Processor/Board and compiler are you using?

PIC32MZ1024EFF100 / Production PCB

What LVGL version are you using?

8.3.9 (end of branch ‘release/v8.3’)

NOTE: it is easy for me to switch versions at this point, if doing so makes this easier or more feasible.

What do you want to achieve?

Reduce LVGL program-size footprint considerably. I was hoping to get it down to 70 KB + the size of the font that I am using.

What have you tried so far?

  1. Compiler optimization towards size reduction.

  2. Setting all the LV_USE_… to 0 except for the one that I actually need: LABELS. (Reduced program size by approx. 170 KB.)

  3. In lv_init(), commenting out calls to _lv_anim_core_init() and _lv_img_decoder_init() (reduced program size by only around 5 KB). (Note: this was just a temporary experiment – I haven’t done enough homework yet to determine if this is safe or not.)

  4. Verified all conditional compilation in lv_extra_init() is not included.

  5. Removed original rendering code (somewhat complex) to reduce program size.

  6. Set LV_BUILD_EXAMPLES to 0.

  7. Set LV_DRAW_COMPLEX to 0. (gained 35 KB!)

  8. Set LV_MEMCPY_MEMSET_STD to 1. (gained 1.1 KB)

Code to reproduce

This is all I have called so far:

	/* Initialize LVGL. */
	lv_init();

	/* Set up display buffer. */
	lv_disp_draw_buf_init(&gDisplayBuffer, gclraDrawBuffer, NULL, HOR_PX_COUNT * 40);

	/* Set up and register display driver. */
	lv_disp_drv_init(&gDisplayDriver);
	gDisplayDriver.hor_res = HOR_PX_COUNT ;
	gDisplayDriver.ver_res = VER_PX_COUNT;
	gDisplayDriver.flush_cb = _UI_FlushBufferToLcdPanel;
	gDisplayDriver.draw_buf = &gDisplayBuffer;
	lv_disp_drv_register(&gDisplayDriver);

Screenshot and/or video

n/a

So I am hoping that someone has experience on reducing this footprint to < 70-80 KB + font size…

I will continue to hunt down call traces to things I am not using, but any advice from someone experienced along this line would be greatly appreciated!

2 Likes

If your text is static you completely dont require lvgl. Based on display size you can achieve all with 2kB code plus text image.

It will indeed be static. Do you mean by just including the rendering engine? If so, how do I do that? If not, please clarify. You have my attention. (I had text-rendering code before, but the font was only 1-bpp and in a different format. My choice of LVGL was that the app [not the Bootloader] would also use it for user input, buttons, etc. and to take advantage of the rendering engine that can render only the changed part of the screen, so thought I would “kill 2 birds with one stone”.)

LVGL base code dont manage display flush. Your idea is only display flush = lvgl not required. For example you plan show UPDATE WAIT text. Then create bitmap with optimal format store it and flush to display in bootloader.

I see what you’re saying. To clarify, the Bootloader displays a variety of messages based on its state and potential errors it can run into (including the name of the file that it is loading, which changes, so the strings cannot be pre-determined at development time). Thus, I need to take it from ASCII strings to font rendering. I wanted LVGL to do the font rendering since it already recognizes the font format and does a nice job with the edges. That saves me the time of creating the font and re-working my rendering logic for the new display pixel format. Using LVGL saves me that development time.

As an experiment, I dropped back to version 6.0.2 and turned everything off but LABELS and BAR in the lv_conf.h file, and the LVGL part of the program + font takes 40 KB (as opposed to ~119 KB with v8.3.9). That solves my problem, though I am still open to and desiring to learn how to significantly cut program size with LVGL v8.3.9, or whatever version is represented by the current ‘master’ branch, if the new features (added since 6.0.2) have been made optional there so as to reduce program-size footprint.

Maybe 8.3.x (my top used now is 7) too can be lighter, but i recommend disable all fonts and create one custom .

That makes sense since that would allow me to exclude characters I don’t use. Thank you!

Latest: creating a custom font (without the Arabic characters in it) now has my LVGL v8.3.9 feature branch program size down to 219 KB. So now I can go either way now.

Should I consider going with the ‘master’ branch instead of v8.3.9 for a product needing a high level of reliability? (I don’t know the history of the ‘master’ branch, so I am asking. I did, however, notice that ‘master’ was recommended for getting responses in this forum, so it seems very significant.)

I did a spike trying using the ‘master’ branch, and the answer to my above question is:

DEFINITELY NO!

Too many things broken (try turning off most widgets and set LV_USE_DRAW_SW, LV_USE_FLEX and LV_USE_GRID to 0 and see what happens).

But I still would like to know from anyone who is working with the ‘master’ branch. What is its state? Is it getting stable (aside from the things I mentioned above, which I can work around – I think – with conditional compilation directives)? And how close are we to a stable version 9.0.0? @kisvegabor, can you give us a hint?

If you just need to display various predefined messages depending on the update progress / status, LVGL is a huge overkill. Some displays have a build-in character generator, in that case printf() / putc() can do the job. If no character generator present, you could render the messages on a computer, have the LVGL graphic converter turn these into C arrays (strip what you do not need), fiddle out the bit format and transfer the arrays to the display. You may need to dig into interfacing CPU <—> Display (I2C, SPI, GPIO, other). Would take somewhere between 2 - 4K Bytes

Wat are manufacturer and part number of the display controller?

Hi, JJ7.

Fair point, and I agree IF the strings were able to be predetermined. However, the strings will vary each time the Bootloader uses the display, so I have to go from ASCII strings to rendering. Thank you for your input. (I’m not at liberty to divulge part numbers, but thanks for your willingness to look into it. :slightly_smiling_face: )

Does the display controller have an internal character generator?

If so, you just need to set colors, cursor position and printf() (variable) strings to the controller
which takes care of rendering. Should fit into 2 - 3KB.

If not, rendering strings into graphic bit patterns is not a big deal either. Just keep it simple.

BR, JJ7

No, it’s pure pixels. That would be a neat rig for certain types of applications.