Skip to content

Commit 87a4afc

Browse files
authored
Implement flashing HEX files with JLink (#201)
1 parent e50e46e commit 87a4afc

File tree

3 files changed

+185
-4
lines changed

3 files changed

+185
-4
lines changed

nanoFirmwareFlasher.Library/JLinkCli.cs

Lines changed: 166 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,35 @@ public class JLinkCli
2929
/// </summary>
3030
private const string FlashAddressToken = "{FLASH_ADDRESS}";
3131

32+
/// <summary>
33+
/// Token to replace with address to flash.
34+
/// </summary>
35+
private const string LoadFileListToken = "{LOAD_FILE_LIST}";
36+
3237
/// <summary>
3338
/// Template for JLink command file to flash a file.
3439
/// </summary>
35-
private const string FlashFileCommandTemplate = $@"
40+
private const string FlashSingleFileCommandTemplate = $@"
3641
USB
3742
speed auto
3843
Halt
3944
LoadFile {FilePathToken} {FlashAddressToken}
4045
Reset
4146
Go
4247
Exit
48+
";
49+
50+
/// <summary>
51+
/// Template for JLink command file to flash multiple files.
52+
/// </summary>
53+
private const string FlashMultipleFilesCommandTemplate = $@"
54+
USB
55+
speed auto
56+
Halt
57+
{LoadFileListToken}
58+
Reset
59+
Go
60+
Exit
4361
";
4462

4563
/// <summary>
@@ -240,7 +258,7 @@ public ExitCodes ExecuteFlashBinFiles(
240258
}
241259

242260
// compose JLink command file
243-
var jlinkCmdContent = FlashFileCommandTemplate.Replace(FilePathToken, binFilePath).Replace(FlashAddressToken, addresses.ElementAt(index++));
261+
var jlinkCmdContent = FlashSingleFileCommandTemplate.Replace(FilePathToken, binFilePath).Replace(FlashAddressToken, addresses.ElementAt(index++));
244262
var jlinkCmdFilePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.jlink");
245263

246264
// create file
@@ -293,6 +311,152 @@ public ExitCodes ExecuteFlashBinFiles(
293311
return ExitCodes.OK;
294312
}
295313

314+
/// <summary>
315+
/// Executes an operation that flashes a collection of Intel HEX format files to a connected J-Link device.
316+
/// </summary>
317+
/// <param name="files">List of files to flash to the device.</param>
318+
/// <param name="probeId">ID of the J-Link device to execute the operation into. Leave <see langword="null"/> to use the default address.</param>
319+
/// <returns></returns>
320+
public ExitCodes ExecuteFlashHexFiles(
321+
IList<string> files,
322+
string probeId)
323+
{
324+
// check file existence
325+
if (files.Any(f => !File.Exists(f)))
326+
{
327+
return ExitCodes.E5004;
328+
}
329+
330+
List<string> shadowFiles = new List<string>();
331+
332+
// J-Link can't handle diacritc chars
333+
// developer note: reported to Segger (Case: 60276735) and can be removed if this is fixed/improved
334+
foreach (string hexFile in files)
335+
{
336+
if (!hexFile.IsNormalized(NormalizationForm.FormD) ||
337+
hexFile.Contains(' '))
338+
{
339+
var tempFile = Path.Combine(
340+
Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.Machine),
341+
Path.GetFileName(hexFile));
342+
343+
// copy file to shadow file
344+
File.Copy(
345+
hexFile,
346+
tempFile,
347+
true);
348+
349+
shadowFiles.Add(tempFile);
350+
}
351+
else
352+
{
353+
// copy file to shadow list
354+
shadowFiles.Add(hexFile);
355+
}
356+
}
357+
358+
// erase flash
359+
if (DoMassErase)
360+
{
361+
var eraseResult = ExecuteMassErase(probeId);
362+
363+
if (eraseResult != ExitCodes.OK)
364+
{
365+
return eraseResult;
366+
}
367+
368+
// toggle mass erase so it's only performed before the first file is flashed
369+
DoMassErase = false;
370+
}
371+
372+
if (Verbosity < VerbosityLevel.Normal)
373+
{
374+
Console.ForegroundColor = ConsoleColor.White;
375+
Console.Write("Flashing device...");
376+
}
377+
else
378+
{
379+
Console.ForegroundColor = ConsoleColor.White;
380+
Console.WriteLine("Flashing device...");
381+
}
382+
383+
// program HEX file(s)
384+
StringBuilder listOfFiles = new StringBuilder();
385+
386+
foreach (string hexFile in shadowFiles)
387+
{
388+
// make sure path is absolute
389+
var hexFilePath = Utilities.MakePathAbsolute(
390+
Environment.CurrentDirectory,
391+
hexFile);
392+
393+
if (Verbosity > VerbosityLevel.Normal)
394+
{
395+
Console.ForegroundColor = ConsoleColor.Cyan;
396+
Console.WriteLine($"{Path.GetFileName(hexFilePath)}");
397+
}
398+
399+
listOfFiles.AppendLine($"LoadFile {hexFilePath}");
400+
}
401+
402+
// compose JLink command file
403+
var jlinkCmdContent = FlashMultipleFilesCommandTemplate.Replace(
404+
LoadFileListToken,
405+
listOfFiles.ToString());
406+
407+
var jlinkCmdFilePath = Path.Combine(
408+
Path.GetTempPath(),
409+
$"{Guid.NewGuid()}.jlink");
410+
411+
// create file
412+
var jlinkCmdFile = File.CreateText(jlinkCmdFilePath);
413+
jlinkCmdFile.Write(jlinkCmdContent);
414+
jlinkCmdFile.Close();
415+
416+
var cliOutput = RunJLinkCLI(jlinkCmdFilePath);
417+
418+
// OK to delete the JLink command file
419+
File.Delete(jlinkCmdFilePath);
420+
421+
if (Verbosity >= VerbosityLevel.Normal
422+
&& cliOutput.Contains("Skipped. Contents already match"))
423+
{
424+
Console.ForegroundColor = ConsoleColor.Yellow;
425+
426+
Console.WriteLine("");
427+
Console.WriteLine("******************* WARNING *********************");
428+
Console.WriteLine("Skip flashing. Contents already match the update.");
429+
Console.WriteLine("*************************************************");
430+
Console.WriteLine("");
431+
432+
Console.ForegroundColor = ConsoleColor.White;
433+
}
434+
else if (!(cliOutput.Contains("Flash download: Program & Verify")
435+
&& cliOutput.Contains("O.K.")))
436+
{
437+
ShowCLIOutput(cliOutput);
438+
439+
return ExitCodes.E5006;
440+
}
441+
442+
ShowCLIOutput(cliOutput);
443+
444+
if (Verbosity < VerbosityLevel.Normal)
445+
{
446+
Console.ForegroundColor = ConsoleColor.Green;
447+
Console.WriteLine(" OK");
448+
}
449+
else
450+
{
451+
Console.ForegroundColor = ConsoleColor.Green;
452+
Console.WriteLine("Flashing completed...");
453+
}
454+
455+
Console.ForegroundColor = ConsoleColor.White;
456+
457+
return ExitCodes.OK;
458+
}
459+
296460
public void ShowCLIOutput(string cliOutput)
297461
{
298462
// show CLI output, if verbosity is diagnostic

nanoFirmwareFlasher.Library/JLinkDevice.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,5 +164,16 @@ public ExitCodes FlashBinFiles(IList<string> files, IList<string> addresses)
164164
addresses,
165165
ProbeId);
166166
}
167+
168+
/// <summary>
169+
/// Flash the HEX supplied to the connected device.
170+
/// </summary>
171+
/// <param name="files"></param>
172+
public ExitCodes FlashHexFiles(IList<string> files)
173+
{
174+
return ExecuteFlashHexFiles(
175+
files,
176+
ProbeId);
177+
}
167178
}
168179
}

nanoFirmwareFlasher.Library/JLinkOperations.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,13 @@ public static async System.Threading.Tasks.Task<ExitCodes> UpdateFirmwareAsync(
103103

104104
var connectedSilabsJLinkDevices = JLinkDevice.ListDevices();
105105

106-
if (connectedSilabsJLinkDevices.Any())
106+
if (!connectedSilabsJLinkDevices.Any())
107107
{
108108
// no device was found
109109
return ExitCodes.E9010;
110110
}
111111

112-
// JTAG device
112+
// Jlink device
113113
jlinkDevice = new JLinkDevice(probeId);
114114

115115
if (!jlinkDevice.DevicePresent)
@@ -152,6 +152,12 @@ public static async System.Threading.Tasks.Task<ExitCodes> UpdateFirmwareAsync(
152152
// set verbosity
153153
jlinkDevice.Verbosity = verbosity;
154154

155+
// write HEX files to flash
156+
if (filesToFlash.Any(f => f.EndsWith(".hex")))
157+
{
158+
operationResult = jlinkDevice.FlashHexFiles(filesToFlash);
159+
}
160+
155161
if (operationResult == ExitCodes.OK && isApplicationBinFile)
156162
{
157163
// now program the application file

0 commit comments

Comments
 (0)