Refactor of capture buffer management between cores.

Instead of copying data into and out of FIFO, passing buffer pointers between cores that are sized to match preferred/ideal SD card write size.
This commit is contained in:
Jared Boone 2016-05-10 14:12:37 -07:00
parent 85a6ecd5dc
commit b82eff51dd
10 changed files with 180 additions and 73 deletions

View File

@ -143,7 +143,7 @@ private:
RecordView record_view { RecordView record_view {
{ 0 * 8, 2 * 16, 30 * 8, 1 * 16 }, { 0 * 8, 2 * 16, 30 * 8, 1 * 16 },
"AUD_????", RecordView::FileType::WAV, 12, 2, "AUD_????", RecordView::FileType::WAV, 4096, 4
}; };
spectrum::WaterfallWidget waterfall; spectrum::WaterfallWidget waterfall;

View File

@ -78,7 +78,7 @@ private:
RecordView record_view { RecordView record_view {
{ 0 * 8, 2 * 16, 30 * 8, 1 * 16 }, { 0 * 8, 2 * 16, 30 * 8, 1 * 16 },
"BBD_????", RecordView::FileType::RawS16, 14, 1, "BBD_????", RecordView::FileType::RawS16, 16384, 3
}; };
spectrum::WaterfallWidget waterfall; spectrum::WaterfallWidget waterfall;

View File

