Я пытаюсь выполнить модульный тест, используя библиотеку «check.h» в регистре, содержащем шестнадцатеричное число, чтобы проверить правильность возвращаемого значения. Регистры предназначены для программирования микроконтроллера STM32F030F4 ARM Cortex M0. Однако при запуске теста у меня возникла ошибка сегментации.
Вот функция, которую я хочу протестировать:
#define GPIOA_BASE 0x48000000
#define GPIOA_ODR (*(volatile uint32_t *)(GPIOA_BASE + 0x14))
#define LED1 (*(volatile uint32_t *)(0))
#define OFF 0UL
uint32_t LED1_off (void) {
GPIOA_ODR |= (OFF << LED1);
return GPIOA_ODR ;
}
И вот мой тест:
START_TEST (test_LED1_off) {
ck_assert_int_eq(0x48000014, LED1_off());
}
END_TEST
Для информации, функция ck_assert_int_eq работает с uint32_t, у меня есть другой тест, который работает с возвращаемым значением этого типа.
Несмотря на то, что возвращаемое значение не обязательно равно тому, что я хочу протестировать, при запуске тестов я получаю ошибку сегментации. Вот ошибка:
Запуск комплектов: FunctionsTests 66%: Проверок: 3, Сбоев: 0, Ошибок: 1. tests/tests.c:31:E:Core:test_LED1_off:0: (после этого момента) Получен сигнал 11 (ошибка сегментации)
Удалив следующую строку: «GPIOA_ODR |= (OFF << LED1);» и, установив возвращаемое значение равным «1», у меня больше нет ошибки сегментации. У меня такое впечатление, что когда я запускаю тесты, мой компьютер понимает, что я пытаюсь получить доступ к его собственной памяти.
Возможно ли то, что я пытаюсь? Если да, то как? Если нет, какие тесты я могу попробовать? Я боролся с этим уже несколько часов. Спасибо.
У меня есть «основная» цель моего make-файла, которая создает файл .elf с помощью компилятора Arm-none-eabi-gcc, и еще одна цель «tests», которая создает для меня исполняемый файл путем компиляции файла функций, который я хочу протестировать. в .elf (который зажигает светодиоды и т. д.) с помощью gcc. Я провожу тесты независимо от моего .elf, используемого микроконтроллером. Я знаю, это не имеет особого смысла, но это для университетской работы.
«Шестнадцатеричный» обычно относится к текстовому формату представления. Каков настоящий тест?
Вы не можете протестировать взаимодействие аппаратного обеспечения, специфичного для микроконтроллера, на своем компьютере (ну, если у вас нет симулятора). У вас могут быть некоторые аппаратно-независимые функции для общей обработки/расчетов, которые вы можете протестировать. Вы также можете «издеваться» над специфичными для mcu вещами внутри своих тестов, но неясно, что именно тогда они будут тестировать.
@Норронос, что должен был делать GPIOA_ODR |= (OFF << LED1);
, учитывая, что он уменьшается до GPIOA_ODR |= 0UL
? Это NOP
запись?
@WeatherVane нет, это GPIOA_ODR |= (0UL << *(volatile uint32_t *)0)
, который будет разыменовывать NULL-указатель (как есть volatile
) godbolt.org/z/9nofs9qjf
@WeatherVane Эта функция используется для помещения 0 в регистр, чтобы выключить светодиод на выводе PA0 моего микроконтроллера. Поэтому я сдвигаю 0 на соответствующий бит (в соответствии с таблицей данных микроконтроллера). В данном случае это не имеет особого смысла, но чтобы заставить мигать светодиод, я поочередно ставлю 1 и 0. Все эти функции, которые изменяют значения моих регистров, имеют ошибки сегментации, когда я их тестирую. Я не показал это, потому что это было бы лишним в примере, который я привел выше.
@WeatherVane Определение LED1 тоже весьма своеобразно. Я бы ожидал, что вместо этого это будет номер контакта, а OFF
будет определяться примерно так: 0xFFFFFFFE
. И |=
заменено на &=
. обновление: на самом деле 0xFFFFFFFE
неправильно, так как будет сдвигаться на нули. Ответ заключается в правильной логике.
Юнит-тест тестирует модуль программного обеспечения независимо от чего-либо еще, включая оборудование. Тестирование взаимодействия двух вещей (например, программного и аппаратного обеспечения) — это интеграционный тест. Для модульного теста потребуется некоторый уровень изоляции между ним и оборудованием, который можно имитировать, чтобы обеспечить ожидаемую от оборудования активность и проверить поведение программного обеспечения.
@Норронас, как сказал, это неверно. |= 0
немного не очищает, хотя может очистить статус регистра устройства.
Удалив следующую строку: «GPIOA_ODR |= (OFF << LED1);» и по установив возвращаемое значение равным «1», у меня больше нет сегментации вина. У меня такое впечатление, что когда я провожу тесты, мой компьютер понимает, что я пытаюсь получить доступ к его собственной памяти.
На вашем компьютере вы пытаетесь изменить память по адресу GPIOA_BASE + 0x14
(точнее, вы разыменовываете указатель, назначенный GPIOA_BASE + 0x14
, преобразованный в указатель). Это определенно не память, которая принадлежит вам и вызывает сегфолт.
Вся проверка регистров STM32 на ПК не имеет вообще никакого смысла.
Также:
OFF << LED1
разыменовывает NULL-указатель, который UB|
не сбрасывает битЕсли вы хотите запустить его на ПК, вам нужен реальный объект, на который будет ссылаться указатель.
#if defined(__x86_64__) || defined(_M_X64)
static uint32_t GPIOA_ODR;
#else
#define GPIOA_BASE 0x48000000
#define GPIOA_ODR (*(volatile uint32_t *)(GPIOA_BASE + 0x14))
#endif
#define LED1 0
#define OFF 1UL
uint32_t LED1_off (void) {
GPIOA_ODR &= ~(OFF << LED1);
return GPIOA_ODR ;
}
То, что вы сделали, работает для меня, однако, если я скомпилирую свой код с помощью Arm-none-eabi-gcc и загружу его в свой микроконтроллер, определения будут иметь тип «(*( Летучий uint32_t *)» с их адресами?
@Норронас, да, для этого нужны эти #if
Хорошо, большое спасибо за то, что вы сделали, я подозревал, что то, о чем я спрашивал, не слишком понятно.
Какая ошибка сегментации на голом железе?? Такого не существует. Похоже, вместо этого вы компилируете/запускаете код на своем компьютере.