| CNets and General LFSR Counters | |||
| Home CNets and Datapaths >> << Small footprints 
Usenet Postings | 
Subject: XBLOX vs. "CNets", lfsr dividers, etc.
Date: 28 Nov 1995 00:00:00 GMT
newsgroups: comp.arch.fpga,comp.lsi.cad
Perhaps this is of interest.  I have created an experimental set of C++
classes called "CNets" that let me specify structural (not behavioural)
designs which ultimately emit XNF primitives such as nets, gates, and
flipflops.  Using this specification language I can build up higher
level modules, the most sophisticated of which is a pipelined 32-bit
RISC processor plus on-chip peripherals such as boot ROM, UART, and
DRAM controller, in ~65% of a XC4010.
I use this approach in preference to both schematic capture and to
synthesis from HDLs.  It is more flexible, more powerful, and more
reusable than schematic capture and yet still allows me to precisely
and explicitly control primitive instantiation and placement, which can
be tricky using synthesis.  Not to mention this approach is much more
affordable than HDL synthesis.
(By the way, "CNets" does NOT deliver FPGA device independence, but it
could be adapted to that purpose.  I'm not convinced device
independence is such a panacea anyway -- for instance, either you
design to exploit distributed SRAMs and 3-state buses or you don't, and
if your current target device doesn't implement them you probably
should not require them.)
For instance, here is my universal linear feedback shift register
divider, which is known to work nicely for simple divisors like
n==(25000000/9600):
// emit an lfsr counter and decoder to divide by n
//
// See "Efficient Shift Registers, LFSR Counters, and
// Long Pseudo-Random Sequence Generators", Peter Alfke,
// Xilinx App Note, Aug. 1995
//
void lfsr_div(Net out, Net ce, Net reset, unsigned n) {
    ...
    // choose appropriate width counter
    static unsigned taps[32][4] = {
        { 0 }, { 0 }, { 0 }, { 3, 2 },
        { 4, 3 }, { 5, 3 }, { 6, 5 }, { 7, 6 },
        { 8, 6, 5, 4 }, { 9, 5 }, { 10, 7 }, { 11, 9 },
        { 12, 6, 4, 1 }, { 13, 4, 3, 1 }, { 14, 5, 3, 1 }, { 15, 14 },
        { 16, 15, 13, 4 }, { 17, 14 }, { 18, 11 }, { 19, 6, 2, 1 },
        { 20, 17 }, { 21, 19 }, { 22, 21 }, { 23, 18 },
        { 24, 23, 22, 17 }, { 25, 22 }, { 26, 6, 2, 1 }, { 27, 5, 2, 1
},
        { 28, 25 }, { 29, 27 }, { 30, 6, 4, 1, }, { 31, 28 }
    };
    check(n <= (1 << 30));
    for (unsigned bits = 1; n >= (1U << bits); bits++)
        ;
    check((1U << (bits-1)) <= n && n < (1U << bits));
    // determine bit pattern of terminal state (after n-1 clockings of lfsr)
    unsigned w = 0;
    for (unsigned i = 1; i < n; i++) {
        unsigned in = 0;
        for (unsigned j = 0; j < 4 && taps[bits][j]; j++)
            in ^= (w >> (taps[bits][j]) - 1) & 1;
        w = ((w << 1) & ((1 << bits) - 1)) ^ !in;
        check(w != 0);
    }
    // emit shift register and gates to recognize terminal state
    bus(lfsr, bits+1);
    out = lfsr(bits,1) == w;
    lfsr[0] = gnd;
    net(lfsr_in) = nomap(xnor(lfsr[taps[bits][0]], lfsr[taps[bits][1]],
                              lfsr[taps[bits][2]], lfsr[taps[bits][3]]));
    net(lfsr_reset) = out | reset;
    ff(lfsr[1], lfsr_in & ~lfsr_reset, ce);
    for (i = 2; i <= bits; i++)
        ff(lfsr[i], lfsr[i-1] & ~lfsr_reset, ce);
}
In case it's not perfectly obvious, :-), the last two groups of statements do
the following:
    bus(lfsr, bits+1);        -- declare lfsr to be a bus of bits+1 nets
    out = lfsr(bits,1) == w;  -- emit AND gate(s) to recognize the
                                  word 'w' in bits (bits..1) of bus lfsr
    lfsr[0] = gnd;            -- set lfsr[0] to gnd, necessary for the
                                  following XNOR to be correct when
                                  taps[i] is 0
    -- emit an XNOR of up to 4 inputs taking various taps from the lfsr
        shift register flipflop outputs.  'nomap' suppresses the default FMAP:
    net(lfsr_in) = nomap(xnor(lfsr[taps[bits][0]], lfsr[taps[bits][1]],
                              lfsr[taps[bits][2]], lfsr[taps[bits][3]]));
    -- set lfsr_reset to be the OR of out and reset signals:
    net(lfsr_reset) = out | reset;
    -- emit a flipflop driving lfsr[1] whose D is the AND of lfsr_in and
        NOT lfsr_reset and whose clock enable is 'ce':
    ff(lfsr[1], lfsr_in & ~lfsr_reset, ce);
    -- emit the rest of the flipflops, each of whose D input is the Q output of
        the previous 'flop, qualified by not reset, and whose clock enables
        are 'ce':
    for (i = 2; i <= bits; i++)
        ff(lfsr[i], lfsr[i-1] & ~lfsr_reset, ce);
Anyway, the salient ideas are:
* extend a real programming language with circuit specification datatypes
* employ structural specification, close to the FPGA primitive elements
Is anyone else using a similar approach?
Jan Gray
Copyright © 2000, Gray Research LLC.  All rights reserved. |