|
Forum Index : Microcontroller and PC projects : "Not enough heap memory"
| Author | Message | ||||
| lizby Guru Joined: 17/05/2016 Location: United StatesPosts: 3724 |
I've run into an issue with heap memory on a WeAct RP2350B module with 8MB PSRAM. This appeared running my PicoRR program using PicoDB, about 5000 lines of code. I don't know how to make the example truly minimal, but 273 lines does it. Only 19 lines are executable--the rest are variable definitions--including big arrays. The key constant is MAX_RECS. The larger program runs fine if it is set at 500. In this instance, set at 920, the code reports "Heap: 6291712" and then calls an empty stub SUB ("SUB DummySub", which has no code, only a SUB END) and there's an immediate "Error : Not enough Heap memory". Here's the code: ' ============================================================= 'rr.bas - PicoDB + Model Railroad OS ' ============================================================= Option EXPLICIT Option Default Float ' ============================================================= ' PICODB ENGINE GLOBALS (Required for library.bas) ' ============================================================= option local variables 500 Dim Integer GLOBAL_MODE = 2 ' for option resolution 640x320, 320x240 16 colors Dim Integer DBG_LVL = 3 ' 0=min; 1=min; 2=max Dim Integer Is_LCD = 0 Const MAX_TBL = 5 Const MAX_FLD = 30 Const TYPE_STR = 1 Const TYPE_INT = 2 Const TYPE_FLOAT = 3 Const MAX_ENGINES = 5 Const MAX_RECS = 920 ' Tuned for HDMI Memory, no PSRAM (150 works) Const MAX_CARS_PER_TRAIN = 15 Const MAX_PARKED_CARS = 20 Const MAX_GROUPS=5 Const MAX_SCRIPT_LINES=100 Const STOP_MARGIN = 24.0 ' pixels Const SpriteSize = 22 Const HC_06 = 0 ' ============================================================= ' RGB121 (4bpp) HARDWARE SPRITE PALETTE ' Bit Layout: [Red] [Green_Hi] [Green_Lo] [Blue] ' ============================================================= Const HW_BLACK = 0 ' (0000) MMBasic 0 Const HW_BLUE = 1 ' (0001) MMBasic 1 Const HW_MYRTLE = 2 ' (0010) MMBasic 8 (Low Green) Const HW_COBALT = 3 ' (0011) MMBasic 9 Const HW_MIDGREEN = 4 ' (0100) MMBasic 10 (High Green) Const HW_CERULEAN = 5 ' (0101) MMBasic 11 Const HW_GREEN = 6 ' (0110) MMBasic 2 (Full Green) Const HW_CYAN = 7 ' (0111) MMBasic 3 Const HW_RED = 8 ' (1000) MMBasic 4 Const HW_MAGENTA = 9 ' (1001) MMBasic 5 Const HW_BROWN = 10 ' (1010) MMBasic 14 (Red + Low Green) Const HW_LILAC = 11 ' (1011) MMBasic 15 Const HW_RUST = 12 ' (1100) MMBasic 12 (Red + High Green) Const HW_FUCHSIA = 13 ' (1101) MMBasic 13 Const HW_YELLOW = 14 ' (1110) MMBasic 6 (Red + Full Green) Const HW_WHITE = 15 ' (1111) MMBasic 7 Const TransparentColor = HW_MAGENTA ' 9=magenta Dim Float D_Scale Dim Float D_CAR_GAP ' --- DISPLAY SETTINGS --- Dim Float PARK_MARGIN Dim Float CAR_WIDTH Dim STRING dbname$ = "layout", db_command$ Dim STRING makedb_default$ = "make-db layout roster, segments index on location, sequence use" Dim STRING cmd$, defLine$, token$, workLine$, tmp_f$ Dim INTEGER fNum, commaPos, tokIdx, startTime, tStart Dim STRING temp_alias$, temp_file$, temp_owner$, temp_name$ Dim INTEGER temp_len, temp_type, temp_start, temp_width, spc Dim STRING Q_Filter$, Q_OrderFld$, Q_ShowFlds$, Q_OutFile$ Dim INTEGER Q_Limit, Q_OrderDesc, Q_OutType, Q_OutHandle Dim INTEGER Result_Count Dim INTEGER Aggr_Mode, Aggr_Fld_ID, Aggr_Count, Action_Mode Dim FLOAT Aggr_Val Dim INTEGER SQL_Mode, CACHE_LOADED Dim String DB_Callback$ Dim INTEGER DB_File_Ptr(MAX_TBL) Dim INTEGER DB_Auto_Index = 1 Dim INTEGER DB_Dirty_Flag(MAX_TBL) Dim STRING Upd_Target_Fld$, Upd_Target_Val$, Upd_Target_Tbl$ Dim STRING Group_Keys$(MAX_GROUPS) length 31 Dim FLOAT Group_Vals(MAX_GROUPS) Dim INTEGER Group_Counts(MAX_GROUPS) Dim INTEGER Group_Top, Group_Fld_ID Dim INTEGER Join_Driver_Tbl, Link_Fld_ID Dim STRING DB_Link_Fld$, DB_Link_File$ Dim STRING DB_Tbl_Name$(MAX_TBL) LENGTH 31 Dim STRING DB_Tbl_File$(MAX_TBL) LENGTH 31 Dim INTEGER DB_Tbl_Len(MAX_TBL) Dim INTEGER Table_Count Dim STRING DB_Fld_Owner$(MAX_FLD) LENGTH 31 Dim STRING DB_Fld_Name$(MAX_FLD) LENGTH 31 Dim INTEGER DB_Fld_Type(MAX_FLD), DB_Fld_Start(MAX_FLD), DB_Fld_Width(MAX_FLD) Dim INTEGER Field_Count, db_Record_Count Dim STRING DB_RowBuf$(MAX_TBL) Type IndexItem Key As STRING LENGTH 31 RecNo As INTEGER End Type Type IndexItemNum Key As FLOAT RecNo As INTEGER End Type Dim IndexItemNum DB_IndexNum(MAX_RECS) Dim INTEGER Link_Rec_Count Dim IndexItem DB_Index(MAX_RECS) Dim IndexItem DB_Lookup(MAX_RECS) Dim IndexItemNum DB_LookupNum(MAX_RECS) Dim IndexItem Result_List(MAX_RECS) Type joinRecsNum RecNo1 As INTEGER RecNo2 As INTEGER Key As FLOAT End Type Type joinRecsAlpha RecNo1 As INTEGER RecNo2 As INTEGER Key As STRING * 31 End Type Dim joinRecsNum DB_JoinResNum(1) Dim joinRecsAlpha DB_JoinResAlpha(1) ' ============================================================= ' RAILROAD GRAPHICS GLOBALS ' ============================================================= Dim Float Scale Dim Integer OffX, OffY Dim Float GridW, GridH Dim Float ind = 0.6 Dim Float x1_top, x1_bot, x2_top, x2_bot 'Dim Float SegLen(10) 'Dim Integer MacSeg(40), MacRev(40) 'Dim Float MacLen(40), MacOdo(40) Dim Float TotalMacroLen Dim Integer mIdx = 0 Dim Integer cTrack = RGB(255, 255, 255) Dim Integer cSiding = RGB(255, 255, 255) Dim Integer cSwitch = RGB(255, 255, 0) Dim Integer cCross = RGB(255, 128, 0) ' --- GLOBAL ACTIVE LAYOUT --- Dim String Active_Layout$ LENGTH 4 = "1" ' Database Active Roster Arrays (Loaded on RUN) Const MAX_SWITCHES = 20 Dim Integer SwState(MAX_SWITCHES) Dim String dCmd$ = "" Dim Float V(MAX_ENGINES), TV(MAX_ENGINES), D(MAX_ENGINES), oldD(MAX_ENGINES) Dim String E_Leg$(MAX_ENGINES) length 20, old_E_Leg$(MAX_ENGINES) length 20 Dim Integer wasMoving(MAX_ENGINES) ' Multi-Train Physics Arrays Dim Integer TR_Count(MAX_ENGINES) Dim Float TR_PixLen(MAX_ENGINES, MAX_CARS_PER_TRAIN) Dim Integer TR_IsEng(MAX_ENGINES, MAX_CARS_PER_TRAIN) Dim Integer TR_Color(MAX_ENGINES, MAX_CARS_PER_TRAIN) Dim Integer TR_Dir(MAX_ENGINES) Dim Integer Active_Engine Dim String TR_ID$(MAX_ENGINES, MAX_CARS_PER_TRAIN) LENGTH 4 E_Leg$(1) = "L3" : old_E_Leg$(1) = "L3" V(1) = 8.75 Dim String Found_Loc$ Dim Integer PK_Count = 0 Dim String PK_Leg$(MAX_PARKED_CARS) LENGTH 16 Dim Float PK_Dist(MAX_PARKED_CARS), PK_Len(MAX_PARKED_CARS), PK_Offset(MAX_PARKED_CARS) Dim Integer PK_Color(MAX_PARKED_CARS), PK_IsEng(MAX_PARKED_CARS), PK_Dir(MAX_PARKED_CARS) Dim String PK_ID$(MAX_PARKED_CARS) LENGTH 4 ' --- SPRITE ENGINE GLOBALS --- Dim Integer Use_Sprites = 0 Dim Integer TR_Sprite(MAX_ENGINES, MAX_CARS_PER_TRAIN) Dim Integer PK_Sprite(MAX_PARKED_CARS) ' New dynamic cache globals Dim String Cache_Seg_Name$ LENGTH 16 Dim Integer Cache_Seg_Idx, Cache_Dir Dim Float Cache_Start_D Dim Float LegLen(15), Cursor_X, Cursor_Y Dim Integer Node(MAX_SWITCHES, 2) ' --- DYNAMIC GEOMETRY ARRAYS --- Dim Integer Layout_Seg_Count, headSeq Dim String Seg_ID$(MAX_RECS) LENGTH 16 Dim String Seg_Type$(MAX_RECS) LENGTH 2, Seg_CurveType$(MAX_RECS) LENGTH 2 Dim Float Seg_Len(MAX_RECS), Seg_Rad(MAX_RECS) Dim Float Seg_X1(MAX_RECS), Seg_Y1(MAX_RECS) Dim Float Seg_X2(MAX_RECS), Seg_Y2(MAX_RECS) Dim Integer Seg_WestNorth(MAX_RECS) ' 0 if x1,y1 is West/North, 1 if x2,y2 ' NEW: Friendly Names Dim String Seg_Friendly$(MAX_RECS) LENGTH 20 Dim Integer Show_Friendly = 0 ' flag Dim Integer Show_Names = 0 ' flag Dim Integer Show_Cars = 0 ' flag Dim Integer Show_Legend = 1 ' flag ' NEW: Switch and Crossing Storage Dim Float Switch_X(MAX_SWITCHES), Switch_Y(MAX_SWITCHES) Dim Float Cross_X, Cross_Y Dim Integer Has_Cross = 0 Dim Integer Trace_Mode = 0 Dim Integer Force_Redraw = 1 ' flag Dim String Seg_N1$(MAX_RECS) length 20 Dim String Seg_N2$(MAX_RECS) length 20 Dim String Seg_N3$(MAX_RECS) length 20 ' --- DIRTY SEGMENT TRACKING --- Dim Integer isErase_Global = 0 ' flag Dim Integer Dirty_Seg(MAX_RECS) ' --- TURNTABLE TRACKING --- Dim Float RH_CX, RH_CY, RH_Len, RH_BaseAng Dim Float RH_Orig_X1, RH_Orig_Y1, RH_Orig_X2, RH_Orig_Y2 Dim Float RH_Angle = 0.0 Dim Float RH_Target = 0.0 Dim Integer RH_Idx = 0 Dim Integer Override_Color = -1 Dim Integer rh_moving = 0 ' --- SCRIPT ENGINE GLOBALS --- Dim String Script_Lines$(MAX_SCRIPT_LINES) length 39 Dim Integer Script_Line_Map(MAX_SCRIPT_LINES) Dim Integer Script_Count = 0 Dim Integer Script_Idx = 0 Dim Float Script_Wait_Time = 0.0 Dim String Script_Wait_Event$ = "" Dim Integer Is_Recording = 0 ' flag Dim String Record_File$ = "" ' --- GRAPH CACHE --- Dim String FastSeg_ID$(5) Dim Integer FastSeg_Idx(5) Dim Integer FastSeg_Ptr = 1 Dim Integer Sys_Paused = 0 ' flag ' ============================================================= ' MAIN COMMAND LOOP ' ============================================================= If DBG_LVL>2 then Print "Diag0: MAX_RECS: ";MAX_RECS;"; Heap: ";mm.info(heap) Print "Initializing Database Engine... " + MM.Info(CURRENT) Option CONSOLE SERIAL ' in program only--not a console option which remais persistant IF instr(mm.info$(lcdpanel),"MODE") then mode GLOBAL_MODE ' set video mode for HDMI or VGA Print mm.device$+" ";mm.info(version);" Video Mode ";GLOBAL_MODE Else Is_LCD=1 ' LCD--not HDMI/DVI or VGA endif If DBG_LVL>2 then Print "Diag0: MAX_RECS: ";MAX_RECS;"; Heap: ";mm.info(heap) DummySub Do Loop End Sub DummySub End Sub Diag0: MAX_RECS: 920; Heap: 6291712 Initializing Database Engine... NONE Diag0: MAX_RECS: 920; Heap: 6291712 [272] Sub DummySub Error : Not enough Heap memory No library is present > memory Program: 8K ( 2%) Program (273 lines) 320K (98%) Free Saved Variables: 16K (100%) Free RAM: 206K ( 3%) 221 Variables 135K ( 2%) General 6178K (95%) Free > ?mm.info(heap) 6292224 > I don't know if running this on a different device would produce a different error. If so, changing MAX_RECS might make it fail at this point. I don't have a non-WeAct Pico2 with PSRAM to test with. PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on FOTS |
||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 11127 |
When PSRAM is enabled there are two heap memory pools. One in main RAM and one in PSRAM. For performance reasons internal functions that use memory always only use normal RAM so in your example calling a subroutine needs memory and always gets it from the normal ram pool. Variable memory usage will first try to use normal RAM as it is faster and if not enough is available will then use PSRAM. It will automatically use PSRAM for a variable if the request for memory is more than half the size of the RAM pool (i.e. big arrays). You have created an edge case. You have lots of relatively small variables that are filling the heap in normal RAM and not triggering the condition that places them in PSRAM. Then when the system needs RAM for internal use there isn't any hence the error. I can't see an easy solution that is performant. The obvious answer would be to place variables in PSRAM if the heap in normal ram falls below a certain level. The problem with this is that calling the routine to calculate how much normal ram is left would massively slow down variable creation. This wouldn't matter too much for global variables but would be a disaster for local variables. |
||||
| lizby Guru Joined: 17/05/2016 Location: United StatesPosts: 3724 |
Thanks for looking into it. Perhaps a solution will make itself known. The problem as I see it is the array sizes set by MAX_RECS. The number of variables doesn't seem to be an issue if MAX_RECS is small enough. These arrays of structures chew up a lot of heap. Dim IndexItemNum DB_IndexNum(MAX_RECS) ' a structure, size 16 Dim IndexItem DB_Index(MAX_RECS) ' a structure, size 40 Dim IndexItem DB_Lookup(MAX_RECS) ' a structure, size 40 Dim IndexItemNum DB_LookupNum(MAX_RECS) ' a structure, size 16 Dim IndexItem Result_List(MAX_RECS) ' a structure, size 40 For MAX_RECS of 1000, that would be about 150K. I'd like for MAX_RECS to be a maximum of 2000. If there were a way to make sure they went into PSRAM, it would probably solve the problem. Something like Dim PSRAM IndexItem DB_Index(MAX_RECS) might do it. ~ Edited 2026-04-02 22:44 by lizby PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on FOTS |
||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 11127 |
I assume you are running standard PicoMiteRP2350 firmware? What happens if you set MAX_RECS very big, say 20,000 |
||||
| lizby Guru Joined: 17/05/2016 Location: United StatesPosts: 3724 |
WeAct RP2350B 48-pin module with ILI9488 LCD: MAX_RECS=5000 works for stub program; also works for PicoRR+PicoDB: ?MAX_RECS,MM.INFO(HEAP) 5000 5166336 So it looks like those big arrays of structures are in PSRAM now. And all with the change in the value of a single variable. Thanks so much for all your efforts. PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on FOTS |
||||
| The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2026 |