quic/qbox
Loading...
Searching...
No Matches
char_backend_stdio.h
1/*
2 * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#ifndef _GS_UART_BACKEND_STDIO_H_
8#define _GS_UART_BACKEND_STDIO_H_
9
10#include <systemc>
11#include <tlm.h>
12#include <tlm_utils/simple_target_socket.h>
13
14#include <unistd.h>
15
16#include <async_event.h>
17#include <uutils.h>
18#include <ports/biflow-socket.h>
19#include <module_factory_registery.h>
20#include <queue>
21#include <signal.h>
22#include <termios.h>
23#include <poll.h>
24#include <regex>
25#include <atomic>
26
27#define RCV_POLL_TIMEOUT_MS 300
28
29class char_backend_stdio : public sc_core::sc_module
30{
31protected:
32 cci::cci_param<bool> p_read_write;
33 cci::cci_param<std::string> p_expect;
34 cci::cci_param<std::string> p_highlight;
35
36private:
37 std::atomic_bool m_running;
38 std::thread rcv_thread_id;
39 pthread_t rcv_pthread_id = 0;
40 std::atomic<bool> c_flag;
41 SCP_LOGGER();
42 std::string line;
43 std::string ecmd;
44 sc_core::sc_event ecmdev;
45 bool processing = false;
46
47public:
49 static struct termios oldtty;
50 static bool oldtty_valid;
51
52#ifdef WIN32
53#pragma message("CharBackendStdio not yet implemented for WIN32")
54#endif
55 static void catch_fn(int signo) {}
56
57 static void tty_reset()
58 {
59 int fd = STDIN_FILENO; /* stdin */
60 if (char_backend_stdio::oldtty_valid) {
61 tcsetattr(fd, TCSANOW, &char_backend_stdio::oldtty);
62 } else {
63 struct termios tty;
64 tcgetattr(fd, &tty);
65 tty.c_lflag |= ECHO | ECHONL | ICANON | IEXTEN;
67 }
68 }
69 // trim from end of string (right)
70 std::string& rtrim(std::string& s)
71 {
72 const char* t = "\n\r\f\v";
73 s.erase(s.find_last_not_of(t) + 1);
74 return s;
75 }
76 std::string getecmdstr(const std::string& cmd)
77 {
78 return ecmd.substr(cmd.length(), ecmd.find_first_of("\n") - cmd.length());
79 }
80 void process()
81 {
82 processing = true;
83 expect_process();
84 }
85 void expect_process()
86 {
87 if (!processing) return;
88 const std::string expectstr = "expect ";
89 const std::string sendstr = "send ";
90 const std::string waitstr = "wait ";
91 const std::string exitstr = "exit";
92 do {
93 if (ecmd.substr(0, expectstr.length()) == expectstr) {
94 std::string str = getecmdstr(expectstr);
95 std::regex restr(str);
96 if (std::regex_search(line, restr)) {
97 SCP_WARN(())("Found expect string {}", rtrim(line));
98 ecmd.erase(0, ecmd.find_first_of("\n"));
99 if (ecmd != "") ecmd.erase(0, 1);
100 if (ecmd.substr(0, expectstr.length()) == expectstr) break;
101 continue;
102 }
103 }
104 if (ecmd.substr(0, sendstr.length()) == sendstr) {
105 SCP_WARN(())("Sending expect string {}", getecmdstr(sendstr));
106 for (char c : getecmdstr(sendstr)) {
107 enqueue(c);
108 }
109 enqueue('\n');
110 ecmd.erase(0, ecmd.find_first_of("\n"));
111 if (ecmd != "") ecmd.erase(0, 1);
112 continue;
113 }
114 if (ecmd.substr(0, waitstr.length()) == waitstr) {
115 SCP_WARN(())("Expect waiting {}s", getecmdstr(waitstr));
116 processing = false;
117 ecmdev.notify(stod(getecmdstr(waitstr)), sc_core::SC_SEC);
118 ecmd.erase(0, ecmd.find_first_of("\n"));
119 if (ecmd != "") ecmd.erase(0, 1);
120 break;
121 }
122 if (ecmd.substr(0, exitstr.length()) == exitstr) {
123 SCP_WARN(())("Expect caused exit");
124 sc_core::sc_stop();
125 }
126 break;
127 } while (true);
128 }
129
130 /*
131 * char_backend_stdio constructor
132 * Parameters:
133 * \param read_write bool : when set to true, accept input from STDIO
134 * \param expect String : An 'expect' like string of commands, each command should be separated by \n
135 * The acceptable commands are:
136 * expect [string] : dont process any more commands until the "string" is seen on the output (STDIO)
137 * send [string] : send "string" to the input buffer (NB this will happen whether of not read_write is set)
138 * wait [float] : Wait for "float" (simulated) seconds, until processing continues.
139 * exit : Cause the simulation to terminate normally.
140 */
141 char_backend_stdio(sc_core::sc_module_name name)
142 : sc_core::sc_module(name)
143 , p_read_write("read_write", true, "read_write if true start rcv_thread")
144 , p_expect("expect", "", "string of expect commands")
145 , p_highlight("ansi_highlight", "", "ANSI highlight code to use for output, default bold")
146 , socket("biflow_socket")
147 , m_running(true)
148 , c_flag(false)
149 {
150 SCP_TRACE(()) << "CharBackendStdio constructor";
151
152 ecmd = p_expect;
153 SC_METHOD(process);
154 sensitive << ecmdev;
155 if (p_expect.get_value() != "") {
156 ecmd = p_expect;
157 processing = true;
158 SCP_WARN(())("Processing expect string {}", ecmd);
159 }
160
161 gs::SigHandler::get().register_on_exit_cb(std::string(this->name()) + ".char_backend_stdio::tty_reset",
162 tty_reset);
163 gs::SigHandler::get().add_sig_handler(SIGINT, gs::SigHandler::Handler_CB::PASS);
164 gs::SigHandler::get().register_handler(std::string(this->name()) + ".char_backend_stdio::SIGINT_handler",
165 [&](int signo) {
166 if (signo == SIGINT) {
167 c_flag = true;
168 }
169 });
170 if (p_read_write) rcv_thread_id = std::thread(&char_backend_stdio::rcv_thread, this);
171
172 socket.register_b_transport(this, &char_backend_stdio::writefn);
173 }
174
175 void end_of_elaboration() { socket.can_receive_any(); }
176
177 void enqueue(char c) { socket.enqueue(c); }
178 void rcv_thread()
179 {
180 int fd = STDIN_FILENO;
181 struct pollfd rcv_monitor;
182 rcv_monitor.fd = fd;
183 rcv_monitor.events = POLLIN;
184
185 while (m_running) {
186 int ret = poll(&rcv_monitor, 1, RCV_POLL_TIMEOUT_MS);
187 if ((ret == -1 && errno == EINTR) || (ret == 0) /*timeout*/) {
188 if (c_flag) {
189 c_flag = false;
190 enqueue('\x03');
191 }
192 continue;
193 } else if (rcv_monitor.revents & POLLIN) {
194 char c;
195 int r = read(fd, &c, 1);
196 if (r == 1) {
197 enqueue(c);
198 }
199 if (r == 0) {
200 break;
201 }
202 } else {
203 break;
204 }
205 }
206 }
207
208 void writefn(tlm::tlm_generic_payload& txn, sc_core::sc_time& t)
209 {
210 uint8_t* data = txn.get_data_ptr();
211 if (!p_highlight.get_value().empty()) std::cout << p_highlight.get_value();
212 for (int i = 0; i < txn.get_streaming_width(); i++) {
213 putchar(data[i]);
214 if ((char)data[i] == '\n') {
215 expect_process();
216 line = "";
217 } else {
218 line = line + (char)data[i];
219 }
220 }
221 if (!p_highlight.get_value().empty()) std::cout << "\x1B[0m"; // ANSI color reset.
222 fflush(stdout);
223 }
224
225 void start_of_simulation()
226 {
227 if (!char_backend_stdio::oldtty_valid) {
228 struct termios tty;
229 int fd = STDIN_FILENO; /* stdin */
230 tcgetattr(fd, &tty);
231 char_backend_stdio::oldtty = tty;
232 char_backend_stdio::oldtty_valid = true;
233 tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
235 }
236 }
237
238 void end_of_simulation() { tty_reset(); }
239
241 {
242 gs::SigHandler::get().deregister_on_exit_cb(std::string(name()) + ".char_backend_stdio::tty_reset");
243 gs::SigHandler::get().deregister_handler(std::string(name()) + ".char_backend_stdio::SIGINT_handler");
244 m_running = false;
245 if (rcv_thread_id.joinable()) rcv_thread_id.join();
246 tty_reset();
247 }
248};
249extern "C" void module_register();
250#endif
Definition target.h:160
Definition char_backend_stdio.h:30
Definition biflow-socket.h:73
void can_receive_any()
can_receive_any Allow unlimited items to arrive.
Definition biflow-socket.h:264
void enqueue(T data)
enqueue Enqueue data to be sent (unlimited queue size) NOTE: Thread safe.
Definition biflow-socket.h:277
void register_b_transport(MODULE *mod, void(MODULE::*cb)(tlm::tlm_generic_payload &, sc_core::sc_time &))
Register b_transport to be called whenever data is received from the socket.
Definition biflow-socket.h:227