mirror of
https://github.com/google/pebble.git
synced 2025-08-30 11:17:08 -04:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
99
tests/fw/drivers/test_battery.c
Normal file
99
tests/fw/drivers/test_battery.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
#include "drivers/battery.h"
|
||||
|
||||
static uint32_t prv_convert_millivolts_to_12bit_reading(int millivolts) {
|
||||
return 4095 * millivolts / 1800;
|
||||
}
|
||||
|
||||
#define VREF_VOLTAGE 1200
|
||||
|
||||
void test_battery__reading_conversion_boring(void) {
|
||||
ADCVoltageMonitorReading reading = {
|
||||
.vref_total = prv_convert_millivolts_to_12bit_reading(VREF_VOLTAGE),
|
||||
.vmon_total = prv_convert_millivolts_to_12bit_reading(1800)
|
||||
};
|
||||
|
||||
uint32_t result = battery_convert_reading_to_millivolts(reading, 1, 1);
|
||||
cl_assert_equal_i(result, 1800);
|
||||
|
||||
reading.vmon_total = prv_convert_millivolts_to_12bit_reading(1200);
|
||||
|
||||
result = battery_convert_reading_to_millivolts(reading, 1, 1);
|
||||
cl_assert_equal_i(result, 1200);
|
||||
|
||||
reading.vmon_total = prv_convert_millivolts_to_12bit_reading(0);
|
||||
|
||||
result = battery_convert_reading_to_millivolts(reading, 1, 1);
|
||||
cl_assert_equal_i(result, 0);
|
||||
}
|
||||
|
||||
void test_battery__reading_conversion_40_samples(void) {
|
||||
ADCVoltageMonitorReading reading = {
|
||||
.vref_total = prv_convert_millivolts_to_12bit_reading(VREF_VOLTAGE) * 40,
|
||||
.vmon_total = prv_convert_millivolts_to_12bit_reading(1800) * 40
|
||||
};
|
||||
|
||||
uint32_t result = battery_convert_reading_to_millivolts(reading, 1, 1);
|
||||
cl_assert_equal_i(result, 1800);
|
||||
|
||||
reading.vmon_total = prv_convert_millivolts_to_12bit_reading(1200) * 40;
|
||||
|
||||
result = battery_convert_reading_to_millivolts(reading, 1, 1);
|
||||
cl_assert_equal_i(result, 1200);
|
||||
|
||||
reading.vmon_total = prv_convert_millivolts_to_12bit_reading(0);
|
||||
|
||||
result = battery_convert_reading_to_millivolts(reading, 1, 1);
|
||||
cl_assert_equal_i(result, 0);
|
||||
}
|
||||
|
||||
// Make sure our new method for calculating battery usage matches the old one for stm32f2
|
||||
///////////////////////////////////////////////////////////
|
||||
static int prv_legacy_f2_calculation_millivolts(ADCVoltageMonitorReading reading) {
|
||||
return (int) ((reading.vmon_total * (2730) / reading.vref_total) * 295) / 256;
|
||||
}
|
||||
|
||||
void test_battery__reading_conversion_f2(void) {
|
||||
ADCVoltageMonitorReading reading = {
|
||||
.vref_total = prv_convert_millivolts_to_12bit_reading(VREF_VOLTAGE),
|
||||
.vmon_total = prv_convert_millivolts_to_12bit_reading(1800)
|
||||
};
|
||||
|
||||
uint32_t result = battery_convert_reading_to_millivolts(reading, 3599, 1373);
|
||||
cl_assert_equal_i(result, prv_legacy_f2_calculation_millivolts(reading));
|
||||
}
|
||||
|
||||
|
||||
// Make sure our new method for calculating battery usage matches the old one for stm32f4
|
||||
///////////////////////////////////////////////////////////
|
||||
static int prv_legacy_f4_calculation_millivolts(ADCVoltageMonitorReading reading) {
|
||||
return (int) (((reading.vmon_total * (2730) / reading.vref_total) * 120) / 91);
|
||||
}
|
||||
|
||||
void test_battery__reading_conversion_f4(void) {
|
||||
ADCVoltageMonitorReading reading = {
|
||||
.vref_total = prv_convert_millivolts_to_12bit_reading(VREF_VOLTAGE),
|
||||
.vmon_total = prv_convert_millivolts_to_12bit_reading(1800)
|
||||
};
|
||||
|
||||
uint32_t result = battery_convert_reading_to_millivolts(reading, 3, 1);
|
||||
cl_assert_equal_i(result, prv_legacy_f4_calculation_millivolts(reading));
|
||||
}
|
||||
|
245
tests/fw/drivers/test_flash_api.c
Normal file
245
tests/fw/drivers/test_flash_api.c
Normal file
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
#include "fake_new_timer.h"
|
||||
#include "stubs_analytics.h"
|
||||
#include "stubs_freertos.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_mutex.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pebble_tasks.h"
|
||||
#include "stubs_prompt.h"
|
||||
#include "stubs_queue.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_stop.h"
|
||||
#include "stubs_task_watchdog.h"
|
||||
#include "stubs_worker_manager.h"
|
||||
|
||||
#include "drivers/flash.h"
|
||||
#include "drivers/flash/flash_impl.h"
|
||||
|
||||
void flash_api_reset_for_test(void);
|
||||
TimerID flash_api_get_erase_poll_timer_for_test(void);
|
||||
|
||||
|
||||
status_t return_success(void) {
|
||||
return S_SUCCESS;
|
||||
}
|
||||
|
||||
status_t return_error(void) {
|
||||
return E_ERROR;
|
||||
}
|
||||
|
||||
status_t flash_impl_init(bool coredump_mode) {
|
||||
return S_SUCCESS;
|
||||
}
|
||||
|
||||
void flash_impl_use(void) {}
|
||||
void flash_impl_release_many(uint32_t num_locks) {}
|
||||
|
||||
|
||||
int get_subsector_base_calls = 0;
|
||||
FlashAddress flash_impl_get_subsector_base_address(FlashAddress addr) {
|
||||
get_subsector_base_calls++;
|
||||
return addr & 0xffffff00;
|
||||
}
|
||||
|
||||
int get_sector_base_calls = 0;
|
||||
FlashAddress flash_impl_get_sector_base_address(FlashAddress addr) {
|
||||
get_sector_base_calls++;
|
||||
return addr & 0xfffff000;
|
||||
}
|
||||
|
||||
int erase_subsector_begin_calls = 0;
|
||||
status_t erase_subsector_begin_return = S_SUCCESS;
|
||||
status_t flash_impl_erase_subsector_begin(FlashAddress addr) {
|
||||
erase_subsector_begin_calls++;
|
||||
return erase_subsector_begin_return;
|
||||
}
|
||||
|
||||
int erase_sector_begin_calls = 0;
|
||||
status_t erase_sector_begin_return = S_SUCCESS;
|
||||
status_t flash_impl_erase_sector_begin(FlashAddress addr) {
|
||||
erase_sector_begin_calls++;
|
||||
return erase_sector_begin_return;
|
||||
}
|
||||
|
||||
int get_erase_status_calls = 0;
|
||||
status_t (*get_erase_status_fn)(void) = return_success;
|
||||
status_t flash_impl_get_erase_status(void) {
|
||||
get_erase_status_calls++;
|
||||
return get_erase_status_fn();
|
||||
}
|
||||
|
||||
int blank_check_subsector_calls = 0;
|
||||
status_t blank_check_subsector_return = S_FALSE;
|
||||
status_t flash_impl_blank_check_subsector(FlashAddress addr) {
|
||||
blank_check_subsector_calls++;
|
||||
return blank_check_subsector_return;
|
||||
}
|
||||
|
||||
int blank_check_sector_calls = 0;
|
||||
status_t blank_check_sector_return = S_FALSE;
|
||||
status_t flash_impl_blank_check_sector(FlashAddress addr) {
|
||||
blank_check_sector_calls++;
|
||||
return blank_check_sector_return;
|
||||
}
|
||||
|
||||
status_t flash_impl_enter_low_power_mode(void) {
|
||||
return S_SUCCESS;
|
||||
}
|
||||
|
||||
status_t flash_impl_exit_low_power_mode(void) {
|
||||
return S_SUCCESS;
|
||||
}
|
||||
|
||||
status_t flash_impl_erase_suspend(FlashAddress addr) {
|
||||
return S_SUCCESS;
|
||||
}
|
||||
|
||||
status_t flash_impl_erase_resume(FlashAddress addr) {
|
||||
return S_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t flash_impl_get_typical_subsector_erase_duration_ms(void) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
uint32_t flash_impl_get_typical_sector_erase_duration_ms(void) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
status_t flash_impl_get_write_status(void) {
|
||||
return E_UNKNOWN;
|
||||
}
|
||||
|
||||
status_t flash_impl_read_sync(void *buffer, FlashAddress addr, size_t len) {
|
||||
return E_UNKNOWN;
|
||||
}
|
||||
|
||||
status_t flash_impl_set_burst_mode(bool enable) {
|
||||
return E_UNKNOWN;
|
||||
}
|
||||
|
||||
status_t flash_impl_unprotect(void) {
|
||||
return S_SUCCESS;
|
||||
}
|
||||
|
||||
int flash_impl_write_page_begin(const void *buffer, FlashAddress addr,
|
||||
size_t len) {
|
||||
return E_UNKNOWN;
|
||||
}
|
||||
|
||||
void flash_impl_enable_write_protection(void) {
|
||||
}
|
||||
|
||||
status_t flash_impl_write_protect(FlashAddress start_sector,
|
||||
FlashAddress end_sector) {
|
||||
return E_UNKNOWN;
|
||||
}
|
||||
|
||||
status_t flash_impl_set_nvram_erase_status(bool is_subsector,
|
||||
FlashAddress addr) {
|
||||
return S_SUCCESS;
|
||||
}
|
||||
|
||||
status_t flash_impl_clear_nvram_erase_status(void) {
|
||||
return S_SUCCESS;
|
||||
}
|
||||
|
||||
status_t flash_impl_get_nvram_erase_status(bool *is_subsector,
|
||||
FlashAddress *addr) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
void flash_erase_init(void) {
|
||||
}
|
||||
|
||||
|
||||
void *callback_context = NULL;
|
||||
status_t callback_status = -12345;
|
||||
static void callback(void *context, status_t status) {
|
||||
callback_context = context;
|
||||
callback_status = status;
|
||||
}
|
||||
|
||||
void test_flash_api__initialize(void) {
|
||||
callback_context = NULL;
|
||||
callback_status = -12345;
|
||||
|
||||
get_sector_base_calls = 0;
|
||||
get_subsector_base_calls = 0;
|
||||
erase_subsector_begin_calls = 0;
|
||||
erase_sector_begin_calls = 0;
|
||||
get_erase_status_calls = 0;
|
||||
get_erase_status_fn = return_success;
|
||||
blank_check_subsector_calls = 0;
|
||||
blank_check_sector_calls = 0;
|
||||
|
||||
flash_api_reset_for_test();
|
||||
flash_init();
|
||||
}
|
||||
|
||||
void test_flash_api__cleanup(void) {
|
||||
stub_new_timer_cleanup();
|
||||
}
|
||||
|
||||
void test_flash_api__erase_subsector_calls_right_impl_func(void) {
|
||||
flash_erase_subsector(0, callback, NULL);
|
||||
cl_assert_equal_i(erase_subsector_begin_calls, 1);
|
||||
cl_assert_equal_i(erase_sector_begin_calls, 0);
|
||||
}
|
||||
|
||||
void test_flash_api__erase_sector_calls_right_impl_func(void) {
|
||||
flash_erase_sector(0, callback, NULL);
|
||||
cl_assert_equal_i(erase_sector_begin_calls, 1);
|
||||
cl_assert_equal_i(erase_subsector_begin_calls, 0);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
status_t erase_status_return_error_once(void) {
|
||||
get_erase_status_fn = return_success;
|
||||
return E_ERROR;
|
||||
}
|
||||
|
||||
void test_flash_api__retry_erase_on_first_error(void) {
|
||||
get_erase_status_fn = erase_status_return_error_once;
|
||||
flash_erase_sector_blocking(0);
|
||||
cl_assert_equal_i(erase_sector_begin_calls, 2);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool uncorrectable_erase_error_cb_called = false;
|
||||
void uncorrectable_erase_error_cb(void *context, status_t result) {
|
||||
cl_assert_equal_i(E_ERROR, result);
|
||||
uncorrectable_erase_error_cb_called = true;
|
||||
}
|
||||
|
||||
void test_flash_api__handle_uncorrectable_erase_error(void) {
|
||||
get_erase_status_fn = return_error;
|
||||
TimerID erase_timer = flash_api_get_erase_poll_timer_for_test();
|
||||
flash_erase_sector(0, uncorrectable_erase_error_cb, NULL);
|
||||
int i;
|
||||
for (i = 0; i < 20 && !uncorrectable_erase_error_cb_called; ++i) {
|
||||
cl_assert(stub_new_timer_is_scheduled(erase_timer));
|
||||
stub_new_timer_fire(erase_timer);
|
||||
}
|
||||
cl_assert(i > 1 && i < 20);
|
||||
cl_assert_equal_i(uncorrectable_erase_error_cb_called, true);
|
||||
}
|
320
tests/fw/drivers/test_flash_erase.c
Normal file
320
tests/fw/drivers/test_flash_erase.c
Normal file
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/flash.h"
|
||||
#include "services/common/new_timer/new_timer.h"
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// Stubs
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
|
||||
// Fakes
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
typedef enum EraseCommandType {
|
||||
SectorEraseCommand = 1,
|
||||
SubsectorEraseCommand
|
||||
} EraseCommandType;
|
||||
|
||||
typedef struct EraseCommand {
|
||||
uint32_t addr;
|
||||
EraseCommandType type;
|
||||
} EraseCommand;
|
||||
|
||||
static EraseCommand s_command_list[32];
|
||||
static int s_command_list_index = 0;
|
||||
static int s_callback_called_count = 0;
|
||||
static status_t s_callback_status;
|
||||
static int s_simulate_flash_driver_error_countdown;
|
||||
static int s_simulate_work_queue_full_countdown;
|
||||
static bool s_erase_mutex_locked;
|
||||
|
||||
void prv_init_erase_mutex(void) {
|
||||
}
|
||||
|
||||
void prv_lock_erase_mutex(void) {
|
||||
cl_assert_equal_i(s_erase_mutex_locked, false);
|
||||
s_erase_mutex_locked = true;
|
||||
}
|
||||
|
||||
void prv_unlock_erase_mutex(void) {
|
||||
s_erase_mutex_locked = false;
|
||||
}
|
||||
|
||||
void flash_erase_subsector_blocking(uint32_t subsector_addr) {
|
||||
s_command_list[s_command_list_index++] = (EraseCommand) {
|
||||
.addr = subsector_addr,
|
||||
.type = SubsectorEraseCommand
|
||||
};
|
||||
}
|
||||
|
||||
void flash_erase_sector_blocking(uint32_t sector_addr) {
|
||||
s_command_list[s_command_list_index++] = (EraseCommand) {
|
||||
.addr = sector_addr,
|
||||
.type = SectorEraseCommand
|
||||
};
|
||||
}
|
||||
|
||||
void flash_erase_subsector(uint32_t subsector_addr, FlashOperationCompleteCb cb,
|
||||
void *context) {
|
||||
s_command_list[s_command_list_index++] = (EraseCommand) {
|
||||
.addr = subsector_addr,
|
||||
.type = SubsectorEraseCommand
|
||||
};
|
||||
cl_assert_equal_i(s_erase_mutex_locked, true);
|
||||
cb(context, (--s_simulate_flash_driver_error_countdown? S_SUCCESS : E_BUSY));
|
||||
}
|
||||
|
||||
void flash_erase_sector(uint32_t sector_addr, FlashOperationCompleteCb cb,
|
||||
void *context) {
|
||||
s_command_list[s_command_list_index++] = (EraseCommand) {
|
||||
.addr = sector_addr,
|
||||
.type = SectorEraseCommand
|
||||
};
|
||||
cl_assert_equal_i(s_erase_mutex_locked, true);
|
||||
cb(context, (--s_simulate_flash_driver_error_countdown? S_SUCCESS : E_BUSY));
|
||||
}
|
||||
|
||||
bool new_timer_add_work_callback(NewTimerWorkCallback cb, void *data) {
|
||||
if (--s_simulate_work_queue_full_countdown == 0) {
|
||||
return false;
|
||||
}
|
||||
cb(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void prv_assert_erase_commands(EraseCommand *commands) {
|
||||
int expected_command_count = 0;
|
||||
EraseCommand *cursor = commands;
|
||||
while ((cursor++)->type != 0) {
|
||||
++expected_command_count;
|
||||
}
|
||||
cl_assert_equal_i(s_command_list_index, expected_command_count);
|
||||
for (int i = 0; i < s_command_list_index; ++i) {
|
||||
cl_assert_equal_i(s_command_list[i].addr, commands[i].addr);
|
||||
cl_assert_equal_i(s_command_list[i].type, commands[i].type);
|
||||
}
|
||||
}
|
||||
|
||||
static int s_dummy_value = 42;
|
||||
static int *s_callback_ctx = &s_dummy_value;
|
||||
|
||||
static void prv_callback(void *context, status_t status) {
|
||||
cl_assert_equal_p(context, s_callback_ctx);
|
||||
cl_assert_equal_i(s_erase_mutex_locked, false);
|
||||
s_callback_status = status;
|
||||
s_callback_called_count++;
|
||||
}
|
||||
|
||||
static void prv_assert_callback_called(status_t expected_status) {
|
||||
cl_assert_equal_i(s_callback_called_count, 1);
|
||||
cl_assert_equal_i(s_callback_status, expected_status);
|
||||
}
|
||||
|
||||
static void prv_test_erase_optimal_range(
|
||||
uint32_t min_start, uint32_t max_start, uint32_t min_end, uint32_t max_end,
|
||||
EraseCommand *expected_commands) {
|
||||
flash_erase_optimal_range(min_start, max_start, min_end, max_end,
|
||||
prv_callback, s_callback_ctx);
|
||||
prv_assert_erase_commands(expected_commands);
|
||||
prv_assert_callback_called(
|
||||
s_command_list_index? S_SUCCESS : S_NO_ACTION_REQUIRED);
|
||||
}
|
||||
|
||||
// Tests
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
void test_flash_erase__initialize(void) {
|
||||
s_command_list_index = 0;
|
||||
s_callback_called_count = 0;
|
||||
s_callback_status = 42;
|
||||
s_simulate_work_queue_full_countdown = -1;
|
||||
s_simulate_flash_driver_error_countdown = -1;
|
||||
s_erase_mutex_locked = false;
|
||||
}
|
||||
|
||||
void test_flash_erase__cleanup(void) {
|
||||
}
|
||||
|
||||
void test_flash_erase__empty(void) {
|
||||
prv_test_erase_optimal_range(0, 0, 0, 0, (EraseCommand[]){ { } });
|
||||
}
|
||||
|
||||
void test_flash_erase__sectors_simple_1(void) {
|
||||
// Erase one sector 0x10000 - 0x20000
|
||||
prv_test_erase_optimal_range(
|
||||
64 * 1024, 64 * 1024, 2 * 64 * 1024, 2 * 64 * 1024,
|
||||
(EraseCommand[]) {
|
||||
{ 64 * 1024, SectorEraseCommand },
|
||||
{ },
|
||||
});
|
||||
}
|
||||
|
||||
void test_flash_erase__sectors_simple_2(void) {
|
||||
// Erase one sectors 0x10000 - 0x20000 but allow us to erase more
|
||||
prv_test_erase_optimal_range(
|
||||
0, 64 * 1024, 2 * 64 * 1024, 3 * 64 * 1024,
|
||||
(EraseCommand[]) {
|
||||
{ 64 * 1024, SectorEraseCommand },
|
||||
{ },
|
||||
});
|
||||
}
|
||||
|
||||
void test_flash_erase__two_sectors(void) {
|
||||
// Erase two sectors 0x10000 - 0x30000 but allow us to erase more
|
||||
prv_test_erase_optimal_range(
|
||||
0, 64 * 1024, 3 * 64 * 1024, 4 * 64 * 1024,
|
||||
(EraseCommand[]) {
|
||||
{ 64 * 1024, SectorEraseCommand },
|
||||
{ 2 * 64 * 1024, SectorEraseCommand },
|
||||
{ },
|
||||
});
|
||||
}
|
||||
|
||||
void test_flash_erase__subsectors_1(void) {
|
||||
// Offer a less than full sector range but erase the full range
|
||||
prv_test_erase_optimal_range(
|
||||
0, 4 * 1024, 64 * 1024, 64 * 1024,
|
||||
(EraseCommand[]) {
|
||||
{ 0, SectorEraseCommand },
|
||||
{ },
|
||||
});
|
||||
}
|
||||
|
||||
void test_flash_erase__sector_and_subsector(void) {
|
||||
// Offer a more than a full sector range, needs a sector and a subsector
|
||||
prv_test_erase_optimal_range(
|
||||
60 * 1024, 60 * 1024, 2 * 64 * 1024, 2 * 64 * 1024,
|
||||
(EraseCommand[]) {
|
||||
{ 60 * 1024, SubsectorEraseCommand },
|
||||
{ 64 * 1024, SectorEraseCommand },
|
||||
{ },
|
||||
});
|
||||
}
|
||||
|
||||
void test_flash_erase__subsectors_on_both_sides(void) {
|
||||
// Offer a more than a full sector range, needs subsectors on both sides
|
||||
prv_test_erase_optimal_range(
|
||||
60 * 1024, 60 * 1024, ((2 * 64) + 4) * 1024, ((2 * 64) + 8) * 1024,
|
||||
(EraseCommand[]) {
|
||||
{ 60 * 1024, SubsectorEraseCommand },
|
||||
{ 64 * 1024, SectorEraseCommand },
|
||||
{ 2 * 64 * 1024, SubsectorEraseCommand },
|
||||
{ },
|
||||
});
|
||||
}
|
||||
|
||||
// Various tests that look like erasing our 96k app resource banks
|
||||
|
||||
void test_flash_erase__96k_app_banks_1(void) {
|
||||
// App that's in an aligned bank but smaller than 64k
|
||||
prv_test_erase_optimal_range(
|
||||
0, 0, 32 * 1024, 96 * 1024,
|
||||
(EraseCommand[]) {
|
||||
{ 0, SectorEraseCommand },
|
||||
{ },
|
||||
});
|
||||
}
|
||||
|
||||
void test_flash_erase__96k_app_banks_2(void) {
|
||||
// App that's in an aligned bank but larger than than 64k
|
||||
prv_test_erase_optimal_range(
|
||||
0, 0, 69 * 1024, 96 * 1024,
|
||||
(EraseCommand[]) {
|
||||
{ 0, SectorEraseCommand },
|
||||
{ 64 * 1024, SubsectorEraseCommand },
|
||||
{ 68 * 1024, SubsectorEraseCommand },
|
||||
{ },
|
||||
});
|
||||
}
|
||||
|
||||
void test_flash_erase__96k_app_banks_3(void) {
|
||||
// App that's in an unaligned bank but smaller than 64k
|
||||
prv_test_erase_optimal_range(
|
||||
32 * 1024, 32 * 1024, (32 + 18) * 1024, (32 + 96) * 1024,
|
||||
(EraseCommand[]) {
|
||||
{ 32 * 1024, SubsectorEraseCommand },
|
||||
{ 36 * 1024, SubsectorEraseCommand },
|
||||
{ 40 * 1024, SubsectorEraseCommand },
|
||||
{ 44 * 1024, SubsectorEraseCommand },
|
||||
{ 48 * 1024, SubsectorEraseCommand },
|
||||
{ },
|
||||
});
|
||||
}
|
||||
|
||||
void test_flash_erase__96k_app_banks_4(void) {
|
||||
// App that's in an unaligned bank but larger than than 64k
|
||||
prv_test_erase_optimal_range(
|
||||
32 * 1024, 32 * 1024, (32 + 71) * 1024, (32 + 96) * 1024,
|
||||
(EraseCommand[]) {
|
||||
{ 32 * 1024, SubsectorEraseCommand },
|
||||
{ 36 * 1024, SubsectorEraseCommand },
|
||||
{ 40 * 1024, SubsectorEraseCommand },
|
||||
{ 44 * 1024, SubsectorEraseCommand },
|
||||
{ 48 * 1024, SubsectorEraseCommand },
|
||||
{ 52 * 1024, SubsectorEraseCommand },
|
||||
{ 56 * 1024, SubsectorEraseCommand },
|
||||
{ 60 * 1024, SubsectorEraseCommand },
|
||||
{ 64 * 1024, SectorEraseCommand },
|
||||
{ },
|
||||
});
|
||||
}
|
||||
|
||||
void test_flash_erase__watch_and_learn(void) {
|
||||
// Test cases stolen from Alvin's watch and learn app that originally hit this bug
|
||||
prv_test_erase_optimal_range(
|
||||
0x320000, 0x320000, 0x33177c, 0x338000,
|
||||
(EraseCommand[]) {
|
||||
{ 0x320000, SectorEraseCommand },
|
||||
{ 0x330000, SubsectorEraseCommand },
|
||||
{ 0x331000, SubsectorEraseCommand },
|
||||
{ },
|
||||
});
|
||||
}
|
||||
|
||||
void test_flash_erase__handle_work_queue_full(void) {
|
||||
s_simulate_work_queue_full_countdown = 3;
|
||||
flash_erase_optimal_range(
|
||||
32 * 1024, 32 * 1024, (32 + 71) * 1024, (32 + 96) * 1024,
|
||||
prv_callback, s_callback_ctx);
|
||||
prv_assert_callback_called(E_INTERNAL);
|
||||
prv_assert_erase_commands((EraseCommand[]) {
|
||||
{ 32 * 1024, SubsectorEraseCommand },
|
||||
{ 36 * 1024, SubsectorEraseCommand },
|
||||
{ 40 * 1024, SubsectorEraseCommand },
|
||||
{ },
|
||||
});
|
||||
}
|
||||
|
||||
void test_flash_erase__handle_flash_driver_error(void) {
|
||||
s_simulate_flash_driver_error_countdown = 3;
|
||||
flash_erase_optimal_range(
|
||||
32 * 1024, 32 * 1024, (32 + 71) * 1024, (32 + 96) * 1024,
|
||||
prv_callback, s_callback_ctx);
|
||||
prv_assert_callback_called(E_BUSY);
|
||||
prv_assert_erase_commands((EraseCommand[]) {
|
||||
{ 32 * 1024, SubsectorEraseCommand },
|
||||
{ 36 * 1024, SubsectorEraseCommand },
|
||||
{ 40 * 1024, SubsectorEraseCommand },
|
||||
{ },
|
||||
});
|
||||
}
|
83
tests/fw/drivers/test_fpc_pinstrap.c
Normal file
83
tests/fw/drivers/test_fpc_pinstrap.c
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
#include "board/board.h"
|
||||
#include "drivers/fpc_pinstrap.h"
|
||||
|
||||
static bool s_pin_pull_up_enabled[2] = { false, false };
|
||||
|
||||
void gpio_input_init_pull_up_down(const InputConfig *input_cfg, GPIOPuPd_TypeDef pupd) {
|
||||
s_pin_pull_up_enabled[input_cfg->gpio_pin] = (pupd == GPIO_PuPd_UP);
|
||||
}
|
||||
|
||||
void gpio_analog_init(const InputConfig *input_cfg) {
|
||||
}
|
||||
|
||||
static enum {
|
||||
PinstrapResult_GND,
|
||||
PinstrapResult_Vplus,
|
||||
PinstrapResult_Float
|
||||
} s_pinstrap_results[2];
|
||||
|
||||
bool gpio_input_read(const InputConfig *input_cfg) {
|
||||
switch (s_pinstrap_results[input_cfg->gpio_pin]) {
|
||||
case PinstrapResult_GND:
|
||||
return false;
|
||||
case PinstrapResult_Vplus:
|
||||
return true;
|
||||
case PinstrapResult_Float:
|
||||
return s_pin_pull_up_enabled[input_cfg->gpio_pin];
|
||||
}
|
||||
}
|
||||
|
||||
void test_fpc_pinstrap__simple(void) {
|
||||
s_pinstrap_results[0] = PinstrapResult_GND;
|
||||
|
||||
s_pinstrap_results[1] = PinstrapResult_GND;
|
||||
cl_assert(fpc_pinstrap_get_value() == 0x0);
|
||||
|
||||
s_pinstrap_results[1] = PinstrapResult_Vplus;
|
||||
cl_assert(fpc_pinstrap_get_value() == 0x1);
|
||||
|
||||
s_pinstrap_results[1] = PinstrapResult_Float;
|
||||
cl_assert(fpc_pinstrap_get_value() == 0x2);
|
||||
|
||||
|
||||
s_pinstrap_results[0] = PinstrapResult_Vplus;
|
||||
|
||||
s_pinstrap_results[1] = PinstrapResult_GND;
|
||||
cl_assert(fpc_pinstrap_get_value() == 0x3);
|
||||
|
||||
s_pinstrap_results[1] = PinstrapResult_Vplus;
|
||||
cl_assert(fpc_pinstrap_get_value() == 0x4);
|
||||
|
||||
s_pinstrap_results[1] = PinstrapResult_Float;
|
||||
cl_assert(fpc_pinstrap_get_value() == 0x5);
|
||||
|
||||
|
||||
s_pinstrap_results[0] = PinstrapResult_Float;
|
||||
|
||||
s_pinstrap_results[1] = PinstrapResult_GND;
|
||||
cl_assert(fpc_pinstrap_get_value() == 0x6);
|
||||
|
||||
s_pinstrap_results[1] = PinstrapResult_Vplus;
|
||||
cl_assert(fpc_pinstrap_get_value() == 0x7);
|
||||
|
||||
s_pinstrap_results[1] = PinstrapResult_Float;
|
||||
cl_assert(fpc_pinstrap_get_value() == 0x8);
|
||||
}
|
130
tests/fw/drivers/test_i2c_timingr.c
Normal file
130
tests/fw/drivers/test_i2c_timingr.c
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
#include "drivers/stm32f7/i2c_timingr.h"
|
||||
|
||||
#define MHZ_TO_HZ(val) ((val) * 1000000)
|
||||
#define KHZ_TO_HZ(val) ((val) * 1000)
|
||||
|
||||
|
||||
static void prv_check_result(uint32_t timingr, uint32_t expected_prescaler,
|
||||
uint32_t expected_scl_low, uint32_t expected_scl_high,
|
||||
uint32_t expected_scl_delay) {
|
||||
cl_assert_(timingr != I2C_TIMINGR_INVALID_VALUE, "timingr == I2C_TIMINGR_INVALID_VALUE");
|
||||
cl_assert_equal_i((timingr >> 28) + 1, expected_prescaler);
|
||||
cl_assert_equal_i((timingr & 0xFF) + 1, expected_scl_low);
|
||||
cl_assert_equal_i(((timingr >> 8) & 0xFF) + 1, expected_scl_high);
|
||||
cl_assert_equal_i(((timingr >> 20) & 0xF) + 1, expected_scl_delay);
|
||||
}
|
||||
|
||||
void test_i2c_timingr__valid_no_prescaler_no_rise_fall_time(void) {
|
||||
// We'll use a base clock speed of 36Mhz and try to get to 400kHz I2C. We should be able to do
|
||||
// this with a prescaler of 1.
|
||||
//
|
||||
// 36MHz / 400kHz = 90 cycles => 90 - 6 sync cycles = 84 cycles to play with
|
||||
// minimum low = ceil(1300ns / (1 / 36MHz)) = 47 cycles
|
||||
// minimum high = ceil(600ns / (1 / 36MHz)) = 22 cycles
|
||||
// extra cycles = 15 => (7 low, 8 high) => SCLL of 54 and SCLH of 30
|
||||
//
|
||||
// SCLDEL = ceil((t_r 0 + t_SU 100ns) / (1 / 36MHz)) = 4 cycles
|
||||
prv_check_result(i2c_timingr_calculate(MHZ_TO_HZ(36), I2CBusMode_FastMode,
|
||||
KHZ_TO_HZ(400), 0, 0),
|
||||
1, 54, 30, 4);
|
||||
}
|
||||
|
||||
void test_i2c_timingr__valid_prescaler_no_rise_fall_time(void) {
|
||||
// We'll use a base clock speed of 360Mhz and try to get to 100kHz I2C. This requires a prescaler
|
||||
// of 8 which gets us down to a base clock speed of 45MHz.
|
||||
//
|
||||
// 45MHz / 100kHz = 450 cycles => 450 - ceil(6 / 8) sync cycles = 449 cycles to play with
|
||||
// minimum low = ceil(4700ns / (1 / 45MHz)) = 212 cycles
|
||||
// minimum high = ceil(4000ns / (1 / 45MHz)) = 181 cycles
|
||||
// extra cycles = 56 => (28 low, 28 high) => SCLL of 240 and SCLH of 209
|
||||
//
|
||||
// SCLDEL = ceil((t_r 0 + t_SU 250ns) / (1 / 45MHz)) = 12
|
||||
prv_check_result(i2c_timingr_calculate(MHZ_TO_HZ(360), I2CBusMode_Standard,
|
||||
KHZ_TO_HZ(100), 0, 0),
|
||||
8, 240, 209, 12);
|
||||
}
|
||||
|
||||
void test_i2c_timingr__valid_no_prescaler_rise_fall_time(void) {
|
||||
// We'll use a base clock speed of 20MHz and try to get to 100kHz I2C with fall and rise times of
|
||||
// 500ns each.
|
||||
//
|
||||
// 20MHz / 100kHz = 200 cycles => 200 - 6 sync cycles - (2 * 500ns / (1 / 20MHz)) = 174 cycles to
|
||||
// play with
|
||||
// minimum low = ceil(4700ns / (1 / 20MHz)) = 94 cycles
|
||||
// minimum high = ceil(4000ns / (1 / 20MHz)) = 80 cycles
|
||||
// extra cycles = 0 => (0 low, 0 high) => SCLL of 94 and SCLH of 80
|
||||
//
|
||||
// SCLDEL = ceil((t_r 500ns + t_SU 250ns) / (1 / 20MHz)) = 15
|
||||
prv_check_result(i2c_timingr_calculate(MHZ_TO_HZ(20), I2CBusMode_Standard,
|
||||
KHZ_TO_HZ(100), 500, 500),
|
||||
1, 94, 80, 15);
|
||||
}
|
||||
|
||||
void test_i2c_timingr__data_delay_requires_prescaler(void) {
|
||||
// We'll increase the rise time enough that the required SCLDEL will exceed the max value with
|
||||
// no prescaler, forcing the use of the prescaler even though the SCLL and SCLH values wouldn't
|
||||
// otherwise require it.
|
||||
//
|
||||
// With prescaler 1: SCLDEL = ceil((800ns + 250ns) / (1 / 20MHz)) = 21 > 16
|
||||
// With prescaler 2: base clock is 10MHz.
|
||||
// 10MHz / 100kHz = 100 cycles => 100 - 6 sync cycles - ((800ns + 200ns) / (1 / 10 MHz)) = 84
|
||||
// cycles to play with
|
||||
// minimum low = ceil(4700ns / (1 / 10MHz)) = 47 cycles
|
||||
// minimum high = ceil(4000ns / (1 / 10MHz)) = 40 cycles
|
||||
// extra cycles = 0 => (0 low, 0 high) => SCLL of 47 and SCLH of 80
|
||||
//
|
||||
// SCLDEL = ceil((t_r 800ns + t_SU 250ns) / (1 / 10MHz)) = 11
|
||||
prv_check_result(i2c_timingr_calculate(MHZ_TO_HZ(20), I2CBusMode_Standard,
|
||||
KHZ_TO_HZ(100), 800, 200),
|
||||
2, 47, 40, 11);
|
||||
}
|
||||
|
||||
void test_i2c_timingr__invalid_speed_too_high(void) {
|
||||
// We'll use a base clock speed of 1Mhz and try to get to 400KHz I2C, which won't be possible
|
||||
// because the sync cycles alone will make us way slower than 400kHz.
|
||||
cl_assert_equal_i(i2c_timingr_calculate(MHZ_TO_HZ(1), I2CBusMode_FastMode,
|
||||
KHZ_TO_HZ(400), 0, 0),
|
||||
I2C_TIMINGR_INVALID_VALUE);
|
||||
}
|
||||
|
||||
void test_i2c_timingr__invalid_speed_too_low(void) {
|
||||
// We'll use a base clock speed of 1600Mhz and try to get to 100KHz I2C, which won't be possible
|
||||
// because the max prescaler is 16, which still leaves us with 1000 clock periods which is too
|
||||
// many to fit.
|
||||
cl_assert_equal_i(i2c_timingr_calculate(MHZ_TO_HZ(1600), I2CBusMode_Standard,
|
||||
KHZ_TO_HZ(100), 0, 0),
|
||||
I2C_TIMINGR_INVALID_VALUE);
|
||||
}
|
||||
|
||||
void test_i2c_timingr__invalid_speed_too_high_for_mode(void) {
|
||||
// Try calculating timing for 400kHz in Standard mode, which is out of spec for that mode.
|
||||
cl_assert_equal_i(i2c_timingr_calculate(MHZ_TO_HZ(36), I2CBusMode_Standard,
|
||||
KHZ_TO_HZ(400), 0, 0),
|
||||
I2C_TIMINGR_INVALID_VALUE);
|
||||
}
|
||||
|
||||
void test_i2c_timingr__invalid_long_rise_fall(void) {
|
||||
// We'll use a base clock speed of 100Mhz and try to get to 100KHz I2C with very long (out of
|
||||
// spec) 5us rise and fall times which prevent us from hitting the target frequency.
|
||||
cl_assert_equal_i(i2c_timingr_calculate(MHZ_TO_HZ(100), I2CBusMode_Standard,
|
||||
KHZ_TO_HZ(100), 5000, 5000),
|
||||
I2C_TIMINGR_INVALID_VALUE);
|
||||
}
|
160
tests/fw/drivers/test_qemu_serial.c
Normal file
160
tests/fw/drivers/test_qemu_serial.c
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "drivers/qemu/qemu_serial.h"
|
||||
#include "drivers/qemu/qemu_serial_private.h"
|
||||
#include "util/net.h"
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
extern bool qemu_test_add_byte_from_isr(QemuSerialGlobals *state, uint8_t byte);
|
||||
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_mutex.h"
|
||||
|
||||
|
||||
// Globals
|
||||
QemuSerialGlobals s_state;
|
||||
|
||||
// Setup
|
||||
////////////////////////////////////
|
||||
void test_qemu_serial__initialize(void) {
|
||||
qemu_serial_private_init_state(&s_state);
|
||||
}
|
||||
|
||||
void test_qemu_serial__cleanup(void) {
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
static void prv_send_bytes(void *p, uint32_t size) {
|
||||
uint8_t *src = (uint8_t *)p;
|
||||
for (uint32_t i=0; i<size; i++) {
|
||||
qemu_test_add_byte_from_isr(&s_state, *src++);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
static void prv_send_hdr(uint16_t protocol, uint16_t data_len) {
|
||||
QemuCommChannelHdr hdr = (QemuCommChannelHdr) {
|
||||
.signature = htons(QEMU_HEADER_SIGNATURE),
|
||||
.protocol = htons(protocol),
|
||||
.len = htons(data_len)
|
||||
};
|
||||
prv_send_bytes(&hdr, sizeof(hdr));
|
||||
}
|
||||
|
||||
static void prv_send_footer(void) {
|
||||
QemuCommChannelFooter footer = (QemuCommChannelFooter) {
|
||||
.signature = htons(QEMU_FOOTER_SIGNATURE)
|
||||
};
|
||||
prv_send_bytes(&footer, sizeof(footer));
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// Tests
|
||||
void test_qemu_serial__foo(void) {
|
||||
uint8_t *rcv_msg;
|
||||
uint32_t rcv_bytes;
|
||||
uint16_t rcv_protocol;
|
||||
|
||||
|
||||
// Our test message
|
||||
uint8_t msg_data[] = {0x11, 0x22, 0x33};
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Send message all at once before checking
|
||||
prv_send_hdr(QemuProtocol_SPP, sizeof(msg_data));
|
||||
prv_send_bytes(msg_data, sizeof(msg_data));
|
||||
prv_send_footer();
|
||||
|
||||
rcv_msg = qemu_serial_private_assemble_message(&s_state, &rcv_bytes, &rcv_protocol);
|
||||
cl_assert(rcv_msg);
|
||||
cl_assert_equal_i(rcv_protocol, QemuProtocol_SPP);
|
||||
cl_assert_equal_i(rcv_bytes, sizeof(msg_data));
|
||||
cl_assert_equal_m(msg_data, rcv_msg, sizeof(msg_data));
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Send 2 messages before checking
|
||||
for (int i=0; i<2; i++) {
|
||||
prv_send_hdr(QemuProtocol_SPP, sizeof(msg_data));
|
||||
prv_send_bytes(msg_data, sizeof(msg_data));
|
||||
prv_send_footer();
|
||||
}
|
||||
for (int i=0; i<2; i++) {
|
||||
rcv_msg = qemu_serial_private_assemble_message(&s_state, &rcv_bytes, &rcv_protocol);
|
||||
cl_assert(rcv_msg);
|
||||
cl_assert_equal_i(rcv_protocol, QemuProtocol_SPP);
|
||||
cl_assert_equal_i(rcv_bytes, sizeof(msg_data));
|
||||
cl_assert_equal_m(msg_data, rcv_msg, sizeof(msg_data));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Check after each part
|
||||
prv_send_hdr(QemuProtocol_SPP, sizeof(msg_data));
|
||||
cl_assert(!qemu_serial_private_assemble_message(&s_state, &rcv_bytes, &rcv_protocol));
|
||||
prv_send_bytes(msg_data, sizeof(msg_data));
|
||||
// Message is available now
|
||||
cl_assert(qemu_serial_private_assemble_message(&s_state, &rcv_bytes, &rcv_protocol));
|
||||
prv_send_footer();
|
||||
cl_assert(!qemu_serial_private_assemble_message(&s_state, &rcv_bytes, &rcv_protocol));
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Send garbage before a good packet
|
||||
prv_send_bytes(msg_data, sizeof(msg_data));
|
||||
cl_assert(!qemu_serial_private_assemble_message(&s_state, &rcv_bytes, &rcv_protocol));
|
||||
prv_send_hdr(QemuProtocol_SPP, sizeof(msg_data));
|
||||
cl_assert(!qemu_serial_private_assemble_message(&s_state, &rcv_bytes, &rcv_protocol));
|
||||
prv_send_bytes(msg_data, sizeof(msg_data));
|
||||
cl_assert(qemu_serial_private_assemble_message(&s_state, &rcv_bytes, &rcv_protocol));
|
||||
prv_send_footer();
|
||||
cl_assert(!qemu_serial_private_assemble_message(&s_state, &rcv_bytes, &rcv_protocol));
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Check after just part of the data
|
||||
prv_send_hdr(QemuProtocol_SPP, 2*sizeof(msg_data));
|
||||
cl_assert(!qemu_serial_private_assemble_message(&s_state, &rcv_bytes, &rcv_protocol));
|
||||
prv_send_bytes(msg_data, sizeof(msg_data));
|
||||
cl_assert(!qemu_serial_private_assemble_message(&s_state, &rcv_bytes, &rcv_protocol));
|
||||
prv_send_bytes(msg_data, sizeof(msg_data));
|
||||
|
||||
rcv_msg = qemu_serial_private_assemble_message(&s_state, &rcv_bytes, &rcv_protocol);
|
||||
cl_assert(rcv_msg);
|
||||
cl_assert_equal_i(rcv_bytes, 2*sizeof(msg_data));
|
||||
cl_assert_equal_m(msg_data, rcv_msg, sizeof(msg_data));
|
||||
cl_assert_equal_m(msg_data, rcv_msg+sizeof(msg_data), sizeof(msg_data));
|
||||
|
||||
prv_send_footer();
|
||||
cl_assert(!qemu_serial_private_assemble_message(&s_state, &rcv_bytes, &rcv_protocol));
|
||||
|
||||
}
|
||||
|
92
tests/fw/drivers/test_rtc_calibration.c
Normal file
92
tests/fw/drivers/test_rtc_calibration.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/stm32f2/rtc_calibration.h"
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
// Stubs and stuff
|
||||
#include "stubs_logging.h"
|
||||
#include "mcu.h"
|
||||
|
||||
#define TARGET_FREQUENCY_mHZ (32768 * 1000)
|
||||
#define ALTERNATE_FREQUENCY_mHZ (1000000 * 1000)
|
||||
|
||||
// Tests
|
||||
|
||||
void test_rtc_calibration__no_calibration_required(void) {
|
||||
RTCCalibConfig config = rtc_calibration_get_config(32768000, TARGET_FREQUENCY_mHZ);
|
||||
cl_assert_equal_i(config.units, 0);
|
||||
}
|
||||
|
||||
void test_rtc_calibration__slightly_slow_but_not_enough_to_calibrate(void) {
|
||||
// Approximately -2.01ppm
|
||||
RTCCalibConfig config = rtc_calibration_get_config(32767934, TARGET_FREQUENCY_mHZ);
|
||||
cl_assert_equal_i(config.units, 0);
|
||||
}
|
||||
|
||||
void test_rtc_calibration__just_slow_enough_to_calibrate(void) {
|
||||
// Approximately -2.04ppm
|
||||
RTCCalibConfig config = rtc_calibration_get_config(32767933, TARGET_FREQUENCY_mHZ);
|
||||
cl_assert_equal_i(config.units, 1);
|
||||
cl_assert_equal_i(config.sign, RTC_CalibSign_Positive);
|
||||
}
|
||||
|
||||
void test_rtc_calibration__slightly_fast_but_not_enough_to_calibrate(void) {
|
||||
// Approximately +1.01ppm
|
||||
RTCCalibConfig config = rtc_calibration_get_config(32768033, TARGET_FREQUENCY_mHZ);
|
||||
cl_assert_equal_i(config.units, 0);
|
||||
}
|
||||
|
||||
void test_rtc_calibration__just_fast_enough_to_calibrate(void) {
|
||||
// Approximately +1.04ppm
|
||||
RTCCalibConfig config = rtc_calibration_get_config(32768034, TARGET_FREQUENCY_mHZ);
|
||||
cl_assert_equal_i(config.units, 1);
|
||||
cl_assert_equal_i(config.sign, RTC_CalibSign_Negative);
|
||||
}
|
||||
|
||||
void test_rtc_calibration__out_of_bounds_slow(void) {
|
||||
// Approximately -130ppm
|
||||
RTCCalibConfig config = rtc_calibration_get_config(32763740, TARGET_FREQUENCY_mHZ);
|
||||
cl_assert_equal_i(config.units, 31);
|
||||
cl_assert_equal_i(config.sign, RTC_CalibSign_Positive);
|
||||
}
|
||||
|
||||
void test_rtc_calibration__out_of_bounds_fast(void) {
|
||||
// Approximately +70ppm
|
||||
RTCCalibConfig config = rtc_calibration_get_config(32770294, TARGET_FREQUENCY_mHZ);
|
||||
cl_assert_equal_i(config.units, 31);
|
||||
cl_assert_equal_i(config.sign, RTC_CalibSign_Negative);
|
||||
}
|
||||
|
||||
void test_rtc_calibration__different_target_frequency_not_fast_enough(void) {
|
||||
// Approximately +1.017ppm
|
||||
RTCCalibConfig config = rtc_calibration_get_config(1000001017, ALTERNATE_FREQUENCY_mHZ);
|
||||
cl_assert_equal_i(config.units, 0);
|
||||
}
|
||||
|
||||
void test_rtc_calibration__different_target_frequency_just_fast_enough(void) {
|
||||
// Approximately +1.018ppm
|
||||
RTCCalibConfig config = rtc_calibration_get_config(1000001018, ALTERNATE_FREQUENCY_mHZ);
|
||||
cl_assert_equal_i(config.units, 1);
|
||||
cl_assert_equal_i(config.sign, RTC_CalibSign_Negative);
|
||||
}
|
||||
|
||||
void test_rtc_calibration__invalid_frequency(void) {
|
||||
// Bigboards don't have a frequency stored in their mfg info registry.
|
||||
RTCCalibConfig config = rtc_calibration_get_config(0, TARGET_FREQUENCY_mHZ);
|
||||
cl_assert_equal_i(config.units, 0);
|
||||
}
|
41
tests/fw/drivers/wscript
Normal file
41
tests/fw/drivers/wscript
Normal file
|
@ -0,0 +1,41 @@
|
|||
from waftools.pebble_test import clar
|
||||
|
||||
def build(ctx):
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = "src/fw/drivers/qemu/qemu_serial_util.c" \
|
||||
" src/fw/util/shared_circular_buffer.c" \
|
||||
" tests/fakes/fake_rtc.c",
|
||||
test_sources_ant_glob = "test_qemu_serial.c",
|
||||
override_includes=['dummy_board'])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob="src/fw/drivers/fpc_pinstrap/fpc_pinstrap_snowy.c ",
|
||||
test_sources_ant_glob="test_fpc_pinstrap.c",
|
||||
override_includes=['fpc_pinstrap_board'])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = "src/fw/drivers/battery/battery_adc_conversion.c",
|
||||
test_sources_ant_glob = "test_battery.c")
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = "src/fw/drivers/stm32f2/rtc_calibration.c",
|
||||
test_sources_ant_glob = "test_rtc_calibration.c",
|
||||
override_includes=['rtc_calibration'])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = ('src/fw/drivers/stm32f7/i2c_timingr.c'),
|
||||
test_sources_ant_glob = 'test_i2c_timingr.c')
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = ('src/fw/drivers/flash/flash_api.c'),
|
||||
test_sources_ant_glob = 'test_flash_api.c',
|
||||
override_includes=['dummy_board'])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob=('src/fw/drivers/flash/flash_erase.c'),
|
||||
test_sources_ant_glob='test_flash_erase.c',
|
||||
override_includes=['dummy_board'],
|
||||
platforms=['tintin'])
|
||||
|
||||
# vim:filetype=python
|
Loading…
Add table
Add a link
Reference in a new issue