Felix Domke
13 years ago
commit
7ed6138450
6 changed files with 521 additions and 0 deletions
@ -0,0 +1,20 @@ |
|||
This is a daemon that listens to "xilinx_xvc" (xilinx virtual cable) traffic and |
|||
operates JTAG over GPIOs. |
|||
|
|||
It was written for the NeTV, but the NeTV silvermoon hardware doesn't have the |
|||
necessary GPIO-to-JTAG connections. |
|||
|
|||
I've chosen the following connections: |
|||
|
|||
TDI - GPIO104 - SPARE_IO1 (J102, test point) |
|||
TDO - GPIO35 - MMC3_ALT_CMD (available on RP116, bottom) |
|||
TCK - GPIO118 - FPGA_CCLK (available between R800 and R801, shared with serial configuration) |
|||
TMS - GPIO36 - MMC3_ALT_CLK (available on RP116, bottom) |
|||
|
|||
You should easily be able to make it support other hardware as well. |
|||
|
|||
The daemon listens on port 2542, and multiplexes between multiple connections. |
|||
|
|||
I've tested Impact FPGA Programming, IDCODE looping, and Chipscope. |
|||
|
|||
Have fun! |
@ -0,0 +1,6 @@ |
|||
CFLAGS=-Werror -Wall -Os |
|||
|
|||
all: xvcd |
|||
|
|||
xvcd: gpio.o xvcd.o |
|||
$(CC) -o $@ $^ $(CFLAGS) |
@ -0,0 +1,94 @@ |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <sys/types.h> |
|||
#include <sys/stat.h> |
|||
#include <fcntl.h> |
|||
#include <unistd.h> |
|||
#include <sys/mman.h> |
|||
#include "gpio.h" |
|||
|
|||
volatile void * ioremap(unsigned long physaddr, unsigned size) |
|||
{ |
|||
static int axs_mem_fd = -1; |
|||
unsigned long page_addr, ofs_addr, reg, pgmask; |
|||
void* reg_mem = NULL; |
|||
|
|||
/*
|
|||
* looks like mmap wants aligned addresses? |
|||
*/ |
|||
pgmask = getpagesize()-1; |
|||
page_addr = physaddr & ~pgmask; |
|||
ofs_addr = physaddr & pgmask; |
|||
|
|||
/*
|
|||
* Don't forget O_SYNC, esp. if address is in RAM region. |
|||
* Note: if you do know you'll access in Read Only mode, |
|||
* pass O_RDONLY to open, and PROT_READ only to mmap |
|||
*/ |
|||
if (axs_mem_fd == -1) { |
|||
axs_mem_fd = open("/dev/mem", O_RDWR|O_SYNC); |
|||
if (axs_mem_fd < 0) { |
|||
perror("AXS: can't open /dev/mem"); |
|||
return NULL; |
|||
} |
|||
} |
|||
|
|||
/* memory map */ |
|||
reg_mem = mmap( |
|||
(caddr_t)reg_mem, |
|||
size+ofs_addr, |
|||
PROT_READ|PROT_WRITE, |
|||
MAP_SHARED, |
|||
axs_mem_fd, |
|||
page_addr |
|||
); |
|||
if (reg_mem == MAP_FAILED) { |
|||
perror("AXS: mmap error"); |
|||
close(axs_mem_fd); |
|||
return NULL; |
|||
} |
|||
|
|||
reg = (unsigned long )reg_mem + ofs_addr; |
|||
return (volatile void *)reg; |
|||
} |
|||
|
|||
int iounmap(volatile void *start, size_t length) |
|||
{ |
|||
unsigned long ofs_addr; |
|||
ofs_addr = (unsigned long)start & (getpagesize()-1); |
|||
|
|||
/* do some cleanup when you're done with it */ |
|||
return munmap((unsigned char*)start-ofs_addr, length+ofs_addr); |
|||
} |
|||
|
|||
volatile unsigned long *gpio; |
|||
volatile unsigned long *mfpr; |
|||
|
|||
void gpio_init(void) |
|||
{ |
|||
//
|
|||
// Map GPIO MMIO space.
|
|||
//
|
|||
|
|||
gpio = ioremap(0xD4019000, 0x1000); |
|||
mfpr = ioremap(0xD401E000, 0x1000); |
|||
|
|||
//
|
|||
// Initialize GPIOs.
|
|||
//
|
|||
mfpr[0x1d8/4]= 0x880; |
|||
mfpr[0xd8/4]= 0x880; |
|||
mfpr[0xdc/4]= 0x880; |
|||
|
|||
gpio_output(GPIO_TDI, 1); |
|||
gpio_output(GPIO_TMS, 1); |
|||
gpio_output(GPIO_TCK, 1); |
|||
gpio_output(GPIO_TDO, 0); |
|||
} |
|||
|
|||
void gpio_close(void) |
|||
{ |
|||
iounmap(mfpr, 0x1000); |
|||
iounmap(gpio, 0x1000); |
|||
} |
@ -0,0 +1,16 @@ |
|||
#ifndef __GPIO_H |
|||
#define __GPIO_H |
|||
|
|||
#define GPIO_TDI 0 |
|||
#define GPIO_TDO 1 |
|||
#define GPIO_TCK 2 |
|||
#define GPIO_TMS 3 |
|||
|
|||
static const int gpio_table[] = {104, 35, 118, 36}; |
|||
|
|||
void gpio_init(void); |
|||
void gpio_close(void); |
|||
|
|||
#include "gpio_inline.h" |
|||
|
|||
#endif |
@ -0,0 +1,54 @@ |
|||
extern volatile unsigned long *gpio; |
|||
extern volatile unsigned long *mfpr; |
|||
|
|||
static inline void gpio_output(int i, int dir) |
|||
{ |
|||
i = gpio_table[i]; |
|||
volatile unsigned long *gpdr; |
|||
|
|||
if (i < 96) |
|||
gpdr = gpio + 0xC/4 + i/32; |
|||
else |
|||
gpdr = gpio + 0x10c/4 + (i - 96) / 32; |
|||
|
|||
i &= 31; |
|||
|
|||
if (dir) |
|||
*gpdr |= 1<<i; |
|||
else |
|||
*gpdr &=~(1<<i); |
|||
} |
|||
|
|||
static inline void gpio_set(int i, int val) |
|||
{ |
|||
i = gpio_table[i]; |
|||
volatile unsigned long *gpxr; |
|||
|
|||
if (i < 96) |
|||
gpxr = gpio + 0x18/4 + i/32; |
|||
else |
|||
gpxr = gpio + 0x118/4 + (i - 96) / 32; |
|||
|
|||
if (!val) |
|||
gpxr += 3; // GPSR -> GPCR
|
|||
|
|||
i &= 31; |
|||
|
|||
*gpxr |= 1<<i; |
|||
} |
|||
|
|||
static inline int gpio_get(int i) |
|||
{ |
|||
i = gpio_table[i]; |
|||
volatile unsigned long *gplr; |
|||
|
|||
if (i < 96) |
|||
gplr = gpio + i/32; |
|||
else |
|||
gplr = gpio + 0x100/4 + (i - 96) / 32; |
|||
|
|||
i &= 31; |
|||
|
|||
return !!((*gplr) & (1<<i)); |
|||
} |
|||
|
@ -0,0 +1,331 @@ |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <unistd.h> |
|||
#include <string.h> |
|||
|
|||
#include <sys/types.h> |
|||
#include <sys/socket.h> |
|||
#include <netinet/in.h> |
|||
|
|||
#include "gpio.h" |
|||
|
|||
static int jtag_state; |
|||
static int verbose; |
|||
|
|||
//
|
|||
// JTAG state machine.
|
|||
//
|
|||
|
|||
enum |
|||
{ |
|||
test_logic_reset, run_test_idle, |
|||
|
|||
select_dr_scan, capture_dr, shift_dr, |
|||
exit1_dr, pause_dr, exit2_dr, update_dr, |
|||
|
|||
select_ir_scan, capture_ir, shift_ir, |
|||
exit1_ir, pause_ir, exit2_ir, update_ir, |
|||
|
|||
num_states |
|||
}; |
|||
|
|||
static int jtag_step(int state, int tms) |
|||
{ |
|||
static const int next_state[num_states][2] = |
|||
{ |
|||
[test_logic_reset] = {run_test_idle, test_logic_reset}, |
|||
[run_test_idle] = {run_test_idle, select_dr_scan}, |
|||
|
|||
[select_dr_scan] = {capture_dr, select_ir_scan}, |
|||
[capture_dr] = {shift_dr, exit1_dr}, |
|||
[shift_dr] = {shift_dr, exit1_dr}, |
|||
[exit1_dr] = {pause_dr, update_dr}, |
|||
[pause_dr] = {pause_dr, exit2_dr}, |
|||
[exit2_dr] = {shift_dr, update_dr}, |
|||
[update_dr] = {run_test_idle, select_dr_scan}, |
|||
|
|||
[select_ir_scan] = {capture_ir, test_logic_reset}, |
|||
[capture_ir] = {shift_ir, exit1_ir}, |
|||
[shift_ir] = {shift_ir, exit1_ir}, |
|||
[exit1_ir] = {pause_ir, update_ir}, |
|||
[pause_ir] = {pause_ir, exit2_ir}, |
|||
[exit2_ir] = {shift_ir, update_ir}, |
|||
[update_ir] = {run_test_idle, select_dr_scan} |
|||
}; |
|||
|
|||
return next_state[state][tms]; |
|||
} |
|||
|
|||
static int sread(int fd, void *target, int len) |
|||
{ |
|||
unsigned char *t = target; |
|||
while (len) |
|||
{ |
|||
int r = read(fd, t, len); |
|||
if (r <= 0) |
|||
return r; |
|||
t += r; |
|||
len -= r; |
|||
} |
|||
return 1; |
|||
} |
|||
|
|||
//
|
|||
// handle_data(fd) handles JTAG shift instructions.
|
|||
// To allow multiple programs to access the JTAG chain
|
|||
// at the same time, we only allow switching between
|
|||
// different clients only when we're in run_test_idle
|
|||
// after going test_logic_reset. This ensures that one
|
|||
// client can't disrupt the other client's IR or state.
|
|||
//
|
|||
int handle_data(int fd) |
|||
{ |
|||
int i; |
|||
int seen_tlr = 0; |
|||
|
|||
do |
|||
{ |
|||
char cmd[16]; |
|||
unsigned char buffer[2048], result[1024]; |
|||
|
|||
if (sread(fd, cmd, 6) != 1) |
|||
return 1; |
|||
|
|||
if (memcmp(cmd, "shift:", 6)) |
|||
{ |
|||
cmd[6] = 0; |
|||
fprintf(stderr, "invalid cmd '%s'\n", cmd); |
|||
return 1; |
|||
} |
|||
|
|||
int len; |
|||
if (sread(fd, &len, 4) != 1) |
|||
{ |
|||
fprintf(stderr, "reading length failed\n"); |
|||
return 1; |
|||
} |
|||
|
|||
int nr_bytes = (len + 7) / 8; |
|||
if (nr_bytes * 2 > sizeof(buffer)) |
|||
{ |
|||
fprintf(stderr, "buffer size exceeded\n"); |
|||
return 1; |
|||
} |
|||
|
|||
if (sread(fd, buffer, nr_bytes * 2) != 1) |
|||
{ |
|||
fprintf(stderr, "reading data failed\n"); |
|||
return 1; |
|||
} |
|||
|
|||
memset(result, 0, nr_bytes); |
|||
|
|||
if (verbose) |
|||
{ |
|||
printf("#"); |
|||
for (i = 0; i < nr_bytes * 2; ++i) |
|||
printf("%02x ", buffer[i]); |
|||
printf("\n"); |
|||
} |
|||
|
|||
//
|
|||
// Only allow exiting if the state is rti and the IR
|
|||
// has the default value (IDCODE) by going through test_logic_reset.
|
|||
// As soon as going through capture_dr or capture_ir no exit is
|
|||
// allowed as this will change DR/IR.
|
|||
//
|
|||
seen_tlr = (seen_tlr || jtag_state == test_logic_reset) && (jtag_state != capture_dr) && (jtag_state != capture_ir); |
|||
|
|||
|
|||
//
|
|||
// Due to a weird bug(??) xilinx impacts goes through another "capture_ir"/"capture_dr" cycle after
|
|||
// reading IR/DR which unfortunately sets IR to the read-out IR value.
|
|||
// Just ignore these transactions.
|
|||
//
|
|||
|
|||
if ((jtag_state == exit1_ir && len == 5 && buffer[0] == 0x17) || (jtag_state == exit1_dr && len == 4 && buffer[0] == 0x0b)) |
|||
{ |
|||
if (verbose) |
|||
printf("ignoring bogus jtag state movement in jtag_state %d\n", jtag_state); |
|||
} else |
|||
for (i = 0; i < len; ++i) |
|||
{ |
|||
//
|
|||
// Do the actual cycle.
|
|||
//
|
|||
|
|||
int tms = !!(buffer[i/8] & (1<<(i&7))); |
|||
int tdi = !!(buffer[nr_bytes + i/8] & (1<<(i&7))); |
|||
result[i / 8] |= gpio_get(GPIO_TDO) << (i&7); |
|||
gpio_set(GPIO_TMS, tms); |
|||
gpio_set(GPIO_TDI, tdi); |
|||
gpio_set(GPIO_TCK, 1); |
|||
gpio_set(GPIO_TCK, 0); |
|||
|
|||
//
|
|||
// Track the state.
|
|||
//
|
|||
jtag_state = jtag_step(jtag_state, tms); |
|||
} |
|||
|
|||
if (write(fd, result, nr_bytes) != nr_bytes) |
|||
{ |
|||
perror("write"); |
|||
return 1; |
|||
} |
|||
|
|||
if (verbose) |
|||
{ |
|||
printf("jtag state %d\n", jtag_state); |
|||
} |
|||
} while (!(seen_tlr && jtag_state == run_test_idle)); |
|||
return 0; |
|||
} |
|||
|
|||
int main(int argc, char **argv) |
|||
{ |
|||
int i; |
|||
int s; |
|||
int c; |
|||
struct sockaddr_in address; |
|||
|
|||
opterr = 0; |
|||
|
|||
while ((c = getopt(argc, argv, "v")) != -1) |
|||
switch (c) |
|||
{ |
|||
case 'v': |
|||
verbose = 1; |
|||
break; |
|||
case '?': |
|||
fprintf(stderr, "usage: %s [-v]\n", *argv); |
|||
return 1; |
|||
} |
|||
|
|||
//
|
|||
// Initialize GPIOs (mapping them into the process,
|
|||
// re-setting alternate functions, making input/outputs).
|
|||
//
|
|||
|
|||
gpio_init(); |
|||
|
|||
//
|
|||
// Listen on port 2542.
|
|||
//
|
|||
|
|||
s = socket(AF_INET, SOCK_STREAM, 0); |
|||
|
|||
if (s < 0) |
|||
{ |
|||
perror("socket"); |
|||
return 1; |
|||
} |
|||
|
|||
i = 1; |
|||
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof i); |
|||
|
|||
address.sin_addr.s_addr = INADDR_ANY; |
|||
address.sin_port = htons(2542); |
|||
address.sin_family = AF_INET; |
|||
|
|||
if (bind(s, (struct sockaddr*)&address, sizeof(address)) < 0) |
|||
{ |
|||
perror("bind"); |
|||
return 1; |
|||
} |
|||
|
|||
if (listen(s, 0) < 0) |
|||
{ |
|||
perror("listen"); |
|||
return 1; |
|||
} |
|||
|
|||
fd_set conn; |
|||
int maxfd = 0; |
|||
|
|||
FD_ZERO(&conn); |
|||
FD_SET(s, &conn); |
|||
|
|||
maxfd = s; |
|||
|
|||
while (1) |
|||
{ |
|||
fd_set read = conn, except = conn; |
|||
int fd; |
|||
|
|||
//
|
|||
// Look for work to do.
|
|||
//
|
|||
|
|||
if (select(maxfd + 1, &read, 0, &except, 0) < 0) |
|||
{ |
|||
perror("select"); |
|||
break; |
|||
} |
|||
|
|||
for (fd = 0; fd <= maxfd; ++fd) |
|||
{ |
|||
if (FD_ISSET(fd, &read)) |
|||
{ |
|||
//
|
|||
// Readable listen socket? Accept connection.
|
|||
//
|
|||
|
|||
if (fd == s) |
|||
{ |
|||
int newfd; |
|||
socklen_t nsize = sizeof(address); |
|||
|
|||
newfd = accept(s, (struct sockaddr*)&address, &nsize); |
|||
if (verbose) |
|||
printf("connection accepted - fd %d\n", newfd); |
|||
if (newfd < 0) |
|||
{ |
|||
perror("accept"); |
|||
} else |
|||
{ |
|||
if (newfd > maxfd) |
|||
{ |
|||
maxfd = newfd; |
|||
} |
|||
FD_SET(newfd, &conn); |
|||
} |
|||
} |
|||
//
|
|||
// Otherwise, do work.
|
|||
//
|
|||
else if (handle_data(fd)) |
|||
{ |
|||
//
|
|||
// Close connection when required.
|
|||
//
|
|||
|
|||
if (verbose) |
|||
printf("connection closed - fd %d\n", fd); |
|||
close(fd); |
|||
FD_CLR(fd, &conn); |
|||
} |
|||
} |
|||
//
|
|||
// Abort connection?
|
|||
//
|
|||
else if (FD_ISSET(fd, &except)) |
|||
{ |
|||
if (verbose) |
|||
printf("connection aborted - fd %d\n", fd); |
|||
close(fd); |
|||
FD_CLR(fd, &conn); |
|||
if (fd == s) |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
//
|
|||
// Un-map IOs.
|
|||
//
|
|||
|
|||
gpio_close(); |
|||
|
|||
return 0; |
|||
} |
Loading…
Reference in new issue