Commit ab412e32 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski Committed by Alessandro Rubini

tools: added spec-vuart, spec-fwloader, multiple SPEC support & base address settings

parent 0010068a
......@@ -6,8 +6,13 @@
#define __LOADER_LL_C__ /* Callers won't define this symbol */
#ifdef __KERNEL__
#include "spec.h"
#include "loader-ll.h"
#else
#include "loader-userspace.h"
#endif
/* These must be set to choose the FPGA configuration mode */
#define GPIO_BOOTSEL0 15
......
......@@ -3,9 +3,9 @@ CFLAGS = -ggdb -Wall
LDFLAGS = -L. -lspec
LIB = libspec.a
LIBOBJ = speclib.o
LIBOBJ = speclib.o loader-ll.o
PROGS = specmem spec-cl
PROGS = spec-cl spec-fwloader spec-vuart # specmem
all: $(LIB) $(PROGS)
......@@ -15,6 +15,8 @@ $(PROGS): $(LIB)
$(LIB): $(LIBOBJ)
ar r $@ $^
loader-ll.o: ../kernel/loader-ll.c
${CC} -c $^ -I .
clean:
rm -f *.o $(LIB) $(PROGS) *~
\ No newline at end of file
/*
* This header differentiates between kernel-mode and user-mode compilation,
* as loader-ll.c is meant to be used in both contexts.
*/
#ifndef __iomem
#define __iomem /* nothing, for user space */
#endif
extern int loader_low_level(
int fd, /* This is ignored in kernel space */
void __iomem *bar4, /* This is ignored in user space */
const void *data,
int size8);
/* Registers from the gennum header files */
enum {
GNGPIO_BASE = 0xA00,
GNGPIO_DIRECTION_MODE = GNGPIO_BASE + 0x4,
GNGPIO_OUTPUT_ENABLE = GNGPIO_BASE + 0x8,
GNGPIO_OUTPUT_VALUE = GNGPIO_BASE + 0xC,
GNGPIO_INPUT_VALUE = GNGPIO_BASE + 0x10,
FCL_BASE = 0xB00,
FCL_CTRL = FCL_BASE,
FCL_STATUS = FCL_BASE + 0x4,
FCL_IODATA_IN = FCL_BASE + 0x8,
FCL_IODATA_OUT = FCL_BASE + 0xC,
FCL_EN = FCL_BASE + 0x10,
FCL_TIMER_0 = FCL_BASE + 0x14,
FCL_TIMER_1 = FCL_BASE + 0x18,
FCL_CLK_DIV = FCL_BASE + 0x1C,
FCL_IRQ = FCL_BASE + 0x20,
FCL_TIMER_CTRL = FCL_BASE + 0x24,
FCL_IM = FCL_BASE + 0x28,
FCL_TIMER2_0 = FCL_BASE + 0x2C,
FCL_TIMER2_1 = FCL_BASE + 0x30,
FCL_DBG_STS = FCL_BASE + 0x34,
FCL_FIFO = 0xE00,
PCI_SYS_CFG_SYSTEM = 0x800
};
/* The following part implements a different access rule for user and kernel */
#include <stdio.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <errno.h>
static inline void lll_write(int fd, void __iomem *bar4, uint32_t val, int reg)
{
*(volatile uint32_t *)(bar4+reg) = val;
}
static inline uint32_t lll_read(int fd, void __iomem *bar4, int reg)
{
return *(volatile uint32_t *)(bar4 + reg);
}
#define KERN_ERR /* nothing */
#define printk(format, ...) fprintf (stderr, format, ## __VA_ARGS__)
......@@ -10,6 +10,7 @@
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/mman.h>
......@@ -20,61 +21,64 @@
int main(int argc, char **argv)
{
int i, fd;
FILE *f;
uint32_t base;
int bus = -1, dev_fn = -1, i, c;
struct stat stbuf;
void *map_base;
unsigned char *buf;
unsigned int *ibuf;
volatile uint32_t *p;
void *map_base;
char *fname;
uint32_t lm32_base = 0x80000;
FILE *f;
if (argc != 2) {
fprintf(stderr,
"Use: \"%s <program>\"\n", argv[0]);
exit(1);
}
if((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
fprintf(stderr, "%s: open(/dev/mem): %s\n", argv[0],
strerror(errno));
exit(1);
}
base = spec_get_base(BASE_BAR0);
if (base == (typeof(base))-1) {
fprintf(stderr, "%s: spec_get_base(): %s\n", argv[0],
strerror(errno));
exit(1);
while ((c = getopt (argc, argv, "b:d:c:")) != -1)
{
switch(c)
{
case 'b':
sscanf(optarg, "%i", &bus);
break;
case 'd':
sscanf(optarg, "%i", &dev_fn);
break;
case 'c':
sscanf(optarg, "%i", &lm32_base);
break;
default:
fprintf(stderr,
"Use: \"%s [-b bus] [-d devfn] <fpga_bitstream.bin>\"\n", argv[0]);
fprintf(stderr,
"By default, the first available SPEC is used and the LM32 is assumet at 0x%x.\n", lm32_base);
exit(1);
}
}
map_base = mmap(0, 1024 * 1024, /* gennum's bar 0 is 1M */
PROT_READ | PROT_WRITE, MAP_SHARED, fd, base);
if(map_base == (void *) -1) {
fprintf(stderr, "%s: mmap(/dev/mem): %s\n", argv[0],
strerror(errno));
if (optind >= argc) {
fprintf(stderr, "Expected binary name after options.\n");
exit(1);
}
fname = argv[optind];
close(fd);
f = fopen(argv[1], "r");
f = fopen(fname, "r");
if (!f) {
fprintf(stderr, "%s: %s: %s\n", argv[0], argv[1],
fprintf(stderr, "%s: %s: %s\n", argv[0], fname,
strerror(errno));
exit(1);
}
if (fstat(fileno(f), &stbuf)) {
fprintf(stderr, "%s: stat(%s): %s\n", argv[0], argv[1],
fprintf(stderr, "%s: stat(%s): %s\n", argv[0], fname,
strerror(errno));
exit(1);
}
if (!S_ISREG(stbuf.st_mode)) {
fprintf(stderr, "%s: %s: Not a regular file\n", argv[0],
argv[1]);
fname);
exit(1);
}
buf = malloc(stbuf.st_size);
if (!buf) {
fprintf(stderr, "%s: Can't allocate buffer (%li bytes): %s\n",
......@@ -83,27 +87,36 @@ int main(int argc, char **argv)
}
i = fread(buf, 1, stbuf.st_size, f);
if (i < 0) {
fprintf(stderr, "%s: read(%s): %s\n", argv[0], argv[1],
fprintf(stderr, "%s: read(%s): %s\n", argv[0], fname,
strerror(errno));
exit(1);
}
if (i != stbuf.st_size) {
fprintf(stderr, "%s: short read from %s\n", argv[0], argv[1]);
fprintf(stderr, "%s: short read from %s\n", argv[0], fname);
exit(1);
}
map_base = spec_map_area(bus, dev_fn, BASE_BAR0, 0x100000);
if(!map_base)
{
fprintf(stderr, "%s: can't map the SPEC @ %02x:%02x\n", argv[0], bus, dev_fn);
exit(1);
}
ibuf = (void *)buf;
/* Phew... we are there, finally */
*(volatile uint32_t *)(map_base + 0xA0400) = 0x1deadbee;
while ( !((*(volatile uint32_t *)(map_base + 0xA0400)) & (1<<28)) )
;
*(volatile uint32_t *)(map_base + lm32_base + 0x20400) = 0x1deadbee;
while ( !((*(volatile uint32_t *)(map_base + lm32_base + 0x20400)) & (1<<28)) )
;
p = map_base + 0x80000;
for (i = 0; i < (stbuf.st_size + 3) / 4; i++) {
p = map_base + lm32_base;
for (i = 0; i < (stbuf.st_size + 3) / 4; i++) {
p[i] = htonl(ibuf[i]); /* big endian */
sync();
}
sync();
for (i = 0; i < (stbuf.st_size + 3) / 4; i++) {
if (p[i] != htonl(ibuf[i]))
fprintf(stderr, "programming error at %x "
......@@ -111,10 +124,12 @@ int main(int argc, char **argv)
htonl(ibuf[i]), p[i]);
}
*(volatile uint32_t *)(map_base + 0xA0400) = 0x0deadbee;
sync();
*(volatile uint32_t *)(map_base + lm32_base + 0x20400) = 0x0deadbee;
if (getenv("VERBOSE"))
printf("%s: Wrote %li bytes at offset 0x8000\n", argv[0],
(long)stbuf.st_size);
printf("%s: Wrote %li bytes at offset 0x%x\n", argv[0],
(long)stbuf.st_size, lm32_base);
exit (0);
}
/*
* A tool to program our soft-core (LM32) within the SPEC.
*
* Alessandro Rubini 2012 for CERN, GPLv2 or later.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "spec-tools.h"
int main(int argc, char **argv)
{
int bus = -1, dev_fn = -1, i, c;
struct stat stbuf;
unsigned char *buf;
void *map_base;
char *fname;
FILE *f;
while ((c = getopt (argc, argv, "b:d:")) != -1)
{
switch(c)
{
case 'b':
sscanf(optarg, "%i", &bus);
break;
case 'd':
sscanf(optarg, "%i", &dev_fn);
break;
default:
fprintf(stderr,
"Use: \"%s [-b bus] [-d devfn] <fpga_bitstream.bin>\"\n", argv[0]);
fprintf(stderr,
"By default, the first available SPEC is used.\n");
exit(1);
}
}
if (optind >= argc) {
fprintf(stderr, "Expected binary name after options.\n");
exit(1);
}
fname = argv[optind];
f = fopen(fname, "r");
if (!f) {
fprintf(stderr, "%s: %s: %s\n", argv[0], fname,
strerror(errno));
exit(1);
}
if (fstat(fileno(f), &stbuf)) {
fprintf(stderr, "%s: stat(%s): %s\n", argv[0], fname,
strerror(errno));
exit(1);
}
if (!S_ISREG(stbuf.st_mode)) {
fprintf(stderr, "%s: %s: Not a regular file\n", argv[0],
fname);
exit(1);
}
buf = malloc(stbuf.st_size);
if (!buf) {
fprintf(stderr, "%s: Can't allocate buffer (%li bytes): %s\n",
argv[0], (long)stbuf.st_size, strerror(errno));
exit(1);
}
i = fread(buf, 1, stbuf.st_size, f);
if (i < 0) {
fprintf(stderr, "%s: read(%s): %s\n", argv[0], fname,
strerror(errno));
exit(1);
}
if (i != stbuf.st_size) {
fprintf(stderr, "%s: short read from %s\n", argv[0], fname);
exit(1);
}
map_base = spec_map_area(bus, dev_fn, BASE_BAR4, 0x1000);
if(!map_base)
{
fprintf(stderr, "%s: can't map the SPEC @ %02x:%02x\n", argv[0], bus, dev_fn);
exit(1);
}
loader_low_level(0, map_base, buf, stbuf.st_size);
exit (0);
}
uint32_t spec_get_base(int basenr);
#include "loader-userspace.h"
void *spec_map_area(int bus, int dev, int bar, size_t size);
enum {
BASE_BAR0 = 0, /* for wrpc etc (but lm32 is at 0x80000 offset) */
BASE_BAR2,
BASE_BAR4 /* for gennum-internal registers */
};
BASE_BAR2 = 2,
BASE_BAR4 = 4 /* for gennum-internal registers */
};
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <getopt.h>
#include <errno.h>
#include "spec-tools.h"
#include "wb_uart.h"
void *vuart_regs;
int vc_rx(void *ptr)
{
int csr ;
csr = *(int *)(ptr + UART_REG_HOST_RDR);
if(csr & UART_HOST_RDR_RDY)
return UART_HOST_RDR_DATA_R(csr);
else
return -1;
}
void vc_tx(void *ptr, int c)
{
*(int *)(ptr + UART_REG_HOST_TDR) = UART_HOST_TDR_DATA_W(c);
}
int transfer_byte(int from, int is_control, void *ptr) {
char c;
int ret;
do {
ret = read(from, &c, 1);
} while (ret < 0 && errno == EINTR);
if(ret == 1) {
if(is_control) {
if(c == '\x01') { // C-a
return -1;
}
}
vc_tx(ptr, c);
} else {
fprintf(stderr, "\nnothing to read. probably port disconnected.\n");
return -2;
}
return 0;
}
void term_main(int keep_term)
{
struct termios oldkey, newkey; //place tor old and new port settings for keyboard teletype
int need_exit = 0;
fprintf(stderr, "[press C-a to exit]\n");
if(!keep_term) {
tcgetattr(STDIN_FILENO,&oldkey);
newkey.c_cflag = B9600 | CS8 | CLOCAL | CREAD;
newkey.c_iflag = IGNPAR;
newkey.c_oflag = 0;
newkey.c_lflag = 0;
newkey.c_cc[VMIN]=1;
newkey.c_cc[VTIME]=0;
tcflush(STDIN_FILENO, TCIFLUSH);
tcsetattr(STDIN_FILENO,TCSANOW,&newkey);
}
while(!need_exit) {
fd_set fds;
int ret, rx;
struct timeval tv = {0, 10000};
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
ret = select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
if(ret == -1) {
perror("select");
} else if (ret > 0) {
if(FD_ISSET(STDIN_FILENO, &fds)) {
need_exit = transfer_byte(STDIN_FILENO, 1, vuart_regs);
}
}
while((rx = vc_rx(vuart_regs)) >= 0)
fprintf(stderr,"%c", (char)rx);
}
if(!keep_term)
tcsetattr(STDIN_FILENO,TCSANOW,&oldkey);
}
int main(int argc, char **argv)
{
int bus = -1, dev_fn = -1, c;
uint32_t vuart_base = 0xe0500;
void *map_base;
int keep_term = 0;
while ((c = getopt (argc, argv, "b:d:u:k")) != -1)
{
switch(c)
{
case 'b':
sscanf(optarg, "%i", &bus);
break;
case 'd':
sscanf(optarg, "%i", &dev_fn);
break;
case 'u':
sscanf(optarg, "%i", &vuart_base);
break;
case 'k':
keep_term = 1;
break;
default:
fprintf(stderr,
"Use: \"%s [-b bus] [-d devfn] [-u VUART base] [-k]\"\n", argv[0]);
fprintf(stderr,
"By default, the first available SPEC is used and the VUART is assumed at 0x%x.\n \
-k option keeps the terminal config unchanged.", vuart_base);
exit(1);
}
}
map_base = spec_map_area(bus, dev_fn, BASE_BAR0, 0x100000);
if(!map_base)
{
fprintf(stderr, "%s: can't map the SPEC @ %02x:%02x\n", argv[0], bus, dev_fn);
exit(1);
}
vuart_regs = map_base + vuart_base;
term_main(keep_term);
return 0;
}
......@@ -4,33 +4,81 @@
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <errno.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include "spec-tools.h"
uint32_t spec_get_base(int base)
/* Checks if there's a SPEC card at *bus/*def_fn. If one (or both) parameters are < 0, takes first available card and returns 0.
If no cards have been detected, returns -1 */
int spec_scan(int *bus, int *devfn)
{
FILE *f;
int found = 0;
unsigned int res = ~0;
char s[160];
f = popen("lspci -v", "r");
while(fgets(s, sizeof(s), f)) {
if (!found) {
if (strstr(s, "Device 1a39:0004"))
found = 1;
if (strstr(s, "Device 10dc:018d"))
found = 1;
}
if (found && sscanf(s, " Memory at %x", &res) == 1) {
if (!base)
break;
base--;
struct dirent **namelist;
int n, found = 0;
int my_bus, my_devfn;
n = scandir("/sys/bus/pci/drivers/spec", &namelist, 0, 0);
if (n < 0)
{
perror("scandir");
exit(-1);
} else {
while (n--)
{
if(!found && sscanf(namelist[n]->d_name, "0000:%02x:%02x.0", &my_bus, &my_devfn) == 2)
{
if(*bus < 0) *bus = my_bus;
if(*devfn < 0) *devfn = my_devfn;
if(*bus == my_bus && *devfn == my_devfn)
found = 1;
}
free(namelist[n]);
}
free(namelist);
}
if(!found)
{
fprintf(stderr,"Can't detect any SPEC card :(\n");
return -1;
}
return 0;
}
/* Maps a particular BAR of given SPEC card and returns its virtual address
(or NULL in case of failure) */
void *spec_map_area(int bus, int dev, int bar, size_t size)
{
char path[1024];
int fd;
void *ptr;
if(spec_scan(&bus, &dev) < 0)
return NULL;
snprintf(path, sizeof(path), "/sys/bus/pci/drivers/spec/0000:%02x:%02x.0/resource%d", bus, dev, bar);
fd = open(path, O_RDWR | O_SYNC);
if(fd <= 0)
return NULL;
ptr = mmap(NULL, size & ~(getpagesize()-1), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if((int)ptr == -1)
{
close(fd);
return NULL;
}
pclose(f);
if (res == ~0)
errno = ENODEV;
return res;
return ptr;
}
/*
Register definitions for slave core: Simple Wishbone UART
* File : wb_uart.h
* Author : auto-generated by wbgen2 from simple_uart_wb.wb
* Created : Thu May 3 17:36:38 2012
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE simple_uart_wb.wb
DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
*/
#ifndef __WBGEN2_REGDEFS_SIMPLE_UART_WB_WB
#define __WBGEN2_REGDEFS_SIMPLE_UART_WB_WB
#include <inttypes.h>
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
#else
#error "Unsupported compiler?"
#endif
#ifndef __WBGEN2_MACROS_DEFINED__
#define __WBGEN2_MACROS_DEFINED__
#define WBGEN2_GEN_MASK(offset, size) (((1<<(size))-1) << (offset))
#define WBGEN2_GEN_WRITE(value, offset, size) (((value) & ((1<<(size))-1)) << (offset))
#define WBGEN2_GEN_READ(reg, offset, size) (((reg) >> (offset)) & ((1<<(size))-1))
#define WBGEN2_SIGN_EXTEND(value, bits) (((value) & (1<<bits) ? ~((1<<(bits))-1): 0 ) | (value))
#endif
/* definitions for register: Status Register */
/* definitions for field: TX busy in reg: Status Register */
#define UART_SR_TX_BUSY WBGEN2_GEN_MASK(0, 1)
/* definitions for field: RX ready in reg: Status Register */
#define UART_SR_RX_RDY WBGEN2_GEN_MASK(1, 1)
/* definitions for register: Baudrate control register */
/* definitions for register: Transmit data regsiter */
/* definitions for field: Transmit data in reg: Transmit data regsiter */
#define UART_TDR_TX_DATA_MASK WBGEN2_GEN_MASK(0, 8)
#define UART_TDR_TX_DATA_SHIFT 0
#define UART_TDR_TX_DATA_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define UART_TDR_TX_DATA_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: Receive data regsiter */
/* definitions for field: Received data in reg: Receive data regsiter */
#define UART_RDR_RX_DATA_MASK WBGEN2_GEN_MASK(0, 8)
#define UART_RDR_RX_DATA_SHIFT 0
#define UART_RDR_RX_DATA_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define UART_RDR_RX_DATA_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for register: Host VUART Tx register */
/* definitions for field: TX Data in reg: Host VUART Tx register */
#define UART_HOST_TDR_DATA_MASK WBGEN2_GEN_MASK(0, 8)
#define UART_HOST_TDR_DATA_SHIFT 0
#define UART_HOST_TDR_DATA_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define UART_HOST_TDR_DATA_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for field: TX Ready in reg: Host VUART Tx register */
#define UART_HOST_TDR_RDY WBGEN2_GEN_MASK(8, 1)
/* definitions for register: Host VUART Rx register */
/* definitions for field: RX Data in reg: Host VUART Rx register */
#define UART_HOST_RDR_DATA_MASK WBGEN2_GEN_MASK(0, 8)
#define UART_HOST_RDR_DATA_SHIFT 0
#define UART_HOST_RDR_DATA_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define UART_HOST_RDR_DATA_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for field: RX Ready in reg: Host VUART Rx register */
#define UART_HOST_RDR_RDY WBGEN2_GEN_MASK(8, 1)
/* definitions for field: RX FIFO Count in reg: Host VUART Rx register */
#define UART_HOST_RDR_COUNT_MASK WBGEN2_GEN_MASK(9, 16)
#define UART_HOST_RDR_COUNT_SHIFT 9
#define UART_HOST_RDR_COUNT_W(value) WBGEN2_GEN_WRITE(value, 9, 16)
#define UART_HOST_RDR_COUNT_R(reg) WBGEN2_GEN_READ(reg, 9, 16)
#define UART_REG_HOST_TDR 0x10
#define UART_REG_HOST_RDR 0x14
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment