1 //
2 // Copyright 2020 Ettus Research, a National Instruments Brand
3 //
4 // SPDX-License-Identifier: GPL-3.0-or-later
5 //
6 
7 #include <uhd/rfnoc/defaults.hpp>
8 #include <uhd/rfnoc/multichan_register_iface.hpp>
9 #include <uhd/rfnoc/property.hpp>
10 #include <uhd/rfnoc/registry.hpp>
11 #include <uhd/rfnoc/switchboard_block_control.hpp>
12 
13 using namespace uhd::rfnoc;
14 
15 // Register offsets
16 const uint32_t switchboard_block_control::REG_BLOCK_SIZE        = 8;
17 const uint32_t switchboard_block_control::REG_DEMUX_SELECT_ADDR = 0;
18 const uint32_t switchboard_block_control::REG_MUX_SELECT_ADDR   = 4;
19 
20 // User properties
21 const char* const PROP_KEY_INPUT_SELECT = "input_select";
22 const char* const PROP_KEY_OUTPUT_SELECT = "output_select";
23 
24 class switchboard_block_control_impl : public switchboard_block_control
25 {
26 public:
RFNOC_BLOCK_CONSTRUCTOR(switchboard_block_control)27     RFNOC_BLOCK_CONSTRUCTOR(switchboard_block_control),
28         _num_input_ports(get_num_input_ports()),
29         _num_output_ports(get_num_output_ports()),
30         _switchboard_reg_iface(*this, 0, REG_BLOCK_SIZE)
31     {
32         UHD_ASSERT_THROW(_num_input_ports > 0 && _num_output_ports > 0);
33 
34         _register_props();
35 
36         // Configure property propagation and action forwarding behavior.
37         set_prop_forwarding_policy(forwarding_policy_t::USE_MAP);
38         set_action_forwarding_policy(forwarding_policy_t::USE_MAP);
39 
40         _update_forwarding_map();
41     }
42 
connect(const size_t input,const size_t output)43     void connect(const size_t input, const size_t output)
44     {
45         set_property<int>(PROP_KEY_INPUT_SELECT, static_cast<int>(input), output);
46         set_property<int>(PROP_KEY_OUTPUT_SELECT, static_cast<int>(output), input);
47 
48         _update_forwarding_map();
49     }
50 
51 private:
52     const size_t _num_input_ports;
53     const size_t _num_output_ports;
54 
_register_props()55     void _register_props()
56     {
57         _input_select.reserve(_num_output_ports);
58         _output_select.reserve(_num_input_ports);
59 
60         // Register _input_select properties
61         for (size_t output_port = 0; output_port < _num_output_ports; output_port++) {
62             _input_select.emplace_back(property_t<int>(
63                 PROP_KEY_INPUT_SELECT, 0, {res_source_info::USER, output_port}));
64 
65             register_property(&_input_select.back(), [this, output_port]() {
66                 int select_val = _input_select.at(output_port).get();
67                 if (select_val < 0
68                     || static_cast<unsigned int>(select_val) >= _num_input_ports)
69                     throw uhd::value_error("Index out of bounds");
70                 _switchboard_reg_iface.poke32(
71                     REG_MUX_SELECT_ADDR, select_val, output_port);
72             });
73         }
74 
75         // Register _output_select properties
76         for (size_t input_port = 0; input_port < _num_input_ports; input_port++) {
77             _output_select.emplace_back(property_t<int>(
78                 PROP_KEY_OUTPUT_SELECT, 0, {res_source_info::USER, input_port}));
79 
80             register_property(&_output_select.back(), [this, input_port]() {
81                 int select_val = _output_select.at(input_port).get();
82                 if (select_val < 0
83                     || static_cast<unsigned int>(select_val) >= _num_output_ports)
84                     throw uhd::value_error("Index out of bounds");
85                 _switchboard_reg_iface.poke32(
86                     REG_DEMUX_SELECT_ADDR, select_val, input_port);
87             });
88         }
89     }
90 
_update_forwarding_map()91     void _update_forwarding_map()
92     {
93         node_t::forwarding_map_t prop_fwd_map;
94         node_t::forwarding_map_t action_fwd_map;
95 
96         // Property propagation scheme:
97         //   Connected inputs and outputs will propagate to each other.
98         //   Unconnected inputs and outputs do not propagate.
99         for (size_t input_port = 0; input_port < _num_input_ports; input_port++) {
100             size_t linked_output_port = _output_select.at(input_port).get();
101             size_t linked_input_port  = _input_select.at(linked_output_port).get();
102             if (linked_input_port == input_port) {
103                 prop_fwd_map.insert({{res_source_info::INPUT_EDGE, linked_input_port},
104                     {{res_source_info::OUTPUT_EDGE, linked_output_port}}});
105                 prop_fwd_map.insert({{res_source_info::OUTPUT_EDGE, linked_output_port},
106                     {{res_source_info::INPUT_EDGE, linked_input_port}}});
107                 action_fwd_map.insert({{res_source_info::INPUT_EDGE, linked_input_port},
108                     {{res_source_info::OUTPUT_EDGE, linked_output_port}}});
109                 action_fwd_map.insert({{res_source_info::OUTPUT_EDGE, linked_output_port},
110                     {{res_source_info::INPUT_EDGE, linked_input_port}}});
111             }
112         }
113         set_prop_forwarding_map(prop_fwd_map);
114         set_action_forwarding_map(action_fwd_map);
115     }
116 
117     /**************************************************************************
118      * Attributes
119      *************************************************************************/
120     std::vector<property_t<int>> _input_select;
121     std::vector<property_t<int>> _output_select;
122 
123     /**************************************************************************
124      * Register Interface
125      *************************************************************************/
126     multichan_register_iface _switchboard_reg_iface;
127 };
128 
129 UHD_RFNOC_BLOCK_REGISTER_DIRECT(
130     switchboard_block_control, SWITCHBOARD_BLOCK, "Switchboard", CLOCK_KEY_GRAPH, "bus_clk")
131