· 

ATtinyのUSIでARM Serial Wire Debug(2)

前回あまりにも稚拙なコードだったので、書き直した。

flipはルックアップテーブル法(ていうのかな?)とグルーピングしたビットシフト法(ていうのかな?)とを併用。どっちかだけでもできるんだけど、演算時間とコード領域占有量から併用したほうが効率よさそうだなって、、、実際のところコード量がだいぶ少ないし。

パリティは半分にずらしながらXORする方法で。これは調べればすぐわかる方法。

SWDコマンドも定数テーブル化して、いちいち判断しないようにした。

AP-IDRまで読めるようになったので、保管しておく。

#include <avr/iotnx4.h>

#define UART_DEBUG 1
#if (UART_DEBUG==1)
char dbgstr[40];
#endif
typedef union {
    uint32_t asU32;
    uint8_t asU8[4];
} u32u8_t;

/*SWD command definition*/
#define APnDP_DP (0)
#define APnDP_AP (1)
char APnDP_str[2][4]={"DP\0","AP\0"};
#define RnW_W (0)
#define RnW_R (1)
char RnW_str[2][3]={"W\0","R\0"};
#define TXorRX_TX (0)
#define TXorRX_RX (1)

#define ACK_OK           (0x80)
#define ACK_WAIT         (0x40)
#define ACK_FAULT        (0x20)
#define ACK_DISCONNECT_H (0xE0)
#define ACK_DISCONNECT_L (0x00)
#define ACK_MASK         (0xE0)

#define PIN_SWDI (0x40)
#define PIN_SWDO (0x20)
#define PIN_SWCLK (0x10)
#define PIN_NRST (0x08)

volatile uint8_t txCompleted;
volatile uint8_t rxUSIDR;
uint8_t txBuf[32];
uint8_t rxBuf[32];

ISR(TIM1_COMPA_vect){
    USICR|=0x01;/*Toggle Clock Port Pin*/
}
ISR(USI_OVF_vect){
    TIMSK1&=~(0x02);/*clear Timer/Counter1 Output Compare A Match Interrupt Enable*/
    TCCR1B&=0xF8;
    USISR=0x40;/*Write 1 to clear Counter Overflow Interrupt Flag*/
    txCompleted=1;
    rxUSIDR=USIDR;
}

uint8_t USISR_val[]={0xEE,0xEC,0xEA,0xE8,0xE6,0xE4,0xE2,0xE0};

void Send_or_Receive_Data(uint8_t tx_or_rx,uint8_t dataLength_in_bit,uint8_t NRST_const){
    uint8_t data_num_remain,byte_pos,data_num_tx;
    rxBuf[0]=0x00;
    if(txCompleted==1){
        PORTA|=(PIN_SWDO|PIN_SWCLK);
        if(NRST_const){
            PORTA|=PIN_NRST;
        }else{
            PORTA&=~PIN_NRST;
        }
        data_num_remain=dataLength_in_bit;
        byte_pos=0;
        while(data_num_remain>0){
            data_num_tx=data_num_remain;
            if(data_num_remain>8){
                data_num_tx=8;
            }
            data_num_remain=data_num_remain-data_num_tx;
            if(tx_or_rx==TXorRX_RX){//Rx
                USICR=0x5E;
                txBuf[byte_pos]=0xFF;
            }else{//Tx
                USICR=0x5A;
            }
            
            USIDR = txBuf[byte_pos];
            USISR=USISR_val[data_num_tx-1];
            txCompleted=0;
            TIFR1|=0x02;
            TCCR1B|=0x01;
            TIMSK1=0x02;
            while(txCompleted==0){
                //
            }
            rxBuf[byte_pos]=rxUSIDR<<(8-data_num_tx);
            byte_pos=byte_pos+1;
        }
    }
}

uint8_t flip4_table[]={0x00,0x08,0x04,0x0C,0x02,0x0A,0x06,0x0E,0x01,0x09,0x05,0x0D,0x03,0x0B,0x07,0x0F};
uint8_t flip_8bit(uint8_t src,uint8_t num_bit){
    uint8_t ret;
    ret=(flip4_table[src&0x0F]<<4)|(flip4_table[(src>>4)&0x0F]);
    return ret<<(8-num_bit);
}

