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