├── .gitignore ├── .gitmodules ├── Keil2GCC.cbp ├── Keil2GCC.dsp ├── Keil2GCC.dsw ├── Keil2GCC.sln ├── Keil2GCC.vcproj ├── KeilToARMGCC.cpp ├── KeilToARMGCC.h ├── KeilToGCC.cpp ├── README.md └── Readme before using.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | *.log 4 | *.patch 5 | *.depend 6 | *.layout 7 | Debug 8 | Release 9 | lib 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "PersistenceLibrary"] 2 | path = PersistenceLibrary 3 | url = https://github.com/embedded-tools/PersistenceLibrary.git 4 | -------------------------------------------------------------------------------- /Keil2GCC.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 185 | 186 | -------------------------------------------------------------------------------- /Keil2GCC.dsp: -------------------------------------------------------------------------------- 1 | # Microsoft Developer Studio Project File - Name="Keil2GCCConvertor" - Package Owner=<4> 2 | # Microsoft Developer Studio Generated Build File, Format Version 6.00 3 | # ** DO NOT EDIT ** 4 | 5 | # TARGTYPE "Win32 (x86) Console Application" 0x0103 6 | 7 | CFG=Keil2GCCConvertor - Win32 Debug 8 | !MESSAGE This is not a valid makefile. To build this project using NMAKE, 9 | !MESSAGE use the Export Makefile command and run 10 | !MESSAGE 11 | !MESSAGE NMAKE /f "Keil2GCC.mak". 12 | !MESSAGE 13 | !MESSAGE You can specify a configuration when running NMAKE 14 | !MESSAGE by defining the macro CFG on the command line. For example: 15 | !MESSAGE 16 | !MESSAGE NMAKE /f "Keil2GCC.mak" CFG="Keil2GCCConvertor - Win32 Debug" 17 | !MESSAGE 18 | !MESSAGE Possible choices for configuration are: 19 | !MESSAGE 20 | !MESSAGE "Keil2GCCConvertor - Win32 Release" (based on "Win32 (x86) Console Application") 21 | !MESSAGE "Keil2GCCConvertor - Win32 Debug" (based on "Win32 (x86) Console Application") 22 | !MESSAGE 23 | 24 | # Begin Project 25 | # PROP AllowPerConfigDependencies 0 26 | # PROP Scc_ProjName "" 27 | # PROP Scc_LocalPath "" 28 | CPP=cl.exe 29 | RSC=rc.exe 30 | 31 | !IF "$(CFG)" == "Keil2GCCConvertor - Win32 Release" 32 | 33 | # PROP BASE Use_MFC 0 34 | # PROP BASE Use_Debug_Libraries 0 35 | # PROP BASE Output_Dir "Release" 36 | # PROP BASE Intermediate_Dir "Release" 37 | # PROP BASE Target_Dir "" 38 | # PROP Use_MFC 0 39 | # PROP Use_Debug_Libraries 0 40 | # PROP Output_Dir "Release" 41 | # PROP Intermediate_Dir "Release" 42 | # PROP Target_Dir "" 43 | # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c 44 | # ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c 45 | # ADD BASE RSC /l 0x409 /d "NDEBUG" 46 | # ADD RSC /l 0x409 /d "NDEBUG" 47 | BSC32=bscmake.exe 48 | # ADD BASE BSC32 /nologo 49 | # ADD BSC32 /nologo 50 | LINK32=link.exe 51 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 52 | # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 53 | 54 | !ELSEIF "$(CFG)" == "Keil2GCCConvertor - Win32 Debug" 55 | 56 | # PROP BASE Use_MFC 0 57 | # PROP BASE Use_Debug_Libraries 1 58 | # PROP BASE Output_Dir "Debug" 59 | # PROP BASE Intermediate_Dir "Debug" 60 | # PROP BASE Target_Dir "" 61 | # PROP Use_MFC 0 62 | # PROP Use_Debug_Libraries 1 63 | # PROP Output_Dir "Debug" 64 | # PROP Intermediate_Dir "Debug" 65 | # PROP Ignore_Export_Lib 0 66 | # PROP Target_Dir "" 67 | # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c 68 | # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c 69 | # SUBTRACT CPP /YX /Yc /Yu 70 | # ADD BASE RSC /l 0x409 /d "_DEBUG" 71 | # ADD RSC /l 0x409 /d "_DEBUG" 72 | BSC32=bscmake.exe 73 | # ADD BASE BSC32 /nologo 74 | # SUBTRACT BSC32 /nologo 75 | LINK32=link.exe 76 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept 77 | # ADD LINK32 BasicTypesD.lib FileUtilsD.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept 78 | 79 | !ENDIF 80 | 81 | # Begin Target 82 | 83 | # Name "Keil2GCCConvertor - Win32 Release" 84 | # Name "Keil2GCCConvertor - Win32 Debug" 85 | # Begin Group "Source Files" 86 | 87 | # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" 88 | # Begin Source File 89 | 90 | SOURCE=.\KeilToARMGCC.cpp 91 | # End Source File 92 | # End Group 93 | # Begin Group "Header Files" 94 | 95 | # PROP Default_Filter "h;hpp;hxx;hm;inl" 96 | # Begin Source File 97 | 98 | SOURCE=.\KeilToARMGCC.h 99 | # End Source File 100 | # End Group 101 | # Begin Group "Resource Files" 102 | 103 | # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" 104 | # End Group 105 | # Begin Source File 106 | 107 | SOURCE=.\ReadMe.txt 108 | # End Source File 109 | # End Target 110 | # End Project 111 | -------------------------------------------------------------------------------- /Keil2GCC.dsw: -------------------------------------------------------------------------------- 1 | Microsoft Developer Studio Workspace File, Format Version 6.00 2 | # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! 3 | 4 | ############################################################################### 5 | 6 | Project: "Keil2GCCConvertor"=".\Keil2GCC.dsp" - Package Owner=<4> 7 | 8 | Package=<5> 9 | {{{ 10 | }}} 11 | 12 | Package=<4> 13 | {{{ 14 | }}} 15 | 16 | ############################################################################### 17 | 18 | Global: 19 | 20 | Package=<5> 21 | {{{ 22 | }}} 23 | 24 | Package=<3> 25 | {{{ 26 | }}} 27 | 28 | ############################################################################### 29 | 30 | -------------------------------------------------------------------------------- /Keil2GCC.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Keil2GCC", "Keil2GCC.vcproj", "{A94CAB0E-8882-4378-AB57-7EEDE180FF35}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {A94CAB0E-8882-4378-AB57-7EEDE180FF35}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {A94CAB0E-8882-4378-AB57-7EEDE180FF35}.Debug|Win32.Build.0 = Debug|Win32 14 | {A94CAB0E-8882-4378-AB57-7EEDE180FF35}.Release|Win32.ActiveCfg = Release|Win32 15 | {A94CAB0E-8882-4378-AB57-7EEDE180FF35}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /Keil2GCC.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 26 | 29 | 32 | 35 | 38 | 41 | 53 | 56 | 59 | 62 | 71 | 74 | 77 | 80 | 83 | 86 | 89 | 92 | 93 | 101 | 104 | 107 | 110 | 113 | 116 | 128 | 131 | 134 | 137 | 148 | 151 | 154 | 157 | 160 | 163 | 166 | 169 | 170 | 171 | 172 | 173 | 174 | 179 | 182 | 183 | 186 | 187 | 188 | 193 | 196 | 197 | 198 | 203 | 204 | 207 | 208 | 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /KeilToARMGCC.cpp: -------------------------------------------------------------------------------- 1 | #include "KeilToARMGCC.h" 2 | #include "TFilePath.h" 3 | #include "StringUtils.h" 4 | #include "TTextFile.h" 5 | #include "TXmlDoc.h" 6 | #include "TXmlTagDynamicPool.h" 7 | #include "TRandom.h" 8 | #ifdef WIN32 9 | #include 10 | #else 11 | #include 12 | #include 13 | #include 14 | #endif 15 | #include 16 | 17 | KeilToARMGCC::KeilToARMGCC() 18 | { 19 | m_stackSize = 0; 20 | m_heapSize = 0; 21 | m_useFPU = false; 22 | m_fpuVersion = 0; 23 | m_fileHandle = 0; 24 | m_state = kcsVectors; 25 | } 26 | 27 | void KeilToARMGCC::SetState (KeilConversionState state) 28 | { 29 | m_state = state; 30 | } 31 | 32 | void KeilToARMGCC::ScanLibs(const char* makefilePath, const char* relativePath) 33 | { 34 | #ifdef WIN32 35 | WIN32_FIND_DATAA findData; 36 | HANDLE hFindHandle; 37 | char filter [512]; 38 | char subpath[512]; 39 | 40 | sprintf(filter, "%s%s*.*", makefilePath, relativePath+2); 41 | 42 | hFindHandle = FindFirstFileA(filter, &findData); 43 | if (hFindHandle==INVALID_HANDLE_VALUE) 44 | { 45 | return; 46 | } 47 | 48 | TFilePath filename; 49 | TFilePath fileExt; 50 | TFilePath filedir; 51 | while(true) 52 | { 53 | 54 | if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 55 | { 56 | if (findData.cFileName[0]!='.') 57 | { 58 | if (relativePath==NULL) 59 | { 60 | sprintf(subpath, "%s\\", findData.cFileName ); 61 | } else { 62 | sprintf(subpath, "%s%s\\", relativePath, findData.cFileName ); 63 | } 64 | printf(" %s\r\n", subpath); 65 | 66 | ScanLibs(makefilePath, subpath); 67 | } 68 | } else 69 | { 70 | filename = relativePath; 71 | filename += findData.cFileName; 72 | filename.ChangeSeparator('/'); 73 | filedir = filename.ExtractFileDirectory(); 74 | fileExt = filename.ExtractFileExt(); 75 | if (filedir.LastChar()=='/') 76 | { 77 | filedir.SetLength(filedir.Length()-1); 78 | } 79 | if ((fileExt==".c") || (fileExt==".cpp")) 80 | { 81 | printf(" %s\r\n", filename); 82 | if (m_srcList.IndexOf(filename)==-1) 83 | { 84 | m_srcList.Add(filename); 85 | } 86 | } 87 | if (fileExt==".h") 88 | { 89 | printf(" %s\r\n", filename); 90 | if (m_incList.IndexOf(filedir)==-1) 91 | { 92 | m_incList.Add(filedir); 93 | } 94 | } 95 | } 96 | if (!FindNextFileA(hFindHandle, &findData)) break; 97 | } 98 | FindClose(hFindHandle); 99 | #else 100 | struct dirent *findData = nullptr; 101 | DIR *hFindHandle = nullptr; 102 | char filter [512]; 103 | char subpath[512]; 104 | 105 | sprintf(filter, "%s%s*.*", makefilePath, relativePath+2); 106 | 107 | hFindHandle = opendir(filter); 108 | if (hFindHandle == nullptr) 109 | { 110 | return; 111 | } 112 | 113 | TFilePath filename; 114 | TFilePath fileExt; 115 | TFilePath filedir; 116 | while((findData = readdir(hFindHandle)) != nullptr) 117 | { 118 | if (findData->d_type == DT_DIR) 119 | { 120 | if (findData->d_name[0]!='.') 121 | { 122 | if (relativePath==NULL) 123 | { 124 | sprintf(subpath, "%s\\", findData->d_name ); 125 | } else { 126 | sprintf(subpath, "%s%s\\", relativePath, findData->d_name ); 127 | } 128 | printf(" %s\r\n", subpath); 129 | 130 | ScanLibs(makefilePath, subpath); 131 | } 132 | } else 133 | { 134 | filename = relativePath; 135 | filename += findData->d_name; 136 | filename.ChangeSeparator('/'); 137 | filedir = filename.ExtractFileDirectory(); 138 | fileExt = filename.ExtractFileExt(); 139 | if (filedir.LastChar()=='/') 140 | { 141 | filedir.SetLength(filedir.Length()-1); 142 | } 143 | if ((fileExt==".c") || (fileExt==".cpp")) 144 | { 145 | printf(" %s\r\n", filename); 146 | if (m_srcList.IndexOf(filename)==-1) 147 | { 148 | m_srcList.Add(filename); 149 | } 150 | } else if (fileExt==".h") 151 | { 152 | printf(" %s\r\n", filename); 153 | if (m_incList.IndexOf(filedir)==-1) 154 | { 155 | m_incList.Add(filedir); 156 | } 157 | } 158 | } 159 | } 160 | closedir(hFindHandle); 161 | #endif 162 | } 163 | 164 | void KeilToARMGCC::DoConversion(const char* uv4ProjectFile, 165 | int keilTargetIndex, 166 | const char* targetDirectory, 167 | bool forceSoftFPU, 168 | bool scanlibs) 169 | { 170 | bool res; 171 | bool noErrors = true; 172 | 173 | TFilePath projectFile, projectPath; 174 | projectFile.SetLength(512); 175 | #ifdef WIN32 176 | GetFullPathNameA(uv4ProjectFile, 512, (LPSTR)projectFile.ToPChar(), NULL); 177 | #else 178 | realpath(uv4ProjectFile, (char *)projectFile.ToPChar()); 179 | #endif 180 | projectFile.SetLength(strlen(projectFile.ToPChar())); 181 | 182 | projectPath = projectFile; 183 | projectPath.ChangeFileName(""); 184 | 185 | TFilePath targetFile = projectPath + targetDirectory; 186 | TFilePath targetPath = targetFile.ExtractFileDirectory(); 187 | if (targetFile==targetPath) 188 | { 189 | //target file does not contain filename, just a directory 190 | //in such case is necessary to add a filename 191 | targetFile += "makefile"; 192 | } 193 | 194 | res = ParseKeilProjectSettings(projectFile, keilTargetIndex, targetDirectory, forceSoftFPU); 195 | if (!res) 196 | { 197 | printf("\r\nKeil project parsing failed\r\n"); 198 | return; 199 | } 200 | if (m_stackSize==0) m_stackSize = 512; 201 | if (m_heapSize==0) m_heapSize = 512; 202 | 203 | if (scanlibs) 204 | { 205 | printf("\r\nSearching for source codes in directories:\r\n"); 206 | printf(" .\\\r\n"); 207 | ScanLibs(targetPath, ".\\"); 208 | printf("All files added to makefile file list.\r\n"); 209 | } 210 | m_incList.Sort(); 211 | m_srcList.Sort(); 212 | 213 | TString deviceName = m_cpuType; 214 | deviceName.LowerCase(); 215 | for(int i = 0; i='a')&&(deviceName[i]<='z')) continue; 218 | if ((deviceName[i]>='A')&&(deviceName[i]<='Z')) continue; 219 | if ((deviceName[i]>='0')&&(deviceName[i]<='9')) continue; 220 | deviceName[i]='_'; 221 | } 222 | 223 | //is startup file a gcc version? 224 | bool gcc = m_startupFile.Contains("/gcc/") || m_startupFile.Contains("/gcc_"); 225 | if (!gcc) 226 | { 227 | //new startup file is generated from 228 | //previously parsed irq handlers 229 | m_startupFile = "gcc_startupfile_"; 230 | m_startupFile += deviceName; 231 | m_startupFile += ".s"; 232 | 233 | res = CreateStartupFile(targetPath + m_startupFile); 234 | if (!res) 235 | { 236 | printf("\r\nStartup file creating failed\r\n"); 237 | noErrors = false; 238 | } 239 | } 240 | 241 | m_ldScriptFile = "gcc_linkerfile_"; 242 | m_ldScriptFile += deviceName; 243 | m_ldScriptFile += ".ld"; 244 | 245 | res = CreateLDScript(targetPath + m_ldScriptFile); 246 | if (!res) 247 | { 248 | printf("\r\nLD file creating failed\r\n"); 249 | noErrors = false; 250 | } 251 | 252 | res = CreateJLinkFile(targetPath + "flash.jlink"); 253 | if (!res) 254 | { 255 | printf("\r\nJLink file not created\r\n"); 256 | noErrors = false; 257 | } 258 | 259 | res = CreateMakeFile(targetFile); 260 | if (!res) 261 | { 262 | printf("\r\nMakefile not created\r\n"); 263 | noErrors = false; 264 | } 265 | if (noErrors) 266 | { 267 | printf("\r\nARMGCC makefile created successfully.\r\n"); 268 | } else { 269 | printf("\r\nARMGCC makefile created with errors, errors need to be fixed manually.\r\n"); 270 | } 271 | } 272 | 273 | 274 | bool KeilToARMGCC::ParseKeilProjectSettings(const char* keilProjectFile, int keilTargetIndex, const char* targetFile, bool forceSoftFPU) 275 | { 276 | int i1, i2, i3; 277 | 278 | TTextFile textfile; 279 | TString line; 280 | 281 | TXMLTagDynamicPool xmlPool; 282 | TXMLDoc keilProjectXml; 283 | keilProjectXml.SetPool(&xmlPool); 284 | 285 | TFilePath targetPath = targetFile; 286 | targetPath = targetPath.ExtractFileDirectory(); 287 | targetPath.ChangeSeparator('/'); 288 | 289 | bool res = keilProjectXml.LoadFromFile(keilProjectFile); 290 | if (!res) 291 | { 292 | printf("Project file can not be loaded.\r\n"); 293 | return false; 294 | } 295 | 296 | TXMLTag* header = keilProjectXml.Header(); 297 | 298 | 299 | TXMLTag* root = keilProjectXml.Root(); 300 | if (root==NULL) 301 | { 302 | printf("Project file has no xml root.\r\n"); 303 | return false; 304 | } 305 | 306 | TXMLTag* targets = root->SelectNode("Targets"); 307 | if (targets==NULL) 308 | { 309 | printf("Embedded target not found\r\n"); 310 | return false; 311 | } 312 | 313 | TXMLTagList* targetList = targets->SelectNodes("Target"); 314 | 315 | TXMLTag* target = targetList->First(); 316 | if (target==NULL) 317 | { 318 | printf("Embedded target not found\r\n"); 319 | return false; 320 | } 321 | 322 | TXMLTag* targetNameTag = target->SelectNode("TargetName"); 323 | if (targetNameTag) 324 | { 325 | m_targetName = targetNameTag->GetValue(); 326 | } else { 327 | m_targetName = "NotNamed"; 328 | } 329 | 330 | for(int i = 0; i='a') && (m_targetName[i]<='z')) continue; 333 | if ((m_targetName[i]>='A') && (m_targetName[i]<='Z')) continue; 334 | if ((m_targetName[i]>='0') && (m_targetName[i]<='9')) continue; 335 | m_targetName[i] = '_'; 336 | } 337 | 338 | //include paths 339 | TXMLTag* variousControlsTag = target->SelectNode("TargetOption/TargetArmAds/Cads/VariousControls"); 340 | if (variousControlsTag) 341 | { 342 | int separatorPosition = 0; 343 | TXMLTag* defineTag = variousControlsTag->SelectNode("Define"); 344 | if (defineTag) 345 | { 346 | TString defines = defineTag->GetValue(); 347 | TString define; 348 | while(true) 349 | { 350 | separatorPosition = defines.IndexOf(','); 351 | if (separatorPosition==-1) 352 | { 353 | separatorPosition = defines.IndexOf(';'); 354 | if (separatorPosition==-1) 355 | { 356 | separatorPosition = defines.IndexOf(' '); 357 | } 358 | } 359 | if (separatorPosition>=0) 360 | { 361 | define.CopyFrom(defines.ToPChar(), separatorPosition); 362 | define.Trim(); 363 | m_defineList.Add(define); 364 | defines.Delete(0, separatorPosition+1); 365 | } else break; 366 | } 367 | if (defines.Length()>0) 368 | { 369 | defines.Trim(); 370 | m_defineList.Add(defines); 371 | } 372 | } 373 | separatorPosition = 0; 374 | TXMLTag* includeTag = variousControlsTag->SelectNode("IncludePath"); 375 | 376 | TFilePath target = targetFile; 377 | target.ChangeSeparator('/'); 378 | if (includeTag) 379 | { 380 | TString includes = includeTag->GetValue(); 381 | TString include; 382 | TString tmp; 383 | while(true) 384 | { 385 | separatorPosition = includes.IndexOf(';'); 386 | if (separatorPosition>=0) 387 | { 388 | include.CopyFrom(includes.ToPChar(), separatorPosition); 389 | include.Trim(); 390 | tmp = targetPath + include; 391 | if ((tmp.LastChar()=='\\') || (tmp.LastChar()=='/')) 392 | { 393 | tmp.SetLength(tmp.Length()-1); 394 | } 395 | int n = tmp.IndexOf("portable/RVDS"); 396 | if (n>0) 397 | { 398 | for(int i = n+10; i0) 412 | { 413 | includes.Trim(); 414 | m_incList.Add(targetPath + includes); 415 | } 416 | } 417 | } 418 | 419 | //cpu name 420 | TXMLTag* deviceTag = target->SelectNode("TargetOption/TargetCommonOption/Device"); 421 | if (deviceTag) 422 | { 423 | m_cpuType = deviceTag->GetValue(); 424 | } else { 425 | printf("CPU Type not found\r\n"); 426 | } 427 | 428 | TXMLTag* vendorTag = target->SelectNode("TargetOption/TargetCommonOption/Vendor"); 429 | if (vendorTag) 430 | { 431 | m_cpuVendor = vendorTag->GetValue(); 432 | } else { 433 | printf("CPU Vendor not found\r\n"); 434 | } 435 | 436 | //fpu present 437 | TXMLTag* cpuTag = target->SelectNode("TargetOption/TargetCommonOption/Cpu"); 438 | if (!cpuTag) 439 | { 440 | printf("CPU info not found\r\n"); 441 | return false; 442 | } 443 | 444 | TString cpuValue = cpuTag->GetValue(); 445 | 446 | 447 | //cpu type 448 | i1 = cpuValue.IndexOf("CPUTYPE"); 449 | i1 = cpuValue.IndexOf('"', i1); 450 | i2 = cpuValue.IndexOf(")",i1); 451 | if ((i1>0) && (i2>i1)) 452 | { 453 | m_cpuPlatform.CopyFrom(cpuValue.ToPChar()+i1+1, i2-i1-2); 454 | m_cpuPlatform.LowerCase(); 455 | } 456 | 457 | m_fpuPresent = false; 458 | m_fpuVersion = 0; 459 | if (m_cpuPlatform.Contains("cortex-m4")) 460 | { 461 | m_fpuPresent = true; 462 | m_fpuVersion = 4; 463 | } 464 | if (m_cpuPlatform.Contains("cortex-m7")) 465 | { 466 | m_fpuPresent = true; 467 | m_fpuVersion = 5; 468 | } 469 | if (m_cpuPlatform.Contains("cortex-h7")) 470 | { 471 | m_fpuPresent = true; 472 | m_fpuVersion = 5; 473 | } 474 | if (forceSoftFPU) 475 | { 476 | m_fpuPresent = false; 477 | } 478 | 479 | MemoryFragment memFragment; 480 | memset(&memFragment, 0, sizeof(memFragment)); 481 | 482 | TXMLTag* armAds = target->SelectNode("TargetOption/TargetArmAds/ArmAdsMisc"); 483 | 484 | //rom 485 | i1 = cpuValue.IndexOf("IROM("); 486 | i2 = cpuValue.IndexOf("-",i1+5); 487 | i3 = cpuValue.IndexOf(")",i1+5); 488 | if ((i2==-1) || (i2>i3)) 489 | { 490 | i2 = cpuValue.IndexOf(',', i1+5); 491 | } 492 | if ((i1>0) && (i2>i1) && (i3>i2)) 493 | { 494 | i1+=5; 495 | memFragment.beginAddress = HexToULongInt(cpuValue.ToPChar()+i1, i2-i1); 496 | memFragment.endAddress = HexToULongInt(cpuValue.ToPChar()+i2+1, i3-i2-1); 497 | m_romFragments.Add(memFragment); 498 | } else { 499 | //alternative way how to find ROM address and size (MDK v5) 500 | if (armAds) 501 | { 502 | if (FindMdk5IRom(armAds, memFragment.beginAddress, memFragment.endAddress)) 503 | { 504 | m_romFragments.Add(memFragment); 505 | } 506 | } 507 | if (m_romFragments.Count()==0) 508 | { 509 | memFragment.beginAddress = 0x8100000; 510 | memFragment.endAddress = memFragment.beginAddress + 0x8000; 511 | m_romFragments.Add(memFragment); 512 | printf("ROM address not found, inserted address 0x8100000 and size 0x8000 instead\r\n"); 513 | } 514 | } 515 | 516 | //ram 517 | i1 = cpuValue.IndexOf("IRAM("); 518 | i1 = cpuValue.IndexOf("(",i1); 519 | i2 = cpuValue.IndexOf("-",i1); 520 | i3 = cpuValue.IndexOf(")",i1); 521 | if ((i2==-1) || (i2>i3)) 522 | { 523 | i2 = cpuValue.IndexOf(',', i1); 524 | } 525 | if ((i1>0) && (i2>i1) && (i3>i2)) 526 | { 527 | i1++; 528 | memFragment.beginAddress = HexToULongInt(cpuValue.ToPChar()+i1, i2-i1); 529 | memFragment.endAddress = HexToULongInt(cpuValue.ToPChar()+i2+1, i3-i2-1); 530 | m_ramFragments.Add(memFragment); 531 | } else { 532 | if (armAds) 533 | { 534 | if (FindMdk5IRam1(armAds, memFragment.beginAddress, memFragment.endAddress)) 535 | { 536 | m_ramFragments.Add(memFragment); 537 | } 538 | } 539 | if (m_ramFragments.Count()==0) 540 | { 541 | memFragment.beginAddress = 0x8200000; 542 | memFragment.endAddress = memFragment.beginAddress + 0x4000; 543 | m_ramFragments.Add(memFragment); 544 | printf("RAM address not found, inserted address 0x8200000 and size 0x4000 instead\r\n"); 545 | } 546 | } 547 | 548 | //ccm ram 549 | i1 = cpuValue.IndexOf("IRAM2"); 550 | i1 = cpuValue.IndexOf("(",i1); 551 | i2 = cpuValue.IndexOf("-",i1); 552 | i3 = cpuValue.IndexOf(")",i1); 553 | if ((i2==-1) || (i2>i3)) 554 | { 555 | i2 = cpuValue.IndexOf(',', i1); 556 | } 557 | if ((i1>0) && (i2>i1) && (i3>i2)) 558 | { 559 | i1++; 560 | memFragment.beginAddress = HexToULongInt(cpuValue.ToPChar()+i1, i2-i1); 561 | memFragment.endAddress = HexToULongInt(cpuValue.ToPChar()+i2+1, i3-i2-1); 562 | m_ramFragments.Add(memFragment); 563 | } else { 564 | if (armAds) 565 | { 566 | if (FindMdk5IRam2(armAds, memFragment.beginAddress, memFragment.endAddress)) 567 | { 568 | m_ramFragments.Add(memFragment); 569 | } 570 | } 571 | } 572 | 573 | //cpu clock 574 | i1 = cpuValue.IndexOf("CLOCK"); 575 | i1 = cpuValue.IndexOf("(",i1); 576 | i2 = cpuValue.IndexOf(")",i1); 577 | if ((i1>0) && (i2>i1)) 578 | { 579 | i1++; 580 | m_cpuClock = StrToULongInt(cpuValue.ToPChar()+i1, (unsigned short)(i2-i1)); 581 | } else{ 582 | printf("Clock frequency not found\r\n"); 583 | } 584 | 585 | TXMLTag* groupsTag = target->SelectNode("Groups"); 586 | TXMLTagList* groupTagList = groupsTag->SelectNodes("Group"); 587 | TFilePath filename; 588 | TString fileExt; 589 | 590 | for(TXMLTag* groupTag = groupTagList->First(); groupTag!=NULL; groupTag=groupTagList->Next()) 591 | { 592 | TXMLTag* filesTag = groupTag->SelectNode("Files"); 593 | if (filesTag == NULL) continue; 594 | 595 | TXMLTagList* fileTagList = filesTag->SelectNodes("File"); 596 | if (fileTagList==NULL) continue; 597 | 598 | for(TXMLTag* fileTag = fileTagList->First(); fileTag!=NULL; fileTag=fileTagList->Next()) 599 | { 600 | TXMLTag* fileNameTag = fileTag->SelectNode("FileName"); 601 | TXMLTag* filePathTag = fileTag->SelectNode("FilePath"); 602 | if (fileNameTag && filePathTag) 603 | { 604 | filename = filePathTag->GetValue(); 605 | filename.DeleteDoubleSlash(); 606 | filename.Trim(); 607 | if (filename.Contains("startup_")) 608 | { 609 | if (m_startupFile.Length()>0) 610 | { 611 | printf("Project definition contains more startup files!"); 612 | } 613 | if (filename.Contains("/arm/")) 614 | { 615 | //startup file is designed for keil uvision 616 | //checks if gcc version is present 617 | TString gccRelativePath = filename; 618 | int n = gccRelativePath.IndexOf("/arm/"); 619 | if (n>0) 620 | { 621 | gccRelativePath[n+1] = 'g'; 622 | gccRelativePath[n+2] = 'c'; 623 | gccRelativePath[n+3] = 'c'; 624 | } 625 | TFilePath gccfile = targetPath + gccRelativePath; 626 | FILE* file = fopen(gccfile.ToPChar(), "rb"); 627 | if (file) 628 | { 629 | fclose(file); 630 | //gcc version exists 631 | filename = gccfile; 632 | } 633 | } 634 | m_startupFile = filename; 635 | 636 | } else { 637 | fileExt = filename.ExtractFileExt(); 638 | fileExt.LowerCase(); 639 | if ((fileExt==".c") || (fileExt==".cpp")) 640 | { 641 | TFilePath file = targetPath + filename; 642 | int n = file.IndexOf("portable/RVDS"); 643 | if (n>0) 644 | { 645 | for(int i = n+10; iSelectNode("Ro1Chk"); 734 | romAddressTag = xmlTag->SelectNode("OnChipMemories/OCR_RVCT1"); 735 | result = ReadMemoryBlock(romEnabledTag, romAddressTag, startAddress, endAddress); 736 | if (!result) 737 | { 738 | romEnabledTag = xmlTag->SelectNode("Ro2Chk"); 739 | romAddressTag = xmlTag->SelectNode("OnChipMemories/OCR_RVCT2"); 740 | result = ReadMemoryBlock(romEnabledTag, romAddressTag, startAddress, endAddress); 741 | } 742 | if (!result) 743 | { 744 | romEnabledTag = xmlTag->SelectNode("Ro3Chk"); 745 | romAddressTag = xmlTag->SelectNode("OnChipMemories/OCR_RVCT3"); 746 | result = ReadMemoryBlock(romEnabledTag, romAddressTag, startAddress, endAddress); 747 | } 748 | if (!result) 749 | { 750 | romEnabledTag = xmlTag->SelectNode("Ir1Chk"); 751 | romAddressTag = xmlTag->SelectNode("OnChipMemories/OCR_RVCT4"); 752 | result = ReadMemoryBlock(romEnabledTag, romAddressTag, startAddress, endAddress); 753 | } 754 | if (!result) 755 | { 756 | romEnabledTag = xmlTag->SelectNode("Ir2Chk"); 757 | romAddressTag = xmlTag->SelectNode("OnChipMemories/OCR_RVCT5"); 758 | result = ReadMemoryBlock(romEnabledTag, romAddressTag, startAddress, endAddress); 759 | } 760 | return result; 761 | } 762 | 763 | bool KeilToARMGCC::FindMdk5IRam1(TXMLTag* xmlTag, unsigned long &startAddress, unsigned long& endAddress) 764 | { 765 | return FindMdk5IRam(xmlTag, 0, startAddress, endAddress); 766 | } 767 | 768 | bool KeilToARMGCC::FindMdk5IRam2(TXMLTag* xmlTag, unsigned long &startAddress, unsigned long& endAddress) 769 | { 770 | return FindMdk5IRam(xmlTag, 1, startAddress, endAddress); 771 | } 772 | 773 | bool KeilToARMGCC::FindMdk5IRam(TXMLTag* xmlTag, unsigned long memoryIndex, unsigned long& startAddress, unsigned long& endAddress) 774 | { 775 | TXMLTag* ramEnabledTag = NULL; 776 | TXMLTag* ramAddressTag = NULL; 777 | 778 | for(int i = 0; i<5; i++) 779 | { 780 | switch(i) 781 | { 782 | case 0: 783 | { 784 | ramEnabledTag = xmlTag->SelectNode("Ra1Chk"); 785 | ramAddressTag = xmlTag->SelectNode("OnChipMemories/OCR_RVCT6"); 786 | } 787 | break; 788 | 789 | case 1: 790 | { 791 | ramEnabledTag = xmlTag->SelectNode("Ra2Chk"); 792 | ramAddressTag = xmlTag->SelectNode("OnChipMemories/OCR_RVCT7"); 793 | } 794 | break; 795 | 796 | case 2: 797 | { 798 | ramEnabledTag = xmlTag->SelectNode("Ra3Chk"); 799 | ramAddressTag = xmlTag->SelectNode("OnChipMemories/OCR_RVCT8"); 800 | } 801 | break; 802 | 803 | case 3: 804 | { 805 | ramEnabledTag = xmlTag->SelectNode("Im1Chk"); 806 | ramAddressTag = xmlTag->SelectNode("OnChipMemories/OCR_RVCT9"); 807 | } 808 | break; 809 | 810 | case 4: 811 | { 812 | ramEnabledTag = xmlTag->SelectNode("Im2Chk"); 813 | ramAddressTag = xmlTag->SelectNode("OnChipMemories/OCR_RVCT10"); 814 | } 815 | break; 816 | } 817 | if ((ramEnabledTag) && (ramAddressTag)) 818 | { 819 | if (ReadMemoryBlock(ramEnabledTag, ramAddressTag, startAddress, endAddress)) 820 | { 821 | if (memoryIndex==0) return true; 822 | memoryIndex--; 823 | } 824 | } 825 | } 826 | return false; 827 | } 828 | 829 | bool KeilToARMGCC::ReadMemoryBlock(TXMLTag* ramEnabledTag, TXMLTag* ramAddressTag, unsigned long& startAddress, unsigned long& endAddress) 830 | { 831 | startAddress = -1; 832 | endAddress = 0; 833 | int blockLength = -1; 834 | const char* enabled = ramEnabledTag->GetValue(); 835 | if (enabled) 836 | { 837 | if (enabled[0]!='0') 838 | { 839 | TXMLTag* startAddressTag = ramAddressTag->SelectNode("StartAddress"); 840 | if (startAddressTag) 841 | { 842 | startAddress = HexToULongInt(startAddressTag->GetValue()); 843 | } 844 | TXMLTag* sizeTag = ramAddressTag->SelectNode("Size"); 845 | if (sizeTag) 846 | { 847 | blockLength = HexToULongInt(sizeTag->GetValue()); 848 | } 849 | if ((startAddress>=0) && (blockLength>0)) 850 | { 851 | endAddress = startAddress + blockLength; 852 | return true; 853 | } 854 | } 855 | } 856 | return false; 857 | } 858 | 859 | 860 | bool KeilToARMGCC::CreateStartupFile(const char* startUpFileName) 861 | { 862 | int i; 863 | TString irq; 864 | 865 | m_fileHandle = fopen(startUpFileName, "wb"); 866 | if (m_fileHandle==NULL) 867 | { 868 | return false; 869 | } 870 | 871 | WriteLine("/** Header file generated by Keil2GCC converter.\r\n"); 872 | WriteLine(" * \r\n"); 873 | WriteLine(" * GENERATED STARTUP FILE WITH ISR VECTORS IS PROVIDED \"AS IS\" AND ANY EXPRESS\r\n"); 874 | WriteLine(" * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r\n"); 875 | WriteLine(" * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r\n"); 876 | WriteLine(" * ARE DISCLAIMED.\r\n"); 877 | WriteLine("*/\r\n"); 878 | WriteLine("\r\n"); 879 | WriteLine(" .syntax unified\r\n"); 880 | WriteLine(" .cpu "); WriteLine(m_cpuPlatform.ToPChar()); WriteLine("\r\n"); 881 | WriteLine(" .fpu softvfp\r\n"); 882 | WriteLine(" .thumb\r\n"); 883 | WriteLine("\r\n"); 884 | WriteLine(".global g_pfnVectors\r\n"\ 885 | ".global Default_Handler\r\n"\ 886 | "\r\n"\ 887 | "/* start address for the initialization values of the .data section.\r\n"\ 888 | "defined in linker script */\r\n"\ 889 | ".word _sidata\r\n"\ 890 | "/* start address for the .data section. defined in linker script */\r\n"\ 891 | ".word _sdata\r\n"\ 892 | "/* end address for the .data section. defined in linker script */\r\n"\ 893 | ".word _edata\r\n"\ 894 | "/* start address for the .bss section. defined in linker script */\r\n"\ 895 | ".word _sbss\r\n"\ 896 | "/* end address for the .bss section. defined in linker script */\r\n"\ 897 | ".word _ebss\r\n"\ 898 | "/* stack used for SystemInit_ExtMemCtl; always internal RAM used */\r\n"\ 899 | "\r\n"\ 900 | "/**\r\n"\ 901 | " * @brief This is the code that gets called when the processor first\r\n"\ 902 | " * starts execution following a reset event. Only the absolutely\r\n"\ 903 | " * necessary set is performed, after which the application\r\n"\ 904 | " * supplied main() routine is called.\r\n"\ 905 | " * @param None\r\n"\ 906 | " * @retval : None\r\n"\ 907 | "*/\r\n"\ 908 | "\r\n" 909 | ); 910 | 911 | WriteLine(" .section .text.Reset_Handler\r\n"); 912 | WriteLine(" .weak Reset_Handler\r\n"); 913 | WriteLine(" .type Reset_Handler, %function\r\n"); 914 | WriteLine("Reset_Handler:\r\n"); 915 | WriteLine("/* Disable all interrupts.*/\r\n"); 916 | WriteLine(" cpsid i\r\n"); 917 | WriteLine("\r\n"); 918 | WriteLine("/* Set stack pointer */\r\n"); 919 | if (m_cpuPlatform=="cortex-m0") 920 | { 921 | WriteLine(" ldr r0, =_estack\r\n"); 922 | WriteLine(" mov sp, r0\r\n"); 923 | } else { 924 | WriteLine(" ldr sp, =_estack\r\n"); 925 | } 926 | WriteLine("\r\n"); 927 | WriteLine("/* Copy the data segment initializers from flash to SRAM */\r\n"); 928 | WriteLine(" movs r1, #0\r\n"); 929 | WriteLine(" b LoopCopyDataInit\r\n"); 930 | WriteLine("\r\n"); 931 | WriteLine("CopyDataInit:\r\n"); 932 | WriteLine(" ldr r3, =_sidata\r\n"); 933 | WriteLine(" ldr r3, [r3, r1]\r\n"); 934 | WriteLine(" str r3, [r0, r1]\r\n"); 935 | WriteLine(" adds r1, r1, #4\r\n"); 936 | WriteLine("\r\n"); 937 | WriteLine("LoopCopyDataInit:\r\n"); 938 | WriteLine(" ldr r0, =_sdata\r\n"); 939 | WriteLine(" ldr r3, =_edata\r\n"); 940 | WriteLine(" adds r2, r0, r1\r\n"); 941 | WriteLine(" cmp r2, r3\r\n"); 942 | WriteLine(" bcc CopyDataInit\r\n"); 943 | WriteLine(" ldr r2, =_sbss\r\n"); 944 | WriteLine(" b LoopFillZerobss\r\n"); 945 | WriteLine("/* Zero fill the bss segment. */\r\n"); 946 | WriteLine("FillZerobss:\r\n"); 947 | WriteLine(" movs r3, #0\r\n"); 948 | if (m_cpuPlatform=="cortex-m0") 949 | { 950 | WriteLine(" str r3,[r2]\r\n"); 951 | WriteLine(" adds r2, r2, #4\r\n"); 952 | } else { 953 | WriteLine(" str r3, [r2], #4\r\n"); 954 | } 955 | WriteLine("\r\n"); 956 | WriteLine("LoopFillZerobss:\r\n"); 957 | WriteLine(" ldr r3, = _ebss\r\n"); 958 | WriteLine(" cmp r2, r3\r\n"); 959 | WriteLine(" bcc FillZerobss\r\n"); 960 | WriteLine("\r\n"); 961 | WriteLine("/* Call the clock system intitialization function.*/\r\n"); 962 | WriteLine(" bl SystemInit\r\n"); 963 | WriteLine("/* Call static constructors */\r\n"); 964 | WriteLine(" bl __libc_init_array\r\n"); 965 | WriteLine("/* Enable all interrupts.*/\r\n"); 966 | WriteLine(" cpsie i\r\n"); 967 | WriteLine("/* Call the application's entry point.*/\r\n"); 968 | WriteLine(" bl main\r\n"); 969 | WriteLine(" bx lr\r\n"); 970 | WriteLine(" .size Reset_Handler, .-Reset_Handler\r\n"); 971 | WriteLine("\r\n"); 972 | WriteLine("/**\r\n"); 973 | WriteLine("* @brief This is the code that gets called when the processor receives an\r\n"); 974 | WriteLine("* unexpected interrupt. This simply enters an infinite loop, preserving\r\n"); 975 | WriteLine("* the system state for examination by a debugger.\r\n"); 976 | WriteLine("*\r\n"); 977 | WriteLine("* @param None\r\n"); 978 | WriteLine("* @retval : None\r\n"); 979 | WriteLine("*/\r\n"); 980 | WriteLine(" .section .text.Default_Handler,\"ax\",%progbits\r\n"); 981 | WriteLine("Default_Handler:\r\n"); 982 | WriteLine("Infinite_Loop:\r\n"); 983 | WriteLine(" b Infinite_Loop\r\n"); 984 | WriteLine(" .size Default_Handler, .-Default_Handler\r\n"); 985 | WriteLine("/******************************************************************************\r\n"); 986 | WriteLine("*\r\n"); 987 | WriteLine("* The minimal vector table for a "); 988 | WriteLine(m_cpuPlatform); 989 | WriteLine(". Note that the proper constructs\r\n"); 990 | WriteLine("* must be placed on this to ensure that it ends up at physical address\r\n"); 991 | WriteLine("* 0x0000.0000.\r\n"); 992 | WriteLine("*\r\n"); 993 | WriteLine("******************************************************************************/\r\n"); 994 | WriteLine(" .section .isr_vector,\"a\",%progbits\r\n"); 995 | WriteLine(" .type g_pfnVectors, %object\r\n"); 996 | WriteLine(" .size g_pfnVectors, .-g_pfnVectors\r\n"); 997 | WriteLine("\r\n"); 998 | WriteLine("g_pfnVectors:\r\n"); 999 | WriteLine(" .word \t_estack\r\n"); 1000 | for(i = 0; i0) 1225 | { 1226 | WriteLine("_estack = 0x"); 1227 | WriteHex(m_ramFragments[0].endAddress+1); 1228 | WriteLine(";\t/* end of RAM */\r\n"); 1229 | } 1230 | WriteLine("/* Generate a link error if heap and stack don't fit into RAM */\r\n"); 1231 | WriteLine("_Min_Heap_Size = 0x"); WriteHex(m_heapSize); WriteLine("; /* required amount of heap */\r\n"); 1232 | WriteLine("_Min_Stack_Size = 0x"); WriteHex(m_stackSize); WriteLine("; /* required amount of stack */ \r\n"); 1233 | WriteLine("\r\n"); 1234 | WriteLine("/* Specify the memory areas */\r\n"); 1235 | WriteLine("MEMORY\r\n"); 1236 | WriteLine("{\r\n"); 1237 | if(m_romFragments.Count()>0) 1238 | { 1239 | WriteLine("FLASH (rx)\t:ORIGIN = 0x"); 1240 | WriteHex(m_romFragments[0].beginAddress); 1241 | WriteLine(", LENGTH = "); 1242 | WriteInt((m_romFragments[0].endAddress-m_romFragments[0].beginAddress+1)/1024); 1243 | WriteLine("K\r\n"); 1244 | } 1245 | if(m_ramFragments.Count()>0) 1246 | { 1247 | WriteLine("RAM (xrw)\t:ORIGIN = 0x"); 1248 | WriteHex(m_ramFragments[0].beginAddress); 1249 | WriteLine(", LENGTH = "); 1250 | WriteInt((m_ramFragments[0].endAddress - m_ramFragments[0].beginAddress+1)/1024); 1251 | WriteLine("K\r\n"); 1252 | } 1253 | for(int i = 1; iFLASH\r\n"); 1273 | WriteLine("\r\n"); 1274 | WriteLine(" .descriptor :\r\n"); 1275 | WriteLine(" {\r\n"); 1276 | WriteLine(" . = ALIGN(128);\r\n"); 1277 | WriteLine(" KEEP(*(.descriptor)) /* Startup code */\r\n"); 1278 | WriteLine(" } >FLASH\r\n"); 1279 | WriteLine("\r\n"); 1280 | WriteLine(" /* The program code and other data goes into FLASH */\r\n"); 1281 | WriteLine(" .text :\r\n"); 1282 | WriteLine(" {\r\n"); 1283 | WriteLine(" . = ALIGN(4);\r\n"); 1284 | WriteLine(" *(.text) /* .text sections (code) */\r\n"); 1285 | WriteLine(" *(.text*) /* .text* sections (code) */\r\n"); 1286 | WriteLine(" *(.glue_7) /* glue arm to thumb code */\r\n"); 1287 | WriteLine(" *(.glue_7t) /* glue thumb to arm code */\r\n"); 1288 | WriteLine(" *(.eh_frame)\r\n"); 1289 | WriteLine("\r\n"); 1290 | WriteLine(" KEEP (*(.init))\r\n"); 1291 | WriteLine(" KEEP (*(.fini))\r\n"); 1292 | WriteLine("\r\n"); 1293 | WriteLine(" . = ALIGN(4);\r\n"); 1294 | WriteLine(" _etext = .; /* define a global symbols at end of code */\r\n"); 1295 | WriteLine(" } >FLASH\r\n"); 1296 | WriteLine("\r\n"); 1297 | WriteLine(" /* Constant data goes into FLASH */\r\n"); 1298 | WriteLine(" .rodata :\r\n"); 1299 | WriteLine(" {\r\n"); 1300 | WriteLine(" . = ALIGN(4);\r\n"); 1301 | WriteLine(" *(.rodata) /* .rodata sections (constants, strings, etc.) */\r\n"); 1302 | WriteLine(" *(.rodata*) /* .rodata* sections (constants, strings, etc.) */\r\n"); 1303 | WriteLine(" . = ALIGN(4);\r\n"); 1304 | WriteLine(" } >FLASH\r\n"); 1305 | WriteLine("\r\n"); 1306 | WriteLine(" .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH\r\n"); 1307 | WriteLine(" .ARM : {\r\n"); 1308 | WriteLine(" __exidx_start = .;\r\n"); 1309 | WriteLine(" *(.ARM.exidx*)\r\n"); 1310 | WriteLine(" __exidx_end = .;\r\n"); 1311 | WriteLine(" } >FLASH\r\n"); 1312 | WriteLine("\r\n"); 1313 | WriteLine(" .preinit_array :\r\n"); 1314 | WriteLine(" {\r\n"); 1315 | WriteLine(" PROVIDE_HIDDEN (__preinit_array_start = .);\r\n"); 1316 | WriteLine(" KEEP (*(.preinit_array*))\r\n"); 1317 | WriteLine(" PROVIDE_HIDDEN (__preinit_array_end = .);\r\n"); 1318 | WriteLine(" } >FLASH\r\n"); 1319 | WriteLine(" .init_array :\r\n"); 1320 | WriteLine(" {\r\n"); 1321 | WriteLine(" PROVIDE_HIDDEN (__init_array_start = .);\r\n"); 1322 | WriteLine(" KEEP (*(SORT(.init_array.*)))\r\n"); 1323 | WriteLine(" KEEP (*(.init_array*))\r\n"); 1324 | WriteLine(" PROVIDE_HIDDEN (__init_array_end = .);\r\n"); 1325 | WriteLine(" } >FLASH\r\n"); 1326 | WriteLine(" .fini_array :\r\n"); 1327 | WriteLine(" {\r\n"); 1328 | WriteLine(" PROVIDE_HIDDEN (__fini_array_start = .);\r\n"); 1329 | WriteLine(" KEEP (*(SORT(.fini_array.*)))\r\n"); 1330 | WriteLine(" KEEP (*(.fini_array*))\r\n"); 1331 | WriteLine(" PROVIDE_HIDDEN (__fini_array_end = .);\r\n"); 1332 | WriteLine(" } >FLASH\r\n"); 1333 | WriteLine("\r\n"); 1334 | WriteLine(" /* used by the startup to initialize data */\r\n"); 1335 | WriteLine(" _sidata = LOADADDR(.data);\r\n"); 1336 | WriteLine("\r\n"); 1337 | WriteLine(" /* Initialized data sections goes into RAM, load LMA copy after code */\r\n"); 1338 | WriteLine(" .data :\r\n"); 1339 | WriteLine(" {\r\n"); 1340 | WriteLine(" . = ALIGN(4);\r\n"); 1341 | WriteLine(" _sdata = .; /* create a global symbol at data start */\r\n"); 1342 | WriteLine(" *(.data) /* .data sections */\r\n"); 1343 | WriteLine(" *(.data*) /* .data* sections */\r\n"); 1344 | WriteLine("\r\n"); 1345 | WriteLine(" . = ALIGN(4);\r\n"); 1346 | WriteLine(" _edata = .; /* define a global symbol at data end */\r\n"); 1347 | WriteLine(" } >RAM AT> FLASH\r\n"); 1348 | WriteLine("\r\n"); 1349 | if (m_ramFragments.Count()>1) 1350 | { 1351 | WriteLine(" _siccmram = LOADADDR(.ccmram);\r\n"); 1352 | WriteLine("\r\n"); 1353 | WriteLine(" /* CCM-RAM section \r\n"); 1354 | WriteLine(" * \r\n"); 1355 | WriteLine(" * IMPORTANT NOTE! \r\n"); 1356 | WriteLine(" * If initialized variables will be placed in this section,\r\n"); 1357 | WriteLine(" * the startup code needs to be modified to copy the init-values.\r\n"); 1358 | WriteLine(" */\r\n"); 1359 | WriteLine(" .ccmram :\r\n"); 1360 | WriteLine(" {\r\n"); 1361 | WriteLine(" . = ALIGN(4);\r\n"); 1362 | WriteLine(" _sccmram = .; /* create a global symbol at ccmram start */\r\n"); 1363 | WriteLine(" *(.ccmram)\r\n"); 1364 | WriteLine(" *(.ccmram*)\r\n"); 1365 | WriteLine("\r\n"); 1366 | WriteLine(" . = ALIGN(4);\r\n"); 1367 | WriteLine(" _eccmram = .; /* create a global symbol at ccmram end */\r\n"); 1368 | WriteLine(" } >CCMRAM AT> FLASH\r\n"); 1369 | WriteLine("\r\n"); 1370 | } 1371 | WriteLine("\r\n"); 1372 | WriteLine(" /* Uninitialized data section */\r\n"); 1373 | WriteLine(" . = ALIGN(4);\r\n"); 1374 | WriteLine(" .bss :\r\n"); 1375 | WriteLine(" {\r\n"); 1376 | WriteLine(" /* This is used by the startup in order to initialize the .bss secion */\r\n"); 1377 | WriteLine(" _sbss = .; /* define a global symbol at bss start */\r\n"); 1378 | WriteLine(" __bss_start__ = _sbss;\r\n"); 1379 | WriteLine(" *(.bss)\r\n"); 1380 | WriteLine(" *(.bss*)\r\n"); 1381 | WriteLine(" *(COMMON)\r\n"); 1382 | WriteLine("\r\n"); 1383 | WriteLine(" . = ALIGN(4);\r\n"); 1384 | WriteLine(" _ebss = .; /* define a global symbol at bss end */\r\n"); 1385 | WriteLine(" __bss_end__ = _ebss;\r\n"); 1386 | WriteLine(" } >RAM\r\n"); 1387 | WriteLine("\r\n"); 1388 | WriteLine(" /* User_heap_stack section, used to check that there is enough RAM left */\r\n"); 1389 | WriteLine(" ._user_heap_stack :\r\n"); 1390 | WriteLine(" {\r\n"); 1391 | WriteLine(" . = ALIGN(8);\r\n"); 1392 | WriteLine(" PROVIDE ( end = . );\r\n"); 1393 | WriteLine(" PROVIDE ( _end = . );\r\n"); 1394 | WriteLine(" . = . + _Min_Heap_Size;\r\n"); 1395 | WriteLine(" . = . + _Min_Stack_Size;\r\n"); 1396 | WriteLine(" . = ALIGN(8);\r\n"); 1397 | WriteLine(" } >RAM\r\n"); 1398 | WriteLine("\r\n"); 1399 | WriteLine("\r\n"); 1400 | WriteLine(" /* Remove information from the standard libraries */\r\n"); 1401 | WriteLine(" /DISCARD/ :\r\n"); 1402 | WriteLine(" {\r\n"); 1403 | WriteLine(" libnosys.a ( * )\r\n"); 1404 | WriteLine(" libc.a ( * )\r\n"); 1405 | WriteLine(" libm.a ( * )\r\n"); 1406 | WriteLine(" libgcc.a ( * )\r\n"); 1407 | WriteLine(" }\r\n"); 1408 | WriteLine("\r\n"); 1409 | WriteLine(" .ARM.attributes 0 : { *(.ARM.attributes) }\r\n"); 1410 | WriteLine("}\r\n"); 1411 | fclose(m_fileHandle); 1412 | m_fileHandle = NULL; 1413 | 1414 | return true; 1415 | } 1416 | 1417 | bool KeilToARMGCC::CreateJLinkFile(const char* jlinkFileName) 1418 | { 1419 | m_fileHandle = fopen(jlinkFileName, "wb"); 1420 | if (m_fileHandle==NULL) 1421 | { 1422 | return false; 1423 | } 1424 | 1425 | WriteLine("si 1\r\n"); 1426 | WriteLine("speed 2000\r\n"); 1427 | WriteLine("r\r\n"); 1428 | WriteLine("h\r\n"); 1429 | WriteLine("loadbin "); 1430 | WriteLine(m_targetName); 1431 | WriteLine(".bin,0x"); 1432 | if (m_romFragments.Count()==0) 1433 | { 1434 | return false; 1435 | } 1436 | WriteHex( m_romFragments[0].beginAddress); 1437 | WriteLine("\r\n"); 1438 | WriteLine("r\r\n"); 1439 | WriteLine("exit\r\n"); 1440 | 1441 | return true; 1442 | } 1443 | 1444 | void KeilToARMGCC::WriteLine(const char* text) 1445 | { 1446 | if (text==NULL) return; 1447 | 1448 | fwrite(text, 1, strlen(text), m_fileHandle); 1449 | } 1450 | 1451 | void KeilToARMGCC::WriteInt(int number) 1452 | { 1453 | char num[12]; 1454 | ULongIntToStr(number, num, 12); 1455 | fwrite(num, 1, strlen(num), m_fileHandle); 1456 | } 1457 | 1458 | void KeilToARMGCC::WriteHex(int number) 1459 | { 1460 | char hex[12]; 1461 | ULongIntToHex(number, hex, 12); 1462 | fwrite(hex, 1, strlen(hex), m_fileHandle); 1463 | } 1464 | 1465 | void KeilToARMGCC::ParseLine(TString& line) 1466 | { 1467 | switch(m_state) 1468 | { 1469 | case kcsHeader: 1470 | { 1471 | if ( (line.IndexOf("Stack_Size")==0) || 1472 | (line.IndexOf("Heap_Size")==0) ) 1473 | { 1474 | SetState(kcsParams); 1475 | ParseLine(line); 1476 | }; 1477 | if ( (line.IndexOf("__Vectors")==0)) 1478 | { 1479 | SetState(kcsVectors); 1480 | ParseLine(line); 1481 | } 1482 | } 1483 | break; 1484 | 1485 | case kcsParams: 1486 | { 1487 | if (line.IndexOf("__Vectors")==0) 1488 | { 1489 | SetState(kcsVectors); 1490 | ParseLine(line); 1491 | } else { 1492 | ParseParams(line); 1493 | } 1494 | } 1495 | break; 1496 | 1497 | case kcsVectors: 1498 | { 1499 | if (line.IndexOf("Reset_Handler")==0) 1500 | { 1501 | SetState(kcsEnd); 1502 | } else 1503 | { 1504 | ParseVectors(line); 1505 | 1506 | } 1507 | } 1508 | break; 1509 | 1510 | case kcsEnd: 1511 | { 1512 | //do nothing 1513 | } 1514 | break; 1515 | } 1516 | } 1517 | 1518 | void KeilToARMGCC::ParseParams(TString& line) 1519 | { 1520 | TString sizeString; 1521 | short i; 1522 | 1523 | i = line.IndexOf("Stack_Size"); 1524 | if (i==0) 1525 | { 1526 | i = line.IndexOf("EQU"); 1527 | if (i>0) 1528 | { 1529 | sizeString.CopyFrom(line.ToPChar()+i+3); 1530 | sizeString.Trim(); 1531 | m_stackSize = HexToULongInt(sizeString); 1532 | } 1533 | } 1534 | i = line.IndexOf("Heap_Size"); 1535 | if (i==0) 1536 | { 1537 | i = line.IndexOf("EQU"); 1538 | TString sizeString; 1539 | if (i>0) 1540 | { 1541 | sizeString.CopyFrom(line.ToPChar()+i+3); 1542 | sizeString.Trim(); 1543 | m_heapSize = HexToULongInt(sizeString.ToPChar()); 1544 | } 1545 | } 1546 | } 1547 | 1548 | void KeilToARMGCC::ParseVectors(TString& line) 1549 | { 1550 | TString irqName; 1551 | 1552 | long i1,i2; 1553 | 1554 | i1 = line.IndexOf("DCD"); 1555 | if (i1==-1) return; 1556 | 1557 | i2 = line.IndexOf(";"); 1558 | if (i2==-1) 1559 | { 1560 | i2 = line.Length(); 1561 | } 1562 | if (i2 5 | * 6 | * https://github.com/embedded-tools/keil2gcc 7 | * 8 | * Permission to use, copy, modify, distribute and sell this software 9 | * and its documentation for any purpose is hereby granted without fee, 10 | * provided that the above copyright notice appear in all copies and 11 | * that both that copyright notice and this permission notice appear 12 | * in supporting documentation. 13 | * It is provided "as is" without express or implied warranty. 14 | * 15 | */ 16 | 17 | #ifndef KEIL2GCC___H 18 | #define KEIL2GCC___H 19 | 20 | #include "TString.h" 21 | #include "TStringList.h" 22 | #include "TXmlTag.h" 23 | #include "TList.h" 24 | 25 | enum KeilConversionState 26 | { 27 | kcsHeader, 28 | kcsParams, 29 | kcsVectors, 30 | kcsEnd 31 | }; 32 | 33 | struct MemoryFragment 34 | { 35 | unsigned long beginAddress; 36 | unsigned long endAddress; 37 | }; 38 | 39 | class KeilToARMGCC 40 | { 41 | private: 42 | KeilConversionState m_state; 43 | FILE* m_fileHandle; 44 | 45 | int m_stackSize; 46 | int m_heapSize; 47 | bool m_useFPU; 48 | int m_fpuVersion; 49 | 50 | TStringList m_irqList; 51 | TStringList m_incList; 52 | TStringList m_srcList; 53 | TStringList m_defineList; 54 | 55 | TString m_targetName; 56 | TString m_startupFile; 57 | TString m_ldScriptFile; 58 | TString m_eclipseFile; 59 | 60 | TString m_cpuType; 61 | TString m_cpuPlatform; 62 | TString m_cpuVendor; 63 | unsigned long m_cpuClock; 64 | bool m_fpuPresent; 65 | 66 | TList m_romFragments; 67 | TList m_ramFragments; 68 | 69 | TString m_cflags; 70 | TString m_defines; 71 | TString m_newStartupFile; 72 | TString m_systemFile; 73 | 74 | void SetState (KeilConversionState state); 75 | 76 | void WriteLine(const char* text); 77 | void WriteInt(int number); 78 | void WriteHex(int number); 79 | 80 | bool CreateStartupFile(const char* vectorFileName); 81 | bool CreateMakeFile(const char* makeFileName); 82 | bool CreateJLinkFile(const char* jlinkFileName); 83 | bool CreateLDScript(const char* ldScriptFileName); 84 | 85 | void ParseLine(TString& line); 86 | void ParseParams(TString& line); 87 | void ParseVectors(TString& line); 88 | 89 | void ScanLibs(const char* makefilePath, const char* relativePath); 90 | 91 | bool FindMdk5IRom(TXMLTag* xmlTag, unsigned long& startAddress, unsigned long& endAddress); 92 | bool FindMdk5IRam1(TXMLTag* xmlTag, unsigned long &startAddress, unsigned long& endAddress); 93 | bool FindMdk5IRam2(TXMLTag* xmlTag, unsigned long &startAddress, unsigned long& endAddress); 94 | bool FindMdk5IRam(TXMLTag* xmlTag, unsigned long memoryIndex, unsigned long& startAddress, unsigned long& endAddress); 95 | bool ReadMemoryBlock(TXMLTag* ramEnabledTag, TXMLTag* ramAddressTag, unsigned long& startAddress, unsigned long& endAddress); 96 | 97 | public: 98 | KeilToARMGCC(); 99 | bool ParseKeilProjectSettings(const char* keilProjectFile, 100 | int keilTargetIndex, 101 | const char* targetDirectory, 102 | bool forceSoftFPU); 103 | 104 | void DoConversion(const char* uv4ProjectFile, 105 | int keilTargetIndex, 106 | const char* targetDirectory, 107 | bool forceSoftFPU, 108 | bool scanlibs); 109 | 110 | }; 111 | 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /KeilToGCC.cpp: -------------------------------------------------------------------------------- 1 | // Keil2EclipseConvertor.cpp : Defines the entry point for the console application. 2 | // 3 | #include "TStringList.h" 4 | #include "TFilePath.h" 5 | #include "KeilToARMGCC.h" 6 | #include "TString.h" 7 | 8 | 9 | int main(int argc, char* argv[]) 10 | { 11 | if (argc<=1) 12 | { 13 | printf("\r\nKeil MDK to ARM GCC makefile converter v1.07\r\n"); 14 | printf("(c) 2017-2020 Ondrej Sterba, osterba@atlas.cz \r\n"); 15 | printf("\r\n"); 16 | printf("Usage: keil2gcc uv_project_file [-soft] [-scanlibs] [makefile_name]\r\n"); 17 | printf("\r\n"); 18 | printf("Options:\r\n"); 19 | printf(" -soft Force using software emulated FPU\r\n"); 20 | printf(" -scanlibs Scans subdirectories for source codes not included in project file\r\n"); 21 | printf(" makefile_name optional - makefile path relative to uv_project_file, default value is '.\\makefile'\r\n"); 22 | printf("\r\n"); 23 | return -1; 24 | } 25 | 26 | bool forceSoftFPU = false; 27 | bool scanLibs = false; 28 | 29 | TString uv4ProjectFile = argv[1]; 30 | TFilePath targetFile = "./"; 31 | if (argc>=3) 32 | { 33 | TString lastArg = argv[argc-1]; 34 | if (lastArg.Length()>0) 35 | { 36 | if (lastArg[0]!='-') 37 | { 38 | targetFile = lastArg; 39 | } 40 | } 41 | } 42 | targetFile.ChangeSeparator('/'); 43 | for(int i = 1; i 2 |
3 | This utility goes through Keil project file and creates:
4 | -makefile with list of all source files included into project
5 | -it sets proper C/C++ compiler flags that fit to selected MCU
6 | -it inserts clean command + install command to makefile
7 | -it generates JLink script for installing compiled firmware to MCU (see makefile install command)
8 | -it generates *.ld file
9 | -it generates *.s file
10 | -*.s file contains some additional instructions to make sure that stack pointer is always set to correct value 11 | (even after calling entry point by boot loader) 12 |
13 | 14 | It has been tested with these MCU types:
15 |
16 | -STM 32F010
17 | -STM 32F103
18 | -STM 32F205
19 | -STM 32F207
20 | -STM 32F429
21 | -NXP LCP11C24
22 |
23 | For these MCUs is generated correct S (startup) file and LD file. For other MCUs can happen that generated S file or LD file is not 100% correct (although I hope it will not happen). In such case try to get original S file or LD file from your MCU manufacter and you can send me the correct file.
24 |
25 | This utility is also demostrating how easy is to do such things with PersistantLibrary (see https://github.com/embedded-tools/PersistenceLibrary )
26 |
27 | Binary files:
28 |
29 | 1.03 - not available anymore
30 | 1.04 - https://keil2gcc.bbot.cz/file/keil2gcc_1.04.zip (added -scanlib feature)
31 | 1.05 - https://keil2gcc.bbot.cz/file/keil2gcc_1.05.zip (support for Keil v5 added)
32 | 1.06 - https://keil2gcc.bbot.cz/file/keil2gcc_1.06.zip (minor bug fixes)
33 | 1.07 - https://keil2gcc.bbot.cz/file/keil2gcc_1.07.zip (checks for presence of original gcc startup file by manufacturer - tested with some waveshare dev kits)
34 | 35 | 36 | -------------------------------------------------------------------------------- /Readme before using.txt: -------------------------------------------------------------------------------- 1 | Converts Keil MDK-ARM 4 project (generated by STMCube) to ARM GCC makefile. 2 | Supports ARM Cortex MCUs (M0, M3, M4) by ST and NXP. 3 | Tested with: 4 | NXP LCP11C24 5 | ST STM32F010 6 | ST STM32F103 7 | ST STM32F207 8 | ST STM32F429 9 | 10 | Usage: keil2gcc uv4_project_file [-soft] [-scanlibs] [makefile_name] 11 | 12 | Options: 13 | -soft Force using software emulated FPU 14 | -scanlibs Scans subdirectories for not included source codes and headers 15 | makefile_name Optional, default value is '.\makefile' 16 | 17 | Example: 18 | 19 | Keil2GCC.exe d:\YourKeilProject\YourProject.uvproj 20 | Keil2GCC.exe d:\YourSTMCubeProject\MDK-ARM\TestF103.uvproj ..\makefile 21 | --------------------------------------------------------------------------------