Skip to content

Commit 5a963b6

Browse files
committed
Fixed linker script generation for Nordic-based targets
1 parent c4ebe63 commit 5a963b6

File tree

3 files changed

+136
-43
lines changed

3 files changed

+136
-43
lines changed

generators/mbed/MbedBSPGenerator.cs

+126-41
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@ class MbedBSPGenerator
2323

2424
public readonly string Version;
2525

26+
LinkerScriptCache _LinkerScriptCache;
27+
2628
public MbedBSPGenerator(string version)
2729
{
2830
Directory.CreateDirectory(outputDir);
2931
mbedRoot = Path.GetFullPath(Path.Combine(outputDir, "mbed"));
3032
Version = version;
3133

34+
_LinkerScriptCache = new LinkerScriptCache(toolchainDir, mbedRoot);
3235
nameRules = new List<KeyValuePair<Regex, string>>();
3336
foreach (var line in File.ReadAllLines(Path.Combine(dataDir, "DeviceNameRules.txt")))
3437
{
@@ -91,21 +94,6 @@ public void UpdateGitAndRescanTargets(bool skipRescan = false)
9194
if (proc.ExitCode != 0)
9295
throw new Exception("Git exited with code " + proc.ExitCode);
9396

94-
foreach (var lds in Directory.GetFiles(mbedRoot, "*.ld", SearchOption.AllDirectories))
95-
{
96-
if (File.ReadAllText(lds).Contains("\n#if"))
97-
{
98-
ProcessStartInfo preprocessInfo = new ProcessStartInfo($@"{toolchainDir}\bin\arm-eabi-cpp.exe", $"-P -C {lds} -o {lds}.preprocessed");
99-
preprocessInfo.UseShellExecute = false;
100-
preprocessInfo.EnvironmentVariables["PATH"] += $@";{toolchainDir}\bin";
101-
proc = Process.Start(preprocessInfo);
102-
proc.WaitForExit();
103-
104-
File.Copy(lds + ".preprocessed", lds, true);
105-
File.Delete(lds + ".preprocessed");
106-
}
107-
}
108-
10997
var patchedFile = Path.Combine(mbedRoot, @"tools\config\__init__.py");
11098
var lines = File.ReadAllLines(patchedFile).ToList();
11199
var idx2 = Enumerable.Range(0, lines.Count).First(i => lines[i].Contains("self.value = int(value) if isinstance(value, bool) else value"));
@@ -225,28 +213,130 @@ public string[] ConvertPaths(IEnumerable<string> rawPaths)
225213
{
226214
if (fn.Length == mbedRoot.Length && fn.ToLower() == mbedRoot.ToLower())
227215
return "$$SYS:BSP_ROOT$$";
228-
if (fn.StartsWith(mbedRoot, StringComparison.InvariantCulture))
216+
if (fn.StartsWith(mbedRoot, StringComparison.InvariantCultureIgnoreCase))
229217
return "$$SYS:BSP_ROOT$$/" + fn.Substring(mbedRoot.Length + 1).Replace('\\', '/');
230218
throw new Exception("Source path is not inside the mbed BSP: " + fn);
231219
}).ToArray();
232220
}
233221

222+
public string ConvertPath(string physicalPath) => ConvertPaths(new[] { physicalPath })[0];
223+
234224
public struct ConvertedHexFile
235225
{
236226
public uint LoadAddress;
237227
public int Size;
238228
public string RelativePath;
239229
public string SectionName;
230+
231+
public int End => (int)LoadAddress + Size;
240232
}
241233

242234
Dictionary<string, ConvertedHexFile> _ConvertedHexFiles = new Dictionary<string, ConvertedHexFile>();
243-
Dictionary<string, string> _PatchedLinkerScripts = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
244-
Dictionary<string, string> _PatchedLinkerScriptsReverse = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
245235

246-
public void ConvertSoftdevicesAndPatchTarget(MCU mcu, string[] hexFiles)
236+
static int AlignHexFileSize(int size)
247237
{
248-
string baseDir = Path.Combine(mbedRoot, "SysprogsGenerated");
249-
Directory.CreateDirectory(baseDir);
238+
const int alignment = 4096;
239+
return ((size + alignment - 1) / alignment) * alignment;
240+
}
241+
242+
class LinkerScriptCache
243+
{
244+
public readonly string BaseDir;
245+
readonly string _ToolchainDir;
246+
readonly string _MBEDRoot;
247+
248+
public LinkerScriptCache(string toolchainDir, string mbedRoot)
249+
{
250+
_ToolchainDir = toolchainDir;
251+
_MBEDRoot = mbedRoot;
252+
BaseDir = Path.Combine(mbedRoot, "SysprogsGenerated");
253+
Directory.CreateDirectory(BaseDir);
254+
}
255+
256+
Dictionary<KeyValuePair<string, int>, string> _PreprocessedScripts = new Dictionary<KeyValuePair<string, int>, string>();
257+
HashSet<string> _UsedGeneratedPaths = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
258+
259+
public string ProvidePreprocessedLinkerScript(string baseScript, int appStart)
260+
{
261+
baseScript = baseScript.Replace("$$SYS:BSP_ROOT$$", _MBEDRoot);
262+
263+
var key = new KeyValuePair<string, int>(baseScript, appStart);
264+
if (_PreprocessedScripts.TryGetValue(key, out var val))
265+
return val;
266+
267+
if (!File.ReadAllText(baseScript).Contains("\n#if"))
268+
return _PreprocessedScripts[key] = baseScript;
269+
270+
ProcessStartInfo preprocessInfo = new ProcessStartInfo($@"{_ToolchainDir}\bin\arm-eabi-cpp.exe", $"-P -C {baseScript} -o {baseScript}.preprocessed");
271+
if (appStart != 0)
272+
preprocessInfo.Arguments += $" -DMBED_APP_START=0x{appStart:x}";
273+
274+
preprocessInfo.UseShellExecute = false;
275+
preprocessInfo.EnvironmentVariables["PATH"] += $@";{_ToolchainDir}\bin";
276+
var proc = Process.Start(preprocessInfo);
277+
proc.WaitForExit();
278+
279+
string nameBase = $"{BaseDir}\\{Path.GetFileNameWithoutExtension(baseScript)}-{appStart:x}";
280+
for (int iter = 0; iter < 100000; iter++)
281+
{
282+
string newName = nameBase;
283+
if (iter > 0)
284+
newName += $"-{iter}";
285+
286+
newName += ".ld";
287+
if (_UsedGeneratedPaths.Contains(newName))
288+
continue;
289+
290+
_UsedGeneratedPaths.Add(newName);
291+
292+
File.Copy(baseScript + ".preprocessed", newName, true);
293+
File.Delete(baseScript + ".preprocessed");
294+
return _PreprocessedScripts[key] = newName;
295+
}
296+
297+
throw new Exception("Failed to locate a name for preprocessed " + baseScript);
298+
}
299+
300+
Dictionary<string, string> _PatchedScripts = new Dictionary<string, string>();
301+
302+
public string ProvideNameForPatchedScript(string originalScript, out bool alreadyCreated)
303+
{
304+
if (_PatchedScripts.TryGetValue(originalScript, out var result))
305+
{
306+
alreadyCreated = true;
307+
return result;
308+
}
309+
310+
alreadyCreated = false;
311+
string nameBase = $"{BaseDir}\\{Path.GetFileNameWithoutExtension(originalScript)}-sd";
312+
313+
for (int iter = 0; iter < 100000; iter++)
314+
{
315+
string newName = nameBase;
316+
if (iter > 0)
317+
newName += $"-{iter}";
318+
319+
newName += ".ld";
320+
if (_UsedGeneratedPaths.Contains(newName))
321+
continue;
322+
323+
_UsedGeneratedPaths.Add(newName);
324+
return newName;
325+
}
326+
327+
throw new Exception("Failed to locate a name for patched " + originalScript);
328+
}
329+
}
330+
331+
public string PreprocessLinkerScriptIfNeeded(string linkerScript)
332+
{
333+
return _LinkerScriptCache.ProvidePreprocessedLinkerScript(linkerScript, 0);
334+
}
335+
336+
//Returns true if the linker script was preprocessed.
337+
public bool ConvertSoftdevicesAndPatchTarget(MCU mcu, string[] hexFiles)
338+
{
339+
var baseDir = _LinkerScriptCache.BaseDir;
250340
File.WriteAllText(Path.Combine(baseDir, ".mbedignore"), "*");
251341

252342
foreach (var hexFile in hexFiles)
@@ -281,33 +371,25 @@ public void ConvertSoftdevicesAndPatchTarget(MCU mcu, string[] hexFiles)
281371

282372
if (!generatedCFile.StartsWith(mbedRoot, StringComparison.InvariantCultureIgnoreCase))
283373
throw new Exception("HEX file outside mbed root");
284-
_ConvertedHexFiles[hexFile] = new ConvertedHexFile { LoadAddress = parsedFile.LoadAddress, RelativePath = generatedCFile.Substring(mbedRoot.Length + 1), SectionName = sectionName, Size = parsedFile.Data.Length };
374+
_ConvertedHexFiles[hexFile] = new ConvertedHexFile
375+
{
376+
LoadAddress = parsedFile.LoadAddress,
377+
RelativePath = generatedCFile.Substring(mbedRoot.Length + 1),
378+
SectionName = sectionName,
379+
Size = AlignHexFileSize(parsedFile.Data.Length)
380+
};
285381
}
286382

