initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
from .linker_config import generate_files
|
||||
@@ -0,0 +1,181 @@
|
||||
from string import Template
|
||||
import re
|
||||
|
||||
|
||||
def fix_startup_file(config, filename):
|
||||
|
||||
# Get the stack size from the config file. Note that the startup file
|
||||
# measures in words, not bytes.
|
||||
stack_size = config['STACK']['size'] // 4
|
||||
|
||||
# Read in every line of the given file
|
||||
input_lines = None
|
||||
with open(filename) as f:
|
||||
input_lines = [line for line in f]
|
||||
|
||||
# Perform a search-and-replace with the new stack size.
|
||||
def fix_line(line):
|
||||
return re.sub(r'(g_pui32Stack\[)([x0-9a-fA-F]*)',
|
||||
r'\g<1>' + str(stack_size),
|
||||
line)
|
||||
|
||||
output_lines = map(fix_line, input_lines)
|
||||
output_text = ''.join(output_lines)
|
||||
|
||||
# Write the new file back to the input
|
||||
with open(filename, 'w') as f:
|
||||
f.write(output_text)
|
||||
|
||||
|
||||
def generate_link_script(config):
|
||||
D = dict()
|
||||
D['ro_base'] = format_hex(config['ROMEM']['start'])
|
||||
D['ro_size'] = config['ROMEM']['size']
|
||||
|
||||
D['rw_base'] = format_hex(config['RWMEM']['start'])
|
||||
D['rw_size'] = config['RWMEM']['size']
|
||||
|
||||
D['stack_base'] = format_hex(config['STACK']['start'])
|
||||
D['stack_size'] = config['STACK']['size']
|
||||
|
||||
D['tcm_base'] = format_hex(config['TCM']['start'])
|
||||
D['tcm_size'] = config['TCM']['size']
|
||||
|
||||
D['section_definitions'] = format_section_definitions(config)
|
||||
D['additional_sections'] = format_sections(config)
|
||||
|
||||
return link_script_template.substitute(**D)
|
||||
|
||||
|
||||
def format_section_definitions(config):
|
||||
|
||||
def format_section_definition(section):
|
||||
mapping = {
|
||||
'name': section,
|
||||
'permissions': config[section]['perm'],
|
||||
'base': format_hex(config[section]['start']),
|
||||
'length': format_hex(config[section]['size']),
|
||||
}
|
||||
|
||||
return section_definition_template.substitute(**mapping)
|
||||
|
||||
extra_sections = get_extra_sections(config)
|
||||
return ''.join(map(format_section_definition, extra_sections))
|
||||
|
||||
|
||||
def format_sections(config):
|
||||
|
||||
def format_section(section):
|
||||
mapping = {
|
||||
'name': section,
|
||||
}
|
||||
|
||||
return section_template.substitute(**mapping)
|
||||
|
||||
extra_sections = get_extra_sections(config)
|
||||
return ''.join(map(format_section, extra_sections))
|
||||
|
||||
|
||||
def get_extra_sections(config):
|
||||
def section_not_required(x):
|
||||
return x not in ['ROMEM', 'RWMEM', 'STACK', 'TCM']
|
||||
|
||||
return list(filter(section_not_required, config))
|
||||
|
||||
|
||||
def format_hex(n):
|
||||
return '0x{:08X}'.format(n)
|
||||
|
||||
|
||||
link_script_template = Template('''\
|
||||
/******************************************************************************
|
||||
*
|
||||
* linker_script.ld - Linker script for applications using startup_gnu.c
|
||||
*
|
||||
*****************************************************************************/
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
ROMEM (rx) : ORIGIN = ${ro_base}, LENGTH = ${ro_size}
|
||||
RWMEM (rwx) : ORIGIN = ${rw_base}, LENGTH = ${rw_size}
|
||||
TCM (rwx) : ORIGIN = ${tcm_base}, LENGTH = ${tcm_size}
|
||||
STACKMEM (rwx) : ORIGIN = ${stack_base}, LENGTH = ${stack_size}
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.isr_vector))
|
||||
KEEP(*(.patch))
|
||||
*(.text)
|
||||
*(.text*)
|
||||
*(.rodata)
|
||||
*(.rodata*)
|
||||
. = ALIGN(4);
|
||||
_etext = .;
|
||||
} > ROMEM
|
||||
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sdata = .;
|
||||
*(.data)
|
||||
*(.data*)
|
||||
. = ALIGN(4);
|
||||
_edata = .;
|
||||
} > RWMEM AT>ROMEM
|
||||
|
||||
/* used by startup to initialize data */
|
||||
_init_data = LOADADDR(.data);
|
||||
|
||||
.tcm :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_stcm = .;
|
||||
*(.tcm)
|
||||
*(.tcm*)
|
||||
. = ALIGN(4);
|
||||
_etcm = .;
|
||||
} > TCM AT>ROMEM
|
||||
|
||||
/* used by startup to initialize tcm */
|
||||
_init_tcm = LOADADDR(.tcm);
|
||||
|
||||
/* User stack section initialized by startup code. */
|
||||
.stack (NOLOAD):
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.stack)
|
||||
*(.stack*)
|
||||
. = ALIGN(8);
|
||||
} > STACKMEM
|
||||
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sbss = .;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
} > RWMEM
|
||||
|
||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
||||
}\
|
||||
''')
|
||||
|
||||
section_definition_template = Template('''\
|
||||
${name} (${permissions}) : ORIGIN = ${base}, LENGTH = ${length}
|
||||
''')
|
||||
|
||||
section_template = Template('''\
|
||||
${name} :
|
||||
{
|
||||
*(${name})
|
||||
} > ${name}
|
||||
|
||||
''')
|
||||
@@ -0,0 +1,168 @@
|
||||
from string import Template
|
||||
import re
|
||||
|
||||
required_sections = ['ROMEM', 'RWMEM', 'STACK']
|
||||
|
||||
|
||||
def fix_startup_file(config, filename):
|
||||
|
||||
# Get the stack size from the config file. Note that the startup file
|
||||
# measures in words, not bytes.
|
||||
stack_size = config['STACK']['size'] // 4
|
||||
|
||||
# Read in every line of the given file
|
||||
input_lines = None
|
||||
with open(filename) as f:
|
||||
input_lines = [line for line in f]
|
||||
|
||||
# Perform a search-and-replace with the new stack size.
|
||||
def fix_line(line):
|
||||
return re.sub(r'(pui32Stack\[)([x0-9a-fA-F]*)',
|
||||
r'\g<1>' + str(stack_size),
|
||||
line)
|
||||
|
||||
output_lines = map(fix_line, input_lines)
|
||||
output_text = ''.join(output_lines)
|
||||
|
||||
# Write the new file back to the input
|
||||
with open(filename, 'w') as f:
|
||||
f.write(output_text)
|
||||
|
||||
|
||||
def generate_link_script(config):
|
||||
robase = config['ROMEM']['start']
|
||||
roend = robase + config['ROMEM']['size']
|
||||
rwbase = config['RWMEM']['start']
|
||||
rwend = rwbase + config['RWMEM']['size']
|
||||
tcmbase = config['TCM']['start']
|
||||
tcmend = tcmbase + config['TCM']['size']
|
||||
stackbase = config['STACK']['start']
|
||||
stackend = stackbase + config['STACK']['size']
|
||||
|
||||
D = dict()
|
||||
D['robase'] = format_hex(robase)
|
||||
D['roend'] = format_hex(roend)
|
||||
D['rwbase'] = format_hex(rwbase)
|
||||
D['rwend'] = format_hex(rwend)
|
||||
D['tcmbase'] = format_hex(tcmbase)
|
||||
D['tcmend'] = format_hex(tcmend)
|
||||
|
||||
D['stackbase'] = format_hex(stackbase)
|
||||
D['stackend'] = format_hex(stackend)
|
||||
|
||||
D['stack_size'] = config['STACK']['size']
|
||||
|
||||
#D['section_defs'] = format_sections(section_definition, config)
|
||||
#D['section_blocks'] = format_sections(section_block, config)
|
||||
#D['section_placements'] = format_sections(section_placement, config)
|
||||
|
||||
return link_script_template.substitute(**D)
|
||||
|
||||
|
||||
def format_sections(template, config):
|
||||
|
||||
def fill_section_template(section):
|
||||
mapping = {
|
||||
'section_name': section,
|
||||
'block_name': section + '_BLOCK',
|
||||
'mem_name': section + '_MEM',
|
||||
'align': 4,
|
||||
'start': format_hex(config[section]['start']),
|
||||
'size': format_hex(config[section]['size']),
|
||||
'end': format_hex(config[section]['start'] +
|
||||
config[section]['size']),
|
||||
|
||||
'permissions': convert_permissions(config[section]['perm']),
|
||||
}
|
||||
|
||||
return template.substitute(**mapping)
|
||||
|
||||
def is_extra_section(section):
|
||||
return section not in required_sections
|
||||
|
||||
sections = filter(is_extra_section, config)
|
||||
|
||||
return ''.join(map(fill_section_template, sections))
|
||||
|
||||
|
||||
def convert_permissions(perm_string):
|
||||
if 'r' in perm_string and 'w' in perm_string and 'x' in perm_string:
|
||||
return 'readwrite'
|
||||
else:
|
||||
return 'readonly'
|
||||
|
||||
|
||||
def format_hex(n):
|
||||
return '0x{:08X}'.format(n)
|
||||
|
||||
|
||||
link_script_template = Template('''\
|
||||
//*****************************************************************************
|
||||
//
|
||||
// linker_script.icf
|
||||
//
|
||||
// IAR linker Configuration File
|
||||
//
|
||||
//*****************************************************************************
|
||||
|
||||
//
|
||||
// Define a memory section that covers the entire 4 GB addressable space of the
|
||||
// processor. (32-bit can address up to 4GB)
|
||||
//
|
||||
define memory mem with size = 4G;
|
||||
|
||||
//
|
||||
// Define regions for the various types of internal memory.
|
||||
//
|
||||
define region ROMEM = mem:[from ${robase} to ${roend}];
|
||||
define region RWMEM = mem:[from ${rwbase} to ${rwend}];
|
||||
define region TCM = mem:[from ${tcmbase} to ${tcmend}];
|
||||
define region STACKMEM = mem:[from ${stackbase} to ${stackend}];
|
||||
|
||||
//
|
||||
// Define blocks for logical groups of data.
|
||||
//
|
||||
define block HEAP with alignment = 0x8, size = 0x00000000 { };
|
||||
define block CSTACK with alignment = 0x8, size = ${stack_size}
|
||||
{
|
||||
section .stack
|
||||
};
|
||||
|
||||
define block ROSTART with fixed order
|
||||
{
|
||||
readonly section .intvec,
|
||||
readonly section .patch
|
||||
};
|
||||
|
||||
//
|
||||
// Set section properties.
|
||||
//
|
||||
initialize by copy { readwrite };
|
||||
initialize by copy { section RWMEM };
|
||||
do not initialize { section .noinit };
|
||||
do not initialize { section .stack };
|
||||
|
||||
//
|
||||
// Place code sections in memory regions.
|
||||
//
|
||||
place at start of ROMEM { block ROSTART };
|
||||
place in ROMEM { readonly };
|
||||
place at start of STACKMEM { block CSTACK};
|
||||
place in RWMEM { block HEAP, readwrite, section .noinit };
|
||||
place in TCM { section .tcm };
|
||||
''')
|
||||
|
||||
section_definition = Template('''\
|
||||
define region ${mem_name} = mem:[from ${start} to ${end}];
|
||||
''')
|
||||
|
||||
section_block = Template('''\
|
||||
define block ${block_name} with alignment = ${align}, size = ${size}
|
||||
{
|
||||
${permissions} section ${section_name}
|
||||
};
|
||||
''')
|
||||
|
||||
section_placement = Template('''\
|
||||
place in ${mem_name} { block ${block_name} };
|
||||
''')
|
||||
@@ -0,0 +1,171 @@
|
||||
from string import Template
|
||||
import textwrap
|
||||
import re
|
||||
|
||||
|
||||
def fix_startup_file(config, filename):
|
||||
|
||||
# Get the stack size from the config file. Note that the startup file
|
||||
# measures in words, not bytes.
|
||||
stack_size = config['STACK']['size']
|
||||
|
||||
# Read in every line of the given file
|
||||
input_lines = None
|
||||
with open(filename) as f:
|
||||
input_lines = [line for line in f]
|
||||
|
||||
# Perform a search-and-replace with the new stack size.
|
||||
def fix_line(line):
|
||||
return re.sub(r'(Stack EQU )0x[0-9a-fA-F]*',
|
||||
r'\g<1>' + '0x{:08X}'.format(stack_size),
|
||||
line)
|
||||
|
||||
output_lines = map(fix_line, input_lines)
|
||||
output_text = ''.join(output_lines)
|
||||
|
||||
# Write the new file back to the input
|
||||
with open(filename, 'w') as f:
|
||||
f.write(output_text)
|
||||
|
||||
|
||||
def generate_link_script(config):
|
||||
|
||||
mapping = dict()
|
||||
mapping['ro_base'] = format_number(config['ROMEM']['start'])
|
||||
mapping['ro_size'] = format_number(config['ROMEM']['size'])
|
||||
mapping['rw_base'] = format_number(config['RWMEM']['start'])
|
||||
mapping['rw_size'] = format_number(config['RWMEM']['size'])
|
||||
mapping['stack_base'] = format_number(config['STACK']['start'])
|
||||
mapping['stack_size'] = format_number(config['STACK']['size'])
|
||||
mapping['tcm_base'] = format_number(config['TCM']['start'])
|
||||
mapping['tcm_size'] = format_number(config['TCM']['size'])
|
||||
mapping['additional_sections'] = generate_sections(config)
|
||||
|
||||
link_script = link_script_template.substitute(**mapping)
|
||||
debug_file = debug_file_template.substitute(**mapping)
|
||||
|
||||
return (link_script, debug_file)
|
||||
|
||||
|
||||
def generate_sections(config):
|
||||
# If there aren't any custom sections in the config file, we don't need to
|
||||
# add anything to the linker scripts.
|
||||
if 'custom_sections' not in config:
|
||||
return ''
|
||||
elif not config['custom_sections']:
|
||||
return ''
|
||||
|
||||
L = []
|
||||
for mem_section in config['custom_sections']:
|
||||
D = dict()
|
||||
D['name'] = mem_section['blockname']
|
||||
D['start'] = format_number(mem_section['start'])
|
||||
D['length'] = format_number(mem_section['length'])
|
||||
D['sections'] = '\n'.join(' * ({})'.format(x) for x in mem_section['sections'])
|
||||
|
||||
S = extra_section_template.substitute(**D)
|
||||
L.append(textwrap.indent(S, 4 * ' '))
|
||||
|
||||
return '\n' + '\n'.join(L)
|
||||
|
||||
|
||||
def format_number(n):
|
||||
return '0x{:08X}'.format(n)
|
||||
|
||||
|
||||
link_script_template = Template('''\
|
||||
;******************************************************************************
|
||||
;
|
||||
; Scatter file for Keil linker configuration.
|
||||
;
|
||||
;******************************************************************************
|
||||
LR_1 ${ro_base}
|
||||
{
|
||||
ROMEM ${ro_base} ${ro_size}
|
||||
{
|
||||
*.o (RESET, +First)
|
||||
* (+RO)
|
||||
}
|
||||
|
||||
RWMEM ${rw_base} ${rw_size}
|
||||
{
|
||||
* (+RW, +ZI)
|
||||
}
|
||||
|
||||
TCM ${tcm_base} ${tcm_size}
|
||||
{
|
||||
* (.tcm)
|
||||
}
|
||||
|
||||
STACKMEM ${stack_base} ${stack_size}
|
||||
{
|
||||
startup_keil.o (STACK)
|
||||
}
|
||||
}
|
||||
''')
|
||||
|
||||
extra_section_template = Template('''\
|
||||
${name} ${start} ${length}
|
||||
{
|
||||
${sections}
|
||||
}
|
||||
''')
|
||||
|
||||
debug_file_template = Template('''\
|
||||
/*----------------------------------------------------------------------------
|
||||
* Name: Dbg_RAM.ini
|
||||
* Purpose: RAM Debug Initialization File
|
||||
* Note(s):
|
||||
*----------------------------------------------------------------------------
|
||||
* This file is part of the uVision/ARM development tools.
|
||||
* This software may only be used under the terms of a valid, current,
|
||||
* end user licence from KEIL for a compatible version of KEIL software
|
||||
* development tools. Nothing else gives you the right to use this software.
|
||||
*
|
||||
* This software is supplied "AS IS" without warranties of any kind.
|
||||
*
|
||||
* Copyright (c) 2008-2013 Keil - An ARM Company. All rights reserved.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
TraceSetup() Turn on ITM clocks, etc.
|
||||
*----------------------------------------------------------------------------*/
|
||||
FUNC void TraceSetup (void)
|
||||
{
|
||||
// turn on the ITM/TPIU clock
|
||||
//_WDWORD(0x40020250, 0x00000201); // TPIU clock enabled at 3MHz
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
Setup() configure PC & SP for RAM Debug
|
||||
*----------------------------------------------------------------------------*/
|
||||
FUNC void Setup (void) {
|
||||
SP = _RDWORD(${ro_base}+0x0); // Setup Stack Pointer
|
||||
PC = _RDWORD(${ro_base}+0x4); // Setup Program Counter
|
||||
_WDWORD(0xE000ED08, ${ro_base}+0x0); // Setup Vector Table Offset Register (done in system file)
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
OnResetExec() executed after reset via uVision's 'Reset'-button
|
||||
*----------------------------------------------------------------------------*/
|
||||
FUNC void OnResetExec (void)
|
||||
{
|
||||
}
|
||||
|
||||
LOAD %L INCREMENTAL // load the application
|
||||
Setup(); // Setup for Running
|
||||
|
||||
BS main
|
||||
g
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
*----------------------------------------------------------------------------*/
|
||||
''')
|
||||
|
||||
extra_section_template = Template('''\
|
||||
${name} ${start} ${length}
|
||||
{
|
||||
${sections}
|
||||
}
|
||||
''')
|
||||
@@ -0,0 +1,224 @@
|
||||
# <TextAddressRange></TextAddressRange>
|
||||
# <DataAddressRange></DataAddressRange>
|
||||
import argparse
|
||||
import yaml
|
||||
from . import iar_link
|
||||
from . import gcc_link
|
||||
from . import keil_link
|
||||
|
||||
# Linker file locations.
|
||||
IAR_LD_SCRIPT = "iar/linker_script.icf"
|
||||
KEIL_LD_SCRIPT = "keil/linker_script.sct"
|
||||
KEIL_DEBUG_FILE = "keil/Dbg_RAM.ini"
|
||||
GCC_LD_SCRIPT = "gcc/linker_script.ld"
|
||||
|
||||
KEIL_STARTUP_FILE = "keil/startup_keil.s"
|
||||
IAR_STARTUP_FILE = "iar/startup_iar.c"
|
||||
GCC_STARTUP_FILE = "gcc/startup_gcc.c"
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('config_file', default='default_config_apollo3.yaml')
|
||||
parser.add_argument('-i', dest='iar', action='store_true')
|
||||
parser.add_argument('-k', dest='keil', action='store_true')
|
||||
parser.add_argument('-g', dest='gcc', action='store_true')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Read the configuration file.
|
||||
config = read_configuration(args.config_file)
|
||||
|
||||
# Figure out what we want to build. If no specific toolchains were
|
||||
# specified, just build everything. Otherwise, only build what was
|
||||
# specified.
|
||||
build_all = True
|
||||
|
||||
if args.keil:
|
||||
write_keil_linker_scripts(config)
|
||||
build_all = False
|
||||
|
||||
if args.iar:
|
||||
write_iar_linker_scripts(config)
|
||||
build_all = False
|
||||
|
||||
if args.gcc:
|
||||
write_gcc_linker_scripts(config)
|
||||
build_all = False
|
||||
|
||||
if build_all:
|
||||
write_keil_linker_scripts(config)
|
||||
write_iar_linker_scripts(config)
|
||||
write_gcc_linker_scripts(config)
|
||||
|
||||
# Print the memory map
|
||||
print_memory_map(config)
|
||||
|
||||
|
||||
def generate_files(config_file, toolchains):
|
||||
config = read_configuration(config_file)
|
||||
|
||||
print_memory_map(config)
|
||||
|
||||
if 'keil' in toolchains:
|
||||
write_keil_linker_scripts(config)
|
||||
|
||||
if 'iar' in toolchains:
|
||||
write_iar_linker_scripts(config)
|
||||
|
||||
if 'gcc' in toolchains:
|
||||
write_gcc_linker_scripts(config)
|
||||
|
||||
|
||||
def read_configuration(config_file):
|
||||
"""Read a configuration YAML files and return a dictionary of memory sections"""
|
||||
|
||||
# Read the YAML configuration file as is.
|
||||
with open(config_file) as file_object:
|
||||
config_string = file_object.read()
|
||||
config = yaml.load(config_string, Loader=yaml.FullLoader)
|
||||
|
||||
memory_sections = config['MemorySections']
|
||||
|
||||
# Search through the memory sections...
|
||||
for name in memory_sections.keys():
|
||||
|
||||
# Find the start value, and convert it if necessary.
|
||||
start = memory_sections[name]['start']
|
||||
memory_sections[name]['start'] = convert_number(start)
|
||||
|
||||
# If we find a size value, use it.
|
||||
if 'size' in memory_sections[name]:
|
||||
size = memory_sections[name]['size']
|
||||
memory_sections[name]['size'] = convert_number(size)
|
||||
|
||||
# It not, try to use an "end" value.
|
||||
elif 'end' in memory_sections[name]:
|
||||
end = memory_sections[name]['end']
|
||||
memory_sections[name]['size'] = convert_number(end) - convert_number(start)
|
||||
|
||||
stack_size = config['StackOptions']['size']
|
||||
config['StackOptions']['size'] = convert_number(stack_size)
|
||||
|
||||
# Create a memory section for the stack.
|
||||
memory_sections['STACK'] = dict()
|
||||
|
||||
# Create a section for the stack. To do this, we'll either need to
|
||||
# carve space out of TCM or RWMEM.
|
||||
if config['StackOptions']['place_in_tcm']:
|
||||
if config['StackOptions']['size'] > memory_sections['TCM']['size']:
|
||||
raise LinkerConfigError("Stack ({} B) doesn't fit in TCM ({} B)".format(
|
||||
config['StackOptions']['size'],
|
||||
memory_sections['TCM']['size']))
|
||||
|
||||
memory_sections['STACK']['start'] = memory_sections['TCM']['start']
|
||||
|
||||
memory_sections['STACK']['size'] = config['StackOptions']['size']
|
||||
|
||||
memory_sections['TCM']['start'] = (memory_sections['STACK']['start'] +
|
||||
config['StackOptions']['size'])
|
||||
|
||||
memory_sections['TCM']['size'] = (memory_sections['TCM']['size'] -
|
||||
config['StackOptions']['size'])
|
||||
|
||||
else:
|
||||
if config['StackOptions']['size'] > memory_sections['RWMEM']['size']:
|
||||
raise LinkerConfigError("Stack ({} B) doesn't fit in RWMEM ({} B)".format(
|
||||
config['StackOptions']['size'],
|
||||
memory_sections['RWMEM']['size']))
|
||||
|
||||
memory_sections['STACK']['start'] = memory_sections['RWMEM']['start']
|
||||
|
||||
memory_sections['STACK']['size'] = config['StackOptions']['size']
|
||||
|
||||
memory_sections['RWMEM']['start'] = (memory_sections['STACK']['start'] +
|
||||
config['StackOptions']['size'])
|
||||
|
||||
memory_sections['RWMEM']['size'] = (memory_sections['RWMEM']['size'] -
|
||||
config['StackOptions']['size'])
|
||||
|
||||
return memory_sections
|
||||
|
||||
|
||||
def write_keil_linker_scripts(config):
|
||||
try:
|
||||
linker_file_data, debug_file_data = keil_link.generate_link_script(config)
|
||||
|
||||
with open(KEIL_LD_SCRIPT, 'w') as linker_file:
|
||||
linker_file.write(linker_file_data)
|
||||
|
||||
with open(KEIL_DEBUG_FILE, 'w') as debug_file:
|
||||
debug_file.write(debug_file_data)
|
||||
|
||||
keil_link.fix_startup_file(config, KEIL_STARTUP_FILE)
|
||||
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
def write_iar_linker_scripts(config):
|
||||
try:
|
||||
with open(IAR_LD_SCRIPT, 'w') as linker_file:
|
||||
linker_file.write(iar_link.generate_link_script(config))
|
||||
|
||||
iar_link.fix_startup_file(config, IAR_STARTUP_FILE)
|
||||
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
def write_gcc_linker_scripts(config):
|
||||
try:
|
||||
with open(GCC_LD_SCRIPT, 'w') as linker_file:
|
||||
linker_file.write(gcc_link.generate_link_script(config))
|
||||
|
||||
gcc_link.fix_startup_file(config, GCC_STARTUP_FILE)
|
||||
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
def convert_number(N):
|
||||
"""Take in an integer or a numerical string ending in 'K', and convert it to an int"""
|
||||
if isinstance(N, int):
|
||||
return N
|
||||
elif isinstance(N, str):
|
||||
if N.endswith('K'):
|
||||
return int(N[:-1]) * 1024
|
||||
else:
|
||||
raise LinkerConfigError('"{}" not recognized as a number'.format(N))
|
||||
else:
|
||||
raise LinkerConfigError('"{}" not recognized as a number'.format(N))
|
||||
|
||||
|
||||
def print_memory_map(memory_sections):
|
||||
"""Show the memory map in a human readable format"""
|
||||
# Sort the section names by their starting address.
|
||||
section_names = sorted(memory_sections.keys(), key=lambda x: memory_sections[x]['start'])
|
||||
|
||||
# Search for the longest section name, and record its length.
|
||||
max_name_length = max(len(x) for x in section_names)
|
||||
|
||||
# This is a roundabout way to copy the maximum name length into a format
|
||||
# string, so we can make the output string look pretty.
|
||||
name_format = '{{:{}}}'.format(max_name_length + 1)
|
||||
|
||||
for name in section_names:
|
||||
section = memory_sections[name]
|
||||
mapping = {
|
||||
'name': name_format.format(name + ':'),
|
||||
'start': '0x{:08X}'.format(section['start']),
|
||||
'end': '0x{:08X}'.format(section['start'] + section['size']),
|
||||
'size': section['size'],
|
||||
}
|
||||
|
||||
print('{name} {start:10} - {end:10} ({size} bytes)'.format(**mapping))
|
||||
|
||||
|
||||
# Custom error for linker configuration problems.
|
||||
class LinkerConfigError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,127 @@
|
||||
import string
|
||||
import datetime
|
||||
|
||||
|
||||
memory_header_string = '''\
|
||||
//*****************************************************************************
|
||||
//
|
||||
// file am_memory_map.h
|
||||
//
|
||||
// brief Memory map include file.
|
||||
//
|
||||
// This file is generated by "memory_map.py".
|
||||
//
|
||||
//*****************************************************************************
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Copyright (c) {copyrightyear}, Ambiq Micro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from this
|
||||
// software without specific prior written permission.
|
||||
//
|
||||
// Third party software included in this distribution is subject to the
|
||||
// additional license terms as defined in the /docs/licenses directory.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This is part of the AmbiqSuite Development Package.
|
||||
//
|
||||
//*****************************************************************************
|
||||
|
||||
#ifndef AM_MEMORY_MAP_H
|
||||
#define AM_MEMORY_MAP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Memory block locations.
|
||||
//
|
||||
//*****************************************************************************
|
||||
${memory_locations}
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// Memory block sizes (in bytes)
|
||||
//
|
||||
//*****************************************************************************
|
||||
${memory_sizes}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // AM_MEMORY_MAP_H
|
||||
'''
|
||||
|
||||
currdatetime = datetime.datetime.now()
|
||||
memory_header_template = string.Template(memory_header_string.format(copyrightyear=currdatetime.year))
|
||||
|
||||
|
||||
|
||||
memory_location_define = '#define AM_MEM_{name:36} ((void *) 0x{start:08X})'
|
||||
memory_size_define = '#define AM_MEM_{name:36} {size}'
|
||||
|
||||
|
||||
def generate(memory_sections):
|
||||
|
||||
mapping = {
|
||||
'memory_locations': '\n'.join(write_memory_locations(memory_sections)),
|
||||
'memory_sizes': '\n'.join(write_memory_sizes(memory_sections)),
|
||||
}
|
||||
|
||||
return memory_header_template.substitute(**mapping)
|
||||
|
||||
|
||||
def write_memory_locations(memory_sections):
|
||||
section_names = sorted_sections(memory_sections)
|
||||
for n in section_names:
|
||||
mapping = {
|
||||
'name': n,
|
||||
'start': memory_sections[n]['start'],
|
||||
}
|
||||
yield memory_location_define.format_map(mapping)
|
||||
|
||||
|
||||
def write_memory_sizes(memory_sections):
|
||||
section_names = sorted_sections(memory_sections)
|
||||
for n in section_names:
|
||||
mapping = {
|
||||
'name': n + '_SIZE',
|
||||
'size': memory_sections[n]['size'],
|
||||
}
|
||||
yield memory_size_define.format_map(mapping)
|
||||
|
||||
|
||||
def sorted_sections(memory_sections):
|
||||
|
||||
def start_address(section):
|
||||
return memory_sections[section]['start']
|
||||
|
||||
return sorted(memory_sections.keys(), key=start_address)
|
||||
@@ -0,0 +1,51 @@
|
||||
import argparse
|
||||
import apollo3p
|
||||
import os
|
||||
|
||||
DEFAULT_TOOLCHAINS = ['iar', 'keil', 'gcc']
|
||||
|
||||
linker_generators = {
|
||||
'apollo3p': apollo3p.generate_files,
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('config_file')
|
||||
parser.add_argument('-i', dest='iar', action='store_true')
|
||||
parser.add_argument('-k', dest='keil', action='store_true')
|
||||
parser.add_argument('-g', dest='gcc', action='store_true')
|
||||
parser.add_argument('-p', dest='part')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
toolchains = []
|
||||
|
||||
if args.iar:
|
||||
toolchains.append('iar')
|
||||
if args.keil:
|
||||
toolchains.append('keil')
|
||||
if args.gcc:
|
||||
toolchains.append('gcc')
|
||||
|
||||
if toolchains == []:
|
||||
#
|
||||
# No command line arguments were specified.
|
||||
# Determine which toolchains need a linker control file based on whether
|
||||
# a directory already exists for that toolchain.
|
||||
#
|
||||
for toolnm in DEFAULT_TOOLCHAINS:
|
||||
if os.path.isdir(toolnm):
|
||||
toolchains.append(toolnm)
|
||||
if toolchains == []:
|
||||
toolchains = DEFAULT_TOOLCHAINS
|
||||
|
||||
if args.part in linker_generators:
|
||||
print('Building linker scripts for {} and toolchains {}.'.format(args.part, toolchains))
|
||||
linker_generators[args.part](args.config_file, toolchains)
|
||||
else:
|
||||
print("Can't generate linker scripts for {}. (No generator functions found)".format(args.part))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user