Add full automotive RTOS project
Add kernel (Cortex-M0/M3/M4, Tricore, S32K, RISC-V ports), drivers, middleware (CAN stack, diagnostics, safety), applications, board support, build/test tooling, and documentation.
This commit is contained in:
Executable
+134
@@ -0,0 +1,134 @@
|
||||
#!/bin/bash
|
||||
/**
|
||||
* @file misra_checker.sh
|
||||
* @brief MISRA C compliance checker
|
||||
*/
|
||||
|
||||
set -e
|
||||
|
||||
# Script directory
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
# Color codes
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# MISRA configuration
|
||||
MISRA_VERSION="MISRA C:2012"
|
||||
MISRA_RULES="required" # required, advisory, all
|
||||
|
||||
# Source directories
|
||||
SOURCE_DIRS=(
|
||||
"${PROJECT_ROOT}/kernel/src"
|
||||
"${PROJECT_ROOT}/drivers/src"
|
||||
"${PROJECT_ROOT}/middleware"
|
||||
"${PROJECT_ROOT}/applications"
|
||||
)
|
||||
|
||||
# Report directory
|
||||
REPORT_DIR="${PROJECT_ROOT}/build/misra"
|
||||
mkdir -p "${REPORT_DIR}"
|
||||
|
||||
# Print banner
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} MISRA C Compliance Checker${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e " Version: ${YELLOW}${MISRA_VERSION}${NC}"
|
||||
echo -e " Rules: ${YELLOW}${MISRA_RULES}${NC}"
|
||||
echo ""
|
||||
|
||||
# Check if cppcheck is available
|
||||
if ! command -v cppcheck >/dev/null 2>&1; then
|
||||
echo -e "${RED}Error: cppcheck not found${NC}"
|
||||
echo "Install with: sudo apt-get install cppcheck"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if misra addon is available
|
||||
MISRA_ADDON="/usr/share/cppcheck/addons/misra.py"
|
||||
if [ ! -f "${MISRA_ADDON}" ]; then
|
||||
echo -e "${YELLOW}Warning: MISRA addon not found at ${MISRA_ADDON}${NC}"
|
||||
echo "Will use cppcheck with basic checks"
|
||||
USE_MISRA_ADDON=false
|
||||
else
|
||||
USE_MISRA_ADDON=true
|
||||
fi
|
||||
|
||||
# Function to check directory
|
||||
check_directory() {
|
||||
local dir=$1
|
||||
local report_file="${REPORT_DIR}/$(basename ${dir})_misra.txt"
|
||||
|
||||
echo -e "${YELLOW}Checking ${dir}...${NC}"
|
||||
|
||||
if [ "${USE_MISRA_ADDON}" = true ]; then
|
||||
cppcheck \
|
||||
--enable=all \
|
||||
--inconclusive \
|
||||
--std=c11 \
|
||||
--platform=arm32 \
|
||||
--addon=misra \
|
||||
--suppress=missingIncludeSystem \
|
||||
--suppress=unusedFunction \
|
||||
-I"${PROJECT_ROOT}/kernel/include" \
|
||||
-I"${PROJECT_ROOT}/drivers/include" \
|
||||
-I"${PROJECT_ROOT}/middleware" \
|
||||
-I"${PROJECT_ROOT}/config" \
|
||||
"${dir}" \
|
||||
2>&1 | tee "${report_file}"
|
||||
else
|
||||
cppcheck \
|
||||
--enable=all \
|
||||
--inconclusive \
|
||||
--std=c11 \
|
||||
--platform=arm32 \
|
||||
--suppress=missingIncludeSystem \
|
||||
--suppress=unusedFunction \
|
||||
-I"${PROJECT_ROOT}/kernel/include" \
|
||||
-I"${PROJECT_ROOT}/drivers/include" \
|
||||
-I"${PROJECT_ROOT}/middleware" \
|
||||
-I"${PROJECT_ROOT}/config" \
|
||||
"${dir}" \
|
||||
2>&1 | tee "${report_file}"
|
||||
fi
|
||||
|
||||
# Count violations
|
||||
local violations=$(grep -c "\[" "${report_file}" || true)
|
||||
echo -e " Found ${violations} potential violations"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Check each source directory
|
||||
TOTAL_VIOLATIONS=0
|
||||
for dir in "${SOURCE_DIRS[@]}"; do
|
||||
if [ -d "${dir}" ]; then
|
||||
check_directory "${dir}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Generate summary report
|
||||
SUMMARY_FILE="${REPORT_DIR}/summary.txt"
|
||||
echo "MISRA C Compliance Summary" > "${SUMMARY_FILE}"
|
||||
echo "=========================" >> "${SUMMARY_FILE}"
|
||||
echo "" >> "${SUMMARY_FILE}"
|
||||
echo "Date: $(date)" >> "${SUMMARY_FILE}"
|
||||
echo "Version: ${MISRA_VERSION}" >> "${SUMMARY_FILE}"
|
||||
echo "Rules: ${MISRA_RULES}" >> "${SUMMARY_FILE}"
|
||||
echo "" >> "${SUMMARY_FILE}"
|
||||
|
||||
for report in "${REPORT_DIR}"/*_misra.txt; do
|
||||
if [ -f "${report}" ]; then
|
||||
dir_name=$(basename "${report}" _misra.txt)
|
||||
violations=$(grep -c "\[" "${report}" || true)
|
||||
echo "${dir_name}: ${violations} violations" >> "${SUMMARY_FILE}"
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} MISRA Check Complete${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "Summary: ${BLUE}${SUMMARY_FILE}${NC}"
|
||||
Executable
+204
@@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
@file stack_usage_analyzer.py
|
||||
@brief Stack usage analyzer for Automotive RTOS
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import argparse
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
class StackUsageAnalyzer:
|
||||
"""Analyze stack usage from map files and source code"""
|
||||
|
||||
def __init__(self, project_root):
|
||||
self.project_root = Path(project_root)
|
||||
self.stack_usage = {}
|
||||
self.function_sizes = {}
|
||||
self.call_graph = defaultdict(list)
|
||||
|
||||
def parse_map_file(self, map_file):
|
||||
"""Parse linker map file for symbol information"""
|
||||
print(f"Parsing map file: {map_file}")
|
||||
|
||||
symbol_pattern = re.compile(
|
||||
r'^\s+0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)\s+(\w+)\s+(\w+)$'
|
||||
)
|
||||
|
||||
with open(map_file, 'r') as f:
|
||||
for line in f:
|
||||
match = symbol_pattern.match(line)
|
||||
if match:
|
||||
address = int(match.group(1), 16)
|
||||
size = int(match.group(2), 16)
|
||||
section = match.group(3)
|
||||
name = match.group(4)
|
||||
|
||||
if section in ['.text', '.data', '.bss']:
|
||||
self.function_sizes[name] = {
|
||||
'size': size,
|
||||
'section': section,
|
||||
'address': address
|
||||
}
|
||||
|
||||
def analyze_source_files(self):
|
||||
"""Analyze source files for stack allocation"""
|
||||
print("Analyzing source files for stack usage...")
|
||||
|
||||
source_patterns = [
|
||||
self.project_root / 'kernel' / 'src' / '**' / '*.c',
|
||||
self.project_root / 'drivers' / 'src' / '**' / '*.c',
|
||||
self.project_root / 'middleware' / '**' / 'src' / '**' / '*.c',
|
||||
self.project_root / 'applications' / '**' / 'src' / '**' / '*.c',
|
||||
]
|
||||
|
||||
stack_var_pattern = re.compile(
|
||||
r'\b(?:uint8_t|uint16_t|uint32_t|int8_t|int16_t|int32_t|'
|
||||
r'char|short|int|long|float|double|struct|union)\s+'
|
||||
r'(\w+)\s*\[(\d+)\]'
|
||||
)
|
||||
|
||||
for pattern in source_patterns:
|
||||
for source_file in self.project_root.glob(str(pattern)):
|
||||
self._analyze_source_file(source_file, stack_var_pattern)
|
||||
|
||||
def _analyze_source_file(self, source_file, pattern):
|
||||
"""Analyze individual source file"""
|
||||
try:
|
||||
with open(source_file, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Find all stack variables
|
||||
for match in pattern.finditer(content):
|
||||
var_name = match.group(1)
|
||||
array_size = int(match.group(2))
|
||||
|
||||
# Estimate variable size
|
||||
var_size = self._estimate_variable_size(match.group(0))
|
||||
|
||||
if var_size > 0:
|
||||
print(f" {source_file.name}: {var_name}[{array_size}] = {var_size} bytes")
|
||||
except Exception as e:
|
||||
print(f" Warning: Could not analyze {source_file}: {e}")
|
||||
|
||||
def _estimate_variable_size(self, declaration):
|
||||
"""Estimate variable size from declaration"""
|
||||
size_map = {
|
||||
'uint8_t': 1, 'int8_t': 1, 'char': 1,
|
||||
'uint16_t': 2, 'int16_t': 2, 'short': 2,
|
||||
'uint32_t': 4, 'int32_t': 4, 'int': 4, 'float': 4,
|
||||
'uint64_t': 8, 'int64_t': 8, 'double': 8, 'long': 8,
|
||||
}
|
||||
|
||||
for type_name, size in size_map.items():
|
||||
if type_name in declaration:
|
||||
# Find array size
|
||||
array_match = re.search(r'\[(\d+)\]', declaration)
|
||||
if array_match:
|
||||
return size * int(array_match.group(1))
|
||||
return size
|
||||
|
||||
return 0
|
||||
|
||||
def analyze_task_stacks(self):
|
||||
"""Analyze task stack configurations"""
|
||||
print("\nAnalyzing task stack configurations...")
|
||||
|
||||
task_config_file = self.project_root / 'config' / 'task_config.h'
|
||||
if not task_config_file.exists():
|
||||
print(" Task configuration file not found")
|
||||
return
|
||||
|
||||
with open(task_config_file, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Find stack size definitions
|
||||
stack_pattern = re.compile(
|
||||
r'#define\s+TASK_STACK_(\w+)\s+(\d+)'
|
||||
)
|
||||
|
||||
total_stack = 0
|
||||
task_count = 0
|
||||
|
||||
print("\n Task Stack Allocations:")
|
||||
print(" " + "-" * 40)
|
||||
|
||||
for match in stack_pattern.finditer(content):
|
||||
task_name = match.group(1)
|
||||
stack_size = int(match.group(2))
|
||||
total_stack += stack_size
|
||||
task_count += 1
|
||||
|
||||
print(f" {task_name:30s} {stack_size:8d} bytes")
|
||||
|
||||
print(" " + "-" * 40)
|
||||
print(f" Total: {total_stack} bytes across {task_count} tasks")
|
||||
|
||||
def generate_report(self, output_file):
|
||||
"""Generate stack usage report"""
|
||||
print(f"\nGenerating report: {output_file}")
|
||||
|
||||
with open(output_file, 'w') as f:
|
||||
f.write("=" * 80 + "\n")
|
||||
f.write("Stack Usage Analysis Report\n")
|
||||
f.write("=" * 80 + "\n\n")
|
||||
|
||||
# Function sizes
|
||||
f.write("Function Sizes:\n")
|
||||
f.write("-" * 40 + "\n")
|
||||
f.write(f"{'Function':30s} {'Size':>8s} {'Section':10s}\n")
|
||||
f.write("-" * 40 + "\n")
|
||||
|
||||
for name, info in sorted(self.function_sizes.items(),
|
||||
key=lambda x: x[1]['size'], reverse=True):
|
||||
f.write(f"{name:30s} {info['size']:8d} {info['section']:10s}\n")
|
||||
|
||||
f.write("\n\n")
|
||||
|
||||
# Recommendations
|
||||
f.write("Recommendations:\n")
|
||||
f.write("-" * 40 + "\n")
|
||||
f.write("1. Review functions with large stack allocations\n")
|
||||
f.write("2. Consider using static allocation for large buffers\n")
|
||||
f.write("3. Monitor stack usage during runtime\n")
|
||||
f.write("4. Ensure stack overflow detection is enabled\n")
|
||||
|
||||
def analyze(self, map_file=None):
|
||||
"""Run complete analysis"""
|
||||
print("=" * 60)
|
||||
print("Stack Usage Analyzer")
|
||||
print("=" * 60)
|
||||
|
||||
# Parse map file if provided
|
||||
if map_file and os.path.exists(map_file):
|
||||
self.parse_map_file(map_file)
|
||||
|
||||
# Analyze source files
|
||||
self.analyze_source_files()
|
||||
|
||||
# Analyze task stacks
|
||||
self.analyze_task_stacks()
|
||||
|
||||
# Generate report
|
||||
report_file = self.project_root / 'build' / 'stack_usage_report.txt'
|
||||
report_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
self.generate_report(report_file)
|
||||
|
||||
print(f"\nReport saved to: {report_file}")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Stack usage analyzer')
|
||||
parser.add_argument('--project', default='.', help='Project root directory')
|
||||
parser.add_argument('--map-file', help='Linker map file')
|
||||
parser.add_argument('--output', help='Output report file')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
analyzer = StackUsageAnalyzer(args.project)
|
||||
analyzer.analyze(args.map_file)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Executable
+215
@@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
@file timing_analyzer.py
|
||||
@brief Timing analyzer for Automotive RTOS
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import argparse
|
||||
import json
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
class TimingAnalyzer:
|
||||
"""Analyze timing requirements and performance"""
|
||||
|
||||
def __init__(self, project_root):
|
||||
self.project_root = Path(project_root)
|
||||
self.tasks = {}
|
||||
self.timing_data = defaultdict(list)
|
||||
self.violations = []
|
||||
|
||||
def analyze_task_configuration(self):
|
||||
"""Analyze task timing configuration"""
|
||||
print("Analyzing task configuration...")
|
||||
|
||||
task_config_file = self.project_root / 'config' / 'task_config.h'
|
||||
if not task_config_file.exists():
|
||||
print("Task configuration file not found")
|
||||
return
|
||||
|
||||
with open(task_config_file, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Parse task priorities and periods
|
||||
task_pattern = re.compile(
|
||||
r'\{"([^"]+)",\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(true|false),\s*(true|false)\}'
|
||||
)
|
||||
|
||||
print("\nTask Configuration:")
|
||||
print("-" * 80)
|
||||
print(f"{'Task Name':25s} {'Priority':>8s} {'Period':>8s} {'Deadline':>8s}")
|
||||
print("-" * 80)
|
||||
|
||||
for match in task_pattern.finditer(content):
|
||||
task_name = match.group(1)
|
||||
priority = int(match.group(2))
|
||||
period = int(match.group(3))
|
||||
deadline = int(match.group(5))
|
||||
|
||||
self.tasks[task_name] = {
|
||||
'priority': priority,
|
||||
'period': period,
|
||||
'deadline': deadline
|
||||
}
|
||||
|
||||
print(f"{task_name:25s} {priority:8d} {period:8d} {deadline:8d}")
|
||||
|
||||
print("-" * 80)
|
||||
|
||||
def analyze_schedulability(self):
|
||||
"""Analyze task schedulability"""
|
||||
print("\nSchedulability Analysis:")
|
||||
print("-" * 60)
|
||||
|
||||
# Calculate CPU utilization
|
||||
total_utilization = 0
|
||||
for task_name, config in self.tasks.items():
|
||||
if config['period'] > 0:
|
||||
utilization = config['deadline'] / config['period']
|
||||
total_utilization += utilization
|
||||
|
||||
print(f"{task_name:25s} {utilization*100:6.2f}%")
|
||||
|
||||
print("-" * 60)
|
||||
print(f"Total CPU Utilization: {total_utilization*100:.2f}%")
|
||||
|
||||
# Check schedulability
|
||||
if total_utilization < 0.69: # Liu & Layland bound for RM
|
||||
print("✓ System is schedulable (Rate Monotonic)")
|
||||
else:
|
||||
print("⚠ System may not be schedulable")
|
||||
self.violations.append("High CPU utilization")
|
||||
|
||||
def analyze_timing_logs(self, log_file):
|
||||
"""Analyze timing logs"""
|
||||
if not os.path.exists(log_file):
|
||||
print(f"Timing log not found: {log_file}")
|
||||
return
|
||||
|
||||
print(f"\nAnalyzing timing log: {log_file}")
|
||||
|
||||
with open(log_file, 'r') as f:
|
||||
for line in f:
|
||||
# Parse timing data
|
||||
match = re.match(
|
||||
r'\[(\d+)\]\s+(\w+):\s+start=(\d+)\s+end=(\d+)\s+duration=(\d+)'
|
||||
, line)
|
||||
|
||||
if match:
|
||||
timestamp = int(match.group(1))
|
||||
task_name = match.group(2)
|
||||
duration = int(match.group(5))
|
||||
|
||||
self.timing_data[task_name].append({
|
||||
'timestamp': timestamp,
|
||||
'duration': duration
|
||||
})
|
||||
|
||||
# Calculate statistics
|
||||
print("\nTiming Statistics:")
|
||||
print("-" * 80)
|
||||
print(f"{'Task Name':25s} {'Count':>6s} {'Min':>8s} {'Max':>8s} {'Avg':>8s} {'Jitter':>8s}")
|
||||
print("-" * 80)
|
||||
|
||||
for task_name, measurements in self.timing_data.items():
|
||||
if measurements:
|
||||
durations = [m['duration'] for m in measurements]
|
||||
count = len(durations)
|
||||
min_duration = min(durations)
|
||||
max_duration = max(durations)
|
||||
avg_duration = sum(durations) / count
|
||||
jitter = max_duration - min_duration
|
||||
|
||||
print(f"{task_name:25s} {count:6d} {min_duration:8d} "
|
||||
f"{max_duration:8d} {avg_duration:8.1f} {jitter:8d}")
|
||||
|
||||
# Check for violations
|
||||
if task_name in self.tasks:
|
||||
deadline = self.tasks[task_name]['deadline']
|
||||
if max_duration > deadline:
|
||||
self.violations.append(
|
||||
f"{task_name} exceeds deadline: {max_duration} > {deadline}"
|
||||
)
|
||||
|
||||
def analyze_interrupt_timing(self):
|
||||
"""Analyze interrupt timing"""
|
||||
print("\nInterrupt Timing Analysis:")
|
||||
print("-" * 40)
|
||||
|
||||
# Common interrupt latencies
|
||||
interrupts = {
|
||||
'SysTick': {'priority': 15, 'typical_latency': 1},
|
||||
'CAN': {'priority': 5, 'typical_latency': 2},
|
||||
'UART': {'priority': 6, 'typical_latency': 1},
|
||||
'SPI': {'priority': 7, 'typical_latency': 1},
|
||||
'I2C': {'priority': 7, 'typical_latency': 2},
|
||||
'ADC': {'priority': 8, 'typical_latency': 3},
|
||||
'Timer': {'priority': 8, 'typical_latency': 1},
|
||||
}
|
||||
|
||||
for irq_name, config in interrupts.items():
|
||||
print(f"{irq_name:15s} Priority: {config['priority']:2d} "
|
||||
f"Latency: {config['typical_latency']}ms")
|
||||
|
||||
def generate_report(self, output_file):
|
||||
"""Generate timing analysis report"""
|
||||
print(f"\nGenerating report: {output_file}")
|
||||
|
||||
with open(output_file, 'w') as f:
|
||||
f.write("=" * 80 + "\n")
|
||||
f.write("Timing Analysis Report\n")
|
||||
f.write("=" * 80 + "\n\n")
|
||||
|
||||
# Task configuration
|
||||
f.write("Task Configuration:\n")
|
||||
f.write("-" * 40 + "\n")
|
||||
for task_name, config in self.tasks.items():
|
||||
f.write(f"{task_name}: Priority={config['priority']}, "
|
||||
f"Period={config['period']}ms, Deadline={config['deadline']}ms\n")
|
||||
|
||||
f.write("\n")
|
||||
|
||||
# Violations
|
||||
if self.violations:
|
||||
f.write("Timing Violations:\n")
|
||||
f.write("-" * 40 + "\n")
|
||||
for violation in self.violations:
|
||||
f.write(f"✗ {violation}\n")
|
||||
else:
|
||||
f.write("No timing violations detected.\n")
|
||||
|
||||
def analyze(self, timing_log=None):
|
||||
"""Run complete analysis"""
|
||||
print("=" * 60)
|
||||
print("Timing Analyzer")
|
||||
print("=" * 60)
|
||||
|
||||
self.analyze_task_configuration()
|
||||
self.analyze_schedulability()
|
||||
self.analyze_interrupt_timing()
|
||||
|
||||
if timing_log:
|
||||
self.analyze_timing_logs(timing_log)
|
||||
|
||||
# Generate report
|
||||
report_file = self.project_root / 'build' / 'timing_report.txt'
|
||||
report_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
self.generate_report(report_file)
|
||||
|
||||
print(f"\nReport saved to: {report_file}")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Timing analyzer')
|
||||
parser.add_argument('--project', default='.', help='Project root directory')
|
||||
parser.add_argument('--timing-log', help='Timing log file')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
analyzer = TimingAnalyzer(args.project)
|
||||
analyzer.analyze(args.timing_log)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Executable
+212
@@ -0,0 +1,212 @@
|
||||
#!/bin/bash
|
||||
/**
|
||||
* @file build_all.sh
|
||||
* @brief Build all targets for Automotive RTOS
|
||||
*/
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
# Script directory
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
# Color codes
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Build configuration
|
||||
BUILD_DIR="${PROJECT_ROOT}/build"
|
||||
LOG_DIR="${BUILD_DIR}/logs"
|
||||
TARGETS=("stm32f407_discovery" "nxp_s32k144_evb" "custom_ecu")
|
||||
BUILD_TYPES=("debug" "release")
|
||||
|
||||
# Create directories
|
||||
mkdir -p "${BUILD_DIR}"
|
||||
mkdir -p "${LOG_DIR}"
|
||||
|
||||
# Print banner
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} Automotive RTOS Build System${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Function to build target
|
||||
build_target() {
|
||||
local target=$1
|
||||
local build_type=$2
|
||||
|
||||
echo -e "${YELLOW}Building ${target} (${build_type})...${NC}"
|
||||
|
||||
local build_dir="${BUILD_DIR}/${target}/${build_type}"
|
||||
mkdir -p "${build_dir}"
|
||||
|
||||
# Change to build directory
|
||||
cd "${build_dir}"
|
||||
|
||||
# Run CMake
|
||||
cmake "${PROJECT_ROOT}" \
|
||||
-DTARGET_BOARD=${target} \
|
||||
-DCMAKE_BUILD_TYPE=${build_type} \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${PROJECT_ROOT}/cmake/toolchain-${target}.cmake" \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
2>&1 | tee "${LOG_DIR}/${target}_${build_type}_configure.log"
|
||||
|
||||
# Build
|
||||
make -j$(nproc) 2>&1 | tee "${LOG_DIR}/${target}_${build_type}_build.log"
|
||||
|
||||
# Check build result
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ ${target} (${build_type}) built successfully${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ ${target} (${build_type}) build failed${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Generate artifacts
|
||||
echo -e "${YELLOW}Generating artifacts...${NC}"
|
||||
|
||||
# Create hex file
|
||||
arm-none-eabi-objcopy -O ihex "${build_dir}/automotive_rtos.elf" \
|
||||
"${build_dir}/automotive_rtos.hex"
|
||||
|
||||
# Create binary file
|
||||
arm-none-eabi-objcopy -O binary "${build_dir}/automotive_rtos.elf" \
|
||||
"${build_dir}/automotive_rtos.bin"
|
||||
|
||||
# Generate size report
|
||||
arm-none-eabi-size "${build_dir}/automotive_rtos.elf" > \
|
||||
"${build_dir}/size_report.txt"
|
||||
|
||||
# Generate map file
|
||||
arm-none-eabi-nm -n "${build_dir}/automotive_rtos.elf" > \
|
||||
"${build_dir}/symbols.txt"
|
||||
|
||||
echo -e "${GREEN}✓ Artifacts generated${NC}"
|
||||
echo ""
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Function to run tests
|
||||
run_tests() {
|
||||
echo -e "${YELLOW}Running unit tests...${NC}"
|
||||
|
||||
local test_dir="${BUILD_DIR}/tests"
|
||||
mkdir -p "${test_dir}"
|
||||
|
||||
# Run each test
|
||||
for test_file in "${PROJECT_ROOT}"/tests/unit/test_*.c; do
|
||||
test_name=$(basename "${test_file}" .c)
|
||||
echo -e " Testing ${test_name}..."
|
||||
|
||||
# Compile test
|
||||
gcc -I"${PROJECT_ROOT}/kernel/include" \
|
||||
-I"${PROJECT_ROOT}/tests" \
|
||||
-I"${PROJECT_ROOT}/third_party/unity" \
|
||||
"${test_file}" \
|
||||
"${PROJECT_ROOT}/tests/unity/unity.c" \
|
||||
-o "${test_dir}/${test_name}" \
|
||||
-Wall -Wextra -g
|
||||
|
||||
# Run test
|
||||
if "${test_dir}/${test_name}" > "${LOG_DIR}/${test_name}.log" 2>&1; then
|
||||
echo -e "${GREEN} ✓ ${test_name} passed${NC}"
|
||||
else
|
||||
echo -e "${RED} ✗ ${test_name} failed${NC}"
|
||||
cat "${LOG_DIR}/${test_name}.log"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${GREEN}✓ All tests passed${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Main build process
|
||||
main() {
|
||||
local build_failed=0
|
||||
|
||||
# Parse arguments
|
||||
local clean_build=false
|
||||
local run_all_tests=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--clean)
|
||||
clean_build=true
|
||||
shift
|
||||
;;
|
||||
--test)
|
||||
run_all_tests=true
|
||||
shift
|
||||
;;
|
||||
--target)
|
||||
TARGETS=("$2")
|
||||
shift 2
|
||||
;;
|
||||
--type)
|
||||
BUILD_TYPES=("$2")
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Clean build if requested
|
||||
if [ "${clean_build}" = true ]; then
|
||||
echo -e "${YELLOW}Cleaning build directory...${NC}"
|
||||
rm -rf "${BUILD_DIR}"
|
||||
mkdir -p "${BUILD_DIR}"
|
||||
echo -e "${GREEN}✓ Clean complete${NC}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Build all targets
|
||||
for target in "${TARGETS[@]}"; do
|
||||
for build_type in "${BUILD_TYPES[@]}"; do
|
||||
if ! build_target "${target}" "${build_type}"; then
|
||||
build_failed=1
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Run tests if requested
|
||||
if [ "${run_all_tests}" = true ]; then
|
||||
if ! run_tests; then
|
||||
build_failed=1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Print summary
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} Build Summary${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
|
||||
if [ ${build_failed} -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ All builds successful${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Some builds failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Print size reports
|
||||
echo ""
|
||||
echo -e "${YELLOW}Size Reports:${NC}"
|
||||
for target in "${TARGETS[@]}"; do
|
||||
for build_type in "${BUILD_TYPES[@]}"; do
|
||||
size_file="${BUILD_DIR}/${target}/${build_type}/size_report.txt"
|
||||
if [ -f "${size_file}" ]; then
|
||||
echo -e " ${target} (${build_type}):"
|
||||
cat "${size_file}" | tail -n 2
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
# Run main
|
||||
main "$@"
|
||||
Executable
+219
@@ -0,0 +1,219 @@
|
||||
#!/bin/bash
|
||||
/**
|
||||
* @file build_target.sh
|
||||
* @brief Build specific target
|
||||
*/
|
||||
|
||||
set -e
|
||||
|
||||
# Script directory
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
# Default values
|
||||
TARGET=""
|
||||
BUILD_TYPE="debug"
|
||||
CLEAN=false
|
||||
VERBOSE=false
|
||||
JOBS=$(nproc)
|
||||
|
||||
# Color codes
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Function to print usage
|
||||
print_usage() {
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -t, --target <target> Target board (required)"
|
||||
echo " Available: stm32f407_discovery"
|
||||
echo " nxp_s32k144_evb"
|
||||
echo " custom_ecu"
|
||||
echo " -b, --build-type <type> Build type (default: debug)"
|
||||
echo " Available: debug, release"
|
||||
echo " -c, --clean Clean build"
|
||||
echo " -v, --verbose Verbose output"
|
||||
echo " -j, --jobs <number> Number of parallel jobs"
|
||||
echo " -h, --help Show this help"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-t|--target)
|
||||
TARGET="$2"
|
||||
shift 2
|
||||
;;
|
||||
-b|--build-type)
|
||||
BUILD_TYPE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-c|--clean)
|
||||
CLEAN=true
|
||||
shift
|
||||
;;
|
||||
-v|--verbose)
|
||||
VERBOSE=true
|
||||
shift
|
||||
;;
|
||||
-j|--jobs)
|
||||
JOBS="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
print_usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
print_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate target
|
||||
if [ -z "${TARGET}" ]; then
|
||||
echo -e "${RED}Error: Target not specified${NC}"
|
||||
print_usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate build type
|
||||
case "${BUILD_TYPE}" in
|
||||
debug|release)
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Error: Invalid build type: ${BUILD_TYPE}${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Build directories
|
||||
BUILD_DIR="${PROJECT_ROOT}/build/${TARGET}/${BUILD_TYPE}"
|
||||
LOG_DIR="${PROJECT_ROOT}/build/logs"
|
||||
|
||||
# Create directories
|
||||
mkdir -p "${BUILD_DIR}"
|
||||
mkdir -p "${LOG_DIR}"
|
||||
|
||||
# Print build info
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} Building Automotive RTOS${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e " Target: ${YELLOW}${TARGET}${NC}"
|
||||
echo -e " Build Type: ${YELLOW}${BUILD_TYPE}${NC}"
|
||||
echo -e " Jobs: ${YELLOW}${JOBS}${NC}"
|
||||
echo ""
|
||||
|
||||
# Clean if requested
|
||||
if [ "${CLEAN}" = true ]; then
|
||||
echo -e "${YELLOW}Cleaning build directory...${NC}"
|
||||
rm -rf "${BUILD_DIR}"/*
|
||||
echo -e "${GREEN}✓ Clean complete${NC}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Configure
|
||||
echo -e "${YELLOW}Configuring build...${NC}"
|
||||
|
||||
# Set CMake arguments
|
||||
CMAKE_ARGS=(
|
||||
"${PROJECT_ROOT}"
|
||||
"-DTARGET_BOARD=${TARGET}"
|
||||
"-DCMAKE_BUILD_TYPE=${BUILD_TYPE}"
|
||||
"-DCMAKE_TOOLCHAIN_FILE=${PROJECT_ROOT}/cmake/toolchain-${TARGET}.cmake"
|
||||
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
|
||||
)
|
||||
|
||||
if [ "${VERBOSE}" = true ]; then
|
||||
CMAKE_ARGS+=("-DCMAKE_VERBOSE_MAKEFILE=ON")
|
||||
fi
|
||||
|
||||
# Run CMake
|
||||
cd "${BUILD_DIR}"
|
||||
cmake "${CMAKE_ARGS[@]}" 2>&1 | tee "${LOG_DIR}/${TARGET}_${BUILD_TYPE}_configure.log"
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}✗ Configuration failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Configuration complete${NC}"
|
||||
echo ""
|
||||
|
||||
# Build
|
||||
echo -e "${YELLOW}Building...${NC}"
|
||||
|
||||
make -j${JOBS} 2>&1 | tee "${LOG_DIR}/${TARGET}_${BUILD_TYPE}_build.log"
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}✗ Build failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Build complete${NC}"
|
||||
echo ""
|
||||
|
||||
# Generate artifacts
|
||||
echo -e "${YELLOW}Generating artifacts...${NC}"
|
||||
|
||||
ELF_FILE="${BUILD_DIR}/automotive_rtos.elf"
|
||||
HEX_FILE="${BUILD_DIR}/automotive_rtos.hex"
|
||||
BIN_FILE="${BUILD_DIR}/automotive_rtos.bin"
|
||||
MAP_FILE="${BUILD_DIR}/automotive_rtos.map"
|
||||
SIZE_FILE="${BUILD_DIR}/size_report.txt"
|
||||
|
||||
# Generate hex file
|
||||
arm-none-eabi-objcopy -O ihex "${ELF_FILE}" "${HEX_FILE}"
|
||||
|
||||
# Generate binary file
|
||||
arm-none-eabi-objcopy -O binary "${ELF_FILE}" "${BIN_FILE}"
|
||||
|
||||
# Generate map file
|
||||
arm-none-eabi-nm -n "${ELF_FILE}" > "${MAP_FILE}"
|
||||
|
||||
# Generate size report
|
||||
arm-none-eabi-size "${ELF_FILE}" > "${SIZE_FILE}"
|
||||
|
||||
echo -e "${GREEN}✓ Artifacts generated:${NC}"
|
||||
echo -e " ELF: ${BLUE}${ELF_FILE}${NC}"
|
||||
echo -e " HEX: ${BLUE}${HEX_FILE}${NC}"
|
||||
echo -e " BIN: ${BLUE}${BIN_FILE}${NC}"
|
||||
echo -e " MAP: ${BLUE}${MAP_FILE}${NC}"
|
||||
echo ""
|
||||
|
||||
# Print size report
|
||||
echo -e "${YELLOW}Size Report:${NC}"
|
||||
cat "${SIZE_FILE}"
|
||||
echo ""
|
||||
|
||||
# Run static analysis if available
|
||||
if command -v cppcheck >/dev/null 2>&1; then
|
||||
echo -e "${YELLOW}Running static analysis...${NC}"
|
||||
|
||||
cppcheck \
|
||||
--enable=all \
|
||||
--inconclusive \
|
||||
--std=c11 \
|
||||
--platform=arm32 \
|
||||
-I"${PROJECT_ROOT}/kernel/include" \
|
||||
-I"${PROJECT_ROOT}/drivers/include" \
|
||||
-I"${PROJECT_ROOT}/middleware/include" \
|
||||
"${PROJECT_ROOT}/kernel/src" \
|
||||
"${PROJECT_ROOT}/drivers/src" \
|
||||
"${PROJECT_ROOT}/middleware/src" \
|
||||
2>&1 | tee "${LOG_DIR}/${TARGET}_${BUILD_TYPE}_cppcheck.log"
|
||||
|
||||
echo -e "${GREEN}✓ Static analysis complete${NC}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} Build Successful${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
Executable
+189
@@ -0,0 +1,189 @@
|
||||
#!/bin/bash
|
||||
/**
|
||||
* @file flash_target.sh
|
||||
* @brief Flash firmware to target board
|
||||
*/
|
||||
|
||||
set -e
|
||||
|
||||
# Script directory
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
# Default values
|
||||
TARGET=""
|
||||
BUILD_TYPE="debug"
|
||||
PROGRAMMER=""
|
||||
INTERFACE=""
|
||||
|
||||
# Color codes
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Function to print usage
|
||||
print_usage() {
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -t, --target <target> Target board (required)"
|
||||
echo " -b, --build-type <type> Build type (default: debug)"
|
||||
echo " -p, --programmer <prog> Programmer type"
|
||||
echo " Available: stlink, jlink, openocd"
|
||||
echo " -i, --interface <iface> Debug interface"
|
||||
echo " Available: swd, jtag"
|
||||
echo " -h, --help Show this help"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-t|--target)
|
||||
TARGET="$2"
|
||||
shift 2
|
||||
;;
|
||||
-b|--build-type)
|
||||
BUILD_TYPE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p|--programmer)
|
||||
PROGRAMMER="$2"
|
||||
shift 2
|
||||
;;
|
||||
-i|--interface)
|
||||
INTERFACE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
print_usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
print_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate target
|
||||
if [ -z "${TARGET}" ]; then
|
||||
echo -e "${RED}Error: Target not specified${NC}"
|
||||
print_usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set default programmer based on target
|
||||
if [ -z "${PROGRAMMER}" ]; then
|
||||
case "${TARGET}" in
|
||||
stm32f407_discovery)
|
||||
PROGRAMMER="stlink"
|
||||
INTERFACE="swd"
|
||||
;;
|
||||
nxp_s32k144_evb)
|
||||
PROGRAMMER="jlink"
|
||||
INTERFACE="swd"
|
||||
;;
|
||||
custom_ecu)
|
||||
PROGRAMMER="openocd"
|
||||
INTERFACE="swd"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Firmware file
|
||||
FIRMWARE_FILE="${PROJECT_ROOT}/build/${TARGET}/${BUILD_TYPE}/automotive_rtos.elf"
|
||||
HEX_FILE="${PROJECT_ROOT}/build/${TARGET}/${BUILD_TYPE}/automotive_rtos.hex"
|
||||
|
||||
# Check if firmware exists
|
||||
if [ ! -f "${FIRMWARE_FILE}" ]; then
|
||||
echo -e "${RED}Error: Firmware not found: ${FIRMWARE_FILE}${NC}"
|
||||
echo -e "${YELLOW}Run build_target.sh first${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Print flash info
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} Flashing Firmware${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e " Target: ${YELLOW}${TARGET}${NC}"
|
||||
echo -e " Programmer: ${YELLOW}${PROGRAMMER}${NC}"
|
||||
echo -e " Interface: ${YELLOW}${INTERFACE}${NC}"
|
||||
echo -e " Firmware: ${YELLOW}${FIRMWARE_FILE}${NC}"
|
||||
echo ""
|
||||
|
||||
# Flash based on programmer
|
||||
case "${PROGRAMMER}" in
|
||||
stlink)
|
||||
echo -e "${YELLOW}Flashing with ST-Link...${NC}"
|
||||
st-flash --reset write "${HEX_FILE}" 0x08000000
|
||||
;;
|
||||
|
||||
jlink)
|
||||
echo -e "${YELLOW}Flashing with J-Link...${NC}"
|
||||
|
||||
# Create J-Link script
|
||||
JLINK_SCRIPT="${PROJECT_ROOT}/build/flash.jlink"
|
||||
cat > "${JLINK_SCRIPT}" << EOF
|
||||
device ${TARGET}
|
||||
si ${INTERFACE}
|
||||
speed 4000
|
||||
loadfile ${HEX_FILE}
|
||||
r
|
||||
g
|
||||
q
|
||||
EOF
|
||||
|
||||
JLinkExe -CommanderScript "${JLINK_SCRIPT}"
|
||||
;;
|
||||
|
||||
openocd)
|
||||
echo -e "${YELLOW}Flashing with OpenOCD...${NC}"
|
||||
|
||||
# OpenOCD configuration
|
||||
case "${TARGET}" in
|
||||
stm32f407_discovery)
|
||||
OOCD_CFG="board/stm32f4discovery.cfg"
|
||||
;;
|
||||
*)
|
||||
OOCD_CFG="board/${TARGET}.cfg"
|
||||
;;
|
||||
esac
|
||||
|
||||
openocd \
|
||||
-f "interface/${INTERFACE}.cfg" \
|
||||
-f "${OOCD_CFG}" \
|
||||
-c "program ${FIRMWARE_FILE} verify reset exit"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo -e "${RED}Error: Unknown programmer: ${PROGRAMMER}${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Flash successful${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Flash failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify flash
|
||||
echo -e "${YELLOW}Verifying flash...${NC}"
|
||||
|
||||
case "${PROGRAMMER}" in
|
||||
stlink)
|
||||
st-flash verify "${HEX_FILE}"
|
||||
;;
|
||||
*)
|
||||
echo -e "${YELLOW}Verification not supported for ${PROGRAMMER}${NC}"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} Flash Complete${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
Executable
+217
@@ -0,0 +1,217 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
@file config_generator.py
|
||||
@brief Configuration file generator for Automotive RTOS
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Any
|
||||
|
||||
class ConfigGenerator:
|
||||
"""Generate RTOS configuration files"""
|
||||
|
||||
def __init__(self, project_root):
|
||||
self.project_root = Path(project_root)
|
||||
self.config_dir = self.project_root / 'config'
|
||||
|
||||
def load_configuration(self, config_file):
|
||||
"""Load configuration from JSON file"""
|
||||
with open(config_file, 'r') as f:
|
||||
return json.load(f)
|
||||
|
||||
def generate_kernel_config(self, config: Dict[str, Any]):
|
||||
"""Generate kernel_config.h"""
|
||||
print("Generating kernel_config.h...")
|
||||
|
||||
kernel = config.get('kernel', {})
|
||||
|
||||
content = f"""/**
|
||||
* @file kernel_config.h
|
||||
* @brief Kernel configuration (auto-generated)
|
||||
* @note This file is auto-generated. Do not modify manually.
|
||||
*/
|
||||
|
||||
#ifndef KERNEL_CONFIG_H
|
||||
#define KERNEL_CONFIG_H
|
||||
|
||||
/* ============================================================================
|
||||
* Basic Kernel Configuration
|
||||
* ============================================================================ */
|
||||
#define MAX_TASKS {kernel.get('max_tasks', 32)}
|
||||
#define MAX_PRIORITY_LEVELS {kernel.get('max_priorities', 16)}
|
||||
#define TICK_RATE_HZ {kernel.get('tick_rate', 1000)}
|
||||
#define MAX_TASK_NAME_LENGTH {kernel.get('max_task_name_length', 16)}
|
||||
|
||||
/* ============================================================================
|
||||
* Scheduler Configuration
|
||||
* ============================================================================ */
|
||||
#define SCHEDULER_TYPE SCHEDULER_PRIORITY_PREEMPTIVE
|
||||
#define ENABLE_ROUND_ROBIN {1 if kernel.get('round_robin', True) else 0}
|
||||
#define ROUND_ROBIN_TIME_SLICE {kernel.get('time_slice', 10)}
|
||||
#define ENABLE_DEADLINE_MONITORING {1 if kernel.get('deadline_monitoring', True) else 0}
|
||||
|
||||
/* ============================================================================
|
||||
* Synchronization Configuration
|
||||
* ============================================================================ */
|
||||
#define MAX_SEMAPHORES {kernel.get('max_semaphores', 32)}
|
||||
#define MAX_MUTEXES {kernel.get('max_mutexes', 16)}
|
||||
#define MAX_QUEUES {kernel.get('max_queues', 16)}
|
||||
#define MAX_TIMERS {kernel.get('max_timers', 16)}
|
||||
|
||||
/* ============================================================================
|
||||
* Memory Configuration
|
||||
* ============================================================================ */
|
||||
#define ENABLE_MEMORY_PROTECTION {1 if kernel.get('memory_protection', True) else 0}
|
||||
#define ENABLE_STACK_CHECK {1 if kernel.get('stack_check', True) else 0}
|
||||
#define DEFAULT_TASK_STACK_SIZE {kernel.get('default_stack_size', 1024)}
|
||||
#define HEAP_SIZE {kernel.get('heap_size', 65536)}
|
||||
|
||||
#endif /* KERNEL_CONFIG_H */
|
||||
"""
|
||||
|
||||
self._write_file(self.config_dir / 'kernel_config.h', content)
|
||||
|
||||
def generate_task_config(self, config: Dict[str, Any]):
|
||||
"""Generate task_config.h"""
|
||||
print("Generating task_config.h...")
|
||||
|
||||
tasks = config.get('tasks', [])
|
||||
|
||||
content = f"""/**
|
||||
* @file task_config.h
|
||||
* @brief Task configuration (auto-generated)
|
||||
* @note This file is auto-generated. Do not modify manually.
|
||||
*/
|
||||
|
||||
#ifndef TASK_CONFIG_H
|
||||
#define TASK_CONFIG_H
|
||||
|
||||
#include "kernel_config.h"
|
||||
|
||||
/* ============================================================================
|
||||
* Task Priority Definitions
|
||||
* ============================================================================ */
|
||||
"""
|
||||
|
||||
# Generate priority definitions
|
||||
for task in tasks:
|
||||
name = task['name'].upper().replace(' ', '_')
|
||||
priority = task.get('priority', 0)
|
||||
content += f"#define TASK_PRIORITY_{name:<30s} {priority}\n"
|
||||
|
||||
content += "\n/* ============================================================================\n"
|
||||
content += " * Task Period Definitions (ms)\n"
|
||||
content += " * ============================================================================ */\n"
|
||||
|
||||
# Generate period definitions
|
||||
for task in tasks:
|
||||
name = task['name'].upper().replace(' ', '_')
|
||||
period = task.get('period_ms', 10)
|
||||
content += f"#define TASK_PERIOD_{name:<30s} {period}\n"
|
||||
|
||||
content += "\n/* ============================================================================\n"
|
||||
content += " * Task Stack Sizes (bytes)\n"
|
||||
content += " * ============================================================================ */\n"
|
||||
|
||||
# Generate stack size definitions
|
||||
for task in tasks:
|
||||
name = task['name'].upper().replace(' ', '_')
|
||||
stack_size = task.get('stack_size', 1024)
|
||||
content += f"#define TASK_STACK_{name:<30s} {stack_size}\n"
|
||||
|
||||
content += "\n/* ============================================================================\n"
|
||||
content += " * Task Configuration Table\n"
|
||||
content += " * ============================================================================ */\n\n"
|
||||
|
||||
content += "static const TaskConfigEntry_t task_config_table[] = {\n"
|
||||
content += " /* Name, Priority, Period, Stack, Deadline, Periodic, Critical */\n"
|
||||
|
||||
for task in tasks:
|
||||
name = task['name']
|
||||
priority = task.get('priority', 0)
|
||||
period = task.get('period_ms', 10)
|
||||
stack_size = task.get('stack_size', 1024)
|
||||
deadline = task.get('deadline_ms', period)
|
||||
periodic = 'true' if task.get('periodic', True) else 'false'
|
||||
critical = 'true' if task.get('critical', False) else 'false'
|
||||
|
||||
content += f' {{"{name}", {priority}, {period}, {stack_size}, '
|
||||
content += f'{deadline}, {periodic}, {critical}}},\n'
|
||||
|
||||
content += "};\n\n"
|
||||
content += f"#define TASK_CONFIG_COUNT {len(tasks)}\n\n"
|
||||
content += "#endif /* TASK_CONFIG_H */\n"
|
||||
|
||||
self._write_file(self.config_dir / 'task_config.h', content)
|
||||
|
||||
def generate_can_config(self, config: Dict[str, Any]):
|
||||
"""Generate can_config.h"""
|
||||
print("Generating can_config.h...")
|
||||
|
||||
can_config = config.get('can', {})
|
||||
|
||||
content = f"""/**
|
||||
* @file can_config.h
|
||||
* @brief CAN configuration (auto-generated)
|
||||
*/
|
||||
|
||||
#ifndef CAN_CONFIG_H
|
||||
#define CAN_CONFIG_H
|
||||
|
||||
/* CAN Baudrate */
|
||||
#define CAN_BAUDRATE {can_config.get('baudrate', 500000)}
|
||||
#define CAN_FD_BAUDRATE {can_config.get('fd_baudrate', 2000000)}
|
||||
#define CAN_FD_ENABLED {1 if can_config.get('fd_enabled', False) else 0}
|
||||
|
||||
/* CAN Message IDs */
|
||||
"""
|
||||
|
||||
# Generate message ID definitions
|
||||
messages = can_config.get('messages', [])
|
||||
for msg in messages:
|
||||
name = msg['name'].upper().replace(' ', '_')
|
||||
msg_id = msg.get('id', 0x100)
|
||||
content += f"#define CAN_ID_{name:<30s} 0x{msg_id:03X}\n"
|
||||
|
||||
content += "\n#endif /* CAN_CONFIG_H */\n"
|
||||
|
||||
self._write_file(self.config_dir / 'can_config.h', content)
|
||||
|
||||
def _write_file(self, file_path: Path, content: str):
|
||||
"""Write content to file"""
|
||||
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(content)
|
||||
print(f" Generated: {file_path}")
|
||||
|
||||
def generate(self, config_file: str):
|
||||
"""Generate all configuration files"""
|
||||
print("=" * 60)
|
||||
print("Configuration Generator")
|
||||
print("=" * 60)
|
||||
|
||||
config = self.load_configuration(config_file)
|
||||
|
||||
# Generate configuration files
|
||||
self.generate_kernel_config(config)
|
||||
self.generate_task_config(config)
|
||||
self.generate_can_config(config)
|
||||
|
||||
print("\nConfiguration generation complete")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Configuration generator')
|
||||
parser.add_argument('config_file', help='Configuration JSON file')
|
||||
parser.add_argument('--project', default='.', help='Project root directory')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
generator = ConfigGenerator(args.project)
|
||||
generator.generate(args.config_file)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
@file task_priority_calculator.py
|
||||
@brief Task priority calculator for Rate Monotonic Scheduling
|
||||
"""
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
from collections import defaultdict
|
||||
from math import ceil
|
||||
|
||||
class TaskPriorityCalculator:
|
||||
"""Calculate task priorities using RMS/DMS algorithms"""
|
||||
|
||||
def __init__(self):
|
||||
self.tasks = []
|
||||
|
||||
def add_task(self, name, period_ms, execution_time_ms, deadline_ms=None):
|
||||
"""Add task for analysis"""
|
||||
if deadline_ms is None:
|
||||
deadline_ms = period_ms
|
||||
|
||||
self.tasks.append({
|
||||
'name': name,
|
||||
'period': period_ms,
|
||||
'execution_time': execution_time_ms,
|
||||
'deadline': deadline_ms
|
||||
})
|
||||
|
||||
def calculate_utilization(self):
|
||||
"""Calculate CPU utilization"""
|
||||
total_utilization = 0
|
||||
|
||||
for task in self.tasks:
|
||||
utilization = task['execution_time'] / task['period']
|
||||
task['utilization'] = utilization
|
||||
total_utilization += utilization
|
||||
|
||||
return total_utilization
|
||||
|
||||
def rate_monotonic_priority(self):
|
||||
"""Assign priorities using Rate Monotonic Scheduling"""
|
||||
# Sort by period (shortest period = highest priority)
|
||||
sorted_tasks = sorted(self.tasks, key=lambda x: x['period'])
|
||||
|
||||
for i, task in enumerate(sorted_tasks):
|
||||
task['rms_priority'] = i
|
||||
|
||||
return sorted_tasks
|
||||
|
||||
def deadline_monotonic_priority(self):
|
||||
"""Assign priorities using Deadline Monotonic Scheduling"""
|
||||
# Sort by deadline (shortest deadline = highest priority)
|
||||
sorted_tasks = sorted(self.tasks, key=lambda x: x['deadline'])
|
||||
|
||||
for i, task in enumerate(sorted_tasks):
|
||||
task['dms_priority'] = i
|
||||
|
||||
return sorted_tasks
|
||||
|
||||
def check_schedulability(self):
|
||||
"""Check if task set is schedulable"""
|
||||
n = len(self.tasks)
|
||||
total_utilization = self.calculate_utilization()
|
||||
|
||||
# Liu & Layland bound for RMS
|
||||
rms_bound = n * (2 ** (1/n) - 1)
|
||||
|
||||
# Exact schedulability test
|
||||
schedulable = True
|
||||
|
||||
for i, task in enumerate(sorted(self.tasks, key=lambda x: x['priority'])):
|
||||
# Calculate response time
|
||||
response_time = task['execution_time']
|
||||
|
||||
while True:
|
||||
interference = 0
|
||||
for j in range(i):
|
||||
higher_task = self.tasks[j]
|
||||
interference += ceil(response_time / higher_task['period']) * \
|
||||
higher_task['execution_time']
|
||||
|
||||
new_response = task['execution_time'] + interference
|
||||
|
||||
if new_response == response_time:
|
||||
break
|
||||
|
||||
response_time = new_response
|
||||
|
||||
if response_time > task['deadline']:
|
||||
schedulable = False
|
||||
break
|
||||
|
||||
task['response_time'] = response_time
|
||||
|
||||
if not schedulable:
|
||||
break
|
||||
|
||||
return schedulable, total_utilization, rms_bound
|
||||
|
||||
def generate_report(self):
|
||||
"""Generate priority assignment report"""
|
||||
print("=" * 80)
|
||||
print("Task Priority Calculation Report")
|
||||
print("=" * 80)
|
||||
print()
|
||||
|
||||
# Calculate utilization
|
||||
total_utilization = self.calculate_utilization()
|
||||
|
||||
print("Task Set:")
|
||||
print("-" * 80)
|
||||
print(f"{'Task':20s} {'Period':>8s} {'Exec Time':>10s} {'Deadline':>8s} {'Util':>8s}")
|
||||
print("-" * 80)
|
||||
|
||||
for task in self.tasks:
|
||||
print(f"{task['name']:20s} {task['period']:8d} "
|
||||
f"{task['execution_time']:10d} {task['deadline']:8d} "
|
||||
f"{task['utilization']*100:7.2f}%")
|
||||
|
||||
print("-" * 80)
|
||||
print(f"{'Total':20s} {'':>8s} {'':>10s} {'':>8s} {total_utilization*100:7.2f}%")
|
||||
print()
|
||||
|
||||
# Rate Monotonic
|
||||
print("Rate Monotonic Priority Assignment:")
|
||||
print("-" * 40)
|
||||
rms_tasks = self.rate_monotonic_priority()
|
||||
for task in rms_tasks:
|
||||
print(f" Priority {task['rms_priority']}: {task['name']}")
|
||||
print()
|
||||
|
||||
# Deadline Monotonic
|
||||
print("Deadline Monotonic Priority Assignment:")
|
||||
print("-" * 40)
|
||||
dms_tasks = self.deadline_monotonic_priority()
|
||||
for task in dms_tasks:
|
||||
print(f" Priority {task['dms_priority']}: {task['name']}")
|
||||
print()
|
||||
|
||||
# Schedulability
|
||||
schedulable, utilization, bound = self.check_schedulability()
|
||||
|
||||
print("Schedulability Analysis:")
|
||||
print("-" * 40)
|
||||
print(f"CPU Utilization: {utilization*100:.2f}%")
|
||||
print(f"Theoretical Bound: {bound*100:.2f}%")
|
||||
|
||||
if schedulable:
|
||||
print("✓ Task set is schedulable")
|
||||
else:
|
||||
print("✗ Task set is NOT schedulable")
|
||||
|
||||
print()
|
||||
print("Response Times:")
|
||||
print("-" * 40)
|
||||
for task in self.tasks:
|
||||
if 'response_time' in task:
|
||||
print(f" {task['name']}: {task['response_time']}ms "
|
||||
f"(deadline: {task['deadline']}ms)")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Task priority calculator')
|
||||
parser.add_argument('--tasks', nargs='+', help='Task definitions: name:period:exec[:deadline]')
|
||||
parser.add_argument('--file', help='Task definition file')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
calculator = TaskPriorityCalculator()
|
||||
|
||||
# Load tasks from file or command line
|
||||
if args.file:
|
||||
with open(args.file, 'r') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#'):
|
||||
parts = line.split(':')
|
||||
if len(parts) >= 3:
|
||||
name = parts[0]
|
||||
period = int(parts[1])
|
||||
exec_time = int(parts[2])
|
||||
deadline = int(parts[3]) if len(parts) > 3 else period
|
||||
calculator.add_task(name, period, exec_time, deadline)
|
||||
elif args.tasks:
|
||||
for task_def in args.tasks:
|
||||
parts = task_def.split(':')
|
||||
if len(parts) >= 3:
|
||||
name = parts[0]
|
||||
period = int(parts[1])
|
||||
exec_time = int(parts[2])
|
||||
deadline = int(parts[3]) if len(parts) > 3 else period
|
||||
calculator.add_task(name, period, exec_time, deadline)
|
||||
else:
|
||||
# Example tasks
|
||||
print("Using example task set:")
|
||||
calculator.add_task("Engine Control", 1, 0.5)
|
||||
calculator.add_task("Brake Control", 5, 1)
|
||||
calculator.add_task("CAN Communication", 10, 2)
|
||||
calculator.add_task("Sensor Reading", 2, 0.3)
|
||||
calculator.add_task("Display Update", 50, 10)
|
||||
|
||||
calculator.generate_report()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,115 @@
|
||||
# GDB initialization script for Automotive RTOS debugging
|
||||
|
||||
# Set target architecture
|
||||
set architecture arm
|
||||
set endian little
|
||||
|
||||
# Configure remote target
|
||||
# target remote localhost:3333
|
||||
# target remote :2331
|
||||
|
||||
# Set program counter
|
||||
set $pc = Reset_Handler
|
||||
|
||||
# Break at main
|
||||
break main
|
||||
continue
|
||||
|
||||
# RTOS-specific commands
|
||||
define rtos_tasks
|
||||
echo === RTOS Tasks ===\n
|
||||
set $task = task_list_head
|
||||
while $task != 0
|
||||
printf "Task: %s\n", $task->name
|
||||
printf " ID: %d\n", $task->task_id
|
||||
printf " Priority: %d\n", $task->priority
|
||||
printf " State: %d\n", $task->state
|
||||
printf " Stack: 0x%08x\n", $task->stack_pointer
|
||||
printf " Stack Base: 0x%08x\n", $task->stack_base
|
||||
printf " Stack Size: %d\n", $task->stack_size
|
||||
printf "\n"
|
||||
set $task = $task->next
|
||||
end
|
||||
end
|
||||
|
||||
define rtos_task_info
|
||||
if $argc != 1
|
||||
echo Usage: rtos_task_info <task_name>\n
|
||||
else
|
||||
set $task = task_list_head
|
||||
while $task != 0
|
||||
if $_streq($task->name, $arg0)
|
||||
printf "Task Information:\n"
|
||||
printf " Name: %s\n", $task->name
|
||||
printf " ID: %d\n", $task->task_id
|
||||
printf " Priority: %d\n", $task->priority
|
||||
printf " State: %d\n", $task->state
|
||||
printf " Stack Pointer: 0x%08x\n", $task->stack_pointer
|
||||
printf " Stack Base: 0x%08x\n", $task->stack_base
|
||||
printf " Stack Size: %d\n", $task->stack_size
|
||||
printf " Stack Used: %d\n", $task->stack_high_water_mark
|
||||
printf " Execution Count: %d\n", $task->statistics.execution_count
|
||||
printf " Last Execution: %d\n", $task->statistics.last_execution_time
|
||||
break
|
||||
end
|
||||
set $task = $task->next
|
||||
end
|
||||
if $task == 0
|
||||
echo Task not found\n
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
define rtos_scheduler_stats
|
||||
echo === Scheduler Statistics ===\n
|
||||
printf "Context Switches: %d\n", scheduler_state.statistics.context_switches
|
||||
printf "Preemptions: %d\n", scheduler_state.statistics.preemptions
|
||||
printf "Max Latency: %d\n", scheduler_state.statistics.max_scheduling_latency
|
||||
printf "Total Idle Time: %d\n", scheduler_state.statistics.total_idle_time
|
||||
end
|
||||
|
||||
define rtos_semaphores
|
||||
echo === Semaphores ===\n
|
||||
# Iterate through semaphore list
|
||||
echo Semaphore information not available in this build\n
|
||||
end
|
||||
|
||||
define rtos_mutexes
|
||||
echo === Mutexes ===\n
|
||||
# Iterate through mutex list
|
||||
echo Mutex information not available in this build\n
|
||||
end
|
||||
|
||||
define rtos_queues
|
||||
echo === Message Queues ===\n
|
||||
# Iterate through queue list
|
||||
echo Queue information not available in this build\n
|
||||
end
|
||||
|
||||
define rtos_stack_check
|
||||
echo === Stack Usage ===\n
|
||||
set $task = task_list_head
|
||||
while $task != 0
|
||||
printf "%s: %d/%d bytes used\n", $task->name, $task->stack_high_water_mark, $task->stack_size
|
||||
set $task = $task->next
|
||||
end
|
||||
end
|
||||
|
||||
# Pretty printing
|
||||
set print pretty on
|
||||
set print array on
|
||||
set print elements 20
|
||||
set print demangle on
|
||||
|
||||
# History
|
||||
set history save on
|
||||
set history size 1000
|
||||
set history filename .gdb_history
|
||||
|
||||
# Convenience
|
||||
set pagination off
|
||||
set confirm off
|
||||
set verbose off
|
||||
|
||||
# Display on stop
|
||||
display/10i $pc
|
||||
Executable
+213
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
@file trace_analyzer.py
|
||||
@brief Trace analyzer for Automotive RTOS debugging
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import argparse
|
||||
import json
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
class TraceAnalyzer:
|
||||
"""Analyze RTOS trace data"""
|
||||
|
||||
def __init__(self, trace_file):
|
||||
self.trace_file = Path(trace_file)
|
||||
self.events = []
|
||||
self.task_timeline = defaultdict(list)
|
||||
self.context_switches = []
|
||||
self.interrupts = []
|
||||
|
||||
def parse_trace_file(self):
|
||||
"""Parse trace file"""
|
||||
print(f"Parsing trace file: {self.trace_file}")
|
||||
|
||||
if not self.trace_file.exists():
|
||||
print(f"Error: Trace file not found")
|
||||
return False
|
||||
|
||||
event_pattern = re.compile(
|
||||
r'\[(\d+\.?\d*)\]\s+(\w+):\s+(.*)'
|
||||
)
|
||||
|
||||
with open(self.trace_file, 'r') as f:
|
||||
for line in f:
|
||||
match = event_pattern.match(line)
|
||||
if match:
|
||||
timestamp = float(match.group(1))
|
||||
event_type = match.group(2)
|
||||
event_data = match.group(3)
|
||||
|
||||
event = {
|
||||
'timestamp': timestamp,
|
||||
'type': event_type,
|
||||
'data': event_data
|
||||
}
|
||||
|
||||
self.events.append(event)
|
||||
|
||||
# Categorize events
|
||||
if event_type == 'TASK_SWITCH':
|
||||
self.context_switches.append(event)
|
||||
elif event_type == 'IRQ_ENTER':
|
||||
self.interrupts.append(event)
|
||||
elif event_type == 'TASK_START':
|
||||
task_name = event_data.split(':')[0].strip()
|
||||
self.task_timeline[task_name].append({
|
||||
'type': 'start',
|
||||
'timestamp': timestamp
|
||||
})
|
||||
elif event_type == 'TASK_END':
|
||||
task_name = event_data.split(':')[0].strip()
|
||||
self.task_timeline[task_name].append({
|
||||
'type': 'end',
|
||||
'timestamp': timestamp
|
||||
})
|
||||
|
||||
print(f"Parsed {len(self.events)} events")
|
||||
return True
|
||||
|
||||
def analyze_context_switches(self):
|
||||
"""Analyze context switches"""
|
||||
print("\nContext Switch Analysis:")
|
||||
print("-" * 60)
|
||||
|
||||
if not self.context_switches:
|
||||
print("No context switches found")
|
||||
return
|
||||
|
||||
# Calculate intervals
|
||||
intervals = []
|
||||
for i in range(1, len(self.context_switches)):
|
||||
interval = (self.context_switches[i]['timestamp'] -
|
||||
self.context_switches[i-1]['timestamp'])
|
||||
intervals.append(interval)
|
||||
|
||||
if intervals:
|
||||
avg_interval = sum(intervals) / len(intervals)
|
||||
min_interval = min(intervals)
|
||||
max_interval = max(intervals)
|
||||
|
||||
print(f"Total context switches: {len(self.context_switches)}")
|
||||
print(f"Average interval: {avg_interval:.3f} ms")
|
||||
print(f"Minimum interval: {min_interval:.3f} ms")
|
||||
print(f"Maximum interval: {max_interval:.3f} ms")
|
||||
|
||||
def analyze_task_execution(self):
|
||||
"""Analyze task execution times"""
|
||||
print("\nTask Execution Analysis:")
|
||||
print("-" * 60)
|
||||
print(f"{'Task Name':25s} {'Executions':>10s} {'Total Time':>10s} {'Avg Time':>10s}")
|
||||
print("-" * 60)
|
||||
|
||||
for task_name, events in self.task_timeline.items():
|
||||
executions = 0
|
||||
total_time = 0
|
||||
|
||||
for i in range(0, len(events) - 1, 2):
|
||||
if i + 1 < len(events):
|
||||
if events[i]['type'] == 'start' and events[i+1]['type'] == 'end':
|
||||
duration = events[i+1]['timestamp'] - events[i]['timestamp']
|
||||
total_time += duration
|
||||
executions += 1
|
||||
|
||||
if executions > 0:
|
||||
avg_time = total_time / executions
|
||||
print(f"{task_name:25s} {executions:10d} {total_time:10.3f} {avg_time:10.3f}")
|
||||
|
||||
def analyze_interrupts(self):
|
||||
"""Analyze interrupt handling"""
|
||||
print("\nInterrupt Analysis:")
|
||||
print("-" * 40)
|
||||
|
||||
if not self.interrupts:
|
||||
print("No interrupts found")
|
||||
return
|
||||
|
||||
irq_count = defaultdict(int)
|
||||
for irq in self.interrupts:
|
||||
irq_name = irq['data'].strip()
|
||||
irq_count[irq_name] += 1
|
||||
|
||||
for irq_name, count in irq_count.items():
|
||||
print(f"{irq_name:20s} {count:5d} occurrences")
|
||||
|
||||
def generate_timeline(self, output_file):
|
||||
"""Generate task timeline visualization"""
|
||||
print(f"\nGenerating timeline: {output_file}")
|
||||
|
||||
with open(output_file, 'w') as f:
|
||||
f.write("RTOS Task Timeline\n")
|
||||
f.write("=" * 80 + "\n\n")
|
||||
|
||||
# Find time range
|
||||
if not self.events:
|
||||
return
|
||||
|
||||
start_time = self.events[0]['timestamp']
|
||||
end_time = self.events[-1]['timestamp']
|
||||
duration = end_time - start_time
|
||||
|
||||
f.write(f"Time range: {start_time:.3f} to {end_time:.3f} ms\n")
|
||||
f.write(f"Duration: {duration:.3f} ms\n\n")
|
||||
|
||||
# Generate ASCII timeline
|
||||
timeline_width = 80
|
||||
time_per_char = duration / timeline_width if duration > 0 else 1
|
||||
|
||||
for task_name, events in self.task_timeline.items():
|
||||
f.write(f"\n{task_name}:\n")
|
||||
|
||||
# Create timeline
|
||||
timeline = [' '] * timeline_width
|
||||
|
||||
for i in range(0, len(events) - 1, 2):
|
||||
if i + 1 < len(events):
|
||||
start_pos = int((events[i]['timestamp'] - start_time) / time_per_char)
|
||||
end_pos = int((events[i+1]['timestamp'] - start_time) / time_per_char)
|
||||
|
||||
for pos in range(start_pos, min(end_pos, timeline_width)):
|
||||
if 0 <= pos < timeline_width:
|
||||
timeline[pos] = '#'
|
||||
|
||||
f.write(''.join(timeline) + '\n')
|
||||
|
||||
f.write("\nLegend: '#' = task running, ' ' = task not running\n")
|
||||
|
||||
def analyze(self):
|
||||
"""Run complete analysis"""
|
||||
print("=" * 60)
|
||||
print("Trace Analyzer")
|
||||
print("=" * 60)
|
||||
|
||||
if not self.parse_trace_file():
|
||||
return
|
||||
|
||||
self.analyze_context_switches()
|
||||
self.analyze_task_execution()
|
||||
self.analyze_interrupts()
|
||||
|
||||
# Generate timeline
|
||||
output_dir = self.trace_file.parent
|
||||
timeline_file = output_dir / 'timeline.txt'
|
||||
self.generate_timeline(timeline_file)
|
||||
|
||||
print(f"\nTimeline saved to: {timeline_file}")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Trace analyzer')
|
||||
parser.add_argument('trace_file', help='Trace file to analyze')
|
||||
parser.add_argument('--output', help='Output directory')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
analyzer = TraceAnalyzer(args.trace_file)
|
||||
analyzer.analyze()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user