osd-contiki/cpu/cc253x/bank-alloc.py

246 lines
8.3 KiB
Python

#!/usr/bin/env python
# Copyright (c) 2010, Loughborough University - Computer Science
# 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 Institute nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 file is part of the Contiki operating system.
# \file
# Automatic allocation of modules to code segments for bankable builds
# with SDCC's huge memory model.
#
# \author
# George Oikonomou - <oikonomou@users.sourceforge.net>
import sys
import re
import operator
import fileinput
import os
# Open a module object file (.rel) and read it's code size
def retrieve_module_size(file_name):
size_pat = re.compile('^A\s+(?:HOME|BANK[0-9])\s+size\s+([1-9A-F][0-9A-F]*)')
for code_line in open(file_name):
matches = size_pat.search(code_line)
if matches is not None:
return int(matches.group(1), 16)
return 0
# Searches for a code segment rule for file_name in the segment_rules file
# If there is a rule, we respect it. Otherwise, we can move the file around
def get_source_seg(source_file, object_file, segment_rules):
for line in open(segment_rules):
tokens = line.split(None)
match = re.search(tokens[1], source_file)
if match is not None:
# Save it in basename.seg
base, ext = os.path.splitext(object_file)
of = open(base + '.seg', 'w')
of.write(tokens[0] + '\n')
of.close
return tokens[0]
return None
# If segment.rules specified a rule for a source file, the respective object
# file's banking requirement will be stored in object_file.seg
def get_object_seg(object_file):
base, ext = os.path.splitext(object_file)
seg = base + '.seg'
bank = None
if os.path.isfile(seg) is True:
of = open(base + '.seg', 'r')
bank = of.readline().strip()
of.close()
return bank
# Open project.mem and retreive the project's total code footprint
def get_total_size(project):
mem_file = project + '.mem'
pat = re.compile('FLASH\s+(0x[0-9a-f]+\s+){2}([0-9]+)')
for line in open(mem_file):
l = pat.search(line)
if l is not None:
return int(l.group(2))
# Open project.map and retrieve the list of modules linked in
# This will only consider contiki sources, not SDCC libraries
# NB: Sometimes object filenames get truncated:
# contiki-sensinode.lib [ obj_sensinode/watchdog-cc2430.re ]
# See how for this file the 'l' in 'rel' is missing. For that reason, we retrieve
# the filaname until the last '.' but without the extension and we append 'rel'
# As long as the filename doesn't get truncated, we're good
def populate(project, modules, segment_rules, bins):
bankable_total = 0
user_total = 0
map_file = project + '.map'
file_pat = re.compile('obj_cc2530dk[^ ]+\.')
for line in open(map_file):
file_name = file_pat.search(line)
if file_name is not None:
mod = file_name.group(0) + 'rel'
code_size = retrieve_module_size(mod)
seg = get_object_seg(mod)
if seg is not None:
# This module has been assigned to a bank by the user
#print 'In', seg, file_name.group(0), 'size', code_size
bins[seg][0] += code_size
user_total += code_size
else:
# We are free to allocate this module
modules.append([mod, code_size, "NONE"])
bankable_total += code_size
return bankable_total, user_total
# Allocate bankable modules to banks according to a simple
# 'first fit, decreasing' bin packing heuristic.
def bin_pack(modules, bins, offset, log):
if offset==1:
bins['HOME'][1] -= 4096
# Sort by size, descending, in=place
modules.sort(key=operator.itemgetter(1), reverse=True)
for module in modules:
# We want to iterate in a specific order and dict.keys() won't do that
for bin_id in ['HOME', 'BANK1', 'BANK2', 'BANK3', 'BANK4', 'BANK5', 'BANK6', 'BANK7']:
if bins[bin_id][0] + module[1] < bins[bin_id][1]:
bins[bin_id][0] += module[1]
module[2] = bin_id
log.writelines(' '.join([module[2].ljust(8), \
str(module[1]).rjust(5), module[0], '\n']))
break
else:
if bin_id == 'BANK7':
print "Failed to allocate", module[0], "with size", module[1], \
"to a code bank. This is fatal"
return 1
return 0
# Hack the new bank directly in the .rel file
def relocate(module, bank):
code_pat = re.compile('(A\s+)(?:HOME|BANK[0-9])(\s+size\s+[1-9A-F][0-9A-F]*.+\n)')
for line in fileinput.input(module, inplace=1):
m = code_pat.search(line)
if m is not None:
line = m.group(1) + bank + m.group(2)
sys.stdout.write(line)
return
if len(sys.argv) < 3:
print 'Usage:'
print 'bank-alloc.py project path_to_segment_rules [offset]'
print 'bank-alloc.py source_file path_to_segment_rules object_file'
sys.exit(1)
modules = list()
file_name = sys.argv[1]
segment_rules = sys.argv[2]
# Magic: Guess whether we want to determine the code bank for a code file
# or whether we want to bin-pack
basename, ext = os.path.splitext(file_name)
if ext == '.c':
# Code Segment determination
if len(sys.argv) < 4:
print 'Usage:'
print 'bank-alloc.py project path_to_segment_rules [offset]'
print 'bank-alloc.py source_file path_to_segment_rules object_file'
sys.exit(1)
object_file = sys.argv[3]
seg = get_source_seg(file_name, object_file, segment_rules)
if seg is None:
print "BANK1"
else:
print seg
exit()
# Bin-Packing
offset = 0
if len(sys.argv) > 3 and sys.argv[3] is not None:
offset = int(sys.argv[3])
sizes = {'total': 0, 'bankable': 0, 'user': 0, 'libs': 0}
# Name : [Allocated, capacity, start_addr]
bins = {
'HOME': [0, 32768, '0x000000'],
'BANK1': [0, 32768, '0x018000'],
'BANK2': [0, 32768, '0x028000'],
'BANK3': [0, 32768, '0x038000'],
'BANK4': [0, 32768, '0x048000'],
'BANK5': [0, 32768, '0x058000'],
'BANK6': [0, 32768, '0x068000'],
'BANK7': [0, 32768, '0x078000'],
}
sizes['total'] = get_total_size(basename)
sizes['bankable'], sizes['user'] = populate(basename, modules, segment_rules, bins)
sizes['libs'] = sizes['total'] - sizes['bankable'] - sizes['user']
print 'Total Size =', sizes['total'], 'bytes (' + \
str(sizes['bankable']), 'bankable,', \
str(sizes['user']), 'user-allocated,', \
str(sizes['libs']), 'const+libs)'
bins['HOME'][0] += sizes['libs']
print 'Preallocations: HOME=' + str(bins['HOME'][0]),
for bin_id in ['BANK1', 'BANK2', 'BANK3', 'BANK4', 'BANK5', 'BANK6', 'BANK7']:
if bins[bin_id][0] > 0:
print ", " + bin_id + "=" + str(bins[bin_id][0]),
print
# Open a log file
of = open(basename + '.banks', 'w')
pack = bin_pack(modules, bins, offset, of)
of.close()
print "Bin-Packing results (target allocation):"
print "Segment - max - alloc"
for bin_id in ['HOME', 'BANK1', 'BANK2', 'BANK3', 'BANK4', 'BANK5', 'BANK6', 'BANK7']:
if bins[bin_id][0] > 0:
print bin_id.rjust(7), str(bins[bin_id][1]).rjust(6), str(bins[bin_id][0]).rjust(6)
if pack > 0:
sys.exit(1)
# If we reach here we seem to have a sane allocation. Start changing .rel files
for module in modules:
relocate(module[0], module[2])
flags = ""
# Export LD_POST_FLAGS
for bin_id in ['BANK1', 'BANK2', 'BANK3', 'BANK4', 'BANK5', 'BANK6', 'BANK7']:
if bins[bin_id][0] > 0:
flags += "-Wl-b" + bin_id + "=" + bins[bin_id][2] + " "
# Write LD_POST_FLAGS in project.flags
of = open(basename + '.flags', 'w')
of.write(flags + '\n')
of.close()