|
|
@@ -0,0 +1,722 @@ |
|
|
|
/*************************************************************************** |
|
|
|
* Copyright (C) 2012 by Jan Dakinevich * |
|
|
|
* jan.dakinevich@gmail.com * |
|
|
|
* * |
|
|
|
* 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 2 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, write to the * |
|
|
|
* Free Software Foundation, Inc., * |
|
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * |
|
|
|
***************************************************************************/ |
|
|
|
#ifdef HAVE_CONFIG_H |
|
|
|
# include "config.h" |
|
|
|
#endif |
|
|
|
|
|
|
|
#include <helper/log.h> |
|
|
|
#include <helper/binarybuffer.h> |
|
|
|
#include <helper/command.h> |
|
|
|
#include <jtag/interface.h> |
|
|
|
#include "libusb_common.h" |
|
|
|
|
|
|
|
struct sequence { |
|
|
|
int len; |
|
|
|
void *tms; |
|
|
|
void *tdo; |
|
|
|
const void *tdi; |
|
|
|
struct sequence *next; |
|
|
|
}; |
|
|
|
|
|
|
|
struct queue { |
|
|
|
struct sequence *head; |
|
|
|
struct sequence *tail; |
|
|
|
}; |
|
|
|
|
|
|
|
static struct sequence *queue_add_tail(struct queue *queue, int len) |
|
|
|
{ |
|
|
|
if (len <= 0) { |
|
|
|
LOG_ERROR("BUG: sequences with zero length are not allowed"); |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
struct sequence *next; |
|
|
|
next = (struct sequence *)malloc(sizeof(*next)); |
|
|
|
if (next) { |
|
|
|
next->tms = calloc(1, DIV_ROUND_UP(len, 8)); |
|
|
|
if (next->tms) { |
|
|
|
next->len = len; |
|
|
|
next->tdo = NULL; |
|
|
|
next->tdi = NULL; |
|
|
|
next->next = NULL; |
|
|
|
|
|
|
|
if (!queue->head) { |
|
|
|
/* Queue is empty at the moment */ |
|
|
|
queue->head = next; |
|
|
|
} else { |
|
|
|
/* Queue already contains at least one sequence */ |
|
|
|
queue->tail->next = next; |
|
|
|
} |
|
|
|
|
|
|
|
queue->tail = next; |
|
|
|
} else { |
|
|
|
free(next); |
|
|
|
next = NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (!next) |
|
|
|
LOG_ERROR("Not enough memory"); |
|
|
|
|
|
|
|
return next; |
|
|
|
} |
|
|
|
|
|
|
|
static void queue_drop_head(struct queue *queue) |
|
|
|
{ |
|
|
|
struct sequence *head = queue->head->next; /* New head */ |
|
|
|
free(queue->head->tms); |
|
|
|
free(queue->head); |
|
|
|
queue->head = head; |
|
|
|
} |
|
|
|
|
|
|
|
static void queue_free(struct queue *queue) |
|
|
|
{ |
|
|
|
if (queue) { |
|
|
|
while (queue->head) |
|
|
|
queue_drop_head(queue); |
|
|
|
|
|
|
|
free(queue); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static struct queue *queue_alloc(void) |
|
|
|
{ |
|
|
|
struct queue *queue = (struct queue *)malloc(sizeof(struct queue)); |
|
|
|
if (queue) |
|
|
|
queue->head = NULL; |
|
|
|
else |
|
|
|
LOG_ERROR("Not enough memory"); |
|
|
|
|
|
|
|
return queue; |
|
|
|
} |
|
|
|
|
|
|
|
/* Size of usb communnication buffer */ |
|
|
|
#define OSBDM_USB_BUFSIZE 64 |
|
|
|
/* Timeout for USB transfer, ms */ |
|
|
|
#define OSBDM_USB_TIMEOUT 1000 |
|
|
|
/* Write end point */ |
|
|
|
#define OSBDM_USB_EP_WRITE 0x01 |
|
|
|
/* Read end point */ |
|
|
|
#define OSBDM_USB_EP_READ 0x82 |
|
|
|
|
|
|
|
/* Initialize OSBDM device */ |
|
|
|
#define OSBDM_CMD_INIT 0x11 |
|
|
|
/* Execute special, not-BDM command. But only this |
|
|
|
* command is used for JTAG operation */ |
|
|
|
#define OSBDM_CMD_SPECIAL 0x27 |
|
|
|
/* Execute JTAG swap (tms/tdi -> tdo) */ |
|
|
|
#define OSBDM_CMD_SPECIAL_SWAP 0x05 |
|
|
|
/* Reset control */ |
|
|
|
#define OSBDM_CMD_SPECIAL_SRST 0x01 |
|
|
|
/* Maximum bit-length in one swap */ |
|
|
|
#define OSBDM_SWAP_MAX (((OSBDM_USB_BUFSIZE - 6) / 5) * 16) |
|
|
|
|
|
|
|
/* Lists of valid VID/PID pairs |
|
|
|
*/ |
|
|
|
static const uint16_t osbdm_vid[] = { 0x15a2, 0x15a2, 0 }; |
|
|
|
static const uint16_t osbdm_pid[] = { 0x0042, 0x0058, 0 }; |
|
|
|
|
|
|
|
struct osbdm { |
|
|
|
struct jtag_libusb_device_handle *devh; /* USB handle */ |
|
|
|
uint8_t buffer[OSBDM_USB_BUFSIZE]; /* Data to send and recieved */ |
|
|
|
int count; /* Count data to send and to read */ |
|
|
|
}; |
|
|
|
|
|
|
|
/* osbdm instance |
|
|
|
*/ |
|
|
|
static struct osbdm osbdm_context; |
|
|
|
|
|
|
|
static int osbdm_send_and_recv(struct osbdm *osbdm) |
|
|
|
{ |
|
|
|
/* Send request */ |
|
|
|
int count = jtag_libusb_bulk_write(osbdm->devh, OSBDM_USB_EP_WRITE, |
|
|
|
(char *)osbdm->buffer, osbdm->count, OSBDM_USB_TIMEOUT); |
|
|
|
|
|
|
|
if (count != osbdm->count) { |
|
|
|
LOG_ERROR("OSBDM communnication error: can't write"); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
/* Save command code for next checking */ |
|
|
|
uint8_t cmd_saved = osbdm->buffer[0]; |
|
|
|
|
|
|
|
/* Reading answer */ |
|
|
|
osbdm->count = jtag_libusb_bulk_read(osbdm->devh, OSBDM_USB_EP_READ, |
|
|
|
(char *)osbdm->buffer, OSBDM_USB_BUFSIZE, OSBDM_USB_TIMEOUT); |
|
|
|
|
|
|
|
/* Now perform basic checks for data sent by BDM device |
|
|
|
*/ |
|
|
|
|
|
|
|
if (osbdm->count < 0) { |
|
|
|
LOG_ERROR("OSBDM communnication error: can't read"); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
if (osbdm->count < 2) { |
|
|
|
LOG_ERROR("OSBDM communnication error: answer too small"); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
if (osbdm->count != osbdm->buffer[1]) { |
|
|
|
LOG_ERROR("OSBDM communnication error: answer size mismatch"); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
if (cmd_saved != osbdm->buffer[0]) { |
|
|
|
LOG_ERROR("OSBDM communnication error: answer command mismatch"); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_srst(struct osbdm *osbdm, int srst) |
|
|
|
{ |
|
|
|
osbdm->count = 0; |
|
|
|
(void)memset(osbdm->buffer, 0, OSBDM_USB_BUFSIZE); |
|
|
|
|
|
|
|
/* Composing request |
|
|
|
*/ |
|
|
|
osbdm->buffer[osbdm->count++] = OSBDM_CMD_SPECIAL; /* Command */ |
|
|
|
osbdm->buffer[osbdm->count++] = OSBDM_CMD_SPECIAL_SRST; /* Subcommand */ |
|
|
|
/* Length in bytes - not used */ |
|
|
|
osbdm->buffer[osbdm->count++] = 0; |
|
|
|
osbdm->buffer[osbdm->count++] = 0; |
|
|
|
/* SRST state */ |
|
|
|
osbdm->buffer[osbdm->count++] = (srst ? 0 : 0x08); |
|
|
|
|
|
|
|
/* Sending data |
|
|
|
*/ |
|
|
|
if (osbdm_send_and_recv(osbdm) != ERROR_OK) |
|
|
|
return ERROR_FAIL; |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_swap(struct osbdm *osbdm, void *tms, void *tdi, |
|
|
|
void *tdo, int length) |
|
|
|
{ |
|
|
|
if (length > OSBDM_SWAP_MAX) { |
|
|
|
LOG_ERROR("BUG: bit sequence too long"); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
if (length <= 0) { |
|
|
|
LOG_ERROR("BUG: bit sequence equal or less to 0"); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
int swap_count = DIV_ROUND_UP(length, 16); |
|
|
|
|
|
|
|
/* cleanup */ |
|
|
|
osbdm->count = 0; |
|
|
|
(void)memset(osbdm->buffer, 0, OSBDM_USB_BUFSIZE); |
|
|
|
|
|
|
|
/* Composing request |
|
|
|
*/ |
|
|
|
|
|
|
|
osbdm->buffer[osbdm->count++] = OSBDM_CMD_SPECIAL; /* Command */ |
|
|
|
osbdm->buffer[osbdm->count++] = OSBDM_CMD_SPECIAL_SWAP; /* Subcommand */ |
|
|
|
/* Length in bytes - not used */ |
|
|
|
osbdm->buffer[osbdm->count++] = 0; |
|
|
|
osbdm->buffer[osbdm->count++] = 0; |
|
|
|
/* Swap count */ |
|
|
|
osbdm->buffer[osbdm->count++] = 0; |
|
|
|
osbdm->buffer[osbdm->count++] = (uint8_t)swap_count; |
|
|
|
|
|
|
|
for (int bit_idx = 0; bit_idx < length; ) { |
|
|
|
/* Bit count in swap */ |
|
|
|
int bit_count = length - bit_idx; |
|
|
|
if (bit_count > 16) |
|
|
|
bit_count = 16; |
|
|
|
|
|
|
|
osbdm->buffer[osbdm->count++] = (uint8_t)bit_count; |
|
|
|
|
|
|
|
/* Copying TMS and TDI data to output buffer */ |
|
|
|
uint32_t tms_data = buf_get_u32(tms, bit_idx, bit_count); |
|
|
|
uint32_t tdi_data = buf_get_u32(tdi, bit_idx, bit_count); |
|
|
|
osbdm->buffer[osbdm->count++] = (uint8_t)(tdi_data >> 8); |
|
|
|
osbdm->buffer[osbdm->count++] = (uint8_t)tdi_data; |
|
|
|
osbdm->buffer[osbdm->count++] = (uint8_t)(tms_data >> 8); |
|
|
|
osbdm->buffer[osbdm->count++] = (uint8_t)tms_data; |
|
|
|
|
|
|
|
/* Next bit offset */ |
|
|
|
bit_idx += bit_count; |
|
|
|
} |
|
|
|
|
|
|
|
assert(osbdm->count <= OSBDM_USB_BUFSIZE); |
|
|
|
|
|
|
|
/* Sending data |
|
|
|
*/ |
|
|
|
if (osbdm_send_and_recv(osbdm) != ERROR_OK) |
|
|
|
return ERROR_FAIL; |
|
|
|
|
|
|
|
/* Extra check |
|
|
|
*/ |
|
|
|
if (((osbdm->buffer[2] << 8) | osbdm->buffer[3]) != 2 * swap_count) { |
|
|
|
LOG_ERROR("OSBDM communnication error: not proper answer to swap command"); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
/* Copy TDO responce |
|
|
|
*/ |
|
|
|
uint8_t *buffer = (uint8_t *)osbdm->buffer + 4; |
|
|
|
for (int bit_idx = 0; bit_idx < length; ) { |
|
|
|
int bit_count = length - bit_idx; |
|
|
|
if (bit_count > 16) |
|
|
|
bit_count = 16; |
|
|
|
|
|
|
|
/* Prepare data */ |
|
|
|
uint32_t tdo_data = 0; |
|
|
|
tdo_data |= (*buffer++) << 8; |
|
|
|
tdo_data |= (*buffer++); |
|
|
|
tdo_data >>= (16 - bit_count); |
|
|
|
|
|
|
|
/* Copy TDO to return */ |
|
|
|
buf_set_u32(tdo, bit_idx, bit_count, tdo_data); |
|
|
|
|
|
|
|
bit_idx += bit_count; |
|
|
|
} |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_flush(struct osbdm *osbdm, struct queue* queue) |
|
|
|
{ |
|
|
|
uint8_t tms[DIV_ROUND_UP(OSBDM_SWAP_MAX, 8)]; |
|
|
|
uint8_t tdi[DIV_ROUND_UP(OSBDM_SWAP_MAX, 8)]; |
|
|
|
uint8_t tdo[DIV_ROUND_UP(OSBDM_SWAP_MAX, 8)]; |
|
|
|
|
|
|
|
int seq_back_len = 0; |
|
|
|
|
|
|
|
while (queue->head) { |
|
|
|
(void)memset(tms, 0, sizeof(tms)); |
|
|
|
(void)memset(tdi, 0, sizeof(tdi)); |
|
|
|
(void)memset(tdo, 0, sizeof(tdo)); |
|
|
|
|
|
|
|
int seq_len; |
|
|
|
int swap_len; |
|
|
|
struct sequence *seq; |
|
|
|
|
|
|
|
/* Copy from queue to tms/tdi streams |
|
|
|
*/ |
|
|
|
seq = queue->head; |
|
|
|
seq_len = seq_back_len; |
|
|
|
swap_len = 0; |
|
|
|
|
|
|
|
while (seq && swap_len != OSBDM_SWAP_MAX) { |
|
|
|
/* Count bit for copy at this iteration. |
|
|
|
* len should fit into remaining space |
|
|
|
* in tms/tdo bitstreams |
|
|
|
*/ |
|
|
|
int len = seq->len - seq_len; |
|
|
|
if (len > OSBDM_SWAP_MAX - swap_len) |
|
|
|
len = OSBDM_SWAP_MAX - swap_len; |
|
|
|
|
|
|
|
/* Set tms data */ |
|
|
|
buf_set_buf(seq->tms, seq_len, tms, swap_len, len); |
|
|
|
|
|
|
|
/* Set tdi data if they exists */ |
|
|
|
if (seq->tdi) |
|
|
|
buf_set_buf(seq->tdi, seq_len, tdi, swap_len, len); |
|
|
|
|
|
|
|
swap_len += len; |
|
|
|
seq_len += len; |
|
|
|
if (seq_len == seq->len) { |
|
|
|
seq = seq->next; /* Move to next sequence */ |
|
|
|
seq_len = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (osbdm_swap(osbdm, tms, tdi, tdo, swap_len)) |
|
|
|
return ERROR_FAIL; |
|
|
|
|
|
|
|
/* Copy from tdo stream to queue |
|
|
|
*/ |
|
|
|
|
|
|
|
for (int swap_back_len = 0; swap_back_len < swap_len; ) { |
|
|
|
int len = queue->head->len - seq_back_len; |
|
|
|
if (len > swap_len - swap_back_len) |
|
|
|
len = swap_len - swap_back_len; |
|
|
|
|
|
|
|
if (queue->head->tdo) |
|
|
|
buf_set_buf(tdo, swap_back_len, queue->head->tdo, seq_back_len, len); |
|
|
|
|
|
|
|
swap_back_len += len; |
|
|
|
seq_back_len += len; |
|
|
|
if (seq_back_len == queue->head->len) { |
|
|
|
queue_drop_head(queue); |
|
|
|
seq_back_len = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
/* Basic operation for opening USB device */ |
|
|
|
static int osbdm_open(struct osbdm *osbdm) |
|
|
|
{ |
|
|
|
(void)memset(osbdm, 0, sizeof(*osbdm)); |
|
|
|
if (jtag_libusb_open(osbdm_vid, osbdm_pid, &osbdm->devh) != ERROR_OK) |
|
|
|
return ERROR_FAIL; |
|
|
|
|
|
|
|
if (jtag_libusb_claim_interface(osbdm->devh, 0) != ERROR_OK) |
|
|
|
return ERROR_FAIL; |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_quit(void) |
|
|
|
{ |
|
|
|
jtag_libusb_close(osbdm_context.devh); |
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_add_pathmove( |
|
|
|
struct queue *queue, |
|
|
|
tap_state_t *path, |
|
|
|
int num_states) |
|
|
|
{ |
|
|
|
assert(num_states <= 32); |
|
|
|
|
|
|
|
struct sequence *next = queue_add_tail(queue, num_states); |
|
|
|
if (!next) { |
|
|
|
LOG_ERROR("BUG: can't allocate bit sequence"); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
uint32_t tms = 0; |
|
|
|
for (int i = 0; i < num_states; i++) { |
|
|
|
if (tap_state_transition(tap_get_state(), 1) == path[i]) { |
|
|
|
tms |= (1 << i); |
|
|
|
} else if (tap_state_transition(tap_get_state(), 0) == path[i]) { |
|
|
|
tms &= ~(1 << i); /* This line not so needed */ |
|
|
|
} else { |
|
|
|
LOG_ERROR("BUG: %s -> %s isn't a valid TAP state transition", |
|
|
|
tap_state_name(tap_get_state()), |
|
|
|
tap_state_name(path[i])); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
tap_set_state(path[i]); |
|
|
|
} |
|
|
|
|
|
|
|
buf_set_u32(next->tms, 0, num_states, tms); |
|
|
|
tap_set_end_state(tap_get_state()); |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_add_statemove( |
|
|
|
struct queue *queue, |
|
|
|
tap_state_t new_state, |
|
|
|
int skip_first) |
|
|
|
{ |
|
|
|
int len = 0; |
|
|
|
int tms; |
|
|
|
|
|
|
|
tap_set_end_state(new_state); |
|
|
|
if (tap_get_end_state() == TAP_RESET) { |
|
|
|
/* Ignore current state */ |
|
|
|
tms = 0xff; |
|
|
|
len = 5; |
|
|
|
} else if (tap_get_state() != tap_get_end_state()) { |
|
|
|
tms = tap_get_tms_path(tap_get_state(), new_state); |
|
|
|
len = tap_get_tms_path_len(tap_get_state(), new_state); |
|
|
|
} |
|
|
|
|
|
|
|
if (len && skip_first) { |
|
|
|
len--; |
|
|
|
tms >>= 1; |
|
|
|
} |
|
|
|
|
|
|
|
if (len) { |
|
|
|
struct sequence *next = queue_add_tail(queue, len); |
|
|
|
if (!next) { |
|
|
|
LOG_ERROR("BUG: can't allocate bit sequence"); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
buf_set_u32(next->tms, 0, len, tms); |
|
|
|
} |
|
|
|
|
|
|
|
tap_set_state(tap_get_end_state()); |
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_add_stableclocks( |
|
|
|
struct queue *queue, |
|
|
|
int count) |
|
|
|
{ |
|
|
|
if (!tap_is_state_stable(tap_get_state())) { |
|
|
|
LOG_ERROR("BUG: current state (%s) is not stable", |
|
|
|
tap_state_name(tap_get_state())); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
struct sequence *next = queue_add_tail(queue, count); |
|
|
|
if (!next) { |
|
|
|
LOG_ERROR("BUG: can't allocate bit sequence"); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
if (tap_get_state() == TAP_RESET) |
|
|
|
(void)memset(next->tms, 0xff, DIV_ROUND_UP(count, 8)); |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_add_tms( |
|
|
|
struct queue *queue, |
|
|
|
const uint8_t *tms, |
|
|
|
int num_bits) |
|
|
|
{ |
|
|
|
struct sequence *next = queue_add_tail(queue, num_bits); |
|
|
|
if (!next) { |
|
|
|
LOG_ERROR("BUG: can't allocate bit sequence"); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
buf_set_buf(tms, 0, next->tms, 0, num_bits); |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_add_scan( |
|
|
|
struct queue *queue, |
|
|
|
struct scan_field *fields, |
|
|
|
int num_fields, |
|
|
|
tap_state_t end_state, |
|
|
|
bool ir_scan) |
|
|
|
{ |
|
|
|
/* Move to desired shift state */ |
|
|
|
if (ir_scan) { |
|
|
|
if (tap_get_state() != TAP_IRSHIFT) { |
|
|
|
if (osbdm_add_statemove(queue, TAP_IRSHIFT, 0) != ERROR_OK) |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
} else { |
|
|
|
if (tap_get_state() != TAP_DRSHIFT) { |
|
|
|
if (osbdm_add_statemove(queue, TAP_DRSHIFT, 0) != ERROR_OK) |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* Add scan */ |
|
|
|
tap_set_end_state(end_state); |
|
|
|
for (int idx = 0; idx < num_fields; idx++) { |
|
|
|
struct sequence *next = queue_add_tail(queue, fields[idx].num_bits); |
|
|
|
if (!next) { |
|
|
|
LOG_ERROR("Can't allocate bit sequence"); |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
(void)memset(next->tms, 0, DIV_ROUND_UP(fields[idx].num_bits, 8)); |
|
|
|
next->tdi = fields[idx].out_value; |
|
|
|
next->tdo = fields[idx].in_value; |
|
|
|
} |
|
|
|
|
|
|
|
/* Move to end state |
|
|
|
*/ |
|
|
|
if (tap_get_state() != tap_get_end_state()) { |
|
|
|
/* Exit from IRSHIFT/DRSHIFT */ |
|
|
|
buf_set_u32(queue->tail->tms, queue->tail->len - 1, 1, 1); |
|
|
|
|
|
|
|
/* Move with skip_first flag */ |
|
|
|
if (osbdm_add_statemove(queue, tap_get_end_state(), 1) != ERROR_OK) |
|
|
|
return ERROR_FAIL; |
|
|
|
} |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_add_runtest( |
|
|
|
struct queue *queue, |
|
|
|
int num_cycles, |
|
|
|
tap_state_t end_state) |
|
|
|
{ |
|
|
|
if (osbdm_add_statemove(queue, TAP_IDLE, 0) != ERROR_OK) |
|
|
|
return ERROR_FAIL; |
|
|
|
|
|
|
|
if (osbdm_add_stableclocks(queue, num_cycles) != ERROR_OK) |
|
|
|
return ERROR_FAIL; |
|
|
|
|
|
|
|
if (osbdm_add_statemove(queue, end_state, 0) != ERROR_OK) |
|
|
|
return ERROR_FAIL; |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_execute_command( |
|
|
|
struct osbdm *osbdm, |
|
|
|
struct queue *queue, |
|
|
|
struct jtag_command *cmd) |
|
|
|
{ |
|
|
|
int retval = ERROR_OK; |
|
|
|
|
|
|
|
switch (cmd->type) { |
|
|
|
case JTAG_RESET: |
|
|
|
if (cmd->cmd.reset->trst) { |
|
|
|
LOG_ERROR("BUG: nTRST signal is not supported"); |
|
|
|
retval = ERROR_FAIL; |
|
|
|
} else { |
|
|
|
retval = osbdm_flush(osbdm, queue); |
|
|
|
if (retval == ERROR_OK) |
|
|
|
retval = osbdm_srst(osbdm, cmd->cmd.reset->srst); |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
case JTAG_PATHMOVE: |
|
|
|
retval = osbdm_add_pathmove( |
|
|
|
queue, |
|
|
|
cmd->cmd.pathmove->path, |
|
|
|
cmd->cmd.pathmove->num_states); |
|
|
|
break; |
|
|
|
|
|
|
|
case JTAG_TLR_RESET: |
|
|
|
retval = osbdm_add_statemove( |
|
|
|
queue, |
|
|
|
cmd->cmd.statemove->end_state, |
|
|
|
0); |
|
|
|
break; |
|
|
|
|
|
|
|
case JTAG_STABLECLOCKS: |
|
|
|
retval = osbdm_add_stableclocks( |
|
|
|
queue, |
|
|
|
cmd->cmd.stableclocks->num_cycles); |
|
|
|
break; |
|
|
|
|
|
|
|
case JTAG_TMS: |
|
|
|
retval = osbdm_add_tms( |
|
|
|
queue, |
|
|
|
cmd->cmd.tms->bits, |
|
|
|
cmd->cmd.tms->num_bits); |
|
|
|
break; |
|
|
|
|
|
|
|
case JTAG_SCAN: |
|
|
|
retval = osbdm_add_scan( |
|
|
|
queue, |
|
|
|
cmd->cmd.scan->fields, |
|
|
|
cmd->cmd.scan->num_fields, |
|
|
|
cmd->cmd.scan->end_state, |
|
|
|
cmd->cmd.scan->ir_scan); |
|
|
|
break; |
|
|
|
|
|
|
|
case JTAG_SLEEP: |
|
|
|
retval = osbdm_flush(osbdm, queue); |
|
|
|
if (retval == ERROR_OK) |
|
|
|
jtag_sleep(cmd->cmd.sleep->us); |
|
|
|
break; |
|
|
|
|
|
|
|
case JTAG_RUNTEST: |
|
|
|
retval = osbdm_add_runtest( |
|
|
|
queue, |
|
|
|
cmd->cmd.runtest->num_cycles, |
|
|
|
cmd->cmd.runtest->end_state); |
|
|
|
break; |
|
|
|
|
|
|
|
default: |
|
|
|
LOG_ERROR("BUG: unknown JTAG command type encountered"); |
|
|
|
retval = ERROR_FAIL; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
return retval; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_execute_queue(void) |
|
|
|
{ |
|
|
|
int retval = ERROR_OK; |
|
|
|
|
|
|
|
struct queue *queue = queue_alloc(); |
|
|
|
if (!queue) { |
|
|
|
LOG_ERROR("BUG: can't allocate bit queue"); |
|
|
|
retval = ERROR_FAIL; |
|
|
|
} else { |
|
|
|
struct jtag_command *cmd = jtag_command_queue; |
|
|
|
|
|
|
|
while (retval == ERROR_OK && cmd) { |
|
|
|
retval = osbdm_execute_command(&osbdm_context, queue, cmd); |
|
|
|
cmd = cmd->next; |
|
|
|
} |
|
|
|
|
|
|
|
if (retval == ERROR_OK) |
|
|
|
retval = osbdm_flush(&osbdm_context, queue); |
|
|
|
|
|
|
|
queue_free(queue); |
|
|
|
} |
|
|
|
|
|
|
|
if (retval != ERROR_OK) { |
|
|
|
LOG_ERROR("FATAL: can't execute jtag command"); |
|
|
|
exit(-1); |
|
|
|
} |
|
|
|
|
|
|
|
return retval; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_init(void) |
|
|
|
{ |
|
|
|
/* Open device */ |
|
|
|
if (osbdm_open(&osbdm_context) != ERROR_OK) { |
|
|
|
LOG_ERROR("Can't open OSBDM device"); |
|
|
|
return ERROR_FAIL; |
|
|
|
} else { |
|
|
|
/* Device successfully opened */ |
|
|
|
LOG_INFO("OSBDM has opened"); |
|
|
|
} |
|
|
|
|
|
|
|
/* Perform initialize command */ |
|
|
|
osbdm_context.count = 0; |
|
|
|
osbdm_context.buffer[osbdm_context.count++] = OSBDM_CMD_INIT; |
|
|
|
if (osbdm_send_and_recv(&osbdm_context) != ERROR_OK) |
|
|
|
return ERROR_FAIL; |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_khz(int khz, int *speed) |
|
|
|
{ |
|
|
|
*speed = khz; |
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_speed(int speed) |
|
|
|
{ |
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int osbdm_speed_div(int speed, int *khz) |
|
|
|
{ |
|
|
|
*khz = speed; |
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
struct jtag_interface osbdm_interface = { |
|
|
|
.name = "osbdm", |
|
|
|
|
|
|
|
.transports = jtag_only, |
|
|
|
.execute_queue = osbdm_execute_queue, |
|
|
|
|
|
|
|
.khz = osbdm_khz, |
|
|
|
.speed = osbdm_speed, |
|
|
|
.speed_div = osbdm_speed_div, |
|
|
|
|
|
|
|
.init = osbdm_init, |
|
|
|
.quit = osbdm_quit |
|
|
|
}; |