AnasSarkiz/usb-c-pd-brushed-motor

USB-C PD-powered motor controller with protected input, selectable 5/9/12 V buck supply, MCU qualification, PWM H-bridge drive, overvoltage sensing, regenerative energy-dump resistors, and safety interlocks.

Version
0.0.1-a48-cloud-pipeline9-unrouted-c41d79a-305b1e7a
License
unset
Stars
0

firmware/stusb4500_rx_test.c

#include "stusb4500_rx.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>

static unsigned checks;
#define CHECK(condition) do { ++checks; assert(condition); } while (0)
struct mock_bus {
  uint8_t bytes[31], protocol_status, protocol_status_after;
  uint32_t now_us, transfer_time_us, expected_deadline_us;
  unsigned calls, fail_call, change_prefix_byte;
  bool change_prefix;
};

static uint32_t now_us(void *context) {
  return ((struct mock_bus *)context)->now_us;
}

static bool read_register(void *context, const struct stusb_read *transfer) {
  struct mock_bus *mock = context;
  mock->calls++;
  CHECK(transfer->deadline_us == mock->expected_deadline_us);
  mock->now_us += mock->transfer_time_us;
  if (mock->calls == mock->fail_call) return false;
  if (mock->calls == 1) {
    CHECK(transfer->register_address == 0x16 && transfer->byte_count == 1);
    transfer->bytes[0] = mock->protocol_status;
  } else if (mock->calls == 2) {
    CHECK(transfer->register_address == 0x30 && transfer->byte_count == 31);
    memcpy(transfer->bytes, mock->bytes, transfer->byte_count);
    if (mock->change_prefix) mock->bytes[mock->change_prefix_byte] ^= 1u;
  } else if (mock->calls == 3) {
    CHECK(transfer->register_address == 0x30 && transfer->byte_count == 3);
    memcpy(transfer->bytes, mock->bytes, transfer->byte_count);
  } else {
    CHECK(mock->calls == 4);
    CHECK(transfer->register_address == 0x16 && transfer->byte_count == 1);
    transfer->bytes[0] = mock->protocol_status_after;
  }
  return true;
}

static void set_header(struct mock_bus *mock, uint16_t header) {
  mock->bytes[0] = ((header >> 12) & 7u) * 4u;
  mock->bytes[1] = header & 255u;
  mock->bytes[2] = header >> 8;
}

static struct mock_bus source_capabilities(void) {
  struct mock_bus mock = {.protocol_status=4, .now_us=100, .transfer_time_us=200,
                          .expected_deadline_us=2600};
  set_header(&mock, (7u<<12) | (5u<<9) | 0x100u | (1u<<6) | 1u);
  const uint32_t pdos[] = {(100u<<10)|300u, (180u<<10)|300u,
                          (240u<<10)|300u, (300u<<10)|300u,
                          (400u<<10)|300u, 0xc1234567u, 0x81234567u};
  for (unsigned i=0; i<7; ++i)
    for (unsigned byte=0; byte<4; ++byte)
      mock.bytes[3+i*4+byte] = pdos[i] >> (8*byte);
  return mock;
}

static enum stusb_rx_result capture(struct mock_bus *mock, struct stusb_message *message) {
  const struct stusb_bus bus = {.context=mock, .read=read_register, .now_us=now_us};
  const struct stusb_capture request = {
    .alert_us=mock->expected_deadline_us-2500u, .message=message
  };
  return stusb_capture_message(&bus, &request);
}

static void check_zero_message(const struct stusb_message *message) {
  CHECK(message->header == 0 && message->object_count == 0 && message->captured_us == 0);
  for (unsigned i=0; i<7; ++i) CHECK(message->source_pdos[i] == 0);
}

