Skip to content

Commit

Permalink
Continued initial SWD implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
azonenberg committed Sep 4, 2018
1 parent 57120a3 commit 92fe249
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 17 deletions.
20 changes: 20 additions & 0 deletions ARMDebugPort.h
Expand Up @@ -63,6 +63,22 @@ class ARMDebugPort : public DebuggerInterface
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AP register access

//Well-defined DP registers (superset of supported registers for all ADI versions)
enum DpReg
{
REG_ABORT = 0x00, //write only
REG_DP_IDR = 0x00, //read only

REG_CTRL_STAT = 0x01, //BANKSEL = 0
REG_TARGETID = 0x01, //BANKSEL = 2

REG_RESEND = 0x02, //read only
REG_AP_SELECT = 0x02, //write only

REG_RDBUFF = 0x03,
REG_TARGETSEL = 0x03
};

//Well-defined AP registers
enum ApReg
{
Expand All @@ -80,6 +96,10 @@ class ARMDebugPort : public DebuggerInterface
//need to be a friend so that the Mem-AP can poke registers
//TODO: try to find a cleaner way to expose this?
friend class ARMDebugMemAccessPort;

//these use register numbers, not addresses
virtual uint32_t DPRegisterRead(DpReg addr) =0;
virtual void DPRegisterWrite(DpReg addr, uint32_t wdata) =0;
virtual uint32_t APRegisterRead(uint8_t ap, ApReg addr) =0;
virtual void APRegisterWrite(uint8_t ap, ApReg addr, uint32_t wdata) =0;

Expand Down
16 changes: 4 additions & 12 deletions ARMJtagDebugPort.h
Expand Up @@ -181,14 +181,6 @@ class ARMJtagDebugPort : public ARMDebugPort
OP_READ = 1
};

//Well-defined DP registers
enum DpReg
{
REG_CTRL_STAT = 1,
REG_AP_SELECT = 2,
REG_RDBUFF = 3
};

public:
ARMJtagDebugPortStatusRegister GetStatusRegister();
void PrintStatusRegister(ARMJtagDebugPortStatusRegister reg, bool children = true);
Expand All @@ -198,14 +190,14 @@ class ARMJtagDebugPort : public ARMDebugPort
protected:
void ClearStatusRegisterErrors();

uint32_t DPRegisterRead(DpReg addr);
void DPRegisterWrite(DpReg addr, uint32_t wdata);
virtual uint32_t DPRegisterRead(DpReg addr);
virtual void DPRegisterWrite(DpReg addr, uint32_t wdata);

//need to be a friend so that the Mem-AP can poke registers
//TODO: try to find a cleaner way to expose this?
friend class ARMDebugMemAccessPort;
uint32_t APRegisterRead(uint8_t ap, ApReg addr);
void APRegisterWrite(uint8_t ap, ApReg addr, uint32_t wdata);
virtual uint32_t APRegisterRead(uint8_t ap, ApReg addr);
virtual void APRegisterWrite(uint8_t ap, ApReg addr, uint32_t wdata);

void EnableDebugging();

Expand Down
100 changes: 95 additions & 5 deletions FTDISWDInterface.cpp
Expand Up @@ -124,6 +124,8 @@ void FTDISWDInterface::ResetInterface()

