-
Notifications
You must be signed in to change notification settings - Fork 8
/
sim_socket.cpp
204 lines (176 loc) · 5.86 KB
/
sim_socket.cpp
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// Labarc FPGA Simulator Socket Server
// This file is derived from:
// verilator-3.922/examples/hello_world_c/sim_main.cpp
// This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.1.
// Icaro Dantas de Araujo Lima and Elmar Melcher at UFCG, 2021
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <iostream>
#include <iomanip>
#include <string>
#include <thread>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
using namespace boost::asio;
using ip::tcp;
using boost::system::error_code;
using boost::posix_time::milliseconds;
using ip::host_name;
using std::string;
using std::istream;
using std::ostream;
using std::cout;
using std::cerr;
using std::setfill;
using std::setw;
using std::hex;
using std::setprecision;
using std::endl;
using std::stoi;
using std::to_string;
using std::thread;
extern int vinit(int argc, char** argv); // initialize Verilator, returns clock period
extern void vtick(); // Verilator timer action
extern void vcmd(unsigned short cmd, ostream& sout); // perform command and get response
extern void vdelete(); // Verilator destructor
// service for timer and socket
io_service io;
milliseconds *interval_ptr;
deadline_timer *timer_ptr; // timer for the clock signal
void tick(const error_code& ) {
vtick();
// Reschedule the timer for 1 second in the future:
timer_ptr->expires_at(timer_ptr->expires_at() + *interval_ptr);
// Posts the timer event
timer_ptr->async_wait(tick);
}
// https://www.codeproject.com/Articles/1264257/Socket-Programming-in-Cplusplus-using-boost-asio-T
//socket creation
tcp::socket sock(io);
// buffer for string received by socket
streambuf binp;
// acceptor for socket opening
tcp::acceptor *acceptor_ptr;
// handle prototypes
void accept_handler(const error_code& error);
void read_handler(const error_code&, size_t);
void write_handler(const error_code&, size_t);
void exit_all() {
sock.close();
timer_ptr->cancel();
// Destroy Verilog model
vdelete();
cerr << "___________________pronto____________________" << endl;
// Fin
exit(0);
}
void accept_handler(const error_code& err) {
if (!err) {
// read operation
async_read_until(sock, binp, '\n', read_handler);
} else {
cerr << "accept error: " << err.message() << endl;
exit_all();
}
}
void read_handler(const error_code& err, size_t bytes_transferred) {
if (!err) {
// get command from input stream
istream is(&binp);
char cmd_str[bytes_transferred], // be sure we allocate enough memory
ip_str[bytes_transferred];
is >> cmd_str >> ip_str;
// read end-of-line character to clean up for next read
char eol;
is >> eol;
if (cmd_str[0] == 'e') exit_all(); // if command is "exit"
unsigned short cmd = stoi(cmd_str, 0, 2); // convert binary command string
// prepare output string
streambuf bout;
ostream sout(&bout);
sout << setfill('0') << hex;
vcmd(cmd,sout); // pass command to Verilator and get response
sout << '\r' << endl; // needed for compatibility with JTAG server
//write operation
async_write(sock, bout, write_handler);
} else {
cerr << "read error: " << err.message() << endl;
exit_all();
}
}
void write_handler(const error_code& err, size_t bytes_transferred) {
if (!err) {
// For compatibility with web server socket client,
// the connection is opened and closed for each send_and_receive.
// However, in Windows opening a connection takes an excessive amount of time
// (7 seconds have been reported), so in Windows the connection is opened only once.
#ifdef MINGW
async_read_until(sock, binp, '\n', read_handler);
#else
sock.close();
acceptor_ptr->async_accept(sock, accept_handler); // accept next connection
#endif
} else {
cerr << "write error: " << err.message() << endl;
exit_all();
}
}
int port;
void independentThread() {
string cmd =
#ifndef MINGW
"./"
#endif
"remote.bin " + to_string(port);
std:system(cmd.c_str());
exit(0);
}
int main(int argc, char** argv, char** env) {
unsigned int divide_by, interval;
if (argc>1) {
divide_by = atoi(argv[1]);
// consume argv[1];
argv[1] = argv[0];
argc--;
argv++;
}
else divide_by = 100000000;
interval = divide_by / 100000; // timer interval in seconds
float f = 500. / interval; // frequency in Hz
if (divide_by > 500000000) {
cerr << "Error: Clock frequency " << f << " Hz is below the allowed 0.1 Hz" << endl;
exit(1);
} else if (divide_by >= 100000 ) {
cerr << setprecision(3) << f << " Hz" << endl;
} else {
cerr << "Error: Clock frequency " << f << " Hz is higher than the allowed 500 Hz" << endl;
exit(1);
}
interval_ptr = new milliseconds(interval);
timer_ptr = new deadline_timer(io, *interval_ptr);
vinit(argc, argv);
//listener for new connection, let OS choose port number
acceptor_ptr = new tcp::acceptor(io, tcp::endpoint(tcp::v4(), 0));
port = acceptor_ptr->local_endpoint().port();
//waiting for first connection
acceptor_ptr->async_accept(sock, accept_handler);
// Schedule the timer for the first time:
timer_ptr->async_wait(tick);
#ifdef LAD
string name = host_name();
cerr << "<h4>Agora digite: ./remote "
<< name.substr(0,name.find('.')) << " " << port << " </h4>" << endl;
// need to use cerr because only stderr is directed to log file
#else
thread t(independentThread);
t.detach();
#endif
// Enter timer IO loop and never return.
// The timer will fire for the first time 1 second from now.
io.run();
#ifndef LAD
t.join();
#endif
}