crosspad-mcp-server 8.1.2 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +13 -0
- package/.claude-plugin/plugin.json +14 -0
- package/.mcp.json +9 -0
- package/README.md +95 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +369 -49
- package/dist/index.js.map +1 -1
- package/dist/tools/idf-flash.js +2 -2
- package/dist/tools/idf-flash.js.map +1 -1
- package/dist/tools/idf-monitor.d.ts +3 -1
- package/dist/tools/idf-monitor.js +19 -3
- package/dist/tools/idf-monitor.js.map +1 -1
- package/dist/tools/midi.js +20 -16
- package/dist/tools/midi.js.map +1 -1
- package/dist/tools/symbols.d.ts +3 -1
- package/dist/tools/symbols.js +31 -1
- package/dist/tools/symbols.js.map +1 -1
- package/dist/tools/trace-buffer.d.ts +40 -0
- package/dist/tools/trace-buffer.js +74 -0
- package/dist/tools/trace-buffer.js.map +1 -0
- package/dist/tools/trace-device.d.ts +10 -0
- package/dist/tools/trace-device.js +26 -0
- package/dist/tools/trace-device.js.map +1 -0
- package/dist/tools/trace-doctor.d.ts +43 -0
- package/dist/tools/trace-doctor.js +150 -0
- package/dist/tools/trace-doctor.js.map +1 -0
- package/dist/tools/trace-export.d.ts +4 -0
- package/dist/tools/trace-export.js +14 -0
- package/dist/tools/trace-export.js.map +1 -0
- package/dist/tools/trace-session.d.ts +118 -0
- package/dist/tools/trace-session.js +346 -0
- package/dist/tools/trace-session.js.map +1 -0
- package/dist/tools/trace-symbols.d.ts +24 -0
- package/dist/tools/trace-symbols.js +44 -0
- package/dist/tools/trace-symbols.js.map +1 -0
- package/dist/tools/trace-webui.d.ts +53 -0
- package/dist/tools/trace-webui.js +222 -0
- package/dist/tools/trace-webui.js.map +1 -0
- package/dist/utils/device.d.ts +5 -0
- package/dist/utils/device.js +43 -15
- package/dist/utils/device.js.map +1 -1
- package/dist/utils/exec.js +26 -0
- package/dist/utils/exec.js.map +1 -1
- package/dist/utils/userConfig.d.ts +13 -0
- package/dist/utils/userConfig.js +43 -0
- package/dist/utils/userConfig.js.map +1 -0
- package/package.json +12 -4
- package/skills/crosspad/SKILL.md +58 -0
- package/skills/crosspad/reference/faq.md +40 -0
- package/skills/crosspad/reference/install.md +84 -0
- package/skills/crosspad/reference/repos.md +29 -0
- package/skills/crosspad/reference/role-contributor.md +64 -0
- package/skills/crosspad/reference/role-fw-dev.md +44 -0
- package/skills/crosspad/reference/role-user.md +49 -0
- package/skills/crosspad/reference/tools.md +68 -0
- package/skills/crosspad/scripts/doctor.sh +65 -0
- package/skills/crosspad/scripts/setup.sh +53 -0
- package/skills/swd-tracer/SKILL.md +135 -0
- package/skills/swd-tracer/reference/signals.md +42 -0
- package/skills/swd-tracer/scripts/detect-env.sh +61 -0
- package/skills/swd-tracer/scripts/install-udev-rules.sh +25 -0
- package/skills/swd-tracer/scripts/setup-venv.sh +26 -0
- package/tracer/PROTOCOL.md +260 -0
- package/tracer/README.md +327 -0
- package/tracer/swd_tracer.py +1066 -0
- package/tracer/ui/index.html +834 -0
package/tracer/README.md
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
# CrossPad SWD Tracer Daemon
|
|
2
|
+
|
|
3
|
+
A single-file Python daemon (`swd_tracer.py`) that communicates with the CrossPad STM32 target over SWD using pyOCD. The Node MCP server drives this daemon as a subprocess.
|
|
4
|
+
|
|
5
|
+
## Output contract
|
|
6
|
+
|
|
7
|
+
- **stdout**: machine-readable JSON/NDJSON only — one JSON object per line.
|
|
8
|
+
- **stderr**: human-readable logs, progress, and error messages only.
|
|
9
|
+
|
|
10
|
+
Never mix JSON output with log lines on stdout. The Node bridge scans stdout for JSON lines.
|
|
11
|
+
|
|
12
|
+
## Dependencies
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
pip install pyocd pyelftools
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Recommended: use a dedicated venv
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
python3 -m venv ~/.local/share/crosspad-mcp/venv
|
|
22
|
+
~/.local/share/crosspad-mcp/venv/bin/pip install pyocd pyelftools
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then set the `pyocd_python` key in `~/.config/crosspad-mcp/config.json` to point at the venv interpreter:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"pyocd_python": "/home/<you>/.local/share/crosspad-mcp/venv/bin/python"
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This prevents conflicts with system or project Python environments. The MCP server also honours the `CROSSPAD_TRACE_PYTHON` environment variable as an override.
|
|
34
|
+
|
|
35
|
+
## Subcommands
|
|
36
|
+
|
|
37
|
+
### `symbols` — resolve firmware variables from DWARF
|
|
38
|
+
|
|
39
|
+
Reads an ELF built with debug info (`-g`) and emits a JSON object listing every fixed-address variable (globals and `static` locals) found in the DWARF info.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
python swd_tracer.py symbols --elf <path/to/firmware.elf> [--query <substring>]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Options:
|
|
46
|
+
|
|
47
|
+
| Flag | Description |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `--elf PATH` | Path to the Debug ELF (required). Must contain DWARF info. |
|
|
50
|
+
| `--query STR` | Case-insensitive substring filter on the symbol name (optional). |
|
|
51
|
+
|
|
52
|
+
Output (stdout):
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{"symbols": [
|
|
56
|
+
{"name": "s_vbat_mv", "address": 536885450, "encoding": "uint", "size": 2},
|
|
57
|
+
{"name": "s_vbus_stm_mv","address": 536885448, "encoding": "uint", "size": 2}
|
|
58
|
+
]}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Fields:
|
|
62
|
+
|
|
63
|
+
| Field | Type | Description |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `name` | string | Symbol name as it appears in DWARF. |
|
|
66
|
+
| `address` | number | Absolute RAM address (decimal integer). |
|
|
67
|
+
| `encoding` | string | Base-type encoding: `uint`, `int`, `uchar`, `char`, `bool`, `float`, `address`. |
|
|
68
|
+
| `size` | number | Byte size of the variable (or total array size for arrays, element size for indexed access). |
|
|
69
|
+
|
|
70
|
+
Results are de-duplicated and sorted by address.
|
|
71
|
+
|
|
72
|
+
Example — query `s_vbat_mv`:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
/path/to/venv/bin/python swd_tracer.py symbols \
|
|
76
|
+
--elf ~/GIT/CrossPad_STM32_r20/build/Debug/CrossPad_STM32_r20.elf \
|
|
77
|
+
--query s_vbat_mv
|
|
78
|
+
# stdout: {"symbols": [{"name": "s_vbat_mv", "address": 536885450, "encoding": "uint", "size": 2}]}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### `trace` — live SWD poll loop (non-halting)
|
|
84
|
+
|
|
85
|
+
Connects to the target via pyOCD, polls the requested signals by reading RAM while the core continues to run, and emits one NDJSON frame per sample on stdout. Optionally writes a `.cptrace` file. Stops when `{"cmd":"stop"}` is received on stdin.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
python swd_tracer.py trace \
|
|
89
|
+
--elf <firmware.elf> \
|
|
90
|
+
--signals <sig1,sig2,...> \
|
|
91
|
+
[--rate <Hz>] \
|
|
92
|
+
[--out <file.cptrace>] \
|
|
93
|
+
[--probe <unique_id>]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### Options
|
|
97
|
+
|
|
98
|
+
| Flag | Default | Description |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `--elf PATH` | (required) | Debug ELF for DWARF symbol resolution. |
|
|
101
|
+
| `--signals LIST` | (required) | Comma-separated signal names (see below). |
|
|
102
|
+
| `--rate HZ` | `0` (max) | Target poll rate in Hz. `0` means poll as fast as possible. |
|
|
103
|
+
| `--out PATH` | `None` | If given, also append samples to a `.cptrace` binary file. |
|
|
104
|
+
| `--probe UID` | `None` | ST-Link unique ID string; omit to auto-select the first probe. |
|
|
105
|
+
|
|
106
|
+
#### Signal name syntax — `name[index]`
|
|
107
|
+
|
|
108
|
+
Signal names may include an array index: `s_inputs[0]`, `s_adc_raw[3]`.
|
|
109
|
+
|
|
110
|
+
Resolution rules:
|
|
111
|
+
1. Parse `base` and optional `[index]` from the spec.
|
|
112
|
+
2. Look up `base` in the DWARF symbol table (exact match).
|
|
113
|
+
3. `address = base.address + index * base.size` (element size from DWARF).
|
|
114
|
+
4. A plain `name` (no brackets) resolves directly.
|
|
115
|
+
5. Unknown `base` → an `error` frame is emitted and the command exits.
|
|
116
|
+
|
|
117
|
+
Example: if `s_inputs` is at address `0x20001000` with element size 1, then `s_inputs[3]` resolves to address `0x20001003`.
|
|
118
|
+
|
|
119
|
+
#### NDJSON output frames (stdout)
|
|
120
|
+
|
|
121
|
+
One JSON object per line. Three frame types:
|
|
122
|
+
|
|
123
|
+
**`sample`** — one reading of all polled signals:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{"type": "sample", "t": 0.012345, "values": {"s_vbat_mv": 3742, "s_inputs[0]": 255}}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
| Field | Description |
|
|
130
|
+
|---|---|
|
|
131
|
+
| `type` | `"sample"` |
|
|
132
|
+
| `t` | Seconds since trace start (float, 6 decimal places). |
|
|
133
|
+
| `values` | Object mapping each signal spec to its decoded integer or float value. |
|
|
134
|
+
|
|
135
|
+
**`status`** — device or session state change:
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{"type": "status", "device_state": "stop_suspected", "t": 1.234}
|
|
139
|
+
{"type": "status", "device_state": "stopped", "samples": 1234}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
| `device_state` | Meaning |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `stop_suspected` | A memory read faulted — the STM32 is probably in STOP mode. Polling pauses 200 ms and resumes. No halt is issued. |
|
|
145
|
+
| `stopped` | Normal exit after receiving `{"cmd":"stop"}`. `samples` field holds total sample count. |
|
|
146
|
+
|
|
147
|
+
**`error`** — fatal signal resolution failure:
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{"type": "error", "error": "unknown symbols: bad_name,also_bad"}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Emitted and the process exits if any requested signal cannot be found in the DWARF symbol table.
|
|
154
|
+
|
|
155
|
+
#### Stopping the daemon
|
|
156
|
+
|
|
157
|
+
Write `{"cmd":"stop"}` followed by a newline to the daemon's stdin:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
echo '{"cmd":"stop"}' | <daemon process stdin>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The daemon drains the current poll iteration, closes the `.cptrace` file if open, disconnects from the probe, and emits a final `status/stopped` frame before exiting.
|
|
164
|
+
|
|
165
|
+
#### `.cptrace` file format
|
|
166
|
+
|
|
167
|
+
Binary-framed header followed by line-delimited JSON body rows.
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
Offset Size Content
|
|
171
|
+
0 4 Magic: ASCII "CPTR"
|
|
172
|
+
4 4 Header length N (little-endian uint32)
|
|
173
|
+
8 N JSON signal list: {"signals":[{"name":…,"encoding":…,"size":…},…]}
|
|
174
|
+
8+N … Body rows, one per sample, each: JSON{"t":…,"v":{…}}\n
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The binary header allows fast seeking to the body start and easy validation. Body rows are line-JSON for simplicity and recoverability (a partial write is still parseable up to the last complete line).
|
|
178
|
+
|
|
179
|
+
#### Example
|
|
180
|
+
|
|
181
|
+
The daemon reads control commands (`add` / `remove` / `stop`) as NDJSON on its
|
|
182
|
+
**stdin**, so drive it through a FIFO you keep open for writing:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
mkfifo /tmp/trace_in
|
|
186
|
+
/path/to/venv/bin/python swd_tracer.py trace \
|
|
187
|
+
--elf ~/GIT/CrossPad_STM32_r20/build/Debug/CrossPad_STM32_r20.elf \
|
|
188
|
+
--signals s_vbat_mv,s_inputs[0],s_inputs[1] \
|
|
189
|
+
--rate 100 \
|
|
190
|
+
--out /tmp/session.cptrace \
|
|
191
|
+
< /tmp/trace_in 2>/tmp/trace.log &
|
|
192
|
+
DAEMON_PID=$!
|
|
193
|
+
exec 3>/tmp/trace_in # hold the write end open
|
|
194
|
+
|
|
195
|
+
# ... let it run for a few seconds ...
|
|
196
|
+
|
|
197
|
+
echo '{"cmd":"stop"}' >&3 # graceful stop via the stdin protocol
|
|
198
|
+
exec 3>&-; rm -f /tmp/trace_in # (or just: kill "$DAEMON_PID")
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
stdout while running:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
{"type": "sample", "t": 0.000012, "values": {"s_vbat_mv": 3742, "s_inputs[0]": 255, "s_inputs[1]": 0}}
|
|
205
|
+
{"type": "sample", "t": 0.010034, "values": {"s_vbat_mv": 3741, "s_inputs[0]": 255, "s_inputs[1]": 0}}
|
|
206
|
+
...
|
|
207
|
+
{"type": "status", "device_state": "stopped", "samples": 200}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
### `device-state` — deep low-power / STOP register dump (non-halting)
|
|
213
|
+
|
|
214
|
+
Reads a fixed set of STM32G0 / Cortex-M debug-bus registers and decodes key low-power bits to characterise whether the core is in run/sleep or STOP without auto-waking it (no halt, no DBGMCU debug-in-stop bits required).
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
python swd_tracer.py device-state [--probe UID] [--target cortex_m]
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Options:
|
|
221
|
+
|
|
222
|
+
| Flag | Default | Description |
|
|
223
|
+
|---|---|---|
|
|
224
|
+
| `--probe UID` | `None` | ST-Link unique ID string; omit to auto-select the first probe. |
|
|
225
|
+
| `--target` | `cortex_m` | pyOCD target override. `cortex_m` (generic) is sufficient — no CMSIS pack needed for plain register reads. |
|
|
226
|
+
|
|
227
|
+
Registers read (`accessible` is `false` if any read faults — which itself indicates the core is in deep STOP):
|
|
228
|
+
|
|
229
|
+
| Register | Address | Description |
|
|
230
|
+
|---|---|---|
|
|
231
|
+
| `PWR_CR1` | `0x40007000` | Power control 1 — `LPMS[2:0]` low-power mode select |
|
|
232
|
+
| `PWR_SR1` | `0x40007010` | Power status 1 |
|
|
233
|
+
| `RCC_CR` | `0x40021000` | RCC clock control — HSI/PLL on bits |
|
|
234
|
+
| `RCC_CFGR` | `0x40021008` | RCC clock configuration |
|
|
235
|
+
| `SCB_SCR` | `0xE000ED10` | System Control Register — `SLEEPDEEP` bit (bit 2) |
|
|
236
|
+
| `DBGMCU_CR` | `0x40015804` | Debug MCU configuration |
|
|
237
|
+
|
|
238
|
+
Decoded fields:
|
|
239
|
+
|
|
240
|
+
| Field | Type | Description |
|
|
241
|
+
|---|---|---|
|
|
242
|
+
| `SLEEPDEEP` | bool | `true` if `SCB_SCR[2]` is set — the next WFI/WFE will enter a deep sleep / STOP mode. |
|
|
243
|
+
| `LPMS` | int (0-7) | Low-power mode select from `PWR_CR1[2:0]`. |
|
|
244
|
+
| `interpretation` | string | `"run/sleep"` when `SLEEPDEEP=false`; `"STOP/low-power likely"` when `true`. |
|
|
245
|
+
|
|
246
|
+
Output example (device running normally):
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{"type": "device_state", "regs": {"PWR_CR1": 776, "PWR_SR1": 0, "RCC_CR": 62915840, "RCC_CFGR": 18, "SCB_SCR": 0, "DBGMCU_CR": 0}, "decoded": {"SLEEPDEEP": false, "LPMS": 0, "interpretation": "run/sleep"}, "accessible": true}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The MCP tool exposes this as `crosspad_trace` with `action="device_state"` — it calls this subcommand and returns the `regs` and `decoded` objects in the response.
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
### EXPERIMENTAL: SWO / ITM channel decode
|
|
257
|
+
|
|
258
|
+
> **Status: EXPERIMENTAL — opt-in only, UNTESTED against a real ITM source.**
|
|
259
|
+
> The current CrossPad firmware does NOT emit ITM data on the SWO pin.
|
|
260
|
+
> This feature exists for future use when firmware-side ITM instrumentation is added.
|
|
261
|
+
|
|
262
|
+
#### What it does
|
|
263
|
+
|
|
264
|
+
When `--swo` is passed, the daemon additionally starts a pyOCD `SWVReader` background thread that reads raw SWO bytes from the probe, passes them through the `SWOParser`, and captures the decoded ITM stimulus-port values in a per-port accumulator. On each poll cycle the latest captured value for each mapped port is merged into the `values` object of the outgoing sample frame alongside the normal RAM-polled signals.
|
|
265
|
+
|
|
266
|
+
#### Syntax
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
--swo PORT:NAME[,PORT:NAME,...]
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Each token maps an ITM stimulus port number to a signal name that will appear in the `values` field of each `sample` frame.
|
|
273
|
+
|
|
274
|
+
Examples:
|
|
275
|
+
```bash
|
|
276
|
+
# Map ITM port 0 → "dbg_phase", port 1 → "isr_us"
|
|
277
|
+
python swd_tracer.py trace \
|
|
278
|
+
--elf firmware.elf \
|
|
279
|
+
--signals s_vbat_mv \
|
|
280
|
+
--swo 0:dbg_phase,1:isr_us \
|
|
281
|
+
--rate 100
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The MCP tool exposes this as:
|
|
285
|
+
```json
|
|
286
|
+
{ "action": "start", "signals": ["s_vbat_mv"], "swo": ["0:dbg_phase", "1:isr_us"] }
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
#### Additional flags (SWO path only)
|
|
290
|
+
|
|
291
|
+
| Flag | Default | Description |
|
|
292
|
+
|---|---|---|
|
|
293
|
+
| `--cpu-hz` | `64000000` | Core clock frequency in Hz for SWO baud derivation. **Must match the actual firmware clock.** CrossPad r20 runs at 64 MHz; confirm in the `.ioc` / CubeMX clock config. |
|
|
294
|
+
| `--swo-hz` | `2000000` | Desired SWO output baud in Hz. **Must match `TPIU_ACPR` configured in the firmware.** |
|
|
295
|
+
|
|
296
|
+
These are kept at daemon-level defaults and are not currently plumbed through the MCP `swo[]` input (they can be added when there is a real firmware source to test against).
|
|
297
|
+
|
|
298
|
+
#### Fail-soft behaviour
|
|
299
|
+
|
|
300
|
+
If SWV initialisation fails (e.g. the probe does not support SWO, the target lacks ITM/TPIU, or the SWO clock cannot be set), the daemon logs a message to stderr and continues with plain RAM polling — it never crashes. ITM ports that receive no data simply produce no entry in `values` for that cycle.
|
|
301
|
+
|
|
302
|
+
#### pyOCD 0.44 API note
|
|
303
|
+
|
|
304
|
+
`SWVReader.init(sys_clock, swo_clock, console:TextIO) -> bool` — the console parameter is accepted for text output but the ITM sink is injected by re-connecting the internal `SWOParser` to our `_ITMValueSink` after `init()` returns.
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
#### pyOCD target name note
|
|
309
|
+
|
|
310
|
+
The daemon defaults to `--target cortex_m`, the generic Cortex-M target built into
|
|
311
|
+
pyOCD. It needs **no CMSIS pack** and is sufficient for everything this tool does
|
|
312
|
+
(non-halting RAM polling + the absolute-address register reads in `device-state`).
|
|
313
|
+
This default is verified working on an ST-Link V2 + STM32G0Bx.
|
|
314
|
+
|
|
315
|
+
A part-specific target is **optional** — only needed if you want pyOCD's
|
|
316
|
+
part-aware features (flash programming, named peripherals, ITM/TPIU descriptors
|
|
317
|
+
for SWO). To use one, install the Keil DFP pack and pass the part name:
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
pyocd pack install Keil.STM32G0xx_DFP
|
|
321
|
+
pyocd pack find g0b1 # list available part names
|
|
322
|
+
# then, e.g.:
|
|
323
|
+
swd_tracer.py trace ... --target stm32g0b1retx
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
The RETx variant matches CrossPad r20; substitute the CB/CC/CE part if your flash
|
|
327
|
+
size differs.
|