@ -31,20 +31,29 @@ public:
~StreamOutput(); ~StreamOutput();
size_t available() { size_t available() {
return fifo->len(); return fifo_buffers_full->len();
} }
size_t read(void* const data, const size_t length) { StreamBuffer* get_buffer() {
return fifo->out(reinterpret_cast<uint8_t*>(data), length); StreamBuffer* p { nullptr };
fifo_buffers_full->out(p);
return p;
} }
static FIFO<uint8_t>* fifo; bool release_buffer(StreamBuffer* const p) {
p->empty();
return fifo_buffers_empty->in(p);
}
static FIFO<StreamBuffer*>* fifo_buffers_empty;
static FIFO<StreamBuffer*>* fifo_buffers_full;
private: private:
CaptureConfig* const config; CaptureConfig* const config;
}; };
FIFO<uint8_t>* StreamOutput::fifo = nullptr; FIFO<StreamBuffer*>* StreamOutput::fifo_buffers_empty = nullptr;
FIFO<StreamBuffer*>* StreamOutput::fifo_buffers_full = nullptr;
StreamOutput::StreamOutput( StreamOutput::StreamOutput(
CaptureConfig* const config CaptureConfig* const config
@ -53,11 +62,13 @@ StreamOutput::StreamOutput(
shared_memory.baseband_queue.push_and_wait( shared_memory.baseband_queue.push_and_wait(
CaptureConfigMessage { config } CaptureConfigMessage { config }
); );
fifo = config->fifo; fifo_buffers_empty = config->fifo_buffers_empty;
fifo_buffers_full = config->fifo_buffers_full;
} }
StreamOutput::~StreamOutput() { StreamOutput::~StreamOutput() {
fifo = nullptr; fifo_buffers_full = nullptr;
fifo_buffers_empty = nullptr;
shared_memory.baseband_queue.push_and_wait( shared_memory.baseband_queue.push_and_wait(
CaptureConfigMessage { nullptr } CaptureConfigMessage { nullptr }
); );
@ -69,9 +80,9 @@ Thread* CaptureThread::thread = nullptr;
CaptureThread::CaptureThread( CaptureThread::CaptureThread(
std::unique_ptr<Writer> writer, std::unique_ptr<Writer> writer,
size_t write_size_log2, size_t write_size,
size_t buffer_count_log2 size_t buffer_count
) : config { write_size_log2, buffer_count_log2 }, ) : config { write_size, buffer_count },
writer { std::move(writer) } writer { std::move(writer) }
{ {
// Need significant stack for FATFS // Need significant stack for FATFS
@ -90,29 +101,22 @@ CaptureThread::~CaptureThread() {
void CaptureThread::check_fifo_isr() { void CaptureThread::check_fifo_isr() {
// TODO: Prevent over-signalling by transmitting a set of // TODO: Prevent over-signalling by transmitting a set of
// flags from the baseband core. // flags from the baseband core.
const auto fifo = StreamOutput::fifo; const auto fifo = StreamOutput::fifo_buffers_full;
if( fifo ) { if( fifo ) {
chEvtSignalI(thread, EVT_MASK_CAPTURE_THREAD); chEvtSignalI(thread, EVT_MASK_CAPTURE_THREAD);
} }
} }
msg_t CaptureThread::run() { msg_t CaptureThread::run() {
const size_t write_size = 1U << config.write_size_log2;
const auto write_buffer = std::make_unique<uint8_t[]>(write_size);
if( !write_buffer ) {
return false;
}
StreamOutput stream { &config }; StreamOutput stream { &config };
while( !chThdShouldTerminate() ) { while( !chThdShouldTerminate() ) {
if( stream.available() >= write_size ) { if( stream.available() ) {
if( stream.read(write_buffer.get(), write_size) != write_size ) { auto buffer = stream.get_buffer();
return false; if( !writer->write(buffer->data(), buffer->size()) ) {
}
if( !writer->write(write_buffer.get(), write_size) ) {
return false; return false;
} }
stream.release_buffer(buffer);
} else { } else {
chEvtWaitAny(EVT_MASK_CAPTURE_THREAD); chEvtWaitAny(EVT_MASK_CAPTURE_THREAD);
} }

View File

@ -40,8 +40,8 @@ class CaptureThread {
public: public:
CaptureThread( CaptureThread(
std::unique_ptr<Writer> writer, std::unique_ptr<Writer> writer,
size_t write_size_log2, size_t write_size,
size_t buffer_count_log2 size_t buffer_count
); );
~CaptureThread(); ~CaptureThread();

View File

@ -139,13 +139,13 @@ RecordView::RecordView(
const Rect parent_rect, const Rect parent_rect,
std::string filename_stem_pattern, std::string filename_stem_pattern,
const FileType file_type, const FileType file_type,
const size_t buffer_size_k, const size_t write_size,
const size_t buffer_count_k const size_t buffer_count
) : View { parent_rect }, ) : View { parent_rect },
filename_stem_pattern { filename_stem_pattern }, filename_stem_pattern { filename_stem_pattern },
file_type { file_type }, file_type { file_type },
buffer_size_k { buffer_size_k }, write_size { write_size },
buffer_count_k { buffer_count_k } buffer_count { buffer_count }
{ {
add_children({ { add_children({ {
&button_record, &button_record,
@ -222,7 +222,7 @@ void RecordView::start() {
button_record.set_bitmap(&bitmap_stop); button_record.set_bitmap(&bitmap_stop);
capture_thread = std::make_unique<CaptureThread>( capture_thread = std::make_unique<CaptureThread>(
std::move(writer), std::move(writer),
buffer_size_k, buffer_count_k write_size, buffer_count
); );
} }
} }

View File

@ -46,8 +46,8 @@ public:
const Rect parent_rect, const Rect parent_rect,
std::string filename_stem_pattern, std::string filename_stem_pattern,
FileType file_type, FileType file_type,
const size_t buffer_size_k, const size_t write_size,
const size_t buffer_count_k const size_t buffer_count
); );
~RecordView(); ~RecordView();
@ -73,8 +73,8 @@ private:
const std::string filename_stem_pattern; const std::string filename_stem_pattern;
const FileType file_type; const FileType file_type;
const size_t buffer_size_k; const size_t write_size;
const size_t buffer_count_k; const size_t buffer_count;
size_t sampling_rate { 0 }; size_t sampling_rate { 0 };
SignalToken signal_token_tick_second; SignalToken signal_token_tick_second;

View File

@ -146,6 +146,7 @@ CPPSRC = main.cpp \
proc_tpms.cpp \ proc_tpms.cpp \
proc_ert.cpp \ proc_ert.cpp \
proc_capture.cpp \ proc_capture.cpp \
stream_input.cpp \
dsp_squelch.cpp \ dsp_squelch.cpp \
clock_recovery.cpp \ clock_recovery.cpp \
packet_builder.cpp \ packet_builder.cpp \

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "stream_input.hpp"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
StreamInput::StreamInput(CaptureConfig* const config) :
fifo_buffers_empty { buffers_empty.data(), buffer_count_max_log2 },
fifo_buffers_full { buffers_full.data(), buffer_count_max_log2 },
config { config },
data { std::make_unique<uint8_t[]>(config->write_size * config->buffer_count) }
{
config->fifo_buffers_empty = &fifo_buffers_empty;
config->fifo_buffers_full = &fifo_buffers_full;
for(size_t i=0; i<config->buffer_count; i++) {
buffers[i] = { &(data.get()[i * config->write_size]), config->write_size };
fifo_buffers_empty.in(&buffers[i]);
}
}
size_t StreamInput::write(const void* const data, const size_t length) {
const uint8_t* p = static_cast<const uint8_t*>(data);
size_t written = 0;
while( written < length ) {
if( !active_buffer ) {
// We need an empty buffer...
if( !fifo_buffers_empty.out(active_buffer) ) {
// ...but none are available. Samples were dropped.
break;
}
}
const auto remaining = length - written;
written += active_buffer->write(&p[written], remaining);
if( active_buffer->is_full() ) {
if( !fifo_buffers_full.in(active_buffer) ) {
// FIFO is fuil of buffers, there's no place for this one.
// Bail out of the loop, and try submitting the buffer in the
// next pass.
// This should never happen if the number of buffers is less
// than the capacity of the FIFO.
break;
}
active_buffer = nullptr;
creg::m4txevent::assert();
}
}
config->baseband_bytes_received += length;
config->baseband_bytes_dropped += (length - written);
return written;
}

View File

@ -25,46 +25,29 @@
#include "message.hpp" #include "message.hpp"
#include "fifo.hpp" #include "fifo.hpp"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
#include <cstdint> #include <cstdint>
#include <cstddef> #include <cstddef>
#include <array>
#include <memory> #include <memory>
class StreamInput { class StreamInput {
public: public:
StreamInput(CaptureConfig* const config) : StreamInput(CaptureConfig* const config);
config { config },
K { config->write_size_log2 + config->buffer_count_log2 },
event_bytes_mask { (1UL << config->write_size_log2) - 1 },
data { std::make_unique<uint8_t[]>(1UL << K) },
fifo { data.get(), K }
{
config->fifo = &fifo;
}
size_t write(const void* const data, const size_t length) { size_t write(const void* const data, const size_t length);
const auto written = fifo.in(reinterpret_cast<const uint8_t*>(data), length);
const auto last_bytes_written = bytes_written;
bytes_written += written;
if( (bytes_written & event_bytes_mask) < (last_bytes_written & event_bytes_mask) ) {
creg::m4txevent::assert();
}
config->baseband_bytes_received += length;
config->baseband_bytes_dropped = config->baseband_bytes_received - bytes_written;
return written;
}
private: private:
CaptureConfig* const config; static constexpr size_t buffer_count_max_log2 = 3;
const size_t K; static constexpr size_t buffer_count_max = 1U << buffer_count_max_log2;
const uint64_t event_bytes_mask;
uint64_t bytes_written = 0; FIFO<StreamBuffer*> fifo_buffers_empty;
FIFO<StreamBuffer*> fifo_buffers_full;
std::array<StreamBuffer, buffer_count_max> buffers;
std::array<StreamBuffer*, buffer_count_max> buffers_empty;
std::array<StreamBuffer*, buffer_count_max> buffers_full;
StreamBuffer* active_buffer { nullptr };
CaptureConfig* const config { nullptr };
std::unique_ptr<uint8_t[]> data; std::unique_ptr<uint8_t[]> data;
FIFO<uint8_t> fifo;
}; };
#endif/*__STREAM_INPUT_H__*/ #endif/*__STREAM_INPUT_H__*/

View File

@ -24,6 +24,7 @@
#include <cstdint> #include <cstdint>
#include <cstddef> #include <cstddef>
#include <cstring>
#include <array> #include <array>
#include <functional> #include <functional>
#include <algorithm> #include <algorithm>
@ -411,21 +412,63 @@ public:
const iir_biquad_config_t audio_hpf_config; const iir_biquad_config_t audio_hpf_config;
}; };
// TODO: Put this somewhere else, or at least the implementation part.
class StreamBuffer {
uint8_t* data_;
size_t used_;
size_t capacity_;
public:
constexpr StreamBuffer(
void* const data = nullptr,
const size_t capacity = 0
) : data_ { static_cast<uint8_t*>(data) },
used_ { 0 },
capacity_ { capacity }
{
}
size_t write(const void* p, const size_t count) {
const auto copy_size = std::min(capacity_ - used_, count);
memcpy(&data_[used_], p, copy_size);
used_ += copy_size;
return copy_size;
}
bool is_full() const {
return used_ >= capacity_;
}
const void* data() const {
return data_;
}
size_t size() const {
return used_;
}
void empty() {
used_ = 0;
}
};
struct CaptureConfig { struct CaptureConfig {
const size_t write_size_log2; const size_t write_size;
const size_t buffer_count_log2; const size_t buffer_count;
uint64_t baseband_bytes_received; uint64_t baseband_bytes_received;
uint64_t baseband_bytes_dropped; uint64_t baseband_bytes_dropped;
FIFO<uint8_t>* fifo; FIFO<StreamBuffer*>* fifo_buffers_empty;
FIFO<StreamBuffer*>* fifo_buffers_full;
constexpr CaptureConfig( constexpr CaptureConfig(
const size_t write_size_log2, const size_t write_size,
const size_t buffer_count_log2 const size_t buffer_count
) : write_size_log2 { write_size_log2 }, ) : write_size { write_size },
buffer_count_log2 { buffer_count_log2 }, buffer_count { buffer_count },
baseband_bytes_received { 0 }, baseband_bytes_received { 0 },
baseband_bytes_dropped { 0 }, baseband_bytes_dropped { 0 },
fifo { nullptr } fifo_buffers_empty { nullptr },
fifo_buffers_full { nullptr }
{ {
} }