Compare commits

..

7 commits

14 changed files with 485 additions and 15 deletions

View file

@ -18,7 +18,7 @@ else()
add_definitions(-DUNIX) add_definitions(-DUNIX)
endif() endif()
# For being able to used LSP # For being able to use LSP
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Request C standard features # Request C standard features

View file

@ -98,23 +98,25 @@ add_compile_definitions(${MCU}=1)
# General Flags # General Flags
#------------------- #-------------------
set(OBJECT_GEN_FLAGS "\
-g \ set(OBJECT_GEN_FLAGS "-g;\
-Os \ -Os;\
-flto \ -flto;\
-ffunction-sections \ -ffunction-sections;\
-fdata-sections \ -fdata-sections;\
-fmessage-length=0 \ -fmessage-length=0;\
-msmall-data-limit=8 ") -msmall-data-limit=8")
#------------------- #-------------------
# CFLAGS for ARCH # CFLAGS for ARCH
#------------------- #-------------------
# For CH32V003(QingKeV2A)
set(C_FLAGS_ARCH "\ set(C_FLAGS_ARCH "\
-march=rv32ec \ -march=rv32ec;\
-mabi=ilp32e \ -mabi=ilp32e;\
-I${ALTCLIB} \ -I${ALTCLIB};\
-nostdlib") -nostdlib")
#------------------- #-------------------

View file

@ -12,6 +12,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC
target_link_libraries(${PROJECT_NAME} PRIVATE target_link_libraries(${PROJECT_NAME} PRIVATE
#blink #blink
wwdg
gcc gcc
) )
@ -62,3 +63,4 @@ add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
add_subdirectory(ADC) add_subdirectory(ADC)
add_subdirectory(RegEdit) add_subdirectory(RegEdit)
add_subdirectory(blink) add_subdirectory(blink)
add_subdirectory(wwdg)

View file

@ -12,9 +12,13 @@ if(NOT UNIT_TESTING)
${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}
${CMAKE_SOURCE_DIR}/inc/ ${CMAKE_SOURCE_DIR}/inc/
) )
#target_link_libraries(RegEdit
#
#) target_compile_options(RegEdit PUBLIC
${OBJECT_GEN_FLAGS}
${C_FLAGS_ARCH}
)
else() else()
target_include_directories(RegEdit PUBLIC target_include_directories(RegEdit PUBLIC
${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}

49
src/wwdg/CMakeLists.txt Normal file
View 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
View 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
View 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

View file

@ -3,6 +3,7 @@
// ImportTestGroups // ImportTestGroups
IMPORT_TEST_GROUP(simple_test); IMPORT_TEST_GROUP(simple_test);
IMPORT_TEST_GROUP(tg_ADC); IMPORT_TEST_GROUP(tg_ADC);
IMPORT_TEST_GROUP(tg_wwdg);
IMPORT_TEST_GROUP(test_RegEdit); IMPORT_TEST_GROUP(test_RegEdit);
IMPORT_TEST_GROUP(test_blink); IMPORT_TEST_GROUP(test_blink);

View file

@ -1,6 +1,7 @@
project(Tests) project(Tests)
# TEST_DIRS # TEST_DIRS
add_subdirectory(wwdg)
add_subdirectory(blink) add_subdirectory(blink)
add_subdirectory(MockRegEdit) add_subdirectory(MockRegEdit)
add_subdirectory(RegEdit) add_subdirectory(RegEdit)
@ -19,6 +20,7 @@ add_executable(AllTests
target_link_libraries(AllTests target_link_libraries(AllTests
${CPPUTEST_LIBRARIES} ${CPPUTEST_LIBRARIES}
# TEST_LINKS # TEST_LINKS
test_wwdg
test_blink test_blink
test_ADC test_ADC
test_RegEdit test_RegEdit

24
tests/wwdg/CMakeLists.txt Normal file
View 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/
)

View file

View file

View file

162
tests/wwdg/test_wwdg.cpp Normal file
View 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));
}