quic/qbox
Loading...
Searching...
No Matches
registers.h
1/*
2 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#ifndef GS_REGISTERS_H
8#define GS_REGISTERS_H
9
10#include <type_traits>
11
12#include <systemc>
13#include <cci_configuration>
14#include <tlm>
15#include <tlm_utils/simple_target_socket.h>
16#include <tlm_utils/simple_initiator_socket.h>
17#include <tlm_utils/multi_passthrough_target_socket.h>
18#include <scp/report.h>
19#include <cciutils.h>
20#include <tlm_sockets_buswidth.h>
21#include <vector>
22#include <algorithm>
23#include <limits>
24#include <bitset>
25
26namespace gs {
27
28template <typename T>
29static T gs_full_mask()
30{
31 return static_cast<T>(std::bitset<std::numeric_limits<T>::digits>(0).flip().to_ullong());
32}
33
39{
40public:
41 typedef std::function<void(tlm::tlm_generic_payload&, sc_core::sc_time&)> TLMFUNC;
42
43private:
44 TLMFUNC m_fnct;
45
46public:
47 tlm_fnct(tlm_fnct&) = delete;
48 tlm_fnct(TLMFUNC cb): m_fnct(cb) {}
49 void operator()(tlm::tlm_generic_payload& txn, sc_core::sc_time& delay) { m_fnct(txn, delay); }
50 virtual void dummy() {} // force polymorphism
51};
52
58class port_fnct : public tlm_utils::simple_target_socket<port_fnct, DEFAULT_TLM_BUSWIDTH>
59{
60 SCP_LOGGER((), "register");
61 std::vector<std::shared_ptr<tlm_fnct>> m_pre_read_fncts;
62 std::vector<std::shared_ptr<tlm_fnct>> m_pre_write_fncts;
63 std::vector<std::shared_ptr<tlm_fnct>> m_post_read_fncts;
64 std::vector<std::shared_ptr<tlm_fnct>> m_post_write_fncts;
65 cci::cci_param<bool> p_is_callback;
66 bool m_in_callback = false;
67
68 std::function<unsigned int(tlm::tlm_generic_payload&)> transport_dbg_func;
69
70 std::string my_name()
71 {
72 std::string n(name());
73 n = n.substr(0, n.length() - strlen("_target_socket")); // get rid of last part of string
74 n = n.substr(n.find_last_of(".") + 1); // take the last part.
75 return n;
76 }
77
78 /* to be implemented !!!!*/
79 unsigned int transport_dbg(tlm::tlm_generic_payload& txn)
80 {
81 if (transport_dbg_func) {
82 return transport_dbg_func(txn);
83 } else
84 return 0;
85 }
86
87 void b_transport(tlm::tlm_generic_payload& txn, sc_core::sc_time& delay)
88 {
89 if (m_pre_read_fncts.size() || m_pre_write_fncts.size() || m_post_read_fncts.size() ||
90 m_post_write_fncts.size()) {
91 if (!m_in_callback) {
92 m_in_callback = true;
93 switch (txn.get_response_status()) {
94 case tlm::TLM_INCOMPLETE_RESPONSE:
95 switch (txn.get_command()) {
96 case tlm::TLM_READ_COMMAND:
97 if (m_pre_read_fncts.size()) {
98 SCP_INFO(())("Pre-Read callbacks: {}", my_name());
99 for (auto& cb : m_pre_read_fncts) (*cb)(txn, delay);
100 }
101 break;
102 case tlm::TLM_WRITE_COMMAND:
103 if (m_pre_write_fncts.size()) {
104 SCP_INFO(())("Pre-Write callbacks: {}", my_name());
105 for (auto& cb : m_pre_write_fncts) (*cb)(txn, delay);
106 }
107 break;
108 default:
109 break;
110 }
111 capture_txn_pre(txn);
112 break;
113 case tlm::TLM_OK_RESPONSE:
114 handle_mask_post(txn);
115 switch (txn.get_command()) {
116 case tlm::TLM_READ_COMMAND:
117 if (m_post_read_fncts.size()) {
118 SCP_INFO(())("Post-Read callbacks: {}", my_name());
119 for (auto& cb : m_post_read_fncts) (*cb)(txn, delay);
120 }
121 break;
122 case tlm::TLM_WRITE_COMMAND:
123 if (m_post_write_fncts.size()) {
124 SCP_INFO(())("Post-Write callbacks: {}", my_name());
125 for (auto& cb : m_post_write_fncts) (*cb)(txn, delay);
126 }
127 break;
128 default:
129 break;
130 }
131 break;
132 default:
133 break;
134 }
135 m_in_callback = false;
136 }
137 }
138 txn.set_dmi_allowed(false);
139 }
140
141 /*template <typename T>
142 void is_a_do(std::shared_ptr<tlm_fnct> cb, std::function<void()> fn)
143 {
144 auto cbt = dynamic_cast<T*>(cb.get());
145 if (cbt) fn();
146 }*/
147
148public:
149 void pre_read(tlm_fnct::TLMFUNC cb) { m_pre_read_fncts.push_back(std::make_shared<tlm_fnct>(cb)); }
150 void pre_write(tlm_fnct::TLMFUNC cb) { m_pre_write_fncts.push_back(std::make_shared<tlm_fnct>(cb)); }
151 void post_read(tlm_fnct::TLMFUNC cb) { m_post_read_fncts.push_back(std::make_shared<tlm_fnct>(cb)); }
152 void post_write(tlm_fnct::TLMFUNC cb) { m_post_write_fncts.push_back(std::make_shared<tlm_fnct>(cb)); }
153 virtual void capture_txn_pre(tlm::tlm_generic_payload& txn) = 0;
154 virtual void handle_mask_post(tlm::tlm_generic_payload& txn) = 0;
155
156 port_fnct() = delete;
157 port_fnct(std::string name, std::string path_name)
159 , p_is_callback(path_name + ".target_socket.is_callback", true, "Is a callback (true)")
160 {
161 SCP_LOGGER_NAME().features[0] = parent(sc_core::sc_object::name()) + "." + name;
162 SCP_TRACE(())("Constructor");
163 p_is_callback = true;
164 p_is_callback.lock();
165 tlm_utils::simple_target_socket<port_fnct, DEFAULT_TLM_BUSWIDTH>::register_b_transport(this,
166 &port_fnct::b_transport);
167 tlm_utils::simple_target_socket<port_fnct, DEFAULT_TLM_BUSWIDTH>::register_transport_dbg(
168 this, &port_fnct::transport_dbg);
169 }
170
171 void register_transport_dbg_func(std::function<unsigned int(tlm::tlm_generic_payload&)> fn)
172 {
173 transport_dbg_func = fn;
174 }
175
176protected:
177 std::string parent(std::string name) { return name.substr(0, name.find_last_of('.')); }
178};
179
184template <class TYPE>
186{
187 scp::scp_logger_cache& SCP_LOGGER_NAME();
188 TYPE* m_dmi = nullptr;
189
190 void check_dmi()
191 {
192 tlm::tlm_generic_payload m_txn;
193 tlm::tlm_dmi m_dmi_data;
194 m_txn.set_byte_enable_length(0);
195 m_txn.set_dmi_allowed(false);
196 m_txn.set_command(tlm::TLM_IGNORE_COMMAND);
197 m_txn.set_data_ptr(nullptr);
198 m_txn.set_address(p_offset);
199 m_txn.set_data_length(sizeof(TYPE) * p_number);
200 m_txn.set_streaming_width(sizeof(TYPE) * p_number);
201 m_txn.set_response_status(tlm::TLM_INCOMPLETE_RESPONSE);
202 if (initiator_socket->get_direct_mem_ptr(m_txn, m_dmi_data)) {
203 uint64_t start = m_dmi_data.get_start_address();
204 unsigned char* ptr = m_dmi_data.get_dmi_ptr();
205 ptr += (p_offset - start);
206 sc_assert(m_dmi_data.get_end_address() >= start + p_offset + (p_number * sizeof(TYPE)));
207 m_dmi = reinterpret_cast<TYPE*>(ptr);
208 }
209 }
210
211public:
212 std::string m_path_name;
213 cci::cci_param<uint64_t> p_number;
214 cci::cci_param<uint64_t> p_offset;
215 cci::cci_param<uint64_t> p_size;
216 cci::cci_param<TYPE> p_mask;
217 cci::cci_param<bool> p_relative_addresses;
218
219 tlm_utils::simple_initiator_socket<proxy_data_array, DEFAULT_TLM_BUSWIDTH> initiator_socket;
220
221 void get(TYPE* dst, uint64_t idx = 0, uint64_t length = 1)
222 {
223 sc_assert(idx + length <= p_number);
224 if (m_dmi) {
225 memcpy(dst, &m_dmi[idx], sizeof(TYPE) * length);
226 SCP_TRACE(())("Got value (DMI) : [{:#x}]", fmt::join(std::vector<TYPE>(&dst[0], &dst[length]), ","));
227 } else {
228 tlm::tlm_generic_payload m_txn;
229 sc_core::sc_time dummy;
230 m_txn.set_byte_enable_length(0);
231 m_txn.set_dmi_allowed(false);
232 m_txn.set_command(tlm::TLM_READ_COMMAND);
233 m_txn.set_data_ptr(reinterpret_cast<unsigned char*>(dst));
234 m_txn.set_address(p_offset + (sizeof(TYPE) * idx));
235 m_txn.set_data_length(sizeof(TYPE) * length);
236 m_txn.set_streaming_width(sizeof(TYPE) * length);
237 m_txn.set_response_status(tlm::TLM_INCOMPLETE_RESPONSE);
238 initiator_socket->b_transport(m_txn, dummy);
239 sc_assert(m_txn.get_response_status() == tlm::TLM_OK_RESPONSE);
240 SCP_TRACE(())("Got value (transport) : [{:#x}]", fmt::join(std::vector<TYPE>(&dst[0], &dst[length]), ","));
241 if (m_txn.is_dmi_allowed()) {
242 check_dmi();
243 }
244 }
245 }
246
247 void set(TYPE* src, uint64_t idx = 0, uint64_t length = 1, bool use_mask = true)
248 {
249 sc_assert(idx + length <= p_number);
250 if (m_dmi) {
251 SCP_TRACE(())("Set value (DMI) : [{:#x}]", fmt::join(std::vector<TYPE>(&src[0], &src[length]), ","));
252 TYPE curr_val = m_dmi[idx];
253 if (!use_mask || (p_mask.get_value() == gs_full_mask<TYPE>())) {
254 memcpy(&m_dmi[idx], src, sizeof(TYPE) * length);
255 } else {
256 write_with_mask(src, reinterpret_cast<TYPE*>(&m_dmi[idx]), length);
257 }
258 } else {
259 tlm::tlm_generic_payload m_txn;
260 sc_core::sc_time dummy;
261 std::vector<unsigned char> curr_data;
262 if ((p_mask.get_value() == gs_full_mask<TYPE>()) || !use_mask) {
263 m_txn.set_data_ptr(reinterpret_cast<unsigned char*>(src));
264 } else {
265 curr_data.resize(sizeof(TYPE) * length);
266 get(reinterpret_cast<TYPE*>(curr_data.data()), idx, length);
267 write_with_mask(src, reinterpret_cast<TYPE*>(curr_data.data()), length);
268 m_txn.set_data_ptr(reinterpret_cast<unsigned char*>(curr_data.data()));
269 }
270 m_txn.set_byte_enable_length(0);
271 m_txn.set_dmi_allowed(false);
272 m_txn.set_command(tlm::TLM_WRITE_COMMAND);
273 m_txn.set_address(p_offset + (sizeof(TYPE) * idx));
274 m_txn.set_data_length(sizeof(TYPE) * length);
275 m_txn.set_streaming_width(sizeof(TYPE) * length);
276 m_txn.set_response_status(tlm::TLM_INCOMPLETE_RESPONSE);
277 SCP_TRACE(())("Set value (transport) : [{:#x}]", fmt::join(std::vector<TYPE>(&src[0], &src[length]), ","));
278 initiator_socket->b_transport(m_txn, dummy);
279 sc_assert(m_txn.get_response_status() == tlm::TLM_OK_RESPONSE);
280 if (m_txn.is_dmi_allowed()) {
281 check_dmi();
282 }
283 }
284 }
285
286 void write_with_mask(TYPE* src, TYPE* dst, uint64_t length)
287 {
288 if (!src) {
289 SCP_FATAL(())("write_with_mask(): src pointer is NULL");
290 }
291 if (!dst) {
292 SCP_FATAL(())("write_with_mask(): dst pointer is NULL");
293 }
294 std::vector<TYPE> mask_to_apply(length, p_mask.get_value());
295 std::vector<TYPE> new_and_mask(length, 0); // result of *src & mask
296 std::vector<TYPE> old_and_not_mask(length, 0); // result of *dmi & ~mask
297 std::transform(src, src + length, mask_to_apply.cbegin(), new_and_mask.begin(),
298 [](TYPE src_val, TYPE mask_val) { return src_val & mask_val; });
299 std::transform(dst, dst + length, mask_to_apply.cbegin(), old_and_not_mask.begin(),
300 [](TYPE curr_val, TYPE mask_val) { return curr_val & (~mask_val); });
301 std::transform(
302 new_and_mask.cbegin(), new_and_mask.cend(), old_and_not_mask.cbegin(), dst,
303 [](TYPE new_and_mask_val, TYPE old_and_not_mask_val) { return new_and_mask_val | old_and_not_mask_val; });
304 }
305
306 TYPE& operator[](int idx)
307 {
308 if (!m_dmi) {
309 check_dmi();
310 if (!m_dmi) {
311 SCP_FATAL(())("Operator[] can only be used for DMI'able registers");
312 }
313 }
314 SCP_TRACE(())("Access value (DMI) using operator [] at idx {}", idx);
315 if (p_mask.get_value() != gs_full_mask<TYPE>()) {
316 SCP_FATAL(()) << "operator[](): Register Mask: 0x" << std::hex << p_mask.get_value()
317 << " has readonly bits, please use set(TYPE* src, uint64_t idx, uint64_t length) and "
318 "get(TYPE* dst, uint64_t idx, uint64_t length) instead";
319 }
320 return m_dmi[idx];
321 }
322 void invalidate_direct_mem_ptr(sc_dt::uint64 start, sc_dt::uint64 end) { m_dmi = nullptr; }
323
324 proxy_data_array(scp::scp_logger_cache& logger, std::string name, std::string path_name, uint64_t _offset = 0,
326 : SCP_LOGGER_NAME()(logger)
327 , m_path_name(path_name)
328 , p_number(path_name + ".number", number, "number of elements in this register")
329 , initiator_socket((name + "_initiator_socket").c_str())
330 , p_offset(path_name + ".target_socket.address", _offset, "Offset of this register")
331 , p_size(path_name + ".target_socket.size", sizeof(TYPE) * number, "size of this register")
332 , p_mask(path_name + ".target_socket.mask", mask, " R/W mask, 0 means read only, and 1 means write/read")
333 , p_relative_addresses(path_name + ".target_socket.relative_addresses", true,
334 "allow relative_addresses for this register")
335
336 {
337 initiator_socket.register_invalidate_direct_mem_ptr(this,
339 }
340};
341
346template <class TYPE = uint32_t>
347class proxy_data : public proxy_data_array<TYPE>
348{
349 scp::scp_logger_cache& SCP_LOGGER_NAME();
350
351public:
352 TYPE get()
353 {
354 TYPE tmp;
356 return tmp;
357 }
358 operator TYPE() { return get(); }
359
360 void set(TYPE value) { proxy_data_array<TYPE>::set(&value); }
361 void operator=(TYPE value) { set(value); }
362
363 proxy_data(scp::scp_logger_cache& logger, std::string name, std::string path_name, uint64_t offset = 0,
364 uint64_t number = 1, uint64_t start = 0, uint64_t length = sizeof(TYPE) * 8,
365 TYPE mask = gs_full_mask<TYPE>())
366 : proxy_data_array<TYPE>(logger, name, path_name, offset, number, mask), SCP_LOGGER_NAME()(logger)
367 {
368 static_assert(std::is_unsigned<TYPE>::value, "Register types must be unsigned");
369 }
370};
371
372/* forward declaration */
373template <class TYPE = uint32_t>
374class gs_bitfield;
375template <class TYPE = uint32_t>
376class gs_field;
382template <class TYPE = uint32_t>
383class gs_register : public port_fnct, public proxy_data<TYPE>
384{
385 SCP_LOGGER((), "register");
386
387private:
388 std::string m_regname;
389 std::string m_path;
390 std::vector<unsigned char> captured_txn_data;
391
392public:
393 gs_register() = delete;
394 gs_register(std::string _name, std::string path = "", uint64_t offset = 0, uint64_t number = 1,
395 TYPE mask = gs_full_mask<TYPE>())
396 : m_regname(_name)
397 , m_path(path)
399 , proxy_data<TYPE>(SCP_LOGGER_NAME(), _name, path, offset, number, mask)
400 {
401 std::string n(name());
402 SCP_LOGGER_NAME().features[0] = parent(n) + "." + _name;
403 n = n.substr(0, n.length() - strlen("_target_socket")); // get rid of last part of string
404 SCP_TRACE((), n)("constructor : {} attching in {}", _name, path);
405
406 register_transport_dbg_func([&](tlm::tlm_generic_payload& txn) {
407 if (txn.get_data_length() < sizeof(TYPE)) return (unsigned int)0;
408 unsigned char* data = txn.get_data_ptr();
409 if (txn.get_command() == tlm::TLM_READ_COMMAND) {
410 TYPE tmp = proxy_data<TYPE>::get();
411 memset(data, 0, txn.get_data_length());
412 memcpy(data, &tmp, sizeof(TYPE));
413 return (unsigned int)sizeof(TYPE);
414 }
415 if (txn.get_command() == tlm::TLM_WRITE_COMMAND) {
416 TYPE tmp;
417 memcpy(&tmp, data, sizeof(TYPE));
418 proxy_data<TYPE>::set(tmp);
419 return (unsigned int)sizeof(TYPE);
420 }
421 return (unsigned int)0;
422 });
423 }
424 void operator=(TYPE value) { proxy_data<TYPE>::set(value); }
425 operator TYPE() { return proxy_data<TYPE>::get(); }
426
428 void operator+=(TYPE other) { proxy_data<TYPE>::set(proxy_data<TYPE>::get() + other); }
429 void operator-=(TYPE other) { proxy_data<TYPE>::set(proxy_data<TYPE>::get() - other); }
430 void operator/=(TYPE other)
431 {
432 if (other == 0) SCP_FATAL(())("Trying to divide a register value by 0!");
434 }
435 void operator*=(TYPE other) { proxy_data<TYPE>::set(proxy_data<TYPE>::get() * other); }
436 void operator&=(TYPE other) { proxy_data<TYPE>::set(proxy_data<TYPE>::get() & other); }
437 void operator|=(TYPE other) { proxy_data<TYPE>::set(proxy_data<TYPE>::get() | other); }
438 void operator^=(TYPE other) { proxy_data<TYPE>::set(proxy_data<TYPE>::get() ^ other); }
440 void operator>>=(TYPE other) { proxy_data<TYPE>::set(proxy_data<TYPE>::get() >> other); }
441
442 TYPE& operator[](int idx) { return proxy_data_array<TYPE>::operator[](idx); }
443 void get(TYPE* dst, uint64_t idx, uint64_t length) { return proxy_data_array<TYPE>::get(dst, idx, length); }
444 void set(TYPE* src, uint64_t idx, uint64_t length, bool use_mask = true)
445 {
447 }
448
449 gs_bitfield<TYPE> operator[](gs_field<TYPE>& f) { return gs_bitfield<TYPE>(*this, f); }
450 std::string get_regname() const { return m_regname; }
451 std::string get_path() const { return m_path; }
452 uint64_t get_offset() const { return proxy_data<TYPE>::p_offset.get_value(); }
453 uint64_t get_size() const { return proxy_data<TYPE>::p_size.get_value(); }
454 void set_mask(TYPE mask) // mask is allowed to be set to implement e.g. write-once semantics
455 {
456 SCP_TRACE(()) << "Set Mask to 0x" << std::hex << mask;
458 }
459 TYPE get_mask() const { return proxy_data<TYPE>::p_mask.get_value(); }
460 void capture_txn_pre(tlm::tlm_generic_payload& txn) override
461 {
463 (txn.get_command() != tlm::tlm_command::TLM_WRITE_COMMAND)) {
464 return;
465 }
466 unsigned int txn_data_len = txn.get_data_length();
467 if ((txn_data_len == 0) || (txn_data_len % sizeof(TYPE)))
468 SCP_FATAL(()) << "capture_txn_pre(): txn data length should be n * sizeof(TYPE) where n >= 1";
469 uint64_t txn_addr = txn.get_address();
470 uint64_t idx = 0;
472 idx = txn_addr / sizeof(TYPE);
473 } else {
474 idx = (txn_addr - proxy_data<TYPE>::p_offset.get_value()) / sizeof(TYPE);
475 }
476 captured_txn_data.clear();
477 captured_txn_data.resize(txn_data_len);
478 get(reinterpret_cast<TYPE*>(captured_txn_data.data()), idx, txn_data_len / sizeof(TYPE));
479 }
480 void handle_mask_post(tlm::tlm_generic_payload& txn) override
481 {
483 (txn.get_command() != tlm::tlm_command::TLM_WRITE_COMMAND)) {
484 return;
485 }
486 unsigned int txn_data_len = txn.get_data_length();
487 if ((txn_data_len == 0) || (txn_data_len % sizeof(TYPE)))
488 SCP_FATAL(()) << "handle_mask_post(): txn data length should be n * sizeof(TYPE) where n >= 1";
489 uint64_t txn_addr = txn.get_address();
490 uint64_t idx = 0;
492 idx = txn_addr / sizeof(TYPE);
493 } else {
494 idx = (txn_addr - proxy_data<TYPE>::p_offset.get_value()) / sizeof(TYPE);
495 }
496 unsigned char* txn_data_ptr = txn.get_data_ptr();
497 proxy_data<TYPE>::write_with_mask(reinterpret_cast<TYPE*>(txn_data_ptr),
498 reinterpret_cast<TYPE*>(captured_txn_data.data()),
499 txn_data_len / sizeof(TYPE));
500 set(reinterpret_cast<TYPE*>(captured_txn_data.data()), idx, txn_data_len / sizeof(TYPE), false);
501 memcpy(txn_data_ptr, captured_txn_data.data(), txn_data_len);
502 }
503};
504
509template <class TYPE>
511{
512 uint32_t start, length;
513 gs_register<TYPE>& m_reg;
514
515public:
516 gs_bitfield(gs_register<TYPE>& r, uint32_t s, uint32_t l): m_reg(r), start(s), length(l) {}
517
518 gs_bitfield(gs_register<TYPE>& r, gs_bitfield& f): m_reg(r), start(f.start), length(f.length) {}
519
520 void operator=(TYPE value)
521 {
522 sc_assert(value < (1ull << length));
523 m_reg &= ~(((1ull << length) - 1) << start);
524 m_reg |= value << start;
525 }
526 operator TYPE() { return (m_reg >> start) & ((1ull << length) - 1); }
527};
528
534template <class TYPE>
536{
537 SCP_LOGGER();
538 gs_register<TYPE>& m_reg;
539 uint32_t m_bit_start;
540 uint32_t m_bit_length;
541 gs_bitfield<TYPE> m_bitfield;
542
543public:
544 gs_field(gs_register<TYPE>& reg, std::string name, uint32_t bit_start = 0, uint32_t bit_length = sizeof(TYPE) * 8)
545 : m_reg(reg), m_bit_start(bit_start), m_bit_length(bit_length), m_bitfield(reg, m_bit_start, m_bit_length)
546 {
547 SCP_TRACE(())("gs_field constructor");
548 if (m_bit_length == 0) SCP_FATAL(())("Can't find bit length for {}", name);
549 }
550
551 void operator=(TYPE value) { m_bitfield = value; }
552 void operator=(gs_field<TYPE>& value) { m_bitfield = (TYPE)value; }
553
554 operator TYPE() { return m_bitfield; }
555
556 operator gs::gs_bitfield<TYPE>&() { return m_bitfield; }
557};
558
559} // namespace gs
560
561#endif // GS_REGISTERS_H
Definition target.h:160
Proxy for bitfield access to a register.
Definition registers.h:511
fields within registered encapsulated using a bitfield proxy. This field is constructed from a specif...
Definition registers.h:536
Class that encapsulates a 'register' that proxies it's data via a tlm interface, and uses callbacks (...
Definition registers.h:384
A class that encapsulates a simple target port and a set of b_transport lambda functions for pre/post...
Definition registers.h:59
A proxy data class that stores it's value using a b_transport interface Data is passed by pointer,...
Definition registers.h:186
A proxy data class that stores it's value using a b_transport interface Data is passed by value.
Definition registers.h:348
Provide a class to provide b_transport callback.
Definition registers.h:39
Tool which reads a Lua configuration file and sets parameters.
Definition biflow.cc:10