Commit 9c906a14 authored by Ulrich Huber's avatar Ulrich Huber
Browse files

[add] Introduce hardware specific functions to handle clocks

parent 9c2527d8
#ifndef FREERTOS_PORTABLE__TIME_H
#define FREERTOS_PORTABLE__TIME_H
#include <stdint.h>
#include <stddef.h>
#include <sys/time.h>
#include <xil/xil_types.h>
/**
* Initialization of the RTC
*/
void rtc_init();
/**
* Set the time of the RTC
* @param now The time to set
*/
void rtc_set_time(struct timespec now);
/**
* Sets the time of the TSU if available
*
* @param ts The timespec to set
*/
void tsu_set_time(struct timespec *ts);
/**
* Returns the time of the TSU if available
*
* @param ts The timespec to fill
* @return 1 if TSU is available, 0 otherwise
*/
int tsu_get_time(struct timespec *ts);
/**
* Adjusts the time of the TSU by applying the
* provided offset
* @param ts The offset
*/
void tsu_adj_time(struct timespec *ts);
/**
* Set the frequency of the TSU via pulses per second.
* @param tick_nsec pps (nsec/sec)
*/
void tsu_set_pps(unsigned long pps);
/**
* Set the tick length of the TTC.
* @param tick_nsec tick length (scaled nsec)
*/
void ttc_set_tick_length(unsigned long tick_nsec);
/**
* Resets the counter of the TTC
*/
void ttc_reset_counter();
#endif /* FREERTOS_PORTABLE__TIME_H */
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
add_subdirectory(memguard)
add_subdirectory(time)
# SPDX-License-Identifier: MIT
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
target_sources(
freertos-portable
#
PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/rtc.c"
"${CMAKE_CURRENT_LIST_DIR}/tsu.c"
"${CMAKE_CURRENT_LIST_DIR}/ttc.c"
)
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos+/time/time.h>
#include <freertos+/_time.h>
#include <xil/drivers/xrtcpsu.h>
#include <xil/drivers/xscugic.h>
#include <xil/xil_cpu.h>
#include <toki/hal/cpu.h>
XRtcPsu xTimexRtcInstance;
static SemaphoreHandle_t rtc_init_sem;
/**
* Handler of the second interrupt of an RTC.
* It sets the kernel time to the RTC time as accurately as possible.
* After setting it once, the handler disables itself
* @param ref
* @param event
*/
void rtc_pps_handler(const void *ref, u32 event)
{
size_t cpuId;
uintptr_t coreMask;
BaseType_t yield = pdFALSE;
// Seconds interrupt
if (event == XRTCPSU_EVENT_SECS_GEN) {
/*
* Reset the internal counter of the TTC so the interval starts with the
* interval of the RTC - crude phase alignment
*
* This will in turn mean, that the current tick will be way longer than
* it should be. As the whole system is still locked and no actual
* tasks are running, this is okay though.
*/
ttc_reset_counter();
/*
* Set the kernel time once from the current RTC value.
* Since the RTC does not support units smaller than seconds, we use the
* interrupt to ensure a reasonably small offset from the actual time.
*/
u32 secs = XRtcPsu_GetCurrentTime(&xTimexRtcInstance);
__do_settimeofday(secs, 0);
// Map the interrupt to the next instance or disable it if this is the last one
cpuId = uxHalGetCpuId();
coreMask = XAMP_CORE_MASK_U64 >> (++cpuId);
// Check if we are the last instance
if (coreMask != 0) {
// There is at least one more instance
while (coreMask % 2 == 0) {
coreMask >>= 1U;
cpuId++;
}
// Map interrupt to next instance
XScuGic_InterruptMaptoCpu(&xInterruptController, cpuId, XPAR_XRTCPSU_SECONDS_INTR);
} else {
// As last instance disable the interrupt as it has done its job
XRtcPsu_ClearInterruptMask(&xTimexRtcInstance, 0);
// Disable the interrupt in the GIC
XScuGic_Disable(&xInterruptController, XPAR_XRTCPSU_SECONDS_INTR);
}
// Unlock this instance
xSemaphoreGiveFromISR(rtc_init_sem, &yield);
portYIELD_FROM_ISR(yield);
}
}
inline void rtc_set_time(struct timespec now) {
/*
* Set the calibration value - this will ensure that any offset over long times is negated
* and that the new seconds counter value will be used in exactly one second
*/
XRtcPsu_CalculateCalibration(&xTimexRtcInstance, now.tv_sec, xTimexRtcInstance.OscillatorFreq);
// Set the time in the RTC - we assume that internal time is UTC
// We must increment the second by one as the value is only taken into account next second
XRtcPsu_SetTime(&xTimexRtcInstance, now.tv_sec + 1);
}
void rtc_init() {
XRtcPsu_Config *xConfig;
BaseType_t uStatus;
// Initialize the semaphore used to await initialization
rtc_init_sem = xSemaphoreCreateBinary();
/*
* Initialize the RTC driver so that it's ready to use.
* Look up the configuration in the config table, then initialize it.
*/
xConfig = XRtcPsu_LookupConfig( XPAR_XRTCPSU_0_DEVICE_ID );
configASSERT( NULL != xConfig );
uStatus = XRtcPsu_CfgInitialize( &xTimexRtcInstance, xConfig, xConfig->BaseAddr );
configASSERT( XST_SUCCESS == uStatus );
/* Check hardware build. */
uStatus = XRtcPsu_SelfTest( &xTimexRtcInstance );
configASSERT( XST_SUCCESS == uStatus );
/*
* Use the second interrupt of the RTC to set the kernel time as accurately as possible
*/
// Set the PPS handler
XScuGic_RegisterHandler(XPAR_SCUGIC_0_CPU_BASEADDR,
XPAR_XRTCPSU_SECONDS_INTR,
(Xil_ExceptionHandler)XRtcPsu_InterruptHandler,
(void *)&xTimexRtcInstance);
XRtcPsu_SetHandler(&xTimexRtcInstance, rtc_pps_handler, NULL);
/*
* Only the master core starts the synchronization of the internal kernel time
* via the RTC. When done synchronization is enabled for the next core and so on.
*/
if (uxHalGetCpuId() == 0)
{
// Enable Interrupts in the RTC
XRtcPsu_SetInterruptMask(&xTimexRtcInstance, XRTC_INT_STS_SECS_MASK);
// Map the interrupt to the current CPU
XScuGic_InterruptMaptoCpu(&xInterruptController, uxHalGetCpuId(), XPAR_XRTCPSU_SECONDS_INTR);
// Enable the interrupt in the GIC
XScuGic_Enable(&xInterruptController, XPAR_XRTCPSU_SECONDS_INTR);
}
/*
* Wait for initialization
* Happens at next incrementation of the second counter in the RTC
*/
xSemaphoreTake(rtc_init_sem, portMAX_DELAY);
vSemaphoreDelete(rtc_init_sem );
}
#include <netif/xadapter.h>
int tsu_get_time(struct timespec *ts) {
xemac_get_time(ts);
}
void tsu_set_time(struct timespec *ts) {
xemac_set_time(ts);
}
void tsu_adj_time(struct timespec *offset) {
xemac_adj_time(offset);
}
void tsu_set_pps(unsigned long pps) {
xemac_set_pps(pps);
}
#include <xil/xil_types.h>
#include <xil/drivers/xttcps.h>
#include <freertos+/_time.h>
extern XTtcPs xPortTimerTickTtcInstance;
void ttc_set_tick_length(unsigned long tick_nsec) {
XInterval interval;
u8 prescaler;
u32 last_int = XTtcPs_GetInterval(&xPortTimerTickTtcInstance);
u8 last_prescaler = XTtcPs_GetPrescaler(&xPortTimerTickTtcInstance);
XTtcPs_CalcPreciseIntervalFromTickLength(&xPortTimerTickTtcInstance, tick_nsec, &interval, &prescaler);
if (interval < XTTCPS_MAX_INTERVAL_COUNT && prescaler < 0XFFU) {
// Only set new values if they actually differ
if (interval != last_int || prescaler != last_prescaler) {
XTtcPs_SetInterval(&xPortTimerTickTtcInstance, interval);
XTtcPs_SetPrescaler(&xPortTimerTickTtcInstance, prescaler);
}
} else {
// Warn user that frequency could not be adjusted
// TODO notify user
//printf("Failed to adjust frequency of tick interrupt!\r\n");
}
}
inline void ttc_reset_counter() {
XTtcPs_ResetCounterValue(&xPortTimerTickTtcInstance);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment