1 //
2 // Copyright 2013 Ettus Research LLC
3 // Copyright 2018 Ettus Research, a National Instruments Company
4 //
5 // SPDX-License-Identifier: GPL-3.0-or-later
6 //
7
8 #include "x300_fw_common.h"
9 #include "x300_regs.hpp"
10 #include <uhd/exception.hpp>
11 #include <uhd/transport/nirio/niriok_proxy.h>
12 #include <uhd/transport/nirio/status.h>
13 #include <uhd/transport/udp_simple.hpp>
14 #include <uhd/types/wb_iface.hpp>
15 #include <uhd/utils/byteswap.hpp>
16 #include <uhd/utils/log.hpp>
17 #include <boost/date_time/posix_time/posix_time.hpp>
18 #include <boost/format.hpp>
19 #include <boost/thread/mutex.hpp>
20 #include <chrono>
21 #include <thread>
22
23 using namespace uhd;
24 using namespace uhd::niusrprio;
25
26 class x300_ctrl_iface : public wb_iface
27 {
28 public:
29 enum { num_retries = 3 };
30
x300_ctrl_iface(bool enable_errors=true)31 x300_ctrl_iface(bool enable_errors = true) : errors(enable_errors)
32 {
33 /* NOP */
34 }
35
flush(void)36 void flush(void)
37 {
38 boost::mutex::scoped_lock lock(reg_access);
39 __flush();
40 }
41
poke32(const wb_addr_type addr,const uint32_t data)42 void poke32(const wb_addr_type addr, const uint32_t data)
43 {
44 for (size_t i = 1; i <= num_retries; i++) {
45 boost::mutex::scoped_lock lock(reg_access);
46 try {
47 return this->__poke32(addr, data);
48 } catch (const uhd::io_error& ex) {
49 std::string error_msg =
50 str(boost::format("%s: x300 fw communication failure #%u\n%s")
51 % __loc_info() % i % ex.what());
52 if (errors)
53 UHD_LOGGER_ERROR("X300") << error_msg;
54 if (i == num_retries)
55 throw uhd::io_error(error_msg);
56 }
57 }
58 }
59
peek32(const wb_addr_type addr)60 uint32_t peek32(const wb_addr_type addr)
61 {
62 for (size_t i = 1; i <= num_retries; i++) {
63 boost::mutex::scoped_lock lock(reg_access);
64 try {
65 uint32_t data = this->__peek32(addr);
66 return data;
67 } catch (const uhd::io_error& ex) {
68 std::string error_msg =
69 str(boost::format("%s: x300 fw communication failure #%u\n%s")
70 % __loc_info() % i % ex.what());
71 if (errors)
72 UHD_LOGGER_ERROR("X300") << error_msg;
73 if (i == num_retries)
74 throw uhd::io_error(error_msg);
75 }
76 }
77 return 0;
78 }
79
80 protected:
81 bool errors;
82
83 virtual void __poke32(const wb_addr_type addr, const uint32_t data) = 0;
84 virtual uint32_t __peek32(const wb_addr_type addr) = 0;
85 virtual void __flush() = 0;
86 virtual std::string __loc_info() = 0;
87
88 boost::mutex reg_access;
89 };
90
91
92 //-----------------------------------------------------
93 // Ethernet impl
94 //-----------------------------------------------------
95 class x300_ctrl_iface_enet : public x300_ctrl_iface
96 {
97 public:
x300_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp,bool enable_errors=true)98 x300_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp, bool enable_errors = true)
99 : x300_ctrl_iface(enable_errors), udp(udp), seq(0)
100 {
101 try {
102 this->peek32(0);
103 } catch (...) {
104 }
105 }
106
107 protected:
__poke32(const wb_addr_type addr,const uint32_t data)108 virtual void __poke32(const wb_addr_type addr, const uint32_t data)
109 {
110 // load request struct
111 x300_fw_comms_t request = x300_fw_comms_t();
112 request.flags =
113 uhd::htonx<uint32_t>(X300_FW_COMMS_FLAGS_ACK | X300_FW_COMMS_FLAGS_POKE32);
114 request.sequence = uhd::htonx<uint32_t>(seq++);
115 request.addr = uhd::htonx(addr);
116 request.data = uhd::htonx(data);
117
118 // send request
119 __flush();
120 udp->send(boost::asio::buffer(&request, sizeof(request)));
121
122 // recv reply
123 x300_fw_comms_t reply = x300_fw_comms_t();
124 const size_t nbytes = udp->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0);
125 if (nbytes == 0)
126 throw uhd::io_error("x300 fw poke32 - reply timed out");
127
128 // sanity checks
129 const size_t flags = uhd::ntohx<uint32_t>(reply.flags);
130 UHD_ASSERT_THROW(nbytes == sizeof(reply));
131 UHD_ASSERT_THROW(not(flags & X300_FW_COMMS_FLAGS_ERROR));
132 UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_POKE32);
133 UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_ACK);
134 UHD_ASSERT_THROW(reply.sequence == request.sequence);
135 UHD_ASSERT_THROW(reply.addr == request.addr);
136 UHD_ASSERT_THROW(reply.data == request.data);
137 }
138
__peek32(const wb_addr_type addr)139 virtual uint32_t __peek32(const wb_addr_type addr)
140 {
141 // load request struct
142 x300_fw_comms_t request = x300_fw_comms_t();
143 request.flags =
144 uhd::htonx<uint32_t>(X300_FW_COMMS_FLAGS_ACK | X300_FW_COMMS_FLAGS_PEEK32);
145 request.sequence = uhd::htonx<uint32_t>(seq++);
146 request.addr = uhd::htonx(addr);
147 request.data = 0;
148
149 // send request
150 __flush();
151 udp->send(boost::asio::buffer(&request, sizeof(request)));
152
153 // recv reply
154 x300_fw_comms_t reply = x300_fw_comms_t();
155 const size_t nbytes = udp->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0);
156 if (nbytes == 0)
157 throw uhd::io_error("x300 fw peek32 - reply timed out");
158
159 // sanity checks
160 const size_t flags = uhd::ntohx<uint32_t>(reply.flags);
161 UHD_ASSERT_THROW(nbytes == sizeof(reply));
162 UHD_ASSERT_THROW(not(flags & X300_FW_COMMS_FLAGS_ERROR));
163 UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_PEEK32);
164 UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_ACK);
165 UHD_ASSERT_THROW(reply.sequence == request.sequence);
166 UHD_ASSERT_THROW(reply.addr == request.addr);
167
168 // return result!
169 return uhd::ntohx<uint32_t>(reply.data);
170 }
171
__flush(void)172 virtual void __flush(void)
173 {
174 char buff[X300_FW_COMMS_MTU] = {};
175 while (udp->recv(boost::asio::buffer(buff), 0.0)) {
176 } // flush
177 }
178
__loc_info(void)179 virtual std::string __loc_info(void)
180 {
181 return udp->get_send_addr();
182 }
183
184 private:
185 uhd::transport::udp_simple::sptr udp;
186 size_t seq;
187 };
188
189
190 //-----------------------------------------------------
191 // PCIe impl
192 //-----------------------------------------------------
193 class x300_ctrl_iface_pcie : public x300_ctrl_iface
194 {
195 public:
x300_ctrl_iface_pcie(niriok_proxy::sptr drv_proxy,bool enable_errors=true)196 x300_ctrl_iface_pcie(niriok_proxy::sptr drv_proxy, bool enable_errors = true)
197 : x300_ctrl_iface(enable_errors), _drv_proxy(drv_proxy)
198 {
199 nirio_status status = 0;
200 nirio_status_chain(
201 _drv_proxy->set_attribute(RIO_ADDRESS_SPACE, BUS_INTERFACE), status);
202
203 // Verify that the Ettus FPGA loaded in the device. This may not be true if the
204 // user is switching to UHD after using LabVIEW FPGA.
205 uint32_t pcie_fpga_signature = 0;
206 _drv_proxy->peek(FPGA_PCIE_SIG_REG, pcie_fpga_signature);
207 if (pcie_fpga_signature != FPGA_X3xx_SIG_VALUE)
208 throw uhd::io_error(
209 "cannot create x300_ctrl_iface_pcie. incorrect/no fpga image");
210
211 // Also, poll on the ZPU_STATUS bit to ensure all the state machines in the FPGA
212 // are ready to accept register transaction requests.
213 uint32_t reg_data = 0xffffffff;
214 boost::posix_time::ptime start_time =
215 boost::posix_time::microsec_clock::local_time();
216 boost::posix_time::time_duration elapsed;
217
218 do {
219 std::this_thread::sleep_for(
220 std::chrono::microseconds(500)); // Avoid flooding the bus
221 elapsed = boost::posix_time::microsec_clock::local_time() - start_time;
222 nirio_status_chain(
223 _drv_proxy->peek(PCIE_ZPU_STATUS_REG(0), reg_data), status);
224 } while (nirio_status_not_fatal(status) && (reg_data & PCIE_ZPU_STATUS_SUSPENDED)
225 && elapsed.total_milliseconds() < INIT_TIMEOUT_IN_MS);
226
227 nirio_status_to_exception(status, "Could not initialize x300_ctrl_iface_pcie.");
228
229 try {
230 this->peek32(0);
231 } catch (...) {
232 }
233 }
234
235 protected:
__poke32(const wb_addr_type addr,const uint32_t data)236 virtual void __poke32(const wb_addr_type addr, const uint32_t data)
237 {
238 nirio_status status = 0;
239 uint32_t reg_data = 0xffffffff;
240 boost::posix_time::ptime start_time =
241 boost::posix_time::microsec_clock::local_time();
242 boost::posix_time::time_duration elapsed;
243
244 nirio_status_chain(_drv_proxy->poke(PCIE_ZPU_DATA_REG(addr), data), status);
245 if (nirio_status_not_fatal(status)) {
246 do {
247 std::this_thread::sleep_for(
248 std::chrono::microseconds(50)); // Avoid flooding the bus
249 elapsed = boost::posix_time::microsec_clock::local_time() - start_time;
250 nirio_status_chain(
251 _drv_proxy->peek(PCIE_ZPU_STATUS_REG(addr), reg_data), status);
252 } while (
253 nirio_status_not_fatal(status)
254 && ((reg_data & (PCIE_ZPU_STATUS_BUSY | PCIE_ZPU_STATUS_SUSPENDED)) != 0)
255 && elapsed.total_milliseconds() < READ_TIMEOUT_IN_MS);
256 }
257
258 if (nirio_status_fatal(status))
259 throw uhd::io_error("x300 fw poke32 - hardware IO error");
260 if (elapsed.total_milliseconds() > READ_TIMEOUT_IN_MS)
261 throw uhd::io_error("x300 fw poke32 - operation timed out");
262 }
263
__peek32(const wb_addr_type addr)264 virtual uint32_t __peek32(const wb_addr_type addr)
265 {
266 nirio_status status = 0;
267 uint32_t reg_data = 0xffffffff;
268 boost::posix_time::ptime start_time =
269 boost::posix_time::microsec_clock::local_time();
270 boost::posix_time::time_duration elapsed;
271
272 nirio_status_chain(
273 _drv_proxy->poke(PCIE_ZPU_READ_REG(addr), PCIE_ZPU_READ_START), status);
274 if (nirio_status_not_fatal(status)) {
275 do {
276 std::this_thread::sleep_for(
277 std::chrono::microseconds(50)); // Avoid flooding the bus
278 elapsed = boost::posix_time::microsec_clock::local_time() - start_time;
279 nirio_status_chain(
280 _drv_proxy->peek(PCIE_ZPU_STATUS_REG(addr), reg_data), status);
281 } while (
282 nirio_status_not_fatal(status)
283 && ((reg_data & (PCIE_ZPU_STATUS_BUSY | PCIE_ZPU_STATUS_SUSPENDED)) != 0)
284 && elapsed.total_milliseconds() < READ_TIMEOUT_IN_MS);
285 }
286 nirio_status_chain(_drv_proxy->peek(PCIE_ZPU_DATA_REG(addr), reg_data), status);
287
288 if (nirio_status_fatal(status))
289 throw uhd::io_error("x300 fw peek32 - hardware IO error");
290 if (elapsed.total_milliseconds() > READ_TIMEOUT_IN_MS)
291 throw uhd::io_error("x300 fw peek32 - operation timed out");
292
293 return reg_data;
294 }
295
__flush(void)296 virtual void __flush(void)
297 {
298 __peek32(0);
299 }
300
__loc_info(void)301 virtual std::string __loc_info(void)
302 {
303 return std::to_string(_drv_proxy->get_interface_num());
304 }
305
306 private:
307 niriok_proxy::sptr _drv_proxy;
308 static const uint32_t READ_TIMEOUT_IN_MS = 100;
309 static const uint32_t INIT_TIMEOUT_IN_MS = 5000;
310 };
311
x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp,bool enable_errors=true)312 wb_iface::sptr x300_make_ctrl_iface_enet(
313 uhd::transport::udp_simple::sptr udp, bool enable_errors = true)
314 {
315 return wb_iface::sptr(new x300_ctrl_iface_enet(udp, enable_errors));
316 }
317
x300_make_ctrl_iface_pcie(niriok_proxy::sptr drv_proxy,bool enable_errors=true)318 wb_iface::sptr x300_make_ctrl_iface_pcie(
319 niriok_proxy::sptr drv_proxy, bool enable_errors = true)
320 {
321 return wb_iface::sptr(new x300_ctrl_iface_pcie(drv_proxy, enable_errors));
322 }
323