On 12/08/2013 02:09 AM, David Koontz wrote:
On 8 Dec 2013, at 2:00 am, Brian Drummond<br...@shapes.demon.co.uk> wrote:
The specific import of this - to ghdl - is that ghdl's VHPI interface
really only supports procedure/function calls between languages.
So if you can structure the interaction such that a VHDL process calls a
C function or procedure (void function), that process will block until
the function's return, and that takes zero simulation time (occurs
within a delta cycle).
Thus with the VHDL side in control, the simulation cycle controls the
timing.
There was a ghdl-discuss thread earlier this year, mid April "Calling functions
written in VHDL from a linked C-language-compiled binary" which got slightly
derailed by a post by René Doß, where I modified an assignment statement making a foreign
call from:
Oh I remember it.
Yes I would make a similary connection but inverse. I had to verificate
a CPU written in VHDL.
background:
I have developed a 32bit MIPS softcore. In a development phase I would
control the simulator with a local Ethernet port. I had written a
gdb-server in C a but I could not change signals in VHDL/GHDL for outside.
The GDB server started in the addition process in the testbench.
gdb_init : process
variable i: integer ;
begin
i:=wait_for_connect(2020);
wait;
end process;
The C entry code and VHDL code is attached on this email.
I stopped this debugging tool because I could not initiate signal
changes by the C code.
Now I am over this development phase.
The process is a little bit others.
I have a converter tool, that produce from an elf file (compiled and
Linked Software) an VHDL file.
The OP-codes are lockated in a Block Ram. After all files are VHDL files
and I have to simulate only VHDL files.
The Softcore can you found at:
http://www.dossmatik.de/mais-cpu.html
The Mais CPU runs MIPS-I opcodes. And you can also use gcc as C compiler.
René Doß
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netdb.h>
#include <pthread.h>
#define GDBSTUB_EXECUTION_BREAKPOINT (0xac1)
#define GDBSTUB_TRACE (0xac2)
#define GDBSTUB_USER_BREAK (0xac3)
static int listen_socket_fd;
static int socket_fd;
static int hex(char ch)
{
if ((ch >= 'a') && (ch <= 'f')) return(ch - 'a' + 10);
if ((ch >= '0') && (ch <= '9')) return(ch - '0');
if ((ch >= 'A') && (ch <= 'F')) return(ch - 'A' + 10);
return(-1);
}
static void put_debug_char(char ch)
{
send(socket_fd, &ch, 1, 0);
}
static char get_debug_char(void)
{
char ch;
recv(socket_fd, &ch, 1, 0);
return(ch);
}
static const char hexchars[]="0123456789abcdef";
static void put_reply(char* buffer)
{
unsigned char csum;
int i;
do
{
put_debug_char('$');
csum = 0;
i = 0;
while (buffer[i] != 0)
{
put_debug_char(buffer[i]);
csum = csum + buffer[i];
i++;
}
put_debug_char('#');
put_debug_char(hexchars[csum >> 4]);
put_debug_char(hexchars[csum % 16]);
} while (get_debug_char() != '+');
}
static void get_command(char* buffer)
{
unsigned char checksum;
unsigned char xmitcsum;
char ch;
unsigned int count;
unsigned int i;
do
{
while ((ch = get_debug_char()) != '$');
checksum = 0;
xmitcsum = 0;
count = 0;
while (1)
{
ch = get_debug_char();
if (ch == '#') break;
checksum = checksum + ch;
buffer[count] = ch;
count++;
}
buffer[count] = 0;
if (ch == '#')
{
xmitcsum = hex(get_debug_char()) << 4;
xmitcsum += hex(get_debug_char());
if (checksum != xmitcsum)
{
// BX_INFO (("Bad checksum"));
}
}
if (checksum != xmitcsum)
{
put_debug_char('-');
}
else
{
put_debug_char('+');
if (buffer[2] == ':')
{
put_debug_char(buffer[0]);
put_debug_char(buffer[1]);
count = strlen(buffer);
for (i = 3; i <= count; i++)
{
buffer[i - 3] = buffer[i];
}
}
}
} while (checksum != xmitcsum);
}
void hex2mem(char* buf, unsigned char* mem, int count)
{
int i;
unsigned char ch;
for (i = 0; i<count; i++)
{
ch = hex(*buf++) << 4;
ch = ch + hex(*buf++);
*mem = ch;
mem++;
}
}
char* mem2hex(char* mem, char* buf, int count)
{
int i;
unsigned char ch;
for (i = 0; i<count; i++)
{
ch = *mem;
mem++;
*buf = hexchars[ch >> 4];
buf++;
*buf = hexchars[ch % 16];
buf++;
}
*buf = 0;
return(buf);
}
static int continue_thread = -1;
static int other_thread = 0;
#define NUMREGS (18)
#define NUMREGSBYTES (NUMREGS * 4)
static int registers[NUMREGS];
#define MAX_BREAKPOINTS (255)
static int breakpoints[MAX_BREAKPOINTS] = {0,};
static int nr_breakpoints = 0;
static int stub_trace_flag = 0;
static int instr_count = 0;
static int saved_eip = 0;
int bx_gdbstub_check(unsigned int pc)
{
unsigned int i;
unsigned char ch;
long arg;
int r;
fd_set fds;
struct timeval tv = {0, 0};
instr_count++;
if ((instr_count % 500) == 0)
{
FD_ZERO(&fds);
FD_SET(socket_fd, &fds);
r = select(socket_fd + 1, &fds, NULL, NULL, &tv);
if (r == 1)
{
r = recv(socket_fd, &ch, 1, 0);
}
if (r == 1)
{
// BX_INFO (("Got byte %x", (unsigned int)ch));
// last_stop_reason = GDBSTUB_USER_BREAK;
// return(GDBSTUB_USER_BREAK);
}
}
// why is trace before breakpoints? does that mean it would never
// hit a breakpoint during tracing?
if (stub_trace_flag == 1)
{
// last_stop_reason = GDBSTUB_TRACE;
// return(GDBSTUB_TRACE);
}
for (i = 0; i < nr_breakpoints; i++)
{
if (pc == breakpoints[i])
{
// BX_INFO (("found breakpoint at %x", pc));
// last_stop_reason = GDBSTUB_EXECUTION_BREAKPOINT;
return(GDBSTUB_EXECUTION_BREAKPOINT);
}
}
// last_stop_reason = GDBSTUB_STOP_NO_REASON;
// return(GDBSTUB_STOP_NO_REASON);
}
static int remove_breakpoint(int addr, int len)
{
unsigned int i;
if (len != 4)
{
return(0);
}
for (i = 0; i < MAX_BREAKPOINTS; i++)
{
if (breakpoints[i] == addr)
{
// BX_INFO (("Removing breakpoint at %x", addr));
breakpoints[i] = 0;
return(1);
}
}
return(0);
}
static void insert_breakpoint(int addr)
{
unsigned int i;
// BX_INFO (("setting breakpoint at %x", addr));
for (i = 0; i < MAX_BREAKPOINTS; i++)
{
if (breakpoints[i] == 0)
{
breakpoints[i] = addr;
if (i >= nr_breakpoints)
{
nr_breakpoints = i + 1;
}
return;
}
}
// BX_INFO (("No slot for breakpoint"));
}
static void write_signal(char* buf, int signal)
{
buf[0] = hexchars[signal >> 4];
buf[1] = hexchars[signal % 16];
buf[2] = 0;
}
static int access_linear(int laddress,
unsigned len,
unsigned int rw,
int* data)
{
int phys;
int valid;
if (((laddress & 0xfff) + len) > 4096)
{
valid = access_linear(laddress,
4096 - (laddress & 0xfff),
rw,
data);
if (!valid)
{
return(valid);
}
// valid = access_linear(laddress,
// len + (laddress & 0xfff) - 4096,
// rw,
// (Bit8u *)((unsigned int)data +
// (laddress & 0xfff)));
return(valid);
}
// bx_memsys.pluginMmu->dbg_xlate_linear2phy((Bit32u)laddress,
// (Bit32u*)&phys,
// (bx_bool*)&valid);
if (!valid)
{
return(0);
}
// if (rw == BX_READ)
// {
// valid = BX_MEM(0)->dbg_fetch_mem(phys, len, data);
// }
// else
// {
/// valid = BX_MEM(0)->dbg_set_mem(phys, len, data);
// }
return(valid);
}
void* debug_loop()
{
printf ("debug_loop\n");
char buffer[1024];
char obuf[1024];
int ne;
unsigned char mem[1024];
ne = 0;
while (ne == 0)
{
get_command(buffer);
printf ("get_buffer %s", buffer);
switch (buffer[0])
{
case 'c':
{
char buf[255];
#if 0
int new_eip;
if (buffer[1] != 0)
{
new_eip = atoi(buffer + 1);
//BX_INFO (("continuing at %x", new_eip));
for (int i=0; i<BX_SMP_PROCESSORS; i++) {
BX_CPU(i)->invalidate_prefetch_q();
}
saved_eip = EIP;
BX_CPU(0)->dword.eip = new_eip;
}
#endif
stub_trace_flag = 0;
// bx_cpu.cpu_loop(-1);
// // DEV_vga_refresh();
#if 0
if (buffer[1] != 0)
{
bx_cpu.invalidate_prefetch_q();
BX_CPU_THIS_PTR dword.eip = saved_eip;
}
#endif
// BX_INFO (("stopped with %x", last_stop_reason));
buf[0] = 'S';
/* if (last_stop_reason == GDBSTUB_EXECUTION_BREAKPOINT ||
last_stop_reason == GDBSTUB_TRACE)
{
write_signal(&buf[1], SIGTRAP);
}
else
{
write_signal(&buf[1], 0);
}
put_reply(buf);
break;
*/ }
case 's':
{
char buf[255];
// BX_INFO (("stepping"));
stub_trace_flag = 1;
// bx_cpu.cpu_loop(-1);
// DEV_vga_refresh();
stub_trace_flag = 0;
// BX_INFO (("stopped with %x", last_stop_reason));
buf[0] = 'S';
// if (last_stop_reason == GDBSTUB_EXECUTION_BREAKPOINT ||
// last_stop_reason == GDBSTUB_TRACE)
// {
// write_signal(&buf[1], SIGTRAP);
// }
// else
// {
// write_signal(&buf[1], SIGTRAP);
// }
put_reply(buf);
break;
}
case 'M':
{
int addr;
int len;
unsigned char mem[255];
char* ebuf;
addr = strtoul(&buffer[1], &ebuf, 16);
len = strtoul(ebuf + 1, &ebuf, 16);
hex2mem(ebuf + 1, mem, len);
//BX_INFO(("write addr%8x\tlen %2x,content %8x",addr,len,*(unsigned int *)&mem[0]));
if (len == 4 && *(unsigned int *)&mem[0] == 0xef9f0001)
{
insert_breakpoint(addr);
put_reply("OK");
}
else if (remove_breakpoint(addr, len))
{
put_reply("OK");
}
else
{
/* if (access_linear(addr,
len,
BX_WRITE,
mem))
{
put_reply("OK");
}
else
{
put_reply("ENN");
}
*/ }
break;
}
case 'm':
{
int addr;
int len;
char* ebuf;
addr = strtoul(&buffer[1], &ebuf, 16);
len = strtoul(ebuf + 1, NULL, 16);
printf("addr=%d\n",addr);
printf("len=%d\n",len);
/*
BX_INFO (("read addr %x len %x", addr, len));
access_linear(addr,
len,
BX_READ,
mem);
*/
mem2hex((char *)mem, obuf, len);
put_reply(obuf);
break;
}
case 'P':
{
int reg;
int value;
char* ebuf;
reg = strtoul(&buffer[1], &ebuf, 16);
value = ntohl(strtoul(ebuf + 1, &ebuf, 16));
// BX_INFO (("reg %d set to %x", reg, value));
//if (reg <=15) GPR(reg) = value;
#if 0
switch (reg)
{
case 1:
GPR(1) = value;
break;
case 2:
GPR(2) = value;
break;
case 3:
GPR(3) = value;
break;
case 4:
ESP = value;
break;
case 5:
EBP = value;
break;
case 6:
ESI = value;
break;
case 7:
EDI = value;
break;
case 8:
EIP = value;
break;
default:
break;
}
#endif
put_reply("OK");
break;
}
case 'g':
{
int i;
// for (i=0;i<15;i++) registers[i] = GPR(i);
/* if (last_stop_reason == GDBSTUB_EXECUTION_BREAKPOINT)
{
registers[15] = GPR(15) + 4;
}
else
{
registers[15] = GPR(15);
} */
//registers[17] = BX_CPU(0)->regs.regs_C.cpsr;
// mem2hex((char *)registers, obuf, NUMREGSBYTES);
int regcounter=72;
char * ziel =obuf;
while (regcounter>0)
{
sprintf(ziel,"00000000");
ziel=ziel+8;
regcounter--;
};
put_reply(obuf);
break;
}
case '?':
sprintf(obuf, "S%02x", SIGTRAP);
put_reply(obuf);
break;
case 'H':
if (buffer[1] == 'c')
{
continue_thread = strtol(&buffer[2], NULL, 16);
put_reply("OK");
}
else if (buffer[1] == 'g')
{
other_thread = strtol(&buffer[2], NULL, 16);
put_reply("OK");
}
else
{
put_reply("ENN");
}
break;
case 'q':
if (buffer[1] == 'C')
{
sprintf(obuf,"QC1");
put_reply(obuf);
}
else if (strncmp(&buffer[1], "Offsets", strlen("Offsets")) == 0)
{
sprintf(obuf,"Text=0;Data=0;Bss=0");
put_reply(obuf);
}
else
{
put_reply("ENN");
}
break;
case 'k':
// BX_PANIC (("Debugger asked us to quit\n"));
break;
default:
put_reply("");
break;
}
}
}
static void server_connect(int portn)
{
struct sockaddr_in sockaddr;
socklen_t sockaddr_len;
struct protoent *protoent;
int r;
int opt;
listen_socket_fd = socket(PF_INET, SOCK_STREAM, 0);
if (listen_socket_fd == -1)
{
printf(("Failed to create socket\n"));
exit(1);
}
/* Allow rapid reuse of this port */
opt = 1;
r = setsockopt(listen_socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if (r == -1)
{
// BX_INFO (("setsockopt(SO_REUSEADDR) failed\n"));
}
memset (&sockaddr, '\000', sizeof sockaddr);
#if BX_HAVE_SOCKADDR_IN_SIN_LEN
// if you don't have sin_len change that to #if 0. This is the subject of
// bug [ 626840 ] no 'sin_len' in 'struct sockaddr_in'.
sockaddr.sin_len = sizeof sockaddr;
#endif
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(portn);
sockaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
r = bind(listen_socket_fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
if (r == -1)
{
printf (("Failed to bind socket\n"));
}
r = listen(listen_socket_fd, 0);
if (r == -1)
{
printf(("Failed to listen on socket\n"));
}
sockaddr_len = sizeof sockaddr;
socket_fd = accept(listen_socket_fd, (struct sockaddr *)&sockaddr, &sockaddr_len);
if (socket_fd == -1)
{
printf (("Failed to accept on socket\n"));
}
close(listen_socket_fd);
protoent = getprotobyname ("tcp");
if (!protoent)
{
printf (("getprotobyname (\"tcp\") failed\n"));
return ;
}
/* Disable Nagle - allow small packets to be sent without delay. */
opt = 1;
r = setsockopt (socket_fd, protoent->p_proto, TCP_NODELAY, &opt, sizeof(opt));
if (r == -1)
{
printf (("setsockopt(TCP_NODELAY) failed\n"));
}
}
//static
int wait_for_connect(int portn)
{
//http://www.risc.jku.at/people/schreine/papers/rt++-linuxmag1/main.html
printf ("wait for connect\n");
pthread_t thread1;
server_connect(portn);
// server_connect(2013);
printf("start debug loop\n");
/* Do debugger command loop */
pthread_create(&thread1, NULL, &debug_loop, NULL);
// debug_loop();
return 1;
}
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package gdb_pack is
function wait_for_connect (portn : integer) return integer;
attribute foreign of wait_for_connect :
function is "VHPIDIRECT wait_for_connect";
end;
package body gdb_pack is
function wait_for_connect (portn : integer) return integer is
begin
assert false report "VHPI" severity failure;
end wait_for_connect;
end gdb_pack;
_______________________________________________
Ghdl-discuss mailing list
Ghdl-discuss@gna.org
https://mail.gna.org/listinfo/ghdl-discuss