void JTAG_to_SWD(uint8_t NRST_const){
    int i,j;
    for(i=0,j=0;j<7;i++,j++){
        txBuf[i]=0x00;
    }
    for(j=0;j<7;i++,j++){
        txBuf[i]=0xFF;
    }
    txBuf[i+1]=flip_8bit(0xE7,8);
    txBuf[i]=flip_8bit(0x9E,8);
    i=i+2;
    for(j=0;j<7;i++,j++){
        txBuf[i]=0xFF;
    }
    txBuf[i-1]=0xF0;
    Send_or_Receive_Data(TXorRX_TX,i*8,NRST_const);
}
void SWD_SYNC(uint8_t NRST_const){
    txBuf[0]=0x00;
    Send_or_Receive_Data(TXorRX_TX,8,NRST_const);
}

uint32_t CalcParity32(uint32_t src){
    src^=(src>>16);
    src^=(src>>8);
    src^=(src>>4);
    src^=(src>>2);
    src^=(src>>1);
    return (src&0x00000001);
}

/*cmd database - [APnDP][RnW][ADDR>>2]*/
uint8_t SWDCMD[2][2][4]={{{0x81,0x95,0x8D,0x99},{0xA5,0xB1,0xA9,0xBD}},{{0xC5,0xD1,0xC9,0xDD},{0xE1,0xF5,0xED,0xF9}}};
uint8_t SendSWDCommand_and_ReceiveAck(uint8_t APnDP,uint8_t RnW,uint8_t ADDR,uint8_t NRST_const){
    txBuf[0]=SWDCMD[APnDP&0x01][RnW&0x01][(ADDR>>2)&0x03];
    txBuf[1]=0xFF;
    Send_or_Receive_Data(TXorRX_TX,9,NRST_const);//Cmd+Trn
    Send_or_Receive_Data(TXorRX_RX,3,NRST_const);//ACK
    return (rxBuf[0]&ACK_MASK);
}
void SendWDATA(uint32_t WDATA,uint8_t NRST_const){
    u32u8_t u32u8_WDATA;
    u32u8_WDATA.asU32=WDATA;
    txBuf[0]=0xFF;
    Send_or_Receive_Data(TXorRX_TX,1,NRST_const);//Trn
    txBuf[0]=flip_8bit(u32u8_WDATA.asU8[0],8);
    txBuf[1]=flip_8bit(u32u8_WDATA.asU8[1],8);
    txBuf[2]=flip_8bit(u32u8_WDATA.asU8[2],8);
    txBuf[3]=flip_8bit(u32u8_WDATA.asU8[3],8);
    if(CalcParity32(WDATA)){
        txBuf[4]=0xFF;
    }else{
        txBuf[4]=0x00;
    }
    Send_or_Receive_Data(TXorRX_TX,33,NRST_const);//WDATA+Parity
}
uint8_t ReceiveRDATA(uint32_t* RDATA,uint8_t NRST_const){
    u32u8_t u32u8_RDATA;
    uint8_t ui8_calc_parity;
    uint8_t ui8_recv_parity;
    Send_or_Receive_Data(TXorRX_RX,34,NRST_const);//RDATA+Parity+Trn
    u32u8_RDATA.asU8[0]=flip_8bit(rxBuf[0],8);
    u32u8_RDATA.asU8[1]=flip_8bit(rxBuf[1],8);
    u32u8_RDATA.asU8[2]=flip_8bit(rxBuf[2],8);
    u32u8_RDATA.asU8[3]=flip_8bit(rxBuf[3],8);
    *RDATA=u32u8_RDATA.asU32;
    ui8_calc_parity=(uint8_t)(CalcParity32(*RDATA));
    ui8_recv_parity=(rxBuf[4]>>7);
    return (ui8_calc_parity^ui8_recv_parity);
}

