Skip to content

Commit 37ad285

Browse files
committed
phy: refactor UL-SCH multiplex
1 parent 611bf17 commit 37ad285

File tree

6 files changed

+307
-24
lines changed

6 files changed

+307
-24
lines changed

include/srsran/phy/upper/channel_processors/ulsch_demultiplex.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,36 @@ class ulsch_demultiplex
6666
/// Default destructor.
6767
virtual ~ulsch_demultiplex() = default;
6868

69+
/// \brief Demultiplexes HARQ-ACK information bits and CSI Part 1 report from the UL-SCH transmission.
70+
///
71+
/// \param[out] harq_ack Destination of HARQ-ACK information soft bits.
72+
/// \param[out] csi_part1 Destination CSI Part 1 report soft bits.
73+
/// \param[in] input Input soft bits to demultiplex.
74+
/// \param[in] config UL-SCH demultiplexing parameters.
75+
virtual void demultiplex_harq_ack_and_csi_part1(span<log_likelihood_ratio> harq_ack,
76+
span<log_likelihood_ratio> csi_part1,
77+
span<const log_likelihood_ratio> input,
78+
const configuration& config) = 0;
79+
80+
/// \brief Demultiplexes Share Channel (SCH) data and CSI Part 2 report from the UL-SCH transmission.
81+
///
82+
/// It demultiplexes HARQ-ACK information bits and CSI Part 1 from the UL-SCH.
83+
///
84+
/// \param[out] sch_data Destination of the resultant shared channel data soft bits.
85+
/// \param[out] csi_part2 Destination CSI Part 2 report soft bits.
86+
/// \param[in] input Input soft bits to demultiplex.
87+
/// \param[in] nof_enc_harq_ack_bits Number of HARQ-ACK information bits multiplexed in the PUSCH message. Parameter
88+
/// \f$O_\textup{HARQ-ACK}\f$.
89+
/// \param[in] nof_enc_csi_part1_bits Number of HARQ-ACK information bits multiplexed in the PUSCH message. Parameter
90+
/// \f$O_\textup{HARQ-ACK}\f$.
91+
/// \param[in] config UL-SCH demultiplexing parameters.
92+
virtual void demultiplex_sch_and_csi_part2(span<log_likelihood_ratio> sch_data,
93+
span<log_likelihood_ratio> csi_part2,
94+
span<const log_likelihood_ratio> input,
95+
unsigned nof_enc_harq_ack_bits,
96+
unsigned nof_csi_part1_bits,
97+
const configuration& config) = 0;
98+
6999
/// \brief Demultiplexes Uplink Shared Channel (UL-SCH).
70100
///
71101
/// Demultiplexes the different information fields from the UL-SCH transmission.

