File size: 11,770 Bytes
dd6740a
a656d3e
5dc1809
 
ddbc12e
e8828b9
ef15281
32f9448
d81801d
ef15281
26214df
ef15281
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
adaf99d
 
ef15281
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee1176b
5dc1809
 
 
6da517e
d602a78
 
5dc1809
dc3b12f
 
14395b3
 
 
 
 
 
 
 
 
 
 
dc3b12f
 
 
14395b3
dc3b12f
9204864
 
dc3b12f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5dc1809
e8828b9
 
dc3b12f
 
 
 
 
 
 
 
 
 
 
 
e8828b9
dc3b12f
 
 
e8828b9
 
 
 
 
 
5987304
2050008
e8828b9
 
 
 
2050008
e8828b9
 
 
 
2050008
e8828b9
 
 
 
d602a78
ab13bd6
e8828b9
f2626c4
14cb284
d602a78
 
ab13bd6
 
2050008
 
 
e8828b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2050008
e8828b9
5c41e64
5dc1809
5c41e64
e8828b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5987304
e8828b9
 
 
35fc714
a3d92be
 
 
 
dfba6d6
 
a3d92be
 
54fdf87
d69b2ea
f8c314d
cfc7d2a
 
8cafa32
5c41e64
ef15281
6f42112
5c41e64
 
 
ef15281
cfc7d2a
ef15281
5c41e64
38c3fdc
e8828b9
8cafa32
 
ef15281
e8828b9
 
cfc7d2a
e8828b9
cfc7d2a
 
c817689
 
 
e8828b9
 
d69b2ea
 
cfc7d2a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
import glob
import gradio as gr
import tempfile
import os
import sr100_model_compiler
import html
import pathlib
import spaces

# ---------- Helpers ----------

def _resolve_uploaded_path(uploaded):
    """
    Normalize Gradio File input into a filesystem path.
    Handles: str, dict with {path|name}, file-like objects with .path/.name,
    or a list/tuple of the above.
    """
    if uploaded is None:
        return None
    if isinstance(uploaded, (list, tuple)) and uploaded:
        return _resolve_uploaded_path(uploaded[0])
    if isinstance(uploaded, str):
        return uploaded
    if isinstance(uploaded, dict):
        return uploaded.get("path") or uploaded.get("name")
    for attr in ("path", "name"):
        if hasattr(uploaded, attr):
            return getattr(uploaded, attr)
    return None


@spaces.GPU
def compile_model(model_name, vmem_value, lpmem_value, uploaded_model):
    # Decide the source model path (uploaded has priority)
    uploaded_path = _resolve_uploaded_path(uploaded_model)
    model_path = uploaded_path or model_name

    # Basic validations
    if not model_path or not os.path.exists(model_path):
        return (
            "<div style='color:#d32f2f; font-weight:bold; font-size:1.1em;'>"
            "❌ ERROR: Could not locate the model file you selected or uploaded."
            "</div>"
        )

    if pathlib.Path(model_path).suffix.lower() != ".tflite":
        return (
            "<div style='color:#d32f2f; font-weight:bold; font-size:1.1em;'>"
            "❌ ERROR: Please provide a <code>.tflite</code> model file.</div>"
        )

    # Create a temporary directory
    with tempfile.TemporaryDirectory() as out_dir:
        print(f"Created temporary directory: {out_dir}")

        vmem_size_limit = int(vmem_value * 1000)
        lpmem_size_limit = int(lpmem_value * 1000)
        
        # Run the model fitter with better error handling
        try:
            original_file_name = os.path.basename(model_path)
            root, ext = os.path.splitext(original_file_name)
            safe_root = root.replace('.', '_')  
            model_file_name = f"{safe_root}{ext}"
            temp_model_path = os.path.join(out_dir, model_file_name)
            
            print(f"Copying model to sanitized path: {temp_model_path}")
            with open(model_path, "rb") as src, open(temp_model_path, "wb") as dst:
                dst.write(src.read())
            
            print(f"Starting model optimization for {temp_model_path}")
            print(f"VMEM limit: {vmem_size_limit}, LPMEM limit: {lpmem_size_limit}")
            
            success, results = sr100_model_compiler.sr100_model_optimizer(
                model_file=temp_model_path,
                vmem_size_limit=vmem_size_limit,
                lpmem_size_limit=lpmem_size_limit,
                optimize='Performance'
            )
            
            print(f"Optimization complete. Success: {success}")
            print(f"Results: {results}")
            
            # Check if results is None or missing expected keys
            if not results:
                return (
                    "<div style='color:#d32f2f; font-weight:bold; font-size:1.2em;'>"
                    "❌ ERROR: Optimization returned empty results</div>"
                )
                
        except Exception as e:
            error_message = str(e)
            print(f"Exception during model optimization: {error_message}")
            
            return (
                "<div style='color:#d32f2f; font-weight:bold; font-size:1.2em;'>"
                "❌ ERROR: Model optimization failed</div>"
                "<div style='margin-top:0.5em;color:#000;'>Error details:</div>"
                f"<pre style='white-space:pre-wrap; background:#f6f8fa; padding:8px; border-radius:6px; color:#000;'>{html.escape(error_message)}</pre>"
            )

    output = []
    
    # Check for specific failure cases from results
    if not success:
        print(f"Optimization reported failure. Reason: {results.get('failure_reason', 'Unknown')}")
    
    # Check if NPU cycles is zero (CPU-only model)
    npu_zero = results.get('cycles_npu', 0) == 0
    
    if npu_zero:
        output.append(
            "<div style='color:#e65100; font-weight:bold; font-size:1.2em;'>"
            "⚠️ CPU-ONLY: Model fits in memory but no operators mapped to the NPU</div>"
        )
        output.append(
            "<div style='color:#000; margin-top:0.25em;'>"
            "This typically means the model contains ops not supported by the SR100 NPU. "
            "Please review/convert unsupported ops or choose an NPU-friendly model.</div>"
        )
        output.append("<div style='margin-top:0.5em;color:#000;'>Compiler log:</div>")
        output.append(
            f"<pre style='white-space:pre-wrap; background:#f6f8fa; padding:8px; border-radius:6px; color:#000;'>"
            f"{html.escape(results.get('vela_log', 'No log available'))}</pre>"
        )
    else:
        if success:
            output.append(
                "<div style='color:#007dc3; font-weight:bold; font-size:1.2em;'>"
                "✅ SUCCESS: Model fits on SR100 and below is the estimates Performance</div>"
            )
        else:
            output.append(
                "<div style='color:#d32f2f; font-weight:bold; font-size:1.2em;'>"
                "❌ FAILURE: Model does not fit on SR100, Please check Memory usage of Model</div>"
            )

        # Format metrics in a nice table
        table_rows = []
        
        # Calculate all the metrics
        weights_size = results['weights_size'] / 1000.0
        arena_size = results['arena_cache_size'] / 1000.0
        clock = results['core_clock'] / 1.0e6
        infer_time = results['inference_time'] * 1000.0
        infer_fps = results['inferences_per_sec']
        vmem_size = results['vmem_size'] / 1000.0
        lpmem_size = results['lpmem_size'] / 1000.0
        vmem_size_limit = results['vmem_size_limit'] / 1000.0
        lpmem_size_limit = results['lpmem_size_limit'] / 1000.0
        vmem_perc = results['vmem_size'] * 100.0 / results['vmem_size_limit']
        lpmem_perc = results['lpmem_size'] * 100.0 / results['lpmem_size_limit']
        
        # Add rows to the table
        metrics = [
            ("Clock Frequency", f"{clock:0.1f} MHz"),
            ("Inference Time", f"{infer_time:0.1f} ms"),
            ("Inferences Per Second", f"{infer_fps:0.1f} fps"),
            ("Arena Cache Size", f"{arena_size:0.3f} kB"),
            ("Model Size", f"{weights_size:0.3f} kB"),
            ("Model Location", f"{results['model_loc']}"),
            ("System Configuration", f"{results['system_config']}"),
            ("VMEM Size", f"{vmem_size:0.3f} kB ({vmem_perc:0.1f}% of {vmem_size_limit:0.3f} kB limit)"),
            ("LPMEM Size", f"{lpmem_size:0.3f} kB ({lpmem_perc:0.1f}% of {lpmem_size_limit:0.3f} kB limit)")
        ]
        
        for label, value in metrics:
            table_rows.append(
                "<tr>"
                f"<td style='padding:4px 12px; font-weight:bold; border-bottom:1px solid #eee; color:#000;'>{label}</td>"
                f"<td style='padding:4px 12px; border-bottom:1px solid #eee; color:#000;'>{value}</td>"
                "</tr>"
            )
        
        output.append(
            "<table style='margin-top:1em; border-collapse:collapse; color:#000;'>"
            + "".join(table_rows) + "</table>"
        )

    return "".join(output)

