/***********************************************************************
* Copyright (c) 2014-2016 Ioannis Charalampidis
* Copyright (c) 2015 Simon Schulz - github.com/fishpepper
Copyright © 2019 Jean Michault.
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 3 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, see .
*************************************************************************/
#include
#include
#include
#include
#include
#include
#include "CCDebugger.h"
/**
* Switch reset pin
*/
void cc_setDDDirection( uint8_t direction );
/**
* Software-overridable instruction table that can be used
* for supporting other CCDebug-Compatible chips purely by software
*/
uint8_t instr[16];
/**
* Local properties
*/
int pinRST= PIN_RST;
int pinDC= PIN_DC;
int pinDD= PIN_DD;
uint8_t errorFlag=0;
uint8_t ddIsOutput=false;
uint8_t inDebugMode=false;
uint8_t cc_active=false;
/**
* Instruction table indices
*/
#define INSTR_VERSION 0
#define I_HALT 1
#define I_RESUME 2
#define I_RD_CONFIG 3
#define I_WR_CONFIG 4
#define I_DEBUG_INSTR_1 5
#define I_DEBUG_INSTR_2 6
#define I_DEBUG_INSTR_3 7
#define I_GET_CHIP_ID 8
#define I_GET_PC 9
#define I_READ_STATUS 10
#define I_STEP_INSTR 11
#define I_CHIP_ERASE 12
#define I_SET_HW_BRKPNT 13
#define I_GET_BM 14
#define I_BURST_WRITE 15
void cc_delay_calibrate( );
int cc_init( int pRST, int pDC, int pDD )
{
if(pRST>=0) pinRST=pRST;
if(pDC>=0) pinDC=pDC;
if(pDD>=0) pinDD=pDD;
if(wiringPiSetup() == -1){
printf("no wiring pi detected\n");
return 0;
}
cc_delay_calibrate();
// Prepare CC Pins
pinMode(pinDC, OUTPUT);
pinMode(pinDD, OUTPUT);
pinMode(pinRST, OUTPUT);
digitalWrite(pinDC, LOW);
digitalWrite(pinDD, LOW);
digitalWrite(pinRST, LOW);
// Prepare default direction
cc_setDDDirection(INPUT);
// Default CCDebug instruction set for CC254x
instr[INSTR_VERSION] = 1;
instr[I_HALT] = 0x40;
instr[I_RESUME] = 0x48;
instr[I_RD_CONFIG] = 0x20;
instr[I_WR_CONFIG] = 0x18;
instr[I_DEBUG_INSTR_1] = 0x51;
instr[I_DEBUG_INSTR_2] = 0x52;
instr[I_DEBUG_INSTR_3] = 0x53;
instr[I_GET_CHIP_ID] = 0x68;
instr[I_GET_PC] = 0x28;
instr[I_READ_STATUS] = 0x30;
instr[I_STEP_INSTR] = 0x58;
instr[I_CHIP_ERASE] = 0x10;
// We are active by default
cc_active = true;
return 1;
};
/**
* Activate/Deactivate debugger
*/
void cc_setActive( uint8_t on )
{
// Reset error flag
errorFlag = CC_ERROR_NONE;
// Continue only if active
if (on == cc_active) return;
cc_active = on;
if (on) {
// Prepare CC Pins
pinMode(pinDC, OUTPUT);
pinMode(pinDD, OUTPUT);
pinMode(pinRST, OUTPUT);
digitalWrite(pinDC, LOW);
digitalWrite(pinDD, LOW);
digitalWrite(pinRST, LOW);
// Default direction is INPUT
cc_setDDDirection(INPUT);
} else {
// Before deactivating, exit debug mode
if (inDebugMode)
cc_exit();
// Put everything in inactive mode
pinMode(pinDC, INPUT);
pinMode(pinDD, INPUT);
pinMode(pinRST, INPUT);
digitalWrite(pinDC, LOW);
digitalWrite(pinDD, LOW);
digitalWrite(pinRST, LOW);
}
}
/**
* Return the error flag
*/
uint8_t cc_error()
{
return errorFlag;
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
//// LOW LEVEL FUNCTIONS ////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/**
* Delay a particular number of cycles
*/
struct timespec tp={0,0};
static int cc_delay_mult=80;
/* delay d * .4 us */
void cc_delay( unsigned int d )
{
volatile unsigned int i = cc_delay_mult*d;
while( i-- );
//tp.tv_nsec=40*d;
//nanosleep(&tp,NULL);
}
void cc_setmult(int mult)
{
cc_delay_mult=mult;
}
/* provas konsideri la rapidecon de la procesoro */
void cc_delay_calibrate( )
{
#if 1
/* The ioctl overhead on FreeBSD far exceeds min timing */
cc_delay_mult = 0;
#else
long time0=micros();
cc_delay(200);
cc_delay(200);
cc_delay(200);
cc_delay(200);
cc_delay(200);
long time1=micros();
/* make 1000 delay units == 400us */
cc_delay_mult=cc_delay_mult*400/(time1-time0);
printf("time0: %ld, time1: %ld, time1-time0: %ld\n", time0, time1, time1 - time0);
printf("dmul: %d\n", cc_delay_mult);
#endif
}
/**
* Enter debug mode
*/
uint8_t cc_enter()
{
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
// =============
// Reset error flag
errorFlag = CC_ERROR_NONE;
// Enter debug mode
digitalWrite(pinRST, LOW);
cc_delay(200);
digitalWrite(pinDC, HIGH);
cc_delay(3);
digitalWrite(pinDC, LOW);
cc_delay(3);
digitalWrite(pinDC, HIGH);
cc_delay(3);
digitalWrite(pinDC, LOW);
cc_delay(4);
digitalWrite(pinRST, HIGH);
cc_delay(200);
// We are now in debug mode
inDebugMode = 1;
// =============
// Success
return 0;
};
/**
* Write a uint8_t to the debugger
*/
uint8_t cc_write( uint8_t data )
{
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
};
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
// =============
uint8_t cnt;
// Make sure DD is on output
cc_setDDDirection(OUTPUT);
// Sent uint8_ts
for (cnt = 8; cnt; cnt--) {
// First put data bit on bus
if (data & 0x80)
digitalWrite(pinDD, HIGH);
else
digitalWrite(pinDD, LOW);
// Place clock on high (other end reads data)
digitalWrite(pinDC, HIGH);
// Shift & Delay
data <<= 1;
cc_delay(2);
// Place clock down
digitalWrite(pinDC, LOW);
cc_delay(2);
}
// =============
return 0;
}
/**
* Wait until input is ready for reading
*/
uint8_t cc_switchRead(uint8_t maxWaitCycles)
{
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
// =============
uint8_t cnt;
uint8_t didWait = 0;
// Switch to input
cc_setDDDirection(INPUT);
// Wait at least 83 ns before checking state t(dir_change)
cc_delay(2);
// Wait for DD to go LOW (Chip is READY)
while (digitalRead(pinDD) == HIGH) {
// Do 8 clock cycles
for (cnt = 8; cnt; cnt--) {
digitalWrite(pinDC, HIGH);
cc_delay(2);
digitalWrite(pinDC, LOW);
cc_delay(2);
}
// Let next function know that we did wait
didWait = 1;
// Check if we ran out if wait cycles
if (!--maxWaitCycles) {
// If we are waiting for too long, we have lost the chip,
// so also assume we are out of debugging mode
errorFlag = CC_ERROR_NOT_WIRED;
inDebugMode = 0;
return 0;
}
}
// Wait t(sample_wait)
if (didWait) cc_delay(2);
// =============
return 0;
}
/**
* Switch to output
*/
uint8_t cc_switchWrite()
{
cc_setDDDirection(OUTPUT);
return 0;
}
/**
* Read an input uint8_t
*/
uint8_t cc_read()
{
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
// =============
uint8_t cnt;
uint8_t data = 0;
// Switch to input
cc_setDDDirection(INPUT);
// Send 8 clock pulses if we are HIGH
for (cnt = 8; cnt; cnt--) {
digitalWrite(pinDC, HIGH);
cc_delay(2);
// Shift and read
data <<= 1;
if (digitalRead(pinDD) == HIGH)
data |= 0x01;
digitalWrite(pinDC, LOW);
cc_delay(2);
}
// =============
return data;
}
/**
* Switch reset pin
*/
void cc_setDDDirection( uint8_t direction )
{
// Switch direction if changed
if (direction == ddIsOutput) return;
ddIsOutput = direction;
// Handle new direction
if (ddIsOutput) {
digitalWrite(pinDD, LOW); // Disable pull-up
pinMode(pinDD, OUTPUT); // Enable output
digitalWrite(pinDD, LOW); // Switch to low
} else {
digitalWrite(pinDD, LOW); // Disable pull-up
pinMode(pinDD, INPUT); // Disable output
digitalWrite(pinDD, LOW); // Don't use output pull-up
}
}
void cc_reset()
{
pinMode(pinDC, INPUT);
pinMode(pinDD, INPUT);
pinMode(pinRST, OUTPUT);
cc_delay(200);
pinMode(pinRST, INPUT);
cc_delay(500);
pinMode(pinRST, INPUT);
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
//// HIGH LEVEL FUNCTIONS ////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/**
* Exit from debug mode
*/
uint8_t cc_exit()
{
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
uint8_t bAns;
cc_write( instr[I_RESUME] ); // RESUME
cc_switchRead(250);
bAns = cc_read(); // debug status
cc_switchWrite();
inDebugMode = 0;
return 0;
}
/**
* Get debug configuration
*/
uint8_t cc_getConfig() {
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
uint8_t bAns;
cc_write( instr[I_RD_CONFIG] ); // RD_CONFIG
cc_switchRead(250);
bAns = cc_read(); // Config
cc_switchWrite();
return bAns;
}
/**
* Set debug configuration
*/
uint8_t cc_setConfig( uint8_t config ) {
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
uint8_t bAns;
cc_write( instr[I_WR_CONFIG] ); // WR_CONFIG
cc_write( config );
cc_switchRead(250);
bAns = cc_read(); // Config
cc_switchWrite();
return bAns;
}
/**
* Invoke a debug instruction with 1 opcode
*/
uint8_t cc_exec( uint8_t oc0 )
{
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
uint8_t bAns;
cc_write( instr[I_DEBUG_INSTR_1] ); // DEBUG_INSTR + 1b
cc_write( oc0 );
cc_switchRead(250);
bAns = cc_read(); // Accumulator
cc_switchWrite();
return bAns;
}
/**
* Invoke a debug instruction with 2 opcodes
*/
uint8_t cc_exec2( uint8_t oc0, uint8_t oc1 )
{
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
uint8_t bAns;
cc_write( instr[I_DEBUG_INSTR_2] ); // DEBUG_INSTR + 2b
cc_write( oc0 );
cc_write( oc1 );
cc_switchRead(250);
bAns = cc_read(); // Accumulator
cc_switchWrite();
return bAns;
}
/**
* Invoke a debug instruction with 3 opcodes
*/
uint8_t cc_exec3( uint8_t oc0, uint8_t oc1, uint8_t oc2 )
{
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
uint8_t bAns;
cc_write( instr[I_DEBUG_INSTR_3] ); // DEBUG_INSTR + 3b
cc_write( oc0 );
cc_write( oc1 );
cc_write( oc2 );
cc_switchRead(250);
bAns = cc_read(); // Accumulator
cc_switchWrite();
return bAns;
}
/**
* Invoke a debug instruction with 1 opcode + 16-bit immediate
*/
uint8_t cc_execi( uint8_t oc0, unsigned short c0 )
{
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
uint8_t bAns;
cc_write( instr[I_DEBUG_INSTR_3] ); // DEBUG_INSTR + 3b
cc_write( oc0 );
cc_write( (c0 >> 8) & 0xFF );
cc_write( c0 & 0xFF );
cc_switchRead(250);
bAns = cc_read(); // Accumulator
cc_switchWrite();
return bAns;
}
/**
* Return chip ID
*/
unsigned short cc_getChipID() {
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
unsigned short bAns;
uint8_t bRes;
cc_write( instr[I_GET_CHIP_ID] ); // GET_CHIP_ID
cc_switchRead(250);
bRes = cc_read(); // High order
bAns = bRes << 8;
bRes = cc_read(); // Low order
bAns |= bRes;
cc_switchWrite();
return bAns;
}
/**
* Return PC
*/
unsigned short cc_getPC() {
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
unsigned short bAns;
uint8_t bRes;
cc_write( instr[I_GET_PC] ); // GET_PC
cc_switchRead(250);
bRes = cc_read(); // High order
bAns = bRes << 8;
bRes = cc_read(); // Low order
bAns |= bRes;
cc_switchWrite();
return bAns;
}
/**
* Return debug status
*/
uint8_t cc_getStatus() {
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
uint8_t bAns;
cc_write( instr[I_READ_STATUS] ); // READ_STATUS
cc_switchRead(250);
bAns = cc_read(); // debug status
cc_switchWrite();
return bAns;
}
/**
* Step instruction
*/
uint8_t cc_step() {
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
uint8_t bAns;
cc_write( instr[I_STEP_INSTR] ); // STEP_INSTR
cc_switchRead(250);
bAns = cc_read(); // Accumulator
cc_switchWrite();
return bAns;
}
/**
* resume instruction
*/
uint8_t cc_resume() {
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
uint8_t bAns;
cc_write( instr[I_RESUME] ); //RESUME
cc_switchRead(250);
bAns = cc_read(); // Accumulator
cc_switchWrite();
return bAns;
}
/**
* halt instruction
*/
uint8_t cc_halt() {
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
}
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
uint8_t bAns;
cc_write( instr[I_HALT] ); //HALT
cc_switchRead(250);
bAns = cc_read(); // Accumulator
cc_switchWrite();
return bAns;
}
/**
* Mass-erase all chip configuration & Lock Bits
*/
uint8_t cc_chipErase()
{
if (!cc_active) {
errorFlag = CC_ERROR_NOT_ACTIVE;
return 0;
};
if (!inDebugMode) {
errorFlag = CC_ERROR_NOT_DEBUGGING;
return 0;
}
uint8_t bAns;
cc_write( instr[I_CHIP_ERASE] ); // CHIP_ERASE
cc_switchRead(250);
bAns = cc_read(); // Debug status
cc_switchWrite();
return bAns;
}
/**
* Update the debug instruction table
*/
uint8_t cc_updateInstructionTable( uint8_t newTable[16] )
{
// Copy table entries
for (uint8_t i=0; i<16; i++)
instr[i] = newTable[i];
// Return the new version
return instr[INSTR_VERSION];
}
/**
* Get the instruction table version
*/
uint8_t cc_getInstructionTableVersion()
{
// Return version of instruction table
return instr[INSTR_VERSION];
}