Skip to content

Commit 17212ed

Browse files
committed
Exporting build_model api, need to change hard paths, and allow for selecting models without export script
1 parent 7e28a04 commit 17212ed

File tree

2 files changed

+176
-0
lines changed

2 files changed

+176
-0
lines changed

extension/embedded/export_add.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import torch
2+
from torch.export import export
3+
from executorch.exir import to_edge
4+
5+
# Start with a PyTorch model that adds two input tensors (matrices)
6+
class Add(torch.nn.Module):
7+
def __init__(self):
8+
super(Add, self).__init__()
9+
10+
def forward(self, x: torch.Tensor, y: torch.Tensor):
11+
return x + y
12+
13+
# 1. torch.export: Defines the program with the ATen operator set.
14+
aten_dialect = export(Add(), (torch.ones(1), torch.ones(1)))
15+
16+
# 2. to_edge: Make optimizations for Edge devices
17+
edge_program = to_edge(aten_dialect)
18+
19+
# 3. to_executorch: Convert the graph to an ExecuTorch program
20+
executorch_program = edge_program.to_executorch()
21+
22+
# 4. Save the compiled .pte program
23+
with open("add.pte", "wb") as file:
24+
file.write(executorch_program.buffer)
25+
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Generates .pte model, operator definitions, and header files
4+
"""
5+
6+
import os
7+
import sys
8+
import subprocess
9+
import argparse
10+
from pathlib import Path
11+
12+
def run_command(cmd, cwd=None, description=""):
13+
"""Run a command and handle errors"""
14+
print(f"Running: {description}")
15+
print(f"Command: {' '.join(cmd)}")
16+
17+
try:
18+
result = subprocess.run(cmd, cwd=cwd, check=True, capture_output=True, text=True)
19+
print(f"✓ {description} completed successfully")
20+
if result.stdout:
21+
print(f"Output: {result.stdout}")
22+
return result
23+
except subprocess.CalledProcessError as e:
24+
print(f"✗ {description} failed")
25+
print(f"Error: {e.stderr}")
26+
sys.exit(1)
27+
28+
def main():
29+
parser = argparse.ArgumentParser(description="Build ExecuTorch ARM Hello World model")
30+
parser.add_argument("--executorch-root", default="~/optional/modules/lib/executorch",
31+
help="Path to ExecuTorch root directory")
32+
parser.add_argument("--model-name", default="add",
33+
help="Name of the model (default: add)")
34+
parser.add_argument("--clean", action="store_true",
35+
help="Clean generated files before building")
36+
37+
args = parser.parse_args()
38+
39+
# Paths
40+
script_dir = Path(__file__).parent
41+
project_root = script_dir.parent.parent.parent.parent.parent.parent # Go up to petriok root
42+
executorch_root = project_root / args.executorch_root
43+
example_files_dir = "/home/zephyruser/zephyr/samples/modules/executorch/arm/hello_world/example_files"
44+
src_dir = script_dir / "src"
45+
46+
model_name = args.model_name
47+
pte_file = f"{model_name}.pte"
48+
ops_def_file = "gen_ops_def.yml"
49+
header_file = "model_pte.h"
50+
51+
print(f"Building ExecuTorch model: {model_name}")
52+
print(f"ExecuTorch root: {executorch_root}")
53+
print(f"Working directory: {script_dir}")
54+
55+
# Clean previous build if requested
56+
if args.clean:
57+
files_to_clean = [pte_file, ops_def_file, src_dir / header_file]
58+
for file_path in files_to_clean:
59+
if Path(file_path).exists():
60+
Path(file_path).unlink()
61+
print(f"Cleaned: {file_path}")
62+
63+
# Step 1: Generate the .pte model file
64+
export_script = os.path.join(example_files_dir, f"export_{model_name}.py")
65+
if not os.path.exists(export_script):
66+
print(f"Error: Export script not found: {export_script}")
67+
sys.exit(1)
68+
69+
try:
70+
run_command(
71+
[sys.executable, str(export_script)],
72+
cwd=script_dir,
73+
description="Generating .pte model file"
74+
)
75+
except SystemExit:
76+
print(f"\n❌ Model generation failed. This is likely because PyTorch/ExecuTorch is not installed.")
77+
print(f"For now, using dummy model_pte.h for compilation testing.")
78+
print(f"To generate a real model, install PyTorch and ExecuTorch:")
79+
print(f" pip install torch")
80+
print(f" # Install ExecuTorch according to documentation")
81+
print(f" python build_model.py")
82+
return
83+
84+
if not Path(script_dir / pte_file).exists():
85+
print(f"Error: Model file {pte_file} was not generated")
86+
sys.exit(1)
87+
88+
# Step 2: Generate operator definitions
89+
90+
gen_ops_script = "/home/zephyruser/optional/modules/lib/executorch/codegen/tools/gen_ops_def.py"
91+
if not os.path.exists(gen_ops_script):
92+
print(f"Error: gen_ops_def.py not found at {gen_ops_script}")
93+
sys.exit(1)
94+
95+
run_command(
96+
[sys.executable, str(gen_ops_script),
97+
"--output_path", ops_def_file,
98+
"--model_file_path", pte_file],
99+
cwd=script_dir,
100+
description="Generating operator definitions"
101+
)
102+
103+
# Step 3: Convert .pte to header file
104+
#pte_to_header_script = executorch_root / "examples" / "arm" / "executor_runner" / "pte_to_header.py"
105+
pte_to_header_script = "/home/zephyruser/optional/modules/lib/executorch/examples/arm/executor_runner/pte_to_header.py"
106+
if not os.path.exists(pte_to_header_script):
107+
print(f"Error: pte_to_header.py not found at {pte_to_header_script}")
108+
sys.exit(1)
109+
110+
run_command(
111+
[sys.executable, str(pte_to_header_script),
112+
"--pte", pte_file,
113+
"--outdir", "src"],
114+
cwd=script_dir,
115+
description="Converting .pte to header file"
116+
)
117+
118+
# Step 4: Make the generated array const and remove section attribute
119+
header_path = src_dir / header_file
120+
if header_path.exists():
121+
content = header_path.read_text()
122+
123+
# Remove section attribute and replace with Zephyr alignment macro
124+
import re
125+
# Replace section+aligned pattern with Zephyr __ALIGN macro
126+
content = re.sub(r'__attribute__\s*\(\s*\(\s*section\s*\([^)]*\)\s*,\s*aligned\s*\(([^)]*)\)\s*\)\s*\)\s*', r'__ALIGN(\1) ', content)
127+
# Remove any remaining section-only attributes
128+
content = re.sub(r'__attribute__\s*\(\s*\(\s*section\s*\([^)]*\)\s*\)\s*\)\s*', '', content)
129+
# Also replace any standalone __attribute__((aligned(n))) with __ALIGN(n)
130+
content = re.sub(r'__attribute__\s*\(\s*\(\s*aligned\s*\(([^)]*)\)\s*\)\s*\)\s*', r'__ALIGN(\1) ', content)
131+
132+
# Replace 'char model_pte_data[]' with 'const char model_pte_data[]'
133+
content = content.replace('char model_pte_data[]', 'const char model_pte_data[]')
134+
# Also handle 'char model_pte[]' variant
135+
content = content.replace('char model_pte[]', 'const char model_pte[]')
136+
137+
header_path.write_text(content)
138+
print(f"✓ Made model data const and removed section attributes in {header_file}")
139+
else:
140+
print(f"Warning: Header file {header_file} not found")
141+
142+
print("\n=== Build Summary ===")
143+
print(f"✓ Generated: {pte_file}")
144+
print(f"✓ Generated: {ops_def_file}")
145+
print(f"✓ Generated: src/{header_file}")
146+
print("\nNext steps:")
147+
print("1. Review gen_ops_def.yml and customize if needed")
148+
print("2. Build the Zephyr application with west build")
149+
150+
if __name__ == "__main__":
151+
main()

0 commit comments

Comments
 (0)