$npx -y skills add matlab/matlab-agentic-toolkit --skill matlab-review-fi-codeReviews MATLAB fixed-point (fi) code for performance, code generation efficiency, and correctness. Identifies antipatterns and suggests idiomatic improvements. Use when reviewing fi, fimath, numerictype, or quantizenumeric code.
| 1 | # fi Best Practices Review |
| 2 | |
| 3 | Reviews MATLAB code for fixed-point (`fi`) best practices and suggests improvements for performance, code generation efficiency, and correctness. |
| 4 | |
| 5 | ## When to Use |
| 6 | |
| 7 | - Reviewing MATLAB code that uses `fi`, `fimath`, `numerictype`, or `quantizenumeric` |
| 8 | - Optimizing fixed-point simulation speed |
| 9 | - Preparing fixed-point code for C or hardware code generation |
| 10 | |
| 11 | ## When Not to Use |
| 12 | |
| 13 | - Code using only built-in integer types (`int8`, `uint16`, etc.) without `fi` |
| 14 | - Pure floating-point algorithms with no fixed-point intent |
| 15 | - Simulink-only workflows where fixed-point is configured through block dialogs (use Fixed-Point Tool instead) |
| 16 | |
| 17 | ## Checklist |
| 18 | |
| 19 | When reviewing code, check for ALL of the following: |
| 20 | |
| 21 | ### 1. Vectorize fi() Calls |
| 22 | |
| 23 | **Problem**: Scalar `fi()` in a loop is slow due to per-element object construction overhead. |
| 24 | |
| 25 | **Fix**: Pass entire arrays to `fi()` at once. |
| 26 | |
| 27 | ```matlab |
| 28 | % BAD — slow: per-element fi object construction |
| 29 | for k = 1:N |
| 30 | x_fi(k) = fi(x(k), 1, 18, 16, F); |
| 31 | end |
| 32 | |
| 33 | % GOOD — fast: single vectorized call, bit-true identical result |
| 34 | x_fi = fi(x, 1, 18, 16, F); |
| 35 | ``` |
| 36 | |
| 37 | ### 2. Separate Data Types from Algorithm |
| 38 | |
| 39 | **Problem**: Hardcoding fi types inside algorithm code makes it impossible to switch between float/fixed or compare configurations. |
| 40 | |
| 41 | **Fix**: Use a types table with empty prototypes and `cast(...,'like',...)`. |
| 42 | |
| 43 | ```matlab |
| 44 | % Types table (separate function) |
| 45 | function T = mytypes(dt) |
| 46 | switch dt |
| 47 | case 'double' |
| 48 | T.b = double([]); T.x = double([]); T.y = double([]); |
| 49 | case 'single' |
| 50 | T.b = single([]); T.x = single([]); T.y = single([]); |
| 51 | case 'fixed16' |
| 52 | F = fimath('RoundingMethod','Floor','OverflowAction','Wrap', ... |
| 53 | 'ProductMode','KeepLSB','ProductWordLength',32, ... |
| 54 | 'SumMode','KeepLSB','SumWordLength',32); |
| 55 | T.b = fi([], 1, 16, 15, F); |
| 56 | T.x = fi([], 1, 16, 15, F); |
| 57 | T.y = fi([], 1, 16, 14, F); |
| 58 | end |
| 59 | end |
| 60 | |
| 61 | % Algorithm — no hardcoded types |
| 62 | function [y,z] = myfilter(b, x, z, T) |
| 63 | y = zeros(size(x), 'like', T.y); |
| 64 | for n = 1:length(x) |
| 65 | z(:) = [x(n); z(1:end-1)]; |
| 66 | y(n) = b * z; |
| 67 | end |
| 68 | end |
| 69 | |
| 70 | % Entrypoint — wraps types + cast + algorithm |
| 71 | function [y,z] = entrypoint(dt, b, x) |
| 72 | T = mytypes(dt); |
| 73 | b = cast(b, 'like', T.b); |
| 74 | x = cast(x, 'like', T.x); |
| 75 | z = zeros(size(b'), 'like', T.x); |
| 76 | [y,z] = myfilter(b, x, z, T); |
| 77 | end |
| 78 | ``` |
| 79 | |
| 80 | **Validation**: Run with `'double'` first, then `'single'` (catches single-precision issues early — important for embedded targets where double is unavailable or slow), then `'fixed16'`. |
| 81 | |
| 82 | ### 3. Prevent Bit Growth with Subscripted Assignment |
| 83 | |
| 84 | **Problem**: `acc = acc + x(n)` overwrites `acc` with a new fi object whose type may change due to FullPrecision word growth. |
| 85 | |
| 86 | **Fix**: Use `acc(:) = acc + x(n)` to retain the original data type. |
| 87 | |
| 88 | ```matlab |
| 89 | % BAD — acc type may grow each iteration |
| 90 | acc = fi(0, 1, 32, 16); |
| 91 | for n = 1:numel(x) |
| 92 | acc = acc + x(n); |
| 93 | end |
| 94 | |
| 95 | % GOOD — preserves acc's declared type |
| 96 | acc = fi(0, 1, 32, 16); |
| 97 | for n = 1:numel(x) |
| 98 | acc(:) = acc + x(n); |
| 99 | end |
| 100 | ``` |
| 101 | |
| 102 | ### 4. Configure fimath for Your Target |
| 103 | |
| 104 | **Problem**: Default fimath (Nearest rounding, Saturate overflow, FullPrecision) generates bloated code. A simple `a + b` can produce many lines of C with sign-extension and overflow checks. |
| 105 | |
| 106 | **Fix**: Choose fimath settings based on your code generation target. |
| 107 | |
| 108 | ```matlab |
| 109 | % For C targets (MATLAB Coder) — models integer truncation behavior |
| 110 | F_c = fimath('RoundingMethod','Floor', 'OverflowAction','Wrap', ... |
| 111 | 'ProductMode','KeepLSB', 'ProductWordLength',32, ... |
| 112 | 'SumMode','KeepLSB', 'SumWordLength',32); |
| 113 | |
| 114 | % For DSP processor targets — models shift-right behavior |
| 115 | F_dsp = fimath('RoundingMethod','Floor', 'OverflowAction','Wrap', ... |
| 116 | 'ProductMode','KeepMSB', 'ProductWordLength',32, ... |
| 117 | 'SumMode','KeepMSB', 'SumWordLength',32); |
| 118 | |
| 119 | % For FPGA/hardware targets — use the built-in helper |
| 120 | % hdlfimath = Floor/Wrap/FullPrecision (hardware coder manages bit widths internally) |
| 121 | F_hw = hdlfimath; |
| 122 | x_fi = fi(x, 1, 18, 16, F_hw); |
| 123 | ``` |
| 124 | |
| 125 | **Product/Sum mode selection**: |
| 126 | |
| 127 | | Mode | Behavior | Use when | |
| 128 | |------|----------|----------| |
| 129 | | `KeepLSB` | Keep least significant bits (C integer truncation) | Targeting C/C++ (MATLAB Coder) | |
| 130 | | `KeepMSB` | Keep most significant bits (shift-right) | Targeting DSP processors | |
| 131 | | `FullPrecision` | Retain all bits (word growth) | Hardware coder (manages widths internally), or debugging | |
| 132 | | `SpecifyPrecision` | Manual word/fraction lengths | Custom precision requirements | |
| 133 | |
| 134 | **Note**: `hdlfimath` returns Floor/Wrap/FullPrecision. The hardware coder manages bit widths through its own pip |