From 940a702e3db3e03d782963fb46918dc8e22a17a6 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Tue, 10 Mar 2015 17:42:41 -0500 Subject: Support for using an external timebase register Change-Id: I2c40fdf88f51315b19e8d639f874fb79717419ce Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/16369 Reviewed-by: Glenn R. Miles Tested-by: Glenn R. Miles --- pk/gpe/gpe_timebase.h | 44 ++++++++++++++++++++ pk/gpe/pk_port.h | 2 +- pk/kernel/pk_api.h | 7 ++-- pk/kernel/pk_init.c | 9 ++--- pk/ppe/pk_port.h | 1 + pk/ppe/ppe_timebase.h | 28 +++++++++++++ pk/ppe42/ppe42_core.c | 100 ++++++++++++++++++++-------------------------- pk/ppe42/ppe42_init.c | 19 ++++----- pk/ppe42/ppe42_timebase.S | 60 ++++++++++++++++++++++------ pk/std/pk_port.h | 1 + pk/std/std_timebase.h | 46 +++++++++++++++++++++ pk/trace/pk_trace.h | 3 +- pk/trace/pk_trace_core.c | 58 ++++++--------------------- 13 files changed, 243 insertions(+), 135 deletions(-) create mode 100644 pk/gpe/gpe_timebase.h create mode 100644 pk/ppe/ppe_timebase.h create mode 100644 pk/std/std_timebase.h (limited to 'pk') diff --git a/pk/gpe/gpe_timebase.h b/pk/gpe/gpe_timebase.h new file mode 100644 index 00000000..3c27bdfb --- /dev/null +++ b/pk/gpe/gpe_timebase.h @@ -0,0 +1,44 @@ +#ifndef __GPE_TIMEBASE_H__ +#define __GPE_TIMEBASE_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file gpe_timebase.h +/// \brief support for using the OCB 32 bit timebase register +/// +/// The OCB timebase register is a 32 bit count-up register that is shared +/// by all GPE's in the OCC complex. + +#include "pk.h" + +//The timebase register is not yet supported in the OCB model. +//remove this line once it is supported. +#define APPCFG_USE_DEC_FOR_TIMEBASE + +#ifndef __ASSEMBLER__ + +#ifndef APPCFG_USE_DEC_FOR_TIMEBASE +static inline +uint32_t pk_timebase32_get(void) +{ + return in32(OCB_OTBR); +} + +#else +//assembly function is defined in ppe42_timebase.S +uint32_t pk_timebase32_get(void); + +#endif /* APPCFG_USE_DEC_FOR_TIMEBASE */ + +#else + + .macro _pk_timebase32_get rT, rA + _lwzi \rT, \rA, OCB_OTBR + .endm +#endif /* __ASSEMBLER__ */ + +#endif /* __GPE_TIMEBASE_H__ */ diff --git a/pk/gpe/pk_port.h b/pk/gpe/pk_port.h index 887c123e..410f4356 100644 --- a/pk/gpe/pk_port.h +++ b/pk/gpe/pk_port.h @@ -12,5 +12,5 @@ #define HWMACRO_GPE #include "ppe42.h" - +#include "gpe_timebase.h" #endif /* __PK_PORT_H__ */ diff --git a/pk/kernel/pk_api.h b/pk/kernel/pk_api.h index b0e7419c..51440275 100644 --- a/pk/kernel/pk_api.h +++ b/pk/kernel/pk_api.h @@ -671,9 +671,6 @@ pk_initialize(PkAddress noncritical_stack, PkTimebase pk_timebase_get(void); -void -pk_timebase_set(PkTimebase timebase); - // Interrupt preemption APIs int @@ -930,11 +927,13 @@ pk_deque_delete(PkDeque *element) element->next = 0; } - +//Trace function prototypes void pk_trace_tiny(uint32_t i_parm); void pk_trace_big(uint32_t i_hash_and_count, uint64_t i_parm1, uint64_t i_parm2); void pk_trace_binary(uint32_t i_hash_and_size, void* bufp); +void pk_trace_set_timebase(PkTimebase timebase); + /// Cast a pointer to another type, in a way that won't cause warnings diff --git a/pk/kernel/pk_init.c b/pk/kernel/pk_init.c index 9174e51d..2ac06f33 100644 --- a/pk/kernel/pk_init.c +++ b/pk/kernel/pk_init.c @@ -68,10 +68,6 @@ pk_initialize(PkAddress noncritical_stack, PK_INVALID_ARGUMENT_INIT); } - if (initial_timebase != PK_TIMEBASE_CONTINUES) { - pk_timebase_set(initial_timebase); - } - __pk_timebase_frequency_hz = timebase_frequency_hz; __pk_timebase_frequency_khz = timebase_frequency_hz / 1000; __pk_timebase_frequency_mhz = timebase_frequency_hz / 1000000; @@ -108,8 +104,9 @@ extern PkTraceBuffer g_pk_trace_buf; //set the trace timebase HZ g_pk_trace_buf.hz = timebase_frequency_hz; - //TODO: set the ppe instance id (for CME's) - + //set the timebase ajdustment for trace synchronization + pk_trace_set_timebase(initial_timebase); + #endif /* PK_TRACE_SUPPORT */ #endif /* PK_TIMER_SUPPORT */ diff --git a/pk/ppe/pk_port.h b/pk/ppe/pk_port.h index f9010581..b88078f2 100644 --- a/pk/ppe/pk_port.h +++ b/pk/ppe/pk_port.h @@ -12,5 +12,6 @@ #define HWMACRO_PPE #include "ppe42.h" +#include "ppe_timebase.h" #endif /* __PK_PORT_H__ */ diff --git a/pk/ppe/ppe_timebase.h b/pk/ppe/ppe_timebase.h new file mode 100644 index 00000000..f3d2c91d --- /dev/null +++ b/pk/ppe/ppe_timebase.h @@ -0,0 +1,28 @@ +#ifndef __PPE_TIMEBASE_H__ +#define __PPE_TIMEBASE_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file gpe_timebase.h +/// \brief support for using the OCB 32 bit timebase register +/// +/// The OCB timebase register is a 32 bit count-up register that is shared +/// by all GPE's in the OCC complex. + +#include "pk.h" + +//The timebase register will never be supported in the base ppe model +#define APPCFG_USE_DEC_FOR_TIMEBASE + +#ifndef __ASSEMBLER__ + +//assembly function is defined in ppe42_timebase.S +uint32_t pk_timebase32_get(void); + +#endif /* __ASSEMBLER__ */ + +#endif /* __PPE_TIMEBASE_H__ */ diff --git a/pk/ppe42/ppe42_core.c b/pk/ppe42/ppe42_core.c index f82b3bf6..6586888c 100644 --- a/pk/ppe42/ppe42_core.c +++ b/pk/ppe42/ppe42_core.c @@ -29,56 +29,6 @@ ppe42_timebase_data_t ppe42_tb_data = {0}; PkTimebase ppe42_64bit_timebase = 0; -#if 0 -/// Get the emulated 64-bit timebase -/// - -PkTimebase -pk_timebase_get(void) -{ - PkTimebase tb; - uint32_t dec_start; - uint32_t dec; - uint32_t time_since_last_update; - PkMachineContext ctx; - - pk_critical_section_enter(&ctx); - - tb = ppe42_64bit_timebase; - dec_start = ppe42_dec_start; - dec = mfspr(SPRN_DEC); - - pk_critical_section_exit(&ctx); - - time_since_last_update = dec_start - dec; - - return (tb + time_since_last_update); -} -#endif - -/// Set the 64-bit timebase in an critical section -/// -/// It is assumed that the caller knows what they are doing; e.g., is aware of -/// what may happen when time warps as a result of this call. - -void -pk_timebase_set(PkTimebase timebase) -{ - PkMachineContext ctx; - - pk_critical_section_enter(&ctx); - - ppe42_64bit_timebase = timebase; - ppe42_tb_data.dec_start = 0; - ppe42_tb_data.dec_change_tag++; - - //This will cause TSR[DIS] to be set on the next timer tick. - mtspr(SPRN_DEC, 0); - - pk_critical_section_exit(&ctx); -} - - /// Enable interrupt preemption /// /// This API can only be called from an interrupt context. Threads will @@ -152,18 +102,16 @@ pk_interrupt_preemption_disable() // If the \a timeout is in the past, we schedule the PIT interrupt for 1 tick // in the future in accordance with the PK specification. +#ifdef APPCFG_USE_DEC_FOR_TIMEBASE void __pk_schedule_hardware_timeout(PkTimebase timeout) { PkTimebase now; uint32_t new_dec; - PkMachineContext ctx; uint32_t dec; if (timeout != PK_TIMEBASE_MAX) { - pk_critical_section_enter(&ctx); - now = pk_timebase_get(); if (timeout <= now) { @@ -174,7 +122,7 @@ __pk_schedule_hardware_timeout(PkTimebase timeout) new_dec = timeout - now; } - //read and write the DEC back-to-back so that we loose as little time + //read and write the DEC back-to-back so that we lose as little time //as possible dec = mfspr(SPRN_DEC); mtspr(SPRN_DEC, new_dec); @@ -187,12 +135,52 @@ __pk_schedule_hardware_timeout(PkTimebase timeout) //this update of the accumulator ppe42_tb_data.dec_start = new_dec; ppe42_tb_data.dec_change_tag++; - - pk_critical_section_exit(&ctx); } } +#else + +void +__pk_schedule_hardware_timeout(PkTimebase timeout) +{ + PkTimebase now; + PkTimebase diff; + uint32_t new_dec; + + if (timeout != PK_TIMEBASE_MAX) { + + now = pk_timebase_get(); + + //update our 64bit accumulator with the current snapshot + ppe42_64bit_timebase = now; + + if (timeout <= now) + { + new_dec = 1; + } + else + { + //FIXME: We have to multiply the difference by 16 + //to workaround missing support for selecting the + //external dec_timer clock source for the decrementer. + diff = (timeout - now) << 4; + + if (diff > 0xfffffffful) + { + new_dec = 0xffffffff; + } + else + { + new_dec = diff; + } + } + + mtspr(SPRN_DEC, new_dec); + + } +} +#endif /* APPCFG_USE_DEC_FOR_TIMEBASE */ #endif /* PK_TIMER_SUPPORT */ diff --git a/pk/ppe42/ppe42_init.c b/pk/ppe42/ppe42_init.c index 2e4a2ad2..0beea37d 100644 --- a/pk/ppe42/ppe42_init.c +++ b/pk/ppe42/ppe42_init.c @@ -50,15 +50,16 @@ __ppe42_system_setup() */ #endif /*STATIC_IRQ_TABLE*/ - // Set the DEC to decrement on every cycle and enable the DEC interrupt. Clear the status - // of all timers for good measure. - - //andc_spr(SPRN_TCR, TCR_DS); - //or_spr(SPRN_TCR, TCR_DIE); - - //Use dec_timer signal for decrementer - or_spr(SPRN_TCR, TCR_DIE | TCR_DS); - or_spr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS); + //Clear all status bits in the TSR + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS); + +#ifndef APPCFG_USE_DEC_FOR_TIMEBASE + //Enable the DEC interrupt and configure it to use the external dec_timer signal + mtspr(SPRN_TCR, TCR_DIE | TCR_DS); +#else + //Enable the DEC interrupt and configure it to use the internal clock signal + mtspr(SPRN_TCR, TCR_DIE); +#endif /* APPCFG_USE_DEC_FOR_TIMEBASE */ #if PK_TIMER_SUPPORT #if PK_TRACE_SUPPORT diff --git a/pk/ppe42/ppe42_timebase.S b/pk/ppe42/ppe42_timebase.S index 16c59529..43d64bb4 100644 --- a/pk/ppe42/ppe42_timebase.S +++ b/pk/ppe42/ppe42_timebase.S @@ -23,7 +23,43 @@ pk_timebase_get(void); .global ppe42_tb_data .global_function pk_timebase_get -//Use the DEC for our timebase until we have a real timebase register (uses 9 instructions) +#ifdef APPCFG_USE_DEC_FOR_TIMEBASE + + /// Note that it is ok to use this function in a fast interrupt + /// context + .align 5 + .global_function pk_timebase32_get +pk_timebase32_get: + + //load the decrementer start time and change tag + lvd %r4, ppe42_tb_data@sda21(0) + + //load the lower 32 bits of the 64bit timebase accumulator + lwz %r3, ppe42_64bit_timebase+4@sda21(0) + + //load the current decrementer value + mfdec %r0 + + //load the change tag again (should already be in the cache) + lwz %r6, ppe42_tb_data+4@sda21(0) + + //loop until the change tag is the same (typically should be same) + cmplwbne %r5, %r6, pk_timebase32_get + + //calculate how much time has passed since the decrementer was started and store in r6 + subf %r5, %r0, %r4 + + //add the 32bit difference to our 32bit timebase accumulator + add %r3, %r5, %r3 + + blr + + +/// Use the DEC for our timebase until we have a real timebase register (uses +/// 9 instructions). +/// Note: It is not ok to use this function in a fast interrupt context due to +/// its use of r7 + .align 5 pk_timebase_get: //load the decrementer start time and change tag @@ -40,9 +76,7 @@ pk_timebase_get: lwz %r7, ppe42_tb_data+4@sda21(0) //loop until the change tag is the same - //cmplwbne %r6, %r7, pk_timebase_get - cmplw %r6, %r7 - bne pk_timebase_get + cmplwbne %r6, %r7, pk_timebase_get //calculate how much time has passed since the decrementer was started and store in r6 subf %r6, %r0, %r5 @@ -54,28 +88,28 @@ pk_timebase_get: blr //enable this once we have a local timebase register in the model -#if 0 +#else // use the local timebase register to keep more accurate time with just 6 instructions // in the common case and 7 otherwise. + .align 5 pk_timebase_get: - //load the timebase local 32bit register address - lwz r5, timebase_local_reg@sda21(0) - //load the 64bit timebase accumulator - lvd r3, ppe42_64bit_timebase@sda21(0) + lvd r3, ppe42_64bit_timebase@sda21(0) - //read the local timebase register - lwz r5, 0(r5) + //read the local timebase register (2 instructions) + _pk_timebase32_get r5, r5 //increment the upper 32 bits if the lower 32 bits have flipped cmplwbgt r5, r4, update_lower_32 - addi r3, 1 //update upper 32 + + //increment the upper 32 bits + addi r3, r3, 1 update_lower_32: //replace the lower 32bits with what we read from the local timebase register - mr r4, r6 + mr r4, r5 blr #endif diff --git a/pk/std/pk_port.h b/pk/std/pk_port.h index 5998bd4b..4d400cc5 100644 --- a/pk/std/pk_port.h +++ b/pk/std/pk_port.h @@ -12,5 +12,6 @@ #define HWMACRO_STD #include "ppe42.h" +#include "std_timebase.h" #endif /* __PK_PORT_H__ */ diff --git a/pk/std/std_timebase.h b/pk/std/std_timebase.h new file mode 100644 index 00000000..e88a9bc6 --- /dev/null +++ b/pk/std/std_timebase.h @@ -0,0 +1,46 @@ +#ifndef __STD_TIMEBASE_H__ +#define __STD_TIMEBASE_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file std_timebase.h +/// \brief support for using the standard PPE 32 bit timebase register +/// +/// Each standard PPE has it's own timebase register that runs at a constant +/// frequency. + +#include "pk.h" + +//The timebase register is not yet supported in the STD model. +//remove this line once it is supported. +#define APPCFG_USE_DEC_FOR_TIMEBASE + +#ifndef __ASSEMBLER__ + +#ifndef APPCFG_USE_DEC_FOR_TIMEBASE +static inline +uint32_t pk_timebase32_get(void) +{ + return (uint32_t)((in64(STD_LCL_TBR)) >> 32); +} + +#else +//assembly function is defined in ppe42_timebase.S +uint32_t pk_timebase32_get(void); + +#endif /* APPCFG_USE_DEC_FOR_TIMEBASE */ + +#else + + .macro _pk_timebase32_get rT, rA + lis \rA, STD_LCL_TBR@ha + lvd \rT, STD_LCL_TBR@l(\rA) + .endm + +#endif /* __ASSEMBLER__ */ + +#endif /* __STD_TIMEBASE_H__ */ diff --git a/pk/trace/pk_trace.h b/pk/trace/pk_trace.h index 14a7278b..bcb1ce06 100644 --- a/pk/trace/pk_trace.h +++ b/pk/trace/pk_trace.h @@ -12,7 +12,7 @@ #include -#define PK_TRACE_VERSION 1 +#define PK_TRACE_VERSION 2 #ifndef PK_TRACE_SZ #define PK_TRACE_SZ 256 @@ -263,6 +263,7 @@ typedef struct uint32_t max_time_change; uint32_t hz; uint32_t pad; + uint64_t time_adj64; //updated with each new trace entry PkTraceState state; diff --git a/pk/trace/pk_trace_core.c b/pk/trace/pk_trace_core.c index 3f416c75..90a1a36c 100644 --- a/pk/trace/pk_trace_core.c +++ b/pk/trace/pk_trace_core.c @@ -42,6 +42,7 @@ PkTraceBuffer g_pk_trace_buf = .size = PK_TRACE_SZ, .max_time_change = PK_TRACE_MTBT, .hz = 500000000, //default value. Actual value is set in pk_init.c + .time_adj64 = 0, .state.word64 = 0, .cb = {0} }; @@ -96,52 +97,8 @@ void pk_trace_tiny(uint32_t i_parm) void pk_trace_timer_callback(void* arg) { -#if 0 - PkTraceTime64_t footer; - PkTraceState state; - uint64_t* ptr64; - PkMachineContext ctx; - -#define TIMESTAMP64_EXISTS 0x80000000 - - //If the timestamp64 flag is not set then we need another 64 bit timestamp - if(!(g_pk_trace_buf.state.tbu32 & TIMESTAMP64_EXISTS)) - { - - //fill in the footer data - footer.word64 = pk_timebase_get(); - footer.time_format.format = PK_TRACE_FORMAT_TIME64; - - state.tbu32 = footer.upper32 | TIMESTAMP64_EXISTS; - - //The following operations must be done atomically - pk_critical_section_enter(&ctx); - - //load the current byte count and calculate the address for this - //entry in the cb - ptr64 = (uint64_t*)&g_pk_trace_buf.cb[g_pk_trace_buf.state.offset & PK_TRACE_CB_MASK]; - - //calculate the offset for the next entry in the cb - state.offset = g_pk_trace_buf.state.offset + sizeof(PkTraceTiny); - - //update the cb state (tbu and offset) - g_pk_trace_buf.state.word64 = state.word64; - - //write the 64bit timestamp to the buffer - *ptr64 = footer.word64; - - //exit the critical section - pk_critical_section_exit(&ctx); - - } - -#else - - // doing it this way requires less code, but it also means that the - // trace can fill up with these traces over time. - + // guarantee at least one trace before the lower 32bit timebase flips PK_TRACE("PERIODIC TIMESTAMPING TRACE"); -#endif // restart the timer pk_timer_schedule(&g_pk_trace_timer, @@ -149,4 +106,15 @@ void pk_trace_timer_callback(void* arg) 0); } +// Use this function to synchronize the timebase between multiple PPEs. +// PPE A can send PPE B it's current timebase and then PPE B can set that +// as the current timebase for tracing purposes. It can also be used +// to set the current time to 0. This function changes the timebase for +// all entries that are currently in the trace buffer. Setting the current +// timebase to 0 will cause previous traces to have very large timestamps. +void pk_trace_set_timebase(PkTimebase timebase) +{ + g_pk_trace_buf.time_adj64 = timebase - pk_timebase_get(); +} + #endif -- cgit v1.2.1