Quilted Dies of CPU & RAM
Dave Long
dave.long at bluewin.ch
Sun Nov 26 08:47:39 EST 2006
> Interesting. Ward Cunningham is currently using bit-reversed counters
> in his Bynase code for PDM [0] --- he's doing the equivalent of a
> fixed dither pattern in the time domain rather than just doing the
> delta-sigma thing.
Gradients work pretty well for simplistic goal seeking. It's possible
to use the statistics of sums of random numbers to create
bacterium-like foo-taxis using a pair of motors instead of a flagellum.
(I believe current understanding is that the flagellum has a true
rotary driver, and is stranded, like a rope -- so rotation in the
aligned sense produces propulsion, but rotation counter to that
separates the strands and produces a tumble. Operations Research at
the microbial level pretty much consists of deciding between "stay the
course" and "try something new")
Any idea why he's using the reversed counter instead of an LFSR to
dither?
[On an older page, he mentions that tiny12s don't have a "register
based jump instruction". I generally use table-driven state machines
on the tiny13s (there not being room for much else), with dynamic
linkage via PUSH/RET, which seems simple enough to work on just about
any AVR]
> My current best approaches to this in software are
>
> unsigned char spec_binc(unsigned char acc) {
> unsigned char mask;
> for (mask = 0x80; mask & acc; mask >>= 1)
> acc ^= mask;
> return mask | acc;
> }
The worst case doesn't matter so much, as it only occurs once every 256
increments. Half the time, it's only the first bit that flips, which I
think comes down to:
ldi mask, 0x80
mov t0, mask
and t0, acc
brz (taken)
or acc, mask
or about 6 clocks, giving an average cost of close to 13.
> unsigned char table16[] = { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
> 0x0e, 0x0f,
> 0x04, 0x05, 0x06, 0x07, 0x02, 0x03,
> 0x01, 0x00 };
>
> unsigned char table16_reverse_counter(unsigned char input) {
> unsigned char lefthalf = input >> 4;
> unsigned char nextleft = table16[lefthalf];
> unsigned char righthalf = input & 0x0f;
> unsigned char nextright = (nextleft ? righthalf :
> table16[righthalf]);
> return ((unsigned char)(nextleft << 4) | nextright);
> }
>
> which I think can be better both in the average case and the worst
> case if you avoid doing address arithmetic
What does gcc generate for this? Doing it by hand, I'd need 8 clocks
worth of LSR and LSL for shifting lefthalf/nextleft, 2 clocks for the
ANDI and OR on righthalf, and 3 more clocks (avg) for the two lookups.
That's already more expensive, without even handling the conditional.
If there's a way to do multiple shifts, then the following fixed-time
algorithm (using boolean smears a la APL*) would be competitive, but as
it stands the eight places worth of ASR take more than half the budget.
def revinc8(x):
y = x & 0xff # 1 1110....
y &= (y>>1) + 0x80 # 3 11100...
y &= (y>>2) + 0xc0 # 4 1110000.
y &= (y>>4) + 0xf0 # 6 11100000
m = (y >> 1) + 0x80 # 1 11110000
return (x ^ m) & 0xff # 1 0001....
-Dave
:: :: ::
* smearing is one of the oldest techniques in CS. To digress a bit,
I'd mentioned being surprised at finding out how elegantly the idea of
a while-loop consisting of a (one hopes) finite sequence of operations
followed by an infinite tail of identity ops (f**0, where f**1 is our
original op) worked in J:
"J, Dijkstra, while-loops, and infinity"
http://lists.canonical.org/pipermail/kragen-discuss/2003-February/
000897.html
Well, it seems that it wasn't just pure coincidence: Iverson, who
originated the APL family of array languages, is also apparently known
in mathematical circles for introducing the "Iverson bracket" notation,
in which suitable use of boolean expressions allow one to express
finite summations and integrals as more readily manipulated infinite
ones. (similar to how wavefunctions are integrated over all of space,
but, at the classical scale, are really only concerned with points
thereof). It seems like an obvious improvement these days, but that
may just be the influence of several decades of computers.
But it turns out it works in theory as well as in practice, and Kleene,
by 1952, used zero to both (a) absorb multiplications and (b) preserve
additions, in order to define the μ operator for minimization. In APL
terms, it was a product scan followed by a sum reduction (+/*\), which
meant that the first solution (first zero bit after a string of ones,
just as in the reverse adder above) would cause its success to
propagate (0*X=0) through the remainder of the naturals. Adding up
that initial string of ones then ignores the infinite tail of zeros
(X+0=X), yielding the first natural with a solution.
http://en.wikipedia.org/wiki/Mu_operator
[ Knuth finds even deeper roots here, tracing it back to an Italian
HAKMEM of the 1830's exploring the use of expressions of the form
0**0**x:
Donald Knuth, "Two Notes on Notation", American Mathematical Monthly,
Volume 99, Number 5, May 1992, pp. 403-422.
http://arxiv.org/PS_cache/math/pdf/9205/9205211.pdf]
More information about the Kragen-discuss
mailing list