Jump to content
JohnPCAE

Intellivoice SP0256 die shot -- cleaned up

Recommended Posts

Since about New Year's, I've been cleaning up the SP0256 die shot to make it more of a "logical" view: making all of the various components a single color, minimizing the use of any "extraneous" colors, and generally making the file smaller. This is the first version that has everything cleaned up (no "noise" in the image). It hasn't been checked against the original for correctness, and I could use some extra eyes if anyone has some time to spare. A correct, cleaned-up version would presumably allow a "logical" diagram to follow.

 

Anyway, some features:

 

Lossless .PNG file format (so editing it won't introduce noise into the image)

 

Layers and colors:

 

T : metal (top)

B: n- (bottom)

P: polysilicon (transistors)

R: polysilicon (transistors kept turned on, used as resistive loads -- I think)

V: vias

 

T: green

B: burgundy

T+B: white

T+B+V: blue

T+B+P: purple

T+P: cyan

T+B+R: orange

T+R: yellow

T+B+R+V: red

 

I think that some transistors are larger than others due to conductivity: the larger the transistor, the higher its conductivity, so putting transistors in series necessitates making them larger so the overall conductivity matches the conductivity in alternate paths (I think). I'm a novice at this so take everything I'm saying with a grain of salt.

 

 

GI_SP0256_die_shot_8500w_10.zip

  • Like 3

Share this post


Link to post
Share on other sites

Since this got bumped, I thought I might share some details I worked out back in 2011 when this was decapped. Apologies if it's redundant; I don't remember if I posted it here before. Also, excellent work brightening up the actual traces. Some day I'd love to make a bit-accurate SP0256 emulation.

 

 

The instruction decoder is the PLA in the lower right. Here's how it decodes the 16 instructions:

 

post-14113-0-40824200-1477629644_thumb.jpg

 

The PLA above it decodes the number and lengths of the various VTM parameters. I wrote the following C program to decode its contents. Basically, it decodes the LSB of the first significant bit for each VTM parameter across both precision maps, and the number of significant bits.

#include <stdio.h>

const unsigned char *op_fmt_rom[17] =
{
    "001111001111000011110000",
    "111100110011000000000000",
    "001011011010010100001010",
    "001111001010000011110101",
    "001011011010010100001010",
    "001111001010000011110101",
    "001011011010010100001010",
    "001111001010000011110101",
    "001011011011000001011111",
    "101101100101000001011010",
    "101101110110000000000101",
    "101101100111000001010000",
    "111100110011000000000000",
    "111100110011000000000000",
    "111100000000000000000000",
    "111100000000000000000000",
    "111111111111000000000000"
};


unsigned read_rom(int rmsb, int rlsb)
{
    unsigned out_bit_5 = op_fmt_rom[rlsb][23 - (5*4 + rmsb)] == '1';
    unsigned out_bit_4 = op_fmt_rom[rlsb][23 - (4*4 + rmsb)] == '1';
    unsigned out_bit_3 = op_fmt_rom[rlsb][23 - (3*4 + rmsb)] == '1';
    unsigned out_bit_2 = op_fmt_rom[rlsb][23 - (2*4 + rmsb)] == '1';
    unsigned out_bit_1 = op_fmt_rom[rlsb][23 - (1*4 + rmsb)] == '1';
    unsigned out_bit_0 = op_fmt_rom[rlsb][23 - (0*4 + rmsb)] == '1';

    return (out_bit_5 << 5)
         | (out_bit_4 << 4)
         | (out_bit_3 << 3)
         | (out_bit_2 << 2)
         | (out_bit_1 << 1)
         | (out_bit_0 << 0);
}

const char *label[17] =
{
    "Amplitude",
    "Pitch",
    "B0", "F0",
    "B1", "F1",
    "B2", "F2",
    "B3", "F3",
    "B4", "F4",
    "B5", "F5",
    "Amplitude Interpolation",
    "Pitch Interpolation",
    "All parameters (Load-All instruction)"
};

int main()
{
    int rmsb, rlsb;


    for (rmsb = 0; rmsb < 4; rmsb++)
    {
        printf("%s update, %s precision\n", 
               rmsb & 2 ? "Delta" : "Absolute",
               rmsb & 1 ? "High" : "Low");

        printf("Param#  Length   1st LSB   Parameter\n");
        for (rlsb = 0; rlsb < 17; rlsb++)
        {
            unsigned dec = read_rom(rmsb, rlsb);

            printf("%6d  %6d    %6d   %s\n", 
                    rlsb, 1 + (dec >> 3), dec & 7, label[rlsb]);
        }
        printf("\n");
    }

    return 0;
}

There's a 16 bit register at the far lower right (going vertically) which keeps track of which of the 16 parameters will be loaded, while a pair of counters between the two PLAs count out the various fields. One counter keeps track of where the first significant bit is, and the other keeps track of how many significant bits are in the coefficient, directly from the table above.

 

The ROM at the upper left is a compressed form of the 7 bit => 9 bit coefficient ROM. This ROM maps 7 bit linear values to 9-bit non-linear values Some of the columns were compressed away as they were trivially computable with logic functions. (Specifically, likely by tying 4-to-1 mux inputs to 1 or 0 as appropriate.) This C program I wrote some years ago demonstrates how to unpack it. It compares it to the qtbl from jzintv, which in turn comes from the GI patent.

#include <stdio.h>

const char *rom_img_char[24] =
{
    "################................",
    "###########################.....",
    "########........########........",
    "###########................#####",
    "#################...............",
    "####....####....####....####....",
    "####.......########........#####",
    "###..............###############",
    "#...............................",
    "##..##..##..##..##..##..##..##..",
    "##..###....####....####....####.",
    "...######........########.......",
    ".###############................",
    "#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.",
    "#.#.#..##..##..##..##..##..##..#",
    "#..##....####....####....####...",
    ".#######........########........",
    "######.#.#.#.#.#.#.#.#.#.#.#.#.#",
    ".#.#.##..##..##..##..##..##..##.",
    ".###....####....####....####....",
    "######.#.#.#.#.#.#.#.#.#.#.#.#.#",
    ".#..##..##..##..##..##..##..##..",
    "#...............................",
    "..#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.",
};

unsigned int rom_img[24];

void init_rom_img(void)
{
    int i, j;
    unsigned bit;

    for (i = 0; i < 24; i++)
    {
        bit = 0;
        for (j = 0; j < 32; j++)
            bit = (bit << 1) | (rom_img_char[i][j] == '#' ? 1 : 0);

        rom_img[i] = bit;
    }
}


unsigned get_bit(int row, int col)
{
    return (rom_img[col] >> (row & 0x1F)) & 1;
}

unsigned output_bit_0(int rmsb, int rlsb)
{
    return rmsb == 3 ? !get_bit(rlsb, 23)
         : rmsb == 2 ? 1
         : rmsb == 1 ? 1
         :             !get_bit(rlsb, 22);
}

unsigned output_bit_1(int rmsb, int rlsb)
{
    return rmsb == 3 ? !get_bit(rlsb, 21)
         : rmsb == 2 ? !get_bit(rlsb, 20)
         :             0;
}

unsigned output_bit_2(int rmsb, int rlsb)
{
    return rmsb == 3 ? !get_bit(rlsb, 19)
         : rmsb == 2 ? !get_bit(rlsb, 18)
         : rmsb == 1 ? !get_bit(rlsb, 17)
         :             0;
}

unsigned output_bit_3(int rmsb, int rlsb)
{
    return rmsb == 3 ? !get_bit(rlsb, 16)
         : rmsb == 2 ? !get_bit(rlsb, 15)
         : rmsb == 1 ? !get_bit(rlsb, 14)
         :             !get_bit(rlsb, 13);
}

unsigned output_bit_4(int rmsb, int rlsb)
{
    return rmsb == 3 ? !get_bit(rlsb, 12)
         : rmsb == 2 ? !get_bit(rlsb, 11)
         : rmsb == 1 ? !get_bit(rlsb, 10)
         :             !get_bit(rlsb,  9);
}

unsigned output_bit_5(int rmsb, int rlsb)
{
    return rmsb == 3 ? !get_bit(rlsb,  
         : rmsb == 2 ? !get_bit(rlsb,  7)
         : rmsb == 1 ? !get_bit(rlsb,  6)
         :             !get_bit(rlsb,  5);

}

unsigned output_bit_6(int rmsb, int rlsb)
{
    return rmsb == 3 ? 1
         : rmsb == 2 ? !get_bit(rlsb, 4)
         : rmsb == 1 ? !get_bit(rlsb, 3)
         :             !get_bit(rlsb, 2);
}

unsigned output_bit_7(int rmsb, int rlsb)
{
    return rmsb == 3 ? 1
         : rmsb == 2 ? 1
         : rmsb == 1 ? !get_bit(rlsb, 1)
         :             !get_bit(rlsb, 0);
}

unsigned output_bit_8(int rmsb, int rlsb)
{
    return rmsb == 3 ? 1 
         : rmsb == 2 ? 1
         : rmsb == 1 ? 1
         :             0;
}

unsigned get_coeff(int index)
{
    int rmsb = (index >> 5) & 3;
    int rlsb = (index & 0x1F) ^ 0x1F;

    return output_bit_8(rmsb, rlsb) << 8
         | output_bit_7(rmsb, rlsb) << 7
         | output_bit_6(rmsb, rlsb) << 6
         | output_bit_5(rmsb, rlsb) << 5
         | output_bit_4(rmsb, rlsb) << 4
         | output_bit_3(rmsb, rlsb) << 3
         | output_bit_2(rmsb, rlsb) << 2
         | output_bit_1(rmsb, rlsb) << 1
         | output_bit_0(rmsb, rlsb) << 0;
}


const unsigned qtbl[128] =
{
    0,      9,      17,     25,     33,     41,     49,     57,
    65,     73,     81,     89,     97,     105,    113,    121,
    129,    137,    145,    153,    161,    169,    177,    185,
    193,    201,    209,    217,    225,    233,    241,    249,
    257,    265,    273,    281,    289,    297,    301,    305,
    309,    313,    317,    321,    325,    329,    333,    337,
    341,    345,    349,    353,    357,    361,    365,    369,
    373,    377,    381,    385,    389,    393,    397,    401,
    405,    409,    413,    417,    421,    425,    427,    429,
    431,    433,    435,    437,    439,    441,    443,    445,
    447,    449,    451,    453,    455,    457,    459,    461,
    463,    465,    467,    469,    471,    473,    475,    477,
    479,    481,    482,    483,    484,    485,    486,    487,
    488,    489,    490,    491,    492,    493,    494,    495,
    496,    497,    498,    499,    500,    501,    502,    503,
    504,    505,    506,    507,    508,    509,    510,    511
};

int main(void)
{
    int i;

    init_rom_img();

    printf("Decoded ROM:\n");

    printf("Idx:  Expect     Got  Match?\n");
    for (i = 0; i < 128; i++)
    {
        int dec = get_coeff(i);
        printf("%3d:     %3d    %3d   %c\n", 
               i, qtbl[i], dec, dec == qtbl[i] ? 'Y' : 'n');
    }

    return 0;
}

The big ROM at the top right interleaves bytes starting at the left and reading rightward. 16 bytes per column, 128 columns. I don't remember the exact interleaving pattern, but I did verify it against the mask ROM image for at least the first several bytes.

Edited by intvnut
  • Like 1

Share this post


Link to post
Share on other sites

I think that some transistors are larger than others due to conductivity: the larger the transistor, the higher its conductivity, so putting transistors in series necessitates making them larger so the overall conductivity matches the conductivity in alternate paths (I think). I'm a novice at this so take everything I'm saying with a grain of salt.

 

Generally, you need a wider transistor to drive a larger load. Load in this case is the capacitance on the gates of the transistors downstream. The higher the fanout of a gate, the more capacitance it needs to drive. Of course, a wider gate has higher capacitance as an input, so there's a tradeoff.

 

This PDF goes into a lot of detail for CMOS gates: https://www.ece.umd.edu/class/enee359a.S2008/enee359a-sizing.pdf

 

Fortunately for us, we only need to consider the nFETs, as this is NMOS. Also, given the huge wires, series resistance in the wires isn't nearly as much of a factor as it is in submicron technology. IIRC, when I did my 2 micron chip back in college, I only really looked at relative gate sizes and ignored the wires.

Share this post


Link to post
Share on other sites

I don't remember the interleaving off the top of my head, but I did have to figure it out to make sure that I got the bits correct. There are some spots in the big ROM where the traces weren't cleaned properly.

Share this post


Link to post
Share on other sites

I don't remember the interleaving off the top of my head, but I did have to figure it out to make sure that I got the bits correct. There are some spots in the big ROM where the traces weren't cleaned properly.

 

I found the perl script I used to interleave the SP0256 ROM image to match the ROM itself. It's no work of beauty. :-) According to my notes, it takes as input a text file with one byte per line already converted to 1s and 0s.

.

#!/usr/bin/perl -w

my @rom;

foreach my $line (<STDIN>)
{
    chomp $line;
    my @bits = split //, $line;
    push @rom, [ reverse @bits ];
}


my ($i, $j, $k, $m);

for ($i = 0; $i < 128; $i++)
{
    print "Row $i\n";
    for ($j = 0; $j < 8; $j++)
    {
        print "Bit $j:  ";
        $m = "";
        for ($k = 0; $k < 16; $k++)
        {
            $m .= $rom[$i * 16 + $k]->[$j];
        }
        print $m, "\n";
    }
    print "\n";
}

.

This outputs sequences like so:

.

Row 0
Bit 0:  1111101011111111
Bit 1:  1010101011101111
Bit 2:  1110101111101010
Bit 3:  0100010101000001
Bit 4:  1110101010111101
Bit 5:  0001010101010100
Bit 6:  0101010101010110
Bit 7:  0101010101010100

.

Note that each "Row" according to my script is one column in the image. Normal RAM/ROM nomenclature calls those rows, while the interleaved structures are called columns. The ROM on the die is effectively rotated 90° CCW.

  • Like 1

Share this post


Link to post
Share on other sites

Just a little FYI: I contacted visual6502.org and they are going to look into building a simulation based on the images. I mentioned that it would be wonderful if we had die shots of the STIC; the person who responded was enthusiastic about it, but he needs a donor chip to take apart. I don't know if he has contacted anyone here, but there might be opportunity to fully uncover the STIC's internal workings if anyone has a suitable donor they wouldn't mind parting with. If anyone is interested, you can contact them on the visual6502.org site.

  • Like 1

Share this post


Link to post
Share on other sites

Intvnut

Seeing your code brings me back to my basic days, especially $. But... no $G?

Sorry, bad joke 😂

 

 

I found the perl script I used to interleave the SP0256 ROM image to match the ROM itself. It's no work of beauty. :-) According to my notes, it takes as input a text file with one byte per line already converted to 1s and 0s.

.

#!/usr/bin/perl -w

my @rom;

foreach my $line (<STDIN>)
{
    chomp $line;
    my @bits = split //, $line;
    push @rom, [ reverse @bits ];
}


my ($i, $j, $k, $m);

for ($i = 0; $i < 128; $i++)
{
    print "Row $i\n";
    for ($j = 0; $j < 8; $j++)
    {
        print "Bit $j:  ";
        $m = "";
        for ($k = 0; $k < 16; $k++)
        {
            $m .= $rom[$i * 16 + $k]->[$j];
        }
        print $m, "\n";
    }
    print "\n";
}
.

This outputs sequences like so:

.

Row 0
Bit 0:  1111101011111111
Bit 1:  1010101011101111
Bit 2:  1110101111101010
Bit 3:  0100010101000001
Bit 4:  1110101010111101
Bit 5:  0001010101010100
Bit 6:  0101010101010110
Bit 7:  0101010101010100
.

Note that each "Row" according to my script is one column in the image. Normal RAM/ROM nomenclature calls those rows, while the interleaved structures are called columns. The ROM on the die is effectively rotated 90° CCW.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...