Browse Source

Initial commit of xvcd, the Xilinx Virtual Cable Driver

master
Felix Domke 13 years ago
commit
7ed6138450
  1. 20
      README
  2. 6
      src/Makefile
  3. 94
      src/gpio.c
  4. 16
      src/gpio.h
  5. 54
      src/gpio_inline.h
  6. 331
      src/xvcd.c

20
README

@ -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!

6
src/Makefile

@ -0,0 +1,6 @@
CFLAGS=-Werror -Wall -Os
all: xvcd
xvcd: gpio.o xvcd.o
$(CC) -o $@ $^ $(CFLAGS)

94
src/gpio.c

@ -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);
}

16
src/gpio.h

@ -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

54
src/gpio_inline.h

@ -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));
}

331
src/xvcd.c

@ -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…
Cancel
Save