public static List <ProgrammableRegion> BuildProgrammableBlocksFromSettings(IDebugStartService service, IESP32Settings settings) { List <ProgrammableRegion> blocks; if (BuildProgrammableBlocksFromSynthesizedESPIDFVariables(service, out blocks)) { //Nothing to do. Successfully built the block list. } else { bool patchBootloader = settings.PatchBootloader; blocks = ESP32StartupSequence.BuildFLASHImages(service.TargetPath, service.SystemDictionary, settings.FLASHSettings, patchBootloader); } if (settings.FLASHResources != null) { foreach (var r in settings.FLASHResources) { if (r.Valid) { blocks.Add(r.ToProgrammableRegion(service)); } } } return(blocks); }
public static List <ProgrammableRegion> BuildProgrammableBlocksFromSettings(IDebugStartService service, IESP32Settings settings) { List <ProgrammableRegion> blocks; if (service.MCU.Configuration.TryGetValue("com.sysprogs.esp32.esptool.binaries.count", out var tmp) && int.TryParse(tmp, out var binaryCount) && binaryCount > 0) { blocks = new List <ProgrammableRegion>(); for (int i = 0; i < binaryCount; i++) { string fn = service.MCU.Configuration[$"com.sysprogs.esp32.esptool.binaries[{i}].path"]; blocks.Add(new ProgrammableRegion { FileName = fn, Size = File.ReadAllBytes(fn).Length, Offset = int.Parse(service.MCU.Configuration[$"com.sysprogs.esp32.esptool.binaries[{i}].address"]) }); } } else { bool patchBootloader = settings.PatchBootloader; blocks = ESP32StartupSequence.BuildFLASHImages(service.TargetPath, service.SystemDictionary, settings.FLASHSettings, patchBootloader); } if (settings.FLASHResources != null) { foreach (var r in settings.FLASHResources) { if (r.Valid) { blocks.Add(r.ToProgrammableRegion(service)); } } } return(blocks); }
public IGDBStubInstance StartGDBStub(IDebugStartService startService, DebugStartContext context) { var settings = context.Configuration as ESP32GDBStubSettings; if (settings == null) { throw new Exception("Missing ESP32 stub settings"); } int comPort; if (context.ResolvedDevices?.BestMatch.COMPortNumber.HasValue == true) { comPort = context.ResolvedDevices.BestMatch.COMPortNumber ?? 1; } else { if (settings.COMPort?.StartsWith("COM", StringComparison.InvariantCultureIgnoreCase) != true) { throw new Exception("Invalid COM port specifier: " + settings.COMPort); } comPort = int.Parse(settings.COMPort.Substring(3)); } var regions = ESP32StartupSequence.BuildFLASHImages(startService.TargetPath, startService.SystemDictionary, settings.FLASHSettings, settings.PatchBootloader); if (settings.FLASHResources != null) { foreach (var r in settings.FLASHResources) { if (r.Valid) { regions.Add(r.ToProgrammableRegion(startService)); } } } foreach (var r in regions) { var pythonExe = Path.GetFullPath(context.Method.Directory + @"\..\..\..\..\bin\bash.exe"); string args = $"python /esp32-bsp/esp-idf/components/esptool_py/esptool/esptool.py --port /dev/ttyS{comPort - 1} {settings.AdditionalToolArguments} write_flash"; foreach (var region in regions) { if (region.FileName.Contains(" ")) { throw new Exception($"{region.FileName} contains spaces in path. Cannot program it using esptool.py. Please move your project to a directory without spaces."); } args += $" 0x{region.Offset:x} " + region.FileName.Replace('\\', '/'); } var tool = startService.LaunchCommandLineTool(new CommandLineToolLaunchInfo { Command = pythonExe, Arguments = $"--login -c \"{args}\"", }); return(new GDBStubInstance(context, regions) { Tool = tool }); } throw new OperationCanceledException(); }
public static List <ProgrammableRegion> BuildFLASHImages(BSPEngine.IDebugStartService service, IESP8266Settings settings, BSPEngine.LiveMemoryLineHandler lineHandler) { var bspDict = service.SystemDictionary; var targetPath = service.TargetPath; string bspPath = bspDict["SYS:BSP_ROOT"]; string toolchainPath = bspDict["SYS:TOOLCHAIN_ROOT"]; Regex rgBinFile = new Regex("^" + Path.GetFileName(targetPath) + "-0x([0-9a-fA-F]+)\\.bin$", RegexOptions.IgnoreCase); foreach (var fn in Directory.GetFiles(Path.GetDirectoryName(targetPath))) { if (rgBinFile.IsMatch(Path.GetFileName(fn))) { File.Delete(fn); } } int initDataAddress = 0; if (!string.IsNullOrEmpty(settings.InitDataAddress)) { initDataAddress = (int)(ESP32StartupSequence.TryParseNumber(settings.InitDataAddress) ?? 0); } else { switch (settings.FLASHSettings.Size) { case ESP8266BinaryImage.FLASHSize.size4M: initDataAddress = 0x7c000; break; case ESP8266BinaryImage.FLASHSize.size8M: initDataAddress = 0xfc000; break; case ESP8266BinaryImage.FLASHSize.size16M: case ESP8266BinaryImage.FLASHSize.size16M_c1: initDataAddress = 0x1fc000; break; case ESP8266BinaryImage.FLASHSize.size32M: case ESP8266BinaryImage.FLASHSize.size32M_c1: case ESP8266BinaryImage.FLASHSize.size32M_c2: initDataAddress = 0x3fc000; break; } } List <ProgrammableRegion> regions = new List <ProgrammableRegion>(); if (initDataAddress != 0) { string initFile = settings.InitDataFile; if (initFile != "") { if (initFile == null) { initFile = ESPxxOpenOCDSettingsEditor.DefaultInitDataFile; } if (initFile.StartsWith("$$SYS:BSP_ROOT$$")) { initFile = bspDict["SYS:BSP_ROOT"] + initFile.Substring("$$SYS:BSP_ROOT$$".Length); } if (!Path.IsPathRooted(initFile)) { initFile = Path.Combine(bspDict["SYS:PROJECT_DIR"], initFile); } if (!File.Exists(initFile)) { throw new Exception("Missing initialization data file: " + initFile); } regions.Add(new ProgrammableRegion { FileName = initFile, Offset = initDataAddress, Size = File.ReadAllBytes(initFile).Length }); } } if (settings.FLASHResources != null) { foreach (var r in settings.FLASHResources) { if (r.Valid) { regions.Add(r.ToProgrammableRegion(service)); } } } using (var elfFile = new ELFFile(targetPath)) { string pathBase = Path.Combine(Path.GetDirectoryName(targetPath), Path.GetFileName(targetPath)); string status; int appMode = ESP8266BinaryImage.DetectAppMode(elfFile, out status); if (status != null && lineHandler != null) { lineHandler(status, true); } if (appMode == 0) { var img = ESP8266BinaryImage.MakeNonBootloaderImageFromELFFile(elfFile, settings.FLASHSettings); string fn = pathBase + "-0x00000.bin"; using (var fs = new FileStream(fn, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) { img.Save(fs); regions.Add(new ProgrammableRegion { FileName = fn, Offset = 0, Size = (int)fs.Length }); } foreach (var sec in ESP8266BinaryImage.GetFLASHSections(elfFile)) { fn = string.Format("{0}-0x{1:x5}.bin", pathBase, sec.OffsetInFLASH); using (var fs = new FileStream(fn, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) { fs.Write(sec.Data, 0, sec.Data.Length); regions.Add(new ProgrammableRegion { FileName = fn, Offset = (int)sec.OffsetInFLASH, Size = sec.Data.Length }); } } } else { var img = ESP8266BinaryImage.MakeBootloaderBasedImageFromELFFile(elfFile, settings.FLASHSettings, appMode); var header = settings.FLASHSettings; string bspRoot, bootloader; if (!bspDict.TryGetValue("SYS:BSP_ROOT", out bspRoot) || !bspDict.TryGetValue("com.sysprogs.esp8266.bootloader", out bootloader)) { throw new Exception("Cannot determine bootloader image path. Please check your BSP consistency."); } string fn = Path.Combine(bspRoot, bootloader); if (!File.Exists(fn)) { throw new Exception(fn + " not found. Cannot program OTA images."); } byte[] data = File.ReadAllBytes(fn); data[2] = (byte)header.Mode; data[3] = (byte)(((byte)header.Size << 4) | (byte)header.Frequency); fn = string.Format("{0}-0x00000.bin", pathBase); File.WriteAllBytes(fn, data); regions.Add(new ProgrammableRegion { FileName = fn, Offset = 0, Size = File.ReadAllBytes(fn).Length }); fn = string.Format("{0}-0x{1:x5}.bin", pathBase, img.BootloaderImageOffset); using (var fs = new FileStream(fn, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) { img.Save(fs); regions.Add(new ProgrammableRegion { FileName = fn, Offset = (int)img.BootloaderImageOffset, Size = (int)fs.Length }); } } } return(regions); }
bool LoadFLASH(DebugStartContext context, IDebugStartService service, ISimpleGDBSession session, ESP32OpenOCDSettings settings, ESP32GDBStub stub) { string val; if (!service.SystemDictionary.TryGetValue("com.sysprogs.esp32.load_flash", out val) || val != "1") { //This is a RAM-only configuration return(session.RunGDBCommand("load").IsDone); } else { if (settings.ProgramUsingIDF) { string bash, bashArgs; if (!service.SystemDictionary.TryGetValue("com.sysprogs.esp32.esptool.bash", out bash) || !service.SystemDictionary.TryGetValue("com.sysprogs.esp32.esptool.bash_args", out bashArgs)) { throw new Exception("ESP-IDF did not report esptool arguments"); } if (service.SystemDictionary.TryGetValue("com.sysprogs.esp32.esptool.script", out var script) && File.Exists(script)) { var lines = File.ReadAllLines(script).ToList(); int idx = Enumerable.Range(0, lines.Count).FirstOrDefault(i => lines[i].Contains("def hard_reset(self):")); if (idx > 0 && idx < (lines.Count - 1)) { if (!lines[idx + 1].Contains("self._port.setDTR(False)")) { if (service.GUIService.Prompt("The esptool.py binary used in the current ESP-IDF contains known compatibility issue with Cygwin. Do you want to patch it automatically?")) { Regex rgIndent = new Regex("^([ \t]*)[^ \t]"); var m = rgIndent.Match(lines[idx + 1]); lines.Insert(idx + 1, m.Groups[1].Value + "self._port.setDTR(False) # IO0=HIGH"); File.WriteAllLines(script, lines.ToArray()); } } } } var tool = service.LaunchCommandLineTool(new CommandLineToolLaunchInfo { Command = bash, Arguments = bashArgs }); ESP32StubDebugController.GDBStubInstance.ReportFLASHProgrammingProgress(tool, service, session); string text = tool.AllText; int validLines = text.Split('\n').Count(l => l.Trim() == "Hash of data verified."); int binCount = 0; if (service.MCU.Configuration.TryGetValue("com.sysprogs.esp32.esptool.binaries.count", out var tmp)) { int.TryParse(tmp, out binCount); } binCount = 0; if (validLines < binCount) { service.GUIService.Report("Warning: some of the FLASH regions could not be programmed. Please examine the stub output for details.", System.Windows.Forms.MessageBoxIcon.Warning); } } else { List <ProgrammableRegion> blocks; if (service.MCU.Configuration.TryGetValue("com.sysprogs.esp32.esptool.binaries.count", out var tmp) && int.TryParse(tmp, out var binaryCount) && binaryCount > 0) { blocks = new List <ProgrammableRegion>(); for (int i = 0; i < binaryCount; i++) { string fn = service.MCU.Configuration[$"com.sysprogs.esp32.esptool.binaries[{i}].path"]; blocks.Add(new ProgrammableRegion { FileName = fn, Size = File.ReadAllBytes(fn).Length, Offset = int.Parse(service.MCU.Configuration[$"com.sysprogs.esp32.esptool.binaries[{i}].address"]) }); } } else { bool patchBootloader = (settings as IESP32Settings)?.PatchBootloader ?? false; blocks = ESP32StartupSequence.BuildFLASHImages(service.TargetPath, service.SystemDictionary, settings.FLASHSettings, patchBootloader); } if (settings.FLASHResources != null) { foreach (var r in settings.FLASHResources) { if (r.Valid) { blocks.Add(r.ToProgrammableRegion(service)); } } } Regex rgFLASHSize = new Regex("Auto-detected flash size ([0-9]+) KB"); if (settings.CheckFLASHSize) { var match = stub.Tool.AllText.Split('\n').Select(s => rgFLASHSize.Match(s)).FirstOrDefault(m => m.Success); if (match != null) { int detectedSizeKB = int.Parse(match.Groups[1].Value); int specifiedSizeMB = 0; switch (settings.FLASHSettings.Size) { case ESP8266BinaryImage.ESP32FLASHSize.size1MB: specifiedSizeMB = 1; break; case ESP8266BinaryImage.ESP32FLASHSize.size2MB: specifiedSizeMB = 2; break; case ESP8266BinaryImage.ESP32FLASHSize.size4MB: specifiedSizeMB = 4; break; case ESP8266BinaryImage.ESP32FLASHSize.size8MB: specifiedSizeMB = 8; break; case ESP8266BinaryImage.ESP32FLASHSize.size16MB: specifiedSizeMB = 16; break; } if (detectedSizeKB < (specifiedSizeMB * 1024) && detectedSizeKB >= 1024) { if (service.GUIService.Prompt($"The FLASH size specified via Project Properties is greater than the actual SPI FLASH size on your device. Please switch FLASH size to {detectedSizeKB / 1024}MB or less.\nDo you want to cancel FLASH programming?", System.Windows.Forms.MessageBoxIcon.Warning)) { throw new OperationCanceledException(); } } } } using (var ctx = session.CreateScopedProgressReporter("Programming FLASH...", new[] { "Programming FLASH memory" })) { int blkNum = 0; Regex rgWriteXBytes = new Regex("wrote ([0-9]+) bytes from file"); foreach (var blk in blocks) { ctx.ReportTaskProgress(blkNum++, blocks.Count); string path = blk.FileName.Replace('\\', '/'); var result = session.RunGDBCommand($"mon program_esp32 \"{path}\" 0x{blk.Offset:x}"); bool succeeded = result.StubOutput?.FirstOrDefault(l => l.Contains("** Programming Finished **")) != null; if (!succeeded) { throw new Exception("FLASH programming failed. Please review the gdb/OpenOCD logs for details."); } var m = result.StubOutput.Select(l => rgWriteXBytes.Match(l)).FirstOrDefault(m2 => m2.Success); if (m == null) { throw new Exception("FLASH programming did not report the amount of written bytes. Please review the gdb/OpenOCD logs for details."); } int bytesWritten = int.Parse(m.Groups[1].Value); if (bytesWritten == 0 && blk.Size > 0) { throw new Exception("FLASH programming did not write any data. This is a known bug of ESP32 OpenOCD. Please restart your device and JTAG programmer and try again."); } } } } } if (!session.RunGDBCommand("mon reset halt").IsDone) { throw new Exception("Failed to reset target after programming"); } return(true); }
bool LoadFLASH(DebugStartContext context, IDebugStartService service, ISimpleGDBSession session, ESP32OpenOCDSettings settings, ESP32GDBStub stub) { string val; if (!service.SystemDictionary.TryGetValue("com.sysprogs.esp32.load_flash", out val) || val != "1") { //This is a RAM-only configuration return(session.RunGDBCommand("load").IsDone); } else { var blocks = ESP32StartupSequence.BuildFLASHImages(service.TargetPath, service.SystemDictionary, settings.FLASHSettings, (settings as IESP32Settings)?.PatchBootloader ?? false); if (settings.FLASHResources != null) { foreach (var r in settings.FLASHResources) { if (r.Valid) { blocks.Add(r.ToProgrammableRegion(service)); } } } Regex rgFLASHSize = new Regex("Auto-detected flash size ([0-9]+) KB"); if (settings.CheckFLASHSize) { var match = stub.Tool.AllText.Split('\n').Select(s => rgFLASHSize.Match(s)).FirstOrDefault(m => m.Success); if (match != null) { int detectedSizeKB = int.Parse(match.Groups[1].Value); int specifiedSizeMB = 0; switch (settings.FLASHSettings.Size) { case ESP8266BinaryImage.ESP32FLASHSize.size1MB: specifiedSizeMB = 1; break; case ESP8266BinaryImage.ESP32FLASHSize.size2MB: specifiedSizeMB = 2; break; case ESP8266BinaryImage.ESP32FLASHSize.size4MB: specifiedSizeMB = 4; break; case ESP8266BinaryImage.ESP32FLASHSize.size8MB: specifiedSizeMB = 8; break; case ESP8266BinaryImage.ESP32FLASHSize.size16MB: specifiedSizeMB = 16; break; } if (detectedSizeKB < (specifiedSizeMB * 1024) && detectedSizeKB >= 1024) { if (service.GUIService.Prompt($"The FLASH size specified via Project Properties is greater than the actual SPI FLASH size on your device. Please switch FLASH size to {detectedSizeKB / 1024}MB or less.\nDo you want to cancel FLASH programming?", System.Windows.Forms.MessageBoxIcon.Warning)) { throw new OperationCanceledException(); } } } } using (var ctx = session.CreateScopedProgressReporter("Programming FLASH...", new[] { "Programming FLASH memory" })) { int blkNum = 0; Regex rgWriteXBytes = new Regex("wrote ([0-9]+) bytes from file"); foreach (var blk in blocks) { ctx.ReportTaskProgress(blkNum++, blocks.Count); string path = blk.FileName.Replace('\\', '/'); var result = session.RunGDBCommand($"mon program_esp32 \"{path}\" 0x{blk.Offset:x}"); bool succeeded = result.StubOutput?.FirstOrDefault(l => l.Contains("** Programming Finished **")) != null; if (!succeeded) { throw new Exception("FLASH programming failed. Please review the gdb/OpenOCD logs for details."); } var m = result.StubOutput.Select(l => rgWriteXBytes.Match(l)).FirstOrDefault(m2 => m2.Success); if (m == null) { throw new Exception("FLASH programming did not report the amount of written bytes. Please review the gdb/OpenOCD logs for details."); } int bytesWritten = int.Parse(m.Groups[1].Value); if (bytesWritten == 0 && blk.Size > 0) { throw new Exception("FLASH programming did not write any data. This is a known bug of ESP32 OpenOCD. Please restart your device and JTAG programmer and try again."); } } } } if (!session.RunGDBCommand("mon reset halt").IsDone) { throw new Exception("Failed to reset target after programming"); } return(true); }