From c1247552709f01281e5b4e8d8078886747268e21 Mon Sep 17 00:00:00 2001 From: Mason Ticehurst Date: Fri, 16 Feb 2024 13:15:32 -0500 Subject: [PATCH] Refactor debugging console to use a queue, fix damn race condition in screen buffer --- eRINA_STM32F7/erina_stm32f7.gpr | 23 +++-- eRINA_STM32F7/src/net/receiver.adb | 10 +- eRINA_STM32F7/src/rina/board.adb | 2 +- eRINA_STM32F7/src/rina/debug.adb | 152 +++++++++++++++-------------- eRINA_STM32F7/src/rina/debug.ads | 29 +++--- eRINA_STM32F7/src/rina/demo.adb | 31 +++--- eRINA_STM32F7/src/rina/gui.adb | 4 +- 7 files changed, 136 insertions(+), 115 deletions(-) diff --git a/eRINA_STM32F7/erina_stm32f7.gpr b/eRINA_STM32F7/erina_stm32f7.gpr index 8d08623..7c6d173 100644 --- a/eRINA_STM32F7/erina_stm32f7.gpr +++ b/eRINA_STM32F7/erina_stm32f7.gpr @@ -1,19 +1,18 @@ with "config/erina_stm32f7_config.gpr"; with "config/stm32_hal_config.gpr"; -project eRINA_STM32F7 is +project ERINA_STM32F7 is for Target use "arm-eabi"; - for Runtime ("Ada") use "embedded-" & stm32_hal_config.DEVICE & "disco"; - - for Source_Dirs use ("src/", "src/rina", "src/net", "src/net/stm32", "config/"); - for Object_Dir use "obj/" & eRINA_STM32F7_Config.Build_Profile; + for Runtime ("ada") use "embedded-" & Stm32_Hal_Config.Device & "disco"; + for Source_Dirs use ("src", "src/rina", "src/net", "src/net/stm32", "config"); + for Object_Dir use "obj/" & Erina_Stm32F7_Config.Build_Profile; for Create_Missing_Dirs use "True"; for Exec_Dir use "bin"; for Main use ("demo.adb"); package Compiler is - for Default_Switches ("Ada") use eRINA_STM32F7_Config.Ada_Compiler_Switches & "-mno-unaligned-access"; + for Default_Switches ("ada") use Erina_Stm32F7_Config.Ada_Compiler_Switches & "-mno-unaligned-access"; end Compiler; package Binder is @@ -25,7 +24,15 @@ project eRINA_STM32F7 is end Install; package Builder is - for Global_Configuration_Pragmas use "gnat.adc"; + for Global_Configuration_Pragmas use "gnat.adc"; end Builder; -end eRINA_STM32F7; + package Ide is + for Connection_Tool use "st-util"; + for Communication_Protocol use "remote"; + for Connection_Config_File use "../../../../../../usr/share/openocd/scripts/board/stm32f7discovery.cfg"; + for Program_Host use "localhost:4242"; + end Ide; + +end ERINA_STM32F7; + diff --git a/eRINA_STM32F7/src/net/receiver.adb b/eRINA_STM32F7/src/net/receiver.adb index 78f3dae..048892f 100644 --- a/eRINA_STM32F7/src/net/receiver.adb +++ b/eRINA_STM32F7/src/net/receiver.adb @@ -6,6 +6,7 @@ with Net.Protos.Dispatchers; with Net.Headers; with Network; with Debug; +with GUI; package body Receiver is @@ -35,19 +36,18 @@ package body Receiver is Count : Net.Uint64 := 0; begin - Debug.Console.Print (Debug.Info, "Initializing Ethernet Controller..."); + Debug.Print (Debug.Info, "Initializing Ethernet Controller..."); -- Wait until the Ethernet driver is ready. Ada.Synchronous_Task_Control.Suspend_Until_True (Ready); - Debug.Console.Print (Debug.Info, "Ethernet Controller Initialized!"); - + Debug.Print (Debug.Info, "Ethernet Controller Initialized!"); + -- Loop receiving packets and dispatching them. Min_Receive_Time := Us_Time'Last; Max_Receive_Time := Us_Time'First; loop - if Packet.Is_Null then Net.Buffers.Allocate (Packet); end if; @@ -60,7 +60,7 @@ package body Receiver is if Ether.Ether_Type = Net.Headers.To_Network (Net.Protos.ETHERTYPE_ARP) then - Debug.Console.Print (Debug.Info, "ARP Packet Received"); + Debug.Print (Debug.Info, "ARP Packet Received (" & Network.Ifnet.Rx_Stats.Ignored'Image & ") total"); Net.Protos.Arp.Receive (Network.Ifnet, Packet); elsif Ether.Ether_Type = Net.Headers.To_Network (Net.Protos.ETHERTYPE_RINA) diff --git a/eRINA_STM32F7/src/rina/board.adb b/eRINA_STM32F7/src/rina/board.adb index e098309..0376d16 100644 --- a/eRINA_STM32F7/src/rina/board.adb +++ b/eRINA_STM32F7/src/rina/board.adb @@ -5,7 +5,7 @@ package body Board is procedure Pressed is begin STM32.Board.All_LEDs_On; - Debug.Console.Print(Debug.Info, "Button pressed!"); + Debug.Print(Debug.Info, "Button pressed!"); end Pressed; end Button_Handler; end Board; \ No newline at end of file diff --git a/eRINA_STM32F7/src/rina/debug.adb b/eRINA_STM32F7/src/rina/debug.adb index 87a78e8..3b1984a 100644 --- a/eRINA_STM32F7/src/rina/debug.adb +++ b/eRINA_STM32F7/src/rina/debug.adb @@ -1,96 +1,98 @@ with Bitmapped_Drawing; -with STM32.Board; with GUI; package body Debug is + + procedure Print (Debug_Level : in Debug; Msg : in String) is + M : Message; + + -- These line variables shouldn't be confused with the array of lines that are drawn to the screen + -- This is specifically for strings that are too long to fit on a single line, so they can be broken up + Lines_Count : constant Positive := (Msg'Length + (Max_Characters_Per_Line - 1)) / Max_Characters_Per_Line; + Lines : array(1 .. Lines_Count) of String(1 .. Max_Characters_Per_Line); + Line_Start : Positive := Lines'First; + begin + for I in 1 .. Lines_Count loop + -- Calculate the start of the next line segment. If it's the last segment, it may be shorter. + if Line_Start + Max_Characters_Per_Line - 1 <= Msg'Length then + Lines(I) := Msg(Line_Start .. Line_Start + Max_Characters_Per_Line - 1); + else + -- Last segment, potentially shorter than Max_Characters_Per_Line + declare + Last_Segment : constant String := Msg(Line_Start .. Msg'Last); + Padded_Last_Segment : constant String(1 .. Max_Characters_Per_Line) := (Last_Segment & (Last_Segment'Length + 1 .. Max_Characters_Per_Line => ' ')); + begin + Lines(I) := Padded_Last_Segment; + end; + + -- Damn why can't it be this simple + -- Lines(I) := Msg(Line_Start .. Msg'Length) & (others => ' '); -- Fill the rest with spaces + end if; + -- Prepare for the next segment + Line_Start := Line_Start + Max_Characters_Per_Line; + end loop; + + for I in Lines'Range loop + M.Msg := Lines(I); + M.Level := Debug_Level; + + -- Any lines after the first don't have the debug heading + if I > 1 then + M.Cont := True; + end if; + + if Messages.Full then + Messages.Pop; + end if; + + Messages.Push (M); + end loop; + end Print; - protected body Console is - procedure Print (Debug_Level : in Debug; Msg : in String) is - currentLine : constant Natural := - (CURRENT_CONSOLE_POSITION.Y - 100) / FONT_HEIGHT; -- zero indexed - Foreground : HAL.Bitmap.Bitmap_Color; - Background : HAL.Bitmap.Bitmap_Color; - begin + procedure Render is + Foreground : HAL.Bitmap.Bitmap_Color; + Background : HAL.Bitmap.Bitmap_Color; + Pos : HAL.Bitmap.Point := Starting_Point; + Msg : Message; + begin + for I in 0 .. Messages.Size - 1 loop + Msg := Messages.Peek (Queue_Max(I)); + Pos := (Starting_Point.X, Starting_Point.Y + I * (Font_Height + Line_Padding)); Foreground := - (case Debug_Level is when Info | Error => HAL.Bitmap.White, + (case Msg.Level is when Info | Error => HAL.Bitmap.White, when Warning => HAL.Bitmap.Black); Background := - (case Debug_Level is when Info => HAL.Bitmap.Blue, + (case Msg.Level is when Info => HAL.Bitmap.Blue, when Warning => HAL.Bitmap.Yellow, when Error => HAL.Bitmap.Red); + + -- Restart X position to start + Pos.X := Starting_Point.X; - if currentLine > MAX_LINES then - -- Shift console lines up - for I in 1 .. MAX_LINES loop - CopyLine (I, I - 1, I = MAX_LINES); - end loop; - - -- Adjust Y position to overwrite the last line - CURRENT_CONSOLE_POSITION.Y := - CURRENT_CONSOLE_POSITION.Y - FONT_HEIGHT - LINE_PADDING; + if not Msg.Cont then + -- Draw debug prefix + Bitmapped_Drawing.Draw_String + (Buffer => GUI.Screen_Buffer.all, + Start => + GUI.Scale + ((Pos.X, Pos.Y)), + Msg => Msg.Level'Image, Font => BMP_Fonts.Font8x8, + Foreground => Foreground, Background => Background); + + -- Offset X position for debug level header + Pos.X := Pos.X + GUI.MeasureText (Msg.Level'Image, BMP_Fonts.Font8x8).Width; end if; - -- Draw debug prefix - Bitmapped_Drawing.Draw_String - (Buffer => GUI.Screen_Buffer.all, - Start => - GUI.Scale - ((CURRENT_CONSOLE_POSITION.X, CURRENT_CONSOLE_POSITION.Y)), - Msg => Debug_Level'Image, Font => BMP_Fonts.Font8x8, - Foreground => Foreground, Background => Background); - -- Draw actual message text Bitmapped_Drawing.Draw_String (Buffer => GUI.Screen_Buffer.all, Start => GUI.Scale - ((CURRENT_CONSOLE_POSITION.X + - GUI.MeasureText (Debug_Level'Image, BMP_Fonts.Font8x8).Width + - LINE_PADDING * 2, - CURRENT_CONSOLE_POSITION.Y)), - Msg => Msg, Font => BMP_Fonts.Font8x8, Foreground => HAL.Bitmap.White, + ((Pos.X + Line_Padding, + Pos.Y)), + Msg => Msg.Msg, Font => BMP_Fonts.Font8x8, Foreground => HAL.Bitmap.White, Background => HAL.Bitmap.Black); - - -- Move the Y position down for the next message - CURRENT_CONSOLE_POSITION.Y := - CURRENT_CONSOLE_POSITION.Y + FONT_HEIGHT + LINE_PADDING; - - STM32.Board.Display.Update_Layer (1, True); - end Print; - - procedure CopyLine - (SrcLineNumber : in Natural; DstLineNumer : in Natural; - DeleteSrc : in Boolean) - is - -- TODO: Cleanup later... - srcToPoint : constant HAL.Bitmap.Point := - (0, - Natural'Min - (CONSOLE_STARTING_POINT.Y + - (FONT_HEIGHT + LINE_PADDING) * SrcLineNumber, - GUI.Board_Resolution.Height - 2)); - dstToPoint : constant HAL.Bitmap.Point := - (0, - Natural'Min - (CONSOLE_STARTING_POINT.Y + - (FONT_HEIGHT + LINE_PADDING) * DstLineNumer, - GUI.Board_Resolution.Height - 2)); - srcRect : constant HAL.Bitmap.Rect := - (Position => srcToPoint, Width => GUI.Board_Resolution.Width, - Height => FONT_HEIGHT + LINE_PADDING); - begin - HAL.Bitmap.Copy_Rect - (Src_Buffer => GUI.Screen_Buffer.all, Src_Pt => srcToPoint, - Dst_Buffer => GUI.Screen_Buffer.all, Dst_Pt => dstToPoint, - Width => GUI.Board_Resolution.Width, - Height => FONT_HEIGHT + LINE_PADDING, Synchronous => True); - if DeleteSrc then - HAL.Bitmap.Fill_Rect - (Buffer => GUI.Screen_Buffer.all, Area => srcRect); - end if; - STM32.Board.Display.Update_Layer (1, True); - end CopyLine; - end Console; - + end loop; + end Render; end Debug; diff --git a/eRINA_STM32F7/src/rina/debug.ads b/eRINA_STM32F7/src/rina/debug.ads index b3acf55..58f83ea 100644 --- a/eRINA_STM32F7/src/rina/debug.ads +++ b/eRINA_STM32F7/src/rina/debug.ads @@ -1,22 +1,27 @@ with HAL.Bitmap; with BMP_Fonts; +with Queues; package Debug is type Debug is (Error, Warning, Info); - FONT_HEIGHT : constant Natural := + Font_Height : constant Natural := BMP_Fonts.Char_Height (Font => BMP_Fonts.Font8x8); - MAX_LINES : constant Natural := 20; - LINE_PADDING : constant Natural := 2; + Max_Characters_Per_Line : constant Positive := 50; + Line_Padding : constant Natural := 4; + Starting_Point : constant HAL.Bitmap.Point := (0, 100); + Max_Messages : constant := 14; - protected Console is - procedure Print (Debug_Level : in Debug; Msg : in String); - procedure CopyLine - (SrcLineNumber : in Natural; DstLineNumer : in Natural; - DeleteSrc : in Boolean); - private - CONSOLE_STARTING_POINT : HAL.Bitmap.Point := (0, 100); - CURRENT_CONSOLE_POSITION : HAL.Bitmap.Point := (0, 100); - end Console; + type Message is record + Msg : String(1 .. Max_Characters_Per_Line); + Level : Debug; + Cont : Boolean := False; + end record; + + type Queue_Max is mod Max_Messages; + package Message_Queue is new Queues (Queue_Max, Message); + Messages : Message_Queue.Queue; + procedure Print (Debug_Level : in Debug; Msg : in String); + procedure Render; end Debug; diff --git a/eRINA_STM32F7/src/rina/demo.adb b/eRINA_STM32F7/src/rina/demo.adb index b4ae5b1..7cbe0f2 100644 --- a/eRINA_STM32F7/src/rina/demo.adb +++ b/eRINA_STM32F7/src/rina/demo.adb @@ -5,8 +5,11 @@ with Network; with STM32; with STM32.Board; with Debug; +with Ada.Real_Time; use Ada.Real_Time; procedure Demo is + Period : constant Time_Span := Milliseconds(1 / GUI.Frame_Rate * 1000); + Next_Render : Time := Clock; begin GUI.Initialize; Network.Initialize; @@ -14,24 +17,30 @@ begin STM32.Board.Initialize_LEDs; STM32.Board.All_LEDs_Off; - -- Keep board from immediately terminating + -- Render loop keeps the board from immediately terminating loop - Textures.Print (Textures.PSU.Bitmap, (5, 10)); - GUI.Print ("eRINA Debug", (80, 15)); GUI.Print - ("Ignored Packets: " & Network.Ifnet.Rx_Stats.Ignored'Image, - (80, 30)); - - GUI.Print - ("Status: ", + ("Status: ", (80, 45)); GUI.Print - ("Waiting for enrollment request", + ("Waiting for enrollment request", (145, 45)); - delay Duration(1.0 / GUI.Frame_Rate); + Textures.Print (Textures.PSU.Bitmap, (5, 10)); + + GUI.Print + ("Ignored Packets: " & Network.Ifnet.Rx_Stats.Ignored'Image, + (80, 30)); + + Debug.Render; + GUI.Update; + + -- Set activation time of next render in loop + Next_Render := Next_Render + Period; + + delay until Next_Render; end loop; -end Demo; \ No newline at end of file +end Demo; diff --git a/eRINA_STM32F7/src/rina/gui.adb b/eRINA_STM32F7/src/rina/gui.adb index 860c397..81b65c4 100644 --- a/eRINA_STM32F7/src/rina/gui.adb +++ b/eRINA_STM32F7/src/rina/gui.adb @@ -22,7 +22,7 @@ package body GUI is procedure Update is begin - STM32.Board.Display.Update_Layer (1, True); + STM32.Board.Display.Update_Layer (1); end Update; procedure Print (Msg : in String; Pos : in HAL.Bitmap.Point) is @@ -31,8 +31,6 @@ package body GUI is (Buffer => Screen_Buffer.all, Start => Scale ((Pos.X, Pos.Y)), Msg => Msg, Font => BMP_Fonts.Font8x8, Foreground => Foreground, Background => Background); - - Update; end Print; procedure Initialize is