comp.lang.ada
 help / color / mirror / Atom feed
From: Ken Garlington <garlingtonke@lmtas.lmco.com>
To: 72325.1327@compuserve.com
Cc: garlingtonke
Subject: ESP code rewritten in Ada
Date: 1996/11/05
Date: 1996-11-05T00:00:00+00:00	[thread overview]
Message-ID: <327F5B2B.47E3@lmtas.lmco.com> (raw)


[comp.lang.ada folks: My news server is down, but hopefully you will get
this...]

Jack W. Crenshaw has a "Programmer's Toolbox" column in Embedded Systems
Programming magazine. I like the column, but it suffers from having all
code examples written in C. For fun, I've decided to start coding some
of his examples in Ada. I found this month's example to be a good
example of what Ada can do for reliability; see if you agree.

First, here's the C code example for a simple filter:

/* General-purpose low-pass filter,
*inplementing a first-order lag. Call
*it with the input signal u, and a
*gain represented by the factor shift.
*Example, shift = 4 inples gain= 1/16
*
* Output is the return value, a short.
*The long integer x is for working
*storage, and should be neither read
*nor written by the user.
*/
short lag_filter(short u, short shift, long*x){
  x += u - (x>>shift);
  return (short)(x>>shift);
}

Elsewhere in the article, it notes that:
(1) "shift" should not change from call to call.
(2) "x" must be declared static and initialized to zero.
(2) If "shift" is known beforehand, it's better to use division rather
than a shift, to handle negative signals properly (although Microsoft
Visual C++ defeats this by always replacing the divide with a shift).

Here's my Ada version, which has the advantages of
(1) forcing "shift" to be constant from call to call
(2) hiding the definition of "x", and automatically initializing it
    (although it still has to be defined "statically" by the user,
    which is annoying.)
(3) Using division to fix the small error for negative signals.
    (Interestingly, one compiler I tried did substitute a
    shift for the divide, but if "x" was negative, it added (shift-1)
    to it first to make the shift correct.)
(4) Permits the use of types other than "short" (within reason)

-- General-purpose low-pass filter,
-- implementing a first-order lag.
-- Instantiate a copy for each input
-- such that copy has same lifetime
-- as input.
generic
  Shift : Positive;
  -- Gain is 2 to the negative Shift.
  -- E.g., shift => 4 implies gain = 1/16
  -- 2**Shift must fit in max precision

  type Signal is range <>;
  -- Signal should be at least Shift+1
  -- bits less than max precision

package Filter is
  function Lag (U : Signal) return Signal;
end Filter;

with System;
package body Filter is

  type Long is range
    System.Min_Int .. System.Max_Int;
  X : Long := 0;

  function Lag (U : Signal) return Signal is
    D : constant Long := Long(2**Shift);
  begin
    X := X + Long(U) - X / D;
    return Signal (X / D);
  end Lag;

end Filter;

I used the following test case:

with Filter;
package Use_Lag is
  subtype Short is Integer range -32768 .. 32767;
  A : Short := 200;
  package A_Filter is new Filter(Shift => 4, Signal => Short);
end Use_Lag;

with Use_Lag; use Use_Lag;
procedure Test is
begin
  A := A_Filter.Lag(A); -- new A should be 12
end Test;

For a 16-bit machine (MIL-STD-1750), I got decent assembly code, similar
to:

  Load R0, U
  Double-Shift Right Arithmetic R0, 16 bits  ; R0/R1 = Long(U)
  Double-Add R0, X
  Double-Load R2, X

Label1: ["fix" negative X before shifting]
  Branch Not Negative, Label2                ; skip if R2/R3 not neg.
  Exclusive-OR R4, R4                        ; R4 = 0
  Load Immediate R5,15                       ; R4/R5 = Long(15)
  Double-Add R2, R4                          ; negative X "fixed"

Label2:
  Double-Shift Right Arithmetic R2, 4        ; R2/R3 = X / D
  Double-Subtract R0, R2                     ; R0/R1 = new X
  Double-Store R0, X                         ; save X for next call

[here, the block of code at Label1 is repeated for the new X, then...]

Label3:
  Double-Shift Right Arithmetic R0, 4        ; R0/R1 = new X / D

[at this point R1 is returned to the user]

Even though using a straight division would have been fewer
instructions, division is so slow on this machine that the sequence
above is still competitive for negative signals (and much faster for
positive signals).

On a 32-bit machine (VAX), I got a much simpler code sequence, since it
used 32 bits for everything:

  Add X and U and store in R3
  Divide X by 16 and store in R2
  Subtract R2 from R3 and store in X
  Divide new X by 16 and return result

In either case, it looks pretty competitive to what a C compiler would
generate. Note that both tests had all checks off, max optimization.

-- 
LMTAS - "Our Brand Means Quality"
For more info, see http://www.lmtas.com or http://www.lmco.com




             reply	other threads:[~1996-11-05  0:00 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1996-11-05  0:00 Ken Garlington [this message]
1996-11-05  0:00 ` ESP code rewritten in Ada Laurent Guerby
1996-11-06  0:00 ` whiting_ms@corning.com (Matt Whiting)
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox