Skip to content

Commit 0cb6340

Browse files
authored
Optimizes the implementation of the railcom driver for multi-channel adapters (#865)
- reduces the number of lock-unlock sequences needed for a packet allocation Adjusts for new overrides: - moves some symbols to public which different implementations will need - Adds a hook for the cutout behavior on a per-hardware basis. In Stm32 implementation: - Makes the time window for sampling resistor smaller during occupancy sample measurement. - Adds support for allocating feedback packets slightly earlier because this takes a lot of time. - removes redundant code when no cutout is present. === * Optimizes the implementation of the railcom driver for multi-channel adapters: - reduces the number of lock-unlock sequences needed for a packet allocation Adjusts for new overrides: - moves some symbols to public which different implementations will need - Adds a hook for the cutout behavior on a per-hardware basis. In Stm32 implementation: - Makes the time window for sampling resistor smaller during occupancy sample measurement. - Adds support for allocating feedback packets slightly earlier because this takes a lot of time. - removes redundant code when no cutout is present. * Fix whitespace. * Add an enum for the DccDecoder::CutoutState. Fix comments. * Update more references to cutout state. * Fix typo in comment.
1 parent c72fea2 commit 0cb6340

File tree

5 files changed

+167
-42
lines changed

5 files changed

+167
-42
lines changed

src/freertos_drivers/common/DccDecoder.hxx

Lines changed: 102 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,28 @@
3838
#include "dcc/PacketProcessor.hxx"
3939
#include "dcc/packet.h"
4040

41+
struct DccDecoderDefs
42+
{
43+
/// Allowed values of DccDecoder::cutoutState_.
44+
enum CutoutState : uint8_t
45+
{
46+
/// Next wakeup of the usec timer will be the beginning of the cutout
47+
/// (nominally 26 usec after the last 1 bit), when the cutout FET
48+
/// should be turned on.
49+
CUTOUT_BEGIN,
50+
/// Next wakeup of the cutout timer is the middle between the two
51+
/// windows.
52+
CUTOUT_MID,
53+
/// Next wakeup of the cutout timer is the end of the cutout, when the
54+
/// railcom fet should be switched off and the booster output be
55+
/// reenabled.
56+
CUTOUT_END,
57+
/// Alias for the cutout end, but do not call the railcomDriver and do
58+
/// not reset the module.
59+
CUTOUT_END_AFTER_DRIVER
60+
};
61+
}; // struct DccDecoderDefs
62+
4163
/**
4264
Device driver for decoding a DCC signal using a Timer resource.
4365
@@ -65,6 +87,8 @@
6587
- SAMPLE_PERIOD_CLOCKS
6688
- Q_SIZE
6789
- TICKS_PER_USEC
90+
- Output, which is a DccOutput compatible static object for creating
91+
Railcom cutout.
6892
- module_init()
6993
- module_enable()
7094
- module_disable()
@@ -84,6 +108,8 @@
84108
template <class Module> class DccDecoder : public Node
85109
{
86110
public:
111+
using Defs = DccDecoderDefs;
112+
87113
/// Constructor.
88114
///
89115
/// @param name name of device node, e.g. "/dev/dccdecode0"
@@ -113,6 +139,16 @@ public:
113139
/** Handles a software interrupt to FreeRTOS. */
114140
inline void os_interrupt_handler() __attribute__((always_inline));
115141

142+
/// How many usec the railcom has before the cutout (measured from the
143+
/// packet end 1 bit complete)
144+
static const auto RAILCOM_CUTOUT_PRE = 26;
145+
/// How many usec the railcom has to the middle of window (measured from the
146+
/// packet end 1 bit complete)
147+
static const auto RAILCOM_CUTOUT_MID = 185;
148+
/// How many usec the railcom has to the end of the window (measured from
149+
/// the packet end 1 bit complete)
150+
static const auto RAILCOM_CUTOUT_END = 471;
151+
116152
private:
117153
/** Read from a file or device.
118154
* @param file file reference for this device
@@ -224,7 +260,7 @@ private:
224260
/// colliding with cutout.
225261
bool prepCutout_ = false;
226262
/// Which window of the cutout we are in.
227-
uint32_t cutoutState_;
263+
Defs::CutoutState cutoutState_;
228264
/// Counts unique identifiers for DCC packets to be returned.
229265
uint32_t packetId_ = 0;
230266
/// How many times did we lose a DCC packet due to no buffer available.
@@ -239,16 +275,6 @@ private:
239275
/// DCC packet decoder state machine and internal state.
240276
dcc::DccDecoder decoder_ {Module::get_ticks_per_usec()};
241277

242-
/// How many usec the railcom has before the cutout (measured from the
243-
/// packet end 1 bit complete)
244-
static const auto RAILCOM_CUTOUT_PRE = 26;
245-
/// How many usec the railcom has to the middle of window (measured from the
246-
/// packet end 1 bit complete)
247-
static const auto RAILCOM_CUTOUT_MID = 185;
248-
/// How many usec the railcom has to the end of the window (measured from
249-
/// the packet end 1 bit complete)
250-
static const auto RAILCOM_CUTOUT_END = 471;
251-
252278
DISALLOW_COPY_AND_ASSIGN(DccDecoder);
253279
};
254280

@@ -324,9 +350,24 @@ __attribute__((optimize("-O3"))) void DccDecoder<Module>::interrupt_handler()
324350
debugLog_.add(new_value);
325351
#endif
326352
bool cutout_just_finished = false;
353+
Debug::DccDecodeInterrupts::set(false);
327354
decoder_.process_data(new_value);
355+
Debug::DccDecodeInterrupts::set(true);
356+
if (decoder_.state() == dcc::DccDecoder::DCC_END_OF_PREAMBLE)
357+
{
358+
// Resets these state bits in case the state machines have
359+
// diverged due to a bug.
360+
inCutout_ = false;
361+
prepCutout_ = false;
362+
cutoutState_ = DccDecoderDefs::CUTOUT_BEGIN;
363+
}
364+
if (decoder_.state() == dcc::DccDecoder::DCC_MAYBE_CUTOUT)
365+
{
366+
Debug::DccInCutoutPin::set(true);
367+
}
328368
if (decoder_.before_dcc_cutout())
329369
{
370+
Debug::RailComBeforeCutoutTiming::set(true);
330371
prepCutout_ = true;
331372
auto* p = decoder_.pkt();
332373
if (p)
@@ -345,11 +386,12 @@ __attribute__((optimize("-O3"))) void DccDecoder<Module>::interrupt_handler()
345386
true) // Module::NRZ_Pin::get())
346387
{
347388
//Debug::RailcomDriverCutout::set(true);
389+
Debug::RailComBeforeCutoutTiming::set(false);
348390
Module::set_cap_timer_time();
349391
Module::set_cap_timer_delay_usec(
350392
RAILCOM_CUTOUT_PRE + Module::time_delta_railcom_pre_usec());
351393
inCutout_ = true;
352-
cutoutState_ = 0;
394+
cutoutState_ = DccDecoderDefs::CUTOUT_BEGIN;
353395
if (decoder_.pkt())
354396
{
355397
nextPacketFilled_ = true;
@@ -361,21 +403,23 @@ __attribute__((optimize("-O3"))) void DccDecoder<Module>::interrupt_handler()
361403
//railcomDriver_->start_cutout();
362404
//inCutout_ = true;
363405
}
364-
else if (decoder_.state() == dcc::DccDecoder::DCC_PACKET_FINISHED)
406+
else if ((decoder_.state() == dcc::DccDecoder::DCC_PACKET_FINISHED &&
407+
!inCutout_) ||
408+
(decoder_.state() == dcc::DccDecoder::DCC_PREAMBLE && prepCutout_ &&
409+
!inCutout_))
365410
{
411+
// The first partial bit after the cutout is done (for upstream
412+
// cutout), or the preamble bit which came after the
413+
// locally-generated cutout was finished.
366414
Debug::DccPacketFinishedHook::set(true);
367-
if (inCutout_) {
368-
//railcomDriver_->end_cutout();
369-
inCutout_ = false;
370-
}
371415
Module::dcc_packet_finished_hook();
372416
prepCutout_ = false;
373417
cutout_just_finished = true;
374418
Debug::DccPacketFinishedHook::set(false);
375419
}
376420
lastTimerValue_ = raw_new_value;
377-
if (sampleActive_ && Module::NRZ_Pin::get() && !prepCutout_ &&
378-
!cutout_just_finished)
421+
if (sampleActive_ // && !inCutout_
422+
&& !prepCutout_ && !cutout_just_finished && Module::NRZ_Pin::get())
379423
{
380424
sampleActive_ = false;
381425
// The first positive edge after the sample timer expired (but
@@ -384,6 +428,8 @@ __attribute__((optimize("-O3"))) void DccDecoder<Module>::interrupt_handler()
384428
Module::after_feedback_hook();
385429
}
386430
}
431+
Debug::DccInCutoutPin::set(inCutout_);
432+
Debug::DccDecodeInterrupts::set(false);
387433
}
388434

389435
template <class Module>
@@ -394,35 +440,67 @@ DccDecoder<Module>::rcom_interrupt_handler()
394440
if (Module::int_get_and_clear_delay_event())
395441
{
396442
// Debug::RailcomDriverCutout::set(false);
443+
Module::rcom_cutout_hook(&cutoutState_);
397444
switch (cutoutState_)
398445
{
399-
case 0:
446+
case Defs::CUTOUT_BEGIN:
400447
{
401448
Module::set_cap_timer_delay_usec(
402449
RAILCOM_CUTOUT_MID + Module::time_delta_railcom_mid_usec());
450+
if (Module::Output::need_railcom_cutout())
451+
{
452+
Debug::RailcomTurnonPhase1::set(true);
453+
unsigned delay_usec =
454+
Module::Output::start_railcom_cutout_phase1();
455+
Module::Output::isRailcomCutoutActive_ = 1;
456+
microdelay(delay_usec);
457+
Debug::RailcomTurnonPhase1::set(false);
458+
delay_usec = Module::Output::start_railcom_cutout_phase2();
459+
microdelay(delay_usec);
460+
}
403461
railcomDriver_->start_cutout();
404-
cutoutState_ = 1;
462+
cutoutState_ = Defs::CUTOUT_MID;
405463
break;
406464
}
407-
case 1:
465+
case Defs::CUTOUT_MID:
408466
{
409467
Module::set_cap_timer_delay_usec(
410468
RAILCOM_CUTOUT_END + Module::time_delta_railcom_end_usec());
411469
railcomDriver_->middle_cutout();
412-
cutoutState_ = 2;
470+
cutoutState_ = Defs::CUTOUT_END;
413471
break;
414472
}
415-
default:
473+
case Defs::CUTOUT_END:
416474
{
417475
Module::stop_cap_timer_time();
418476
Module::set_cap_timer_capture();
419477
railcomDriver_->end_cutout();
478+
} // fall through
479+
case Defs::CUTOUT_END_AFTER_DRIVER:
480+
{
481+
if (Module::Output::isRailcomCutoutActive_)
482+
{
483+
Debug::RailcomTurnonPhase1::set(true);
484+
unsigned delay_usec =
485+
Module::Output::stop_railcom_cutout_phase1();
486+
microdelay(delay_usec);
487+
Debug::RailcomTurnonPhase1::set(false);
488+
Module::Output::stop_railcom_cutout_phase2();
489+
Module::Output::isRailcomCutoutActive_ = 0;
490+
}
420491
inCutout_ = false;
492+
if (Module::Output::should_be_enabled())
493+
{
494+
Module::Output::enable_output();
495+
}
421496
break;
422497
}
498+
default:
499+
break;
423500
}
424501
}
425502
Debug::DccDecodeInterrupts::set(false);
503+
Debug::DccInCutoutPin::set(inCutout_);
426504
}
427505

428506
template <class Module>

src/freertos_drivers/common/FixedQueue.hxx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,27 @@ public:
134134
__atomic_fetch_add(&count_, 1, __ATOMIC_SEQ_CST);
135135
}
136136

137+
/// Checks if there is space in the back. If yes, allocates one entry as
138+
/// noncommit space and returns the pointer. If full, returns nullptr.
139+
/// @return nullptr if the queue is full, otherwise a noncommit entry.
140+
T *noncommit_back_or_null()
141+
{
142+
auto sz = size();
143+
if (sz >= SIZE)
144+
{
145+
return nullptr;
146+
}
147+
if (sz != 0 && rdIndex_ == wrIndex_)
148+
{
149+
// noncommit members make the queue full.
150+
return nullptr;
151+
}
152+
auto *ret = &storage_[wrIndex_];
153+
if (++wrIndex_ >= SIZE)
154+
wrIndex_ = 0;
155+
return ret;
156+
}
157+
137158
private:
138159
/// Payload of elements stored.
139160
T storage_[SIZE];

src/freertos_drivers/common/RailcomImpl.hxx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -222,26 +222,24 @@ public:
222222
}
223223
}
224224

225-
private:
225+
protected:
226226
void set_feedback_key(uint32_t key) OVERRIDE
227227
{
228228
feedbackKey_ = key;
229229
}
230230

231-
protected:
232231
/** Takes a new empty packet at the front of the queue, fills in feedback
233232
* key and channel information.
234233
* @param channel is which channel to set the packet for.
235234
* @return a packet pointer to write into. If there is no space, returns
236235
* nullptr.*/
237236
dcc::Feedback *alloc_new_packet(uint8_t channel)
238237
{
239-
if (!feedbackQueue_.has_noncommit_space())
238+
dcc::Feedback *entry = feedbackQueue_.noncommit_back_or_null();
239+
if (!entry)
240240
{
241241
return nullptr;
242242
}
243-
dcc::Feedback *entry = &feedbackQueue_.back();
244-
feedbackQueue_.noncommit_back();
245243
entry->reset(feedbackKey_);
246244
entry->channel = channel;
247245
return entry;

src/freertos_drivers/st/Stm32DCCDecoder.hxx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ struct DccDecoderHW
115115
template <class HW> class Stm32DccTimerModule
116116
{
117117
public:
118+
// Declaration matching the DccOutput static class pattern.
119+
using Output = typename HW::Output;
120+
118121
/// Exports the input pin to the driver on the module interface.
119122
using NRZ_Pin = typename HW::NRZ_Pin;
120123

@@ -261,6 +264,13 @@ public:
261264
//TIM_CCxChannelCmd(usec_timer(), HW::USEC_CHANNEL, TIM_CCx_DISABLE);
262265
}
263266

267+
/// Called during the railcom cutout when the railcom usec timer expires.
268+
/// The implementation is expected to modify cutout_state if the default
269+
/// sequence of handlers are not matching the desired
270+
/// functionality. Additional wakeups can be scheduled with
271+
/// set_cap_timer_delay_usec(...). An empty implementation is acceptable.
272+
static inline void rcom_cutout_hook(DccDecoderDefs::CutoutState *cutout_state);
273+
264274
private:
265275
static TIM_HandleTypeDef captureTimerHandle_;
266276
static TIM_HandleTypeDef usecTimerHandle_;
@@ -409,8 +419,8 @@ template <class HW> void Stm32DccTimerModule<HW>::module_enable()
409419
__HAL_TIM_DISABLE_IT(usec_timer_handle(), HW::USEC_IF);
410420

411421
#if defined(GCC_ARMCM0)
412-
HAL_NVIC_SetPriority(HW::CAPTURE_IRQn, 0, 0);
413-
HAL_NVIC_SetPriority(HW::TIMER_IRQn, 0, 0);
422+
HAL_NVIC_SetPriority(HW::CAPTURE_IRQn, 1, 0);
423+
HAL_NVIC_SetPriority(HW::TIMER_IRQn, 1, 0);
414424
HAL_NVIC_SetPriority(HW::OS_IRQn, 3, 0);
415425
#elif defined(GCC_ARMCM3)
416426
SetInterruptPriority(HW::CAPTURE_IRQn, 0x20);

0 commit comments

Comments
 (0)