Basis for the C2 protocol from Silicon Labs.

Dependencies:   mbed

main.cpp

Committer:
Ivop
Date:
2014-06-11
Revision:
17:0c5581ae2471
Parent:
16:844edb89d863

File content as of revision 17:0c5581ae2471:

/*
 * SiLabs C2 Protocol on mbed pins p5/p6
 *
 * Copyright (c) 2014, Ivo van Poorten <ivopvp@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
#include "mbed.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>

DigitalOut c2ck(p5);
DigitalInOut c2d(p6);

static uint8_t buffer[256];

static struct devices {
    char *name;
    int devid;
} devices[] = {
    { "F30x",                  0x04 },    { "F31x",                  0x08 },
    { "F32x",                  0x09 },    { "F326/7",                0x0d },
    { "F33x",                  0x0a },    { "F336/7",                0x14 },
    { "F34x",                  0x0f },    { "F35x",                  0x0b },
    { "F36x",                  0x12 },    { "F38x",                  0x28 },
    { "F39x/F37x",             0x2b },    { "F41x",                  0x0c },
    { "F50x/F51x",             0x1c },    { "F52xA/F53xA",           0x11 },
    { "F54x",                  0x21 },    { "F55x/F56x/F57x",        0x22 },
    { "F58x/F59x",             0x20 },    { "F70x/F71x",             0x1e },
    { "F80x/F81x/F82x/F83x",   0x23 },    { "F90x/F91x",             0x1f },
    { "F92x/F93x",             0x16 },    { "F96x",                  0x2a },
    { "F99x",                  0x25 },    { "T60x",                  0x10 },
    { "T606",                  0x1b },    { "T61x",                  0x13 },
    { "T62x/T32x",             0x18 },    { "T622/T623/T326/T327",   0x19 },
    { "T63x",                  0x17 },    { NULL, 0 }
};

#define C2_MASK_FLBUSY      0x80
#define C2_MASK_INBUSY      0x02
#define C2_MASK_OUTREADY    0x01

// Programming Interface (PI) Commands

#define C2_PI_CMD_GET_VERSION       0x01
#define C2_PI_CMD_GET_DERIVATIVE    0x02

#define C2_PI_CMD_DEVICE_ERASE      0x03

#define C2_PI_CMD_BLOCK_READ        0x06        // FLASH (max. 64K bytes, bankswapping not supported)
#define C2_PI_CMD_BLOCK_WRITE       0x07        // FLASH

#define C2_PI_CMD_PAGE_ERASE        0x08

#define C2_PI_CMD_DIRECT_READ       0x09        // SFR (Special FUnction Registers, 256 bytes(?))
#define C2_PI_CMD_DIRECT_WRITE      0x0a        // SFR

#define C2_PI_CMD_INDIRECT_READ     0x0b        // RAM (Internal, 256 bytes)
#define C2_PI_CMD_INDIRECT_WRITE    0x0c        // RAM

#define C2_PI_CMD_XRAM_READ         0x0e        // RAM (External, 2K or 4K bytes)
#define C2_PI_CMD_XRAM_WRITE        0x0f        // RAM

// PI Status Values
#define C2_PI_STATUS_COMMAND_INVALID    0x00
#define C2_PI_STATUS_NO_CONNECT         0x01
#define C2_PI_STATUS_FAILED             0x02
#define C2_PI_STATUS_PAGE_LOCKED        0x03    // "security" feature
#define C2_PI_STATUS_TIMEOUT            0x04
#define C2_PI_STATUS_BAD_DATA           0x05
#define C2_PI_STATUS_OK                 0x0d

// C2 Registers
#define C2_REG_DEVID    0
#define C2_REG_REVID    1
#define C2_REG_FPCTL    2
static int C2_REG_FPDAT = 0xb4;         // or 0xad
static int page_size = 512;

static void fatal(char *f, ...) {
    va_list ap;
    va_start(ap,f);
    printf("\r\nFATAL: ");
    vprintf(f, ap);
    va_end(ap);
    exit(1);
}

static void c2ck_reset(void) {
    c2d.input();
    c2ck = 0;
    wait_us(25);
    c2ck = 1;
    wait_us(3);
}

static void c2ck_strobe(void) {
    c2ck = 0;
    wait_us(1);
    c2ck = 1;
    wait_us(1);
}

// Remember, ALL fields are sent LSB first(!)

#define START       do{ c2ck_strobe(); }while(0)
#define STOP        START
#define INS(a,b)    do{ c2d = a; c2ck_strobe(); c2d = b; c2ck_strobe(); }while(0)
#define LENGTH(a,b) INS(a,b)
#define WAIT        c2ck_strobe(); do{\
        while(!c2d && t--) {    \
            c2ck_strobe();      \
            wait_us(1);         \
        } if (!t) return -1;    \
    } while(0)
#define READV       do{\
    for (i=0; i<8; i++) {       \
        v >>= 1;                \
        c2ck_strobe();          \
        if (c2d) v |= 0x80;     \
    } } while(0)
#define WRITEV      do{\
    for (i=0; i<8; i++) {        \
        c2d = v & 1;            \
        c2ck_strobe();          \
        v >>= 1;                \
    } } while(0)

static int c2_read_dr(void) {
    int t = 20, i, v = 0;
    
    START;
    c2d.output();
    INS(0,0);
    LENGTH(0,0);    
    c2d.input();
    WAIT;    
    READV;
    STOP;
    
    return v;
}

static int c2_write_dr(int v) {
    int t = 20, i;
    
    START;
    c2d.output();
    INS(1,0);
    LENGTH(0,0);
    WRITEV;
    c2d.input();
    WAIT;
    STOP;
    
    return 0;
}

static int c2_read_ar(void) {
    int i, v = 0;
    
    START;
    c2d.output();
    INS(0,1);
    c2d.input();
    READV;
    STOP;

    return v;
}

static void c2_write_ar(int v) {
    int i;

    START;
    c2d.output();
    INS(1,1);
    WRITEV;
    c2d.input();
    STOP;
}

static int poll_status(int mask) {
    int t = 20, s;
    do {
        s = c2_read_ar();
        if (mask == C2_MASK_INBUSY) {
            if (!(s & mask)) return 0;
        } else {
            if (s & mask)    return 0;
        }
        wait_us(1);
    } while(--t);
    return -1;
}

static void c2_enable_pi(void) {
    c2ck_reset();
    wait_ms(2);
    c2_write_ar(C2_REG_FPCTL);
    if ((c2_write_dr(0x02))<0)
        fatal("cannot enable PI (wr2)");
    if ((c2_write_dr(0x04))<0)
        fatal("cannot enable PI (wr4)");
    if ((c2_write_dr(0x01))<0)
        fatal("cannot enable PI (wr1)");
    wait_ms(25);
}

static int c2_read_mem(int type, int address, int size, uint8_t *buf) {
    int ret, i, ram_or_sfr = (type == C2_PI_CMD_DIRECT_READ) || (type == C2_PI_CMD_INDIRECT_READ);

    c2_write_ar(C2_REG_FPDAT);
    c2_write_dr(type);

    if (poll_status(C2_MASK_INBUSY) < 0)
        fatal("read_mem: cannot set FPDAT, no input ACK");
    if (poll_status(C2_MASK_OUTREADY) < 0)
        fatal("read_mem: cannot set FPDAT, no output ready");

    ret = c2_read_dr();
    if (ret != C2_PI_STATUS_OK)
        fatal("read_mem: status not OK after setting FPDAT (status %02X)", ret);

    if (!ram_or_sfr) {
        c2_write_dr((address >> 8)&0xff);
        if (poll_status(C2_MASK_INBUSY) < 0)
            fatal("read_mem: cannot write msb of address");
    }

    c2_write_dr(address & 0xff);
    if (poll_status(C2_MASK_INBUSY) < 0)
        fatal("read_mem: cannot write lsb of address");

    c2_write_dr(size&0xff);
    if (poll_status(C2_MASK_INBUSY) < 0)
        fatal("read_mem: cannot set block length");
    if (poll_status(C2_MASK_OUTREADY) < 0)
        fatal("read_mem: no data ready");

    ret = c2_read_dr();
    if (ret != C2_PI_STATUS_OK)
        return ret;
    
    for (i=0; i<size; i++) {
        if (poll_status(C2_MASK_OUTREADY) < 0)
            fatal("read_mem: no data ready during retrieval of block");
        buf[i] = c2_read_dr();
    }
    
    return C2_PI_STATUS_OK;
}

int main() {
    int i, c, devid, revid, ret;
    
    c2d.input();
    c2ck = 1;

//    printf("\033[H\033[J");
    printf("\r\nSiLabs C2 Protocol\r\n\r\n");
    printf("Connect C2GND to GND, C2CK (clock) to p5 and C2D (data) to p6\r\n\r\n");
    printf("Press any key to continue\r\n");

    getc(stdin);

    c2_write_ar(C2_REG_DEVID);
    devid = c2_read_dr();
    if (devid <= 0) fatal("unable to read devid\r\n");
    
    c2_write_ar(C2_REG_REVID);
    revid = c2_read_dr();
    if (revid < 0) fatal("unable to read revid\r\n");

    for (i=0; devices[i].name; i++) {
        if (devices[i].devid == devid) break;
    }
    
    if (!i) fatal("unknown device: %02X:%02X\r\n", devid, revid);

    switch(devid) {
        case 0x0f: case 0x28: case 0x18: case 0x19: C2_REG_FPDAT = 0xad;
        default: break;
    }

    printf("\r\nDevice found: %s (%02X:%02X)", devices[i].name, devid, revid);
    printf("\r\nFPDAT Address: 0x%02X", C2_REG_FPDAT);
    printf("\r\nPage Size: %i\n\r", page_size);
    
    printf("\r\nEnabling Programming Interface\r\n");
    c2_enable_pi();

    for (c=0; c<256; c++) {
        printf("\r\nReading page %i\r\n", c);
        ret = c2_read_mem(C2_PI_CMD_BLOCK_READ, c<<8, 0x100, buffer);
        if (ret == C2_PI_STATUS_OK) {
            for (i=0; i<0x100; i++) {
                printf("%02x ", buffer[i]);
                if (i%16 == 15) printf("\r\n");
            }
        } else {
            if (ret == C2_PI_STATUS_PAGE_LOCKED)
                printf("page is locked, ");
            printf("read failed\r\n");
        }
    }
}