/*
Copyright (C) 2002 Ralf Forsberg
This is based on the program icdprog 0.3 made by Geir Thomassen
gpsim 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, or (at your option)
any later version.
gpsim 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 gpasm; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/*
FIXME - the design of this code needs to be revisited. Instead
of making the icd a separate entity that "controls" a
a processor, it makes MUCH more sense to derive the Icd
class from the Processor class and let it effectively
intercept and re-direct calls to the processor being
debugged. This way, the gui and cli (and even external
regression testing scripts) can treat the Icd just
like it's another processor - no special circumstances
are required.
*/
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <iostream>
#include <iomanip>
#include <string>
#include <list>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <time.h>
#include <assert.h>
#include "../config.h"
#include "pic-processor.h"
#include "icd.h"
#define BAUDRATE B57600
// Not all OSs support the O_SYNC open flag
#ifndef O_SYNC
#define O_SYNC 0
#endif
static bool use_icd = false;
static int bulk_flag = 0;
extern Processor *active_cpu;
static int icd_fd; /* file descriptor for serial port */
static int icd_sync(void);
bool get_use_icd() {
return use_icd;
}
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
class icd_Register : public Register
{
public:
Register *replaced;
int is_stale;
icd_Register();
virtual REGISTER_TYPES isa(void) {return replaced->isa();};
virtual string &name(void)
{
if(replaced)
return replaced->name();
else
return gpsimValue::name();
};
virtual void put_value(unsigned int new_value);
virtual void put(unsigned int new_value);
virtual unsigned int get_value(void);
virtual unsigned int get(void);
};
class icd_StatusReg : public Status_register
{
public:
Status_register *replaced;
int is_stale;
icd_StatusReg();
virtual REGISTER_TYPES isa(void) {return replaced->isa();};
virtual string &name(void)
{
if(replaced)
return replaced->name();
else
return gpsimValue::name();
}
virtual void put_value(unsigned int new_value);
virtual void put(unsigned int new_value);
virtual unsigned int get_value(void);
virtual unsigned int get(void);
};
class icd_WREG : public WREG
{
public:
WREG *replaced;
int is_stale;
icd_WREG();
virtual REGISTER_TYPES isa(void) {return replaced->isa();};
virtual string &name(void)
{
if(replaced)
return replaced->name();
else
return gpsimValue::name();
}
virtual void put_value(unsigned int new_value);
virtual void put(unsigned int new_value);
virtual unsigned int get_value(void);
virtual unsigned int get(void);
};
class icd_PCLATH : public PCLATH
{
public:
PCLATH *replaced;
int is_stale;
icd_PCLATH();
virtual REGISTER_TYPES isa(void) {return replaced->isa();};
virtual string &name(void)
{
if(replaced)
return replaced->name();
else
return gpsimValue::name();
}
virtual void put_value(unsigned int new_value);
virtual void put(unsigned int new_value);
virtual unsigned int get_value(void);
virtual unsigned int get(void);
};
class icd_FSR : public FSR
{
public:
FSR *replaced;
int is_stale;
icd_FSR();
virtual REGISTER_TYPES isa(void) {return replaced->isa();};
virtual string &name(void)
{
if(replaced)
return replaced->name();
else
return gpsimValue::name();
}
virtual void put_value(unsigned int new_value);
virtual void put(unsigned int new_value);
virtual unsigned int get_value(void);
virtual unsigned int get(void);
};
class icd_PC : public Program_Counter
{
public:
Program_Counter *replaced;
int is_stale;
icd_PC();
virtual void put_value(unsigned int new_value);
virtual unsigned int get_value(void);
};
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
static void udelay(unsigned usec)
{
/* wait for msec milliseconds or more ... */
struct timespec time;
time.tv_sec = usec / 1000000;
time.tv_nsec = ( usec % 1000000) * 1000;
nanosleep(&time,0);
}
static void dtr_set()
{
int flag = TIOCM_DTR;
if(icd_fd<0) return;
if(ioctl(icd_fd, TIOCMBIS, &flag)) {
perror("ioctl");
exit(-1);
}
}
static void dtr_clear()
{
int flag = TIOCM_DTR;
if(icd_fd<0) return;
if(ioctl(icd_fd, TIOCMBIC, &flag)) {
perror("ioctl");
exit(-1);
}
}
static void rts_set()
{
int flag = TIOCM_RTS;
if(icd_fd<0) return;
if(ioctl(icd_fd, TIOCMBIS, &flag)) {
perror("ioctl");
exit(-1);
}
}
static void rts_clear()
{
int flag = TIOCM_RTS;
if(icd_fd<0) return;
if(ioctl(icd_fd, TIOCMBIC, &flag)) {
perror("ioctl");
exit(-1);
}
}
void icd_hw_reset()
{
if(icd_fd<0) return;
rts_clear();
dtr_clear(); /* reset */
udelay(10000);
dtr_set(); /* remove reset */
}
static int icd_write(const char *s)
{
if(icd_fd<0)
return -1;
write(icd_fd,s, strlen(s)); /* Error checking ... */
return 1;
}
static int icd_read(unsigned char *p, int len)
{
int n_read;
n_read=read(icd_fd,p,1);
rts_clear();
udelay(1);
rts_set();
if(n_read != 1) {
cout << "Error in number of bytes read \n";
cout << "len="<<len<<endl;
return 0;
}
if(len>1)
return n_read+icd_read(p+1,len-1);
return n_read;
}
#define MAX_CMD_LEN 100
static int icd_cmd(const char *cmd, ...)
{
char command[MAX_CMD_LEN];
unsigned char resp[3];
va_list ap;
if(icd_fd<0) return -1;
va_start(ap, cmd);
(void) vsnprintf(command,MAX_CMD_LEN, cmd, ap);
va_end(ap);
icd_write(command);
if(!icd_read(resp,2))
{
icd_sync();
icd_write(command);
if(!icd_read(resp,2))
{
cout << "Command "<<command<<" failed"<<endl;
return -1;
}
}
return (resp[0] << 8 ) | resp[1];
}
static int icd_sync(void)
{
// This doesn't work FIXME.
int tries=3;
unsigned char buf[0x42];
while(tries>0)
{
tries--;
if(icd_cmd("$$6307\r")==1)
return 1;
icd_write("$");
icd_read(buf,0x42);
}
puts("***************** DID NOT SYNC!");
return 0;
}
static int icd_baudrate_init()
{
int tries=3;
char ch;
if(icd_fd<0) return 0;
while(tries) {
write(icd_fd,"U",1);
if(read(icd_fd,&ch,1) > 0) {
rts_clear();
udelay(10);
rts_set();
if(ch=='u') {
return 1;
}
}
tries--;
}
return 0;
}
char *icd_target(void)
{
static char return_string[256];
unsigned int dev_id, type,rev;
if(icd_fd<0) return 0;
dev_id=icd_cmd("$$7020\r");
type = (dev_id>>5) & 0x1FF;
rev = type & 0x1F;
if(dev_id == 0x3FFF) {
sprintf(return_string,"no target");
} else {
switch(type) {
case 0x68:
sprintf(return_string,"16F870 rev %d",rev);
break;
case 0x69:
sprintf(return_string,"16F871 rev %d",rev);
break;
case 0x47:
sprintf(return_string,"16F872 rev %d",rev);
break;
case 0x4B:
sprintf(return_string,"16F873 rev %d",rev);
break;
case 0x49:
sprintf(return_string,"16F874 rev %d",rev);
break;
case 0x4F:
sprintf(return_string,"16F876 rev %d",rev);
break;
case 0x4D:
sprintf(return_string,"16F877 rev %d",rev);
break;
default:
sprintf(return_string,"Unknown, device id = %02X",dev_id);
break;
}
}
return return_string;
}
struct termios oldtio, newtio;
void put_dumb_register(Register **frp, int address)
{
Register *fr = *frp;
icd_Register *ir = new icd_Register;
ir->set_cpu(fr->get_cpu());
*frp = ir;
ir->replaced = fr;
ir->address = address;
}
void put_dumb_status_register(Status_register **frp)
{
Status_register *fr = *frp;
icd_StatusReg *ir = new icd_StatusReg;
ir->set_cpu(fr->get_cpu());
*frp = ir;
ir->replaced = fr;
ir->address = fr->address;
}
void put_dumb_pc_register(Program_Counter **frp)
{
Program_Counter *fr = *frp;
icd_PC *ir = new icd_PC;
ir->set_cpu(fr->get_cpu());
ir->memory_size_mask = fr->memory_size_mask;
*frp = ir;
ir->replaced = fr;
}
void put_dumb_pclath_register(PCLATH **frp)
{
PCLATH *fr = *frp;
icd_PCLATH *ir = new icd_PCLATH;
ir->set_cpu(fr->get_cpu());
*frp = ir;
ir->replaced = fr;
}
void put_dumb_w_register(WREG **frp)
{
WREG *fr = *frp;
icd_WREG *ir = new icd_WREG;
ir->set_cpu(fr->get_cpu());
*frp = ir;
ir->replaced = fr;
}
void put_dumb_fsr_register(FSR **frp)
{
FSR *fr = *frp;
icd_FSR *ir = new icd_FSR;
ir->set_cpu(fr->get_cpu());
*frp = ir;
ir->replaced = fr;
}
static void create_dumb_register_file(void)
{
pic_processor *cpu=dynamic_cast<pic_processor *>(active_cpu);
if(!cpu)
return;
for(unsigned int i=0;i<cpu->register_memory_size();i++)
{
put_dumb_register(&cpu->registers[i], i);
}
put_dumb_status_register(&cpu->status);
put_dumb_pc_register(&cpu->pc);
put_dumb_pclath_register(&cpu->pclath);
put_dumb_w_register(&cpu->W);
put_dumb_fsr_register(&cpu->fsr);
}
int icd_connect(char *port)
{
pic_processor *pic=dynamic_cast<pic_processor *>(active_cpu);
if(!pic)
{
cout << "You have to load the .cod file (or .hex and processor)" << endl;
return 0;
}
if((icd_fd=open(port, O_NOCTTY | O_RDWR | O_SYNC)) == -1) {
perror("Error opening device:");
return 0;
}
tcgetattr(icd_fd, &oldtio);
memset(&newtio,0, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = 100;
newtio.c_cc[VMIN] = 0;
tcflush(icd_fd, TCIFLUSH);
tcsetattr(icd_fd, TCSANOW, &newtio);
icd_hw_reset();
rts_set();
if(!icd_baudrate_init()) {
fprintf(stderr,"Can't initialize the ICD\n");
return 0;
}
create_dumb_register_file();
use_icd=true;
icd_cmd("$$6300\r");
/* I really don't know what this is, but MPLAB does
this. The program works ok without this though ..*/
// printf("ICD ver %s, target %s\n",icd_version(),icd_target());
// printf("Vdd %.2f, Vpp %.2f\n", icd_vdd(), icd_vpp());
if(icd_has_debug_module())
{
if(verbose)
cout << "Debug module present"<<endl;
}
else
{
cout << "Debug module not present. Enabling..."<<flush;
icd_cmd("$$7008\r"); // Enable debug module
cout << "Done." << endl;
}
icd_reset();
return 1;
}
int icd_has_debug_module(void)
{
if(icd_fd<0) return 0;
icd_cmd("$$700A\r");
if(icd_cmd("$$6307\r")==1)
return 1;
return 0;
}
// Call when gpsim exits.
int icd_disconnect(void)
{
if(icd_fd<0) return 0;
cout << "ICD disconnect" << endl;
icd_hw_reset();
close(icd_fd);
return 1;
}
static void make_stale(void)
{
if(icd_fd<0) return;
pic_processor *cpu=dynamic_cast<pic_processor *>(active_cpu);
if(!cpu)
return;
for(unsigned int i=0;i<cpu->register_memory_size();i++)
{
icd_Register *ir = dynamic_cast<icd_Register*>(cpu->registers[i]);
assert(ir!=0);
ir->is_stale=1;
}
icd_WREG *iw = dynamic_cast<icd_WREG*>(cpu->W);
assert(iw!=0);
iw->is_stale=1;
icd_PC *ipc = dynamic_cast<icd_PC*>(cpu->pc);
assert(ipc!=0);
ipc->is_stale=1;
icd_PCLATH *ipclath = dynamic_cast<icd_PCLATH*>(cpu->pclath);
assert(ipclath!=0);
ipclath->is_stale=1;
icd_FSR *ifsr = dynamic_cast<icd_FSR*>(cpu->fsr);
assert(ifsr!=0);
ifsr->is_stale=1;
icd_StatusReg *isreg = dynamic_cast<icd_StatusReg*>(cpu->status);
assert(isreg!=0);
isreg->is_stale=1;
}
int icd_reset(void)
{
if(icd_fd<0) return 0;
cout << "Reset" << endl;
icd_cmd("$$700A\r");
icd_cmd("$$701B\r");
make_stale();
pic_processor *pic=dynamic_cast<pic_processor *>(active_cpu);
if(!pic)
return 0;
pic->pc->get_value();
gi.simulation_has_stopped();
return 1;
}
int icd_detected(void)
{
if(icd_fd<0) return 0;
if(use_icd)
return 1;
return 0;
}
char *icd_version(void)
{
static char ret[256];
unsigned int ver1,ver2;
if(icd_fd<0) return 0;
ver1 = icd_cmd("$$7F00\r");
ver2 = icd_cmd("$$7021\r");
sprintf(ret, "%X.%02X.%02X", ver1>>8, ver1&0xFF, ver2);
return ret;
}
float icd_vdd(void)
{
unsigned int vdd=0;
if(icd_fd<0) return 0.0;
vdd=icd_cmd("$$701C\r");
return ((double)vdd) / 40.0;
}
float icd_vpp(void)
{
unsigned int vpp=0;
if(icd_fd<0) return 0.0;
icd_cmd("$$7000\r"); // enable Vpp
vpp=icd_cmd("$$701D\r") & 0xFF; // What the heck does the high byte contain ?
icd_cmd("$$7001\r"); // disable Vpp
return ((double)vpp) / 11.25;
}
int icd_step(void)
{
if(icd_fd<0) return 0;
make_stale();
icd_cmd("$$700E\r");
return 1;
}
int icd_run(void)
{
if(icd_fd<0) return 0;
make_stale();
if(icd_cmd("$$700F\r")!=1)
{
icd_sync();
if(icd_cmd("$$700F\r")!=1)
cout << "fjsdk" << endl;
}
return 1;
}
int icd_stopped(void)
{
if(icd_fd<0) return 0;
if(icd_cmd("$$701E\r")!=1)
return 1;
return 0;
}
int icd_halt(void)
{
if(icd_fd<0) return 0;
make_stale();
icd_cmd("$$700D\r");
return 1;
}
int icd_set_break(int address)
{
if(icd_fd<0) return 0;
cout << "Set breakpoint on address " << address << endl;
icd_cmd("$$1F00\r");
if(icd_cmd("$$%04X\r",address)!=address)
{
puts("DEBUG: Set breakpoint failed?");
return 0;
}
return 1;
}
int icd_clear_break(void)
{
if(icd_fd<0) return 0;
cout << "Clear breakpoints" << endl;
icd_cmd("$$1F00\r");
return 1;
}
void icd_set_bulk(int flag)
{
bulk_flag=flag;
}
// Get the value of the specified file memory address
/*int icd_read_file(int address)
{
unsigned char buf[8];
int offset = address - address%8;
if(icd_fd<0) return 0;
int value;
cout << "this is deprecated" << endl;
icd_cmd("$$%04X\r",0x7800+offset);
icd_cmd("$$7C08\r");
icd_write("$$7D08\r");
icd_read(buf,8);
value = buf[address%8];
pic_processor *pic=dynamic_cast<pic_processor *>(active_cpu);
if(!pic)
return;
//if(gpsim_register_is_valid(1,REGISTER_RAM,address) &&
// !gpsim_register_is_alias(1,REGISTER_RAM,address))
{
switch(address)
{
case 2:
case 3:
case 4:
case 10:
break;
default:
pic->registers[address]->put_value(value);
cout << "Read file address " << address << "=" << value << endl;
break;
}
}
return 1;
}
int icd_write_file(int address, int data)
{
if(icd_fd<0) return 0;
printf("Write file address 0x%04X with data 0x%02X\n",address,data);
return 1;
}
int icd_read_eeprom(int address)
{
if(icd_fd<0) return 0;
printf("Read eeprom address 0x%04X\n",address);
return 1;
}
int write_eeprom(int address, int data)
{
if(icd_fd<0) return 0;
printf("Write eeprom address 0x%04X with data 0x%02X\n",address,data);
return 1;
}
int icd_get_state()
{
int pc=4, status, w, pclath, fsr;
if(icd_fd<0) return 0;
cout << "Get state" << endl;
pc=icd_cmd("$$701F\r");
status=icd_cmd("$$7016\r")&0x00ff;
w=icd_cmd("$$7017\r")&0x00ff;
pclath=icd_cmd("$$7018\r")&0x00ff;
fsr=icd_cmd("$$7019\r")&0x00ff;
pic_processor *pic=dynamic_cast<pic_processor *>(active_cpu);
if(!pic)
return;
pic->pc->put_value(pc);
pic->status->put_value(status);
pic->W->put_value(w);
pic->pclath->put_value(pclath);
pic->fsr->put_value(fsr);
return 1;
}
*/
// Get all of file memory
/*int icd_get_file()
{
unsigned char buf[64];
if(icd_fd<0) return 0;
cout << "Get file" << endl;
pic_processor *pic=dynamic_cast<pic_processor *>(active_cpu);
if(!pic)
return;
for(int i=0;i<pic->register_memory_size()/0x40;i++)
{
if(icd_cmd("$$%04X\r",0x7A00+i)!=i)
puts("EEEEEEEEEEEEEEEEEEEEE");
icd_write("$$7D40\r");
icd_read(buf,64);
for(int j=0;j<64;j++)
{
if(gpsim_register_is_valid(1,REGISTER_RAM,i*0x40+j) &&
!gpsim_register_is_alias(1,REGISTER_RAM,i*0x40+j))
{
switch(i*0x40+j)
{
case 2:
case 3:
case 4:
case 10:
break;
default:
pic->registers[i*0x40+j]->put_value(buf[j]);
break;
}
}
}
// }
return 1;
}
*/
icd_Register::icd_Register()
{
replaced=0;
value.put(0x42);
is_stale=1;
};
void icd_Register::put_value(unsigned int new_value)
{
}
void icd_Register::put(unsigned int new_value)
{
}
unsigned int icd_Register::get_value(void)
{
return(get());
}
unsigned int icd_Register::get(void)
{
if(is_stale)
{
switch(address)
{
case 2:
value.put(icd_cmd("$$701F\r"));
cpu_pic->pcl->value.put(value.get() & 0xff);
cpu_pic->pclath->value.put(value.get() >> 8);
is_stale=0;
break;
case 3:
value.put(icd_cmd("$$7016\r")&0x00ff);
is_stale=0;
replaced->update();
break;
case 4:
value.put(icd_cmd("$$7019\r")&0x00ff);
is_stale=0;
replaced->update();
break;
case 10:
value.put(icd_cmd("$$701F\r"));
cpu_pic->pcl->value.put(value.get() & 0xff);
cpu_pic->pclath->value.put(value.get() >> 8);
is_stale=0;
break;
default:
{
if(bulk_flag==0)
{
unsigned char buf[8];
int offset = address - address%8;
icd_cmd("$$%04X\r",0x7800+offset);
icd_cmd("$$7C08\r");
icd_write("$$7D08\r");
icd_read(buf,8);
for(int i=0;i<8;i++)
{
switch(offset+i)
{
case 2:
case 3:
case 4:
case 10:
break;
default:
icd_Register *ifr = static_cast<icd_Register*>(get_cpu()->registers[offset+i]);
assert(ifr!=0);
ifr->value.put(buf[i]);
ifr->is_stale=0;
break;
}
}
for(int i=0;i<0x8;i++)
{
switch(offset+i)
{
case 2:
case 3:
case 4:
case 10:
break;
default:
icd_Register *ifr = static_cast<icd_Register*>(get_cpu()->registers[offset+i]);
assert(ifr!=0);
ifr->replaced->update();
break;
}
}
}
else
{
unsigned char buf[64];
int offset=address-address%0x40;
assert(offset>=0);
if(icd_cmd("$$%04X\r",0x7A00+offset/0x40)!=offset/0x40)
puts("DDDDDDDDDDDDDDDDDDD");
icd_write("$$7D40\r");
int n_read = icd_read(buf,0x40);
for(unsigned int i=0;i<0x40;i++)
{
switch(offset+i)
{
case 2:
case 3:
case 4:
case 10:
break;
default:
icd_Register *ifr = static_cast<icd_Register*>(get_cpu()->registers[offset+i]);
assert(ifr!=0);
ifr->value.put(buf[i]);
ifr->is_stale=0;
break;
}
}
for(int i=0;i<0x40;i++)
{
switch(offset+i)
{
case 2:
case 3:
case 4:
case 10:
break;
default:
icd_Register *ifr = static_cast<icd_Register*>(get_cpu()->registers[offset+i]);
assert(ifr!=0);
ifr->replaced->update();
break;
}
}
}
}
break;
}
}
return(value.get());
}
icd_WREG::icd_WREG()
{
replaced=0;
value.put(0x42);
is_stale=1;
};
void icd_WREG::put_value(unsigned int new_value)
{
}
void icd_WREG::put(unsigned int new_value)
{
}
unsigned int icd_WREG::get_value(void)
{
return(get());
}
unsigned int icd_WREG::get(void)
{
if(is_stale)
{
value.put(icd_cmd("$$7017\r")&0x00ff);
is_stale=0;
replaced->update();
}
return(value.get());
}
icd_StatusReg::icd_StatusReg()
{
replaced=0;
value.put(0x42);
is_stale=1;
};
void icd_StatusReg::put_value(unsigned int new_value)
{
}
void icd_StatusReg::put(unsigned int new_value)
{
}
unsigned int icd_StatusReg::get_value(void)
{
if(icd_fd<0) return 0;
return(get());
}
unsigned int icd_StatusReg::get(void)
{
if(is_stale)
{
value.put(icd_cmd("$$7016\r")&0x00ff);
is_stale=0;
replaced->update();
}
return(value.get());
}
icd_FSR::icd_FSR()
{
replaced=0;
value.put(0x42);
is_stale=1;
};
void icd_FSR::put(unsigned int new_value)
{
}
void icd_FSR::put_value(unsigned int new_value)
{
}
unsigned int icd_FSR::get(void)
{
return get_value();
}
unsigned int icd_FSR::get_value(void)
{
if(icd_fd<0) return 0;
if(is_stale)
{
value.put(icd_cmd("$$7019\r")&0x00ff);
is_stale=0;
replaced->update();
}
return(value.get());
}
icd_PCLATH::icd_PCLATH()
{
replaced=0;
value.put(0x42);
is_stale=1;
};
void icd_PCLATH::put(unsigned int new_value)
{
}
void icd_PCLATH::put_value(unsigned int new_value)
{
}
unsigned int icd_PCLATH::get(void)
{
return get_value();
}
unsigned int icd_PCLATH::get_value(void)
{
if(icd_fd<0) return 0;
if(is_stale)
{
value.put((icd_cmd("$$701F\r")&0xff00)>>8);
is_stale=0;
replaced->update();
}
return(value.get());
}
icd_PC::icd_PC()
{
replaced=0;
value=0x42;
is_stale=1;
};
void icd_PC::put_value(unsigned int new_value)
{
}
unsigned int icd_PC::get_value(void)
{
if(icd_fd<0) return 0;
if(is_stale)
{
value = icd_cmd("$$701F\r");
cpu_pic->pcl->value.put(value & 0xff);
cpu_pic->pclath->value.put(value >> 8);
is_stale=0;
}
return(value);
}
syntax highlighted by Code2HTML, v. 0.9.1