lib/phy/upper/channel_processors/pusch_processor_impl.cpp

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -234,24 +234,23 @@ void pusch_processor_impl::process(span<uint8_t> data,
234234

235235
// Prepare buffers.
236236
span<log_likelihood_ratio> sch_llr = span<log_likelihood_ratio>(temp_sch_llr).first(info.nof_ul_sch_bits.value());
237-
span<log_likelihood_ratio> harq_ack_llr =
238-
span<log_likelihood_ratio>(temp_harq_ack_llr).first(info.nof_harq_ack_bits.value());
239-
span<log_likelihood_ratio> csi_part1_llr =
240-
span<log_likelihood_ratio>(temp_csi_part1_llr).first(info.nof_csi_part1_bits.value());
241-
span<log_likelihood_ratio> csi_part2_llr =
242-
span<log_likelihood_ratio>(temp_csi_part2_llr).first(info.nof_csi_part2_bits.value());
243-
244-
// Demultiplex UL-SCH if any of UCI field is present.
245-
if ((pdu.uci.nof_harq_ack > 0) || (pdu.uci.nof_csi_part1 > 0) || (pdu.uci.nof_csi_part2 > 0)) {
246-
// Demultiplexes UL-SCH codeword.
247-
demultiplex->demultiplex(sch_llr, harq_ack_llr, csi_part1_llr, csi_part2_llr, codeword_llr, demux_config);
248-
} else {
249-
// Overwrite the view of the codeword.
250-
sch_llr = codeword_llr;
251-
}
252237

253-
// Process UCI.
254-
if (pdu.uci.nof_harq_ack || pdu.uci.nof_csi_part1 || pdu.uci.nof_csi_part2) {
238+
// Process UCI if HARQ-ACK or CSI reports are present.
239+
if ((pdu.uci.nof_harq_ack > 0) || (pdu.uci.nof_csi_part1 > 0)) {
240+
span<log_likelihood_ratio> harq_ack_llr =
241+
span<log_likelihood_ratio>(temp_harq_ack_llr).first(info.nof_harq_ack_bits.value());
242+
span<log_likelihood_ratio> csi_part1_llr =
243+
span<log_likelihood_ratio>(temp_csi_part1_llr).first(info.nof_csi_part1_bits.value());
244+
245+
// Depending on CSI Part 2 report.
246+
if (pdu.uci.nof_csi_part2 > 0) {
247+
// Demultiplex HARQ-ACK and CSI Part 1.
248+
demultiplex->demultiplex_harq_ack_and_csi_part1(harq_ack_llr, csi_part1_llr, codeword_llr, demux_config);
249+
} else {
250+
// Demultiplex SCH data, HARQ-ACK and CSI Part 1.
251+
demultiplex->demultiplex(sch_llr, harq_ack_llr, csi_part1_llr, {}, codeword_llr, demux_config);
252+
}
253+
255254
// Prepare UCI decoder configuration.
256255
uci_decoder::configuration uci_dec_config;
257256
uci_dec_config.modulation = pdu.mcs_descr.modulation;
@@ -266,11 +265,35 @@ void pusch_processor_impl::process(span<uint8_t> data,
266265
// Decode CSI Part 1.
267266
result_uci.csi_part1 = decode_uci_field(csi_part1_llr, pdu.uci.nof_csi_part1, uci_dec_config);
268267

269-
// Decode HARQ-ACK.
270-
result_uci.csi_part2 = decode_uci_field(csi_part2_llr, pdu.uci.nof_csi_part2, uci_dec_config);
268+
// If CSI Part 2 is enabled.
269+
if (pdu.uci.nof_csi_part2 > 0) {
270+
// Calculate the number of CSI Part 2 payload bits.
271+
unsigned nof_csi_part2 = pdu.uci.nof_csi_part2;
272+
273+
// Calculate the number of CSI Part 2 encoded bits.
274+
unsigned nof_enc_csi_part2 = info.nof_csi_part2_bits.value();
275+
276+
// Prepare view of CSI Part 2 report LLRs.
277+
span<log_likelihood_ratio> csi_part2_llr =
278+
span<log_likelihood_ratio>(temp_csi_part2_llr).first(nof_enc_csi_part2);
279+
280+
// Demultiplex SCH data and CSI Part 2 bits.
281+
demultiplex->demultiplex_sch_and_csi_part2(
282+
sch_llr, csi_part2_llr, codeword_llr, harq_ack_llr.size(), csi_part1_llr.size(), demux_config);
283+
284+
// Decode CSI Part 2.
285+
result_uci.csi_part2 = decode_uci_field(csi_part2_llr, nof_csi_part2, uci_dec_config);
286+
} else {
287+
// Otherwise, clear CSI Part 2 result.
288+
result_uci.csi_part2.status = uci_status::unknown;
289+
result_uci.csi_part2.payload.clear();
290+
}
271291

272292
// Report UCI if at least one field is present.
273293
notifier.on_uci(result_uci);
294+
} else {
295+
// Overwrite the view of the codeword to avoid copying SCH data.
296+
sch_llr = codeword_llr;
274297
}
275298

276299
// Decode codeword if present.

lib/phy/upper/channel_processors/ulsch_demultiplex_impl.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,113 @@ void ulsch_demultiplex_generic(FuncSchData& func_sch_
301301

302302
} // namespace
303303

304+
void ulsch_demultiplex_impl::demultiplex_harq_ack_and_csi_part1(span<log_likelihood_ratio> harq_ack,
305+
span<log_likelihood_ratio> csi_part1,
306+
span<const log_likelihood_ratio> input,
307+
const ulsch_demultiplex::configuration& config)
308+
{
309+
// Skip demultiplexing if no UCI is multiplexed.
310+
if (harq_ack.empty() && csi_part1.empty()) {
311+
return;
312+
}
313+
314+
unsigned nof_bits_per_re = get_bits_per_symbol(config.modulation) * config.nof_layers;
315+
316+
// Function to ignore input data.
317+
auto func_ignore = [&input, &nof_bits_per_re](bool is_reserved) {
318+
if (is_reserved) {
319+
return;
320+
}
321+
322+
input = input.last(input.size() - nof_bits_per_re);
323+
};
324+
325+
// Function to demultiplex HARQ-ACK bits.
326+
auto func_harq_ack = [&harq_ack, &input, &nof_bits_per_re]() {
327+
srsvec::copy(harq_ack.first(nof_bits_per_re), input.first(nof_bits_per_re));
328+
input = input.last(input.size() - nof_bits_per_re);
329+
harq_ack = harq_ack.last(harq_ack.size() - nof_bits_per_re);
330+
};
331+
332+
// Function to demultiplex CSI Part 1 bits.
333+
auto func_csi_part1 = [&csi_part1, &input, &nof_bits_per_re]() {
334+
srsvec::copy(csi_part1.first(nof_bits_per_re), input.first(nof_bits_per_re));
335+
input = input.last(input.size() - nof_bits_per_re);
336+
csi_part1 = csi_part1.last(csi_part1.size() - nof_bits_per_re);
337+
};
338+
339+
ulsch_demultiplex_generic(
340+
func_ignore, func_harq_ack, func_csi_part1, func_ignore, harq_ack.size(), csi_part1.size(), 0, config);
341+
342+
// Assert that input buffers have been consumed.
343+
srsran_assert(harq_ack.empty(), "{} soft bits have not been multiplexed for HARQ-ACK.", harq_ack.size());
344+
srsran_assert(csi_part1.empty(), "{} soft bits have not been multiplexed for CSI Part 1.", csi_part1.size());
345+
srsran_assert(input.empty(), "{} input soft bits have not been multiplexed.", input.size());
346+
}
347+
348+
void ulsch_demultiplex_impl::demultiplex_sch_and_csi_part2(span<log_likelihood_ratio> sch_data,
349+
span<log_likelihood_ratio> csi_part2,
350+
span<const log_likelihood_ratio> input,
351+
unsigned nof_enc_harq_ack_bits,
352+
unsigned nof_csi_part1_bits,
353+
const ulsch_demultiplex::configuration& config)
354+
{
355+
// Skip demultiplexing if no UCI is multiplexed.
356+
if ((nof_enc_harq_ack_bits == 0) && (nof_csi_part1_bits == 0) && csi_part2.empty()) {
357+
srsran_assert(sch_data.size() == input.size(),
358+
"SCH data number of soft bits (i.e. {}) must be equal to the number of input soft bits (i.e., {}).",
359+
sch_data.size(),
360+
input.size());
361+
srsvec::copy(sch_data, input);
362+
return;
363+
}
364+
365+
unsigned nof_bits_per_re = get_bits_per_symbol(config.modulation) * config.nof_layers;
366+
367+
// Function to ignore input data.
368+
auto func_ignore = [&input, &nof_bits_per_re]() { input = input.last(input.size() - nof_bits_per_re); };
369+
370+
// Function to demultiplex SCH data.
371+
auto func_sch_data = [&sch_data, &input, &nof_bits_per_re](bool is_reserved) {
372+
if (is_reserved) {
373+
srsvec::zero(sch_data.first(nof_bits_per_re));
374+
sch_data = sch_data.last(sch_data.size() - nof_bits_per_re);
375+
return;
376+
}
377+
378+
srsvec::copy(sch_data.first(nof_bits_per_re), input.first(nof_bits_per_re));
379+
input = input.last(input.size() - nof_bits_per_re);
380+
sch_data = sch_data.last(sch_data.size() - nof_bits_per_re);
381+
};
382+
383+
// Function to demultiplex CSI Part 2 bits.
384+
auto func_csi_part2 = [&csi_part2, &input, &nof_bits_per_re](bool is_reserved) {
385+
if (is_reserved) {
386+
srsvec::zero(csi_part2.first(nof_bits_per_re));
387+
csi_part2 = csi_part2.last(csi_part2.size() - nof_bits_per_re);
388+
return;
389+
}
390+
391+
srsvec::copy(csi_part2.first(nof_bits_per_re), input.first(nof_bits_per_re));
392+
input = input.last(input.size() - nof_bits_per_re);
393+
csi_part2 = csi_part2.last(csi_part2.size() - nof_bits_per_re);
394+
};
395+
396+
ulsch_demultiplex_generic(func_sch_data,
397+
func_ignore,
398+
func_ignore,
399+
func_csi_part2,
400+
nof_enc_harq_ack_bits,
401+
nof_csi_part1_bits,
402+
csi_part2.size(),
403+
config);
404+
405+
// Assert that input buffers have been consumed.
406+
srsran_assert(csi_part2.empty(), "{} soft bits have not been multiplexed for CSI Part 2.", csi_part2.size());
407+
srsran_assert(sch_data.empty(), "{} soft bits have not been multiplexed for SCH data.", sch_data.size());
408+
srsran_assert(input.empty(), "{} input soft bits have not been multiplexed.", input.size());
409+
}
410+
304411
void ulsch_demultiplex_impl::demultiplex(span<log_likelihood_ratio> sch_data,
305412
span<log_likelihood_ratio> harq_ack,
306413
span<log_likelihood_ratio> csi_part1,

lib/phy/upper/channel_processors/ulsch_demultiplex_impl.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,20 @@ namespace srsran {
1717
class ulsch_demultiplex_impl : public ulsch_demultiplex
1818
{
1919
public:
20+
// See interface for documentation.
21+
void demultiplex_harq_ack_and_csi_part1(span<log_likelihood_ratio> harq_ack,
22+
span<log_likelihood_ratio> csi_part1,
23+
span<const log_likelihood_ratio> input,
24+
const configuration& config) override;
25+
26+
// See interface for documentation.
27+
void demultiplex_sch_and_csi_part2(span<log_likelihood_ratio> sch_data,
28+
span<log_likelihood_ratio> csi_part2,
29+
span<const log_likelihood_ratio> input,
30+
unsigned nof_enc_harq_ack_bits,
31+
unsigned nof_csi_part1_bits,
32+
const configuration& config) override;
33+
2034
// See interface for documentation.
2135
void demultiplex(span<log_likelihood_ratio> sch_data,
2236
span<log_likelihood_ratio> harq_ack,

tests/unittests/phy/upper/channel_processors/ulsch_demultiplex_test.cpp

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class UlschDemultiplexFixture : public ::testing::TestWithParam<test_case_t>
8080

8181
std::shared_ptr<ulsch_demultiplex_factory> UlschDemultiplexFixture::factory = nullptr;
8282

83-
TEST_P(UlschDemultiplexFixture, FromVector)
83+
TEST_P(UlschDemultiplexFixture, AllFromVector)
8484
{
8585
const test_case_t& test_case = GetParam();
8686

@@ -144,6 +144,59 @@ TEST_P(UlschDemultiplexFixture, FromVector)
144144
ASSERT_EQ(nof_x_placeholders_max, expected_nof_x_placeholders);
145145
}
146146

147+
TEST_P(UlschDemultiplexFixture, HarqAckAndCsiPart1FromVector)
148+
{
149+
const test_case_t& test_case = GetParam();
150+
151+
// Load codeword input.
152+
std::vector<log_likelihood_ratio> input = test_case.input.read();
153+
154+
// Load HARQ-ACK output.
155+
std::vector<log_likelihood_ratio> expected_harq_ack = test_case.output_harq_ack.read();
156+
std::vector<log_likelihood_ratio> harq_ack(expected_harq_ack.size());
157+
158+
// Load CSI Part 1 output.
159+
std::vector<log_likelihood_ratio> expected_csi_part1 = test_case.output_csi_part1.read();
160+
std::vector<log_likelihood_ratio> csi_part1(expected_csi_part1.size());
161+
162+
// Demultiplex.
163+
demultiplexer->demultiplex_harq_ack_and_csi_part1(harq_ack, csi_part1, input, test_case.context.config);
164+
165+
// Verify results.
166+
ASSERT_EQ(span<const log_likelihood_ratio>(harq_ack), span<const log_likelihood_ratio>(expected_harq_ack));
167+
ASSERT_EQ(span<const log_likelihood_ratio>(csi_part1), span<const log_likelihood_ratio>(expected_csi_part1));
168+
}
169+
170+
TEST_P(UlschDemultiplexFixture, SchDataAndCsiPart2FromVector)
171+
{
172+
const test_case_t& test_case = GetParam();
173+
174+
// Load codeword input.
175+
std::vector<log_likelihood_ratio> input = test_case.input.read();
176+
177+
// Load SCH Data output.
178+
std::vector<log_likelihood_ratio> expected_sch_data = test_case.output_ulsch.read();
179+
std::vector<log_likelihood_ratio> sch_data(expected_sch_data.size());
180+
181+
// Get the number HARQ-ACK encoded bits.
182+
unsigned nof_enc_harq_ack_bits = test_case.context.msg_info.nof_enc_harq_ack_bits;
183+
184+
// Get the number CSI Part 1 encoded bits.
185+
unsigned nof_enc_csi_part1_bits = test_case.context.msg_info.nof_enc_csi_part1_bits;
186+
187+
// Load CSI Part 2 output.
188+
std::vector<log_likelihood_ratio> expected_csi_part2 = test_case.output_csi_part2.read();
189+
std::vector<log_likelihood_ratio> csi_part2(expected_csi_part2.size());
190+
191+
// Demultiplex.
192+
demultiplexer->demultiplex_sch_and_csi_part2(
193+
sch_data, csi_part2, input, nof_enc_harq_ack_bits, nof_enc_csi_part1_bits, test_case.context.config);
194+
195+
// Verify results.
196+
ASSERT_EQ(span<const log_likelihood_ratio>(sch_data), span<const log_likelihood_ratio>(expected_sch_data));
197+
ASSERT_EQ(span<const log_likelihood_ratio>(csi_part2), span<const log_likelihood_ratio>(expected_csi_part2));
198+
}
199+
147200
// Creates test suite that combines all possible parameters. Denote zero_correlation_zone exceeds the maximum by one.
148201
INSTANTIATE_TEST_SUITE_P(UlschDemultiplex, UlschDemultiplexFixture, ::testing::ValuesIn(ulsch_demultiplex_test_data));
149202

0 commit comments

Comments
 (0)