The design will depend a lot on your target, a DSP with a single cycle MAC and barrel shifters will result in a very different design to a generic CPU with a deep pipeline, again a different design to an ASIC or FPGA. But the basic steps are:- Simulate the entire system with floating point numbers until it works.
- Determine the largest values possible in each step of the calculations.
- Select scalings to avoid overflow or wraparound.
- Simulate again with the fixed point variables at each step
- Return to step 3 and increase precision if the solution wont stabilise or converge.
- Reimplement on the target and test, return to step 1.
Reducing computational cost in step 3 is the time consuming part and needs a good understanding of the architecture of the target, for your first designs its a lot simpler if you use just a single format for the entire path, then go back and optimise out the parts that are too wide.
^^^ This is the a very well through out reply.
In step 2 (work out the scaling factors) don't just simulate or test, but always look for the worst possible case. Just because a DSP filter has a nominal gain of 1.0 doesn't mean that the output will have the same range as the input - most filters have a small amount of ripple/overshoot/ringing that will need additional range (or slightly less gain) to ensure it doesn't fail due to overflow.
For unsigned binary, here are some rules of thumb.
For addition of two n.m-bit numbers, you need (n+1).m bits of precision on the output
For multiplication of two n.m-bit numbers you need 2n.2m bits in the results to not lose precision in the result.
For division by something that changes dynamically all bets are off : e.g. 1/65535 vs 65535/1
For division by a constant you can work out a sensible amount of precision in the result, but it will always need more digits to the right of the decimal than you would like - unless it is division by a power of 2 (e.g. 0.1 as a binary decimal is 0.0001100110011001...). So avoid division if at all possible, unless it is by a constant power of two (where you can just move the decimal point). For example it might be better to replace n/3 with (n/2 + n/8 + n/32) which totals to n/0.65625.
For signed numbers these changes and has a few more corner cases as the ranges are asymmetric - there is usually one more valid negative value than positive ones. For example for multiplication of n-bit signed numbers, the result of multiplication doesn't fit in an n-bit signed number (e.g. (-1) * (-128) gives 128, which isn't a valid 8-bit signed number)