added window watchdog module and tests.
This commit is contained in:
parent
e887d69e5d
commit
e8d1de0456
9 changed files with 461 additions and 0 deletions
|
|
@ -12,6 +12,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC
|
|||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
#blink
|
||||
wwdg
|
||||
gcc
|
||||
)
|
||||
|
||||
|
|
@ -62,3 +63,4 @@ add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
|||
add_subdirectory(ADC)
|
||||
add_subdirectory(RegEdit)
|
||||
add_subdirectory(blink)
|
||||
add_subdirectory(wwdg)
|
||||
|
|
|
|||
49
src/wwdg/CMakeLists.txt
Normal file
49
src/wwdg/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# File: src/wwdg/CMakeLists.txt
|
||||
add_library(wwdg STATIC
|
||||
wwdg.c
|
||||
)
|
||||
|
||||
# Current list dir: The directory where the cmake config file is.
|
||||
# Source Dir: The dir where the root/main cmake config file is.
|
||||
|
||||
if(NOT UNIT_TESTING)
|
||||
target_include_directories(wwdg PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
${CMAKE_SOURCE_DIR}/inc/
|
||||
)
|
||||
target_link_libraries(wwdg
|
||||
RegEdit
|
||||
)
|
||||
|
||||
target_compile_options(wwdg PUBLIC
|
||||
-g
|
||||
-Os
|
||||
-flto
|
||||
-ffunction-sections
|
||||
-fdata-sections
|
||||
-fmessage-length=0
|
||||
-msmall-data-limit=8
|
||||
-march=rv32ec
|
||||
-mabi=ilp32e
|
||||
)
|
||||
|
||||
|
||||
else()
|
||||
target_include_directories(wwdg PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
#First we include any module specific test dependencies.
|
||||
${CMAKE_SOURCE_DIR}/tests/wwdg/mocks/
|
||||
${CMAKE_SOURCE_DIR}/tests/wwdg/fakes/
|
||||
${CMAKE_SOURCE_DIR}/tests/wwdg/stubs/
|
||||
#Next comes the shared and non-module specific test depencencies.
|
||||
${CMAKE_SOURCE_DIR}/tests/shared/mocks/
|
||||
${CMAKE_SOURCE_DIR}/tests/shared/fakes/
|
||||
${CMAKE_SOURCE_DIR}/tests/shared/stubs/
|
||||
#Finally we include the local stuff, which has likely been overridden.
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
#Place Mocked/Regular dependencies here for unit testing.
|
||||
target_link_libraries(wwdg
|
||||
MockRegEdit
|
||||
)
|
||||
endif()
|
||||
91
src/wwdg/wwdg.c
Normal file
91
src/wwdg/wwdg.c
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Author: Jake G
|
||||
* Date: 2026
|
||||
* filename: wwdg.c
|
||||
* description: Source for window watchdog implimentation.
|
||||
*/
|
||||
|
||||
#include "wwdg.h"
|
||||
#include "RegEdit.h"
|
||||
#include "ch32fun.h"
|
||||
|
||||
#define WWDG_MIN_WINDOW_VAL 0x40 // 64
|
||||
#define WWDG_MAX_WINDOW_VAL 0x7F // 127
|
||||
|
||||
static inline void wwdg_rcc_enable(void)
|
||||
{
|
||||
// RegEdit_u32_AND_Num((void *)&RCC->APB1PCENR, RCC_WWDGEN);
|
||||
RegEdit_u32_SetBit((void *)&RCC->APB1PCENR, RCC_WWDGEN_BIT);
|
||||
}
|
||||
|
||||
void wwdg_enable(void)
|
||||
{
|
||||
wwdg_rcc_enable();
|
||||
RegEdit_u32_SetBit((void *)&WWDG->CTLR, WWDG_CTLR_WDGA_BIT);
|
||||
return;
|
||||
}
|
||||
|
||||
// maybe this should actually set the reset bit in the APB1 reg?
|
||||
void wwdg_disable(void)
|
||||
{
|
||||
RegEdit_u32_ClearBit((void *)&RCC->APB1PCENR, RCC_WWDGEN_BIT);
|
||||
return;
|
||||
}
|
||||
|
||||
bool wwdg_set_clock_div(uint8_t div)
|
||||
{
|
||||
if (div > 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RegEdit_u16_ClearBit((void *)&WWDG->CFGR, 7);
|
||||
RegEdit_u16_ClearBit((void *)&WWDG->CFGR, 8);
|
||||
RegEdit_u16_OR_Num((void *)&WWDG->CFGR, div);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wwdg_set_window(uint8_t value)
|
||||
{
|
||||
// Check if either the input is bigger than 7-Bits or less than the min.
|
||||
if (value < WWDG_MIN_WINDOW_VAL || value > WWDG_MAX_WINDOW_VAL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// First we clear the value in the first 7-Bits.
|
||||
// UINT16_MAX & ~127 = 0xFF80, aka bits [0:6]
|
||||
RegEdit_u16_AND_Num((void *)&WWDG->CFGR, 0xFF80);
|
||||
|
||||
// Now we set the value.
|
||||
RegEdit_u16_OR_Num((void *)&WWDG->CFGR, value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wwdg_reset_timer(uint8_t reset_value)
|
||||
{
|
||||
if (reset_value > WWDG_MAX_WINDOW_VAL || reset_value < WWDG_MIN_WINDOW_VAL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear the last six bits
|
||||
RegEdit_u16_AND_Num((void *)&WWDG->CTLR, 0xFF80);
|
||||
|
||||
// Set the last six bits.
|
||||
RegEdit_u16_OR_Num((void *)&WWDG->CTLR, reset_value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void wwdg_enable_interrupt(void)
|
||||
{
|
||||
RegEdit_u16_SetBit((void *)&WWDG->CFGR, WWDG_EWI_BIT);
|
||||
}
|
||||
|
||||
void wwdg_disable_interrupt(void)
|
||||
{
|
||||
RegEdit_u16_ClearBit((void *)&WWDG->CFGR, WWDG_EWI_BIT);
|
||||
}
|
||||
133
src/wwdg/wwdg.h
Normal file
133
src/wwdg/wwdg.h
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/**
|
||||
* @brief Window Watchdog Timer Module.
|
||||
* @details Window Watchdog Timer with bi-conditional reset.
|
||||
* @author Jake G
|
||||
* @date 2026
|
||||
* @copyright None
|
||||
* @file wwdg.h
|
||||
*
|
||||
* Used to monitor system operation for software faults such as external
|
||||
* disturbances unforseen logic errors and other conditions. It requires a
|
||||
* counter refresh(dog feeding) within a specific window time(with upper and
|
||||
* lower limits), otherwise earlier or later than this window time the
|
||||
* watchdog circuit will generate a system reset.
|
||||
*
|
||||
* This is clocked off the AHB like so:
|
||||
*
|
||||
* AHB --> /4096 --> WDGTB
|
||||
*
|
||||
* Where the WDGTB is a user changable divdider.
|
||||
*
|
||||
*
|
||||
* The minimum value also known as T3 is 0x40. When 0x40 is in the register the
|
||||
* watchdog will have timed out.
|
||||
*/
|
||||
|
||||
// #pragma once
|
||||
#ifndef WWDG_H
|
||||
#define WWDG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define LOG2(x) (log(x) / log(2))
|
||||
|
||||
// These use the ch32fun defines and log to extract the bit position numbers.
|
||||
// Those numbers are then used in the regedit module's functions.
|
||||
#define RCC_WWDGEN_BIT LOG2(RCC_WWDGEN)
|
||||
#define WWDG_CTLR_WDGA_BIT LOG2(WWDG_CTLR_WDGA)
|
||||
#define WWDG_EWI_BIT LOG2(WWDG_CFGR_EWI)
|
||||
|
||||
#define WWDG_MAX_VALUE 127
|
||||
|
||||
enum WWDG_DIVS
|
||||
{
|
||||
DIV_ONE = 0,
|
||||
DIV_TWO = 1,
|
||||
DIV_FOUR = 2,
|
||||
DIV_EIGHT = 3,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t upper_limit;
|
||||
uint8_t reset_value;
|
||||
uint8_t clk_divider;
|
||||
bool enable_ewi;
|
||||
} WWDT_Config;
|
||||
|
||||
// struct WWDG;
|
||||
|
||||
/*
|
||||
* @brief
|
||||
* @param
|
||||
*/
|
||||
void wwdg_setup(WWDT_Config *conf);
|
||||
|
||||
/*
|
||||
* @brief Enables the Window Watchdog Timer.
|
||||
* After the WWDG is enabled it cannot be disabled until the entire
|
||||
* system is reset.
|
||||
*
|
||||
* The watchdog can technically be stopped from operating by disabling the
|
||||
* AHB clock bit for it using the RCC register.
|
||||
*/
|
||||
void wwdg_enable(void);
|
||||
|
||||
/**
|
||||
* @brief Disables the Window Watchdog Timer.
|
||||
* This function doesn't actully disable the peripheral itself but
|
||||
* disables the clock bit that supplies the WWDG.
|
||||
*/
|
||||
void wwdg_disable(void);
|
||||
|
||||
/**
|
||||
* @brief Set's the clock divider value.
|
||||
* @param[in] Divider value from 0-3 are valid as it's a 2Bit value.
|
||||
* @return False on invalid dividers, true on success.
|
||||
*
|
||||
* 0: Divide by 1, aka do nothing.
|
||||
* 1: Divide by 2.
|
||||
* 2: Divide by 4.
|
||||
* 3: Divide by 8.
|
||||
*/
|
||||
bool wwdg_set_clock_div(uint8_t div);
|
||||
|
||||
/**
|
||||
* @brief Sets the WWDG upper threshold.
|
||||
* @param[in] 7-Bit counter value.
|
||||
*
|
||||
* The function set's the W[6:0] or upper threshold value that that the
|
||||
* counter must be less than or equal to in order to refresh the WWDG,
|
||||
* aka feed the dog.
|
||||
*/
|
||||
bool wwdg_set_window(uint8_t value);
|
||||
|
||||
/**
|
||||
* @brief Resets the WWDT counter.
|
||||
* @param[in] The value the counter will be set, must be 7-Bit value.
|
||||
*/
|
||||
bool wwdg_reset_timer(uint8_t reset_value);
|
||||
|
||||
/**
|
||||
* @brief Enables the early wakeup interrupt bit.
|
||||
*/
|
||||
void wwdg_enable_interrupt(void);
|
||||
|
||||
/**
|
||||
* @brief Disables the early wakeup interrupt bit.
|
||||
*/
|
||||
void wwdg_disable_interrupt(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // WWDG_H
|
||||
24
tests/wwdg/CMakeLists.txt
Normal file
24
tests/wwdg/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# File: tests/wwdg/CMakeLists.txt
|
||||
|
||||
add_subdirectory(mocks)
|
||||
add_subdirectory(fakes)
|
||||
add_subdirectory(stubs)
|
||||
|
||||
# TEST_RUNNER
|
||||
add_library(test_wwdg
|
||||
test_wwdg.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(test_wwdg
|
||||
${CPPUTEST_LIBRARIES}
|
||||
wwdg
|
||||
)
|
||||
|
||||
target_include_directories(test_wwdg PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
#Next comes the shared and non-module specific test depencencies.
|
||||
${CMAKE_SOURCE_DIR}/inc/
|
||||
${CMAKE_SOURCE_DIR}/tests/shared/mocks/
|
||||
${CMAKE_SOURCE_DIR}/tests/shared/fakes/
|
||||
${CMAKE_SOURCE_DIR}/tests/shared/stubs/
|
||||
)
|
||||
0
tests/wwdg/fakes/CMakeLists.txt
Normal file
0
tests/wwdg/fakes/CMakeLists.txt
Normal file
0
tests/wwdg/mocks/CMakeLists.txt
Normal file
0
tests/wwdg/mocks/CMakeLists.txt
Normal file
0
tests/wwdg/stubs/CMakeLists.txt
Normal file
0
tests/wwdg/stubs/CMakeLists.txt
Normal file
162
tests/wwdg/test_wwdg.cpp
Normal file
162
tests/wwdg/test_wwdg.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Author: username
|
||||
* Date: todays_date
|
||||
* filename: test_wwdg.c
|
||||
* description: module_purpose
|
||||
*/
|
||||
|
||||
#include "CppUTest/CommandLineTestRunner.h"
|
||||
#include "CppUTestExt/MockSupport.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "ch32fun.h"
|
||||
#include "wwdg.h"
|
||||
}
|
||||
|
||||
TEST_GROUP(tg_wwdg)
|
||||
{
|
||||
void setup()
|
||||
{
|
||||
|
||||
}
|
||||
void teardown()
|
||||
{
|
||||
mock().checkExpectations();
|
||||
mock().clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
TEST(tg_wwdg, EnableCallsCorrectFunctiions)
|
||||
{
|
||||
//The APB1 contains the WWDG peripheral.
|
||||
mock().expectOneCall("RegEdit_u32_SetBit")
|
||||
.withPointerParameter("reg", (void *)&RCC->APB1PCENR)
|
||||
.withUnsignedIntParameter("bit_num", RCC_WWDGEN_BIT);
|
||||
|
||||
mock().expectOneCall("RegEdit_u32_SetBit")
|
||||
.withPointerParameter("reg", (void *)&WWDG->CTLR)
|
||||
.withUnsignedIntParameter("bit_num", WWDG_CTLR_WDGA_BIT);
|
||||
|
||||
wwdg_enable();
|
||||
}
|
||||
|
||||
TEST(tg_wwdg, DisableWWDG_DisablesPeriphClock)
|
||||
{
|
||||
mock().expectOneCall("RegEdit_u32_ClearBit")
|
||||
.withPointerParameter("reg", (void *)&RCC->APB1PCENR)
|
||||
.withUnsignedIntParameter("bit_num", RCC_WWDGEN_BIT);
|
||||
wwdg_disable();
|
||||
}
|
||||
|
||||
TEST(tg_wwdg, SetClockDivReturnsFalseOnInvalidValues)
|
||||
{
|
||||
for(uint8_t i = 4; i > 10; i++){
|
||||
CHECK_FALSE(wwdg_set_clock_div(i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(tg_wwdg, SetClockDivReturnsTrueOnValidValues)
|
||||
{
|
||||
for(uint8_t i = 0; i <= 3; i++){
|
||||
mock().expectOneCall("RegEdit_u16_ClearBit")
|
||||
.withPointerParameter("reg", (void *)&WWDG->CFGR)
|
||||
.withUnsignedIntParameter("bit_num", 7);
|
||||
mock().expectOneCall("RegEdit_u16_ClearBit")
|
||||
.withPointerParameter("reg", (void *)&WWDG->CFGR)
|
||||
.withUnsignedIntParameter("bit_num", 8);
|
||||
|
||||
mock().expectOneCall("RegEdit_u16_OR_Num")
|
||||
.withPointerParameter("reg", (void *)&WWDG->CFGR)
|
||||
.withUnsignedIntParameter("num", i);
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i <= 3; i++){
|
||||
CHECK_TRUE(wwdg_set_clock_div(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(tg_wwdg, SetWindowCounterReturnsFalseOnInvalidInputs)
|
||||
{
|
||||
//We take all zeros logically and'd with the first six bits inverted.
|
||||
//uint16_t mask = UINT16_MAX & ~127; //0xFF80
|
||||
uint8_t value = 0;
|
||||
bool ret = true;
|
||||
|
||||
for(value = 0; value < 0x3F; value++){
|
||||
ret = wwdg_set_window(value);
|
||||
CHECK_EQUAL(false, ret);
|
||||
}
|
||||
for(value = 128; value < 255; value++){
|
||||
ret = wwdg_set_window(value);
|
||||
CHECK_EQUAL(false, ret);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(tg_wwdg, SetWindowCounterReturnsTrueOnValidInputs)
|
||||
{
|
||||
//We take all zeros logically and'd with the first six bits inverted.
|
||||
uint16_t mask = UINT16_MAX & ~127; //0xFF80
|
||||
uint8_t value = 0;
|
||||
bool ret = true;
|
||||
|
||||
for(value = 0x40; value <= 127; value++){
|
||||
//The first bits 0:6 need to be cleared before being set.
|
||||
mock().expectOneCall("RegEdit_u16_AND_Num")
|
||||
.withPointerParameter("reg", (void *)&WWDG->CFGR)
|
||||
.withUnsignedIntParameter("num", mask);
|
||||
|
||||
mock().expectOneCall("RegEdit_u16_OR_Num")
|
||||
.withPointerParameter("reg", (void *)&WWDG->CFGR)
|
||||
.withUnsignedIntParameter("num", value);
|
||||
}
|
||||
|
||||
for(value = 0x40; value <= 127; value++){
|
||||
ret = wwdg_set_window(value);
|
||||
CHECK_EQUAL(true, ret);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(tg_wwdg, SetEalryWakeUpSetsBit)
|
||||
{
|
||||
mock().expectOneCall("RegEdit_u16_SetBit")
|
||||
.withPointerParameter("reg", (void *)&WWDG->CFGR)
|
||||
.withUnsignedIntParameter("bit_num", WWDG_EWI_BIT);
|
||||
|
||||
wwdg_enable_interrupt();
|
||||
}
|
||||
|
||||
|
||||
TEST(tg_wwdg, ClearEalyWakeUpClearsBit)
|
||||
{
|
||||
mock().expectOneCall("RegEdit_u16_ClearBit")
|
||||
.withPointerParameter("reg", (void *)&WWDG->CFGR)
|
||||
.withUnsignedIntParameter("bit_num", WWDG_EWI_BIT);
|
||||
|
||||
wwdg_disable_interrupt();
|
||||
}
|
||||
|
||||
TEST(tg_wwdg, ResetFunctionSetsRegister)
|
||||
{
|
||||
//uint16_t fake_reg = 0;
|
||||
uint16_t reset_value = 126;
|
||||
|
||||
/*
|
||||
mock().expectOneCall("RegEdit_u16_ReadReg")
|
||||
.withPointerParameter("reg", (void *)&WWDG->CTLR);
|
||||
//.andReturnValue();
|
||||
*/
|
||||
|
||||
mock().expectOneCall("RegEdit_u16_AND_Num")
|
||||
.withPointerParameter("reg", (void *)&WWDG->CTLR)
|
||||
.withUnsignedIntParameter("num", 0xFF80);
|
||||
|
||||
mock().expectOneCall("RegEdit_u16_OR_Num")
|
||||
.withPointerParameter("reg", (void *)&WWDG->CTLR)
|
||||
.withUnsignedIntParameter("num", reset_value);
|
||||
|
||||
CHECK_TRUE(wwdg_reset_timer(reset_value));
|
||||
}
|
||||
|
||||
Loading…
Add table
Reference in a new issue