p1load/ploader.c
2015-01-25 21:37:19 -05:00

191 lines
4.8 KiB
C

#ifdef OS_msys
#include <unistd.h>
#else
#include <time.h>
#endif
#include "ploader.h"
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#define ACK_TIMEOUT 20
static int us_sleep(unsigned long usecs);
static void SerialInit(PL_state *state);
static void TByte(PL_state *state, uint8_t x);
static void TLong(PL_state *state, uint32_t x);
static void TComm(PL_state *state);
static int RBit(PL_state *state, int timeout);
static int IterateLFSR(PL_state *state);
void PL_Init(PL_state *state, PL_serial *serial, void *data)
{
state->serial = serial;
state->serialData = data;
}
/* PL_LoadSpinBinary - load a spin binary using the rom loader */
int PL_LoadSpinBinary(PL_state *state, int loadType, uint8_t *image, int size)
{
int retries = 100;
uint8_t buf[1];
int i;
TLong(state, loadType);
TLong(state, size / sizeof(uint32_t));
/* download the spin binary */
for (i = 0; i < size; i += 4) {
uint32_t data = image[i] | (image[i + 1] << 8) | (image[i + 2] << 16) | (image[i + 3] << 24);
TLong(state, data);
}
TComm(state);
us_sleep(1000);
/* wait for an ACK */
while (--retries >= 0) {
TByte(state, 0xf9);
TComm(state);
if ((*state->serial->rx_timeout)(state->serialData, buf, 1, ACK_TIMEOUT) <= 0)
continue;
if (buf[0] == 0xfe)
break;
}
return retries >= 0 ? 0 : -1;
}
static int us_sleep(unsigned long usecs)
{
struct timespec req;
req.tv_sec = (int)(usecs / 1000000L);
req.tv_nsec = (usecs - (req.tv_sec * 1000000L)) * 1000L;
while (nanosleep(&req, &req) < 0)
;
return 1;
}
/* this code is adapted from Chip Gracey's PNut IDE */
int PL_HardwareFound(PL_state *state, int *pVersion)
{
int version, i;
/* initialize the serial buffers */
SerialInit(state);
/* reset the propeller */
(*state->serial->reset)(state->serialData);
/* send the connect string + blanks for echoes */
TByte(state, 0xf9);
state->lfsr = 'P';
for (i = 0; i < 250; ++i)
TByte(state, IterateLFSR(state) | 0xfe);
for (i = 0; i < 250 + 8; ++i)
TByte(state, 0xf9);
TComm(state);
/* receive the connect string */
for (i = 0; i < 250; ++i)
if (RBit(state, 100) != IterateLFSR(state))
return FALSE;
/* receive the chip version */
for (version = i = 0; i < 8; ++i) {
int bit = RBit(state, 50);
if (bit < 0)
return FALSE;
version = ((version >> 1) & 0x7f) | (bit << 7);
}
*pVersion = version;
/* return successfully */
return TRUE;
}
/* SerialInit - initialize the serial buffers */
static void SerialInit(PL_state *state)
{
state->txcnt = 0;
state->rxnext = 0;
state->rxcnt = 0;
}
/* TByte - add a byte to the transmit buffer */
static void TByte(PL_state *state, uint8_t x)
{
state->txbuf[state->txcnt++] = x;
if (state->txcnt >= sizeof(state->txbuf))
TComm(state);
}
/* TLong - add a long to the transmit buffer */
static void TLong(PL_state *state, uint32_t x)
{
int i;
for (i = 0; i < 11; ++i) {
#if 0
// p2 code
uint8_t byte = 0x92
| ((i == 10 ? -1 : 0) & 0x60)
| ((x >> 31) & 1)
| (((x >> 30) & 1) << 3)
| (((x >> 29) & 1) << 6);
TByte(state, byte);
x <<= 3;
#else
// p1 code
uint8_t byte = 0x92
| ((i == 10 ? -1 : 0) & 0x60)
| (x & 1)
| ((x & 2) << 2)
| ((x & 4) << 4);
TByte(state, byte);
x >>= 3;
#endif
}
}
/* TComm - write the transmit buffer to the port */
static void TComm(PL_state *state)
{
(*state->serial->tx)(state->serialData, state->txbuf, state->txcnt);
state->txcnt = 0;
}
/* RBit - receive a bit with a timeout */
static int RBit(PL_state *state, int timeout)
{
int result;
for (;;) {
if (state->rxnext >= state->rxcnt) {
state->rxcnt = (*state->serial->rx_timeout)(state->serialData, state->rxbuf, sizeof(state->rxbuf), timeout);
if (state->rxcnt <= 0) {
/* hardware lost */
return -1;
}
state->rxnext = 0;
}
result = state->rxbuf[state->rxnext++] - 0xfe;
if ((result & 0xfe) == 0)
return result;
/* checksum error */
}
}
/* IterateLFSR - get the next bit in the lfsr sequence */
static int IterateLFSR(PL_state *state)
{
uint8_t lfsr = state->lfsr;
int result = lfsr & 1;
state->lfsr = ((lfsr << 1) & 0xfe) | (((lfsr >> 7) ^ (lfsr >> 5) ^ (lfsr >> 4) ^ (lfsr >> 1)) & 1);
return result;
}
/* end of code adapted from Chip Gracey's PNut IDE */