uint32_t SWD_READ(uint8_t APnDP,uint8_t ADDR,uint8_t NRST_const){
    uint8_t ack=0;
    uint8_t cnt=0;
    uint32_t RDATA;
    while((cnt<3)&&(ack!=0x80)){
        ack=SendSWDCommand_and_ReceiveAck(APnDP,RnW_R,ADDR,NRST_const);
        if(ack!=0x80){
            SWD_SYNC(NRST_const);
            SWD_SYNC(NRST_const);
            SWD_SYNC(NRST_const);
            SWD_SYNC(NRST_const);
        }
        cnt++;
    }
    ReceiveRDATA(&RDATA,NRST_const);
#if (UART_DEBUG==1)    
    sprintf(dbgstr,"%s %s %02X %02X %04X%04X",APnDP_str[APnDP],RnW_str[RnW_R],ADDR,ack,(uint16_t)((RDATA>>16)&0x0000FFFF),(uint16_t)((RDATA>>0)&0x0000FFFF));
    Serial.println(dbgstr);
#endif
    return RDATA;
}
void SWD_WRITE(uint8_t APnDP,uint8_t ADDR,uint32_t WDATA,uint8_t NRST_const){
    uint8_t ack=0;
    uint8_t cnt=0;
    while((cnt<3)&&(ack!=0x80)){
        ack=SendSWDCommand_and_ReceiveAck(APnDP,RnW_W,ADDR,NRST_const);
        if(ack!=0x80){
            SWD_SYNC(NRST_const);
            SWD_SYNC(NRST_const);
            SWD_SYNC(NRST_const);
            SWD_SYNC(NRST_const);
        }
        cnt++;
    }
    SendWDATA(WDATA,NRST_const);
#if (UART_DEBUG==1)    
    sprintf(dbgstr,"%s %s %02X %02X %04X%04X",APnDP_str[APnDP],RnW_str[RnW_W],ADDR,ack,(uint16_t)((WDATA>>16)&0x0000FFFF),(uint16_t)((WDATA>>0)&0x0000FFFF));
    Serial.println(dbgstr);
#endif
}

void DP_WRITE(uint8_t ADDR,uint32_t WDATA,uint8_t NRST_const){
    SWD_WRITE(APnDP_DP,ADDR,WDATA,NRST_const);
}
uint32_t DP_READ(uint8_t ADDR,uint8_t NRST_const){
    return SWD_READ(APnDP_DP,ADDR,NRST_const);
}
uint32_t AP_READ(uint8_t APSEL,uint8_t APBANKSEL,uint8_t NRST_const){
    uint32_t SELECT;
    uint32_t RDATA;
    SELECT=(((uint32_t)APSEL)<<24)|((uint32_t)(APBANKSEL&0xF0));
    DP_WRITE(0x8,SELECT,NRST_const);
    RDATA=SWD_READ(APnDP_AP,APBANKSEL&0x0C,NRST_const);
    RDATA=DP_READ(0xc,NRST_const);
    return RDATA;
}
void AP_WRITE(uint8_t APSEL,uint8_t APBANKSEL,uint32_t WDATA,uint8_t NRST_const){
    uint32_t SELECT;
    uint32_t rxData32;
    SELECT=(((uint32_t)APSEL)<<24)|((uint32_t)(APBANKSEL&0xF0));
    DP_WRITE(0x8,SELECT,NRST_const);
    SWD_WRITE(APnDP_AP,APBANKSEL&0x0C,WDATA,NRST_const);
}
void setup(void){
    Serial.begin(57600);
    DDRA|=(PIN_SWDO|PIN_SWCLK|PIN_NRST);
    DDRA&=~(PIN_SWDI);
    PORTA|=(PIN_SWDI|PIN_SWCLK|PIN_NRST);

    TCCR1A=0x00;
    OCR1A=0x0040;
    TCCR1B=0x08;
    txCompleted=1;
}

void loop(void){
    uint32_t rxData32;
    uint8_t ack;
    char buf[80];
    u32u8_t u32u8_data;

    Serial.println("START");
    JTAG_to_SWD(1);

    DP_READ(0x0,1);
    DP_READ(0x4,1);
    DP_WRITE(0x4,0x50000000,1);
    DP_READ(0x4,1);
    AP_READ(0x0,0xFC,1);

    delay(200);
}

UART出力は

 

START

DP R 00 80 0BC11477

DP R 04 80 F0000040

DP R 04 80 50000000

DP R 04 80 F0000040

DP R 08 80 000000F0

AP R 0C 80 00000000

DP R 0C 80 04770031

 

となっていて、たぶんうまくいっている。

しかし、エラー処理とかまだまだ甘い点がある。なぜかUSIの極性がおかしくなることもある。やっぱダメかもUSI、、、

ところで、今まで触れなかったけど、SWDの仕様と実際のデバイスの動きは違う。

仕様はこちら。

HostフェーズもTargetフェーズもrisingでセットfallingでキャプチャになっている。

実際のread operationはこちら。

Hostフェーズはfallingでセットrisingでキャプチャっぽい波形なのに、Targetフェーズはrisingでセットfallingでキャプチャになっている。そしてしょーがないので上のコードも実際に合わせて作ってある。なんなんだろう。