/*********************************************************************** * 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]; }