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()
|
||||
Reference in New Issue
Block a user