507 lines
10 KiB
C
507 lines
10 KiB
C
/*
|
|
NanoStack: MCU software and PC tools for IP-based wireless sensor networking.
|
|
|
|
Copyright (C) 2006-2007 Sensinode Ltd.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
Address:
|
|
Sensinode Ltd.
|
|
Teknologiantie 6
|
|
90570 Oulu, Finland
|
|
|
|
E-mail:
|
|
info@sensinode.com
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#ifdef PLATFORM_WINDOWS
|
|
#include <windows.h>
|
|
#endif
|
|
#include "cdi.h"
|
|
|
|
/*read none/8*/
|
|
#define CDI_CHIP_ERASE 0x10
|
|
#define CDI_WR_CONFIG 0x19
|
|
#define CDI_SET_HW_BRKPNT 0x3B
|
|
|
|
/*read 8*/
|
|
#define CDI_RD_CONFIG 0x24
|
|
#define CDI_READ_STATUS 0x34
|
|
#define CDI_HALT 0x44
|
|
#define CDI_RESUME 0x4C
|
|
#define CDI_STEP_INSTR 0x5C
|
|
|
|
/*variable len, add data length to command byte*/
|
|
#define CDI_DBGINSTR 0x54
|
|
#define CDI_STEP_REPLACE 0x64
|
|
|
|
/*response = 16 bits*/
|
|
#define CDI_GET_PC 0x28
|
|
#define CDI_GET_CHIP_ID 0x68
|
|
|
|
|
|
/* internals */
|
|
void cdi_command_push(uint8_t *byte, uint8_t bytes_out, uint16_t *retval, uint8_t return_bits);
|
|
|
|
void cdi_flash_bank(uint8_t bank, uint8_t unif_mode);
|
|
|
|
void cdi_dptr_write(uint16_t value);
|
|
|
|
uint8_t cdi_reg_read(uint8_t reg_addr);
|
|
void cdi_reg_write(uint8_t reg_addr, uint8_t value);
|
|
|
|
void cdi_xdata_write(uint8_t value);
|
|
uint8_t cdi_xdata_read(void);
|
|
|
|
void cdi_flash_init(void);
|
|
void cdi_flash_write_page(void);
|
|
|
|
uint32_t cdi_addr = 0;
|
|
|
|
#define pause_ms(x) usleep(x*1000)
|
|
#define pause_us(x) usleep(x)
|
|
|
|
void cdi_command_push(uint8_t *byte, uint8_t bytes_out, uint16_t *retval, uint8_t return_bits)
|
|
{
|
|
uint8_t i, val;
|
|
|
|
for (i=0; i<bytes_out; i++)
|
|
{
|
|
prog_write(*byte);
|
|
byte++;
|
|
}
|
|
|
|
return_bits >>= 3;
|
|
for (i=0; i<return_bits; i++)
|
|
{
|
|
*retval <<= 8;
|
|
|
|
prog_read(&val);
|
|
*retval += val;
|
|
}
|
|
}
|
|
|
|
void cdi_reg_write(uint8_t reg_addr, uint8_t value)
|
|
{
|
|
uint8_t out[4];
|
|
uint16_t retval;
|
|
|
|
out[0] = CDI_DBGINSTR + 3; /*command length 3 bytes*/
|
|
out[1] = 0x75; /*mov sfr, #immediate*/
|
|
out[2] = reg_addr; /*sfr = reg_addr*/
|
|
out[3] = value; /*immediate*/
|
|
cdi_command_push(out, 4, &retval, 8);
|
|
}
|
|
|
|
void cdi_xdata_write(uint8_t value)
|
|
{
|
|
uint8_t out[3];
|
|
uint16_t retval;
|
|
|
|
out[0] = CDI_DBGINSTR + 2; /*command length 3 bytes*/
|
|
out[1] = 0x74; /*mov a, #immediate*/
|
|
out[2] = value; /*immediate*/
|
|
cdi_command_push(out, 3, &retval, 8);
|
|
|
|
out[0] = CDI_DBGINSTR + 1; /*command length 3 bytes*/
|
|
out[1] = 0xF0; /*movx @dptr, a*/
|
|
cdi_command_push(out, 2, &retval, 8);
|
|
|
|
out[0] = CDI_DBGINSTR + 1; /*command length 1 byte*/
|
|
out[1] = 0xA3; /*inc DPTR*/
|
|
cdi_command_push(out, 2, &retval, 8);
|
|
}
|
|
|
|
uint8_t cdi_xdata_read(void)
|
|
{
|
|
uint8_t out[2];
|
|
uint16_t retval;
|
|
uint8_t retval8;
|
|
|
|
out[0] = CDI_DBGINSTR + 1; /*command length 3 bytes*/
|
|
out[1] = 0xE0; /*movx a, @dptr*/
|
|
cdi_command_push(out, 2, &retval, 8);
|
|
retval8 = retval;
|
|
|
|
out[0] = CDI_DBGINSTR + 1; /*command length 1 byte*/
|
|
out[1] = 0xA3; /*inc DPTR*/
|
|
cdi_command_push(out, 2, &retval, 8);
|
|
|
|
return retval8;
|
|
}
|
|
|
|
void cdi_dptr_write(uint16_t value)
|
|
{
|
|
uint8_t out[4];
|
|
uint16_t retval;
|
|
|
|
/*set DPTR to address*/
|
|
out[0] = CDI_DBGINSTR + 3; /*command length 3 bytes*/
|
|
out[1] = 0x90; /*mov DPTR, #immediate16*/
|
|
out[2] = (value >> 8); /*immediateh*/
|
|
out[3] = value; /*immediatel*/
|
|
cdi_command_push(out, 4, &retval, 8);
|
|
}
|
|
|
|
|
|
uint8_t cdi_reg_read(uint8_t reg_addr)
|
|
{
|
|
uint8_t out[3];
|
|
uint16_t retval;
|
|
|
|
out[0] = CDI_DBGINSTR + 2; /*command length 2 bytes*/
|
|
out[1] = 0xE5; /*mov a, sfr*/
|
|
out[2] = reg_addr; /*sfr = reg_addr*/
|
|
cdi_command_push(out, 3, &retval, 8);
|
|
|
|
return (uint8_t) retval;
|
|
}
|
|
|
|
|
|
void cdi_flash_bank(uint8_t bank, uint8_t unif_mode)
|
|
{
|
|
uint8_t out;
|
|
|
|
out = (bank << 4) + 1;
|
|
out &= 0x31;
|
|
if (unif_mode) out |= 0x40; /*set unified memory model*/
|
|
|
|
cdi_reg_write(0xC7, out);
|
|
}
|
|
|
|
|
|
void cdi_flash_read(uint8_t *ptr, uint16_t bytes)
|
|
{
|
|
uint16_t i;
|
|
uint8_t out[4];
|
|
uint16_t retval;
|
|
|
|
cdi_flash_bank((cdi_addr >> 15), 0);
|
|
|
|
cdi_dptr_write(cdi_addr | 0x8000);
|
|
|
|
for (i=0; i<bytes; i++)
|
|
{
|
|
out[0] = CDI_DBGINSTR + 1; /*command length 1 byte*/
|
|
out[1] = 0xE4; /*clr a*/
|
|
cdi_command_push(out, 2, &retval, 8);
|
|
|
|
out[0] = CDI_DBGINSTR + 1; /*command length 1 byte*/
|
|
out[1] = 0x93; /*movc a, @A+DPTR*/
|
|
cdi_command_push(out, 2, &retval, 8);
|
|
*ptr++ = retval;
|
|
|
|
out[0] = CDI_DBGINSTR + 1; /*command length 1 byte*/
|
|
out[1] = 0xA3; /*inc DPTR*/
|
|
cdi_command_push(out, 2, &retval, 8);
|
|
}
|
|
|
|
cdi_addr += bytes;
|
|
}
|
|
|
|
const uint8_t cdi_ram_code[] =
|
|
{
|
|
0x7E, 0x00, /*mov r6, #0*/
|
|
0x7D, 0x04, /*mov r5, #4*/
|
|
/*C0:*/
|
|
0xE5, 0xAE, /*MOV A,_FCTL*/
|
|
0x20, 0xE7, 0xFB, /*JB ACC.7,C0*/
|
|
0x75, 0xAE, 0x02, /*mov _FCTL,#0x02*/
|
|
/*C1:*/
|
|
0xE0, /*movx a, @DPTR*/
|
|
0xF5, 0xAF, /*MOV _FWDATA, a*/
|
|
0xA3, /*inc DPTR*/
|
|
0xE0, /*movx a, @DPTR*/
|
|
0xF5, 0xAF, /*MOV _FWDATA, a*/
|
|
0xA3, /*inc DPTR*/
|
|
0xE0, /*movx a, @DPTR*/
|
|
0xF5, 0xAF, /*MOV _FWDATA, a*/
|
|
0xA3, /*inc DPTR*/
|
|
0xE0, /*movx a, @DPTR*/
|
|
0xF5, 0xAF, /*MOV _FWDATA, a*/
|
|
0xA3, /*inc DPTR*/
|
|
/*C2:*/
|
|
0xE5, 0xAE, /*MOV A,_FCTL*/
|
|
0x20, 0xE6, 0xFB, /*JB ACC.6,C2*/
|
|
0x1E, /*dec r6*/
|
|
0xDE, 0xE8, /*djnz r6,C1*/
|
|
0xDD, 0xE6, /*djnz r5,C1*/
|
|
/*C3:*/
|
|
0xE5, 0xAE, /*MOV A,_FCTL*/
|
|
0x20, 0xE7, 0xFB, /*JB ACC.7,C3*/
|
|
0x00, /*nop*/
|
|
0x00, /*nop*/
|
|
/*C4:*/
|
|
0x80, 0xFE /*sjmp C4*/
|
|
};
|
|
|
|
void cdi_flash_init(void)
|
|
{
|
|
uint8_t i;
|
|
|
|
/*set clock*/
|
|
cdi_reg_write(0xC6, 0xC9); /*sfr = CLKCON*/
|
|
/*set timing*/
|
|
cdi_reg_write(0xAB, 0x15); /*sfr = FWT*/
|
|
/*copy code to ram*/
|
|
cdi_dptr_write(0xE000);
|
|
for (i=0; i<sizeof(cdi_ram_code); i++)
|
|
{
|
|
cdi_xdata_write(cdi_ram_code[i]);
|
|
}
|
|
}
|
|
|
|
void cdi_flash_write_page(void)
|
|
{
|
|
uint16_t retval;
|
|
uint8_t out[4];
|
|
|
|
/*set unified memory model*/
|
|
cdi_flash_bank(0, 1);
|
|
|
|
/*set FADDR to page start*/
|
|
cdi_reg_write(0xAD, (cdi_addr >> 10) & 0x7F); /*sfr = FADDRH*/
|
|
cdi_reg_write(0xAC, (cdi_addr >> 2)); /*sfr = FADDRL*/
|
|
|
|
/*erase page*/
|
|
cdi_reg_write(0xAE, 0x01);
|
|
pause_ms(40);
|
|
|
|
/*set program counter*/
|
|
out[0] = CDI_DBGINSTR + 3; /*command length 3 bytes*/
|
|
out[1] = 0x02; /*ljmp immediate16*/
|
|
out[2] = 0xE0; /*immediateh*/
|
|
out[3] = 0x00; /*immediatel*/
|
|
cdi_command_push(out, 4, &retval, 8);
|
|
|
|
/*set breakpoint*/
|
|
out[0] = CDI_SET_HW_BRKPNT; /*command length 3 bytes*/
|
|
out[1] = 0x04;
|
|
out[2] = 0xE0; /*immediateh*/
|
|
out[3] = sizeof(cdi_ram_code)-2; /*immediatel*/
|
|
cdi_command_push(out, 4, &retval, 0);
|
|
|
|
cdi_dptr_write(0xE800); /*data area, set your page data here*/
|
|
/*execute*/
|
|
out[0] = CDI_RESUME; /*command length 3 bytes*/
|
|
cdi_command_push(out, 1, &retval, 8);
|
|
|
|
pause_ms(30);
|
|
out[3]= 0;
|
|
do
|
|
{
|
|
pause_ms(20);
|
|
out[0] = CDI_READ_STATUS;
|
|
cdi_command_push(out, 1, &retval, 8);
|
|
printf("-");
|
|
fflush(stdout);
|
|
}while( ((retval & 0x20) == 0) && (out[3]++ < 200) );
|
|
}
|
|
|
|
int cdi_flash_write(uint8_t *ptr, uint16_t length)
|
|
{
|
|
uint16_t i, retval;
|
|
uint8_t out[3];
|
|
|
|
if (length > 2048) return -1;
|
|
|
|
cdi_addr &= 0x1F800; /*make sure address is on page boundary*/
|
|
|
|
printf("0x%6.6X: ", cdi_addr);
|
|
fflush(stdout);
|
|
cdi_dptr_write(0xE800); /*our page data buffer is here*/
|
|
for (i=0; i< length; i++)
|
|
{
|
|
cdi_xdata_write(*ptr++);
|
|
if ((i & 0x3F) == 0)
|
|
{
|
|
printf(".");
|
|
}
|
|
if ((i & 0x0F) == 0x00)
|
|
{
|
|
printf("\bo");
|
|
}
|
|
if ((i & 0x0F) == 0x08)
|
|
{
|
|
printf("\b.");
|
|
}
|
|
fflush(stdout);
|
|
}
|
|
while(i<2048)
|
|
{
|
|
cdi_xdata_write(0xFF);
|
|
if ((i & 0x3F) == 0)
|
|
{
|
|
printf(".");
|
|
}
|
|
if ((i & 0x0F) == 0x00)
|
|
{
|
|
printf("\bo");
|
|
}
|
|
if ((i & 0x0F) == 0x08)
|
|
{
|
|
printf("\b.");
|
|
}
|
|
fflush(stdout);
|
|
i++;
|
|
}
|
|
|
|
out[0] = CDI_HALT;
|
|
cdi_command_push(out, 1, &retval, 8);
|
|
|
|
out[0] = CDI_READ_STATUS;
|
|
retval = 0;
|
|
cdi_command_push(out, 1, &retval, 8);
|
|
if ((retval & 0xFF) == 0xB2)
|
|
{
|
|
/*restore config*/
|
|
out[0] = CDI_WR_CONFIG; /*command length 3 bytes*/
|
|
out[1] = 0x0E; /*write flash area*/
|
|
cdi_command_push(out, 2, &retval, 0);
|
|
/*set flash timings and copy code to ram*/
|
|
cdi_flash_init();
|
|
/*write page*/
|
|
cdi_flash_write_page();
|
|
cdi_addr += 2048; /*increment page address*/
|
|
pause_ms(10);
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int cdi_flash_write_mac(uint8_t *ptr)
|
|
{
|
|
uint16_t i, retval;
|
|
uint8_t out[3];
|
|
|
|
cdi_addr = 0x1F800; /*last page*/
|
|
|
|
printf("0x%6.6X", cdi_addr);
|
|
fflush(stdout);
|
|
cdi_dptr_write(0xEFF8); /*our page data buffer is here*/
|
|
for (i=0; i<8; i++)
|
|
{
|
|
cdi_xdata_write(*ptr++);
|
|
if ((i & 0x0F) == 0)
|
|
{
|
|
printf(".");
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
|
|
out[0] = CDI_HALT;
|
|
cdi_command_push(out, 1, &retval, 8);
|
|
|
|
out[0] = CDI_READ_STATUS;
|
|
retval = 0;
|
|
cdi_command_push(out, 1, &retval, 8);
|
|
if ((retval & 0xFF) == 0xB2)
|
|
{
|
|
/*restore config*/
|
|
out[0] = CDI_WR_CONFIG; /*command length 3 bytes*/
|
|
out[1] = 0x0E; /*write flash area*/
|
|
cdi_command_push(out, 2, &retval, 0);
|
|
/*set flash timings and copy code to ram*/
|
|
cdi_flash_init();
|
|
/*write page*/
|
|
cdi_flash_write_page();
|
|
cdi_addr += 2048; /*increment page address*/
|
|
pause_ms(10);
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int cdi_start(uint16_t *chip_id)
|
|
{
|
|
uint8_t out[3];
|
|
uint16_t retval;
|
|
|
|
prog_start(); /*do the CDI startup sequence*/
|
|
|
|
out[0] = CDI_READ_STATUS;
|
|
cdi_command_push(out, 1, &retval, 8);
|
|
|
|
printf("Status: %2.2X.\n", retval & 0xFF);
|
|
|
|
out[0] = CDI_GET_CHIP_ID;
|
|
cdi_command_push(out, 1, &retval, 16);
|
|
|
|
*chip_id = retval;
|
|
|
|
out[0] = CDI_HALT;
|
|
cdi_command_push(out, 1, &retval, 8);
|
|
|
|
pause_ms(100);
|
|
|
|
out[0] = CDI_WR_CONFIG; /*command length 3 bytes*/
|
|
out[1] = 0x0E; /*write flash area*/
|
|
cdi_command_push(out, 2, &retval, 0);
|
|
|
|
pause_ms(10);
|
|
|
|
cdi_reg_write(0xC6, 0xC9); /*sfr = CLKCON*/
|
|
|
|
pause_ms(10);
|
|
|
|
cdi_reg_write(0xAB, 0x15); /*sfr = FWT*/
|
|
|
|
cdi_addr = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cdi_erase(void)
|
|
{
|
|
uint8_t out[3];
|
|
uint16_t retval;
|
|
uint8_t i;
|
|
|
|
out[0] = CDI_WR_CONFIG;
|
|
out[1] = 0x0E;
|
|
cdi_command_push(out, 2, &retval, 0);
|
|
out[0] = CDI_CHIP_ERASE;
|
|
cdi_command_push(out, 1, &retval, 0);
|
|
retval = 0;
|
|
i = 0;
|
|
do
|
|
{
|
|
pause_ms(30);
|
|
out[0] = CDI_READ_STATUS;
|
|
cdi_command_push(out, 1, &retval, 8);
|
|
}while( ((retval & 0x84) != 0x80) && (i++ < 100) );
|
|
|
|
cdi_addr = 0;
|
|
|
|
if (i >= 100)
|
|
{
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void cdi_set_address(uint32_t address)
|
|
{
|
|
cdi_addr = address;
|
|
}
|