/**
@brief Performs a SW-DP write transaction
TODO: optimize to 1 transaction using overrun detection?
*/
void FTDISWDInterface::WriteWord(uint8_t reg_addr, bool ap, uint32_t wdata)
{
Expand Down Expand Up @@ -163,7 +165,7 @@ void FTDISWDInterface::WriteWord(uint8_t reg_addr, bool ap, uint32_t wdata)
(m_gpioDirection[1] << 5) |
(m_gpioDirection[2] << 6) |
(m_gpioDirection[3] << 7) |
0x0B
0x0B;

//Calculate the parity for the data
uint32_t dpartmp = (wdata >> 16) | (wdata >> 8) | (wdata >> 4) | (wdata >> 2) | (wdata >> 1) | wdata;
Expand Down Expand Up @@ -246,16 +248,104 @@ uint32_t FTDISWDInterface::ReadWord(uint8_t reg_addr, bool ap)
{
//Host to target: 8 bit header
//LSB first: 1, AP / #DP, 1, A[2:3], Parity, 0, 1
uint8_t header = 0x85;
bool parity = true;
if(ap)
{
header |= 0x02;
parity ^= 1;
}
if(reg_addr & 4)
{
header |= 0x08;
parity ^= 1;
}
if(reg_addr & 8)
{
header |= 0x10;
parity ^= 1;
}
if(parity)
header |= 0x20;

//1 bit bus turnaround (tristate)
//We need to switch the TDO/SWDIO pin back and forth from output to tristate a couple of times.
//It's also a GPIO bitbang pin, though - so we need to reconfigure the low GPIO bank a bunch.
//Bit 1 = TDI = output (1) by default
unsigned char value_low =
(m_gpioValue[0] << 4) |
(m_gpioValue[1] << 5) |
(m_gpioValue[2] << 6) |
(m_gpioValue[3] << 7) |
0x08;
unsigned char dir_low =
(m_gpioDirection[0] << 4) |
(m_gpioDirection[1] << 5) |
(m_gpioDirection[2] << 6) |
(m_gpioDirection[3] << 7) |
0x0B;

//3 bit ACK from target
uint8_t cmdbuf[] =
{
//The header
header,

//Read data from target to host: 32 bits, LSB first
//Tristate TDI
MPSSE_SET_DATA_LOW,
value_low,
dir_low ^ 0x2, //flip TDI to an input

//1 bit parity
//Send a 1-bit bus turnaround as tristate
MPSSE_DUMMY_CLOCK_BITS,
0x00,

//Read the three-bit ACK from the target
MPSSE_TXRX_BYTES,
0x02,
0x00,
0x00 //TDI is tristated so send data doesn't matter
};
WriteDataRaw(cmdbuf, sizeof(cmdbuf));

//Send everything up to the ACK then see what comes back (should be a single byte)
uint8_t ack = 0;
ReadData(&ack, 1);

//WAIT request? TODO
if(ack == 2)
{
LogError("TODO: Handle WAIT request from SW-DP\n");
return 0;
}

//Some strange response that isn't what we expect
else if(ack != 1)
{
LogError("Weird - we got something other than ACK or WAIT\n");
return 0;
}

//Read data from target to host: 32 bits, LSB first
//1 bit parity
//1 bit bus turnaround (tristate)
uint8_t rxd[5];
uint8_t databuf[] =
{
//Read the data plus parity bits
MPSSE_TXRX_BITS,
0x20, //33 total bits
0x00, 0x00, 0x00, 0x00, 0x00,

//Send a bus turnaround cycle so the target can tristate the bus
MPSSE_DUMMY_CLOCK_BITS,
0x00, //1 bit

//Reconfigure TDI to actually drive again
MPSSE_SET_DATA_LOW,
value_low,
dir_low
};
WriteDataRaw(databuf, sizeof(databuf));
ReadData(&rxd, 5);
}

#endif
27 changes: 27 additions & 0 deletions SWDInterface.cpp
Expand Up @@ -46,9 +46,36 @@ SWDInterface::~SWDInterface()
{
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AP/DP register access

uint32_t SWDInterface::DPRegisterRead(DpReg addr)
{
return ReadWord(addr * 4, false); //convert from register ID to actual address offset
}

void SWDInterface::DPRegisterWrite(DpReg addr, uint32_t wdata)
{
WriteWord(addr*4, wdata); //convert from register ID to actual address offset
}

uint32_t SWDInterface::APRegisterRead(uint8_t ap, ApReg addr)
{
//TODO: select the AP we're using
return ReadWord(addr * 4, true); //convert from register ID to actual address offset
}

void SWDInterface::APRegisterWrite(uint8_t ap, ApReg addr, uint32_t wdata)
{
//TODO: select the AP we're using

WriteWord(addr*4, wdata); //convert from register ID to actual address offset
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Autodetection

void SWDInterface::InitializeDevice()
{
//Read the
}
7 changes: 7 additions & 0 deletions SWDInterface.h
Expand Up @@ -45,6 +45,8 @@
A SWD adapter provides access to a single SW-DP (TODO: or SWJ-DP) on a single ARM SoC.
Note that there is no ARMSWDDebugPort class; this class contains both the adapter and DP logic.
In order to support a new "dumb" SWD adapter without any higher level protocol offload, create a new derived class
and implement each of the following functions:
Expand Down Expand Up @@ -72,6 +74,11 @@ class SWDInterface : public ARMDebugPort

//Raw SWD read/write
protected:
virtual uint32_t DPRegisterRead(DpReg addr);
virtual void DPRegisterWrite(DpReg addr, uint32_t wdata);
virtual uint32_t APRegisterRead(uint8_t ap, ApReg addr);
virtual void APRegisterWrite(uint8_t ap, ApReg addr, uint32_t wdata);

/**
@brief Performs a SW-DP write transaction
*/
Expand Down

0 comments on commit 92fe249

Please sign in to comment.