|
|
|
@ -22,7 +22,6 @@
|
|
|
|
|
#include "srsenb/hdr/stack/mac/scheduler.h"
|
|
|
|
|
#include "srsenb/hdr/stack/mac/scheduler_ue.h"
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <random>
|
|
|
|
|
#include <set>
|
|
|
|
|
#include <srslte/srslte.h>
|
|
|
|
@ -37,14 +36,46 @@
|
|
|
|
|
#include "srslte/phy/utils/debug.h"
|
|
|
|
|
#include "srslte/radio/radio.h"
|
|
|
|
|
|
|
|
|
|
/********************************************************
|
|
|
|
|
* Random Tester for Scheduler.
|
|
|
|
|
* Current Checks:
|
|
|
|
|
* - Check if users are only added during a PRACH TTI
|
|
|
|
|
* - Allocation (DCI+RBs) of users that no longer exist
|
|
|
|
|
* - RAR is scheduled within the RAR window
|
|
|
|
|
* - Msg3 checks:
|
|
|
|
|
* - scheduled/received at expected TTI
|
|
|
|
|
* - with the correct RNTI and without PDCCH alloc
|
|
|
|
|
* - unexpected msg3 arrival
|
|
|
|
|
* - Users without data to Tx cannot be allocated in UL
|
|
|
|
|
* - Retxs always take precedence
|
|
|
|
|
* - DCI:
|
|
|
|
|
* - collisions detected
|
|
|
|
|
* - mismatch between the union of all dcis and
|
|
|
|
|
* scheduler class aggregate dci value
|
|
|
|
|
* - Invalid BC SIB index or TBS
|
|
|
|
|
* - Harqs:
|
|
|
|
|
* - invalid pids scheduled
|
|
|
|
|
* - empty harqs scheduled
|
|
|
|
|
* - invalid harq TTI
|
|
|
|
|
* - consistent NCCE loc
|
|
|
|
|
* - invalid retx number
|
|
|
|
|
* - DL adaptive retx/new tx <=> PDCCH alloc
|
|
|
|
|
* ...
|
|
|
|
|
*******************************************************/
|
|
|
|
|
|
|
|
|
|
/***************************
|
|
|
|
|
* Setup Random generators
|
|
|
|
|
**************************/
|
|
|
|
|
// uint32_t const seed = std::random_device()();
|
|
|
|
|
uint32_t const seed = 2452071795; // time(NULL);
|
|
|
|
|
uint32_t const seed = 2452071795; // time(nullptr);
|
|
|
|
|
std::default_random_engine rand_gen(seed);
|
|
|
|
|
std::uniform_real_distribution<float> unif_dist(0, 1.0);
|
|
|
|
|
float randf()
|
|
|
|
|
|
|
|
|
|
float randf()
|
|
|
|
|
{
|
|
|
|
|
return unif_dist(rand_gen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t err_counter = 0;
|
|
|
|
|
uint32_t warn_counter = 0;
|
|
|
|
|
struct ue_stats_t {
|
|
|
|
@ -73,42 +104,26 @@ class log_tester : public srslte::log_filter
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
explicit log_tester(std::string layer) : srslte::log_filter(layer) {}
|
|
|
|
|
void error(const char* message, ...) __attribute__((format(printf, 2, 3)));
|
|
|
|
|
};
|
|
|
|
|
void log_tester::error(const char* message, ...)
|
|
|
|
|
{
|
|
|
|
|
if (level >= srslte::LOG_LEVEL_ERROR) {
|
|
|
|
|
char* args_msg = NULL;
|
|
|
|
|
va_list args;
|
|
|
|
|
va_start(args, message);
|
|
|
|
|
if (vasprintf(&args_msg, message, args) > 0)
|
|
|
|
|
all_log(srslte::LOG_LEVEL_ERROR, tti, args_msg);
|
|
|
|
|
va_end(args);
|
|
|
|
|
free(args_msg);
|
|
|
|
|
~log_tester() final
|
|
|
|
|
{
|
|
|
|
|
info("[TESTER] UE stats:\n");
|
|
|
|
|
for (auto& e : ue_stats) {
|
|
|
|
|
info("0x%x: {DL RBs: %lu, UL RBs: %lu}\n", e.first, e.second.nof_dl_rbs, e.second.nof_ul_rbs);
|
|
|
|
|
}
|
|
|
|
|
info("[TESTER] This was the seed: %u\n", seed);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
log_tester log_out("ALL");
|
|
|
|
|
|
|
|
|
|
void log_on_exit()
|
|
|
|
|
{
|
|
|
|
|
log_out.info("[TESTER] UE stats:\n");
|
|
|
|
|
for (auto& e : ue_stats) {
|
|
|
|
|
log_out.info("0x%x: {DL RBs: %lu, UL RBs: %lu}\n", e.first, e.second.nof_dl_rbs, e.second.nof_ul_rbs);
|
|
|
|
|
}
|
|
|
|
|
log_out.info("[TESTER] This was the seed: %u\n", seed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define Warning(fmt, ...) \
|
|
|
|
|
log_out.warning(fmt, ##__VA_ARGS__); \
|
|
|
|
|
warn_counter++;
|
|
|
|
|
#define TestError(fmt, ...) \
|
|
|
|
|
log_out.error(fmt, ##__VA_ARGS__); \
|
|
|
|
|
log_on_exit(); \
|
|
|
|
|
exit(-1);
|
|
|
|
|
#define CondError(cond, fmt, ...) \
|
|
|
|
|
if (cond) { \
|
|
|
|
|
log_out.error(fmt, ##__VA_ARGS__); \
|
|
|
|
|
log_on_exit(); \
|
|
|
|
|
exit(-1); \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -119,17 +134,15 @@ void log_on_exit()
|
|
|
|
|
struct sched_sim_args {
|
|
|
|
|
struct tti_event_t {
|
|
|
|
|
struct user_event_t {
|
|
|
|
|
uint32_t sr_data;
|
|
|
|
|
uint32_t dl_data;
|
|
|
|
|
uint32_t dl_nof_retxs;
|
|
|
|
|
user_event_t() : sr_data(0), dl_data(0), dl_nof_retxs(0) {}
|
|
|
|
|
uint32_t sr_data = 0;
|
|
|
|
|
uint32_t dl_data = 0;
|
|
|
|
|
uint32_t dl_nof_retxs = 0;
|
|
|
|
|
};
|
|
|
|
|
std::map<uint16_t, user_event_t> users;
|
|
|
|
|
bool new_user;
|
|
|
|
|
bool rem_user;
|
|
|
|
|
bool new_user = false;
|
|
|
|
|
bool rem_user = false;
|
|
|
|
|
uint32_t new_rnti;
|
|
|
|
|
uint32_t rem_rnti;
|
|
|
|
|
tti_event_t() : new_user(false), rem_user(false) {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::vector<tti_event_t> tti_events;
|
|
|
|
@ -150,8 +163,8 @@ struct sched_tester : public srsenb::sched {
|
|
|
|
|
bool has_ul_retx = false;
|
|
|
|
|
bool has_ul_newtx = false; ///< *no* retx, but has tx
|
|
|
|
|
bool ul_retx_got_delayed = false;
|
|
|
|
|
srsenb::sched_interface::ul_sched_data_t* ul_sched = NULL; // fast lookup
|
|
|
|
|
srsenb::sched_interface::dl_sched_data_t* dl_sched = NULL; // fast lookup
|
|
|
|
|
srsenb::sched_interface::ul_sched_data_t* ul_sched = nullptr; // fast lookup
|
|
|
|
|
srsenb::sched_interface::dl_sched_data_t* dl_sched = nullptr; // fast lookup
|
|
|
|
|
srsenb::dl_harq_proc dl_harqs[2 * FDD_HARQ_DELAY_MS];
|
|
|
|
|
srsenb::ul_harq_proc ul_harq;
|
|
|
|
|
};
|
|
|
|
@ -172,20 +185,18 @@ struct sched_tester : public srsenb::sched {
|
|
|
|
|
typedef std::map<uint16_t, tester_user_results>::iterator ue_it_t;
|
|
|
|
|
};
|
|
|
|
|
struct ue_info {
|
|
|
|
|
int prach_tti, rar_tti, msg3_tti;
|
|
|
|
|
int prach_tti = -1, rar_tti = -1, msg3_tti = -1;
|
|
|
|
|
srsenb::sched_interface::ue_bearer_cfg_t bearer_cfg;
|
|
|
|
|
srsenb::sched_interface::ue_cfg_t user_cfg;
|
|
|
|
|
uint32_t dl_data;
|
|
|
|
|
uint32_t ul_data;
|
|
|
|
|
ue_info() : prach_tti(-1), rar_tti(-1), msg3_tti(-1), dl_data(0), ul_data(0) {}
|
|
|
|
|
uint32_t dl_data = 0;
|
|
|
|
|
uint32_t ul_data = 0;
|
|
|
|
|
};
|
|
|
|
|
struct ack_info_t {
|
|
|
|
|
uint16_t rnti;
|
|
|
|
|
uint32_t tti;
|
|
|
|
|
bool dl_ack;
|
|
|
|
|
uint32_t retx_delay;
|
|
|
|
|
uint16_t rnti;
|
|
|
|
|
uint32_t tti;
|
|
|
|
|
bool dl_ack = false;
|
|
|
|
|
uint32_t retx_delay = 0;
|
|
|
|
|
srsenb::dl_harq_proc dl_harq;
|
|
|
|
|
ack_info_t() : dl_ack(false), retx_delay(0) {}
|
|
|
|
|
};
|
|
|
|
|
struct ul_ack_info_t {
|
|
|
|
|
uint16_t rnti;
|
|
|
|
@ -234,8 +245,8 @@ void sched_tester::add_user(uint16_t rnti,
|
|
|
|
|
info.user_cfg = ue_cfg_;
|
|
|
|
|
tester_ues.insert(std::make_pair(rnti, info));
|
|
|
|
|
|
|
|
|
|
if (ue_cfg(rnti, &ue_cfg_)) {
|
|
|
|
|
TestError("[TESTER] Registering new user rnti=0x%x to SCHED\n", rnti);
|
|
|
|
|
if (ue_cfg(rnti, &ue_cfg_) != SRSLTE_SUCCESS) {
|
|
|
|
|
TestError("[TESTER] Registering new user rnti=0x%x to SCHED\n", rnti)
|
|
|
|
|
}
|
|
|
|
|
dl_sched_rar_info_t rar_info = {};
|
|
|
|
|
rar_info.prach_tti = tti_data.tti_rx;
|
|
|
|
@ -312,12 +323,10 @@ void sched_tester::process_tti_args()
|
|
|
|
|
|
|
|
|
|
void sched_tester::before_sched()
|
|
|
|
|
{
|
|
|
|
|
typedef std::map<uint16_t, srsenb::sched_ue>::iterator it_t;
|
|
|
|
|
|
|
|
|
|
// check pending data buffers
|
|
|
|
|
for (it_t it = ue_db.begin(); it != ue_db.end(); ++it) {
|
|
|
|
|
uint16_t rnti = it->first;
|
|
|
|
|
srsenb::sched_ue* user = &it->second;
|
|
|
|
|
for (auto& it : ue_db) {
|
|
|
|
|
uint16_t rnti = it.first;
|
|
|
|
|
srsenb::sched_ue* user = &it.second;
|
|
|
|
|
tester_user_results d;
|
|
|
|
|
srsenb::ul_harq_proc* hul = user->get_ul_harq(tti_data.tti_tx_ul);
|
|
|
|
|
d.ul_pending_data = get_ul_buffer(rnti);
|
|
|
|
@ -326,9 +335,9 @@ void sched_tester::before_sched()
|
|
|
|
|
d.has_ul_retx = hul->has_pending_retx();
|
|
|
|
|
d.has_ul_tx = d.has_ul_retx or d.ul_pending_data > 0;
|
|
|
|
|
srsenb::dl_harq_proc* hdl = user->get_pending_dl_harq(tti_data.tti_tx_dl);
|
|
|
|
|
d.has_dl_retx = (hdl != NULL) and hdl->has_pending_retx(0, tti_data.tti_tx_dl);
|
|
|
|
|
d.has_dl_tx = (hdl != NULL) or (it->second.get_empty_dl_harq() != NULL and d.dl_pending_data > 0);
|
|
|
|
|
d.has_ul_newtx = not d.has_ul_retx and d.ul_pending_data > 0;
|
|
|
|
|
d.has_dl_retx = (hdl != nullptr) and hdl->has_pending_retx(0, tti_data.tti_tx_dl);
|
|
|
|
|
d.has_dl_tx = (hdl != nullptr) or (it.second.get_empty_dl_harq() != nullptr and d.dl_pending_data > 0);
|
|
|
|
|
d.has_ul_newtx = not d.has_ul_retx and d.ul_pending_data > 0;
|
|
|
|
|
tti_data.ue_data.insert(std::make_pair(rnti, d));
|
|
|
|
|
tti_data.total_ues.dl_pending_data += d.dl_pending_data;
|
|
|
|
|
tti_data.total_ues.ul_pending_data += d.ul_pending_data;
|
|
|
|
@ -449,15 +458,15 @@ void sched_tester::assert_no_empty_allocs()
|
|
|
|
|
{
|
|
|
|
|
// Test if allocations only take place for users with pending data or in RAR
|
|
|
|
|
for (auto& iter : tti_data.ue_data) {
|
|
|
|
|
uint16_t rnti = iter.first;
|
|
|
|
|
srsenb::sched_ue* user = &ue_db[rnti];
|
|
|
|
|
uint16_t rnti = iter.first;
|
|
|
|
|
// srsenb::sched_ue* user = &ue_db[rnti];
|
|
|
|
|
|
|
|
|
|
if (!iter.second.has_ul_tx and tti_data.ue_data[rnti].ul_sched != NULL and
|
|
|
|
|
if (!iter.second.has_ul_tx and tti_data.ue_data[rnti].ul_sched != nullptr and
|
|
|
|
|
tti_data.ue_data[rnti].ul_sched->needs_pdcch) {
|
|
|
|
|
// FIXME: This test does not work for adaptive re-tx
|
|
|
|
|
TestError("[TESTER] There was a user without data that got allocated in UL\n");
|
|
|
|
|
}
|
|
|
|
|
srsenb::ul_harq_proc* hul = user->get_ul_harq(tti_data.tti_tx_ul);
|
|
|
|
|
// srsenb::ul_harq_proc* hul = user->get_ul_harq(tti_data.tti_tx_ul);
|
|
|
|
|
iter.second.ul_retx_got_delayed = iter.second.has_ul_retx and iter.second.ul_harq.is_empty(0);
|
|
|
|
|
tti_data.total_ues.ul_retx_got_delayed |= iter.second.ul_retx_got_delayed;
|
|
|
|
|
// Retxs cannot give space to newtx allocations
|
|
|
|
@ -467,9 +476,8 @@ void sched_tester::assert_no_empty_allocs()
|
|
|
|
|
|
|
|
|
|
// There must be allocations if there is pending data/retxs.
|
|
|
|
|
bool no_dl_allocs = true;
|
|
|
|
|
for (std::map<uint16_t, tester_user_results>::iterator it = tti_data.ue_data.begin(); it != tti_data.ue_data.end();
|
|
|
|
|
++it) {
|
|
|
|
|
if (it->second.dl_sched != NULL) {
|
|
|
|
|
for (auto& it : tti_data.ue_data) {
|
|
|
|
|
if (it.second.dl_sched != nullptr) {
|
|
|
|
|
no_dl_allocs = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -629,13 +637,15 @@ void sched_tester::test_harqs()
|
|
|
|
|
}
|
|
|
|
|
for (const auto& ue : ue_db) {
|
|
|
|
|
const auto& hprev = tti_data.ue_data[ue.first].ul_harq;
|
|
|
|
|
if (not hprev.has_pending_ack())
|
|
|
|
|
if (not hprev.has_pending_ack()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
uint32_t i = 0;
|
|
|
|
|
for (; i < tti_data.sched_result_ul.nof_phich_elems; ++i) {
|
|
|
|
|
const auto& phich = tti_data.sched_result_ul.phich[i];
|
|
|
|
|
if (phich.rnti == ue.first)
|
|
|
|
|
if (phich.rnti == ue.first) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CondError(i == tti_data.sched_result_ul.nof_phich_elems,
|
|
|
|
|
"[TESTER] harq had pending ack but no phich was allocked\n");
|
|
|
|
@ -777,11 +787,11 @@ void sched_tester::test_collisions()
|
|
|
|
|
CondError(srslte_ra_dl_dci_to_grant(
|
|
|
|
|
&cfg.cell, &dl_sf, SRSLTE_TM1, false, &tti_data.sched_result_dl.data[i].dci, &grant) == SRSLTE_ERROR,
|
|
|
|
|
"Failed to decode PDSCH grant\n");
|
|
|
|
|
for (uint32_t i = 0; i < alloc_mask.size(); ++i) {
|
|
|
|
|
if (grant.prb_idx[0][i]) {
|
|
|
|
|
alloc_mask.set(i);
|
|
|
|
|
for (uint32_t j = 0; j < alloc_mask.size(); ++j) {
|
|
|
|
|
if (grant.prb_idx[0][j]) {
|
|
|
|
|
alloc_mask.set(j);
|
|
|
|
|
} else {
|
|
|
|
|
alloc_mask.reset(i);
|
|
|
|
|
alloc_mask.reset(j);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ((dl_allocs & alloc_mask).any()) {
|
|
|
|
@ -798,9 +808,9 @@ void sched_tester::test_collisions()
|
|
|
|
|
&cfg.cell, &dl_sf, SRSLTE_TM1, false, &tti_data.sched_result_dl.bc[i].dci, &grant) == SRSLTE_ERROR,
|
|
|
|
|
"Failed to decode PDSCH grant\n");
|
|
|
|
|
alloc_mask.reset();
|
|
|
|
|
for (uint32_t i = 0; i < alloc_mask.size(); ++i) {
|
|
|
|
|
if (grant.prb_idx[0][i]) {
|
|
|
|
|
alloc_mask.set(i);
|
|
|
|
|
for (uint32_t j = 0; j < alloc_mask.size(); ++j) {
|
|
|
|
|
if (grant.prb_idx[0][j]) {
|
|
|
|
|
alloc_mask.set(j);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ((dl_allocs & alloc_mask).any()) {
|
|
|
|
@ -816,11 +826,11 @@ void sched_tester::test_collisions()
|
|
|
|
|
CondError(srslte_ra_dl_dci_to_grant(
|
|
|
|
|
&cfg.cell, &dl_sf, SRSLTE_TM1, false, &tti_data.sched_result_dl.rar[i].dci, &grant) == SRSLTE_ERROR,
|
|
|
|
|
"Failed to decode PDSCH grant\n");
|
|
|
|
|
for (uint32_t i = 0; i < alloc_mask.size(); ++i) {
|
|
|
|
|
if (grant.prb_idx[0][i]) {
|
|
|
|
|
alloc_mask.set(i);
|
|
|
|
|
for (uint32_t j = 0; j < alloc_mask.size(); ++j) {
|
|
|
|
|
if (grant.prb_idx[0][j]) {
|
|
|
|
|
alloc_mask.set(j);
|
|
|
|
|
} else {
|
|
|
|
|
alloc_mask.reset(i);
|
|
|
|
|
alloc_mask.reset(j);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ((dl_allocs & alloc_mask).any()) {
|
|
|
|
@ -924,7 +934,7 @@ void sched_tester::ack_txs()
|
|
|
|
|
// for (auto it = ue_db.begin(); it != ue_db.end(); ++it) {
|
|
|
|
|
// uint16_t rnti = it->first;
|
|
|
|
|
// srsenb::ul_harq_proc* h = ue_db[rnti].get_ul_harq(tti_data.tti_rx);
|
|
|
|
|
// if (h != NULL and not h->is_empty()) {
|
|
|
|
|
// if (h != nullptr and not h->is_empty()) {
|
|
|
|
|
// ul_crc_info(tti_data.tti_rx, rnti, ack);
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
@ -974,7 +984,7 @@ void test_scheduler_rand(srsenb::sched_interface::cell_cfg_t cell_cfg, const sch
|
|
|
|
|
srsenb::sched_interface::dl_sched_res_t& sched_result_dl = tester.tti_data.sched_result_dl;
|
|
|
|
|
srsenb::sched_interface::ul_sched_res_t& sched_result_ul = tester.tti_data.sched_result_ul;
|
|
|
|
|
|
|
|
|
|
tester.init(NULL, &log_out);
|
|
|
|
|
tester.init(nullptr, &log_out);
|
|
|
|
|
tester.set_metric(&dl_metric, &ul_metric);
|
|
|
|
|
tester.cell_cfg(&cell_cfg);
|
|
|
|
|
|
|
|
|
@ -1005,6 +1015,7 @@ sched_sim_args rand_sim_params(const srsenb::sched_interface::cell_cfg_t& cell_c
|
|
|
|
|
float ul_sr_exps[] = {1, 4}; // log rand
|
|
|
|
|
float dl_data_exps[] = {1, 4}; // log rand
|
|
|
|
|
uint32_t max_nof_users = 500;
|
|
|
|
|
std::uniform_int_distribution<> connection_dur_dist(min_conn_dur, max_conn_dur);
|
|
|
|
|
|
|
|
|
|
bzero(&sim_args.ue_cfg, sizeof(srsenb::sched_interface::ue_cfg_t));
|
|
|
|
|
sim_args.ue_cfg.aperiodic_cqi_period = 40;
|
|
|
|
@ -1029,8 +1040,8 @@ sched_sim_args rand_sim_params(const srsenb::sched_interface::cell_cfg_t& cell_c
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < current_rntis.size(); ++i) {
|
|
|
|
|
uint32_t rnti = current_rntis[i][0];
|
|
|
|
|
for (auto& current_rnti : current_rntis) {
|
|
|
|
|
uint32_t rnti = current_rnti[0];
|
|
|
|
|
if (randf() < P_ul_sr) {
|
|
|
|
|
float exp = ul_sr_exps[0] + randf() * (ul_sr_exps[1] - ul_sr_exps[0]);
|
|
|
|
|
sim_args.tti_events[tti].users[rnti].sr_data = (uint32_t)pow(10, exp);
|
|
|
|
@ -1048,7 +1059,7 @@ sched_sim_args rand_sim_params(const srsenb::sched_interface::cell_cfg_t& cell_c
|
|
|
|
|
std::vector<uint32_t> elem(3);
|
|
|
|
|
elem[0] = rnti_start;
|
|
|
|
|
elem[1] = tti;
|
|
|
|
|
elem[2] = min_conn_dur + rand() % (max_conn_dur - min_conn_dur);
|
|
|
|
|
elem[2] = connection_dur_dist(rand_gen);
|
|
|
|
|
current_rntis.push_back(elem);
|
|
|
|
|
sim_args.tti_events[tti].new_user = true;
|
|
|
|
|
sim_args.tti_events[tti].new_rnti = rnti_start++;
|
|
|
|
@ -1058,11 +1069,10 @@ sched_sim_args rand_sim_params(const srsenb::sched_interface::cell_cfg_t& cell_c
|
|
|
|
|
return sim_args;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(int argc, char* argv[])
|
|
|
|
|
int main()
|
|
|
|
|
{
|
|
|
|
|
printf("[TESTER] This is the chosen seed: %u\n", seed);
|
|
|
|
|
/* initialize random seed: */
|
|
|
|
|
srand(seed);
|
|
|
|
|
uint32_t N_runs = 1, nof_ttis = 10240 + 10;
|
|
|
|
|
|
|
|
|
|
for (uint32_t n = 0; n < N_runs; ++n) {
|
|
|
|
|