SWD(Serial Wire Debug) interface

Dependents:   USBMSD_LPC_HelloWorld lpcterm2 Simple-CMSIS-DAP 11u35_usbLocalFilesystem

SWD.cpp

Committer:
va009039
Date:
2014-07-05
Revision:
1:794d2801ff94
Parent:
0:86fde86e144f

File content as of revision 1:794d2801ff94:

// SWD.cpp 2014/7/5
#include "SWD.h"
#include <algorithm>

SWD::SWD(PinName swdio, PinName swclk, PinName reset)
    : _swdio(swdio), _swclk(swclk), _nreset(reset)
{
    conf.turnaround = 1;
    conf.data_phase = 0;
    idle_cycles = 0;
    retry_count = 100;
    
    _cpu_delay_count = 2;
    TransferAbort = false;
}

void SWD::Setup()
{
    _swclk = 1;
    _swdio.output();
    _swdio = 1;
    _nreset.input();
    _nreset.mode(PullUp);
}

void SWD::reset()
{
    SWJPins(0x00, 0x80);
    SWJPins(0x80, 0x80);
}

void SWD::SWJSequence(int count, const uint8_t* data)
{
    for(int n = 0; n < count; n++) {
        uint8_t val = data[n/8];
        write_bit(val>>(n%8));
    }
}

uint8_t SWD::SWJPins(uint32_t value, uint32_t select ,int waittime_us)
{
    if (select & 0x01) { // swclk
        _swclk = (value & 0x01) ? 1 : 0; 
    }
    if (select & 0x02) { // swdio
        _swdio = (value & 0x02) ? 1 : 0;
    }
    if (select & 0x80) { // nReset
        if (value & 0x80) {
            _nreset.input();
        } else {    
            _nreset.output();
            _nreset = 0;
        }
    }
    if (waittime_us) {
        waittime_us = std::min(waittime_us, 3000000);
        Timer t;
        t.reset();
        t.start();
        while(t.read_us() < waittime_us) {
            if (select & 0x01) { // swclk
                if (_swclk ^ ((value & 0x01) ? 1 : 0)) {
                    continue;
                } 
            }
            if (select & 0x02) { // swdio
                if (_swdio ^ ((value & 0x02) ? 1 : 0)) {
                    continue;
                }
            }
            if (select & 0x80) { // nReset
                if (_nreset ^ ((value & 0x80) ? 1 : 0)) {
                    continue;
                }
            }
            break;
        }
    }
    return (_swclk ? 0x01 : 0x00) | (_swdio ? 0x02 : 0x00) | (_nreset ? 0x80 : 0x00);
}

void SWD::SWJClock(uint32_t clock_hz)
{
    if (clock_hz) {
        uint32_t scc = SystemCoreClock;
        _cpu_delay_count = scc / 2 / clock_hz / 3;
    } 
}

void SWD::TransferConfigure(int _idle_cycles, int _retry_count)
{
    idle_cycles = _idle_cycles;
    retry_count = _retry_count; 
}

void SWD::Configure(int turnaround, int data_phase)
{
    conf.turnaround = turnaround;
    conf.data_phase = data_phase;
}

uint8_t SWD::Transfer(uint8_t request, uint32_t *data)
{
    for(int retry = retry_count; retry >= 0; retry--) {
        uint8_t ack = rawTransfer(request, data);
        if (ack != SWD_WAIT || TransferAbort) {
            return ack;
        }
    }
    return SWD_WAIT;
}

#pragma Otime

static uint32_t calc_parity(uint32_t data, int n)
{
    uint32_t parity = 0;
    for(int i = 0; i < n; i++) {
        parity += data>>i;
    }
    return parity & 1;
}

uint8_t SWD::rawTransfer(uint8_t request, uint32_t *data)
{
    write_bit(1);          // start bit
    write_bit(request, 4); // APnDP,RnW,A2,A3
    write_bit(calc_parity(request, 4)); // parity bit
    write_bit(0);       // stop bit
    write_bit(1);       // park bit
    
    _swdio.input();
    clock_cycle(conf.turnaround);
    
    uint8_t ack = read_bit(3);
    if (ack == SWD_OK) {
        if (request & SWD_RnW) { // read
            uint32_t val = read_bit(32);
            uint32_t parity = read_bit(1);
            if (parity ^ calc_parity(val, 32)) {
                ack = SWD_ERROR;
            }
            if (data) {
                *data = val;
            }
            clock_cycle(conf.turnaround);
            _swdio.output();
        } else {                        // write
            clock_cycle(conf.turnaround);
            _swdio.output();
            uint32_t val = *data;
            write_bit(val, 32);
            write_bit(calc_parity(val, 32));
        }
        if (idle_cycles) {
            _swdio = 0;
            clock_cycle(idle_cycles);
        }
        _swdio = 1;
        return ack;
    }

    if (ack == SWD_WAIT || ack == SWD_FAULT) {
        if (conf.data_phase && (request & SWD_RnW)) {
            clock_cycle(32+1);
        }
        clock_cycle(conf.turnaround);
        _swdio.output();
        if (conf.data_phase && ((request & SWD_RnW) == 0)) {
            _swdio = 0;
            clock_cycle(32+1);
        }
        _swdio = 1;
        return ack;
    }
    clock_cycle(32 + 1 + conf.turnaround);
    _swdio.output();
    return ack;
}

void SWD::pin_delay()
{
    __IO int n = _cpu_delay_count;
    while(n-- > 0)
        ;
}

void SWD::clock_cycle(int n)
{
    while(n-- > 0) {
        _swclk = 0;
        pin_delay();
        _swclk = 1;
        pin_delay();
    }    
}

void SWD::write_bit(uint32_t data, int n)
{
    for(int i = 0; i < n; i++) {
        _swdio = (data>>i) & 1;
        clock_cycle();
    }
}

uint32_t SWD::read_bit(int n)
{
    uint32_t data = 0;
    for(int i = 0; i < n; i++) {
        _swclk = 0;
        pin_delay();
        uint32_t val = _swdio & 1;
        data |= val<<i;
        _swclk = 1;
        pin_delay();
    }    
    return data;
}