Introduce riscv openocd
Install openocd
Download openocd from https://github.com/riscv/riscv-openocd
Download jimtcl from https://github.com/msteveb/jimtcl
Copy jimtcl to riscv-openocd-riscv/jimtcl
1 | > ./bootstrap |
--enable-remote-bitbang
means drive JTAG from a remote process. This sets up a UNIX or TCP socket connection with a remote process and sends ASCII encoded bitbang requests to that process instead of directly driving JTAG.
Integrate to testbench
Integrate
SimJTAG.v
to testbenchhttps://github.com/freechipsproject/rocket-chip/blob/new_remote_bitbang/vsrc/SimJTAG.v
Or the simpler one.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49//VCS coverage exclude_file
import "DPI-C" function int jtag_tick
(
output bit jtag_TCK,
output bit jtag_TMS,
output bit jtag_TDI,
output bit jtag_TRSTn,
input bit jtag_TDO
);
module SimJTAG(
input clock,
input reset,
output jtag_TCK,
output jtag_TMS,
output jtag_TDI,
output jtag_TRSTn,
input jtag_TDO_data,
input jtag_TDO_driven
);
int TICK_DELAY = 5;
int tick_counter;
reg jtag_vpi_enable;
initial begin
if(!$value$plusargs("jtag_vpi_enable=%d",jtag_vpi_enable))
jtag_vpi_enable = 0;
end
always @(posedge clock) begin
if (reset) begin
tick_count = TICK_DELAY;
end else begin
if(jtag_vpi_enable & (tick_counter == 0)) begin
void'(jtag_tick(
jtag_TCK,
jtag_TMS,
jtag_TDI,
jtag_TRSTn,
jtag_TDO_data
));
tick_counter = TICK_DELAY;
end
else begin
tick_counter = tick_counter - 1;
end
end
end
endmodule
Include
SimJTAG.cc
,remote_bitbang.cc
andremote_bitbang.h
to vcs comilationhttps://github.com/freechipsproject/rocket-chip/blob/src/main/resources/csrc/SimJTAG.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
remote_bitbang_t* jtag;
extern "C" int jtag_tick
(
unsigned char * jtag_TCK,
unsigned char * jtag_TMS,
unsigned char * jtag_TDI,
unsigned char * jtag_TRSTn,
unsigned char jtag_TDO
){
if (!jtag) {
// TODO: Pass in real port number
jtag = new remote_bitbang_t(0);
}
jtag->tick(jtag_TCK, jtag_TMS, jtag_TDI, jtag_TRSTn, jtag_TDO);
return jtag->done() ? (jtag->exit_code() << 1 | 1) : 0;
}https://github.com/freechipsproject/rocket-chip/blob/src/main/resources/csrc/remote_bitbang.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/////////// remote_bitbang_t
remote_bitbang_t::remote_bitbang_t(uint16_t port) :
socket_fd(0),
client_fd(0),
recv_start(0),
recv_end(0),
err(0)
{
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (socket_fd == -1) {
fprintf(stderr, "remote_bitbang failed to make socket: %s (%d)\n",
strerror(errno), errno);
abort();
}
fcntl(socket_fd, F_SETFL, O_NONBLOCK);
int reuseaddr = 1;
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
sizeof(int)) == -1) {
fprintf(stderr, "remote_bitbang failed setsockopt: %s (%d)\n",
strerror(errno), errno);
abort();
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
if (::bind(socket_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
fprintf(stderr, "remote_bitbang failed to bind socket: %s (%d)\n",
strerror(errno), errno);
abort();
}
if (listen(socket_fd, 1) == -1) {
fprintf(stderr, "remote_bitbang failed to listen on socket: %s (%d)\n",
strerror(errno), errno);
abort();
}
socklen_t addrlen = sizeof(addr);
if (getsockname(socket_fd, (struct sockaddr *) &addr, &addrlen) == -1) {
fprintf(stderr, "remote_bitbang getsockname failed: %s (%d)\n",
strerror(errno), errno);
abort();
}
tck = 1;
tms = 1;
tdi = 1;
trstn = 1;
quit = 0;
fprintf(stderr, "This emulator compiled with JTAG Remote Bitbang client. To enable, use +jtag_rbb_enable=1.\n");
fprintf(stderr, "Listening on port %d\n",
ntohs(addr.sin_port));
}
void remote_bitbang_t::accept()
{
fprintf(stderr,"Attempting to accept client socket\n");
int again = 1;
while (again != 0) {
client_fd = ::accept(socket_fd, NULL, NULL);
if (client_fd == -1) {
if (errno == EAGAIN) {
// No client waiting to connect right now.
} else {
fprintf(stderr, "failed to accept on socket: %s (%d)\n", strerror(errno),
errno);
again = 0;
abort();
}
} else {
fcntl(client_fd, F_SETFL, O_NONBLOCK);
fprintf(stderr, "Accepted successfully.");
again = 0;
}
}
}
void remote_bitbang_t::tick(
unsigned char * jtag_tck,
unsigned char * jtag_tms,
unsigned char * jtag_tdi,
unsigned char * jtag_trstn,
unsigned char jtag_tdo
)
{
if (client_fd > 0) {
tdo = jtag_tdo;
execute_command();
} else {
this->accept();
}
* jtag_tck = tck;
* jtag_tms = tms;
* jtag_tdi = tdi;
* jtag_trstn = trstn;
}
void remote_bitbang_t::reset(){
//trstn = 0;
}
void remote_bitbang_t::set_pins(char _tck, char _tms, char _tdi){
tck = _tck;
tms = _tms;
tdi = _tdi;
}
void remote_bitbang_t::execute_command()
{
char command;
int again = 1;
while (again) {
ssize_t num_read = read(client_fd, &command, sizeof(command));
if (num_read == -1) {
if (errno == EAGAIN) {
// We'll try again the next call.
//fprintf(stderr, "Received no command. Will try again on the next call\n");
} else {
fprintf(stderr, "remote_bitbang failed to read on socket: %s (%d)\n",
strerror(errno), errno);
again = 0;
abort();
}
} else if (num_read == 0) {
fprintf(stderr, "No Command Received.\n");
again = 1;
} else {
again = 0;
}
}
//fprintf(stderr, "Received a command %c\n", command);
int dosend = 0;
char tosend = '?';
switch (command) {
case 'B': /* fprintf(stderr, "*BLINK*\n"); */ break;
case 'b': /* fprintf(stderr, "_______\n"); */ break;
case 'r': reset(); break; // This is wrong. 'r' has other bits that indicated TRST and SRST.
case '0': set_pins(0, 0, 0); break;
case '1': set_pins(0, 0, 1); break;
case '2': set_pins(0, 1, 0); break;
case '3': set_pins(0, 1, 1); break;
case '4': set_pins(1, 0, 0); break;
case '5': set_pins(1, 0, 1); break;
case '6': set_pins(1, 1, 0); break;
case '7': set_pins(1, 1, 1); break;
case 'R': dosend = 1; tosend = tdo ? '1' : '0'; break;
case 'Q': quit = 1; break;
default:
fprintf(stderr, "remote_bitbang got unsupported command '%c'\n",
command);
}
if (dosend){
while (1) {
ssize_t bytes = write(client_fd, &tosend, sizeof(tosend));
if (bytes == -1) {
fprintf(stderr, "failed to write to socket: %s (%d)\n", strerror(errno), errno);
abort();
}
if (bytes > 0) {
break;
}
}
}
if (quit) {
// The remote disconnected.
fprintf(stderr, "Remote end disconnected\n");
close(client_fd);
client_fd = 0;
}
}
https://github.com/freechipsproject/rocket-chip/blob/src/main/resources/csrc/remote_bitbang.h
1 |
|
Usage
Start the simulation, the port will be displayed.
1
2> ./simv
Listening on port 36054
Run openocd
1
2
3
4
5> setenv JTAG_VPI_PORT 36054; setenv JTAG_DTM_ENABLE_SBA on; openocd -f RocketSim.cfg -d -l log
Listening on port 3333 for gdb connections
Listening on port 6666 for tcl connections
Listening on port 4444 for telnet connections
Run telnet to send command to openocd
1
2
3> telnet 127.0.0.1 4444
Open On-Chip Debugger
>
Openocd commands
config
An example:
https://github.com/freechipsproject/rocket-chip/blob/scripts/RocketSim.cfg
OpenOCD doesn’t have a concept of harts, but thinks of each hart as a completely independent core. gdb doesn’t have a concept of cores, but can only think about threads. OpenOCD does have a concept of threads when there is an RTOS on the system. Combining these 3 pieces, we can create an acceptable debug experience by having OpenOCD pretend there is an RTOS, and expose each hart as a thread to gdb.
Originally we did this using -rtos riscv which is RISC-V-specific. Since we implemented that, OpenOCD has gained a new ability which solves the same problem in a target-independent way: -rtos hwthread. While the behavior for the user is almost identical, the configuration required is a bit different.
-rtos riscv
The OpenOCD -rtos riscv
configuration option is deprecated and will be removed from OpenOCD in a future version. Using -rtos hwthread
is the multi-core configuration option that should be used going forward. Existing config scripts should be converted from riscv to hwthread.
The configuration only mentions a single target, and our code will figure out how many harts there really are and expose each one as a thread. Example configuration:
1 | set _TARGETNAME $_CHIPNAME.cpu |
-rtos hwthread
The configuration mentions every hart explicitly. The harts are combined into SMP groups, and the first target in the SMP group mentions the RTOS. If you like you can create multiple SMP groups, and OpenOCD will listen for one gdb per group. Example:
1 | set _TARGETNAME_0 $_CHIPNAME.cpu0 |
Example
1 | adapter_khz 10000 |
commands
1 | sleep msec [busy] |