287383
var hexFileList = hexFiles.Select(hf => _ConvertedHexFiles[hf]).ToList();
288384
if (hexFiles.Length == 0)
289-
return;
385+
return false;
290386

291387
hexFileList.Sort((a, b) => a.LoadAddress.CompareTo(b.LoadAddress));
292-
var linkerScript = mcu.CompilationFlags.LinkerScript;
293-
string patchedLinkerScript;
388+
var linkerScript = _LinkerScriptCache.ProvidePreprocessedLinkerScript(mcu.CompilationFlags.LinkerScript, hexFileList.Last().End);
294389

295-
if (!_PatchedLinkerScripts.TryGetValue(linkerScript, out patchedLinkerScript))
390+
string patchedLinkerScript = _LinkerScriptCache.ProvideNameForPatchedScript(linkerScript, out bool scriptAlreadyPatched);
391+
if (!scriptAlreadyPatched)
296392
{
297-
patchedLinkerScript = Path.Combine(baseDir, Path.GetFileName(mcu.CompilationFlags.LinkerScript));
298-
if (_PatchedLinkerScriptsReverse.TryGetValue(patchedLinkerScript, out string tmp) && tmp != linkerScript)
299-
{
300-
for (int i = 2; ; i++)
301-
{
302-
patchedLinkerScript = Path.Combine(baseDir, Path.GetFileNameWithoutExtension(mcu.CompilationFlags.LinkerScript) + "_x" + i + Path.GetExtension(mcu.CompilationFlags.LinkerScript));
303-
if (!_PatchedLinkerScriptsReverse.ContainsKey(patchedLinkerScript))
304-
break;
305-
}
306-
307-
}
308-
_PatchedLinkerScripts[linkerScript] = patchedLinkerScript;
309-
_PatchedLinkerScriptsReverse[patchedLinkerScript] = linkerScript;
310-
311393
List<string> linkerScriptLines = File.ReadAllLines(linkerScript.Replace("$$SYS:BSP_ROOT$$", mbedRoot)).ToList();
312394

313395
int firstMemorySectionLine = Enumerable.Range(0, linkerScript.Length)
@@ -338,13 +420,16 @@ public void ConvertSoftdevicesAndPatchTarget(MCU mcu, string[] hexFiles)
338420
File.WriteAllLines(patchedLinkerScript.Replace("$$SYS:BSP_ROOT$$", mbedRoot), linkerScriptLines);
339421
}
340422

341-
mcu.CompilationFlags.LinkerScript = ConvertPaths(new[] { patchedLinkerScript })[0];
423+
mcu.CompilationFlags.LinkerScript = ConvertPath(patchedLinkerScript);
342424
mcu.AdditionalSourceFiles = mcu.AdditionalSourceFiles.Concat(hexFileList.Select(h => "$$SYS:BSP_ROOT$$/" + h.RelativePath.Replace('\\', '/'))).ToArray();
425+
return true;
343426
}
344427

345428
public void DetectAndApplyMemorySizes(MCU mcu, string linkerScript)
346429
{
347430
Console.Write(".");
431+
linkerScript = _LinkerScriptCache.ProvidePreprocessedLinkerScript(linkerScript, 0);
432+
348433
string tmpFile = Path.GetTempPath() + "LinkerScriptQuery.c";
349434
File.WriteAllText(tmpFile, "");
350435
string mapFile = Path.ChangeExtension(tmpFile, ".map");

generators/mbed/Program.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,11 @@ static void Main(string[] args)
318318
agg.AddedSettingsPerTargets[target.ID] = cfg.Configuration.Subtract(target.BaseConfiguration, cfg.CanonicalKey, cfg.Library != null);
319319
}
320320

321-
generator.ConvertSoftdevicesAndPatchTarget(mcu, target.BaseConfiguration.HexFiles);
321+
if (!generator.ConvertSoftdevicesAndPatchTarget(mcu, target.BaseConfiguration.HexFiles))
322+
{
323+
mcu.CompilationFlags.LinkerScript = generator.ConvertPath(generator.PreprocessLinkerScriptIfNeeded(mcu.CompilationFlags.LinkerScript));
324+
}
325+
322326
generator.CopyAndAttachRegisterDefinitions(mcu);
323327
mcus.Add(mcu);
324328
}

generators/mbed/data/bootloader_scanner.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ def LocateHexFiles(toolchain, resources):
2424
try:
2525
hook = toolchain.target.post_binary_hook['function']
2626
except:
27-
return None
27+
hook = None
2828
if hook == "MCU_NRF51Code.binary_hook":
2929
return LocateNordicSoftdeviceAndBootloader(toolchain, resources)
30+
elif "NORDIC" in toolchain.target.extra_labels:
31+
if len(resources.hex_files) != 1:
32+
raise Exception("Unexpected hex file count for " + t_self.target.name)
33+
return resources.hex_files
3034
return None

0 commit comments

Comments
 (0)