Initial version of CCTL, 1KB serial bootloader for CC1110/CC1111

This commit is contained in:
Joby Taffey committed 2012-03-07 17:08:43 +00:00
commit 11cfdfdcdc
13 files changed
+2950

No files matched your search

+1283
View File
File diff suppressed because it is too large. Load diff
+339
View File
@@ -0,0 +1,339 @@
/*
* CCTL - ChipCon Tiny Loader
* A 1KB serial bootloader for the CC1110/CC1111
* Joby Taffey (c) 2012 <jrt-cctl@hodgepig.org>
*
* Derived from CC Bootloader
* Fergus Noble (c) 2011
*
* 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; version 2 of the License.
*
* 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.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
#include <stdint.h>
#include <cc1110.h>
#include "cc1110-ext.h"
// Flash write timer value:
// FWT = 21000 * FCLK / (16 * 10^9)
// For FCLK = 24MHz, FWT = 0x1F
// For FCLK = 26MHz, FWT = 0x11
#define FLASH_FWT 0x11
// Address of flash controller data register
#define FLASH_FWDATA_ADDR 0xDFAF
#define RXFIFO_ELEMENTS 2048
#define RXFIFO_SIZE (RXFIFO_ELEMENTS - 1)
static __xdata uint8_t rxfifo[RXFIFO_SIZE];
static uint8_t rxfifo_in;
static uint8_t rxfifo_out;
static __xdata struct cc_dma_channel dma0_config;
static const __code uint8_t * __at (0x0000) flashp;
__xdata uint8_t rambuf[1024];
uint8_t page;
static const char banner[] = {'\r', '\n', 'C', 'C', 'T', 'L', '\r', '\n'};
struct cc_dma_channel
{
uint8_t src_high;
uint8_t src_low;
uint8_t dst_high;
uint8_t dst_low;
uint8_t len_high;
uint8_t len_low;
uint8_t cfg0;
uint8_t cfg1;
};
#define DMA_CFG0_TRIGGER_FLASH 18
#define DMA_CFG1_SRCINC_1 (1 << 6)
#define DMA_CFG1_DESTINC_0 (0 << 4)
#define DMA_CFG1_PRIORITY_HIGH (2 << 0)
#define DMAARM_DMAARM0 (1 << 0)
#define DMA_LEN_HIGH_VLEN_MASK (7 << 5)
#define DMA_LEN_HIGH_VLEN_LEN (0 << 5)
#define DMA_LEN_HIGH_VLEN_PLUS_1 (1 << 5)
#define DMA_LEN_HIGH_VLEN (2 << 5)
#define DMA_LEN_HIGH_VLEN_PLUS_2 (3 << 5)
#define DMA_LEN_HIGH_VLEN_PLUS_3 (4 << 5)
#define DMA_LEN_HIGH_MASK (0x1f)
#define DMA_CFG0_WORDSIZE_8 (0 << 7)
#define DMA_CFG0_WORDSIZE_16 (1 << 7)
#define DMA_CFG0_TMODE_MASK (3 << 5)
#define DMA_CFG0_TMODE_SINGLE (0 << 5)
#define DMA_CFG0_TMODE_BLOCK (1 << 5)
#define DMA_CFG0_TMODE_REPEATED_SINGLE (2 << 5)
#define DMA_CFG0_TMODE_REPEATED_BLOCK (3 << 5)
void flash_erase_page(void)
{
while (FCTL & FCTL_BUSY);
FWT = FLASH_FWT;
FADDRH = page << 1;
FADDRL = 0x00;
// Erase the page that will be written to
FCTL |= FCTL_ERASE;
__asm nop __endasm;
// Wait for the erase operation to complete
while (FCTL & FCTL_BUSY) {}
}
void flash_write_trigger(void)
{
// Enable flash write. Generates a DMA trigger. Must be aligned on a 2-byte
// boundary and is therefore implemented in assembly.
// p.s. if this looks a little crazy its because it is, sdcc doesn't currently
// support explicitly specifying code alignment which would make this easier
__asm
.globl flash_write_trigger_instruction
.globl flash_write_trigger_done
; Put our trigger instruction in the HOME segment (shared with some startup code)
; where it wont move around too much
.area HOME (CODE)
; Comment or uncomment these lines to adjust if you change the start.asm code
nop ; Padding to get onto 16-bit boundary
flash_write_trigger_instruction:
orl _FCTL, #0x02 ; FCTL |= FCTL_ERASE
nop ; Required, see datasheet.
ljmp flash_write_trigger_done ; Jump back into our function
; Meanwhile, back in the main CSEG segment...
.area CSEG (CODE)
; Jump to the trigger instruction
ljmp flash_write_trigger_instruction
flash_write_trigger_done:
__endasm;
}
void flash_write(void)
{
// Setup DMA descriptor
dma0_config.src_high = (((uint16_t)(__xdata uint16_t *)rambuf) >> 8) & 0x00FF;
dma0_config.src_low = ((uint16_t)(__xdata uint16_t *)rambuf) & 0x00FF;
dma0_config.dst_high = (FLASH_FWDATA_ADDR >> 8) & 0x00FF;
dma0_config.dst_low = FLASH_FWDATA_ADDR & 0x00FF;
dma0_config.len_high = DMA_LEN_HIGH_VLEN_LEN;
dma0_config.len_high |= ((1024) >> 8) & DMA_LEN_HIGH_MASK;
dma0_config.len_low = (1024) & 0x00FF;
dma0_config.cfg0 = \
DMA_CFG0_WORDSIZE_8 | \
DMA_CFG0_TMODE_SINGLE | \
DMA_CFG0_TRIGGER_FLASH;
dma0_config.cfg1 = \
DMA_CFG1_SRCINC_1 | \
DMA_CFG1_DESTINC_0 | \
DMA_CFG1_PRIORITY_HIGH;
// Point DMA controller at our DMA descriptor
DMA0CFGH = ((uint16_t)&dma0_config >> 8) & 0x00FF;
DMA0CFGL = (uint16_t)&dma0_config & 0x00FF;
// Waiting for the flash controller to be ready
while (FCTL & FCTL_BUSY);
// Configure the flash controller
FWT = FLASH_FWT;
FADDRH = (page << 1) & 0x3F;
//FADDRL = 0;//(page << 9) & 0xFF; // reset value is 0x00
// Arm the DMA channel, so that a DMA trigger will initiate DMA writing
DMAARM |= DMAARM_DMAARM0;
// Enable flash write - triggers the DMA transfer
flash_write_trigger();
// Wait for DMA transfer to complete
while (!(DMAIRQ & DMAIRQ_DMAIF0));
// Wait until flash controller not busy
while (FCTL & (FCTL_BUSY | FCTL_SWBSY));
// By now, the transfer is completed, so the transfer count is reached.
// The DMA channel 0 interrupt flag is then set, so we clear it here.
DMAIRQ &= ~DMAIRQ_DMAIF0;
}
uint8_t cons_getch(void)
{
if (rxfifo_in == rxfifo_out)
return 0;
page = rxfifo[rxfifo_out];
if (rxfifo_out + 1 == RXFIFO_SIZE)
rxfifo_out = 0;
else
rxfifo_out++;
return 1;
}
void cons_putc(uint8_t ch)
{
U0DBUF = ch;
while(!(U0CSR & U0CSR_TX_BYTE)); // wait for byte to be transmitted
U0CSR &= ~U0CSR_TX_BYTE; // Clear transmit byte status
}
void uart0_isr(void) __interrupt URX0_VECTOR
{
URX0IF = 0;
// HACK we know the buffer is big enough, as client is waiting for our ACK
// if(rxfifo_in != (( rxfifo_out - 1 + RXFIFO_SIZE) % RXFIFO_SIZE)) // not full
{
rxfifo[rxfifo_in] = U0DBUF;
if (rxfifo_in + 1 == RXFIFO_SIZE)
rxfifo_in = 0;
else
rxfifo_in++;
}
}
void jump_to_user(void)
{
if (*((__xdata uint8_t*)0x400) != 0xFF)
{
// Disable all interrupts
EA = 0;
IEN0 = IEN1 = IEN2 = 0;
// bootloader not running
F1 = 0;
// Jump to user code
__asm
ljmp #0x400
__endasm;
}
}
void bootloader_main(void)
{
uint16_t i;
uint8_t n;
// Initialise clocks
SLEEP &= ~SLEEP_OSC_PD; // enable RC oscillator
while( !(SLEEP & SLEEP_XOSC_S) ); // let oscillator stabilise
CLKCON = CLKCON_OSC32 | CLKCON_OSC | TICKSPD_DIV_32 | CLKSPD_DIV_2; // select internal HS RC oscillator
while (!(CLKCON & CLKCON_OSC));
CLKCON = CLKCON_OSC32 | TICKSPD_DIV_32 | CLKSPD_DIV_1; // select external crystal
// while (CLKCON & CLKCON_OSC);
// SLEEP |= SLEEP_OSC_PD; // Disable RC oscillator now that we have an external crystal
rxfifo_in = rxfifo_out = 0;
PERCFG = (PERCFG & ~PERCFG_U0CFG) | PERCFG_U1CFG;
P0SEL |= (1<<3) | (1<<2);
U0CSR = 0x80 | 0x40; // UART, RX on
U0BAUD = 34; // 115200
U0GCR = 13; // 115k2 baud at 13MHz, useful for coming out of sleep. Assumes clkspd_div2 in clkcon for HSRC osc
URX0IF = 0; // No interrupts pending at start
URX0IE = 1; // Serial Rx irqs enabled in system interrupt register
F1 = 1;
EA = 1;
n = 0;
while(n < sizeof(banner))
cons_putc(banner[n++]);
n = 8;
i = 65535;
while(!cons_getch() && n > 0)
{
if (i-- == 0)
{
cons_putc('B');
n--;
}
}
if (n != 0)
goto upgrade_loop;
jump_to_user();
upgrade_loop:
WDCTL = (WDCTL & ~WDCTL_INT) | WDCTL_INT_SEC_1; // watchdog on LS RCOSC, ~1s
WDCTL = (WDCTL & ~WDCTL_MODE) | WDCTL_EN; // start
while(1)
{
if (cons_getch())
{
WDCTL = (WDCTL & ~0xF0) | (0xA0); // pat
WDCTL = (WDCTL & ~0xF0) | (0x50);
switch(page)
{
case 'e':
while(!cons_getch());
flash_erase_page();
goto ack;
break;
case 'p':
while(!cons_getch());
flash_write();
goto ack;
break;
case 'r':
while(!cons_getch());
for (i=page<<10;i<(page+1)<<10;i++)
cons_putc(flashp[i]);
goto ack;
break;
case 'l':
i = 0;
while(i<1024)
{
while(!cons_getch());
rambuf[i] = page;
i++;
}
goto ack;
break;
case 'j':
jump_to_user();
break;
ack:
cons_putc(0);
}
}
}
}
+101
View File
@@ -0,0 +1,101 @@
.globl __start__stack
;--------------------------------------------------------
; Stack segment in internal ram
;--------------------------------------------------------
.area SSEG (DATA)
__start__stack:
.ds 1
;--------------------------------------------------------
; interrupt vector
;--------------------------------------------------------
.area VECTOR (CODE)
.globl __interrupt_vect
__interrupt_vect:
ljmp __sdcc_gsinit_startup
ljmp #(0x400+0x03)
.ds 5
ljmp #(0x400+0x0B)
.ds 5
ljmp uart0_isr_forward;
.ds 5
ljmp #(0x400+0x1B)
.ds 5
ljmp #(0x400+0x23)
.ds 5
ljmp #(0x400+0x2B)
.ds 5
ljmp #(0x400+0x33)
.ds 5
ljmp #(0x400+0x3B)
.ds 5
ljmp #(0x400+0x43)
.ds 5
ljmp #(0x400+0x4B)
.ds 5
ljmp #(0x400+0x53)
.ds 5
ljmp #(0x400+0x5B)
.ds 5
ljmp #(0x400+0x63)
.ds 5
ljmp #(0x400+0x6B)
.ds 5
ljmp #(0x400+0x73)
.ds 5
ljmp #(0x400+0x7B)
.ds 5
ljmp #(0x400+0x83)
.ds 5
ljmp #(0x400+0x8B)
.ds 5
uart0_isr_forward:
push psw
jnb psw.1, 00001$
pop psw
ljmp _uart0_isr
00001$:
pop psw
ljmp #(0x400+0x13)
;--------------------------------------------------------
; external initialized ram data
;--------------------------------------------------------
.area XISEG (XDATA)
.area HOME (CODE)
.area GSINIT0 (CODE)
.area GSINIT1 (CODE)
.area GSINIT2 (CODE)
.area GSINIT3 (CODE)
.area GSINIT4 (CODE)
.area GSINIT5 (CODE)
.area GSINIT (CODE)
.area GSFINAL (CODE)
.area CSEG (CODE)
;--------------------------------------------------------
; global & static initialisations
;--------------------------------------------------------
.area GSINIT (CODE)
.globl __sdcc_gsinit_startup
.globl __sdcc_program_startup
.globl __start__stack
.globl __mcs51_genXINIT
.globl __mcs51_genXRAMCLEAR
.globl __mcs51_genRAMCLEAR
.area GSFINAL (CODE)
.globl __sdcc_program_startup
ljmp __sdcc_program_startup
;--------------------------------------------------------
; Home
;--------------------------------------------------------
.area HOME (CODE)
.area HOME (CODE)
__sdcc_program_startup:
lcall _bootloader_main
; return from main will lock up
sjmp .