int main(void) {
  struct mock_bus mock = source_capabilities();
  struct stusb_message message = {0};
  CHECK(capture(&mock, &message) == STUSB_RX_OK);
  CHECK(mock.calls == 4 && message.object_count == 7 && message.message_id == 5);
  CHECK(message.kind == STUSB_MESSAGE_SOURCE_CAPABILITIES);
  CHECK(message.alert_us == 100 && message.captured_us == 900);
  CHECK(message.source_pdos[0] == ((100u<<10)|300u));
  CHECK(message.source_pdos[5] == 0xc1234567u && message.source_pdos[6] == 0x81234567u);
  for (uint8_t selector=0; selector<3; ++selector) {
    const struct pd_plan plan = pd_make_plan(selector, &(struct pd_capabilities){.source_pdos=message.source_pdos, .count=message.object_count, .source_generation=77});
    CHECK(plan.valid && plan.source_generation == 77);
    CHECK(plan.source_object_position == (selector >= 1 ? 5 : 4));
    CHECK(plan.voltage_mv == (selector >= 1 ? 20000 : 15000));
  }
  /* A control header must never decode stale source-object bytes as PDOs. */
  const uint8_t controls[] = {3,4,6,12,13,1};
  const enum stusb_message_kind kinds[] = {STUSB_MESSAGE_ACCEPT, STUSB_MESSAGE_REJECT,
    STUSB_MESSAGE_PS_RDY, STUSB_MESSAGE_WAIT, STUSB_MESSAGE_SOFT_RESET, STUSB_MESSAGE_OTHER};
  for (unsigned i=0; i<sizeof controls; ++i) {
    mock = source_capabilities();
    set_header(&mock, 0x100u | (2u<<6) | controls[i]);
    mock.bytes[0] = 28; /* Control count may retain the prior data count. */
    CHECK(capture(&mock, &message) == STUSB_RX_OK);
    CHECK(message.kind == kinds[i] && message.object_count == 0);
    for (unsigned j=0; j<7; ++j) CHECK(message.source_pdos[j] == 0);
  }
  for (unsigned failed=1; failed<=4; ++failed) {
    mock = source_capabilities(); mock.fail_call=failed;
    CHECK(capture(&mock, &message) == STUSB_RX_IO_ERROR);
    check_zero_message(&message);
  }
  for (unsigned byte=0; byte<3; ++byte) {
    mock = source_capabilities(); mock.change_prefix=true; mock.change_prefix_byte=byte;
    CHECK(capture(&mock, &message) == STUSB_RX_CHANGED);
    check_zero_message(&message);
  }
  mock = source_capabilities(); mock.protocol_status_after = 4;
  CHECK(capture(&mock, &message) == STUSB_RX_CHANGED); check_zero_message(&message);
  for (unsigned bit=0; bit<8; ++bit) {
    if (!(0x83u & (1u<<bit))) continue;
    mock = source_capabilities(); mock.protocol_status_after = 1u<<bit;
    CHECK(capture(&mock, &message) == STUSB_RX_RESET_OR_TX_ERROR); check_zero_message(&message);
  }
  mock = source_capabilities(); mock.bytes[0]--;
  CHECK(capture(&mock, &message) == STUSB_RX_MALFORMED); check_zero_message(&message);
  mock = source_capabilities(); mock.bytes[2] |= 0x80u;
  CHECK(capture(&mock, &message) == STUSB_RX_UNSUPPORTED); check_zero_message(&message);
  mock = source_capabilities(); mock.bytes[1] &= ~0xc0u;
  CHECK(capture(&mock, &message) == STUSB_RX_UNSUPPORTED); check_zero_message(&message);
  mock = source_capabilities(); mock.bytes[1] |= 0xc0u;
  CHECK(capture(&mock, &message) == STUSB_RX_UNSUPPORTED); check_zero_message(&message);
  mock = source_capabilities(); mock.bytes[2] &= ~1u;
  CHECK(capture(&mock, &message) == STUSB_RX_MALFORMED); check_zero_message(&message);
  mock = source_capabilities(); mock.protocol_status=0;
  CHECK(capture(&mock, &message) == STUSB_RX_NO_MESSAGE); check_zero_message(&message);
  for (unsigned bit=0; bit<8; ++bit) {
    if (!(0x83u & (1u<<bit))) continue;
    mock = source_capabilities(); mock.protocol_status |= 1u<<bit;
    CHECK(capture(&mock, &message) == STUSB_RX_RESET_OR_TX_ERROR); check_zero_message(&message);
  }
  mock = source_capabilities(); mock.now_us=2601;
  CHECK(capture(&mock, &message) == STUSB_RX_LATE && mock.calls == 0);
  check_zero_message(&message);
  mock = source_capabilities(); mock.now_us=99; /* Future ALERT timestamp. */
  CHECK(capture(&mock, &message) == STUSB_RX_LATE); check_zero_message(&message);
  for (unsigned transfer=1; transfer<=4; ++transfer) {
    mock = source_capabilities(); mock.transfer_time_us=2501u/transfer+1u;
    CHECK(capture(&mock, &message) == STUSB_RX_LATE); check_zero_message(&message);
  }
  mock = source_capabilities(); mock.expected_deadline_us=2000;
  mock.now_us=UINT32_MAX-499u; /* Correct unsigned timer wrap. */
  CHECK(capture(&mock, &message) == STUSB_RX_OK);
  CHECK(message.captured_us == 300);
  struct stusb_capture request = {.message=&message};
  CHECK(stusb_capture_message(NULL, &request) == STUSB_RX_INVALID_ARGUMENT);
  check_zero_message(&message);
  CHECK(stusb_capture_message(NULL, NULL) == STUSB_RX_INVALID_ARGUMENT);
  printf("STUSB4500 RX host tests passed: %u assertions; target peripheral/handshake/hardware pending\n", checks);
}