initial commit

This commit is contained in:
2022-10-23 23:45:43 -07:00
commit e190fa5193
6450 changed files with 8626944 additions and 0 deletions
@@ -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)