# Get all available models
model_choices = glob.glob('models/*.tflite')

custom_css = """
:root {
    --color-accent: #007dc3;
    --color-primary-500: #007dc3;
    --color-primary-600: #007dc3;
}
body, .gradio-container, #root {
    background: #fff !important;
}
/* Hide Gradio footer and settings */
footer, .gradio-footer, .svelte-1ipelgc, .gradio-logo, .gradio-app__settings {
    display: none !important;
}
/* Style input labels and controls */
.gradio-slider label,
.gradio-radio label,
.gradio-dropdown label,
.gradio-file label {
    color: #007dc3 !important;
    font-weight: bold;
}
.gradio-slider input[type="range"]::-webkit-slider-thumb,
.gradio-slider input[type="range"]::-moz-range-thumb,
.gradio-slider input[type="range"]::-ms-thumb {
    background: #007dc3 !important;
}
.gradio-radio input[type="radio"]:checked + span {
    background: #007dc3 !important;
    border-color: #007dc3 !important;
}
.gradio-dropdown select,
.gradio-file input[type="file"] {
    border-color: #007dc3 !important;
}
.gradio-button {
    background: #007dc3 !important;
    color: #fff !important;
    border: none !important;
}
"""
    
with gr.Blocks(css=custom_css) as demo:
    gr.Markdown("<h1 style='font-size:2.5em; color:#007dc3; margin-bottom:0;'>SR100 Model Compiler</h1>", elem_id="main_title")
    gr.Markdown("<h3 style='margin-top:0; color:#000;'>Bring a TFlite INT8 model and compile it for Synaptics Astra SR100. Learn more at <a href='https://developer.synaptics.com/docs/sr/sr100/quick-start?utm_source=hf' target='_blank' style='color:#007dc3; text-decoration:underline;'>Synaptics AI Developer Zone</a></h3>", elem_id="subtitle")
    gr.Markdown("""
    <p style='margin-top:0; color:#000; font-style:italic;'>
    SR100 includes the following on-chip SRAM memories:<br>
    - 1536 kB of Virtual Memory SRAM (VMEM) for high-speed operations.<br>
    - 1536 kB of Low Power SRAM (LPMEM) for images, audio, and other less-performance-critical data.<br><br>
    The amount of memory allocated to the model is customizable. Any memory not allocated to the model is usable by the application.<br>
    Ensure that the Arena cache size is smaller than the available VMEM to ensure it fits and runs optimally.
    </p>
    """, elem_id="memory_note"
    )

    with gr.Row():
        vmem_slider = gr.Slider(minimum=1, maximum=1536, step=1.024, label="Set total VMEM SRAM size available in kB", value=1536.0)
        lpmem_slider = gr.Slider(minimum=1, maximum=1536, step=1.024, label="Set total LPMEM SRAM size in kB", value=1536.0)
    
    model_dropdown = gr.Dropdown(
        label="Select a model",
        value='models/person_classification_256x448.tflite',
        choices=model_choices
    )
    
    # Add file upload component
    model_upload = gr.File(label="Or upload a .tflite INT8 model. Please note, Uploaded models are stored in a temporary directory and will be deleted automatically after processing.", file_types=[".tflite"], file_count="single")
    
    # Run the compile
    compile_btn = gr.Button("Compile Model")
    compile_text = gr.Markdown("<span style='color:#000;'>Waiting for model results</span>")
    
    # Compute options
    compile_btn.click(compile_model, inputs=[model_dropdown, vmem_slider, lpmem_slider, model_upload], outputs=[compile_text])
    
    gr.HTML("""
    <div style="max-width: 900px; margin: 2rem auto; background: white; color: black; border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); border: 1px solid #e5e7eb; padding: 1.5rem; text-align: center;">
        For a detailed walkthrough, please see our
        <a href="https://developer.synaptics.com/docs/sr/sr100/evaluate-sr?utm_source=hf" target="_blank" style="color: #1a0dab;">Evaluate Model Guide</a>.<br>
        This Space uses a simulation toolchain to estimate model performance providing results that closely reflect real hardware behavior.
        <br><br>
        Request a 
        <a href="https://synacsm.atlassian.net/servicedesk/customer/portal/543/group/597/create/7208?utm_source=hf" target="_blank" style="color: #1a0dab;">Machina Micro [MCU] Dev Kit</a> with Astra SR100 MCU.
    </div>
    """)

if __name__ == "__main__":
    demo.launch()