URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
Compare Revisions
- This comparison shows the changes necessary to convert path
/openrisc/trunk/orpsocv2/bench
- from Rev 6 to Rev 40
- ↔ Reverse comparison
Rev 6 → Rev 40
/verilog/orpsoc_testbench.v
124,9 → 124,6
// Tie off some inputs |
assign spi1_miso_i = 0; |
assign uart0_srx_i = 1; |
assign dbg_tdi_i = 1; |
assign dbg_tck_i = 0; |
assign dbg_tms_i = 1; |
|
|
orpsoc_top dut |
179,6 → 176,22
.rst_pad_i (rst_i), |
.clk_pad_i (clk_i)); |
|
`ifdef VPI_DEBUG_ENABLE |
// Debugging interface |
vpi_debug_module vpi_dbg( |
.tms(dbg_tms_i), |
.tck(dbg_tck_i), |
.tdi(dbg_tdi_i), |
.tdo(dbg_tdo_o)); |
`else |
// If no VPI debugging, tie off JTAG inputs |
assign dbg_tdi_i = 1; |
assign dbg_tck_i = 0; |
assign dbg_tms_i = 1; |
`endif |
|
|
|
// External memories, if enabled |
`ifdef USE_SDRAM |
// SPI Flash |
/verilog/vpi/c/rsp-rtl_sim.h
0,0 → 1,158
/*$$HEADER*/ |
/******************************************************************************/ |
/* */ |
/* H E A D E R I N F O R M A T I O N */ |
/* */ |
/******************************************************************************/ |
|
// Project Name : ORPSoCv2 |
// File Name : rsp-rtl_sim.h |
// Prepared By : jb, jb@orsoc.se |
// Project Start : 2009-05-01 |
|
/*$$COPYRIGHT NOTICE*/ |
/******************************************************************************/ |
/* */ |
/* C O P Y R I G H T N O T I C E */ |
/* */ |
/******************************************************************************/ |
/* |
This library is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; |
version 2.1 of the License, a copy of which is available from |
http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt. |
|
This library 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 |
Lesser General Public License for more details. |
|
You should have received a copy of the GNU Lesser General Public |
License along with this library; if not, write to the Free Software |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
*/ |
|
#ifndef _RSP_RTL_SIM_H_ |
#define _RSP_RTL_SIM_H_ |
|
#include <stdint.h> // For uint32_t types |
|
//#define DEBUG 1 |
//#define DEBUG2 1 |
|
#define Boolean int |
#define false 0 |
#define true 1 |
|
#ifdef DEBUG |
#define debug printf |
#else |
#define debug |
#endif |
|
#ifdef DEBUG2 |
#define debug2 printf |
#else |
#define debug2 |
#endif |
|
|
#define DBG_ON 0 |
|
#define DBG_VPI 0 |
|
extern int vpi_to_rsp_pipe[2]; // [0] - read, [1] - write |
extern int rsp_to_vpi_pipe[2]; // [0] - read, [1] - write |
extern int command_pipe[2]; // RSP end writes, VPI end reads ONLY |
|
#if (DEBUG) || (DEBUG2) |
#define flush_debug() fflush(stdout) |
#else |
#define flush_debug() |
#endif |
|
# define JTAG_WAIT() usleep(1000) |
# define JTAG_RETRY_WAIT() usleep (1000) |
|
/* Selects crc trailer size in bits. Currently supported: 8 */ |
#define CRC_SIZE (8) |
|
/* Scan chain size in bits. */ |
#define SC_SIZE (4) |
|
/* function to kick off this server */ |
void run_rsp_server(int); |
|
/* read a word from wishbone */ |
int dbg_wb_read32(uint32_t adr, uint32_t *data); |
|
/* write a word to wishbone */ |
int dbg_wb_write32(uint32_t adr, uint32_t data); |
|
/* read a block from wishbone */ |
int dbg_wb_read_block32(uint32_t adr, uint32_t *data, int len); |
|
/* write a block to wishbone */ |
int dbg_wb_write_block32(uint32_t adr, uint32_t *data, int len); |
|
/* read a register from cpu */ |
int dbg_cpu0_read(uint32_t adr, uint32_t *data); |
|
/* read a register from cpu module */ |
int dbg_cpu0_read_ctrl(uint32_t adr, unsigned char *data); |
|
/* write a cpu register */ |
int dbg_cpu0_write(uint32_t adr, uint32_t data); |
|
/* write a cpu module register */ |
int dbg_cpu0_write_ctrl(uint32_t adr, unsigned char data); |
|
/* send a message to the sim that the debugging client has disconnected */ |
void dbg_client_detached(void); |
|
#define DC_SIZE 4 |
#define DC_STATUS_SIZE 4 |
|
#define DC_WISHBONE 0 |
#define DC_CPU0 1 |
#define DC_CPU1 2 |
|
#define DI_GO 0 |
#define DI_READ_CMD 1 |
#define DI_WRITE_CMD 2 |
#define DI_READ_CTRL 3 |
#define DI_WRITE_CTRL 4 |
|
#define DBG_CRC_SIZE 32 |
#define DBG_CRC_POLY 0x04c11db7 |
|
#define DBG_ERR_OK 0 |
#define DBG_ERR_CRC 8 |
|
#define NUM_SOFT_RETRIES 3 |
#define NUM_HARD_RETRIES 3 |
#define NUM_ACCESS_RETRIES 10 |
|
/* Possible errors are listed here. */ |
enum enum_errors /* modified <chris@asics.ws> CZ 24/05/01 */ |
{ |
/* Codes > 0 are for system errors */ |
|
ERR_NONE = 0, |
ERR_CRC = -1, |
ERR_MEM = -2, |
JTAG_PROXY_INVALID_COMMAND = -3, |
JTAG_PROXY_SERVER_TERMINATED = -4, |
JTAG_PROXY_NO_CONNECTION = -5, |
JTAG_PROXY_PROTOCOL_ERROR = -6, |
JTAG_PROXY_COMMAND_NOT_IMPLEMENTED = -7, |
JTAG_PROXY_INVALID_CHAIN = -8, |
JTAG_PROXY_INVALID_ADDRESS = -9, |
JTAG_PROXY_ACCESS_EXCEPTION = -10, /* Write to ROM */ |
JTAG_PROXY_INVALID_LENGTH = -11, |
JTAG_PROXY_OUT_OF_MEMORY = -12, |
}; |
|
#endif |
|
/verilog/vpi/c/gdb.h
0,0 → 1,189
/*$$HEADER*/ |
/******************************************************************************/ |
/* */ |
/* H E A D E R I N F O R M A T I O N */ |
/* */ |
/******************************************************************************/ |
|
// Project Name : ORPSoCv2 |
// File Name : gdb.h |
// Prepared By : jb, rmd |
// Project Start : 2008-10-01 |
|
/*$$COPYRIGHT NOTICE*/ |
/******************************************************************************/ |
/* */ |
/* C O P Y R I G H T N O T I C E */ |
/* */ |
/******************************************************************************/ |
/* |
This library is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; |
version 2.1 of the License, a copy of which is available from |
http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt. |
|
This library 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 |
Lesser General Public License for more details. |
|
You should have received a copy of the GNU Lesser General Public |
License along with this library; if not, write to the Free Software |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
*/ |
|
/*$$CHANGE HISTORY*/ |
/******************************************************************************/ |
/* */ |
/* C H A N G E H I S T O R Y */ |
/* */ |
/******************************************************************************/ |
|
// Date Version Description |
//------------------------------------------------------------------------ |
// 081101 First revision, adapted from existing "jp" |
// debug proxy. jb, rmd |
// 090608 A few hacks for VPI compatibilty added jb |
|
#ifndef GDB_H |
#define GDB_H |
|
#include <sys/types.h> |
#include <inttypes.h> |
|
extern void HandleServerSocket(void); |
extern void handle_rsp (void); |
int GetServerSocket(const char* name, const char* proto, int port); |
extern void JTAGRequest(void); |
void setup_or32(void); |
void gdb_close(); |
void rsp_set_server_port(int); |
|
//extern int err; |
|
/* All JTAG chains. */ |
enum jtag_chains |
{ |
SC_GLOBAL, /* 0 Global BS Chain */ |
SC_RISC_DEBUG, /* 1 RISC Debug Interface chain */ |
SC_RISC_TEST, /* 2 RISC Test Chain */ |
SC_TRACE, /* 3 Trace Chain */ |
SC_REGISTER, /* 4 Register Chain */ |
SC_WISHBONE, /* 5 Memory chain */ |
SC_BLOCK, /* 6 Block Chains */ |
}; |
|
/* See JTAG documentation about these. */ |
#define JI_SIZE (4) |
enum jtag_instr |
{ |
JI_EXTEST, |
JI_SAMPLE_PRELOAD, |
JI_IDCODE, |
JI_CHAIN_SELECT, |
JI_INTEST, |
JI_CLAMP, |
JI_CLAMPZ, |
JI_HIGHZ, |
JI_DEBUG, |
JI_BYPASS = 0xF |
}; |
|
/* JTAG registers. */ |
#define JTAG_MODER (0x0) |
#define JTAG_TSEL (0x1) |
#define JTAG_QSEL (0x2) |
#define JTAG_SSEL (0x3) |
#define JTAG_RISCOP (0x4) |
#define JTAG_RECWP0 (0x10) |
#define JTAG_RECBP0 (0x1b) |
|
/* This is repeated from gdb tm-or1k.h There needs to be |
a better mechanism for tracking this, but I don't see |
an easy way to share files between modules. */ |
|
typedef enum { |
JTAG_COMMAND_READ = 1, |
JTAG_COMMAND_WRITE = 2, |
JTAG_COMMAND_BLOCK_READ = 3, |
JTAG_COMMAND_BLOCK_WRITE = 4, |
JTAG_COMMAND_CHAIN = 5, |
} JTAG_proxy_protocol_commands; |
|
/* Each transmit structure must begin with an integer |
which specifies the type of command. Information |
after this is variable. Make sure to have all information |
aligned properly. If we stick with 32 bit integers, it |
should be portable onto every platform. These structures |
will be transmitted across the network in network byte |
order. |
*/ |
|
typedef struct { |
uint32_t command; |
uint32_t length; |
uint32_t address; |
uint32_t data_H; |
uint32_t data_L; |
} JTAGProxyWriteMessage; |
|
typedef struct { |
uint32_t command; |
uint32_t length; |
uint32_t address; |
} JTAGProxyReadMessage; |
|
typedef struct { |
uint32_t command; |
uint32_t length; |
uint32_t address; |
int32_t nRegisters; |
uint32_t data[1]; |
} JTAGProxyBlockWriteMessage; |
|
typedef struct { |
uint32_t command; |
uint32_t length; |
uint32_t address; |
int32_t nRegisters; |
} JTAGProxyBlockReadMessage; |
|
typedef struct { |
uint32_t command; |
uint32_t length; |
uint32_t chain; |
} JTAGProxyChainMessage; |
|
/* The responses are messages specific, however convention |
states the first word should be an error code. Again, |
sticking with 32 bit integers should provide maximum |
portability. */ |
|
typedef struct { |
int32_t status; |
} JTAGProxyWriteResponse; |
|
typedef struct { |
int32_t status; |
uint32_t data_H; |
uint32_t data_L; |
} JTAGProxyReadResponse; |
|
typedef struct { |
int32_t status; |
} JTAGProxyBlockWriteResponse; |
|
typedef struct { |
int32_t status; |
int32_t nRegisters; |
uint32_t data[1]; |
/* uint32_t data[nRegisters-1] still unread */ |
} JTAGProxyBlockReadResponse; |
|
typedef struct { |
int32_t status; |
} JTAGProxyChainResponse; |
|
|
#endif /* GDB_H */ |
/verilog/vpi/c/rsp-vpi.h
0,0 → 1,74
/*$$HEADER*/ |
/******************************************************************************/ |
/* */ |
/* H E A D E R I N F O R M A T I O N */ |
/* */ |
/******************************************************************************/ |
|
// Project Name : ORPSoCv2 |
// File Name : rsp-vpi.h |
// Prepared By : jb, jb@orsoc.se |
// Project Start : 2009-05-01 |
|
/*$$COPYRIGHT NOTICE*/ |
/******************************************************************************/ |
/* */ |
/* C O P Y R I G H T N O T I C E */ |
/* */ |
/******************************************************************************/ |
/* |
This library is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; |
version 2.1 of the License, a copy of which is available from |
http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt. |
|
This library 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 |
Lesser General Public License for more details. |
|
You should have received a copy of the GNU Lesser General Public |
License along with this library; if not, write to the Free Software |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
*/ |
|
// Defines for protocol ensuring synchronisation with the simulation process |
|
// 1. rsp-rtl_sim will first write a command byte |
// 2. rsp-rtl_sim will then send the address if it's a read or write |
// or the data to write if it's a dbg_cpu_wr_ctrl (stall & reset bits) |
// 3. will then send data if we're writing and we sent address in 2 |
// 4. wait for response from vpi functions |
|
// commands: |
// 4'h1 jtag set instruction register (input: instruction value) |
// 4'h2 set debug chain (dbg_set_command here) (input: chain value) |
// 4'h3 cpu_ctrl_wr (input: ctrl value (2 bits)) |
// 4'h4 cpu_ctrl_rd (output: ctrl value (2bits)) |
// 4'h5 cpu wr reg (inputs: address, data) |
// 4'h6 cpu rd reg (input: address; output: data) |
// 4'h7 wb wr 32 (inputs: address, data) |
// 4'h8 wb rd 32 (input: address; output: data) |
// 4'h9 wb wr block 32 (inputs: address, length, data) |
// 4'ha wb rd block 32 (inputs: address, length; output: data) |
// 4'hb reset |
// 4'hc read jtag id (output: data) |
// 4'hd GDB detach - do something (like close down, restart, etc.) |
|
// There should be a correlating set of verilog `define's in the |
// verilog debug testbench module's include file, test_defines.v |
|
#define CMD_JTAG_SET_IR 0x1 |
#define CMD_SET_DEBUG_CHAIN 0x2 |
#define CMD_CPU_CTRL_WR 0x3 |
#define CMD_CPU_CTRL_RD 0x4 |
#define CMD_CPU_WR_REG 0x5 |
#define CMD_CPU_RD_REG 0x6 |
#define CMD_WB_WR32 0x7 |
#define CMD_WB_RD32 0x8 |
#define CMD_WB_BLOCK_WR32 0x9 |
#define CMD_WB_BLOCK_RD32 0xa |
#define CMD_RESET 0xb |
#define CMD_READ_JTAG_ID 0xc |
#define CMD_GDB_DETACH 0xd |
/verilog/vpi/c/jp_vpi.c
0,0 → 1,1064
/*$$HEADER*/ |
/******************************************************************************/ |
/* */ |
/* H E A D E R I N F O R M A T I O N */ |
/* */ |
/******************************************************************************/ |
|
// Project Name : ORPSoCv2 |
// File Name : jp_vpi.c |
// Prepared By : jb, jb@orsoc.se |
// Project Start : 2009-05-01 |
|
/*$$COPYRIGHT NOTICE*/ |
/******************************************************************************/ |
/* */ |
/* C O P Y R I G H T N O T I C E */ |
/* */ |
/******************************************************************************/ |
/* |
This library is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; |
version 2.1 of the License, a copy of which is available from |
http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt. |
|
This library 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 |
Lesser General Public License for more details. |
|
You should have received a copy of the GNU Lesser General Public |
License along with this library; if not, write to the Free Software |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
*/ |
|
/*$$DESCRIPTION*/ |
/******************************************************************************/ |
/* */ |
/* D E S C R I P T I O N */ |
/* */ |
/******************************************************************************/ |
// |
// Implements communication between verilog simulator and RSP server |
// |
// |
/* |
Functions used via the Verilog Procedural Interface (VPI), in a verilog |
simulation of an OpenRISC processor, providing a means of communication to the |
simulatin for a GDB stub. |
|
The communication between the GDB stub and this module is via a "custom" |
protocol, which is decoded into the verilog debug task module and the |
appropriate transactions are performed with the debug interface inside the |
OpenRISC design. |
|
Operation: |
See the verilog file containing the calls to the VPI tasks we outline in this |
file for exact details of how they are used (debug_vpi_module.v), but |
following is a brief outline of how it is meant to work. |
|
The RSP GDB stub is initialised after compile via a VPI callback |
(cbEndOfCompile). A process is forked off to run the RSP server, and IPC is via |
pipes. Note: This is probably an extremely ineffecient way to do this as the |
fork() creates a copy of the program, including it's ~200MB memory space, so |
maybe a different method should be worked out for massive sims, but for smaller |
OpenRISC designs on a power machine this is not a problem. |
|
The verilog debug module polls for incoming commands from the GDB stub at a |
#delay rate set in the verilog code. |
|
The port which the GDB server runs on is #define'd in this file by |
RSP_SERVER_PORT. |
|
When a GDB connection is established, the state of the processor is downloaded |
by GDB, so expect a slight delay after connection. |
|
To close down the simulation gracefully, issue a "detach" command from GDB. |
This will close the connection with the stub and will also send a message to |
$finish the simulation. |
|
Note: Simulation reset is untested, but should probably work OK. |
|
Note: Reading uninitialised memory which returns Xs will break things. |
Specifically, the CRC generation breaks, and then many other things. So to help |
avoid hours of X tracing, ensure you're reading initialised space from GDB. |
|
To Do: |
* Comment this better! Sorry, it's a little lacking in this area. |
* Block transfers (ie. what happens when "load"ing from GDB) ignore any bytes |
over the word boundary at the end of a transfer. Currently a warning printf |
will appear. |
* Make the RSP server process not be a complete copy of the vvp image - ie. |
don't fork() in the sim, maybe compile and exec() a separate app for this. |
|
*/ |
|
|
/* EXAMPLE |
// Associate C Function with a New System Task |
voi |
d registerHelloSystfs() { |
s_vpi_systf_data task_data_s; |
p_vpi_systf_data task_data_p = &task_data_s; |
task_data_p->type = vpiSysTask; |
task_data_p->tfname = "$hello"; |
task_data_p->calltf = hello; |
task_data_p->compiletf = 0; |
|
vpi_register_systf(task_data_p); |
} |
|
*/ |
|
|
/* |
To associate your C function with a system task, create a |
data structure of type s_vpi_systf_data and a pointer to |
that structure. The vpi_systf_data data type is defined in |
the vpi_user.h include file. Below is the data structure |
of s_vpi_systf_data. |
|
typedef struct t_vpi_systf_data { |
PLI_INT32 type; // vpiSysTask, vpiSysFunc - task not return val, Func does |
PLI_INT32 sysfunctype; // vpiSysTask, vpi[Int,Real,Time,Sized, SizedSigned]Func - if it's a func, this is the typ of return |
PLI_BYTE8 *tfname; // First character must be `$' |
PLI_INT32 (*calltf)(PLI_BYTE8 *); //pointer to the function |
PLI_INT32 (*compiletf)(PLI_BYTE8 *); // pointer to a function that the simulator calls |
// when it's compiled - can be NULL |
PLI_INT32 (*sizetf)(PLI_BYTE8 *); // For sized function callbacks only, This field is a |
// pointer to a routine that returns the size, in bits, |
// of the value that the system task or function returns. |
PLI_BYTE8 *user_data; --optional extra data? |
} s_vpi_systf_data, *p_vpi_systf_data; |
*/ |
|
|
|
/*$$CHANGE HISTORY*/ |
/******************************************************************************/ |
/* */ |
/* C H A N G E H I S T O R Y */ |
/* */ |
/******************************************************************************/ |
// Date Version Description |
//------------------------------------------------------------------------ |
// 090501 Imported code from "jp" VPI project jb |
// Changed to use pipes instead of sockets jb |
|
|
|
|
#include <stdio.h> |
#include <stdlib.h> |
#include <errno.h> |
#include <unistd.h> |
#include <sys/types.h> |
#include <sys/wait.h> |
#include <sys/select.h> |
#include <sys/poll.h> |
#include <sys/stat.h> |
#include <fcntl.h> |
#include <ctype.h> |
#include <string.h> |
#include <stdarg.h> |
#include <stdint.h> |
#include <signal.h> |
#include <inttypes.h> |
|
// VPI includes |
#include <vpi_user.h> |
|
#ifdef CDS_VPI |
// Cadence ncverilog specific include |
#include <vpi_user_cds.h> |
#endif |
|
// Includes for the RSP server side of things |
#include "gdb.h" |
#include "rsp-rtl_sim.h" |
#include "rsp-vpi.h" |
|
// Define the port we open the RSP server on |
#define RSP_SERVER_PORT 5555 |
|
//Function to register the function which sets up the sockets interface |
void register_init_rsp_server_functions() ; |
// Function which sets up the socket interface |
void init_rsp_server(); |
//install a callback on simulation reset which calls setup |
void setup_reset_callbacks(); |
//install a callback on simulation compilation finish |
void setup_endofcompile_callbacks(); |
//install a callback on simulation finish |
void setup_finish_callbacks(); |
//callback function which closes and clears the socket file descriptors |
// on simulation reset |
void sim_reset_callback(); |
void sim_endofcompile_callback(); |
void sim_finish_callback(); |
|
void register_check_for_command(); |
void register_get_command_address(); |
void register_get_command_data(); |
void register_return_command_block_data(); |
void register_return_command_data(); |
void register_get_command_block_data(); |
void register_return_response(); |
|
void check_for_command(); |
void get_command_address(); |
void get_command_data(); |
void get_command_block_data(); |
void return_command_data(); |
void return_command_block_data(); |
void return_response(); |
|
|
#include <time.h> |
|
int vpi_to_rsp_pipe[2]; // [0] - read, [1] - write |
int rsp_to_vpi_pipe[2]; // [0] - read, [1] - write |
int command_pipe[2]; // RSP end writes, VPI end reads ONLY |
|
/* Global static to store the child rsp server PID if we want to kill it */ |
static pid_t rsp_server_child_pid = (pid_t) 0; // pid_t is just a signed int |
|
|
/********************************************************************/ |
/* init_rsp_server |
* |
* Fork off the rsp server process |
* / |
/********************************************************************/ |
void init_rsp_server(){ |
|
|
// First get the port number to start the RSP server on |
|
vpiHandle systfref, args_iter, argh; |
|
struct t_vpi_value argval; |
|
int value,i; |
|
int n; |
|
int portNum; |
|
char* send_buf; |
|
/* |
|
// Currently removed - ability to call $rsp_init_server() with a |
// port number as parameter. Hardcoded allows us to run this as |
// a callback after compiile (cbEndOfCompile) |
|
// Obtain a handle to the argument list |
systfref = vpi_handle(vpiSysTfCall, NULL); |
|
// Now call iterate with the vpiArgument parameter |
args_iter = vpi_iterate(vpiArgument, systfref); |
|
// get a handle on the object passed to the function |
argh = vpi_scan(args_iter); |
|
// now store the command value back in the sim |
argval.format = vpiIntVal; |
|
// Now set the data value |
vpi_get_value(argh, &argval); |
|
portNum = (int) argval.value.integer; |
|
// Cleanup and return |
vpi_free_object(args_iter); |
|
// We should now have our port number. |
|
*/ |
|
// Fork. Let the child run the RSP server |
pid_t pid; |
int rv; |
|
if(DBG_ON) printf("jp_vpi: init_rsp_server\n"); |
|
// Setup pipes |
if(pipe(vpi_to_rsp_pipe) == -1) |
{ |
perror("jp_vpi: init_rsp_server pipes"); |
exit(1); |
} |
if(pipe(rsp_to_vpi_pipe) == -1) |
{ |
perror("jp_vpi: init_rsp_server pipes"); |
exit(1); |
} |
if(pipe(command_pipe) == -1) |
{ |
perror("jp_vpi: init_rsp_server pipes"); |
exit(1); |
} |
|
// Set command pipe to be non-blocking |
#if defined (STRIDE) || (defined (pfa) && defined (HAVE_PTYS)) || defined (AIX) |
{ |
int one = 1; |
ioctl (command_pipe[0], FIONBIO, &one); |
} |
#endif |
|
#ifdef O_NONBLOCK /* The POSIX way */ |
fcntl (command_pipe[0], F_SETFL, O_NONBLOCK); |
#elif defined (O_NDELAY) |
fcntl (command_pipe[0], F_SETFL, O_NDELAY); |
#endif /* O_NONBLOCK */ |
|
// Check on the child process. If it has not been started it will |
// be 0, else it will be something else and we'll just return |
if ((int) rsp_server_child_pid > 0) |
return; |
|
switch(pid=fork()) |
{ |
case -1: |
perror("fork"); |
exit(1); |
break; |
case 0: // Child |
run_rsp_server(RSP_SERVER_PORT); |
// exit if it ever returns, which it shouldn't |
exit(0); |
break; |
default: |
// We're the parent process, so continue on. |
rsp_server_child_pid = pid; |
break; |
} |
|
// Parent will only ever get this far... |
|
return; |
|
} |
|
void register_init_rsp_server_functions() { |
s_vpi_systf_data data = {vpiSysTask, |
0, |
"$init_rsp_server", |
(void *)init_rsp_server, |
0, |
0, |
0}; |
|
vpi_register_systf(&data); |
|
return; |
} |
|
void print_command_string(unsigned char cmd) |
{ |
switch(cmd) |
{ |
case 0x1 : |
printf(" TAP instruction register set\n"); |
break; |
case 0x2 : |
printf(" set debug chain\n"); |
break; |
case 0x3 : |
printf(" CPU control (stall/reset) reg write\n"); |
break; |
case 0x4 : |
printf(" CPU control (stall/reset) reg read\n"); |
break; |
case 0x5 : |
printf(" CPU reg write\n"); |
break; |
case 0x6 : |
printf(" CPU reg read\n"); |
break; |
case 0x7 : |
printf(" WB write 32\n"); |
break; |
case 0x8 : |
printf(" WB read 32\n"); |
break; |
case 0x9 : |
printf(" WB block write 32\n"); |
break; |
case 0xa : |
printf(" WB block read 32\n"); |
break; |
case 0xb : |
printf(" reset\n"); |
break; |
case 0xc : |
printf(" read jtag id\n"); |
break; |
} |
} |
|
// See if there's anything on the FIFO for us |
|
void check_for_command(char *userdata){ |
|
vpiHandle systfref, args_iter, argh; |
|
struct t_vpi_value argval; |
|
int value,i; |
|
int n; |
|
unsigned char data; |
|
//if(DBG_ON) printf("check_for_command\n"); |
|
//n = read(rsp_to_vpi_pipe[0], &data, 1); |
|
n = read(command_pipe[0], &data, 1); |
|
if ( ((n < 0) && (errno == EAGAIN)) || (n==0) ) |
{ |
// Nothing in the fifo this time, let's return |
return; |
} |
else if (n < 0) |
{ |
// some sort of error |
perror("check_for_command"); |
|
exit(1); |
} |
|
if (DBG_ON) |
{ |
printf("jp_vpi: c = %x:",data); |
print_command_string(data); |
fflush(stdout); |
} |
|
// Return the command to the sim |
|
// Obtain a handle to the argument list |
systfref = vpi_handle(vpiSysTfCall, NULL); |
|
// Now call iterate with the vpiArgument parameter |
args_iter = vpi_iterate(vpiArgument, systfref); |
|
// get a handle on the variable passed to the function |
argh = vpi_scan(args_iter); |
|
// now store the command value back in the sim |
argval.format = vpiIntVal; |
|
// Now set the command value |
vpi_get_value(argh, &argval); |
|
argval.value.integer = (unsigned int) data; |
|
// And vpi_put_value() it back into the sim |
vpi_put_value(argh, &argval, NULL, vpiNoDelay); |
|
// Cleanup and return |
vpi_free_object(args_iter); |
|
n = write(vpi_to_rsp_pipe[1],&data,1); |
if (DBG_ON) printf("jp_vpi: r"); |
|
if (DBG_ON) printf("\n"); |
|
return; |
} |
|
void get_command_address(char *userdata){ |
|
vpiHandle systfref, args_iter, argh; |
|
struct t_vpi_value argval; |
|
int value,i; |
|
int n; |
|
unsigned int data; |
|
char* recv_buf; |
|
recv_buf = (char *) &data; // cast data as our receive char buffer |
|
n = read(rsp_to_vpi_pipe[0],recv_buf,4); |
if (n<0) |
{ |
//client has closed connection |
//attempt to close and return gracefully |
return; |
} |
|
if (DBG_ON) printf("jp_vpi: get_command_address adr=0x%.8x\n",data); |
|
// now put the address into the argument passed to the task |
|
// Obtain a handle to the argument list |
systfref = vpi_handle(vpiSysTfCall, NULL); |
|
// Now call iterate with the vpiArgument parameter |
args_iter = vpi_iterate(vpiArgument, systfref); |
|
// get a handle on the variable passed to the function |
argh = vpi_scan(args_iter); |
|
// now store the command value back in the sim |
argval.format = vpiIntVal; |
|
// Now set the address value |
vpi_get_value(argh, &argval); |
argval.value.integer = (unsigned int) data; |
|
// And vpi_put_value() it back into the sim |
vpi_put_value(argh, &argval, NULL, vpiNoDelay); |
|
// Cleanup and return |
vpi_free_object(args_iter); |
|
return; |
|
} |
|
void get_command_data(char *userdata){ |
|
vpiHandle systfref, args_iter, argh; |
|
struct t_vpi_value argval; |
|
int value,i; |
|
int n = 0; |
|
unsigned int data; |
|
char* recv_buf; |
|
recv_buf = (char *) &data; // cast data as our receive char buffer |
|
read_command_data_again: |
n = read(rsp_to_vpi_pipe[0],recv_buf,4); |
|
if ((n < 4) && errno==EAGAIN) |
goto read_command_data_again; |
else if (n < 4) |
{ |
printf("jp_vpi: get_command_data errno: %d\n",errno); |
perror("jp_vpi: get_command_data read failed"); |
} |
if (DBG_ON) printf("jp_vpi: get_command_data = 0x%.8x\n",data); |
|
// Obtain a handle to the argument list |
systfref = vpi_handle(vpiSysTfCall, NULL); |
|
// Now call iterate with the vpiArgument parameter |
args_iter = vpi_iterate(vpiArgument, systfref); |
|
// get a handle on the variable passed to the function |
argh = vpi_scan(args_iter); |
|
// now store the command value back in the sim |
argval.format = vpiIntVal; |
|
// Now set the data value |
vpi_get_value(argh, &argval); |
argval.value.integer = (unsigned int) data; |
|
// And vpi_put_value() it back into the sim |
vpi_put_value(argh, &argval, NULL, vpiNoDelay); |
|
// Cleanup and return |
vpi_free_object(args_iter); |
|
return; |
|
} |
|
|
void get_command_block_data(){ // $get_command_block_data(length, mem_array) |
|
vpiHandle systfref, args_iter, argh; |
|
struct t_vpi_value argval; |
|
int value,i; |
|
int n; |
|
unsigned int data; |
unsigned int length; |
|
char* recv_buf; |
|
// Now setup the handles to verilog objects and check things |
// Obtain a handle to the argument list |
systfref = vpi_handle(vpiSysTfCall, NULL); |
|
// Now call iterate with the vpiArgument parameter |
args_iter = vpi_iterate(vpiArgument, systfref); |
|
// get a handle on the length variable |
argh = vpi_scan(args_iter); |
|
argval.format = vpiIntVal; |
|
// get the value for the length object |
vpi_get_value(argh, &argval); |
|
// now set length |
length = argval.value.integer; |
|
int num_words = length/4; |
|
if((length % 4) != 0) vpi_printf("length of %d bytes is not exactly word-aligned\n",length); |
// If non-word aligned we throw away remainder |
int throw_away_bytes = length %4; |
|
int loaded_words = 0; |
|
if(DBG_ON)printf("jp_vpi: get_command_block_data: length=%d, num_words=%d\n",length,num_words); |
|
// now get a handle on the next object (memory array) |
argh = vpi_scan(args_iter); |
|
// check we got passed a memory (array of regs) |
if (vpi_get(vpiType, argh) != vpiMemory) |
{ |
vpi_printf("jp_vpi: ERROR: did not pass a memory to get_command_block_data\n"); |
return; |
} |
|
// check the memory we're writing into is big enough |
if (vpi_get(vpiSize, argh) < num_words ) |
{ |
vpi_printf("jp_vpi: ERROR: buffer passed to get_command_block_data too small. size is %d words, needs to be %d\n", |
vpi_get(vpiSize, argh), num_words); |
return; |
} |
|
vpiHandle array_word; |
|
// Loop to load the words |
while (loaded_words < num_words) { |
|
recv_buf = (char *) &data; |
|
// blocking receive for data block |
n = read(rsp_to_vpi_pipe[0],recv_buf,4); |
|
// now get a handle on the current word we want in the array that was passed to us |
array_word = vpi_handle_by_index(argh, loaded_words); |
|
if (array_word != NULL) |
{ |
argval.value.integer = (unsigned int) data; |
|
// And vpi_put_value() it back into the sim |
vpi_put_value(array_word, &argval, NULL, vpiNoDelay); |
} |
else |
return; |
|
loaded_words++; |
} |
// TODO: This is a quick fix, should be delt with properly!! |
if (throw_away_bytes) |
{ |
//printf("reading off %d extra data bytes\n",throw_away_bytes); |
n = read(rsp_to_vpi_pipe[0],&data,throw_away_bytes); |
//printf("read off %d bytes \n",n); |
} |
|
// Cleanup and return |
vpi_free_object(args_iter); |
|
return; |
|
} |
|
void return_command_data(char *userdata){ |
|
vpiHandle systfref, args_iter, argh; |
|
struct t_vpi_value argval; |
|
int value,i; |
|
int n; |
|
uint32_t data; |
|
char* send_buf; |
|
// Obtain a handle to the argument list |
systfref = vpi_handle(vpiSysTfCall, NULL); |
|
// Now call iterate with the vpiArgument parameter |
args_iter = vpi_iterate(vpiArgument, systfref); |
|
// get a handle on the object passed to the function |
argh = vpi_scan(args_iter); |
|
// now store the command value back in the sim |
argval.format = vpiIntVal; |
|
// Now set the data value |
vpi_get_value(argh, &argval); |
|
data = (unsigned int) argval.value.integer; |
|
// Cleanup and return |
vpi_free_object(args_iter); |
|
if (DBG_ON) printf("jp_vpi: return_command_data 0x%.8x\n",data); |
|
send_buf = (char *) &data; //cast our long as a char buf |
|
// write the data back |
n = write(vpi_to_rsp_pipe[1],send_buf,4); |
|
return; |
|
} |
|
void return_command_block_data(){ |
|
vpiHandle systfref, args_iter, argh; |
|
struct t_vpi_value argval; |
|
int value,i; |
|
int n; |
|
unsigned int data; |
unsigned int length; |
|
char* recv_buf; |
|
// Now setup the handles to verilog objects and check things |
// Obtain a handle to the argument list |
systfref = vpi_handle(vpiSysTfCall, NULL); |
|
// Now call iterate with the vpiArgument parameter |
args_iter = vpi_iterate(vpiArgument, systfref); |
|
// get a handle on the length variable |
argh = vpi_scan(args_iter); |
|
argval.format = vpiIntVal; |
|
// get the value for the length object |
vpi_get_value(argh, &argval); |
|
// now set length |
length = argval.value.integer; |
|
// now get a handle on the next object (memory array) |
argh = vpi_scan(args_iter); |
|
// check we got passed a memory (array of regs) |
if (vpi_get(vpiType, argh) != vpiMemory) |
{ |
vpi_printf("jp_vpi: ERROR: did not pass a memory to return_command_block_data\n"); |
return; |
} |
|
vpiHandle array_word; |
|
int num_words = length/4; |
|
int sent_words = 0; |
|
// Loop to load the words |
while (sent_words < num_words) { |
|
// now get a handle on the current word we want in the array that was passed to us |
array_word = vpi_handle_by_index(argh, sent_words); |
|
if (array_word != NULL) |
{ |
vpi_get_value(array_word, &argval); |
|
data = (unsigned int) argval.value.integer; |
} |
else |
return; |
|
recv_buf = (char *) &data; |
|
n = write(vpi_to_rsp_pipe[1],recv_buf,4); |
|
sent_words++; |
|
} |
|
// Cleanup and return |
vpi_free_object(args_iter); |
|
return; |
|
} |
|
|
|
void return_response(char *userdata){ |
|
int n; |
|
char resp = 0; |
|
// send a response byte |
n = write(vpi_to_rsp_pipe[1],&resp,1); |
|
if (DBG_ON) printf("jp_vpi: ret\n\n"); |
|
return; |
|
} |
|
void register_check_for_command() { |
s_vpi_systf_data data = {vpiSysTask, |
0, |
"$check_for_command", |
(void *)check_for_command, |
0, |
0, |
0}; |
|
vpi_register_systf(&data); |
|
return; |
} |
|
void register_get_command_address() { |
s_vpi_systf_data data = {vpiSysTask, |
0, |
"$get_command_address", |
(void *)get_command_address, |
0, |
0, |
0}; |
|
vpi_register_systf(&data); |
|
return; |
} |
|
void register_get_command_data() { |
s_vpi_systf_data data = {vpiSysTask, |
0, |
"$get_command_data", |
(void *)get_command_data, |
0, |
0, |
0}; |
|
vpi_register_systf(&data); |
|
return; |
} |
|
void register_get_command_block_data() { |
s_vpi_systf_data data = {vpiSysTask, |
0, |
"$get_command_block_data", |
(void *)get_command_block_data, |
0, |
0, |
0}; |
|
vpi_register_systf(&data); |
|
return; |
} |
|
|
void register_return_command_block_data() { |
s_vpi_systf_data data = {vpiSysTask, |
0, |
"$return_command_block_data", |
(void *)return_command_block_data, |
0, |
0, |
0}; |
|
vpi_register_systf(&data); |
|
return; |
} |
|
void register_return_command_data() { |
s_vpi_systf_data data = {vpiSysTask, |
0, |
"$return_command_data", |
(void *)return_command_data, |
0, |
0, |
0}; |
|
vpi_register_systf(&data); |
|
return; |
} |
|
void register_return_response() { |
s_vpi_systf_data data = {vpiSysTask, |
0, |
"$return_response", |
(void *)return_response, |
0, |
0, |
0}; |
|
vpi_register_systf(&data); |
|
return; |
} |
|
|
void setup_reset_callbacks() |
{ |
|
// here we setup and install callbacks for |
// the setup and management of connections to |
// the simulator upon simulation start and |
// reset |
|
static s_vpi_time time_s = {vpiScaledRealTime}; |
static s_vpi_value value_s = {vpiBinStrVal}; |
static s_cb_data cb_data_s = |
{ |
cbEndOfReset, // or start of simulation - initing socket fds etc |
(void *)sim_reset_callback, |
NULL, |
&time_s, |
&value_s |
}; |
|
cb_data_s.obj = NULL; /* trigger object */ |
|
cb_data_s.user_data = NULL; |
|
// actual call to register the callback |
vpi_register_cb(&cb_data_s); |
|
} |
|
void sim_reset_callback() |
{ |
|
// nothing to do! |
|
return; |
|
} |
|
void setup_endofcompile_callbacks() |
{ |
|
// here we setup and install callbacks for |
// simulation finish |
|
static s_vpi_time time_s = {vpiScaledRealTime}; |
static s_vpi_value value_s = {vpiBinStrVal}; |
static s_cb_data cb_data_s = |
{ |
cbEndOfCompile, // end of compile |
(void *)sim_endofcompile_callback, |
NULL, |
&time_s, |
&value_s |
}; |
|
cb_data_s.obj = NULL; /* trigger object */ |
|
cb_data_s.user_data = NULL; |
|
// actual call to register the callback |
vpi_register_cb(&cb_data_s); |
|
} |
|
void sim_endofcompile_callback() |
{ |
// Init the RSP server |
init_rsp_server(); // Start the RSP server from here! |
|
} |
|
|
void setup_finish_callbacks() |
{ |
|
// here we setup and install callbacks for |
// simulation finish |
|
static s_vpi_time time_s = {vpiScaledRealTime}; |
static s_vpi_value value_s = {vpiBinStrVal}; |
static s_cb_data cb_data_s = |
{ |
cbEndOfSimulation, // end of simulation |
(void *)sim_finish_callback, |
NULL, |
&time_s, |
&value_s |
}; |
|
cb_data_s.obj = NULL; /* trigger object */ |
|
cb_data_s.user_data = NULL; |
|
// actual call to register the callback |
vpi_register_cb(&cb_data_s); |
|
} |
|
void sim_finish_callback() |
{ |
printf("Closing RSP server\n"); |
// Close down the child process, if it hasn't already |
kill(rsp_server_child_pid,SIGTERM); |
} |
|
|
|
// Register the new system task here |
void (*vlog_startup_routines[ ] ) () = { |
register_init_rsp_server_functions, |
#ifdef CDS_VPI |
// this installs a callback on simulator reset - something which |
// icarus does not do, so we only do it for cadence currently |
setup_reset_callbacks, |
#endif |
setup_endofcompile_callbacks, |
setup_finish_callbacks, |
register_check_for_command, |
register_get_command_address, |
register_get_command_data, |
register_get_command_block_data, |
register_return_command_data, |
register_return_command_block_data, |
register_return_response, |
0 // last entry must be 0 |
}; |
|
|
|
// Entry point for testing development of the vpi functions |
int main(int argc, char *argv[]) |
{ |
|
return 0; |
|
} |
|
|
|
/verilog/vpi/c/rsp-rtl_sim.c
0,0 → 1,605
/*$$HEADER*/ |
/******************************************************************************/ |
/* */ |
/* H E A D E R I N F O R M A T I O N */ |
/* */ |
/******************************************************************************/ |
|
// Project Name : ORPSoCv2 |
// File Name : rsp-rtl_sim.c |
// Prepared By : jb, jb@orsoc.se |
// Project Start : 2009-05-01 |
|
/*$$COPYRIGHT NOTICE*/ |
/******************************************************************************/ |
/* */ |
/* C O P Y R I G H T N O T I C E */ |
/* */ |
/******************************************************************************/ |
/* |
This library is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; |
version 2.1 of the License, a copy of which is available from |
http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt. |
|
This library 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 |
Lesser General Public License for more details. |
|
You should have received a copy of the GNU Lesser General Public |
License along with this library; if not, write to the Free Software |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
*/ |
|
|
/* Establishes GDB proxy server and communicates via pipes |
to some functions running under the VPI in an RTL sim */ |
|
#include <stdio.h> |
#include <sys/stat.h> |
#include <sys/types.h> |
#include <assert.h> |
|
#include <ctype.h> |
#include <stdint.h> |
#include <string.h> |
#include <stdlib.h> |
#include <unistd.h> |
#include <stdarg.h> |
#include <fcntl.h> |
#include <errno.h> |
#include <signal.h> |
|
#include "gdb.h" |
#include "rsp-rtl_sim.h" |
#include "rsp-vpi.h" |
|
static int err = 0; |
|
/* Currently selected scan chain - just to prevent unnecessary |
transfers. */ |
static int current_chain = -1; |
|
/* The chain that should be currently selected. */ |
static int dbg_chain = -1; |
|
/* Crc of current read or written data. */ |
static int crc_r, crc_w = 0; |
|
/* Generates new crc, sending in new bit input_bit */ |
static uint32_t crc_calc(uint32_t crc, int input_bit) { |
uint32_t d = (input_bit&1) ? 0xfffffff : 0x0000000; |
uint32_t crc_32 = ((crc >> 31)&1) ? 0xfffffff : 0x0000000; |
crc <<= 1; |
return crc ^ (d ^ crc_32) & DBG_CRC_POLY; |
} |
|
/* VPI communication prototyopes */ |
static void get_response_from_vpi(); |
static void get_data_from_vpi(); |
static void get_block_data_from_vpi(int len, uint32_t *data); |
static void send_data_to_vpi(uint32_t data); |
static void send_block_data_to_vpi(int len, uint32_t *data); |
static void send_address_to_vpi(uint32_t address); |
static void send_command_to_vpi(char CMD); |
|
void send_command_to_vpi(char CMD) |
{ |
// first thing we do is send a command |
// and wait for an ack |
int n; |
char cmd_resp; |
|
|
//n = write(rsp_to_vpi_pipe[1],&CMD, 1); // send the command to the sim |
n = write(command_pipe[1],&CMD, 1); // send the command to the sim |
|
if (n < 0) error("ERROR writing to VPI FIFO"); |
|
n = read(vpi_to_rsp_pipe[0],&cmd_resp,1); // block and wait for the ack |
|
if (cmd_resp != CMD) error("Response from RTL sim incorrect"); //check it acked with cmd |
|
return; |
} |
|
void send_address_to_vpi(uint32_t address) |
{ |
// Now send address |
int n; |
|
char* send_buf; |
|
send_buf = (char *) &address; |
|
n = write(rsp_to_vpi_pipe[1],send_buf, 4); // send the address to the sim |
|
if (n < 0) error("ERROR writing to VPI socket"); |
|
return; |
} |
|
void send_data_to_vpi(uint32_t data) |
{ |
// Now send data |
int n; |
|
char* send_buf; |
|
send_buf = (char *) &data; |
|
n = write(rsp_to_vpi_pipe[1],send_buf, 4); // Write the data to the socket |
|
if (n < 0) error("ERROR writing to VPI socket"); |
|
return; |
|
} |
|
void send_block_data_to_vpi(int len, uint32_t *data) |
{ |
// Now send data |
int n, i; |
|
char* send_buf; |
|
send_buf = (char *) data; |
|
n = write(rsp_to_vpi_pipe[1],send_buf, len); // Write the data to the fifo |
|
if (n < 0) error("ERROR writing to VPI socket"); |
|
return; |
|
} |
|
void get_data_from_vpi(uint32_t* data) |
{ |
|
int n; |
|
uint32_t inc_data; |
|
char* recv_buf; |
|
recv_buf = (char*) &inc_data; |
|
n = read(vpi_to_rsp_pipe[0],recv_buf,4); // block and wait for the data |
|
if (DBG_VPI) printf("rsp-rtl_sim: get_data_from_vpi: 0x%.8x\n",inc_data); |
|
*data = inc_data; |
|
return; |
|
} |
|
void get_block_data_from_vpi(int len, uint32_t* data) |
{ |
int n, i, status; |
|
uint32_t inc_data; |
|
char* recv_buf; |
|
uint32_t* data_ptr; |
|
recv_buf = (char *) data; |
|
n=0; |
|
while (n < len) |
{ |
|
status = read(vpi_to_rsp_pipe[0], recv_buf, len - n); // block and wait for the data |
|
if (status > 0) n += status; // we read "status" number of bytes |
|
} |
|
|
if (DBG_VPI){ |
printf("rsp-rtl_sim: get_block_data_from_vpi: %d bytes",len); |
for (i = 0;i < (len/4); i++) |
{ |
printf("0x%.8x ",data[i]); |
} |
printf("\n"); |
} |
|
return; |
|
} |
|
void get_response_from_vpi() |
{ |
// Basically just wait for the response from VPI |
// by blocking wait on recv |
|
int n = 0; |
char tmp; |
|
n = read(vpi_to_rsp_pipe[0],&tmp,1); // block and wait |
|
return; |
} |
|
/* Resets JTAG |
Writes TRST=0 |
and TRST=1 */ |
static void jp2_reset_JTAG() { |
int i; |
|
debug2("\nreset("); |
|
send_command_to_vpi(CMD_RESET); |
|
get_response_from_vpi(); |
|
debug2(")\n"); |
|
} |
|
/* Resets JTAG, and sets DEBUG scan chain */ |
int dbg_reset() { |
|
int err; |
uint32_t id; |
|
jp2_reset_JTAG(); |
|
/* set idcode jtag chain */ |
send_command_to_vpi(CMD_JTAG_SET_IR); |
|
send_data_to_vpi(JI_IDCODE); |
|
get_response_from_vpi(); |
|
/* now read out the jtag id */ |
send_command_to_vpi(CMD_READ_JTAG_ID); |
|
//id = get_data_from_vpi(); |
get_data_from_vpi((uint32_t *)&id); |
|
get_response_from_vpi(); |
|
printf("JTAG ID = %08x\n", id); |
|
/* now set the chain to debug */ |
send_command_to_vpi(CMD_JTAG_SET_IR); |
|
send_data_to_vpi(JI_DEBUG); |
|
get_response_from_vpi(); |
|
current_chain = -1; |
return DBG_ERR_OK; |
} |
|
/* counts retries and returns zero if we should abort */ |
/* TODO: dinamically adjust timings for jp2 */ |
static int retry_no = 0; |
int retry_do() { |
int i, err; |
printf("RETRY\n"); |
//exit(2); |
if (retry_no >= NUM_SOFT_RETRIES) { |
if ((err = dbg_reset())) return err; |
} |
if (retry_no >= NUM_SOFT_RETRIES + NUM_HARD_RETRIES) { |
retry_no = 0; |
return 0; |
} |
retry_no++; |
return 1; |
} |
|
/* resets retry counter */ |
void retry_ok() { |
retry_no = 0; |
} |
|
/* Sets scan chain. */ |
int dbg_set_chain(int chain) { |
|
debug("\n"); |
debug2("dbg_set_chain %d\n", chain); |
|
if (current_chain == chain) |
return DBG_ERR_OK; |
|
dbg_chain = chain; |
|
send_command_to_vpi(CMD_SET_DEBUG_CHAIN); |
|
send_data_to_vpi(chain); |
|
get_response_from_vpi(); |
|
current_chain = chain; |
|
return DBG_ERR_OK; |
} |
|
/* writes a ctrl reg */ |
int dbg_ctrl(int reset, int stall) |
{ |
|
debug("\n"); |
debug2("ctrl\n"); |
|
dbg_set_chain(dbg_chain); |
|
send_command_to_vpi(CMD_CPU_CTRL_WR); |
|
//send_data_to_vpi(((reset & 0x1) | ((stall&0x1)<<1))); |
send_data_to_vpi(((stall & 0x1) | ((reset&0x1)<<1))); |
|
get_response_from_vpi(); |
|
return DBG_ERR_OK; |
} |
|
/* reads control register */ |
int dbg_ctrl_read(int *reset, int *stall) |
{ |
|
uint32_t resp; |
|
dbg_set_chain(dbg_chain); |
|
debug("\n"); |
debug2("ctrl\n"); |
|
dbg_set_chain(dbg_chain); |
|
send_command_to_vpi(CMD_CPU_CTRL_RD); |
|
get_data_from_vpi((uint32_t *)&resp); |
|
if (DBG_VPI) printf("rsp-rtl_sim: dbg_ctrl_read: 0x%.8x\n",resp); |
|
get_response_from_vpi(); |
|
*reset = (int)(resp & 0x00000001); |
|
*stall = (int)((resp >> 1) & 0x00000001); |
|
return DBG_ERR_OK; |
} |
|
/* read a word from wishbone */ |
int dbg_wb_read32(uint32_t adr, uint32_t *data) |
{ |
//uint32_t resp; |
|
dbg_set_chain(DC_WISHBONE); |
|
send_command_to_vpi(CMD_WB_RD32); |
|
send_address_to_vpi(adr); |
|
get_data_from_vpi(data); |
|
get_response_from_vpi(); |
|
return 0; |
} |
|
/* write a word to wishbone */ |
int dbg_wb_write32(uint32_t adr, uint32_t data) |
{ |
|
dbg_set_chain(DC_WISHBONE); |
|
send_command_to_vpi(CMD_WB_WR32); |
|
send_address_to_vpi(adr); |
|
send_data_to_vpi(data); |
|
get_response_from_vpi(); |
|
return 0; |
} |
|
/* read a block from wishbone */ |
int dbg_wb_read_block32(uint32_t adr, uint32_t *data, int len) |
{ |
|
// len is in B Y T E S ! ! |
|
if (DBG_VPI) printf("rsp-rtl_sim: block read len: %d from addr: 0x%.8x\n",len, adr); |
|
dbg_set_chain(DC_WISHBONE); |
|
send_command_to_vpi(CMD_WB_BLOCK_RD32); |
|
send_data_to_vpi(adr); |
|
send_data_to_vpi(len); |
|
get_block_data_from_vpi(len, data); |
|
get_response_from_vpi(); |
|
return DBG_ERR_OK; |
} |
|
/* write a block to wishbone */ |
int dbg_wb_write_block32(uint32_t adr, uint32_t *data, int len) |
{ |
|
dbg_set_chain(DC_WISHBONE); |
|
send_command_to_vpi(CMD_WB_BLOCK_WR32); |
|
send_data_to_vpi(adr); |
|
send_data_to_vpi(len); |
|
send_block_data_to_vpi(len, data); |
|
get_response_from_vpi(); |
|
return DBG_ERR_OK; |
} |
|
/* read a register from cpu */ |
int dbg_cpu0_read(uint32_t adr, uint32_t *data) |
{ |
|
dbg_set_chain(DC_CPU0); |
|
send_command_to_vpi(CMD_CPU_RD_REG); |
|
send_address_to_vpi(adr); |
|
get_data_from_vpi(data); |
|
get_response_from_vpi(); |
|
return 0; |
|
} |
|
/* write a cpu register */ |
int dbg_cpu0_write(uint32_t adr, uint32_t data) |
{ |
|
uint32_t resp; |
|
dbg_set_chain(DC_CPU0); |
|
send_command_to_vpi(CMD_CPU_WR_REG); |
|
send_address_to_vpi(adr); |
|
send_data_to_vpi(data); |
|
get_response_from_vpi(); |
|
return 0; |
} |
|
/* read a register from cpu */ |
int dbg_cpu1_read(uint32_t adr, uint32_t *data) { |
/* |
int err; |
if ((err = dbg_set_chain(DC_CPU1))) return err; |
if ((err = dbg_command(0x6, adr, 4))) return err; |
if ((err = dbg_go((unsigned char*)data, 4, 1))) return err; |
*data = ntohl(*data); |
*/ |
return DBG_ERR_OK; |
} |
|
/* write a cpu register */ |
int dbg_cpu1_write(uint32_t adr, uint32_t data) { |
/* |
int err; |
data = ntohl(data); |
if ((err = dbg_set_chain(DC_CPU1))) return err; |
if ((err = dbg_command(0x2, adr, 4))) return err; |
if ((err = dbg_go((unsigned char*)&data, 4, 0))) return err; |
*/ |
return DBG_ERR_OK; |
} |
|
/* read a register from cpu module */ |
int dbg_cpu1_read_ctrl(uint32_t adr, unsigned char *data) { |
/* |
int err; |
int r, s; |
if ((err = dbg_set_chain(DC_CPU1))) return err; |
if ((err = dbg_ctrl_read(&r, &s))) return err; |
*data = (r << 1) | s; |
*/ |
return DBG_ERR_OK; |
} |
|
/* write a cpu module register */ |
int dbg_cpu0_write_ctrl(uint32_t adr, unsigned char data) { |
int err; |
if ((err = dbg_set_chain(DC_CPU0))) return err; |
if ((err = dbg_ctrl(data & 2, data &1))) return err; |
return DBG_ERR_OK; |
} |
|
/* read a register from cpu module */ |
int dbg_cpu0_read_ctrl(uint32_t adr, unsigned char *data) { |
int err; |
int r, s; |
if ((err = dbg_set_chain(DC_CPU0))) return err; |
if ((err = dbg_ctrl_read(&r, &s))) return err; |
*data = (r << 1) | s; |
return DBG_ERR_OK; |
} |
|
void dbg_test() { |
int i; |
uint32_t npc, ppc, r1, insn, result; |
unsigned char stalled; |
// uint32_t stalled; |
unsigned char read_byte; |
|
printf(" Stall or1k\n"); |
dbg_cpu0_write_ctrl(0, 0x01); // stall or1k |
|
dbg_cpu0_read_ctrl(0, &stalled); |
if (!(stalled & 0x1)) { |
printf("\tor1k stall failed. read: 0x%x\n", stalled); // check stall or1k |
//exit(1); |
} |
|
debug2(" Reading npc\n"); |
dbg_cpu0_read((0 << 11) + 16, &npc); |
debug2(" Reading ppc\n"); |
dbg_cpu0_read((0 << 11) + 18, &ppc); |
debug2(" Reading r1\n"); |
dbg_cpu0_read(0x401, &r1); |
printf(" Read npc = %.8x ppc = %.8x r1 = %.8x\n", npc, ppc, r1); |
|
} |
|
// Catch the term/int signals, close gdb then close ourselves |
void catch_sigint(int sig_num) |
{ |
gdb_close(); |
exit(0); |
} |
|
void dbg_client_detached(void) |
{ |
// Send this message back to the sim |
send_command_to_vpi(CMD_GDB_DETACH); |
} |
|
|
// This function is called after the fork in the VPI function. |
|
void run_rsp_server(int portNum) |
{ |
// Send commands to init and reset debug interface |
dbg_reset(); |
// Stall and read NPC, PPC etc |
dbg_test(); |
|
set_rsp_server_port(portNum); |
|
// Install SIGINT/SIGTERM handlers, to close down gracefully |
signal(SIGINT, catch_sigint); |
signal(SIGTERM, catch_sigint); |
|
if (DBG_ON) printf("rsp-rtl_sim: starting handle_rsp()\n"); |
// Now, start the RSP server. This should not return |
handle_rsp (); |
|
// Exit gracefully if it returns (shouldn't though) |
exit(0); |
|
} |
|
|
/verilog/vpi/c/Makefile
0,0 → 1,54
###################################################################### |
#### #### |
#### VPI Makefile #### |
#### #### |
#### Description #### |
#### Makefile for VPI libraries #### |
#### #### |
#### To Do: #### |
#### Add compatability for other simulators (Cadence, etc.) #### |
#### #### |
#### Author(s): #### |
#### - jb, jb@orsoc.se #### |
#### #### |
#### #### |
###################################################################### |
#### #### |
#### Copyright (C) 2009 Authors and OPENCORES.ORG #### |
#### #### |
#### This source file may be used and distributed without #### |
#### restriction provided that this copyright statement is not #### |
#### removed from the file and that any derivative work contains #### |
#### the original copyright notice and the associated disclaimer. #### |
#### #### |
#### This source file is free software; you can redistribute it #### |
#### and/or modify it under the terms of the GNU Lesser General #### |
#### Public License as published by the Free Software Foundation; #### |
#### either version 2.1 of the License, or (at your option) any #### |
#### later version. #### |
#### #### |
#### This source 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 Lesser General Public License for more #### |
#### details. #### |
#### #### |
#### You should have received a copy of the GNU Lesser General #### |
#### Public License along with this source; if not, download it #### |
#### from http://www.opencores.org/lgpl.shtml #### |
#### #### |
###################################################################### |
|
SOURCE_FILES= jp_vpi.c rsp-rtl_sim.c gdb.c |
|
# Uncomment this line to enable debugging of all VPI code |
|
#DEBUG_DEFINES=-DDEBUG -DDEBUG2 -DDEBUG_ON=1 -DDEBUG_GDB=1 -DDEBUG_CMDS=1 |
|
all: jp_vpi |
|
jp_vpi: $(SOURCE_FILES) |
iverilog-vpi $(SOURCE_FILES) $(DEBUG_DEFINES) |
|
clean: |
rm -f *.o *~ jp_vpi.vpi |
/verilog/vpi/c/gdb.c
0,0 → 1,4093
/*$$HEADER*/ |
/******************************************************************************/ |
/* */ |
/* H E A D E R I N F O R M A T I O N */ |
/* */ |
/******************************************************************************/ |
|
// Project Name : ORPSoCv2 |
// File Name : gdb.c |
// Prepared By : jb, rmd |
// Project Start : 2008-10-01 |
|
/*$$COPYRIGHT NOTICE*/ |
/******************************************************************************/ |
/* */ |
/* C O P Y R I G H T N O T I C E */ |
/* */ |
/******************************************************************************/ |
/* |
This library is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; |
version 2.1 of the License, a copy of which is available from |
http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt. |
|
This library 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 |
Lesser General Public License for more details. |
|
You should have received a copy of the GNU Lesser General Public |
License along with this library; if not, write to the Free Software |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
*/ |
|
/*$$DESCRIPTION*/ |
/******************************************************************************/ |
/* */ |
/* D E S C R I P T I O N */ |
/* */ |
/******************************************************************************/ |
// |
// Implements RSP comatible GDB stub |
// |
|
|
/*$$CHANGE HISTORY*/ |
/******************************************************************************/ |
/* */ |
/* C H A N G E H I S T O R Y */ |
/* */ |
/******************************************************************************/ |
|
// Date Version Description |
//------------------------------------------------------------------------ |
// 081101 Imported code from "jp" project jb |
// 090219 Adapted code from Jeremy Bennett's RSP server |
// for the or1ksim project. rmb |
// 090304 Finished RSP server code import, added extra |
// functions, adding stability when debugging on |
// a remote target. jb |
// 090608 A few hacks for VPI compatibilty added jb |
|
#ifdef CYGWIN_COMPILE |
|
#else |
// linux includes |
#include <time.h> |
#include <sched.h> |
#endif |
|
#include <stdio.h> |
#include <ctype.h> |
#include <string.h> |
#include <stdlib.h> |
#include <unistd.h> |
#include <stdarg.h> |
|
/* Libraries for JTAG proxy server. */ |
#include <sys/stat.h> |
#include <sys/types.h> |
#include <sys/socket.h> |
#include <netinet/in.h> |
#include <sys/ioctl.h> |
#include <sys/select.h> |
#include <sys/poll.h> |
#include <fcntl.h> |
#include <netdb.h> |
#include <netinet/tcp.h> |
#include <signal.h> |
#include <inttypes.h> |
#include <errno.h> |
#include <arpa/inet.h> |
|
|
#ifndef DEBUG_GDB |
#define DEBUG_GDB 0 |
#endif |
|
#ifndef DEBUG_GDB_DUMP_DATA |
#define DEBUG_GDB_DUMP_DATA 0 |
#endif |
|
#ifndef DEBUG_GDB_BLOCK_DATA |
#define DEBUG_GDB_BLOCK_DATA 0 |
#endif |
|
#ifndef DEBUG_CMDS |
#define DEBUG_CMDS 0 |
#endif |
|
/*! Name of the Or1ksim RSP service */ |
#define OR1KSIM_RSP_SERVICE "or1ksim-rsp" |
|
#include "gdb.h" /* partially copied from gdb/config/or1k */ |
#include "rsp-rtl_sim.h" |
|
#define MAX_GPRS (32) |
|
/* Indices of GDB registers that are not GPRs. Must match GDB settings! */ |
#define PPC_REGNUM (MAX_GPRS + 0) /*!< Previous PC */ |
#define NPC_REGNUM (MAX_GPRS + 1) /*!< Next PC */ |
#define SR_REGNUM (MAX_GPRS + 2) /*!< Supervision Register */ |
#define NUM_REGS (MAX_GPRS + 3) /*!< Total GDB registers */ |
|
/* OR1k CPU registers address */ |
#define NPC_CPU_REG_ADD 0x10 /* Next PC */ |
#define SR_CPU_REG_ADD 0x11 /* Supervision Register */ |
#define PPC_CPU_REG_ADD 0x12 /* Previous PC */ |
#define DMR1_CPU_REG_ADD ((6 << 11) + 16) /* Debug Mode Register 1 (DMR1) 0x3010 */ |
#define DMR2_CPU_REG_ADD ((6 << 11) + 17) /* Debug Mode Register 2 (DMR2) 0x3011 */ |
#define DSR_CPU_REG_ADD ((6 << 11) + 20) /* Debug Stop Register (DSR) 0x3014 */ |
#define DRR_CPU_REG_ADD ((6 << 11) + 21) /* Debug Reason Register (DRR) 0x3015 */ |
|
/*! Trap instruction for OR32 */ |
#define OR1K_TRAP_INSTR 0x21000001 |
|
/*! The maximum number of characters in inbound/outbound buffers. The largest |
packets are the 'G' packet, which must hold the 'G' and all the registers |
with two hex digits per byte and the 'g' reply, which must hold all the |
registers, and (in our implementation) an end-of-string (0) |
character. Adding the EOS allows us to print out the packet as a |
string. So at least NUMREGBYTES*2 + 1 (for the 'G' or the EOS) are needed |
for register packets */ |
#define GDB_BUF_MAX ((NUM_REGS) * 8 + 1) |
|
/*! Size of the matchpoint hash table. Largest prime < 2^10 */ |
#define MP_HASH_SIZE 1021 |
|
/* Definition of special-purpose registers (SPRs). */ |
#define MAX_SPRS (0x10000) |
|
#define SPR_DMR1_ST 0x00400000 /* Single-step trace*/ |
#define SPR_DMR2_WGB 0x003ff000 /* Watchpoints generating breakpoint */ |
#define SPR_DSR_TE 0x00002000 /* Trap exception */ |
|
#define WORDSBIGENDIAN_N |
|
/* Definition of OR1K exceptions */ |
#define EXCEPT_NONE 0x0000 |
#define EXCEPT_RESET 0x0100 |
#define EXCEPT_BUSERR 0x0200 |
#define EXCEPT_DPF 0x0300 |
#define EXCEPT_IPF 0x0400 |
#define EXCEPT_TICK 0x0500 |
#define EXCEPT_ALIGN 0x0600 |
#define EXCEPT_ILLEGAL 0x0700 |
#define EXCEPT_INT 0x0800 |
#define EXCEPT_DTLBMISS 0x0900 |
#define EXCEPT_ITLBMISS 0x0a00 |
#define EXCEPT_RANGE 0x0b00 |
#define EXCEPT_SYSCALL 0x0c00 |
#define EXCEPT_FPE 0x0d00 |
#define EXCEPT_TRAP 0x0e00 |
|
// Changed to #defines from static const int's due to compile error |
// DRR (Debug Reason Register) Bits |
#define SPR_DRR_RSTE 0x00000001 //!< Reset |
#define SPR_DRR_BUSEE 0x00000002 //!< Bus error |
#define SPR_DRR_DPFE 0x00000004 //!< Data page fault |
#define SPR_DRR_IPFE 0x00000008 //!< Insn page fault |
#define SPR_DRR_TTE 0x00000010 //!< Tick timer |
#define SPR_DRR_AE 0x00000020 //!< Alignment |
#define SPR_DRR_IIE 0x00000040 //!< Illegal instruction |
#define SPR_DRR_IE 0x00000080 //!< Interrupt |
#define SPR_DRR_DME 0x00000100 //!< DTLB miss |
#define SPR_DRR_IME 0x00000200 //!< ITLB miss |
#define SPR_DRR_RE 0x00000400 //!< Range fault |
#define SPR_DRR_SCE 0x00000800 //!< System call |
#define SPR_DRR_FPE 0x00001000 //!< Floating point |
#define SPR_DRR_TE 0x00002000 //!< Trap |
|
|
/*! Definition of GDB target signals. Data taken from the GDB 6.8 |
source. Only those we use defined here. The exact meaning of |
signal number is defined by the header `include/gdb/signals.h' |
in the GDB source code. For an explanation of what each signal |
means, see target_signal_to_string.*/ |
enum target_signal { |
TARGET_SIGNAL_NONE = 0, |
TARGET_SIGNAL_INT = 2, |
TARGET_SIGNAL_ILL = 4, |
TARGET_SIGNAL_TRAP = 5, |
TARGET_SIGNAL_FPE = 8, |
TARGET_SIGNAL_BUS = 10, |
TARGET_SIGNAL_SEGV = 11, |
TARGET_SIGNAL_ALRM = 14, |
TARGET_SIGNAL_USR2 = 31, |
TARGET_SIGNAL_PWR = 32 |
}; |
|
/*! String to map hex digits to chars */ |
static const char hexchars[]="0123456789abcdef"; |
|
|
//! Is the NPC cached? |
|
//! Setting the NPC flushes the pipeline, so subsequent reads will return |
//! zero until the processor has refilled the pipeline. This will not be |
//! happening if the processor is stalled (as it is when GDB had control), |
//! so we must cache the NPC. As soon as the processor is unstalled, this |
//! cached value becomes invalid. So we must track the stall state, and if |
//! appropriate cache the NPC. |
enum stallStates { |
STALLED, |
UNSTALLED, |
UNKNOWN |
} stallState; |
|
int npcIsCached; //!< Is the NPC cached - should be bool |
uint32_t npcCachedValue; //!< Cached value of the NPC |
|
static int err = 0; |
|
|
/************************ |
JTAG Server Routines |
************************/ |
int serverIP = 0; |
int serverPort = 0; |
int server_fd = 0; |
int gdb_fd = 0; |
|
static int tcp_level = 0; |
|
/* global to store what chain the debug unit is currently connected to |
(not the JTAG TAP, but the onchip debug module has selected) */ |
int gdb_chain = -1; |
|
/*! Data structure for RSP buffers. Can't be null terminated, since it may |
include zero bytes */ |
struct rsp_buf |
{ |
char data[GDB_BUF_MAX]; |
int len; |
}; |
|
/*! Enumeration of different types of matchpoint. These have explicit values |
matching the second digit of 'z' and 'Z' packets. */ |
enum mp_type { |
BP_MEMORY = 0, // software-breakpoint Z0 break |
BP_HARDWARE = 1, // hardware-breakpoint Z1 hbreak |
WP_WRITE = 2, // write-watchpoint Z2 watch |
WP_READ = 3, // read-watchpoint Z3 rwatch |
WP_ACCESS = 4 // access-watchpoint Z4 awatch |
}; |
|
/*! Data structure for a matchpoint hash table entry */ |
struct mp_entry |
{ |
enum mp_type type; /*!< Type of matchpoint */ |
uint32_t addr; /*!< Address with the matchpoint */ |
uint32_t instr; /*!< Substituted instruction */ |
struct mp_entry *next; /*!< Next entry with this hash */ |
}; |
|
/*! Central data for the RSP connection */ |
static struct |
{ |
int client_waiting; /*!< Is client waiting a response? */ |
// Not used int proto_num; /*!< Number of the protocol used */ |
int client_fd; /*!< FD for talking to GDB */ |
int sigval; /*!< GDB signal for any exception */ |
uint32_t start_addr; /*!< Start of last run */ |
struct mp_entry *mp_hash[MP_HASH_SIZE]; /*!< Matchpoint hash table */ |
} rsp; |
|
/* Forward declarations of static functions */ |
static char *printTime(void); |
static int gdb_read(void*, int); |
static int gdb_write(void*, int); |
static void ProtocolClean(int, int32_t); |
static void GDBRequest(void); |
static void rsp_interrupt(); |
static char rsp_peek(); |
static struct rsp_buf *get_packet (void); |
static void rsp_init (void); |
static void set_npc (uint32_t addr); |
static uint32_t get_npc(); |
static void rsp_check_for_exception(); |
static int check_for_exception_vector(uint32_t ppc); |
static void rsp_exception (uint32_t except); |
static int get_rsp_char (void); |
static int hex (int c); |
static void rsp_get_client (void); |
static void rsp_client_request (void); |
static void rsp_client_close (void); |
static void client_close (char err); |
static void put_str_packet (const char *str); |
static void rsp_report_exception (void); |
static void put_packet (struct rsp_buf *p_buf); |
static void send_rsp_str (unsigned char *data, int len); |
static void rsp_query (struct rsp_buf *p_buf); |
static void rsp_vpkt (struct rsp_buf *p_buf); |
static void rsp_step (struct rsp_buf *p_buf); |
static void rsp_step_with_signal (struct rsp_buf *p_buf); |
static void rsp_step_generic (uint32_t addr, uint32_t except); |
static void rsp_continue (struct rsp_buf *p_buf); |
static void rsp_continue_with_signal (struct rsp_buf *p_buf); |
static void rsp_continue_generic (uint32_t addr, uint32_t except); |
static void rsp_read_all_regs (void); |
static void rsp_write_all_regs (struct rsp_buf *p_buf); |
static void rsp_read_mem (struct rsp_buf *p_buf); |
static void rsp_write_mem (struct rsp_buf *p_buf); |
static void rsp_write_mem_bin (struct rsp_buf *p_buf); |
static int rsp_unescape (char *data, int len); |
static void rsp_read_reg (struct rsp_buf *p_buf); |
static void rsp_write_reg (struct rsp_buf *p_buf); |
static void mp_hash_init (void); |
static void mp_hash_add (enum mp_type type, uint32_t addr, uint32_t instr); |
static struct mp_entry * mp_hash_lookup (enum mp_type type, uint32_t addr); |
static struct mp_entry * mp_hash_delete (enum mp_type type, uint32_t addr); |
static void rsp_remove_matchpoint (struct rsp_buf *p_buf); |
static void rsp_insert_matchpoint (struct rsp_buf *p_buf); |
static void rsp_command (struct rsp_buf *p_buf); |
static void rsp_set (struct rsp_buf *p_buf); |
static void rsp_restart (void); |
static void ascii2hex (char *dest,char *src); |
static void hex2ascii (char *dest, char *src); |
static uint32_t hex2reg (char *p_buf); |
static void reg2hex (uint32_t val, char *p_buf); |
static void swap_buf(char* p_buf, int len); |
static void set_stall_state (int state); |
static void reset_or1k (void); |
static void gdb_ensure_or1k_stalled(); |
static int gdb_set_chain(int chain); |
static int gdb_write_reg(uint32_t adr, uint32_t data); |
static int gdb_read_reg(uint32_t adr, uint32_t *data); |
static int gdb_write_block(uint32_t adr, uint32_t *data, int len); |
static int gdb_read_block(uint32_t adr, uint32_t *data, int len); |
|
char *printTime(void) |
{ |
time_t tid; |
struct tm *strtm; |
static char timeBuf[20]; |
|
time(&tid); |
strtm = localtime(&tid); |
sprintf(timeBuf,"[%.02d:%.02d:%.02d] ",strtm->tm_hour,strtm->tm_min,strtm->tm_sec); |
return timeBuf; |
} |
/*---------------------------------------------------------------------------*/ |
/*!Set the serverPort variable |
*/ |
/*---------------------------------------------------------------------------*/ |
|
void |
set_rsp_server_port(int portNum) |
{ |
serverPort = portNum; |
} |
|
/*---------------------------------------------------------------------------*/ |
/*!Initialize the Remote Serial Protocol connection |
|
Set up the central data structures. */ |
/*---------------------------------------------------------------------------*/ |
void |
rsp_init (void) |
{ |
/* Clear out the central data structure */ |
rsp.client_waiting = 0; /* GDB client is not waiting for us */ |
rsp.client_fd = -1; /* i.e. invalid */ |
rsp.sigval = 0; /* No exception */ |
rsp.start_addr = EXCEPT_RESET; /* Default restart point */ |
|
/* Set up the matchpoint hash table */ |
mp_hash_init (); |
|
/* RSP always starts stalled as though we have just reset the processor. */ |
rsp_exception (EXCEPT_TRAP); |
|
/* Setup the NPC caching variables */ |
stallState = STALLED; |
// Force a caching of the NPC |
npcIsCached = 0; |
get_npc(); |
|
} /* rsp_init () */ |
|
/*---------------------------------------------------------------------------*/ |
/*!Look for action on RSP |
|
This function is called when the processor has stalled, which, except for |
initialization, must be due to an interrupt. |
|
If we have no RSP client, we get one. We can make no progress until the |
client is available. |
|
Then if the cause is an exception following a step or continue command, and |
the exception not been notified to GDB, a packet reporting the cause of the |
exception is sent. |
|
The next client request is then processed. */ |
/*---------------------------------------------------------------------------*/ |
void |
handle_rsp (void) |
{ |
uint32_t temp_uint32; |
|
rsp_init(); |
|
while (1){ |
/* If we have no RSP client, wait until we get one. */ |
while (-1 == rsp.client_fd) |
{ |
rsp_get_client (); |
rsp.client_waiting = 0; /* No longer waiting */ |
} |
|
/* If we have an unacknowledged exception tell the GDB client. If this |
exception was a trap due to a memory breakpoint, then adjust the NPC. */ |
if (rsp.client_waiting) |
{ |
|
// Check for exception |
rsp_check_for_exception(); |
|
if(stallState == STALLED) |
// Get the PPC if we're stalled |
gdb_read_reg(PPC_CPU_REG_ADD, &temp_uint32); |
|
|
if ((TARGET_SIGNAL_TRAP == rsp.sigval) && (NULL != mp_hash_lookup (BP_MEMORY, temp_uint32))) |
{ |
if (stallState != STALLED) |
// This is a quick fix for a strange situation seen in some of the simulators where |
// the sw bp would be detected, but the stalled state variable wasn't updated correctly |
// indicating that last time it checked, it wasn't set but the processor has now hit the |
// breakpoint. So run rsp_check_for_exception() to bring everything up to date. |
rsp_check_for_exception(); |
|
if(DEBUG_GDB) printf("Software breakpoint hit at 0x%08x. Rolling back NPC to this instruction\n", temp_uint32); |
|
set_npc (temp_uint32); |
|
rsp_report_exception(); |
rsp.client_waiting = 0; /* No longer waiting */ |
} |
else if(stallState == STALLED) { |
// If we're here, the thing has stalled, but not because of a breakpoint we set |
// report back the exception |
|
rsp_report_exception(); |
rsp.client_waiting = 0; /* No longer waiting */ |
|
} |
} |
|
// See if there's any incoming data from the client by peeking at the socket |
if (rsp_peek() > 0) |
{ |
if (rsp_peek() == 0x03 && (stallState != STALLED)) // ETX, end of text control char |
{ |
// Got an interrupt command from GDB, this function should |
// pull the packet off the socket and stall the processor. |
// and then send a stop reply packet with signal TARGET_SIGNAL_NONE |
rsp_interrupt(); |
rsp.client_waiting = 0; |
} |
else if (rsp.client_waiting == 0) |
{ |
// Default handling of data from the client: |
/* Get a RSP client request */ |
rsp_client_request (); |
} |
} /* end if (rsp_peek() > 0) */ |
|
} |
|
} /* handle_rsp () */ |
|
|
/* |
Check if processor is stalled - if it is, read the DRR |
and return the target signal code |
*/ |
static void rsp_check_for_exception() |
{ |
|
unsigned char stalled; |
uint32_t drr; |
err = dbg_cpu0_read_ctrl(0, &stalled); /* check if we're stalled */ |
|
if (!(stalled & 0x01)) |
{ |
// Processor not stalled. Just return; |
return; |
} |
|
if (DEBUG_GDB) printf("rsp_check_for_exception() detected processor was stalled\nChecking DRR\n"); |
|
// We're stalled |
stallState = STALLED; |
npcIsCached = 0; |
|
gdb_set_chain(SC_RISC_DEBUG); |
|
// Now read the DRR (Debug Reason Register) |
gdb_read_reg(DRR_CPU_REG_ADD, &drr); |
|
if (DEBUG_GDB) printf("DRR: 0x%08x\n", drr); |
|
switch ((int)(drr&0xffffffff)) |
{ |
case SPR_DRR_RSTE: rsp.sigval = TARGET_SIGNAL_PWR; break; |
case SPR_DRR_BUSEE: rsp.sigval = TARGET_SIGNAL_BUS; break; |
case SPR_DRR_DPFE: rsp.sigval = TARGET_SIGNAL_SEGV; break; |
case SPR_DRR_IPFE: rsp.sigval = TARGET_SIGNAL_SEGV; break; |
case SPR_DRR_TTE: rsp.sigval = TARGET_SIGNAL_ALRM; break; |
case SPR_DRR_AE: rsp.sigval = TARGET_SIGNAL_BUS; break; |
case SPR_DRR_IIE: rsp.sigval = TARGET_SIGNAL_ILL; break; |
case SPR_DRR_IE: rsp.sigval = TARGET_SIGNAL_INT; break; |
case SPR_DRR_DME: rsp.sigval = TARGET_SIGNAL_SEGV; break; |
case SPR_DRR_IME: rsp.sigval = TARGET_SIGNAL_SEGV; break; |
case SPR_DRR_RE: rsp.sigval = TARGET_SIGNAL_FPE; break; |
case SPR_DRR_SCE: rsp.sigval = TARGET_SIGNAL_USR2; break; |
case SPR_DRR_FPE: rsp.sigval = TARGET_SIGNAL_FPE; break; |
case SPR_DRR_TE: rsp.sigval = TARGET_SIGNAL_TRAP; break; |
|
default: |
// This must be the case of single step (which does not set DRR) |
rsp.sigval = TARGET_SIGNAL_TRAP; break; |
} |
|
if (DEBUG_GDB) printf("rsp.sigval: 0x%x\n", rsp.sigval); |
|
return; |
} |
|
/*---------------------------------------------------------------------------*/ |
/*!Check if PPC is in an exception vector that halts program flow |
|
Compare the provided PPC with known exception vectors that are fatal |
to a program's execution. Call rsp_exception(ppc) to set the appropriate |
sigval and return. |
|
@param[in] ppc Value of current PPC, as read from debug unit |
@return: 1 if we set a sigval and should return control to GDB, else 0 */ |
/*---------------------------------------------------------------------------*/ |
static int |
check_for_exception_vector(uint32_t ppc) |
{ |
switch(ppc) |
{ |
// The following should return sigvals to GDB for processing |
case EXCEPT_BUSERR: |
case EXCEPT_ALIGN: |
case EXCEPT_ILLEGAL: |
case EXCEPT_TRAP: if(DEBUG_GDB) |
printf("PPC at exception address\n"); |
rsp_exception(ppc); |
return 1; |
|
default: |
return 0; |
} |
return 1; |
} |
|
/*---------------------------------------------------------------------------*/ |
/*!Note an exception for future processing |
|
The simulator has encountered an exception. Record it here, so that a |
future call to handle_exception will report it back to the client. The |
signal is supplied in Or1ksim form and recorded in GDB form. |
|
We flag up a warning if an exception is already pending, and ignore the |
earlier exception. |
|
@param[in] except The exception (Or1ksim form) */ |
/*---------------------------------------------------------------------------*/ |
void |
rsp_exception (uint32_t except) |
{ |
int sigval; /* GDB signal equivalent to exception */ |
|
switch (except) |
{ |
case EXCEPT_RESET: sigval = TARGET_SIGNAL_PWR; break; |
case EXCEPT_BUSERR: sigval = TARGET_SIGNAL_BUS; break; |
case EXCEPT_DPF: sigval = TARGET_SIGNAL_SEGV; break; |
case EXCEPT_IPF: sigval = TARGET_SIGNAL_SEGV; break; |
case EXCEPT_TICK: sigval = TARGET_SIGNAL_ALRM; break; |
case EXCEPT_ALIGN: sigval = TARGET_SIGNAL_BUS; break; |
case EXCEPT_ILLEGAL: sigval = TARGET_SIGNAL_ILL; break; |
case EXCEPT_INT: sigval = TARGET_SIGNAL_INT; break; |
case EXCEPT_DTLBMISS: sigval = TARGET_SIGNAL_SEGV; break; |
case EXCEPT_ITLBMISS: sigval = TARGET_SIGNAL_SEGV; break; |
case EXCEPT_RANGE: sigval = TARGET_SIGNAL_FPE; break; |
case EXCEPT_SYSCALL: sigval = TARGET_SIGNAL_USR2; break; |
case EXCEPT_FPE: sigval = TARGET_SIGNAL_FPE; break; |
case EXCEPT_TRAP: sigval = TARGET_SIGNAL_TRAP; break; |
|
default: |
fprintf (stderr, "Warning: Unknown RSP exception %u: Ignored\n", except); |
return; |
} |
|
if ((0 != rsp.sigval) && (sigval != rsp.sigval)) |
{ |
fprintf (stderr, "Warning: RSP signal %d received while signal " |
"%d pending: Pending exception replaced\n", sigval, rsp.sigval); |
} |
|
rsp.sigval = sigval; /* Save the signal value */ |
|
} /* rsp_exception () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Get a new client connection. |
|
Blocks until the client connection is available. |
|
A lot of this code is copied from remote_open in gdbserver remote-utils.c. |
|
This involves setting up a socket to listen on a socket for attempted |
connections from a single GDB instance (we couldn't be talking to multiple |
GDBs at once!). |
|
The service is specified either as a port number in the Or1ksim configuration |
(parameter rsp_port in section debug, default 51000) or as a service name |
in the constant OR1KSIM_RSP_SERVICE. |
|
The protocol used for communication is specified in OR1KSIM_RSP_PROTOCOL. */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_get_client (void) |
{ |
int tmp_fd; /* Temporary descriptor for socket */ |
int optval; /* Socket options */ |
struct sockaddr_in sock_addr; /* Socket address */ |
socklen_t len; /* Size of the socket address */ |
|
/* 0 is used as the RSP port number to indicate that we should use the |
service name instead. */ |
if (0 == serverPort) |
{ |
struct servent *service = getservbyname (OR1KSIM_RSP_SERVICE, "tcp"); |
if (NULL == service) |
{ |
fprintf (stderr, "Warning: RSP unable to find service \"%s\": %s \n", |
OR1KSIM_RSP_SERVICE, strerror (errno)); |
return; |
} |
serverPort = ntohs (service->s_port); |
} |
|
/* Open a socket on which we'll listen for clients */ |
tmp_fd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); |
if (tmp_fd < 0) |
{ |
fprintf (stderr, "ERROR: Cannot open RSP socket\n"); |
exit (0); |
} |
|
/* Allow rapid reuse of the port on this socket */ |
optval = 1; |
setsockopt (tmp_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, |
sizeof (optval)); |
|
/* Bind the port to the socket */ |
sock_addr.sin_family = PF_INET; |
sock_addr.sin_port = htons (serverPort); |
sock_addr.sin_addr.s_addr = INADDR_ANY; |
if (bind (tmp_fd, (struct sockaddr *) &sock_addr, sizeof (sock_addr))) |
{ |
fprintf (stderr, "ERROR: Cannot bind to RSP socket\n"); |
exit (0); |
} |
|
/* Listen for (at most one) client */ |
if (0 != listen (tmp_fd, 1)) |
{ |
fprintf (stderr, "ERROR: Cannot listen on RSP socket\n"); |
exit (0); |
} |
|
printf("Waiting for gdb connection on localhost:%d\n", serverPort); |
fflush (stdout); |
|
printf("Press CTRL+c and type 'finish' to exit.\n"); |
fflush (stdout); |
|
/* Accept a client which connects */ |
len = sizeof (sock_addr); |
rsp.client_fd = accept (tmp_fd, (struct sockaddr *)&sock_addr, &len); |
|
if (-1 == rsp.client_fd) |
{ |
fprintf (stderr, "Warning: Failed to accept RSP client\n"); |
return; |
} |
|
/* Enable TCP keep alive process */ |
optval = 1; |
setsockopt (rsp.client_fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, |
sizeof (optval)); |
|
int flags; |
|
/* If they have O_NONBLOCK, use the Posix way to do it */ |
|
#if defined(O_NONBLOCK) |
/* Fixme: O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. */ |
if (-1 == (flags = fcntl(rsp.client_fd, F_GETFL, 0))) |
flags = 0; |
|
fcntl(rsp.client_fd, F_SETFL, flags | O_NONBLOCK); |
#else |
/* Otherwise, use the old way of doing it */ |
flags = 1; |
ioctl(fd, FIOBIO, &flags); |
#endif |
|
|
|
/* Set socket to be non-blocking */ |
|
/* We do this because when we're given a continue, or step |
instruction,command we set the processor stall off, then instnatly check |
if it's stopped. If it hasn't then we drop through and wait for input |
from GDB. Obviously this will cause problems when it will stop after we |
do the check. So now, rsp_peek() has been implemented to simply check if |
there's an incoming command from GDB (only interested in interrupt |
commands), otherwise it returns back to and poll the processor's PPC and |
stall bit. It can only do this if the socket is non-blocking. |
|
At first test, simply adding this line appeared to give no problems with |
the existing code. No "simulation" of blocking behaviour on the |
non-blocking socket was required (in the event that a read/write throws |
back a EWOULDBLOCK error, as was looked to be the case in the previous |
GDB handling code) -- Julius |
*/ |
if (ioctl(rsp.client_fd, FIONBIO, (char *)&optval) > 0 ) |
{ |
perror("ioctl() failed"); |
close(rsp.client_fd); |
close(tmp_fd); |
exit(0); |
} |
|
|
/* Don't delay small packets, for better interactive response (disable |
Nagel's algorithm) */ |
optval = 1; |
setsockopt (rsp.client_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&optval, |
sizeof (optval)); |
|
/* Socket is no longer needed */ |
close (tmp_fd); /* No longer need this */ |
signal (SIGPIPE, SIG_IGN); /* So we don't exit if client dies */ |
|
printf ("Remote debugging from host %s\n", inet_ntoa (sock_addr.sin_addr)); |
} /* rsp_get_client () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Deal with a request from the GDB client session |
|
In general, apart from the simplest requests, this function replies on |
other functions to implement the functionality. */ |
/*---------------------------------------------------------------------------*/ |
static void rsp_client_request (void) |
{ |
struct rsp_buf *p_buf = get_packet (); /* Message sent to us */ |
|
// Null packet means we hit EOF or the link was closed for some other |
// reason. Close the client and return |
if (NULL == p_buf) |
{ |
rsp_client_close (); |
return; |
} |
|
if (DEBUG_GDB){ |
printf("%s-----------------------------------------------------\n", printTime()); |
printf ("Packet received %s: %d chars\n", p_buf->data, p_buf->len ); |
fflush (stdout); |
} |
|
switch (p_buf->data[0]) |
{ |
case '!': |
/* Request for extended remote mode */ |
put_str_packet ("OK"); // OK = supports and has enabled extended mode. |
return; |
|
case '?': |
/* Return last signal ID */ |
rsp_report_exception(); |
return; |
|
case 'A': |
/* Initialization of argv not supported */ |
fprintf (stderr, "Warning: RSP 'A' packet not supported: ignored\n"); |
put_str_packet ("E01"); |
return; |
|
case 'b': |
/* Setting baud rate is deprecated */ |
fprintf (stderr, "Warning: RSP 'b' packet is deprecated and not " |
"supported: ignored\n"); |
return; |
|
case 'B': |
/* Breakpoints should be set using Z packets */ |
fprintf (stderr, "Warning: RSP 'B' packet is deprecated (use 'Z'/'z' " |
"packets instead): ignored\n"); |
return; |
|
case 'c': |
/* Continue */ |
rsp_continue (p_buf); |
return; |
|
case 'C': |
/* Continue with signal */ |
rsp_continue_with_signal (p_buf); |
return; |
|
case 'd': |
/* Disable debug using a general query */ |
fprintf (stderr, "Warning: RSP 'd' packet is deprecated (define a 'Q' " |
"packet instead: ignored\n"); |
return; |
|
case 'D': |
/* Detach GDB. Do this by closing the client. The rules say that |
execution should continue. TODO. Is this really then intended |
meaning? Or does it just mean that only vAttach will be recognized |
after this? */ |
put_str_packet ("OK"); |
// In VPI disconnect everyone and exit |
rsp_client_close(); |
client_close('0'); |
dbg_client_detached(); // Send message to sim that the client detached |
exit(0); |
//reset_or1k (); |
//set_stall_state (0); |
return; |
|
case 'F': |
/* File I/O is not currently supported */ |
fprintf (stderr, "Warning: RSP file I/O not currently supported: 'F' " |
"packet ignored\n"); |
return; |
|
case 'g': |
rsp_read_all_regs (); |
return; |
|
case 'G': |
rsp_write_all_regs (p_buf); |
return; |
|
case 'H': |
/* Set the thread number of subsequent operations. For now ignore |
silently and just reply "OK" */ |
put_str_packet ("OK"); |
return; |
|
case 'i': |
/* Single instruction step */ |
fprintf (stderr, "Warning: RSP cycle stepping not supported: target " |
"stopped immediately\n"); |
rsp.client_waiting = 1; /* Stop reply will be sent */ |
return; |
|
case 'I': |
/* Single instruction step with signal */ |
fprintf (stderr, "Warning: RSP cycle stepping not supported: target " |
"stopped immediately\n"); |
rsp.client_waiting = 1; /* Stop reply will be sent */ |
return; |
|
case 'k': |
/* Kill request. Do nothing for now. */ |
return; |
|
case 'm': |
/* Read memory (symbolic) */ |
rsp_read_mem (p_buf); |
return; |
|
case 'M': |
/* Write memory (symbolic) */ |
rsp_write_mem (p_buf); |
return; |
|
case 'p': |
/* Read a register */ |
rsp_read_reg (p_buf); |
return; |
|
case 'P': |
/* Write a register */ |
rsp_write_reg (p_buf); |
return; |
|
case 'q': |
/* Any one of a number of query packets */ |
rsp_query (p_buf); |
return; |
|
case 'Q': |
/* Any one of a number of set packets */ |
rsp_set (p_buf); |
return; |
|
case 'r': |
/* Reset the system. Deprecated (use 'R' instead) */ |
fprintf (stderr, "Warning: RSP 'r' packet is deprecated (use 'R' " |
"packet instead): ignored\n"); |
return; |
|
case 'R': |
/* Restart the program being debugged. */ |
rsp_restart (); |
return; |
|
case 's': |
/* Single step (one high level instruction). This could be hard without |
DWARF2 info */ |
rsp_step (p_buf); |
return; |
|
case 'S': |
/* Single step (one high level instruction) with signal. This could be |
hard without DWARF2 info */ |
rsp_step_with_signal (p_buf); |
return; |
|
case 't': |
/* Search. This is not well defined in the manual and for now we don't |
support it. No response is defined. */ |
fprintf (stderr, "Warning: RSP 't' packet not supported: ignored\n"); |
return; |
|
case 'T': |
/* Is the thread alive. We are bare metal, so don't have a thread |
context. The answer is always "OK". */ |
put_str_packet ("OK"); |
return; |
|
case 'v': |
/* Any one of a number of packets to control execution */ |
rsp_vpkt (p_buf); |
return; |
|
case 'X': |
/* Write memory (binary) */ |
rsp_write_mem_bin (p_buf); |
return; |
|
case 'z': |
/* Remove a breakpoint/watchpoint. */ |
rsp_remove_matchpoint (p_buf); |
return; |
|
case 'Z': |
/* Insert a breakpoint/watchpoint. */ |
rsp_insert_matchpoint (p_buf); |
return; |
|
default: |
/* Unknown commands are ignored */ |
fprintf (stderr, "Warning: Unknown RSP request %s\n", p_buf->data); |
return; |
} |
} /* rsp_client_request () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Close the connection to the client if it is open */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_client_close (void) |
{ |
if (-1 != rsp.client_fd) |
{ |
close (rsp.client_fd); |
rsp.client_fd = -1; |
} |
} /* rsp_client_close () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Send a packet to the GDB client |
|
Modeled on the stub version supplied with GDB. Put out the data preceded by |
a '$', followed by a '#' and a one byte checksum. '$', '#', '*' and '}' are |
escaped by preceding them with '}' and then XORing the character with |
0x20. |
|
@param[in] p_buf The data to send */ |
/*---------------------------------------------------------------------------*/ |
static void |
put_packet (struct rsp_buf *p_buf) |
{ |
unsigned char data[GDB_BUF_MAX * 2]; |
int len; |
int ch; /* Ack char */ |
|
/* Construct $<packet info>#<checksum>. Repeat until the GDB client |
acknowledges satisfactory receipt. */ |
do |
{ |
unsigned char checksum = 0; /* Computed checksum */ |
int count = 0; /* Index into the buffer */ |
|
if (DEBUG_GDB_DUMP_DATA){ |
printf ("Putting %s\n\n", p_buf->data); |
fflush (stdout); |
} |
|
len = 0; |
data[len++] = '$'; /* Start char */ |
|
/* Body of the packet */ |
for (count = 0; count < p_buf->len; count++) |
{ |
unsigned char ch = p_buf->data[count]; |
|
/* Check for escaped chars */ |
if (('$' == ch) || ('#' == ch) || ('*' == ch) || ('}' == ch)) |
{ |
ch ^= 0x20; |
checksum += (unsigned char)'}'; |
data[len++] = '}'; |
} |
|
checksum += ch; |
data[len++] = ch; |
} |
|
data[len++] = '#'; /* End char */ |
|
/* Computed checksum */ |
data[len++] = (hexchars[checksum >> 4]); |
data[len++] = (hexchars[checksum % 16]); |
|
send_rsp_str ((unsigned char *) &data, len); |
|
/* Check for ack of connection failure */ |
ch = get_rsp_char (); |
if (0 > ch) |
{ |
return; /* Fail the put silently. */ |
} |
} |
while ('+' != ch); |
|
} /* put_packet () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Convenience to put a constant string packet |
|
param[in] str The text of the packet */ |
/*---------------------------------------------------------------------------*/ |
static void |
put_str_packet (const char *str) |
{ |
struct rsp_buf buffer; |
int len = strlen (str); |
|
/* Construct the packet to send, so long as string is not too big, |
otherwise truncate. Add EOS at the end for convenient debug printout */ |
|
if (len >= GDB_BUF_MAX) |
{ |
fprintf (stderr, "Warning: String %s too large for RSP packet: " |
"truncated\n", str); |
len = GDB_BUF_MAX - 1; |
} |
|
strncpy (buffer.data, str, len); |
buffer.data[len] = 0; |
buffer.len = len; |
|
put_packet (&buffer); |
|
} /* put_str_packet () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Get a packet from the GDB client |
|
Modeled on the stub version supplied with GDB. The data is in a static |
buffer. The data should be copied elsewhere if it is to be preserved across |
a subsequent call to get_packet(). |
|
Unlike the reference implementation, we don't deal with sequence |
numbers. GDB has never used them, and this implementation is only intended |
for use with GDB 6.8 or later. Sequence numbers were removed from the RSP |
standard at GDB 5.0. |
|
@return A pointer to the static buffer containing the data */ |
/*---------------------------------------------------------------------------*/ |
static struct rsp_buf * |
get_packet (void) |
{ |
static struct rsp_buf buf; /* Survives the return */ |
|
/* Keep getting packets, until one is found with a valid checksum */ |
while (1) |
{ |
unsigned char checksum; /* The checksum we have computed */ |
int count; /* Index into the buffer */ |
int ch; /* Current character */ |
|
/* Wait around for the start character ('$'). Ignore all other |
characters */ |
ch = get_rsp_char (); |
|
while (ch != '$') |
{ |
if (-1 == ch) |
{ |
return NULL; /* Connection failed */ |
} |
|
ch = get_rsp_char (); |
|
// Potentially handle an interrupt character (0x03) here |
} |
|
/* Read until a '#' or end of buffer is found */ |
checksum = 0; |
count = 0; |
while (count < GDB_BUF_MAX - 1) |
{ |
ch = get_rsp_char (); |
|
if(rsp.client_waiting && DEBUG_GDB) |
{ |
printf("%x\n",ch); |
} |
|
|
/* Check for connection failure */ |
if (0 > ch) |
{ |
return NULL; |
} |
|
/* If we hit a start of line char begin all over again */ |
if ('$' == ch) |
{ |
checksum = 0; |
count = 0; |
|
continue; |
} |
|
/* Break out if we get the end of line char */ |
if ('#' == ch) |
{ |
break; |
} |
|
/* Update the checksum and add the char to the buffer */ |
|
checksum = checksum + (unsigned char)ch; |
buf.data[count] = (char)ch; |
count = count + 1; |
} |
|
/* Mark the end of the buffer with EOS - it's convenient for non-binary |
data to be valid strings. */ |
buf.data[count] = 0; |
buf.len = count; |
|
/* If we have a valid end of packet char, validate the checksum */ |
if ('#' == ch) |
{ |
unsigned char xmitcsum; /* The checksum in the packet */ |
|
ch = get_rsp_char (); |
if (0 > ch) |
{ |
return NULL; /* Connection failed */ |
} |
xmitcsum = hex (ch) << 4; |
|
ch = get_rsp_char (); |
if (0 > ch) |
{ |
return NULL; /* Connection failed */ |
} |
|
xmitcsum += hex (ch); |
|
/* If the checksums don't match print a warning, and put the |
negative ack back to the client. Otherwise put a positive ack. */ |
if (checksum != xmitcsum) |
{ |
fprintf (stderr, "Warning: Bad RSP checksum: Computed " |
"0x%02x, received 0x%02x\n", checksum, xmitcsum); |
|
ch = '-'; |
send_rsp_str ((unsigned char *) &ch, 1); /* Failed checksum */ |
} |
else |
{ |
ch = '+'; |
send_rsp_str ((unsigned char *) &ch, 1); /* successful transfer */ |
break; |
} |
} |
else |
{ |
fprintf (stderr, "Warning: RSP packet overran buffer\n"); |
} |
} |
return &buf; /* Success */ |
} /* get_packet () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Put a single character out onto the client socket |
|
This should only be called if the client is open, but we check for safety. |
|
@param[in] c The character to put out */ |
/*---------------------------------------------------------------------------*/ |
static void |
send_rsp_str (unsigned char *data, int len) |
{ |
if (-1 == rsp.client_fd) |
{ |
fprintf (stderr, "Warning: Attempt to write '%s' to unopened RSP " |
"client: Ignored\n", data); |
return; |
} |
|
/* Write until successful (we retry after interrupts) or catastrophic |
failure. */ |
while (1) |
{ |
switch (write (rsp.client_fd, data, len)) |
{ |
case -1: |
/* Error: only allow interrupts or would block */ |
if ((EAGAIN != errno) && (EINTR != errno)) |
{ |
fprintf (stderr, "Warning: Failed to write to RSP client: " |
"Closing client connection: %s\n", |
strerror (errno)); |
rsp_client_close (); |
return; |
} |
|
break; |
|
case 0: |
break; /* Nothing written! Try again */ |
|
default: |
return; /* Success, we can return */ |
} |
} |
} /* send_rsp_str () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Get a single character from the client socket |
|
This should only be called if the client is open, but we check for safety. |
|
@return The character read, or -1 on failure */ |
/*---------------------------------------------------------------------------*/ |
static int |
get_rsp_char () |
{ |
if (-1 == rsp.client_fd) |
{ |
fprintf (stderr, "Warning: Attempt to read from unopened RSP " |
"client: Ignored\n"); |
return -1; |
} |
|
/* Non-blocking read until successful (we retry after interrupts) or |
catastrophic failure. */ |
while (1) |
{ |
unsigned char c; |
|
switch (read (rsp.client_fd, &c, sizeof (c))) |
{ |
case -1: |
/* Error: only allow interrupts */ |
if ((EAGAIN != errno) && (EINTR != errno)) |
{ |
fprintf (stderr, "Warning: Failed to read from RSP client: " |
"Closing client connection: %s\n", |
strerror (errno)); |
rsp_client_close (); |
return -1; |
} |
|
break; |
|
case 0: |
// EOF |
rsp_client_close (); |
return -1; |
|
default: |
return c & 0xff; /* Success, we can return (no sign extend!) */ |
} |
} |
} /* get_rsp_char () */ |
|
/*---------------------------------------------------------------------------*/ |
/* !Peek at data coming into server from GDB |
|
Useful for polling for ETX (0x3) chars being sent when GDB wants to |
interrupt |
|
@return the char we peeked, 0 otherwise */ |
/*---------------------------------------------------------------------------*/ |
static char |
rsp_peek() |
{ |
/* |
if (-1 == rsp.client_fd) |
{ |
fprintf (stderr, "Warning: Attempt to read from unopened RSP " |
"client: Ignored\n"); |
return -1; |
} |
*/ |
char c; |
int n; |
// Using recv here instead of read becuase we can pass the MSG_PEEK |
// flag, which lets us look at what's on the socket, without actually |
// taking it off |
|
//if (DEBUG_GDB) |
// printf("peeking at GDB socket...\n"); |
|
n = recv (rsp.client_fd, &c, sizeof (c), MSG_PEEK); |
|
//if (DEBUG_GDB) |
// printf("peeked, got n=%d, c=0x%x\n",n, c); |
|
if (n > 0) |
return c; |
else |
return '\0'; |
|
} |
|
/*---------------------------------------------------------------------------*/ |
/*!Handle an interrupt from GDB |
|
Detect an interrupt from GDB and stall the processor */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_interrupt() |
{ |
unsigned char c; |
|
if (read (rsp.client_fd, &c, sizeof (c)) <= 0) |
{ |
// Had issues, just return |
return; |
} |
|
// Ensure this is a ETX control char (0x3), currently, we only call this |
// function when we've peeked and seen it, otherwise, ignore, return and pray |
// things go back to normal... |
if (c != 0x03) |
{ |
printf("Warning: Interrupt character expected but not found on socket.\n"); |
return; |
} |
|
// Otherwise, it's an interrupt packet, stall the processor, and upon return |
// to the main handle_rsp() loop, it will inform GDB. |
|
if (DEBUG_GDB) printf("Interrupt received from GDB. Stalling processor.\n"); |
|
set_stall_state (1); |
|
// Send a stop reply response, manually set rsp.sigval to TARGET_SIGNAL_NONE |
rsp.sigval = TARGET_SIGNAL_NONE; |
rsp_report_exception(); |
rsp.client_waiting = 0; /* No longer waiting */ |
|
return; |
|
} |
|
|
/*---------------------------------------------------------------------------*/ |
/*!"Unescape" RSP binary data |
|
'#', '$' and '}' are escaped by preceding them by '}' and oring with 0x20. |
|
This function reverses that, modifying the data in place. |
|
@param[in] data The array of bytes to convert |
@para[in] len The number of bytes to be converted |
|
@return The number of bytes AFTER conversion */ |
/*---------------------------------------------------------------------------*/ |
static int |
rsp_unescape (char *data, |
int len) |
{ |
int from_off = 0; /* Offset to source char */ |
int to_off = 0; /* Offset to dest char */ |
|
while (from_off < len) |
{ |
/* Is it escaped */ |
if ( '}' == data[from_off]) |
{ |
from_off++; |
data[to_off] = data[from_off] ^ 0x20; |
} |
else |
{ |
data[to_off] = data[from_off]; |
} |
|
from_off++; |
to_off++; |
} |
|
return to_off; |
|
} /* rsp_unescape () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Initialize the matchpoint hash table |
|
This is an open hash table, so this function clears all the links to |
NULL. */ |
/*---------------------------------------------------------------------------*/ |
static void |
mp_hash_init (void) |
{ |
int i; |
|
for (i = 0; i < MP_HASH_SIZE; i++) |
{ |
rsp.mp_hash[i] = NULL; |
} |
} /* mp_hash_init () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Add an entry to the matchpoint hash table |
|
Add the entry if it wasn't already there. If it was there do nothing. The |
match just be on type and addr. The instr need not match, since if this is |
a duplicate insertion (perhaps due to a lost packet) they will be |
different. |
|
@param[in] type The type of matchpoint |
@param[in] addr The address of the matchpoint |
@para[in] instr The instruction to associate with the address */ |
/*---------------------------------------------------------------------------*/ |
static void |
mp_hash_add (enum mp_type type, |
uint32_t addr, |
uint32_t instr) |
{ |
int hv = addr % MP_HASH_SIZE; |
struct mp_entry *curr; |
|
/* See if we already have the entry */ |
for(curr = rsp.mp_hash[hv]; NULL != curr; curr = curr->next) |
{ |
if ((type == curr->type) && (addr == curr->addr)) |
{ |
return; /* We already have the entry */ |
} |
} |
|
/* Insert the new entry at the head of the chain */ |
curr = (struct mp_entry*) malloc (sizeof (*curr)); |
|
curr->type = type; |
curr->addr = addr; |
curr->instr = instr; |
curr->next = rsp.mp_hash[hv]; |
|
rsp.mp_hash[hv] = curr; |
|
} /* mp_hash_add () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Look up an entry in the matchpoint hash table |
|
The match must be on type AND addr. |
|
@param[in] type The type of matchpoint |
@param[in] addr The address of the matchpoint |
|
@return The entry deleted, or NULL if the entry was not found */ |
/*---------------------------------------------------------------------------*/ |
static struct mp_entry * mp_hash_lookup (enum mp_type type, uint32_t addr) |
{ |
int hv = addr % MP_HASH_SIZE; |
struct mp_entry *curr; |
|
/* Search */ |
for (curr = rsp.mp_hash[hv]; NULL != curr; curr = curr->next) |
{ |
if ((type == curr->type) && (addr == curr->addr)) |
{ |
return curr; /* The entry found */ |
} |
} |
|
/* Not found */ |
return NULL; |
|
} /* mp_hash_lookup () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Delete an entry from the matchpoint hash table |
|
If it is there the entry is deleted from the hash table. If it is not |
there, no action is taken. The match must be on type AND addr. |
|
The usual fun and games tracking the previous entry, so we can delete |
things. |
|
@note The deletion DOES NOT free the memory associated with the entry, |
since that is returned. The caller should free the memory when they |
have used the information. |
|
@param[in] type The type of matchpoint |
@param[in] addr The address of the matchpoint |
|
@return The entry deleted, or NULL if the entry was not found */ |
/*---------------------------------------------------------------------------*/ |
static struct mp_entry * |
mp_hash_delete (enum mp_type type, |
uint32_t addr) |
{ |
int hv = addr % MP_HASH_SIZE; |
struct mp_entry *prev = NULL; |
struct mp_entry *curr; |
|
/* Search */ |
for (curr = rsp.mp_hash[hv]; NULL != curr; curr = curr->next) |
{ |
if ((type == curr->type) && (addr == curr->addr)) |
{ |
/* Found - delete. Method depends on whether we are the head of |
chain. */ |
if (NULL == prev) |
{ |
rsp.mp_hash[hv] = curr->next; |
} |
else |
{ |
prev->next = curr->next; |
} |
|
return curr; /* The entry deleted */ |
} |
|
prev = curr; |
} |
|
/* Not found */ |
return NULL; |
|
} /* mp_hash_delete () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Utility to give the value of a hex char |
|
@param[in] ch A character representing a hexadecimal digit. Done as -1, |
for consistency with other character routines, which can use |
-1 as EOF. |
|
@return The value of the hex character, or -1 if the character is |
invalid. */ |
/*---------------------------------------------------------------------------*/ |
static int hex (int c) |
{ |
return ((c >= 'a') && (c <= 'f')) ? c - 'a' + 10 : |
((c >= '0') && (c <= '9')) ? c - '0' : |
((c >= 'A') && (c <= 'F')) ? c - 'A' + 10 : -1; |
|
} /* hex () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Convert a register to a hex digit string |
|
The supplied 32-bit value is converted to an 8 digit hex string according |
the target endianism. It is null terminated for convenient printing. |
|
@param[in] val The value to convert |
@param[out] p_buf The buffer for the text string */ |
/*---------------------------------------------------------------------------*/ |
static void |
reg2hex (uint32_t val, char *p_buf) |
{ |
int n; /* Counter for digits */ |
int nyb_shift; |
|
for (n = 0; n < 8; n++) |
{ |
#ifdef WORDSBIGENDIAN |
if(n%2==0){ |
nyb_shift = n * 4 + 4; |
} |
else{ |
nyb_shift = n * 4 - 4; |
} |
#else |
nyb_shift = 28 - (n * 4); |
#endif |
p_buf[n] = hexchars[(val >> nyb_shift) & 0xf]; |
} |
|
p_buf[8] = 0; /* Useful to terminate as string */ |
|
} /* reg2hex () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Convert a hex digit string to a register value |
|
The supplied 8 digit hex string is converted to a 32-bit value according |
the target endianism |
|
@param[in] p_buf The buffer with the hex string |
|
@return The value to convert */ |
/*---------------------------------------------------------------------------*/ |
static uint32_t |
hex2reg (char *p_buf) |
{ |
int n; /* Counter for digits */ |
uint32_t val = 0; /* The result */ |
|
for (n = 0; n < 8; n++) |
{ |
#ifdef WORDSBIGENDIAN |
int nyb_shift = n * 4; |
#else |
int nyb_shift = 28 - (n * 4); |
#endif |
val |= hex (p_buf[n]) << nyb_shift; |
} |
|
return val; |
|
} /* hex2reg () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Convert an ASCII character string to pairs of hex digits |
|
Both source and destination are null terminated. |
|
@param[out] dest Buffer to store the hex digit pairs (null terminated) |
@param[in] src The ASCII string (null terminated) */ |
/*---------------------------------------------------------------------------*/ |
static void ascii2hex (char *dest, |
char *src) |
{ |
int i; |
|
/* Step through converting the source string */ |
for (i = 0; src[i] != '\0'; i++) |
{ |
char ch = src[i]; |
|
dest[i * 2] = hexchars[ch >> 4 & 0xf]; |
dest[i * 2 + 1] = hexchars[ch & 0xf]; |
} |
|
dest[i * 2] = '\0'; |
|
} /* ascii2hex () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Convert pairs of hex digits to an ASCII character string |
|
Both source and destination are null terminated. |
|
@param[out] dest The ASCII string (null terminated) |
@param[in] src Buffer holding the hex digit pairs (null terminated) */ |
/*---------------------------------------------------------------------------*/ |
static void hex2ascii (char *dest, |
char *src) |
{ |
int i; |
|
/* Step through convering the source hex digit pairs */ |
for (i = 0; src[i * 2] != '\0' && src[i * 2 + 1] != '\0'; i++) |
{ |
dest[i] = ((hex (src[i * 2]) & 0xf) << 4) | (hex (src[i * 2 + 1]) & 0xf); |
} |
|
dest[i] = '\0'; |
|
} /* hex2ascii () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Set the program counter |
|
This sets the value in the NPC SPR. Not completely trivial, since this is |
actually cached in cpu_state.pc. Any reset of the NPC also involves |
clearing the delay state and setting the pcnext global. |
|
Only actually do this if the requested address is different to the current |
NPC (avoids clearing the delay pipe). |
|
@param[in] addr The address to use */ |
/*---------------------------------------------------------------------------*/ |
static void |
set_npc (uint32_t addr) |
{ |
|
// First set the chain |
gdb_set_chain(SC_RISC_DEBUG); /* 1 RISC Debug Interface chain */ |
|
|
if (addr != get_npc()) |
{ |
|
gdb_write_reg(NPC_CPU_REG_ADD, addr); |
|
if (STALLED == stallState) |
{ |
if (DEBUG_GDB) printf("set_npc(): New NPC value (0x%08x) written and locally cached \n", addr); |
npcCachedValue = addr; |
npcIsCached = 1; |
} |
else |
{ |
if (DEBUG_GDB) printf("set_npc(): New NPC value (0x%08x) written \n", addr); |
npcIsCached = 0; |
} |
|
|
} |
else |
return; |
|
|
} /* set_npc () */ |
|
|
//! Read the value of the Next Program Counter (a SPR) |
|
//! Setting the NPC flushes the pipeline, so subsequent reads will return |
//! zero until the processor has refilled the pipeline. This will not be |
//! happening if the processor is stalled (as it is when GDB had control), |
//! so we must cache the NPC. As soon as the processor is unstalled, this |
//! cached value becomes invalid. |
|
//! If we are stalled and the value has been cached, use it. If we are stalled |
//! and the value has not been cached, cache it (for efficiency) and use |
//! it. Otherwise read the corresponding SPR. |
|
//! @return The value of the NPC |
static uint32_t get_npc () |
{ |
uint32_t current_npc; |
|
if (STALLED == stallState) |
{ |
if (npcIsCached == 0) |
{ |
err = gdb_set_chain(SC_RISC_DEBUG); |
err = gdb_read_reg(NPC_CPU_REG_ADD, &npcCachedValue); |
if(err > 0){ |
printf("Error %d reading NPC\n", err); |
rsp_client_close (); |
return 0; |
} |
if (DEBUG_GDB) printf("get_npc(): caching newly read NPC value (0x%08x)\n",npcCachedValue); |
|
|
npcIsCached = 1; |
} |
else |
if (DEBUG_GDB) printf("get_npc(): reading cached NPC value (0x%08x)\n",npcCachedValue); |
|
return npcCachedValue; |
} |
else |
{ |
err = gdb_read_reg(NPC_CPU_REG_ADD, ¤t_npc); |
if(err > 0){ |
printf("Error %d reading NPC\n", err); |
rsp_client_close (); |
return 0; |
} |
return current_npc; |
|
} |
} // get_npc () |
|
|
|
/*---------------------------------------------------------------------------*/ |
/*!Send a packet acknowledging an exception has occurred |
|
This is only called if there is a client FD to talk to */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_report_exception (void) |
{ |
struct rsp_buf buffer; |
|
/* Construct a signal received packet */ |
buffer.data[0] = 'S'; |
buffer.data[1] = hexchars[rsp.sigval >> 4]; |
buffer.data[2] = hexchars[rsp.sigval % 16]; |
buffer.data[3] = 0; |
buffer.len = strlen (buffer.data); |
|
put_packet (&buffer); |
|
} /* rsp_report_exception () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP continue request |
|
Parse the command to see if there is an address. Uses the underlying |
generic continue function, with EXCEPT_NONE. |
|
@param[in] p_buf The full continue packet */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_continue (struct rsp_buf *p_buf) |
{ |
uint32_t addr; /* Address to continue from, if any */ |
|
// First set the chain |
err = gdb_set_chain(SC_RISC_DEBUG); /* 1 RISC Debug Interface chain */ |
|
// Make sure the processor is stalled |
gdb_ensure_or1k_stalled(); |
|
if(err > 0){ |
printf("Error %d to set RISC Debug Interface chain in the CONTINUE command 'c'\n", err); |
rsp_client_close (); |
return; |
} |
|
if (0 == strcmp ("c", p_buf->data)) |
{ |
// Arc Sim Code --> addr = cpu_state.pc; /* Default uses current NPC */ |
/* ---------- NPC ---------- */ |
addr = get_npc(); |
} |
else if (1 != sscanf (p_buf->data, "c%x", &addr)) |
{ |
fprintf (stderr, |
"Warning: RSP continue address %s not recognized: ignored\n", |
p_buf->data); |
|
// Arc Sim Code --> addr = cpu_state.pc; /* Default uses current NPC */ |
/* ---------- NPC ---------- */ |
addr = get_npc(); |
} |
|
if (DEBUG_GDB) printf("rsp_continue() --> Read NPC = 0x%08x\n", addr); |
|
rsp_continue_generic (addr, EXCEPT_NONE); |
|
} /* rsp_continue () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP continue with signal request |
|
Currently null. Will use the underlying generic continue function. |
|
@param[in] p_buf The full continue with signal packet */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_continue_with_signal (struct rsp_buf *p_buf) |
{ |
printf ("RSP continue with signal '%s' received\n", p_buf->data); |
|
} /* rsp_continue_with_signal () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Generic processing of a continue request |
|
The signal may be EXCEPT_NONE if there is no exception to be |
handled. Currently the exception is ignored. |
|
The single step flag is cleared in the debug registers and then the |
processor is unstalled. |
|
@param[in] addr Address from which to step |
@param[in] except The exception to use (if any) */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_continue_generic (uint32_t addr, |
uint32_t except) |
{ |
uint32_t temp_uint32; |
|
/* Set the address as the value of the next program counter */ |
set_npc (addr); |
|
/* Clear Debug Reason Register (DRR) 0x3015 */ |
// Arc sim --> cpu_state.sprs[SPR_DRR] = 0; |
if(gdb_write_reg(DRR_CPU_REG_ADD, 0)) printf("Error write to DRR register\n"); |
|
/* Clear watchpoint break generation in Debug Mode Register 2 (DMR2) 0x3011 */ |
// Arc sim --> cpu_state.sprs[SPR_DMR2] &= ~SPR_DMR2_WGB; |
if(gdb_read_reg(DMR2_CPU_REG_ADD, &temp_uint32)) printf("Error read from DMR2 register\n"); |
temp_uint32 &= ~SPR_DMR2_WGB; |
if(gdb_write_reg(DMR2_CPU_REG_ADD, temp_uint32)) printf("Error write to DMR2 register\n"); |
|
/* Clear the single step trigger in Debug Mode Register 1 (DMR1) Register 0x3010 */ |
// Arc sim --> cpu_state.sprs[SPR_DMR1] &= ~SPR_DMR1_ST; |
if(gdb_read_reg(DMR1_CPU_REG_ADD, &temp_uint32)) printf("Error read from DMR1 register\n"); |
temp_uint32 &= ~SPR_DMR1_ST; |
if(gdb_write_reg(DMR1_CPU_REG_ADD, temp_uint32)) printf("Error write to DMR1 register\n"); |
|
/* Set traps to be handled by the debug unit in the Debug Stop Register (DSR) Register 0x3014 */ |
// Arc sim --> cpu_state.sprs[SPR_DSR] |= SPR_DSR_TE; |
if(gdb_read_reg(DSR_CPU_REG_ADD, &temp_uint32)) printf("Error read from DSR register\n"); |
temp_uint32 |= SPR_DSR_TE; |
if(gdb_write_reg(DSR_CPU_REG_ADD, temp_uint32)) printf("Error write to DSR register\n"); |
|
/* Unstall the processor */ |
set_stall_state (0); |
|
/* Note the GDB client is now waiting for a reply. */ |
rsp.client_waiting = 1; |
|
} /* rsp_continue_generic () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP read all registers request |
|
The registers follow the GDB sequence for OR1K: GPR0 through GPR31, PPC |
(i.e. SPR PPC), NPC (i.e. SPR NPC) and SR (i.e. SPR SR). Each register is |
returned as a sequence of bytes in target endian order. |
|
Each byte is packed as a pair of hex digits. */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_read_all_regs (void) |
{ |
struct rsp_buf buffer; /* Buffer for the reply */ |
int r; /* Register index */ |
uint32_t temp_uint32; |
|
// Make sure the processor is stalled |
gdb_ensure_or1k_stalled(); |
|
// First set the chain |
gdb_set_chain(SC_RISC_DEBUG); /* 1 RISC Debug Interface chain */ |
|
|
// Read all GPRs |
for (r = 0; r < MAX_GPRS; r++){ |
|
err = gdb_read_reg(0x400 + r, &temp_uint32); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in gdb_read_reg at reg. %d\n", err, r); |
put_str_packet ("E01"); |
return; |
} |
reg2hex (temp_uint32, &(buffer.data[r * 8])); |
|
if (DEBUG_GDB_DUMP_DATA){ |
switch(r % 4) |
{ |
case 0: |
printf("gpr%02d 0x%08x ", r, temp_uint32); |
break; |
case 1: |
case 2: |
printf("0x%08x ", temp_uint32); |
break; |
case 3: |
printf("0x%08x\n", temp_uint32); |
break; |
default: |
break; |
} |
} |
|
} |
/* ---------- PPC ---------- */ |
err = gdb_read_reg(PPC_CPU_REG_ADD, &temp_uint32); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in gdb_read_reg read --> PPC\n", err); |
put_str_packet ("E01"); |
return; |
} |
reg2hex (temp_uint32, &(buffer.data[PPC_REGNUM * 8])); |
if (DEBUG_GDB_DUMP_DATA) printf("PPC 0x%08x\n", temp_uint32); |
/* ---------- NPC ---------- */ |
temp_uint32 = get_npc(); |
/* |
err = gdb_read_reg(NPC_CPU_REG_ADD, &temp_uint32); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in gdb_read_reg read --> NPC\n", err); |
put_str_packet ("E01"); |
return; |
} |
*/ |
reg2hex (temp_uint32, &(buffer.data[NPC_REGNUM * 8])); |
if (DEBUG_GDB_DUMP_DATA) printf("NPC 0x%08x\n", temp_uint32); |
/* ---------- SR ---------- */ |
err = gdb_read_reg(SR_CPU_REG_ADD, &temp_uint32); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in gdb_read_reg read --> SP\n", err); |
put_str_packet ("E01"); |
return; |
} |
reg2hex (temp_uint32, &(buffer.data[SR_REGNUM * 8])); |
if (DEBUG_GDB_DUMP_DATA) printf("SR 0x%08x\n", temp_uint32); |
|
/* Finalize the packet and send it */ |
buffer.data[NUM_REGS * 8] = 0; |
buffer.len = NUM_REGS * 8; |
|
put_packet (&buffer); |
return; |
} /* rsp_read_all_regs () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP write all registers request |
|
The registers follow the GDB sequence for OR1K: GPR0 through GPR31, PPC |
(i.e. SPR PPC), NPC (i.e. SPR NPC) and SR (i.e. SPR SR). Each register is |
supplied as a sequence of bytes in target endian order. |
|
Each byte is packed as a pair of hex digits. |
|
@todo There is no error checking at present. Non-hex chars will generate a |
warning message, but there is no other check that the right amount |
of data is present. The result is always "OK". |
|
@param[in] p_buf The original packet request. */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_write_all_regs (struct rsp_buf *p_buf) |
{ |
uint32_t regnum; /* Register index */ |
// char valstr[9]; /* Allow for EOS on the string */ |
|
// /* Check for valid data */ |
// if (0 != (strcmp ("G", p_buf->data)) && (GDB_BUF_MAX != strlen(p_buf->data))) |
// { |
// fprintf (stderr, "Warning: Failed to recognize RSP write register " |
// "command: %s\n", p_buf->data); |
// // put_str_packet ("E01"); |
// return; |
// } |
|
// Make sure the processor is stalled |
gdb_ensure_or1k_stalled(); |
|
// First set the chain |
err = gdb_set_chain(SC_RISC_DEBUG); /* 1 RISC Debug Interface chain */ |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in gdb_set_chain\n", err); |
put_str_packet ("E01"); |
return; |
} |
|
/* ---------- GPRS ---------- */ |
for (regnum = 0; regnum < MAX_GPRS; regnum++) |
{ |
err = gdb_write_reg(0x400 + regnum, hex2reg (&p_buf->data[regnum * 8 + 1])); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_write_reg write --> GPRS\n", err); |
put_str_packet ("E01"); |
return; |
} |
} |
|
/* ---------- PPC ---------- */ |
err = gdb_write_reg(PPC_CPU_REG_ADD, hex2reg (&p_buf->data[PPC_REGNUM * 8 + 1])); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_write_reg write --> PPC\n", err); |
put_str_packet ("E01"); |
return; |
} |
/* ---------- SR ---------- */ |
err = gdb_write_reg(SR_CPU_REG_ADD, hex2reg (&p_buf->data[SR_REGNUM * 8 + 1])); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_write_reg write --> SR\n", err); |
put_str_packet ("E01"); |
return; |
} |
/* ---------- NPC ---------- */ |
set_npc(hex2reg (&p_buf->data[NPC_REGNUM * 8 + 1])); |
/* |
err = gdb_write_reg(NPC_CPU_REG_ADD, hex2reg (&p_buf->data[NPC_REGNUM * 8 + 1])); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_write_reg write --> NPC\n", err); |
put_str_packet ("E01"); |
return; |
} |
*/ |
/* Acknowledge. TODO: We always succeed at present, even if the data was |
defective. */ |
put_str_packet ("OK"); |
} /* rsp_write_all_regs () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/* Handle a RSP read memory (symbolic) request |
|
Syntax is: |
|
m<addr>,<length>: |
|
The response is the bytes, lowest address first, encoded as pairs of hex |
digits. |
|
The length given is the number of bytes to be read. |
|
@note This function reuses p_buf, so trashes the original command. |
|
@param[in] p_buf The command received */ |
/*---------------------------------------------------------------------------*/ |
static void rsp_read_mem (struct rsp_buf *p_buf) |
{ |
unsigned int addr; /* Where to read the memory */ |
int len; /* Number of bytes to read */ |
int off; /* Offset into the memory */ |
uint32_t temp_uint32 = 0; |
char *rec_buf; |
|
|
if (2 != sscanf (p_buf->data, "m%x,%x:", &addr, &len)) |
{ |
fprintf (stderr, "Warning: Failed to recognize RSP read memory " |
"command: %s\n", p_buf->data); |
put_str_packet ("E01"); |
return; |
} |
|
/* Make sure we won't overflow the buffer (2 chars per byte) */ |
if ((len * 2) >= GDB_BUF_MAX) |
{ |
fprintf (stderr, "Warning: Memory read %s too large for RSP packet: " |
"truncated\n", p_buf->data); |
len = (GDB_BUF_MAX - 1) / 2; |
} |
|
if(!(rec_buf = (char*)malloc(len))) { |
put_str_packet ("E01"); |
ProtocolClean(0, JTAG_PROXY_OUT_OF_MEMORY); |
return; |
} |
|
// Make sure the processor is stalled |
gdb_ensure_or1k_stalled(); |
|
// Set chain 5 --> Wishbone Memory chain |
err = gdb_set_chain(SC_WISHBONE); |
if(err){ |
if (DEBUG_GDB) printf("Error %d in gdb_set_chain\n", err); |
put_str_packet ("E01"); |
return; |
} |
|
// Read the data from Wishbone Memory chain |
err = gdb_read_block(addr, (uint32_t*)rec_buf, len); |
if(err){ |
put_str_packet ("E01"); |
return; |
} |
|
/* Refill the buffer with the reply */ |
for( off = 0 ; off < len ; off ++ ) { |
; |
temp_uint32 = (temp_uint32 << 8) | (0x000000ff & *(rec_buf + off)); |
|
if((off %4 ) == 3){ |
temp_uint32 = htonl(temp_uint32); |
reg2hex (temp_uint32, &(p_buf->data[off * 2 - 6])); |
} |
if (DEBUG_GDB_BLOCK_DATA){ |
switch(off % 16) |
{ |
case 3: |
printf("Add 0x%08x Data 0x%08x ", addr + off - 3, temp_uint32); |
break; |
case 7: |
case 11: |
printf("0x%08x ", temp_uint32); |
break; |
case 15: |
printf("0x%08x\n", temp_uint32); |
break; |
default: |
break; |
} |
if ((len - off == 1) && (off % 16) < 15) printf("\n"); |
} |
} |
|
if (DEBUG_GDB && (err > 0)) printf("\nError %x\n", err);fflush (stdout); |
free(rec_buf); |
p_buf->data[off * 2] = 0; /* End of string */ |
p_buf->len = strlen (p_buf->data); |
put_packet (p_buf); |
} /* rsp_read_mem () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP write memory (symbolic) request ("M") |
|
Syntax is: |
|
M<addr>,<length>:<data> |
|
Example: M4015cc,2:c320# |
(Write the value 0xc320 to address 0x4015cc.) |
|
An example target response: |
+ $OK# |
|
The data is the bytes, lowest address first, encoded as pairs of hex |
digits. |
|
The length given is the number of bytes to be written. |
|
@note This function reuses p_buf, so trashes the original command. |
|
@param[in] p_buf The command received */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_write_mem (struct rsp_buf *p_buf) |
{ |
unsigned int addr; /* Where to write the memory */ |
int len; /* Number of bytes to write */ |
char *symdat; /* Pointer to the symboli data */ |
int datlen; /* Number of digits in symbolic data */ |
int off; /* Offset into the memory */ |
int nibc; /* Nibbel counter */ |
uint32_t val; |
|
if (2 != sscanf (p_buf->data, "M%x,%x:", &addr, &len)) |
{ |
fprintf (stderr, "Warning: Failed to recognize RSP write memory " |
"command: %s\n", p_buf->data); |
put_str_packet ("E01"); |
return; |
} |
|
/* Find the start of the data and check there is the amount we expect. */ |
symdat = (char*) memchr ((const void *)p_buf->data, ':', GDB_BUF_MAX) + 1; |
datlen = p_buf->len - (symdat - p_buf->data); |
|
/* Sanity check */ |
if (len * 2 != datlen) |
{ |
fprintf (stderr, "Warning: Write of %d digits requested, but %d digits " |
"supplied: packet ignored\n", len * 2, datlen ); |
put_str_packet ("E01"); |
return; |
} |
|
// Make sure the processor is stalled |
gdb_ensure_or1k_stalled(); |
|
|
// Set chain 5 --> Wishbone Memory chain |
err = gdb_set_chain(SC_WISHBONE); |
if(err){ |
put_str_packet ("E01"); |
return; |
} |
|
val = 0; |
off = 0; |
/* Write the bytes to memory */ |
for (nibc = 0; nibc < datlen; nibc++) |
{ |
val |= 0x0000000f & hex (symdat[nibc]); |
if(nibc % 8 == 7){ |
err = gdb_write_block(addr + off, &val, 4); |
if (DEBUG_GDB) printf("Error %x\n", err);fflush (stdout); |
if(err){ |
put_str_packet ("E01"); |
return; |
} |
val = 0; |
off += 4; |
} |
val <<= 4; |
} |
put_str_packet ("OK"); |
} /* rsp_write_mem () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Read a single register |
|
The registers follow the GDB sequence for OR1K: GPR0 through GPR31, PC |
(i.e. SPR NPC) and SR (i.e. SPR SR). The register is returned as a |
sequence of bytes in target endian order. |
|
Each byte is packed as a pair of hex digits. |
|
@param[in] p_buf The original packet request. Reused for the reply. */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_read_reg (struct rsp_buf *p_buf) |
{ |
unsigned int regnum; |
uint32_t temp_uint32; |
|
/* Break out the fields from the data */ |
if (1 != sscanf (p_buf->data, "p%x", ®num)) |
{ |
fprintf (stderr, "Warning: Failed to recognize RSP read register " |
"command: %s\n", p_buf->data); |
put_str_packet ("E01"); |
return; |
} |
|
// Make sure the processor is stalled |
gdb_ensure_or1k_stalled(); |
|
// First set the chain |
err = gdb_set_chain(SC_RISC_DEBUG); /* 1 RISC Debug Interface chain */ |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in gdb_set_chain\n", err); |
put_str_packet ("E01"); |
return; |
} |
|
/* Get the relevant register */ |
if (regnum < MAX_GPRS) |
{ |
err = gdb_read_reg(0x400 + regnum, &temp_uint32); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_read_reg at reg. %d \n", err, regnum); |
put_str_packet ("E01"); |
return; |
} |
reg2hex (temp_uint32, p_buf->data); |
} |
else if (PPC_REGNUM == regnum) /* ---------- PPC ---------- */ |
{ |
err = gdb_read_reg(PPC_CPU_REG_ADD, &temp_uint32); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_read_reg read --> PPC\n", err); |
put_str_packet ("E01"); |
return; |
} |
reg2hex (temp_uint32, p_buf->data); |
} |
else if (NPC_REGNUM == regnum) /* ---------- NPC ---------- */ |
{ |
temp_uint32 = get_npc(); |
/* |
err = gdb_read_reg(NPC_CPU_REG_ADD, &temp_uint32); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_read_reg read --> PPC\n", err); |
put_str_packet ("E01"); |
return; |
} |
*/ |
reg2hex (temp_uint32, p_buf->data); |
} |
else if (SR_REGNUM == regnum) /* ---------- SR ---------- */ |
{ |
err = gdb_read_reg(SR_CPU_REG_ADD, &temp_uint32); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_read_reg read --> PPC\n", err); |
put_str_packet ("E01"); |
return; |
} |
reg2hex (temp_uint32, p_buf->data); |
} |
else |
{ |
/* Error response if we don't know the register */ |
fprintf (stderr, "Warning: Attempt to read unknown register 0x%x: " |
"ignored\n", regnum); |
put_str_packet ("E01"); |
return; |
} |
|
p_buf->len = strlen (p_buf->data); |
put_packet (p_buf); |
|
} /* rsp_read_reg () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Write a single register |
|
The registers follow the GDB sequence for OR1K: GPR0 through GPR31, PC |
(i.e. SPR NPC) and SR (i.e. SPR SR). The register is specified as a |
sequence of bytes in target endian order. |
|
Each byte is packed as a pair of hex digits. |
|
@param[in] p_buf The original packet request. */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_write_reg (struct rsp_buf *p_buf) |
{ |
unsigned int regnum; |
char valstr[9]; /* Allow for EOS on the string */ |
// int err = 0; |
|
/* Break out the fields from the data */ |
if (2 != sscanf (p_buf->data, "P%x=%8s", ®num, valstr)) |
{ |
fprintf (stderr, "Warning: Failed to recognize RSP write register " |
"command: %s\n", p_buf->data); |
put_str_packet ("E01"); |
return; |
} |
|
// Make sure the processor is stalled |
gdb_ensure_or1k_stalled(); |
|
// First set the chain |
err = gdb_set_chain(SC_RISC_DEBUG); /* 1 RISC Debug Interface chain */ |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in gdb_set_chain\n", err); |
put_str_packet ("E01"); |
return; |
} |
|
/* Set the relevant register */ |
if (regnum < MAX_GPRS) /* ---------- GPRS ---------- */ |
{ |
err = gdb_write_reg(0x400 + regnum, hex2reg (valstr)); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_write_reg write --> GPRS\n", err); |
put_str_packet ("E01"); |
return; |
} |
} |
else if (PPC_REGNUM == regnum) /* ---------- PPC ---------- */ |
{ |
err = gdb_write_reg(PPC_CPU_REG_ADD, hex2reg (valstr)); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_write_reg write --> PPC\n", err); |
put_str_packet ("E01"); |
return; |
} |
} |
else if (NPC_REGNUM == regnum) /* ---------- NPC ---------- */ |
{ |
set_npc(hex2reg (valstr)); |
/* |
err = gdb_write_reg(NPC_CPU_REG_ADD, hex2reg (valstr)); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_write_reg write --> NPC\n", err); |
put_str_packet ("E01"); |
return; |
} |
*/ |
} |
else if (SR_REGNUM == regnum) /* ---------- SR ---------- */ |
{ |
err = gdb_write_reg(SR_CPU_REG_ADD, hex2reg (valstr)); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_write_reg write --> SR\n", err); |
put_str_packet ("E01"); |
return; |
} |
} |
else |
{ |
/* Error response if we don't know the register */ |
fprintf (stderr, "Warning: Attempt to write unknown register 0x%x: " |
"ignored\n", regnum); |
put_str_packet ("E01"); |
return; |
} |
|
put_str_packet ("OK"); |
|
} /* rsp_write_reg () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP query request |
|
@param[in] p_buf The request */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_query (struct rsp_buf *p_buf) |
{ |
if (0 == strcmp ("qC", p_buf->data)) |
{ |
/* Return the current thread ID (unsigned hex). A null response |
indicates to use the previously selected thread. Since we do not |
support a thread concept, this is the appropriate response. */ |
put_str_packet (""); |
} |
else if (0 == strncmp ("qCRC", p_buf->data, strlen ("qCRC"))) |
{ |
/* Return CRC of memory area */ |
fprintf (stderr, "Warning: RSP CRC query not supported\n"); |
put_str_packet ("E01"); |
} |
else if (0 == strcmp ("qfThreadInfo", p_buf->data)) |
{ |
/* Return info about active threads. We return just '-1' */ |
put_str_packet ("m-1"); |
} |
else if (0 == strcmp ("qsThreadInfo", p_buf->data)) |
{ |
/* Return info about more active threads. We have no more, so return the |
end of list marker, 'l' */ |
put_str_packet ("l"); |
} |
else if (0 == strncmp ("qGetTLSAddr:", p_buf->data, strlen ("qGetTLSAddr:"))) |
{ |
/* We don't support this feature */ |
put_str_packet (""); |
} |
else if (0 == strncmp ("qL", p_buf->data, strlen ("qL"))) |
{ |
/* Deprecated and replaced by 'qfThreadInfo' */ |
fprintf (stderr, "Warning: RSP qL deprecated: no info returned\n"); |
put_str_packet ("qM001"); |
} |
else if (0 == strcmp ("qOffsets", p_buf->data)) |
{ |
/* Report any relocation */ |
put_str_packet ("Text=0;Data=0;Bss=0"); |
} |
else if (0 == strncmp ("qP", p_buf->data, strlen ("qP"))) |
{ |
/* Deprecated and replaced by 'qThreadExtraInfo' */ |
fprintf (stderr, "Warning: RSP qP deprecated: no info returned\n"); |
put_str_packet (""); |
} |
else if (0 == strncmp ("qRcmd,", p_buf->data, strlen ("qRcmd,"))) |
{ |
/* This is used to interface to commands to do "stuff" */ |
rsp_command (p_buf); |
} |
else if (0 == strncmp ("qSupported", p_buf->data, strlen ("qSupported"))) |
{ |
/* Report a list of the features we support. For now we just ignore any |
supplied specific feature queries, but in the future these may be |
supported as well. Note that the packet size allows for 'G' + all the |
registers sent to us, or a reply to 'g' with all the registers and an |
EOS so the buffer is a well formed string. */ |
setup_or32(); // setup cpu |
char reply[GDB_BUF_MAX]; |
sprintf (reply, "PacketSize=%x", GDB_BUF_MAX); |
put_str_packet (reply); |
} |
else if (0 == strncmp ("qSymbol:", p_buf->data, strlen ("qSymbol:"))) |
{ |
/* Offer to look up symbols. Nothing we want (for now). TODO. This just |
ignores any replies to symbols we looked up, but we didn't want to |
do that anyway! */ |
put_str_packet ("OK"); |
} |
else if (0 == strncmp ("qThreadExtraInfo,", p_buf->data, |
strlen ("qThreadExtraInfo,"))) |
{ |
/* Report that we are runnable, but the text must be hex ASCI |
digits. For now do this by steam, reusing the original packet */ |
sprintf (p_buf->data, "%02x%02x%02x%02x%02x%02x%02x%02x%02x", |
'R', 'u', 'n', 'n', 'a', 'b', 'l', 'e', 0); |
p_buf->len = strlen (p_buf->data); |
put_packet (p_buf); |
} |
else if (0 == strncmp ("qXfer:", p_buf->data, strlen ("qXfer:"))) |
{ |
/* For now we support no 'qXfer' requests, but these should not be |
expected, since they were not reported by 'qSupported' */ |
fprintf (stderr, "Warning: RSP 'qXfer' not supported: ignored\n"); |
put_str_packet (""); |
} |
else |
{ |
fprintf (stderr, "Unrecognized RSP query: ignored\n"); |
} |
} /* rsp_query () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP qRcmd request |
|
The actual command follows the "qRcmd," in ASCII encoded to hex |
|
@param[in] p_buf The request in full */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_command (struct rsp_buf *p_buf) |
{ |
char cmd[GDB_BUF_MAX]; |
unsigned int regno; |
uint32_t temp_uint32; |
|
hex2ascii (cmd, &(p_buf->data[strlen ("qRcmd,")])); |
|
/* Work out which command it is */ |
if (0 == strncmp ("readspr ", cmd, strlen ("readspr"))) |
{ |
/* Parse and return error if we fail */ |
if( 1 != sscanf (cmd, "readspr %4x", ®no)) |
{ |
fprintf (stderr, "Warning: qRcmd %s not recognized: ignored\n", cmd); |
put_str_packet ("E01"); |
return; |
} |
|
/* SPR out of range */ |
if (regno > MAX_SPRS) |
{ |
fprintf (stderr, "Warning: qRcmd readspr %x too large: ignored\n", |
regno); |
put_str_packet ("E01"); |
return; |
} |
|
/* Construct the reply */ |
|
// Make sure the processor is stalled |
gdb_ensure_or1k_stalled(); |
|
// First set the chain |
gdb_set_chain(SC_RISC_DEBUG); /* 1 RISC Debug Interface chain */ |
|
// special case for NPC |
if(regno == NPC_CPU_REG_ADD) |
temp_uint32 = get_npc(); |
else |
{ |
err = gdb_read_reg(regno, &temp_uint32); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_command at reg. %x \n", err, regno); |
} |
else{ |
reg2hex (temp_uint32, cmd); |
if (DEBUG_GDB) printf("Error %d Command readspr Read reg. %x = 0x%08x\n", err, regno, temp_uint32); |
} |
} |
|
// pack the result into the buffer to send back |
sprintf (cmd, "%8x", (unsigned int)temp_uint32); |
ascii2hex (p_buf->data, cmd); |
p_buf->len = strlen (p_buf->data); |
put_packet (p_buf); |
} |
else if (0 == strncmp ("writespr ", cmd, strlen ("writespr"))) |
{ |
unsigned int regno; |
uint32_t val; |
|
/* Parse and return error if we fail */ |
if( 2 != sscanf (cmd, "writespr %4x %8x", ®no, &val)) |
{ |
fprintf (stderr, "Warning: qRcmd %s not recognized: ignored\n", |
cmd); |
put_str_packet ("E01"); |
return; |
} |
|
/* SPR out of range */ |
if (regno > MAX_SPRS) |
{ |
fprintf (stderr, "Warning: qRcmd writespr %x too large: ignored\n", |
regno); |
put_str_packet ("E01"); |
return; |
} |
|
// Make sure the processor is stalled |
gdb_ensure_or1k_stalled(); |
|
// First set the chain |
gdb_set_chain(SC_RISC_DEBUG); /* 1 RISC Debug Interface chain */ |
|
/* set the relevant register */ |
// special case for NPC |
if(regno == NPC_CPU_REG_ADD) |
set_npc(val); |
else |
{ |
|
err = gdb_write_reg(regno, val); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_command write Reg. %x = 0x%08x\n", err, regno, val); |
put_str_packet ("E01"); |
return; |
} |
else{ |
if (DEBUG_GDB) printf("Error %d Command writespr Write reg. %x = 0x%08x\n", err, regno, val); |
} |
} |
put_str_packet ("OK"); |
} |
} /* rsp_command () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP set request |
|
@param[in] p_buf The request */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_set (struct rsp_buf *p_buf) |
{ |
if (0 == strncmp ("QPassSignals:", p_buf->data, strlen ("QPassSignals:"))) |
{ |
/* Passing signals not supported */ |
put_str_packet (""); |
} |
else if ((0 == strncmp ("QTDP", p_buf->data, strlen ("QTDP"))) || |
(0 == strncmp ("QFrame", p_buf->data, strlen ("QFrame"))) || |
(0 == strcmp ("QTStart", p_buf->data)) || |
(0 == strcmp ("QTStop", p_buf->data)) || |
(0 == strcmp ("QTinit", p_buf->data)) || |
(0 == strncmp ("QTro", p_buf->data, strlen ("QTro")))) |
{ |
/* All tracepoint features are not supported. This reply is really only |
needed to 'QTDP', since with that the others should not be |
generated. */ |
put_str_packet (""); |
} |
else |
{ |
fprintf (stderr, "Unrecognized RSP set request: ignored\n"); |
} |
} /* rsp_set () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP restart request |
|
For now we just put the program counter back to the one used with the last |
vRun request. There is no point in unstalling the processor, since we'll |
never get control back. */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_restart (void) |
{ |
// Make sure the processor is stalled |
gdb_ensure_or1k_stalled(); |
|
// First set the chain |
err = gdb_set_chain(SC_RISC_DEBUG); /* 1 RISC Debug Interface chain */ |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in gdb_set_chain\n", err); |
put_str_packet ("E01"); |
return; |
} |
// OR32 Arc sim equivalent --> set_npc (rsp.start_addr); |
/* Set NPC to reset vector 0x100 */ |
set_npc(rsp.start_addr); |
/* |
err = gdb_write_reg(NPC_CPU_REG_ADD, rsp.start_addr); |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in rsp_restart write Reg. %x = 0x%08x\n", err, NPC_CPU_REG_ADD, rsp.start_addr); |
put_str_packet ("E01"); |
return; |
} |
|
else{ |
if (DEBUG_GDB) printf("Error %d Command Reset. Set NPC to Start vector %x = 0x%08x\n", err, NPC_CPU_REG_ADD, rsp.start_addr); |
} |
*/ |
} /* rsp_restart () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP step request |
|
Parse the command to see if there is an address. Uses the underlying |
generic step function, with EXCEPT_NONE. |
|
@param[in] p_buf The full step packet */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_step (struct rsp_buf *p_buf) |
{ |
uint32_t addr; /* The address to step from, if any */ |
|
// Make sure the processor is stalled |
gdb_ensure_or1k_stalled(); |
|
// First set the chain |
err = gdb_set_chain(SC_RISC_DEBUG); /* 1 RISC Debug Interface chain */ |
if(err > 0){ |
printf("Error %d to set RISC Debug Interface chain in the STEP command 's'\n", err); |
rsp_client_close (); |
return; |
} |
|
if (0 == strcmp ("s", p_buf->data)) |
{ |
// Arc Sim Code --> addr = cpu_state.pc; /* Default uses current NPC */ |
/* ---------- Npc ---------- */ |
addr = get_npc(); |
/* |
err = gdb_read_reg(NPC_CPU_REG_ADD, &addr); |
if(err > 0){ |
printf("Error %d to read NPC in the STEP command 's'\n", err); |
rsp_client_close (); |
return; |
} |
*/ |
} |
else if (1 != sscanf (p_buf->data, "s%x", &addr)) |
{ |
fprintf (stderr, |
"Warning: RSP step address %s not recognized: ignored\n", |
p_buf->data); |
|
// Arc Sim Code --> addr = cpu_state.pc; /* Default uses current NPC */ |
/* ---------- NPC ---------- */ |
addr = get_npc(); |
/* |
err = gdb_read_reg(NPC_CPU_REG_ADD, &addr); |
if(err > 0){ |
printf("Error %d to read NPC in the STEP command 's'\n", err); |
rsp_client_close (); |
return; |
} |
*/ |
} |
|
//if (DEBUG_GDB) printf("rsp_step() --> Read NPC = 0x%08x\n", addr); |
rsp_step_generic (addr, EXCEPT_NONE); |
|
} /* rsp_step () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP step with signal request |
|
Currently null. Will use the underlying generic step function. |
|
@param[in] p_buf The full step with signal packet */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_step_with_signal (struct rsp_buf *p_buf) |
{ |
printf ("RSP step with signal '%s' received\n", p_buf->data); |
|
} /* rsp_step_with_signal () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Generic processing of a step request |
|
The signal may be EXCEPT_NONE if there is no exception to be |
handled. Currently the exception is ignored. |
|
The single step flag is set in the debug registers and then the processor |
is unstalled. |
|
@param[in] addr Address from which to step |
@param[in] except The exception to use (if any) */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_step_generic (uint32_t addr, |
uint32_t except) |
{ |
uint32_t temp_uint32; |
|
/* Set the address as the value of the next program counter */ |
|
set_npc (addr); |
|
/* Clear Debug Reason Register (DRR) 0x3015 */ |
// Arc sim --> cpu_state.sprs[SPR_DRR] = 0; |
if(gdb_write_reg(DRR_CPU_REG_ADD, 0)) printf("Error write to DRR register\n"); |
|
/* Clear watchpoint break generation in Debug Mode Register 2 (DMR2) 0x3011 */ |
// Arc sim --> cpu_state.sprs[SPR_DMR2] &= ~SPR_DMR2_WGB; |
if(gdb_read_reg(DMR2_CPU_REG_ADD, &temp_uint32)) printf("Error read from DMR2 register\n"); |
temp_uint32 &= ~SPR_DMR2_WGB; |
if(gdb_write_reg(DMR2_CPU_REG_ADD, temp_uint32)) printf("Error write to DMR2 register\n"); |
|
/* Set the single step trigger in Debug Mode Register 1 (DMR1) Register 0x3010 */ |
// Arc sim --> cpu_state.sprs[SPR_DMR1] |= SPR_DMR1_ST; |
if(gdb_read_reg(DMR1_CPU_REG_ADD, &temp_uint32)) printf("Error read from DMR1 register\n"); |
temp_uint32 |= SPR_DMR1_ST; |
if(gdb_write_reg(DMR1_CPU_REG_ADD, temp_uint32)) printf("Error write to DMR1 register\n"); |
|
/* Set traps to be handled by the debug unit in the Debug Stop Register (DSR) Register 0x3014 */ |
// Arc sim --> cpu_state.sprs[SPR_DSR] |= SPR_DSR_TE; |
if(gdb_read_reg(DSR_CPU_REG_ADD, &temp_uint32)) printf("Error read from DSR register\n"); |
temp_uint32 |= SPR_DSR_TE; |
if(gdb_write_reg(DSR_CPU_REG_ADD, temp_uint32)) printf("Error write to DSR register\n"); |
|
/* Unstall the processor */ |
set_stall_state (0); |
|
/* Note the GDB client is now waiting for a reply. */ |
rsp.client_waiting = 1; |
|
} /* rsp_step_generic () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP 'v' packet |
|
These are commands associated with executing the code on the target |
|
@param[in] p_buf The request */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_vpkt (struct rsp_buf *p_buf) |
{ |
if (0 == strncmp ("vAttach;", p_buf->data, strlen ("vAttach;"))) |
{ |
/* Attaching is a null action, since we have no other process. We just |
return a stop packet (using TRAP) to indicate we are stopped. */ |
put_str_packet ("S05"); |
return; |
} |
else if (0 == strcmp ("vCont?", p_buf->data)) |
{ |
/* For now we don't support this. */ |
put_str_packet (""); |
return; |
} |
else if (0 == strncmp ("vCont", p_buf->data, strlen ("vCont"))) |
{ |
/* This shouldn't happen, because we've reported non-support via vCont? |
above */ |
fprintf (stderr, "Warning: RSP vCont not supported: ignored\n" ); |
return; |
} |
else if (0 == strncmp ("vFile:", p_buf->data, strlen ("vFile:"))) |
{ |
/* For now we don't support this. */ |
fprintf (stderr, "Warning: RSP vFile not supported: ignored\n" ); |
put_str_packet (""); |
return; |
} |
else if (0 == strncmp ("vFlashErase:", p_buf->data, strlen ("vFlashErase:"))) |
{ |
/* For now we don't support this. */ |
fprintf (stderr, "Warning: RSP vFlashErase not supported: ignored\n" ); |
put_str_packet ("E01"); |
return; |
} |
else if (0 == strncmp ("vFlashWrite:", p_buf->data, strlen ("vFlashWrite:"))) |
{ |
/* For now we don't support this. */ |
fprintf (stderr, "Warning: RSP vFlashWrite not supported: ignored\n" ); |
put_str_packet ("E01"); |
return; |
} |
else if (0 == strcmp ("vFlashDone", p_buf->data)) |
{ |
/* For now we don't support this. */ |
fprintf (stderr, "Warning: RSP vFlashDone not supported: ignored\n" ); |
put_str_packet ("E01"); |
return; |
} |
else if (0 == strncmp ("vRun;", p_buf->data, strlen ("vRun;"))) |
{ |
/* We shouldn't be given any args, but check for this */ |
if (p_buf->len > (int) strlen ("vRun;")) |
{ |
fprintf (stderr, "Warning: Unexpected arguments to RSP vRun " |
"command: ignored\n"); |
} |
|
/* Restart the current program. However unlike a "R" packet, "vRun" |
should behave as though it has just stopped. We use signal |
5 (TRAP). */ |
rsp_restart (); |
put_str_packet ("S05"); |
} |
else |
{ |
fprintf (stderr, "Warning: Unknown RSP 'v' packet type %s: ignored\n", |
p_buf->data); |
put_str_packet ("E01"); |
return; |
} |
} /* rsp_vpkt () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP write memory (binary) request |
|
Syntax is: |
|
X<addr>,<length>: |
|
Followed by the specified number of bytes as raw binary. Response should be |
"OK" if all copied OK, E<nn> if error <nn> has occurred. |
|
The length given is the number of bytes to be written. However the number |
of data bytes may be greater, since '#', '$' and '}' are escaped by |
preceding them by '}' and oring with 0x20. |
|
@param[in] p_buf The command received */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_write_mem_bin (struct rsp_buf *p_buf) |
{ |
unsigned int addr; /* Where to write the memory */ |
int len; /* Number of bytes to write */ |
char *bindat; /* Pointer to the binary data */ |
int off = 0; /* Offset to start of binary data */ |
int newlen; /* Number of bytes in bin data */ |
|
if (2 != sscanf (p_buf->data, "X%x,%x:", &addr, &len)) |
{ |
fprintf (stderr, "Warning: Failed to recognize RSP write memory " |
"command: %s\n", p_buf->data); |
put_str_packet ("E01"); |
return; |
} |
|
/* Find the start of the data and "unescape" it */ |
bindat = p_buf->data; |
while(off < GDB_BUF_MAX){ |
if(bindat[off] == ':'){ |
bindat = bindat + off + 1; |
off++; |
break; |
} |
off++; |
} |
if(off >= GDB_BUF_MAX){ |
put_str_packet ("E01"); |
return; |
} |
|
newlen = rsp_unescape (bindat, p_buf->len - off); |
|
/* Sanity check */ |
if (newlen != len) |
{ |
int minlen = len < newlen ? len : newlen; |
|
fprintf (stderr, "Warning: Write of %d bytes requested, but %d bytes " |
"supplied. %d will be written\n", len, newlen, minlen); |
len = minlen; |
} |
|
// Make sure the processor is stalled |
gdb_ensure_or1k_stalled(); |
|
// Set chain 5 --> Wishbone Memory chain |
err = gdb_set_chain(SC_WISHBONE); |
if(err){ |
put_str_packet ("E01"); |
return; |
} |
|
/* Write the bytes to memory */ |
if (len) |
{ |
swap_buf(bindat, len); |
|
if (DEBUG_GDB_BLOCK_DATA){ |
uint32_t temp_uint32; |
for (off = 0; off < len; off++){ |
temp_uint32 = (temp_uint32 << 8) | (0x000000ff & bindat[off]); |
if((off %4 ) == 3){ |
temp_uint32 = htonl(temp_uint32); |
} |
switch(off % 16) |
{ |
case 3: |
printf("Add 0x%08x Data 0x%08x ", addr + off - 3, temp_uint32); |
break; |
case 7: |
case 11: |
printf("0x%08x ", temp_uint32); |
break; |
case 15: |
printf("0x%08x\n", temp_uint32); |
break; |
default: |
break; |
} |
if ((len - off == 1) && (off % 16) < 15) printf("\n"); |
} |
} |
|
err = gdb_write_block(addr, (uint32_t*)bindat, len); |
if(err){ |
put_str_packet ("E01"); |
return; |
} |
if (DEBUG_GDB) printf("Error %x\n", err);fflush (stdout); |
} |
put_str_packet ("OK"); |
|
} /* rsp_write_mem_bin () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP remove breakpoint or matchpoint request |
|
For now only memory breakpoints are implemented, which are implemented by |
substituting a breakpoint at the specified address. The implementation must |
cope with the possibility of duplicate packets. |
|
@todo This doesn't work with icache/immu yet |
|
@param[in] p_buf The command received */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_remove_matchpoint (struct rsp_buf *p_buf) |
{ |
enum mp_type type; /* What sort of matchpoint */ |
uint32_t addr; /* Address specified */ |
int len; /* Matchpoint length (not used) */ |
struct mp_entry *mpe; /* Info about the replaced instr */ |
|
/* Break out the instruction */ |
if (3 != sscanf (p_buf->data, "z%1d,%x,%1d", (int *)&type, &addr, &len)) |
{ |
fprintf (stderr, "Warning: RSP matchpoint deletion request not " |
"recognized: ignored\n"); |
put_str_packet ("E01"); |
return; |
} |
|
/* Sanity check that the length is 4 */ |
if (4 != len) |
{ |
fprintf (stderr, "Warning: RSP matchpoint deletion length %d not " |
"valid: 4 assumed\n", len); |
len = 4; |
} |
|
/* Sort out the type of matchpoint */ |
switch (type) |
{ |
case BP_MEMORY: |
/* Memory breakpoint - replace the original instruction. */ |
mpe = mp_hash_delete (type, addr); |
|
/* If the BP hasn't yet been deleted, put the original instruction |
back. Don't forget to free the hash table entry afterwards. */ |
if (NULL != mpe) |
{ |
// Arc Sim Code --> set_program32 (addr, mpe->instr); |
// Make sure the processor is stalled |
gdb_ensure_or1k_stalled(); |
|
// Set chain 5 --> Wishbone Memory chain |
err = gdb_set_chain(SC_WISHBONE); |
if(err){ |
put_str_packet ("E01"); |
return; |
} |
err = gdb_write_block(addr, &mpe->instr, 4); |
if(err){ |
put_str_packet ("E01"); |
return; |
} |
free (mpe); |
} |
|
put_str_packet ("OK"); |
return; |
|
case BP_HARDWARE: |
put_str_packet (""); /* Not supported */ |
return; |
|
case WP_WRITE: |
put_str_packet (""); /* Not supported */ |
return; |
|
case WP_READ: |
put_str_packet (""); /* Not supported */ |
return; |
|
case WP_ACCESS: |
put_str_packet (""); /* Not supported */ |
return; |
|
default: |
fprintf (stderr, "Warning: RSP matchpoint type %d not " |
"recognized: ignored\n", type); |
put_str_packet ("E01"); |
return; |
|
} |
} /* rsp_remove_matchpoint () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Handle a RSP insert breakpoint or matchpoint request |
|
For now only memory breakpoints are implemented, which are implemented by |
substituting a breakpoint at the specified address. The implementation must |
cope with the possibility of duplicate packets. |
|
@todo This doesn't work with icache/immu yet |
|
@param[in] p_buf The command received */ |
/*---------------------------------------------------------------------------*/ |
static void |
rsp_insert_matchpoint (struct rsp_buf *p_buf) |
{ |
enum mp_type type; /* What sort of matchpoint */ |
uint32_t addr; /* Address specified */ |
int len; /* Matchpoint length (not used) */ |
uint32_t instr; |
|
/* Break out the instruction */ |
if (3 != sscanf (p_buf->data, "Z%1d,%x,%1d", (int *)&type, &addr, &len)) |
{ |
fprintf (stderr, "Warning: RSP matchpoint insertion request not " |
"recognized: ignored\n"); |
put_str_packet ("E01"); |
return; |
} |
|
/* Sanity check that the length is 4 */ |
if (4 != len) |
{ |
fprintf (stderr, "Warning: RSP matchpoint insertion length %d not " |
"valid: 4 assumed\n", len); |
len = 4; |
} |
|
/* Sort out the type of matchpoint */ |
switch (type) |
{ |
case BP_MEMORY: // software-breakpoint Z0 break |
/* Memory breakpoint - substitute a TRAP instruction */ |
// Make sure the processor is stalled |
gdb_ensure_or1k_stalled(); |
|
// Set chain 5 --> Wishbone Memory chain |
gdb_set_chain(SC_WISHBONE); |
|
// Read the data from Wishbone Memory chain |
// Arc Sim Code --> mp_hash_add (type, addr, eval_direct32 (addr, 0, 0)); |
gdb_read_block(addr, &instr, 4); |
|
mp_hash_add (type, addr, instr); |
|
// Arc Sim Code --> set_program32 (addr, OR1K_TRAP_INSTR); |
instr = OR1K_TRAP_INSTR; |
err = gdb_write_block(addr, &instr, 4); |
if(err){ |
put_str_packet ("E01"); |
return; |
} |
put_str_packet ("OK"); |
return; |
|
case BP_HARDWARE: // hardware-breakpoint Z1 hbreak |
put_str_packet (""); /* Not supported */ |
return; |
|
case WP_WRITE: // write-watchpoint Z2 watch |
put_str_packet (""); /* Not supported */ |
return; |
|
case WP_READ: // read-watchpoint Z3 rwatch |
put_str_packet (""); /* Not supported */ |
return; |
|
case WP_ACCESS: // access-watchpoint Z4 awatch |
put_str_packet (""); /* Not supported */ |
return; |
|
default: |
fprintf (stderr, "Warning: RSP matchpoint type %d not " |
"recognized: ignored\n", type); |
put_str_packet ("E01"); |
return; |
} |
} /* rsp_insert_matchpoint () */ |
|
|
/*--------------------------------------------------------------------------- |
Setup the or32 to init state |
|
---------------------------------------------------------------------------*/ |
void setup_or32(void) |
{ |
uint32_t temp_uint32; |
// First set the chain |
err = gdb_set_chain(SC_REGISTER); /* 4 Register Chain */ |
if(err > 0){ |
if (DEBUG_GDB) printf("Error %d in gdb_set_chain\n", err); |
} |
if(gdb_read_reg(0x04, &temp_uint32)) printf("Error read from register\n"); |
if (DEBUG_GDB) printf("Read from chain 4 SC_REGISTER at add 0x00000004 = 0x%08x\n", temp_uint32 ); |
|
if(gdb_write_reg(0x04, 0x00000001)) printf("Error write to register\n"); |
if (DEBUG_GDB) printf("Write to chain 4 SC_REGISTER at add 0x00000004 = 0x00000001\n"); |
|
// if(gdb_read_reg(0x04, &temp_uint32)) printf("Error read from register\n"); |
// if (DEBUG_GDB) printf("Read from chain 4 SC_REGISTER at add 0x00000004 = 0x%08x\n", temp_uint32 ); |
|
// if(gdb_read_reg(0x04, &temp_uint32)) printf("Error read from register\n"); |
// if (DEBUG_GDB) printf("Read from chain 4 SC_REGISTER at add 0x00000004 = 0x%08x\n", temp_uint32 ); |
|
if(gdb_write_reg(0x00, 0x01000001)) printf("Error write to register\n"); |
if (DEBUG_GDB) printf("Write to chain 4 SC_REGISTER at add 0x00000000 = 0x01000001\n"); |
} |
|
// Function to check if the processor is stalled - if not then stall it. |
// this is useful in the event that GDB thinks the processor is stalled, but has, in fact |
// been hard reset on the board and is running. |
static void gdb_ensure_or1k_stalled() |
{ |
// Disable continual checking that the or1k is stalled |
#ifdef ENABLE_OR1K_STALL_CHECK |
unsigned char stalled; |
dbg_cpu0_read_ctrl(0, &stalled); |
if ((stalled & 0x1) != 0x1) |
{ |
if (DEBUG_GDB) |
printf("Processor not stalled, like we thought\n"); |
|
// Set the TAP controller to its OR1k chain |
dbg_set_tap_ir(JI_DEBUG); |
gdb_chain = -1; |
|
// Processor isn't stalled, contrary to what we though, so stall it |
printf("Stalling or1k\n"); |
dbg_cpu0_write_ctrl(0, 0x01); // stall or1k |
} |
#endif |
return; |
} |
|
|
int gdb_read_reg(uint32_t adr, uint32_t *data) { |
switch (gdb_chain) { |
case SC_RISC_DEBUG: return dbg_cpu0_read(adr, data) ? ERR_CRC : ERR_NONE; |
case SC_REGISTER: return dbg_cpu0_read_ctrl(adr, (unsigned char*)data) ? |
ERR_CRC : ERR_NONE; |
case SC_WISHBONE: return dbg_wb_read32(adr, data) ? ERR_CRC : ERR_NONE; |
case SC_TRACE: *data = 0; return 0; |
default: return JTAG_PROXY_INVALID_CHAIN; |
} |
} |
|
int gdb_write_reg(uint32_t adr, uint32_t data) { |
switch (gdb_chain) { /* remap registers, to be compatible with jp1 */ |
case SC_RISC_DEBUG: if (adr == JTAG_RISCOP) adr = 0x00; |
return dbg_cpu0_write(adr, data) ? ERR_CRC : ERR_NONE; |
case SC_REGISTER: return dbg_cpu0_write_ctrl(adr, data) ? ERR_CRC : ERR_NONE; |
case SC_WISHBONE: return dbg_wb_write32(adr, data) ? ERR_CRC : ERR_NONE; |
case SC_TRACE: return 0; |
default: return JTAG_PROXY_INVALID_CHAIN; |
} |
} |
|
int gdb_read_block(uint32_t adr, uint32_t *data, int len) { |
if (DEBUG_CMDS) printf("rb %d\n", gdb_chain); |
switch (gdb_chain) { |
case SC_WISHBONE: return dbg_wb_read_block32(adr, data, len) ? |
ERR_CRC : ERR_NONE; |
default: return JTAG_PROXY_INVALID_CHAIN; |
} |
} |
|
int gdb_write_block(uint32_t adr, uint32_t *data, int len) { |
if (DEBUG_CMDS) printf("wb %d\n", gdb_chain); |
switch (gdb_chain) { |
case SC_WISHBONE: return dbg_wb_write_block32(adr, data, len) ? |
ERR_CRC : ERR_NONE; |
default: return JTAG_PROXY_INVALID_CHAIN; |
} |
} |
|
int gdb_set_chain(int chain) { |
int rv; |
switch (chain) { |
case SC_RISC_DEBUG: |
case SC_REGISTER: |
case SC_TRACE: |
case SC_WISHBONE: gdb_chain = chain; |
rv = ERR_NONE; |
break; |
default: rv = JTAG_PROXY_INVALID_CHAIN; |
break; |
} |
return rv; |
} |
|
/* Added by CZ 24/05/01 */ |
int GetServerSocket(const char* name, const char* proto, int port) { |
struct servent *service; |
struct protoent *protocol; |
struct sockaddr_in sa; |
struct hostent *hp; |
int sockfd; |
char myname[256]; |
//int flags; --changed to socklen_t for c++?! -- Julius |
socklen_t flags; |
char sTemp[256]; |
|
/* First, get the protocol number of TCP */ |
if (!(protocol = getprotobyname(proto))) { |
sprintf(sTemp, "Unable to load protocol \"%s\"", proto); |
perror(sTemp); |
return 0; |
} |
tcp_level = protocol->p_proto; /* Save for later */ |
|
/* If we weren't passed a non standard port, get the port |
from the services directory. */ |
if (!port && (service = getservbyname(name, protocol->p_name))) |
port = ntohs(service->s_port); |
|
/* Create the socket using the TCP protocol */ |
if ((sockfd = socket(PF_INET, SOCK_STREAM, protocol->p_proto)) < 0) { |
perror("Unable to create socket"); |
return 0; |
} |
|
flags = 1; |
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, |
(const char*)&flags, sizeof(int)) < 0) { |
sprintf(sTemp, "Can not set SO_REUSEADDR option on socket %d", sockfd); |
perror(sTemp); |
close(sockfd); |
return 0; |
} |
|
/* The server should also be non blocking. Get the current flags. */ |
if(fcntl(sockfd, F_GETFL, &flags) < 0) { |
sprintf(sTemp, "Unable to get flags for socket %d", sockfd); |
perror(sTemp); |
close(sockfd); |
return 0; |
} |
|
/* Set the nonblocking flag */ |
if(fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) { |
sprintf(sTemp, "Unable to set flags for socket %d to value 0x%08x", |
sockfd, flags | O_NONBLOCK); |
perror(sTemp); |
close(sockfd); |
return 0; |
} |
|
/* Find out what our address is */ |
memset(&sa, 0, sizeof(struct sockaddr_in)); |
gethostname(myname, sizeof(myname)); |
if(!(hp = gethostbyname(myname))) { |
perror("Unable to read hostname"); |
close(sockfd); |
return 0; |
} |
|
/* Bind our socket to the appropriate address */ |
sa.sin_family = hp->h_addrtype; |
sa.sin_port = htons(port); |
if(bind(sockfd, (struct sockaddr*)&sa, sizeof(struct sockaddr_in)) < 0) { |
sprintf(sTemp, "Unable to bind socket %d to port %d", sockfd, port); |
perror(sTemp); |
close(sockfd); |
return 0; |
} |
serverIP = sa.sin_addr.s_addr; |
flags = sizeof(struct sockaddr_in); |
if(getsockname(sockfd, (struct sockaddr*)&sa, &flags) < 0) { |
sprintf(sTemp, "Unable to get socket information for socket %d", sockfd); |
perror(sTemp); |
close(sockfd); |
return 0; |
} |
serverPort = ntohs(sa.sin_port); |
|
/* Set the backlog to 1 connections */ |
if(listen(sockfd, 1) < 0) { |
sprintf(sTemp, "Unable to set backlog on socket %d to %d", sockfd, 1); |
perror(sTemp); |
close(sockfd); |
return 0; |
} |
|
return sockfd; |
} |
|
//void HandleServerSocket(Boolean block) { |
void HandleServerSocket(void) { |
struct pollfd fds[2]; |
int n; |
uint32_t temp_uint32; |
|
rebuild: |
n = 0; |
if(!server_fd && !gdb_fd) return; |
|
if(server_fd) { |
fds[n].fd = server_fd; |
fds[n].events = POLLIN; |
fds[n++].revents = 0; |
} |
if(gdb_fd) { |
fds[n].fd = gdb_fd; |
fds[n].events = POLLIN; |
fds[n++].revents = 0; |
} |
|
while(1) { |
switch(poll(fds, n, -1)) { |
case 0: |
case -1: |
if(errno == EINTR) continue; |
perror("poll"); |
server_fd = 0; |
return; |
default: |
/* Make sure to handle the gdb port first! */ |
if (gdb_fd && ((fds[0].revents && !server_fd) || (fds[1].revents && server_fd))) |
{ |
int revents = server_fd ? fds[1].revents : fds[0].revents; |
if (revents & POLLIN){ |
/* If we have an unacknowledged exception tell the GDB client. If this |
exception was a trap due to a memory breakpoint, then adjust the NPC. */ |
if (rsp.client_waiting) |
{ |
err = gdb_read_reg(PPC_CPU_REG_ADD, &temp_uint32); |
if(err) printf("Error read from PPC register\n"); |
if ((TARGET_SIGNAL_TRAP == rsp.sigval) && |
(NULL != mp_hash_lookup (BP_MEMORY, temp_uint32))) |
{ |
set_npc (temp_uint32); |
} |
|
rsp_report_exception(); |
rsp.client_waiting = 0; /* No longer waiting */ |
} |
GDBRequest(); |
} |
else {/* Error Occurred */ |
printf("\n%sSocket closed.\n",printTime()); |
//fprintf(stderr, |
//"Received flags 0x%08x on gdb socket. Shutting down.\n", revents); |
close(gdb_fd); |
gdb_fd = 0; |
} |
} |
|
// Go to blocking accept() instead of looping around through poll(), |
// takes a loot of CPU resources and it doesn't work when |
|
if(!gdb_fd) |
{ |
JTAGRequest(); |
rsp.client_waiting = 0; /* No longer waiting */ |
goto rebuild; |
} |
|
if(fds[0].revents && server_fd) { |
if(fds[0].revents & POLLIN) { |
JTAGRequest(); |
rsp.client_waiting = 0; /* No longer waiting */ |
goto rebuild; |
} else { /* Error Occurred */ |
fprintf(stderr, |
"Received flags 0x%08x on server. Shutting down.\n", |
fds[0].revents); |
close(server_fd); |
server_fd = 0; |
serverPort = 0; |
serverIP = 0; |
return; |
} |
} |
break; |
} /* End of switch statement */ |
} /* End of while statement */ |
} |
|
void JTAGRequest(void) { |
struct sockaddr_in sa; |
struct sockaddr* addr = (struct sockaddr*)&sa; |
//int n = sizeof(struct sockaddr_in); --changed to socklen_t from int type |
socklen_t n = sizeof(struct sockaddr_in); |
int fd = accept(server_fd, addr, &n); |
int on_off = 0; /* Turn off Nagel's algorithm on the socket */ |
int flags; |
char sTemp[256]; |
if (DEBUG_GDB) printf("JTAGRequest\n"); |
|
if(fd < 0) { |
/* This is valid, because a connection could have started, |
and then terminated due to a protocol error or user |
initiation before the accept could take place. */ |
if(errno != EWOULDBLOCK && errno != EAGAIN) { |
perror("accept"); |
close(server_fd); |
server_fd = 0; |
serverPort = 0; |
serverIP = 0; |
} |
return; |
} |
|
if(gdb_fd) { |
close(fd); |
return; |
} |
|
if(fcntl(fd, F_GETFL, &flags) < 0) { |
sprintf(sTemp, "Unable to get flags for gdb socket %d", fd); |
perror(sTemp); |
close(fd); |
return; |
} |
|
/* Rene |
if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { |
sprintf(sTemp, "Unable to set flags for gdb socket %d to value 0x%08x", |
fd, flags | O_NONBLOCK); |
perror(sTemp); |
close(fd); |
return; |
} Rene */ |
|
if(setsockopt(fd, tcp_level, TCP_NODELAY, &on_off, sizeof(int)) < 0) { |
sprintf(sTemp, "Unable to disable Nagel's algorithm for socket %d.\nsetsockopt", fd); |
perror(sTemp); |
close(fd); |
return; |
} |
|
printf("\n%sConnection established from %s on port %d\n", printTime(),inet_ntoa(sa.sin_addr),ntohs(sa.sin_port)); |
gdb_fd = fd; |
} |
|
|
/*--------------------------------------------------------------------------- |
* Decode the GDB command. |
* |
*---------------------------------------------------------------------------*/ |
static void GDBRequest(void) { |
JTAGProxyWriteMessage msg_write; |
JTAGProxyReadMessage msg_read; |
JTAGProxyChainMessage msg_chain; |
JTAGProxyWriteResponse resp_write; |
JTAGProxyReadResponse resp_read; |
JTAGProxyChainResponse resp_chain; |
JTAGProxyBlockWriteMessage *msg_bwrite; |
JTAGProxyBlockReadMessage msg_bread; |
JTAGProxyBlockWriteResponse resp_bwrite; |
JTAGProxyBlockReadResponse *resp_bread; |
char *p_buf; |
uint32_t command; |
uint32_t length; |
int len, i; |
|
/* First, we must read the incomming command */ |
if(gdb_read(&command, sizeof(uint32_t)) < 0) { |
client_close ('1'); |
return; |
} |
command = ntohl(command); |
|
if(gdb_read(&length, sizeof(uint32_t)) < 0) { |
client_close ('2'); |
return; |
} |
length = ntohl(length); |
if (DEBUG_GDB) printf("\n%s-----------------------------------------------------\nCommand %d Length %d ", printTime(), command, length); |
|
if (DEBUG_GDB){ |
switch(command){ |
case JTAG_COMMAND_READ: |
printf("JTAG_COMMAND_READ \n"); |
break; |
case JTAG_COMMAND_WRITE: |
printf("JTAG_COMMAND_WRITE \n"); |
break; |
case JTAG_COMMAND_BLOCK_READ: |
printf("JTAG_COMMAND_BLOCK_READ \n"); |
break; |
case JTAG_COMMAND_BLOCK_WRITE: |
printf("JTAG_COMMAND_BLOCK_WRITE\n"); |
break; |
case JTAG_COMMAND_CHAIN: |
printf("JTAG_COMMAND_CHAIN \n"); |
break; |
} |
} |
|
/* Now, verify the protocol and implement the command */ |
switch(command) { |
case JTAG_COMMAND_WRITE: |
if(length != sizeof(msg_write) - 8) { |
ProtocolClean(length, JTAG_PROXY_PROTOCOL_ERROR); |
return; |
} |
p_buf = (char*)&msg_write; |
if(gdb_read(&p_buf[8], length) < 0) { |
client_close ('3'); |
return; |
} |
msg_write.address = ntohl(msg_write.address); |
msg_write.data_H = ntohl(msg_write.data_H); |
msg_write.data_L = ntohl(msg_write.data_L); |
err = gdb_write_reg(msg_write.address, msg_write.data_L); |
resp_write.status = htonl(err); |
if (DEBUG_GDB) printf("Write Reg to Chain %d at add 0x%08x -> H-Data 0x%08x L-Data 0x%08x Error %d", |
gdb_chain, msg_write.address, msg_write.data_H, msg_write.data_L, err);fflush (stdout); |
if(gdb_write(&resp_write, sizeof(resp_write)) < 0) { |
client_close ('4'); |
return; |
} |
break; |
case JTAG_COMMAND_READ: |
if(length != sizeof(msg_read) - 8) { |
ProtocolClean(length, JTAG_PROXY_PROTOCOL_ERROR); |
return; |
} |
p_buf = (char*)&msg_read; |
if(gdb_read(&p_buf[8], length) < 0) { |
client_close ('5'); |
return; |
} |
msg_read.address = ntohl(msg_read.address); |
err = gdb_read_reg(msg_read.address, (uint32_t *)&resp_read.data_L); |
if (DEBUG_GDB) printf("Read Reg from Chain %d at add 0x%08x", gdb_chain, msg_read.address); |
resp_read.status = htonl(err); |
resp_read.data_H = 0; |
resp_read.data_L = htonl(resp_read.data_L); |
if(gdb_write(&resp_read, sizeof(resp_read)) < 0) { |
client_close ('6'); |
return; |
} |
if (DEBUG_GDB) printf(" --> Data 0x%08x Error %d\n", htonl(resp_read.data_L), err);fflush (stdout); |
break; |
case JTAG_COMMAND_BLOCK_WRITE: |
if(length < sizeof(JTAGProxyBlockWriteMessage)-8) { |
ProtocolClean(length, JTAG_PROXY_PROTOCOL_ERROR); |
return; |
} |
if(!(p_buf = (char*)malloc(8+length))) { |
ProtocolClean(length, JTAG_PROXY_OUT_OF_MEMORY); |
return; |
} |
msg_bwrite = (JTAGProxyBlockWriteMessage*)p_buf; |
if(gdb_read(&p_buf[8], length) < 0) { |
client_close ('5'); |
free(p_buf); |
return; |
} |
msg_bwrite->address = ntohl(msg_bwrite->address); |
msg_bwrite->nRegisters = ntohl(msg_bwrite->nRegisters); |
if (DEBUG_GDB) printf("Block Write to Chain %d start add 0x%08x Write %d (32 bit words):\n\n", gdb_chain, msg_bwrite->address, msg_bwrite->nRegisters); |
for(i=0;i<msg_bwrite->nRegisters;i++) { |
msg_bwrite->data[i] = ntohl(msg_bwrite->data[i]); |
if (DEBUG_GDB_BLOCK_DATA){ |
if ((i % 4) == 0) printf("Add 0x%08x Data 0x%08x ", msg_bwrite->address + (i * 4), msg_bwrite->data[i]); |
else if ((i % 4) == 3) printf("0x%08x\n", msg_bwrite->data[i]); |
else printf("0x%08x ", msg_bwrite->data[i]); |
|
// add a new line on the last data, but not if it is the last one in the colum |
if ((msg_bwrite->nRegisters - i == 1) && (i % 4) < 3) printf("\n"); |
} |
} |
err = gdb_write_block(msg_bwrite->address, (uint32_t*)msg_bwrite->data, msg_bwrite->nRegisters * 4); |
if (DEBUG_GDB) printf("Error %x\n", err);fflush (stdout); |
resp_bwrite.status = htonl(err); |
free(p_buf); |
msg_bwrite = (JTAGProxyBlockWriteMessage *)NULL; |
p_buf = (char *)msg_bwrite; |
if(gdb_write(&resp_bwrite, sizeof(resp_bwrite)) < 0) { |
client_close ('4'); |
return; |
} |
break; |
case JTAG_COMMAND_BLOCK_READ: |
if(length != sizeof(msg_bread) - 8) { |
ProtocolClean(length, JTAG_PROXY_PROTOCOL_ERROR); |
return; |
} |
p_buf = (char*)&msg_bread; |
if(gdb_read(&p_buf[8], length) < 0) { |
client_close ('5'); |
return; |
} |
msg_bread.address = ntohl(msg_bread.address); |
msg_bread.nRegisters = ntohl(msg_bread.nRegisters); |
if (DEBUG_GDB) printf("Block Read from Chain %d start add 0x%08x Read %d (32 bit words):\n\n", gdb_chain, msg_bread.address, msg_bread.nRegisters); |
len = sizeof(JTAGProxyBlockReadResponse) + 4*(msg_bread.nRegisters-1); |
if(!(p_buf = (char*)malloc(len))) { |
ProtocolClean(0, JTAG_PROXY_OUT_OF_MEMORY); |
return; |
} |
resp_bread = (JTAGProxyBlockReadResponse*)p_buf; |
err = gdb_read_block(msg_bread.address, (uint32_t*)resp_bread->data, msg_bread.nRegisters * 4); |
for(i=0;i<msg_bread.nRegisters;i++) { |
/* Read previous, address next one. */ |
resp_bread->data[i] = htonl(resp_bread->data[i]); |
if (DEBUG_GDB_BLOCK_DATA){ |
if ((i % 4) == 0) printf("Add 0x%08x Data 0x%08x ", msg_bread.address + (i * 4), htonl(resp_bread->data[i])); |
else if ((i % 4) == 3) printf("0x%08x\n", htonl(resp_bread->data[i])); |
else printf("0x%08x ", htonl(resp_bread->data[i])); |
} |
// add a new line on the last data, but not if it is the last one in the colum |
if ((msg_bread.nRegisters - i == 1) && (i % 4) < 3) printf("\n"); |
} |
resp_bread->status = htonl(err); |
resp_bread->nRegisters = htonl(msg_bread.nRegisters); |
if (DEBUG_GDB) printf("\nError %x\n", err);fflush (stdout); |
if(gdb_write(resp_bread, len) < 0) { |
client_close ('6'); |
free(p_buf); |
return; |
} |
free(p_buf); |
resp_bread = (JTAGProxyBlockReadResponse *)NULL; |
p_buf = (char *)resp_bread; |
break; |
case JTAG_COMMAND_CHAIN: |
if(length != sizeof(msg_chain) - 8) { |
ProtocolClean(length, JTAG_PROXY_PROTOCOL_ERROR); |
return; |
} |
p_buf = (char*)&msg_chain; |
if(gdb_read(&p_buf[8], sizeof(msg_chain)-8) < 0) { |
client_close ('7'); |
return; |
} |
msg_chain.chain = htonl(msg_chain.chain); |
err = gdb_set_chain(msg_chain.chain); |
resp_chain.status = htonl(err); |
if (DEBUG_GDB){ |
switch(msg_chain.chain){ |
case SC_GLOBAL: /* 0 Global BS Chain */ |
printf("Set Chain %d Global BS Chain Error %x\n", msg_chain.chain, err); |
break; |
case SC_RISC_DEBUG: /* 1 RISC Debug Interface chain */ |
printf("Set Chain %d RISC Debug Interface chain Error %x\n", msg_chain.chain, err); |
break; |
case SC_RISC_TEST: /* 2 RISC Test Chain */ |
printf("Set Chain %d RISC Test Chain Error %x\n", msg_chain.chain, err); |
break; |
case SC_TRACE: /* 3 Trace Chain */ |
printf("Set Chain %d Trace Chain Error %x\n", msg_chain.chain, err); |
break; |
case SC_REGISTER: /* 4 Register Chain */ |
printf("Set Chain %d Register Chain Error %x\n", msg_chain.chain, err); |
break; |
case SC_WISHBONE: /* 5 Memory chain */ |
printf("Set Chain %d Wishbone Memory chain Error %x\n", msg_chain.chain, err); |
break; |
case SC_BLOCK: /* 6 Block Chains */ |
printf("Set Chain %d Block Chains Error %x\n", msg_chain.chain, err); |
break; |
default: /* Invalid chain */ |
printf("Set Chain %d Invalid chain Error %x\n", msg_chain.chain, err); |
break; |
} |
fflush (stdout); |
} |
if(gdb_write(&resp_chain, sizeof(resp_chain)) < 0) { |
client_close ('8'); |
return; |
} |
break; |
default: |
perror("Unknown JTAG command.");fflush (stdout); |
ProtocolClean(length, JTAG_PROXY_COMMAND_NOT_IMPLEMENTED); |
break; |
} |
} |
|
static void ProtocolClean(int length, int32_t err) { |
char buffer[4096]; |
|
err = htonl(err); |
if(((gdb_read(buffer, length) < 0) || (gdb_write(&err, sizeof(err)) < 0)) && gdb_fd) { |
perror("gdb socket - 9"); |
close(gdb_fd); |
gdb_fd = 0; |
} |
} |
|
static int gdb_write(void* p_buf, int len) { |
int n; |
char* w_buf = (char*)p_buf; |
struct pollfd block; |
|
while(len) { |
if((n = write(gdb_fd, w_buf, len)) < 0) { |
switch(errno) { |
case EWOULDBLOCK: /* or EAGAIN */ |
/* We've been called on a descriptor marked |
for nonblocking I/O. We better simulate |
blocking behavior. */ |
block.fd = gdb_fd; |
block.events = POLLOUT; |
block.revents = 0; |
poll(&block, 1, -1); |
continue; |
case EINTR: |
continue; |
case EPIPE: |
close(gdb_fd); |
gdb_fd = 0; |
return -1; |
default: |
return -1; |
} |
} else { |
len -= n; |
w_buf += n; |
} |
} |
return 0; |
} |
|
static int gdb_read(void* p_buf, int len) { |
int n; |
char* r_buf = (char*)p_buf; |
struct pollfd block; |
|
while(len) { |
if((n = read(gdb_fd, r_buf, len)) < 0) { |
switch(errno) { |
case EWOULDBLOCK: /* or EAGAIN */ |
/* We've been called on a descriptor marked |
for nonblocking I/O. We better simulate |
blocking behavior. */ |
block.fd = gdb_fd; |
block.events = POLLIN; |
block.revents = 0; |
poll(&block, 1, -1); |
continue; |
case EINTR: |
continue; |
default: |
return -1; |
} |
} else if(n == 0) { |
close(gdb_fd); |
gdb_fd = 0; |
return -1; |
} else { |
len -= n; |
r_buf += n; |
} |
} |
return 0; |
} |
|
|
/***************************************************************************** |
* Close the connection to the client if it is open |
******************************************************************************/ |
static void client_close (char err) |
{ |
if(gdb_fd) { |
perror("gdb socket - " + err); |
close(gdb_fd); |
gdb_fd = 0; |
} |
} /* client_close () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/* Swap a buffer of 4-byte from 1234 to 4321 |
|
parameter[in] p_buf and len |
parameter[out] none */ |
/*---------------------------------------------------------------------------*/ |
static void swap_buf(char* p_buf, int len) |
{ |
int temp; |
int n = 0; |
|
if (len > 2) |
{ |
while(n < len){ |
// swap 0 and 3 |
temp = p_buf[n]; |
p_buf[n] = p_buf[n + 3]; |
p_buf[n + 3] = temp; |
// swap 1 and 2 |
temp = p_buf[n + 1]; |
p_buf[n + 1] = p_buf[n + 2]; |
p_buf[n + 2] = temp; |
|
n += 4; |
} |
} |
} |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Set the stall state of the processor |
|
@param[in] state If non-zero stall the processor. */ |
/*---------------------------------------------------------------------------*/ |
static void |
set_stall_state (int state) |
{ |
|
if(state == 0) |
{ |
err = dbg_cpu0_write_ctrl(0, 0); /* unstall or1k */ |
stallState = UNSTALLED; |
npcIsCached = 0; |
rsp.sigval = TARGET_SIGNAL_NONE; |
} |
else |
{ |
err = dbg_cpu0_write_ctrl(0, 0x01); /* stall or1k */ |
stallState = STALLED; |
} |
|
if(err > 0 && DEBUG_GDB)printf("Error %d in set_stall_state Stall state = %d\n", err, state); |
|
} /* set_stall_state () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Set the reset bit of the processor's control reg in debug interface |
*/ |
/*---------------------------------------------------------------------------*/ |
static void |
reset_or1k (void) |
{ |
|
err = dbg_cpu0_write_ctrl(0, 0x02); /* reset or1k */ |
|
if(err > 0 && DEBUG_GDB)printf("Error %d in reset_or1k()\n", err); |
|
} /* reset_or1k () */ |
|
/*---------------------------------------------------------------------------*/ |
/*!Close down the connection with GDB in the event of a kill signal |
|
*/ |
/*---------------------------------------------------------------------------*/ |
void gdb_close() |
{ |
rsp_client_close(); |
client_close('0'); |
// Maybe do other things here! |
} |
/verilog/vpi/verilog/vpi_debug_defines.v
0,0 → 1,216
////////////////////////////////////////////////////////////////////// |
//// //// |
//// ORPSoC Testbench //// |
//// //// |
//// Description //// |
//// ORPSoC VPI Debugging Testbench defines file //// |
//// //// |
//// To Do: //// |
//// //// |
//// //// |
//// Author(s): //// |
//// - jb, jb@orsoc.se //// |
//// //// |
//// //// |
////////////////////////////////////////////////////////////////////// |
//// //// |
//// Copyright (C) 2009 Authors and OPENCORES.ORG //// |
//// //// |
//// This source file may be used and distributed without //// |
//// restriction provided that this copyright statement is not //// |
//// removed from the file and that any derivative work contains //// |
//// the original copyright notice and the associated disclaimer. //// |
//// //// |
//// This source file is free software; you can redistribute it //// |
//// and/or modify it under the terms of the GNU Lesser General //// |
//// Public License as published by the Free Software Foundation; //// |
//// either version 2.1 of the License, or (at your option) any //// |
//// later version. //// |
//// //// |
//// This source 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 Lesser General Public License for more //// |
//// details. //// |
//// //// |
//// You should have received a copy of the GNU Lesser General //// |
//// Public License along with this source; if not, download it //// |
//// from http://www.opencores.org/lgpl.shtml //// |
//// //// |
////////////////////////////////////////////////////////////////////// |
`timescale 1ns/10ps |
// Defines from the following files: |
// tap_defines.v |
|
// Define IDCODE Value |
`define IDCODE_VALUE 32'h14951185 |
|
// Length of the Instruction register |
`define IR_LENGTH 4 |
|
// Supported Instructions |
`define EXTEST 4'b0000 |
`define SAMPLE_PRELOAD 4'b0001 |
`define IDCODE 4'b0010 |
`define DEBUG 4'b1000 |
`define MBIST 4'b1001 |
`define BYPASS 4'b1111 |
|
// Number of cells in boundary scan chain |
`define BS_CELL_NB 32'd558 |
|
//dbg_defines.v |
|
// Length of the MODULE ID register |
`define DBG_TOP_MODULE_ID_LENGTH 4 |
|
// Length of data |
`define DBG_TOP_MODULE_DATA_LEN `DBG_TOP_MODULE_ID_LENGTH + 1 |
`define DBG_TOP_DATA_CNT 3 |
|
// Length of status |
`define DBG_TOP_STATUS_LEN 3'd4 |
`define DBG_TOP_STATUS_CNT_WIDTH 3 |
|
// Length of the CRC |
`define DBG_TOP_CRC_LEN 32 |
`define DBG_TOP_CRC_CNT 6 |
|
// Chains |
`define DBG_TOP_WISHBONE_DEBUG_MODULE 4'h0 |
`define DBG_TOP_CPU0_DEBUG_MODULE 4'h1 |
`define DBG_TOP_CPU1_DEBUG_MODULE 4'h2 |
|
// dbg_wb_defines.v |
|
// If WISHBONE sub-module is supported uncomment the folowing line |
`define DBG_WISHBONE_SUPPORTED |
|
// If CPU_0 sub-module is supported uncomment the folowing line |
`define DBG_CPU0_SUPPORTED |
|
// If CPU_1 sub-module is supported uncomment the folowing line |
//`define DBG_CPU1_SUPPORTED |
|
// If more debug info is needed, uncomment the follofing line |
//`define DBG_MORE_INFO |
|
|
// Defining length of the command |
`define DBG_WB_CMD_LEN 3'd4 |
`define DBG_WB_CMD_CNT_WIDTH 3 |
|
// Defining length of the access_type field |
`define DBG_WB_ACC_TYPE_LEN 3'd4 |
|
// Defining length of the address |
`define DBG_WB_ADR_LEN 6'd32 |
|
// Defining length of the length register |
`define DBG_WB_LEN_LEN 5'd16 |
|
// Defining total length of the DR needed |
`define DBG_WB_DR_LEN (`DBG_WB_ACC_TYPE_LEN + `DBG_WB_ADR_LEN + `DBG_WB_LEN_LEN) |
|
// Defining length of the CRC |
`define DBG_WB_CRC_LEN 6'd32 |
`define DBG_WB_CRC_CNT_WIDTH 6 |
|
// Defining length of status |
`define DBG_WB_STATUS_LEN 3'd4 |
`define DBG_WB_STATUS_CNT_WIDTH 3 |
|
// Defining length of the data |
`define DBG_WB_DATA_CNT_WIDTH (`DBG_WB_LEN_LEN + 3) |
`define DBG_WB_DATA_CNT_LIM_WIDTH `DBG_WB_LEN_LEN |
|
//Defining commands |
`define DBG_WB_GO 4'h0 |
`define DBG_WB_RD_COMM 4'h1 |
`define DBG_WB_WR_COMM 4'h2 |
|
// Defining access types for wishbone |
`define DBG_WB_WRITE8 4'h0 |
`define DBG_WB_WRITE16 4'h1 |
`define DBG_WB_WRITE32 4'h2 |
`define DBG_WB_READ8 4'h4 |
`define DBG_WB_READ16 4'h5 |
`define DBG_WB_READ32 4'h6 |
|
// dbg_cpu_defines.v |
|
|
// Defining length of the command |
`define DBG_CPU_CMD_LEN 3'd4 |
`define DBG_CPU_CMD_CNT_WIDTH 3 |
|
// Defining length of the access_type field |
`define DBG_CPU_ACC_TYPE_LEN 3'd4 |
|
// Defining length of the address |
`define DBG_CPU_ADR_LEN 6'd32 |
|
// Defining length of the length register |
`define DBG_CPU_LEN_LEN 5'd16 |
|
// Defining total length of the DR needed |
//define DBG_CPU_DR_LEN (`DBG_CPU_ACC_TYPE_LEN + `DBG_CPU_ADR_LEN + `DBG_CPU_LEN_LEN) |
`define DBG_CPU_DR_LEN 52 |
// Defining length of the CRC |
`define DBG_CPU_CRC_LEN 6'd32 |
`define DBG_CPU_CRC_CNT_WIDTH 6 |
|
// Defining length of status |
`define DBG_CPU_STATUS_LEN 3'd4 |
`define DBG_CPU_STATUS_CNT_WIDTH 3 |
|
// Defining length of the data |
//define DBG_CPU_DATA_CNT_WIDTH `DBG_CPU_LEN_LEN + 3 |
`define DBG_CPU_DATA_CNT_WIDTH 19 |
//define DBG_CPU_DATA_CNT_LIM_WIDTH `DBG_CPU_LEN_LEN |
`define DBG_CPU_DATA_CNT_LIM_WIDTH 16 |
// Defining length of the control register |
`define DBG_CPU_CTRL_LEN 2 |
|
//Defining commands |
`define DBG_CPU_GO 4'h0 |
`define DBG_CPU_RD_COMM 4'h1 |
`define DBG_CPU_WR_COMM 4'h2 |
`define DBG_CPU_RD_CTRL 4'h3 |
`define DBG_CPU_WR_CTRL 4'h4 |
|
// Defining access types for wishbone |
`define DBG_CPU_WRITE 4'h2 |
`define DBG_CPU_READ 4'h6 |
|
|
// commands from jp_vpi |
`define CMD_JTAG_SET_IR 4'h1 |
`define CMD_SET_DEBUG_CHAIN 4'h2 |
`define CMD_CPU_CTRL_WR 4'h3 |
`define CMD_CPU_CTRL_RD 4'h4 |
`define CMD_CPU_WR_REG 4'h5 |
`define CMD_CPU_RD_REG 4'h6 |
`define CMD_WB_WR32 4'h7 |
`define CMD_WB_RD32 4'h8 |
`define CMD_WB_BLOCK_WR32 4'h9 |
`define CMD_WB_BLOCK_RD32 4'ha |
`define CMD_RESET 4'hb |
`define CMD_READ_JTAG_ID 4'hc |
`define CMD_GDB_DETACH 4'hd |
|
// commands: |
// 4'h1 jtag set instruction register (input: instruction value) |
// 4'h2 set debug chain (dbg_set_command here) (input: chain value) |
// 4'h3 cpu_ctrl_wr (input: ctrl value (2 bits)) |
// 4'h4 cpu_ctrl_rd (output: ctrl value (2bits)) |
// 4'h5 cpu wr reg (inputs: address, data) |
// 4'h6 cpu rd reg (input: address; output: data) |
// 4'h7 wb wr 32 (inputs: address, data) |
// 4'h8 wb rd 32 (input: address; output: data) |
// 4'h9 wb wr block 32 (inputs: address, length, data) |
// 4'ha wb rd block 32 (inputs: address, length; output: data) |
// 4'hb reset |
// 4'hc read jtag id (output: data) |
|
`define SDRAM_BASE_ADDRESS 32'h00000000 |
/verilog/vpi/verilog/vpi_debug_module.v
0,0 → 1,1613
////////////////////////////////////////////////////////////////////// |
//// //// |
//// ORPSoC Testbench //// |
//// //// |
//// Description //// |
//// ORPSoC VPI Debugging Testbench file //// |
//// //// |
//// To Do: //// |
//// //// |
//// //// |
//// Author(s): //// |
//// - jb, jb@orsoc.se //// |
//// //// |
//// //// |
////////////////////////////////////////////////////////////////////// |
//// //// |
//// Copyright (C) 2009 Authors and OPENCORES.ORG //// |
//// //// |
//// This source file may be used and distributed without //// |
//// restriction provided that this copyright statement is not //// |
//// removed from the file and that any derivative work contains //// |
//// the original copyright notice and the associated disclaimer. //// |
//// //// |
//// This source file is free software; you can redistribute it //// |
//// and/or modify it under the terms of the GNU Lesser General //// |
//// Public License as published by the Free Software Foundation; //// |
//// either version 2.1 of the License, or (at your option) any //// |
//// later version. //// |
//// //// |
//// This source 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 Lesser General Public License for more //// |
//// details. //// |
//// //// |
//// You should have received a copy of the GNU Lesser General //// |
//// Public License along with this source; if not, download it //// |
//// from http://www.opencores.org/lgpl.shtml //// |
//// //// |
////////////////////////////////////////////////////////////////////// |
`timescale 1ns/10ps |
|
`include "vpi_debug_defines.v" |
|
// uncomment the following line to get more debug output for this module |
//`define DEBUG_INFO |
//`define VPI_DEBUG_INFO |
|
module vpi_debug_module(tms, tck, tdi, tdo); |
|
output tms; |
output tck; |
output tdi; |
input tdo; |
|
reg tms; |
reg tck; |
reg tdi; |
|
reg [31:0] in_data_le, in_data_be; |
reg err; |
integer i; |
|
reg [31:0] id; |
reg [31:0] npc; |
reg [31:0] ppc; |
reg [31:0] r1; |
reg [31:0] insn; |
reg [31:0] result; |
reg [31:0] tmp; |
|
reg [31:0] crc_out; |
reg [31:0] crc_in; |
wire crc_match_in; |
|
reg [`DBG_TOP_STATUS_LEN -1:0] status; |
reg [`DBG_WB_STATUS_LEN -1:0] status_wb; |
reg [`DBG_CPU_STATUS_LEN -1:0] status_cpu; |
|
// Text used for easier debugging |
reg [199:0] test_text; |
reg [`DBG_WB_CMD_LEN -1:0] last_wb_cmd; |
reg [`DBG_CPU_CMD_LEN -1:0] last_cpu_cmd; |
reg [199:0] last_wb_cmd_text; |
reg [199:0] last_cpu_cmd_text; |
|
reg [31:0] data_storage [0:4095]; // Data storage (for write and read operations). |
reg [18:0] length_global; |
|
parameter Tp = 1; |
//parameter Tck = 25; // Clock half period (Clok period = 50 ns => 20 MHz) |
parameter Tck = 50; // Clock half period (Clok period = 100 ns => 10 MHz) |
|
integer cmd; |
integer block_cmd_length; |
integer jtag_instn_val; |
integer set_chain_val; |
|
reg [1:0] cpu_ctrl_val; // two important bits for the ctrl reg |
reg [31:0] cmd_adr; |
reg [31:0] cmd_data; |
|
|
initial |
begin |
$display("JTAG debug module with VPI interface enabled\n"); |
tck <=#Tp 1'b0; |
tdi <=#Tp 1'bz; |
tms <=#Tp 1'b0; |
|
// Insert a #delay here because we need to |
// wait until the PC isn't pointing to flash anymore |
// (this is around 20k ns if the flash_crash boot code |
// is being booted from, else much bigger, around 10mil ns) |
|
#200_000 main; |
|
end |
|
task main; |
begin |
|
id <=#Tp 32'h00; |
npc <=#Tp 32'h00; |
ppc <=#Tp 32'h00; |
insn <=#Tp 32'h00; |
result <=#Tp 32'h00; |
err <=#Tp 1'b0; |
tmp <=#Tp 32'h00; |
|
// execute some cycles |
#50000; |
|
reset_tap; |
goto_run_test_idle; |
|
//$init_rsp_server(); |
|
while (1) begin |
|
// Check for incoming command |
|
// wait until a command is sent |
// poll with a delay here |
cmd = -1; |
|
while (cmd == -1) |
begin |
#1000 $check_for_command(cmd); |
end |
|
// now switch on the command |
case (cmd) |
|
`CMD_RESET : // reset |
begin |
|
// call reset task |
reset_tap; |
// and put TAP into run_test_idle state |
goto_run_test_idle; |
|
end |
|
`CMD_JTAG_SET_IR : // set jtag instruction register |
begin |
|
$get_command_data(jtag_instn_val); |
|
set_instruction(jtag_instn_val); |
|
end |
`CMD_SET_DEBUG_CHAIN : // set debug chain |
begin |
|
$get_command_data(set_chain_val); |
|
module_select(set_chain_val, 1'b0); // {chain, gen_crc_err} |
|
end |
`CMD_CPU_CTRL_WR : // cpu CTRL write |
begin |
|
$get_command_data(cpu_ctrl_val); |
|
debug_cpu_wr_ctrl(cpu_ctrl_val, ""); |
|
end |
|
`CMD_CPU_CTRL_RD : // cpu CTRL read |
begin |
|
debug_cpu_rd_ctrl(cpu_ctrl_val); |
|
$return_command_data(cpu_ctrl_val); |
|
end |
|
`CMD_CPU_WR_REG : |
begin |
|
$get_command_address(cmd_adr); |
|
$get_command_data(cmd_data); |
|
`ifdef VPI_DEBUG_INFO |
$display("CPU reg write. adr: 0x%x (reg group: %d reg#: %d), val: 0x%x", |
cmd_adr,cmd_adr[15:11], cmd_adr[10:0], cmd_data); |
`endif |
|
cpu_write_32(cmd_data, cmd_adr,16'h3); |
|
end |
|
`CMD_CPU_RD_REG : |
begin |
|
$get_command_address(cmd_adr); |
|
cpu_read_32(cmd_data, cmd_adr, 16'h3); |
|
`ifdef VPI_DEBUG_INFO |
$display("CPU reg read. adr: 0x%x (reg group: %d reg#: %d), val: 0x%x", |
cmd_adr,cmd_adr[15:11], cmd_adr[10:0], cmd_data); |
`endif |
|
$return_command_data(cmd_data); |
|
end |
|
`CMD_WB_WR32 : |
begin |
|
$get_command_address(cmd_adr); |
|
$get_command_data(cmd_data); |
|
wb_write_32(cmd_data, cmd_adr, 16'h3); |
|
end |
|
`CMD_WB_RD32 : |
begin |
|
$get_command_address(cmd_adr); |
|
wb_read_32(cmd_data, cmd_adr, 16'h3); |
|
$return_command_data(cmd_data); |
|
end |
|
`CMD_WB_BLOCK_WR32 : |
begin |
|
$get_command_address(cmd_adr); |
|
$get_command_data(block_cmd_length); |
|
$get_command_block_data(block_cmd_length, data_storage); |
|
wb_block_write_32(cmd_adr ,block_cmd_length); |
|
end |
|
`CMD_WB_BLOCK_RD32 : |
begin |
|
$get_command_address(cmd_adr); |
|
$get_command_data(block_cmd_length); |
|
wb_block_read_32(cmd_adr, block_cmd_length); |
|
$return_command_block_data(block_cmd_length, data_storage); |
|
end |
|
`CMD_READ_JTAG_ID : |
begin |
|
read_id_code(id); |
|
$return_command_data(id); |
|
end |
|
`CMD_GDB_DETACH : |
begin |
|
$display("Debugging client disconnected. Finishing simulation"); |
|
|
$finish(); |
|
end |
|
default: |
begin |
$display("Somehow got to the default case in the command case statement."); |
$display("Command was: %x", cmd); |
$display("Exiting..."); |
|
$finish();//shouldn't be here |
end |
|
endcase // case (cmd) |
|
// send back response, which is currently nothing |
// but could be used to signal something |
$return_response(); |
|
end // while (1) |
end |
|
endtask // main |
|
|
|
// Receiving data and calculating input crc |
always @(posedge tck) |
begin |
in_data_be[31:1] <= #1 in_data_be[30:0]; |
in_data_be[0] <= #1 tdo; |
|
in_data_le[31] <= #1 tdo; |
in_data_le[30:0] <= #1 in_data_le[31:1]; |
end |
|
// Generation of the TCK signal |
task gen_clk; |
input [31:0] number; |
integer i; |
begin |
for(i=0; i<number; i=i+1) |
begin |
#Tck tck<=1; |
#Tck tck<=0; |
end |
end |
endtask |
|
// TAP reset |
task reset_tap; |
|
begin |
`ifdef DEBUG_INFO |
$display("(%0t) Task reset_tap", $time); |
`endif |
tms<=#1 1'b1; |
gen_clk(5); |
end |
endtask |
|
|
// Goes to RunTestIdle state |
task goto_run_test_idle; |
begin |
`ifdef DEBUG_INFO |
$display("(%0t) Task goto_run_test_idle", $time); |
`endif |
tms<=#1 1'b0; |
gen_clk(1); |
end |
endtask |
|
// sets the instruction to the IR register and goes to the RunTestIdle state |
task set_instruction; |
input [3:0] instr; |
integer i; |
|
begin |
`ifdef DEBUG_INFO |
case (instr) |
`EXTEST : $display("(%0t) Task set_instruction (EXTEST)", $time); |
`SAMPLE_PRELOAD : $display("(%0t) Task set_instruction (SAMPLE_PRELOAD)", $time); |
`IDCODE : $display("(%0t) Task set_instruction (IDCODE)", $time); |
`DEBUG : $display("(%0t) Task set_instruction (DEBUG)", $time); |
`MBIST : $display("(%0t) Task set_instruction (MBIST)", $time); |
`BYPASS : $display("(%0t) Task set_instruction (BYPASS)", $time); |
default |
begin |
$display("(%0t) Task set_instruction (Unsupported instruction !!!)", $time); |
$display("\tERROR: Unsupported instruction !!!", $time); |
$stop; |
end |
endcase |
`endif |
|
tms<=#1 1; |
gen_clk(2); |
tms<=#1 0; |
gen_clk(2); // we are in shiftIR |
|
for(i=0; i<`IR_LENGTH-1; i=i+1) |
begin |
tdi<=#1 instr[i]; |
gen_clk(1); |
end |
|
tdi<=#1 instr[i]; // last shift |
tms<=#1 1; // going out of shiftIR |
gen_clk(1); |
tdi<=#1 'hz; // tri-state |
gen_clk(1); |
tms<=#1 0; |
gen_clk(1); // we are in RunTestIdle |
end |
endtask |
|
|
// send 32 bits through the device |
task test_bypass; |
input [31:0] in; |
output [31:0] out; |
integer i; |
|
reg [31:0] out; |
|
begin |
tms<=#Tp 1; |
gen_clk(1); |
tms<=#Tp 0; |
gen_clk(2); // we are in shiftDR |
|
for(i=31; i>=0; i=i-1) |
begin |
tdi<=#Tp in[i]; |
gen_clk(1); |
end |
|
tms<=#Tp 1; // going out of shiftDR |
gen_clk(1); |
|
out <=#Tp in_data_be; |
tdi<=#Tp 'hz; |
|
gen_clk(1); |
tms<=#Tp 0; |
gen_clk(1); // we are in RunTestIdle |
end |
endtask |
|
// Reads the ID code |
task read_id_code; |
output [31:0] code; |
reg [31:0] code; |
begin |
`ifdef DEBUG_INFO |
$display("(%0t) Task read_id_code", $time); |
`endif |
|
tms<=#1 1; |
gen_clk(1); |
tms<=#1 0; |
gen_clk(2); // we are in shiftDR |
|
tdi<=#1 0; |
gen_clk(31); |
|
tms<=#1 1; // going out of shiftIR |
gen_clk(1); |
|
code = in_data_le; |
|
tdi<=#1 'hz; // tri-state |
gen_clk(1); |
tms<=#1 0; |
gen_clk(1); // we are in RunTestIdle |
end |
endtask |
|
// test bundary scan chain |
task test_bs; |
input [31:0] in; |
output [31:0] out; |
integer i; |
|
reg [31:0] out; |
|
begin |
tms<=#Tp 1; |
gen_clk(1); |
tms<=#Tp 0; |
gen_clk(2); // we are in shiftDR |
|
for(i=31; i>=0; i=i-1) |
begin |
tdi<=#Tp in[i]; |
gen_clk(1); |
end |
|
gen_clk(`BS_CELL_NB-1); |
tms<=#Tp 1; // going out of shiftDR |
gen_clk(1); |
|
out <=#Tp in_data_be; |
|
gen_clk(1); |
tms<=#Tp 0; |
gen_clk(1); // we are in RunTestIdle |
end |
endtask |
|
|
|
// sets the selected scan chain and goes to the RunTestIdle state |
task module_select; |
input [`DBG_TOP_MODULE_ID_LENGTH -1:0] data; |
input gen_crc_err; |
integer i; |
|
begin |
`ifdef DEBUG_INFO |
case (data) |
`DBG_TOP_CPU1_DEBUG_MODULE : $display("(%0t) Task module_select (DBG_TOP_CPU1_DEBUG_MODULE, gen_crc_err=%0d)", $time, gen_crc_err); |
`DBG_TOP_CPU0_DEBUG_MODULE : $display("(%0t) Task module_select (DBG_TOP_CPU0_DEBUG_MODULE, gen_crc_err=%0d)", $time, gen_crc_err); |
`DBG_TOP_WISHBONE_DEBUG_MODULE : $display("(%0t) Task module_select (DBG_TOP_WISHBONE_DEBUG_MODULE gen_crc_err=%0d)", $time, gen_crc_err); |
default : $display("(%0t) Task module_select (ERROR!!! Unknown module selected)", $time); |
endcase |
`endif |
|
tms<=#1 1'b1; |
gen_clk(1); |
tms<=#1 1'b0; |
gen_clk(2); // we are in shiftDR |
|
status = {`DBG_TOP_STATUS_LEN{1'b0}}; // Initialize status to all 0's |
crc_out = {`DBG_TOP_CRC_LEN{1'b1}}; // Initialize outgoing CRC to all ff |
tdi<=#1 1'b1; // module_select bit |
calculate_crc(1'b1); |
gen_clk(1); |
|
for(i=`DBG_TOP_MODULE_ID_LENGTH -1; i>=0; i=i-1) // Shifting module ID |
begin |
tdi<=#1 data[i]; |
calculate_crc(data[i]); |
gen_clk(1); |
end |
|
for(i=`DBG_TOP_CRC_LEN -1; i>=0; i=i-1) |
begin |
if (gen_crc_err & (i==0)) // Generate crc error at last crc bit |
tdi<=#1 ~crc_out[i]; // error crc |
else |
tdi<=#1 crc_out[i]; // ok crc |
|
gen_clk(1); |
end |
|
tdi<=#1 1'hz; // tri-state |
|
crc_in = {`DBG_TOP_CRC_LEN{1'b1}}; // Initialize incoming CRC to all ff |
|
for(i=`DBG_TOP_STATUS_LEN -1; i>=0; i=i-1) |
begin |
gen_clk(1); // Generating 1 clock to read out a status bit. |
status[i] = tdo; |
end |
|
for(i=0; i<`DBG_TOP_CRC_LEN -1; i=i+1) |
gen_clk(1); |
|
tms<=#1 1'b1; |
gen_clk(1); // to exit1_dr |
|
if (~crc_match_in) |
begin |
$display("(%0t) Incoming CRC failed !!!", $time); |
`ifdef DEBUG_INFO |
$stop; |
`endif |
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to update_dr |
tms<=#1 1'b0; |
gen_clk(1); // to run_test_idle |
|
if (|status) |
begin |
$write("(*E) (%0t) Module select error: ", $time); |
casex (status) |
4'b1xxx : $display("CRC error !!!\n\n", $time); |
4'bx1xx : $display("Non-existing module selected !!!\n\n", $time); |
4'bxx1x : $display("Status[1] should be 1'b0 !!!\n\n", $time); |
4'bxxx1 : $display("Status[0] should be 1'b0 !!!\n\n", $time); |
endcase |
`ifdef DEBUG_INFO |
$stop; |
`endif |
end |
end |
endtask // module_select |
|
|
|
// 32-bit write to the wishbone |
task wb_write_32; |
input [31:0] data; |
input [`DBG_WB_ADR_LEN -1:0] addr; |
input [`DBG_WB_LEN_LEN -1:0] length; |
|
begin |
data_storage[0] = data; |
debug_wishbone_wr_comm(`DBG_WB_WRITE32, addr, length, 1'b0); |
last_wb_cmd = `DBG_WB_WRITE32; last_wb_cmd_text = "DBG_WB_WRITE32"; |
length_global = length + 1; |
debug_wishbone_go(1'b0, 1'b0); |
//debug_wishbone_go(1'b1, 1'b0); // maybe causes underrun/overrun error when wait for WB ready? |
if (length>3) |
$display("WARNING: Only first data word is stored for writting ( See module %m)"); |
end |
endtask |
|
// block 32-bit write to the wishbone |
// presumes data is already in data_storage[] |
task wb_block_write_32; |
|
input [`DBG_WB_ADR_LEN -1:0] addr; |
input [`DBG_WB_LEN_LEN -1:0] length; |
|
begin |
|
debug_wishbone_wr_comm(`DBG_WB_WRITE32, addr, length-1, 1'b0); |
|
last_wb_cmd = `DBG_WB_WRITE32; last_wb_cmd_text = "DBG_WB_WRITE32"; |
|
length_global = length; // number of bytes! |
|
debug_wishbone_go(1'b0, 1'b0); |
|
//debug_wishbone_go(1'b1, 1'b0); // maybe causes underrun/overrun error when wait for WB ready? |
|
end |
endtask |
|
// 32-bit read from the wishbone |
task wb_read_32; |
|
output [31:0] data; |
|
input [`DBG_WB_ADR_LEN -1:0] addr; |
input [`DBG_WB_LEN_LEN -1:0] length; |
|
begin |
debug_wishbone_wr_comm(`DBG_WB_READ32, addr, length, 1'b0); |
last_wb_cmd = `DBG_WB_READ32; last_wb_cmd_text = "DBG_WB_READ32"; |
length_global = length + 1; |
//debug_wishbone_go(1'b0, 1'b0); |
debug_wishbone_go(1'b1, 1'b0); |
data = data_storage[0]; |
if (length>3) |
$display("WARNING: Only first data word is stored for writting ( See module %m)"); |
end |
endtask |
|
|
// block 32-bit read from the wishbone |
// assumes data will be stored into data_storage[] |
task wb_block_read_32; |
|
input [`DBG_WB_ADR_LEN -1:0] addr; |
input [`DBG_WB_LEN_LEN -1:0] length; |
|
begin |
debug_wishbone_wr_comm(`DBG_WB_READ32, addr, length-1, 1'b0); |
|
last_wb_cmd = `DBG_WB_READ32; last_wb_cmd_text = "DBG_WB_READ32"; |
|
length_global = length; |
|
//debug_wishbone_go(1'b0, 1'b0); |
debug_wishbone_go(1'b1, 1'b0); |
|
end |
endtask |
|
|
// 16-bit write to the wishbone |
task wb_write_16; |
input [15:0] data; |
input [`DBG_WB_ADR_LEN -1:0] addr; |
input [`DBG_WB_LEN_LEN -1:0] length; |
|
begin |
data_storage[0] = {data, 16'h0}; |
debug_wishbone_wr_comm(`DBG_WB_WRITE16, addr, length, 1'b0); |
last_wb_cmd = `DBG_WB_WRITE16; last_wb_cmd_text = "DBG_WB_WRITE16"; |
length_global = length + 1; |
debug_wishbone_go(1'b0, 1'b0); |
if (length>1) |
$display("WARNING: Only first data half is stored for writting ( See module %m)"); |
end |
endtask |
|
|
|
// 8-bit write to the wishbone |
task wb_write_8; |
input [7:0] data; |
input [`DBG_WB_ADR_LEN -1:0] addr; |
input [`DBG_WB_LEN_LEN -1:0] length; |
|
begin |
data_storage[0] = {data, 24'h0}; |
debug_wishbone_wr_comm(`DBG_WB_WRITE8, addr, length, 1'b0); |
last_wb_cmd = `DBG_WB_WRITE8; last_wb_cmd_text = "DBG_WB_WRITE8"; |
length_global = length + 1; |
debug_wishbone_go(1'b0, 1'b0); |
if (length>0) |
$display("WARNING: Only first data byte is stored for writting ( See module %m)"); |
end |
endtask |
|
|
|
task debug_wishbone_wr_comm; |
input [`DBG_WB_ACC_TYPE_LEN -1:0] acc_type; |
input [`DBG_WB_ADR_LEN -1:0] addr; |
input [`DBG_WB_LEN_LEN -1:0] length; |
input gen_crc_err; |
integer i; |
reg [`DBG_WB_CMD_LEN -1:0] command; |
|
begin |
`ifdef DEBUG_INFO |
$display("(%0t) Task debug_wishbone_wr_comm: ", $time); |
`endif |
|
command = `DBG_WB_WR_COMM; |
tms<=#1 1'b1; |
gen_clk(1); |
tms<=#1 1'b0; |
gen_clk(2); // we are in shiftDR |
|
crc_out = {`DBG_WB_CRC_LEN{1'b1}}; // Initialize outgoing CRC to all ff |
|
tdi<=#1 1'b0; // module_select bit = 0 |
calculate_crc(1'b0); |
gen_clk(1); |
|
for(i=`DBG_WB_CMD_LEN -1; i>=0; i=i-1) |
begin |
tdi<=#1 command[i]; // command |
calculate_crc(command[i]); |
gen_clk(1); |
end |
|
for(i=`DBG_WB_ACC_TYPE_LEN -1; i>=0; i=i-1) |
begin |
tdi<=#1 acc_type[i]; // command |
calculate_crc(acc_type[i]); |
gen_clk(1); |
end |
|
for(i=`DBG_WB_ADR_LEN -1; i>=0; i=i-1) // address |
begin |
tdi<=#1 addr[i]; |
calculate_crc(addr[i]); |
gen_clk(1); |
end |
|
for(i=`DBG_WB_LEN_LEN -1; i>=0; i=i-1) // length |
begin |
tdi<=#1 length[i]; |
calculate_crc(length[i]); |
gen_clk(1); |
end |
|
for(i=`DBG_WB_CRC_LEN -1; i>=0; i=i-1) |
begin |
if (gen_crc_err & (i==0)) // Generate crc error at last crc bit |
tdi<=#1 ~crc_out[i]; // error crc |
else |
tdi<=#1 crc_out[i]; // ok crc |
|
gen_clk(1); |
end |
|
tdi<=#1 1'hz; |
|
crc_in = {`DBG_WB_CRC_LEN{1'b1}}; // Initialize incoming CRC to all ff |
|
for(i=`DBG_WB_STATUS_LEN -1; i>=0; i=i-1) |
begin |
gen_clk(1); // Generating clock to read out a status bit. |
status_wb[i] = tdo; |
end |
|
if (|status_wb) |
begin |
$write("(*E) (%0t) debug_wishbone_wr_comm error: ", $time); |
casex (status_wb) |
4'b1xxx : $display("CRC error !!!\n\n", $time); |
4'bx1xx : $display("Unknown command !!!\n\n", $time); |
4'bxx1x : $display("WISHBONE error !!!\n\n", $time); |
4'bxxx1 : $display("Overrun/Underrun !!!\n\n", $time); |
endcase |
`ifdef DEBUG_INFO |
$stop; |
`endif |
end |
|
|
for(i=0; i<`DBG_WB_CRC_LEN -1; i=i+1) // Getting in the CRC |
begin |
gen_clk(1); |
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to exit1_dr |
|
if (~crc_match_in) |
begin |
$display("(%0t) Incoming CRC failed !!!", $time); |
`ifdef DEBUG_INFO |
$stop; |
`endif |
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to update_dr |
tms<=#1 1'b0; |
gen_clk(1); // to run_test_idle |
end |
endtask // debug_wishbone_wr_comm |
|
|
|
task debug_wishbone_go; |
|
input wait_for_wb_ready; |
input gen_crc_err; |
integer i; |
reg [4:0] bit_pointer; |
integer word_pointer; |
reg [31:0] tmp_data; |
reg [`DBG_WB_CMD_LEN -1:0] command; |
|
|
begin |
|
`ifdef DEBUG_INFO |
$display("(%0t) Task debug_wishbone_go (previous command was %0s): ", $time, last_wb_cmd_text); |
`endif |
|
command = `DBG_WB_GO; |
word_pointer = 0; |
|
tms<=#1 1'b1; |
gen_clk(1); |
tms<=#1 1'b0; |
gen_clk(2); // we are in shiftDR |
|
crc_out = {`DBG_WB_CRC_LEN{1'b1}}; // Initialize outgoing CRC to all ff |
|
tdi<=#1 1'b0; // module_select bit = 0 |
calculate_crc(1'b0); |
gen_clk(1); |
|
for(i=`DBG_WB_CMD_LEN -1; i>=0; i=i-1) |
begin |
tdi<=#1 command[i]; // command |
calculate_crc(command[i]); |
gen_clk(1); |
end |
|
// W R I T E |
if ( |
(last_wb_cmd == `DBG_WB_WRITE8) | (last_wb_cmd == `DBG_WB_WRITE16) | |
(last_wb_cmd == `DBG_WB_WRITE32) |
) // When WB_WRITEx was previously activated, data needs to be shifted. |
begin |
for (i=0; i<((length_global) << 3); i=i+1) |
begin |
|
if ((!(i%32)) && (i>0)) |
begin |
word_pointer = word_pointer + 1; |
end |
|
tmp_data = data_storage[word_pointer]; |
|
bit_pointer = 31-i[4:0]; |
|
tdi<=#1 tmp_data[bit_pointer]; |
|
calculate_crc(tmp_data[bit_pointer]); |
|
gen_clk(1); |
|
end |
end |
|
for(i=`DBG_WB_CRC_LEN -1; i>=1; i=i-1) |
begin |
tdi<=#1 crc_out[i]; |
gen_clk(1); |
end |
|
if (gen_crc_err) // Generate crc error at last crc bit |
tdi<=#1 ~crc_out[0]; // error crc |
else |
tdi<=#1 crc_out[0]; // ok crc |
|
if (wait_for_wb_ready) |
begin |
tms<=#1 1'b1; |
gen_clk(1); // to exit1_dr. Last CRC is shifted on this clk |
tms<=#1 1'b0; |
gen_clk(1); // to pause_dr |
|
#2; // wait a bit for tdo to activate |
while (tdo) // waiting for wb to send "ready" |
begin |
gen_clk(1); // staying in pause_dr |
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to exit2_dr |
tms<=#1 1'b0; |
gen_clk(1); // to shift_dr |
end |
else |
begin |
gen_clk(1); // Last CRC is shifted on this clk |
end |
|
|
tdi<=#1 1'hz; |
|
// R E A D |
|
crc_in = {`DBG_WB_CRC_LEN{1'b1}}; // Initialize incoming CRC to all ff |
|
if ( |
(last_wb_cmd == `DBG_WB_READ8) | (last_wb_cmd == `DBG_WB_READ16) | |
(last_wb_cmd == `DBG_WB_READ32) |
) // When WB_READx was previously activated, data needs to be shifted. |
begin |
|
`ifdef DEBUG_INFO |
$display("\t\tGenerating %0d clocks to read %0d data bytes.", length_global<<3, length_global); |
`endif |
word_pointer = 0; // Reset pointer |
|
for (i=0; i<(length_global<<3); i=i+1) |
begin |
|
gen_clk(1); |
|
if (i[4:0] == 31) // Latching data |
begin |
|
data_storage[word_pointer] = in_data_be; |
`ifdef DEBUG_INFO |
$display("\t\tin_data_be = 0x%x", in_data_be); |
`endif |
word_pointer = word_pointer + 1; |
|
end |
end |
end |
|
for(i=`DBG_WB_STATUS_LEN -1; i>=0; i=i-1) |
begin |
|
gen_clk(1); // Generating clock to read out a status bit. |
status_wb[i] = tdo; |
|
end |
|
if (|status_wb) |
begin |
$write("(*E) (%0t) debug_wishbone_go error: ", $time); |
casex (status_wb) |
4'b1xxx : $display("CRC error !!!\n\n", $time); |
4'bx1xx : $display("Unknown command !!!\n\n", $time); |
4'bxx1x : $display("WISHBONE error !!!\n\n", $time); |
4'bxxx1 : $display("Overrun/Underrun !!!\n\n", $time); |
endcase |
`ifdef DEBUG_INFO |
$stop; |
`endif |
end |
|
|
for(i=0; i<`DBG_WB_CRC_LEN -1; i=i+1) // Getting in the CRC |
begin |
gen_clk(1); |
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to exit1_dr |
|
if (~crc_match_in) |
begin |
$display("(%0t) Incoming CRC failed !!!", $time); |
`ifdef DEBUG_INFO |
$stop; |
`endif |
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to update_dr |
tms<=#1 1'b0; |
gen_clk(1); // to run_test_idle |
end |
endtask // debug_wishbone_go |
|
|
|
task debug_cpu_wr_ctrl; |
input [`DBG_CPU_DR_LEN -1:0] data; |
input [99:0] text; |
integer i; |
reg [`DBG_CPU_CMD_LEN -1:0] command; |
|
begin |
test_text = text; |
|
`ifdef DEBUG_INFO |
$display("(%0t) Task debug_cpu_wr_ctrl (data=0x%0x (%0s))", $time, data, text); |
`endif |
|
command = `DBG_CPU_WR_CTRL; |
tms<=#1 1'b1; |
gen_clk(1); |
tms<=#1 1'b0; |
gen_clk(2); // we are in shiftDR |
|
crc_out = {`DBG_CPU_CRC_LEN{1'b1}}; // Initialize outgoing CRC to all ff |
|
tdi<=#1 1'b0; // module_select bit = 0 |
calculate_crc(1'b0); |
gen_clk(1); |
|
for(i=`DBG_CPU_CMD_LEN -1; i>=0; i=i-1) |
begin |
tdi<=#1 command[i]; // command |
calculate_crc(command[i]); |
gen_clk(1); |
end |
|
|
for(i=`DBG_CPU_CTRL_LEN -1; i>=0; i=i-1) |
begin |
tdi<=#1 data[i]; // data (used cotrol bits |
calculate_crc(data[i]); |
gen_clk(1); |
end |
|
for(i=`DBG_CPU_DR_LEN - `DBG_CPU_CTRL_LEN -1; i>=0; i=i-1) // unused control bits |
begin |
tdi<=#1 1'b0; |
calculate_crc(1'b0); |
gen_clk(1); |
end |
|
|
for(i=`DBG_CPU_CRC_LEN -1; i>=0; i=i-1) |
begin |
tdi<=#1 crc_out[i]; // ok crc |
gen_clk(1); |
end |
|
tdi<=#1 1'hz; |
|
crc_in = {`DBG_CPU_CRC_LEN{1'b1}}; // Initialize incoming CRC to all ff |
|
for(i=`DBG_CPU_STATUS_LEN -1; i>=0; i=i-1) |
begin |
gen_clk(1); // Generating clock to read out a status bit. |
status_cpu[i] = tdo; |
end |
|
if (|status_cpu) |
begin |
$write("(*E) (%0t) debug_cpu_wr_ctrl error: ", $time); |
casex (status_cpu) |
4'b1xxx : $display("CRC error !!!\n\n", $time); |
4'bx1xx : $display("??? error !!!\n\n", $time); |
4'bxx1x : $display("??? error !!!\n\n", $time); |
4'bxxx1 : $display("??? error !!!\n\n", $time); |
endcase |
`ifdef DEBUG_INFO |
$stop; |
`endif |
end |
|
|
for(i=0; i<`DBG_CPU_CRC_LEN -1; i=i+1) // Getting in the CRC |
begin |
gen_clk(1); |
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to exit1_dr |
|
if (~crc_match_in) |
begin |
$display("(%0t) Incoming CRC failed !!!", $time); |
`ifdef DEBUG_INFO |
$stop; |
`endif |
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to update_dr |
tms<=#1 1'b0; |
gen_clk(1); // to run_test_idle |
end |
endtask // debug_cpu_wr_ctrl |
|
task debug_cpu_rd_ctrl; |
output [`DBG_CPU_DR_LEN -1:0] data; |
//input [99:0] text; |
integer i; |
reg [`DBG_CPU_CMD_LEN -1:0] command; |
|
begin |
|
|
`ifdef DEBUG_INFO |
$display("(%0t) Task debug_cpu_rd_ctrl", $time); |
`endif |
|
command = `DBG_CPU_RD_CTRL; |
tms<=#1 1'b1; |
gen_clk(1); |
tms<=#1 1'b0; |
gen_clk(2); // we are in shiftDR |
|
crc_out = {`DBG_CPU_CRC_LEN{1'b1}}; // Initialize outgoing CRC to all ff |
|
tdi<=#1 1'b0; // module_select bit = 0 |
calculate_crc(1'b0); |
gen_clk(1); |
|
for(i=`DBG_CPU_CMD_LEN -1; i>=0; i=i-1) |
begin |
tdi<=#1 command[i]; // command |
calculate_crc(command[i]); |
gen_clk(1); |
end |
|
for(i=`DBG_CPU_CRC_LEN -1; i>=0; i=i-1) |
begin |
tdi<=#1 crc_out[i]; // ok crc |
gen_clk(1); |
end |
|
tdi<=#1 1'hz; |
|
crc_in = {`DBG_CPU_CRC_LEN{1'b1}}; // Initialize incoming CRC to all ff |
|
// Read incoming debug ctrl data (52 bits) |
//cpu_ctrl_val[1:0]; |
gen_clk(1); |
//cpu_ctrl_val[0] <= #1 tdo; // cpu reset bit |
data[0] <= #1 tdo; // cpu reset bit |
gen_clk(1); |
//cpu_ctrl_val[1] <= #1 tdo; // cpu stall bit |
data[1] <= #1 tdo; // cpu stall bit |
|
for(i=`DBG_CPU_DR_LEN - `DBG_CPU_CTRL_LEN -1; i>=0; i=i-1) // unused control bits |
begin |
gen_clk(1); |
end |
|
for(i=`DBG_CPU_STATUS_LEN -1; i>=0; i=i-1) |
begin |
gen_clk(1); // Generating clock to read out a status bit. |
status_cpu[i] = tdo; |
end |
|
if (|status_cpu) |
begin |
$write("(*E) (%0t) debug_cpu_wr_ctrl error: ", $time); |
casex (status_cpu) |
4'b1xxx : $display("CRC error !!!\n\n", $time); |
4'bx1xx : $display("??? error !!!\n\n", $time); |
4'bxx1x : $display("??? error !!!\n\n", $time); |
4'bxxx1 : $display("??? error !!!\n\n", $time); |
endcase // casex (status_cpu) |
|
end |
|
|
for(i=0; i<`DBG_CPU_CRC_LEN -1; i=i+1) // Getting in the CRC |
begin |
gen_clk(1); |
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to exit1_dr |
|
if (~crc_match_in) |
begin |
$display("(%0t) Incoming CRC failed !!!", $time); |
|
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to update_dr |
tms<=#1 1'b0; |
gen_clk(1); // to run_test_idle |
end |
endtask // debug_cpu_rd_ctrl |
|
|
// 32-bit read from cpu |
task cpu_read_32; |
output [31:0] data; |
input [`DBG_WB_ADR_LEN -1:0] addr; |
input [`DBG_WB_LEN_LEN -1:0] length; |
|
reg [31:0] tmp; |
|
begin |
debug_cpu_wr_comm(`DBG_CPU_READ, addr, length, 1'b0); |
|
last_cpu_cmd = `DBG_CPU_READ; last_cpu_cmd_text = "DBG_CPU_READ"; |
|
length_global = length + 1; |
|
debug_cpu_go(1'b0, 1'b0); |
|
data = data_storage[0]; |
|
if (length>3) |
$display("WARNING: Only first data word is returned( See module %m.)"); |
|
end |
endtask |
|
|
|
// 32-bit write to cpu |
task cpu_write_32; |
input [31:0] data; |
input [`DBG_WB_ADR_LEN -1:0] addr; |
input [`DBG_WB_LEN_LEN -1:0] length; |
|
reg [31:0] tmp; |
|
begin |
debug_cpu_wr_comm(`DBG_CPU_WRITE, addr, length, 1'b0); |
last_cpu_cmd = `DBG_CPU_WRITE; last_cpu_cmd_text = "DBG_CPU_WRITE"; |
length_global = length + 1; |
data_storage[0] = data; |
debug_cpu_go(1'b0, 1'b0); |
if (length>3) |
$display("WARNING: Only first data word is stored for writting ( See module %m)"); |
end |
endtask |
|
|
|
task debug_cpu_wr_comm; |
input [`DBG_CPU_ACC_TYPE_LEN -1:0] acc_type; |
input [`DBG_CPU_ADR_LEN -1:0] addr; |
input [`DBG_CPU_LEN_LEN -1:0] length; |
input gen_crc_err; |
integer i; |
reg [`DBG_CPU_CMD_LEN -1:0] command; |
|
begin |
`ifdef DEBUG_INFO |
$display("(%0t) Task debug_cpu_wr_comm: ", $time); |
`endif |
|
command = `DBG_CPU_WR_COMM; |
tms<=#1 1'b1; |
gen_clk(1); |
tms<=#1 1'b0; |
gen_clk(2); // we are in shiftDR |
|
crc_out = {`DBG_CPU_CRC_LEN{1'b1}}; // Initialize outgoing CRC to all ff |
|
tdi<=#1 1'b0; // module_select bit = 0 |
calculate_crc(1'b0); |
gen_clk(1); |
|
for(i=`DBG_CPU_CMD_LEN -1; i>=0; i=i-1) |
begin |
tdi<=#1 command[i]; // command |
calculate_crc(command[i]); |
gen_clk(1); |
end |
|
for(i=`DBG_CPU_ACC_TYPE_LEN -1; i>=0; i=i-1) |
begin |
tdi<=#1 acc_type[i]; // command |
calculate_crc(acc_type[i]); |
gen_clk(1); |
end |
|
for(i=`DBG_CPU_ADR_LEN -1; i>=0; i=i-1) // address |
begin |
tdi<=#1 addr[i]; |
calculate_crc(addr[i]); |
gen_clk(1); |
end |
|
for(i=`DBG_CPU_LEN_LEN -1; i>=0; i=i-1) // length |
begin |
tdi<=#1 length[i]; |
calculate_crc(length[i]); |
gen_clk(1); |
end |
|
for(i=`DBG_CPU_CRC_LEN -1; i>=0; i=i-1) |
begin |
if (gen_crc_err & (i==0)) // Generate crc error at last crc bit |
tdi<=#1 ~crc_out[i]; // error crc |
else |
tdi<=#1 crc_out[i]; // ok crc |
|
gen_clk(1); |
end |
|
tdi<=#1 1'hz; |
|
crc_in = {`DBG_CPU_CRC_LEN{1'b1}}; // Initialize incoming CRC to all ff |
|
for(i=`DBG_CPU_STATUS_LEN -1; i>=0; i=i-1) |
begin |
gen_clk(1); // Generating clock to read out a status bit. |
status_cpu[i] = tdo; |
end |
|
if (|status_cpu) |
begin |
$write("(*E) (%0t) debug_cpu_wr_comm error: ", $time); |
casex (status_cpu) |
4'b1xxx : $display("CRC error !!!\n\n", $time); |
4'bx1xx : $display("Unknown command !!!\n\n", $time); |
4'bxx1x : $display("??? error !!!\n\n", $time); |
4'bxxx1 : $display("Overrun/Underrun !!!\n\n", $time); |
endcase |
`ifdef DEBUG_INFO |
$stop; |
`endif |
end |
|
|
for(i=0; i<`DBG_CPU_CRC_LEN -1; i=i+1) // Getting in the CRC |
begin |
gen_clk(1); |
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to exit1_dr |
|
if (~crc_match_in) |
begin |
$display("(%0t) Incoming CRC failed !!!", $time); |
`ifdef DEBUG_INFO |
$stop; |
`endif |
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to update_dr |
tms<=#1 1'b0; |
gen_clk(1); // to run_test_idle |
end |
endtask // debug_cpu_wr_comm |
|
|
|
task debug_cpu_go; |
input wait_for_cpu_ready; |
input gen_crc_err; |
integer i; |
reg [4:0] bit_pointer; |
integer word_pointer; |
reg [31:0] tmp_data; |
reg [`DBG_CPU_CMD_LEN -1:0] command; |
|
|
begin |
`ifdef DEBUG_INFO |
$display("(%0t) Task debug_cpu_go (previous command was %0s): ", $time, last_cpu_cmd_text); |
`endif |
command = `DBG_CPU_GO; |
word_pointer = 0; |
|
tms<=#1 1'b1; |
gen_clk(1); |
tms<=#1 1'b0; |
gen_clk(2); // we are in shiftDR |
|
crc_out = {`DBG_CPU_CRC_LEN{1'b1}}; // Initialize outgoing CRC to all ff |
|
tdi<=#1 1'b0; // module_select bit = 0 |
calculate_crc(1'b0); |
gen_clk(1); |
|
for(i=`DBG_CPU_CMD_LEN -1; i>=0; i=i-1) |
begin |
tdi<=#1 command[i]; // command |
calculate_crc(command[i]); |
gen_clk(1); |
end |
|
|
if (last_cpu_cmd == `DBG_CPU_WRITE) // When DBG_CPU_WRITE was previously activated, data needs to be shifted. |
begin |
for (i=0; i<((length_global) << 3); i=i+1) |
begin |
tmp_data = data_storage[word_pointer]; |
if ((!(i%32)) && (i>0)) |
begin |
word_pointer = word_pointer + 1; |
end |
bit_pointer = 31-i[4:0]; |
tdi<=#1 tmp_data[bit_pointer]; |
calculate_crc(tmp_data[bit_pointer]); |
gen_clk(1); |
|
end |
end |
|
for(i=`DBG_CPU_CRC_LEN -1; i>=1; i=i-1) |
begin |
tdi<=#1 crc_out[i]; |
gen_clk(1); |
end |
|
if (gen_crc_err) // Generate crc error at last crc bit |
tdi<=#1 ~crc_out[0]; // error crc |
else |
tdi<=#1 crc_out[0]; // ok crc |
|
if (wait_for_cpu_ready) |
begin |
tms<=#1 1'b1; |
gen_clk(1); // to exit1_dr. Last CRC is shifted on this clk |
tms<=#1 1'b0; |
gen_clk(1); // to pause_dr |
|
#2; // wait a bit for tdo to activate |
while (tdo) // waiting for wb to send "ready" |
begin |
gen_clk(1); // staying in pause_dr |
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to exit2_dr |
tms<=#1 1'b0; |
gen_clk(1); // to shift_dr |
end |
else |
begin |
gen_clk(1); // Last CRC is shifted on this clk |
end |
|
|
tdi<=#1 1'hz; |
crc_in = {`DBG_CPU_CRC_LEN{1'b1}}; // Initialize incoming CRC to all ff |
|
if (last_cpu_cmd == `DBG_CPU_READ) // When DBG_CPU_READ was previously activated, data needs to be shifted. |
begin |
`ifdef DEBUG_INFO |
$display("\t\tGenerating %0d clocks to read %0d data bytes.", length_global<<3, length_global); |
`endif |
word_pointer = 0; // Reset pointer |
for (i=0; i<(length_global<<3); i=i+1) |
begin |
|
gen_clk(1); |
|
if (i[4:0] == 31) // Latching data |
begin |
data_storage[word_pointer] = in_data_be; |
`ifdef DEBUG_INFO |
$display("\t\tin_data_be = 0x%x", in_data_be); |
`endif |
word_pointer = word_pointer + 1; |
end |
|
end |
end |
|
|
for(i=`DBG_CPU_STATUS_LEN -1; i>=0; i=i-1) |
begin |
gen_clk(1); // Generating clock to read out a status bit. |
status_cpu[i] = tdo; |
end |
|
if (|status_cpu) |
begin |
$write("(*E) (%0t) debug_cpu_go error: ", $time); |
casex (status_cpu) |
4'b1xxx : $display("CRC error !!!\n\n", $time); |
4'bx1xx : $display("Unknown command !!!\n\n", $time); |
4'bxx1x : $display("??? error !!!\n\n", $time); |
4'bxxx1 : $display("Overrun/Underrun !!!\n\n", $time); |
endcase |
$stop; |
end |
|
|
for(i=0; i<`DBG_CPU_CRC_LEN -1; i=i+1) // Getting in the CRC |
begin |
gen_clk(1); |
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to exit1_dr |
|
if (~crc_match_in) |
begin |
$display("(%0t) Incoming CRC failed !!!", $time); |
$stop; |
end |
|
tms<=#1 1'b1; |
gen_clk(1); // to update_dr |
tms<=#1 1'b0; |
gen_clk(1); // to run_test_idle |
end |
endtask // debug_cpu_go |
|
|
|
|
// Calculating outgoing CRC |
task calculate_crc; |
input data; |
|
begin |
crc_out[0] <= #1 data ^ crc_out[31]; |
crc_out[1] <= #1 data ^ crc_out[0] ^ crc_out[31]; |
crc_out[2] <= #1 data ^ crc_out[1] ^ crc_out[31]; |
crc_out[3] <= #1 crc_out[2]; |
crc_out[4] <= #1 data ^ crc_out[3] ^ crc_out[31]; |
crc_out[5] <= #1 data ^ crc_out[4] ^ crc_out[31]; |
crc_out[6] <= #1 crc_out[5]; |
crc_out[7] <= #1 data ^ crc_out[6] ^ crc_out[31]; |
crc_out[8] <= #1 data ^ crc_out[7] ^ crc_out[31]; |
crc_out[9] <= #1 crc_out[8]; |
crc_out[10] <= #1 data ^ crc_out[9] ^ crc_out[31]; |
crc_out[11] <= #1 data ^ crc_out[10] ^ crc_out[31]; |
crc_out[12] <= #1 data ^ crc_out[11] ^ crc_out[31]; |
crc_out[13] <= #1 crc_out[12]; |
crc_out[14] <= #1 crc_out[13]; |
crc_out[15] <= #1 crc_out[14]; |
crc_out[16] <= #1 data ^ crc_out[15] ^ crc_out[31]; |
crc_out[17] <= #1 crc_out[16]; |
crc_out[18] <= #1 crc_out[17]; |
crc_out[19] <= #1 crc_out[18]; |
crc_out[20] <= #1 crc_out[19]; |
crc_out[21] <= #1 crc_out[20]; |
crc_out[22] <= #1 data ^ crc_out[21] ^ crc_out[31]; |
crc_out[23] <= #1 data ^ crc_out[22] ^ crc_out[31]; |
crc_out[24] <= #1 crc_out[23]; |
crc_out[25] <= #1 crc_out[24]; |
crc_out[26] <= #1 data ^ crc_out[25] ^ crc_out[31]; |
crc_out[27] <= #1 crc_out[26]; |
crc_out[28] <= #1 crc_out[27]; |
crc_out[29] <= #1 crc_out[28]; |
crc_out[30] <= #1 crc_out[29]; |
crc_out[31] <= #1 crc_out[30]; |
end |
endtask // calculate_crc |
|
|
// Calculating and checking input CRC |
always @(posedge tck) |
begin |
crc_in[0] <= #1 tdo ^ crc_in[31]; |
crc_in[1] <= #1 tdo ^ crc_in[0] ^ crc_in[31]; |
crc_in[2] <= #1 tdo ^ crc_in[1] ^ crc_in[31]; |
crc_in[3] <= #1 crc_in[2]; |
crc_in[4] <= #1 tdo ^ crc_in[3] ^ crc_in[31]; |
crc_in[5] <= #1 tdo ^ crc_in[4] ^ crc_in[31]; |
crc_in[6] <= #1 crc_in[5]; |
crc_in[7] <= #1 tdo ^ crc_in[6] ^ crc_in[31]; |
crc_in[8] <= #1 tdo ^ crc_in[7] ^ crc_in[31]; |
crc_in[9] <= #1 crc_in[8]; |
crc_in[10] <= #1 tdo ^ crc_in[9] ^ crc_in[31]; |
crc_in[11] <= #1 tdo ^ crc_in[10] ^ crc_in[31]; |
crc_in[12] <= #1 tdo ^ crc_in[11] ^ crc_in[31]; |
crc_in[13] <= #1 crc_in[12]; |
crc_in[14] <= #1 crc_in[13]; |
crc_in[15] <= #1 crc_in[14]; |
crc_in[16] <= #1 tdo ^ crc_in[15] ^ crc_in[31]; |
crc_in[17] <= #1 crc_in[16]; |
crc_in[18] <= #1 crc_in[17]; |
crc_in[19] <= #1 crc_in[18]; |
crc_in[20] <= #1 crc_in[19]; |
crc_in[21] <= #1 crc_in[20]; |
crc_in[22] <= #1 tdo ^ crc_in[21] ^ crc_in[31]; |
crc_in[23] <= #1 tdo ^ crc_in[22] ^ crc_in[31]; |
crc_in[24] <= #1 crc_in[23]; |
crc_in[25] <= #1 crc_in[24]; |
crc_in[26] <= #1 tdo ^ crc_in[25] ^ crc_in[31]; |
crc_in[27] <= #1 crc_in[26]; |
crc_in[28] <= #1 crc_in[27]; |
crc_in[29] <= #1 crc_in[28]; |
crc_in[30] <= #1 crc_in[29]; |
crc_in[31] <= #1 crc_in[30]; |
end |
|
assign crc_match_in = crc_in == 32'h0; |
|
|
endmodule // vpi_debug_module |