Skip to content

USB CDC blocks the stratum task: 19% hashrate lost when plugged into a host (S3/C3) #810

Description

@Gheop

If you run an S3 or C3 board plugged into a computer, and you do not have a serial
monitor open, you are losing about 19% of your hash rate. One line fixes it.

Measurements

Same miner (T-Display-S3), before and after, read from the pool-side dashboard so
that nothing was attached to the serial port during the measurement:

before   255 kH/s   58.8 C
after    304 kH/s   62.8 C

The temperature is the giveaway: the die runs cooler while the miner is broken,
because it is genuinely doing less work.

Watching it degrade, again with no serial reader attached, one sample per 75 s:

280.7 kH/s  62.8 C
276.9 kH/s  62.8 C
255.0 kH/s  61.8 C
255.0 kH/s  60.8 C
256.2 kH/s  60.8 C
255.3 kH/s  60.8 C

It settles around 255. The five sibling miners on the same firmware, powered from a
plain USB supply rather than a host, held 300.5 throughout. And opening a serial
reader on the affected board brings it straight back to 304.

Cause

HWCDC.cpp in the Arduino core starts with:

static uint32_t tx_timeout_ms = 0;

and raises it as soon as a USB host enumerates the port:

tx_timeout_ms = 100;

HWCDC::write() then waits up to that timeout on the TX ring buffer:

if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
    return 0;
}
...
if(xRingbufferSend(tx_ring_buf, (void*) (buffer+so_far), max_size,
                   tx_timeout_ms / portTICK_PERIOD_MS) != pdTRUE){

A host that enumerates the port but never reads it lets that buffer fill up and stay
full. Every log line then costs the calling task up to 100 ms.

The reason this hits hash rate rather than just delaying logs is which task does the
logging. runStratumWorker prints on every job (Sending, Receiving, target,
coinbase, and so on) and is also the task that calls JobPush() to refill the
miners' queues. While it is blocked on Serial, it is not handing out work, so the
mining tasks drain their queue and idle.

That also explains why both paths drop together and in proportion: they are not slower,
they are being starved.

Why this is a common configuration

USB is how most of these boards are powered. Plugging one into a spare port on a
desktop or a laptop is an obvious thing to do, and there is nothing on screen to
suggest anything is wrong: the display shows a hash rate, the pool shows the worker,
and the number is simply lower than it should be. Users comparing boards, or comparing
against a closed-source firmware, would see a difference with no visible cause.

A plain USB charger does not enumerate a CDC endpoint, so it does not trigger this.
It needs a host.

Fix

#if ARDUINO_USB_CDC_ON_BOOT
  Serial.setTxTimeoutMs(0);
#endif

Writes become non-blocking: log lines are dropped when the buffer is full instead of
stalling the task that emitted them. Nothing else changes, and with a monitor attached
the logs are unaffected.

https://github.com/Gheop/NerdMiner_v2/tree/fix/usb-cdc-blocks-mining

Guarded by ARDUINO_USB_CDC_ON_BOOT, so boards using a UART bridge (CP2102, CH340)
compile and behave exactly as before. Builds verified for NerdminerV2 and
TTGO-T-Display.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions