이번 포스팅에서는 ARM 프로세서의 성능 측정 지표인, 사이클 정보를 얻는 방법을 정리한다.
아쉽게도 관련 문서나 코드를 100% 이해한 내용은 아니나, 리서치한 부분까지 정리한다.
리눅스 커널의 ARM 어셈블리 코드 분석
아래 소스를 열어보면 뭔가 의미있어 보이는 주석이 있다.
* Cortex-A8 has up to 4 configurable performance counters and
* a single cycle counter.
* Cortex-A9 has up to 31 configurable performance counters and
* a single cycle counter.
*
* All counters can be enabled/disabled and IRQ masked separately. The cycle
* counter and all 4 performance counters together can be reset separately.
*/
Cortex-A8 프로세서는 4개의 설정 가능한 카운터, Cortex-A9은 31개가 있다는 소리인데 조금 더 분석을 해봐야 겠다.
armv7_a8_perf_map 배열을 보면 뭔가 의미있는 매크로로 구성됐다는 점을 알게 해준다.
static const unsigned armv7_a8_perf_map[PERF_COUNT_HW_MAX] = {
PERF_MAP_ALL_UNSUPPORTED,
[PERF_COUNT_HW_CPU_CYCLES] = ARMV7_PERFCTR_CPU_CYCLES,
[PERF_COUNT_HW_INSTRUCTIONS] = ARMV7_PERFCTR_INSTR_EXECUTED,
[PERF_COUNT_HW_CACHE_REFERENCES] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
[PERF_COUNT_HW_CACHE_MISSES] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERFCTR_PC_WRITE,
[PERF_COUNT_HW_BRANCH_MISSES] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = ARMV7_A8_PERFCTR_STALL_ISIDE,
};
이 코드도 나중에 조금 더 분석해보자.
이제 다음 매크로를 보자.
/*
* Per-CPU PMNC: config reg
*/
#define ARMV7_PMNC_E (1 << 0) /* Enable all counters */
#define ARMV7_PMNC_P (1 << 1) /* Reset all counters */
#define ARMV7_PMNC_C (1 << 2) /* Cycle counter reset */
#define ARMV7_PMNC_D (1 << 3) /* CCNT counts every 64th cpu cycle */
#define ARMV7_PMNC_X (1 << 4) /* Export to ETM */
#define ARMV7_PMNC_DP (1 << 5) /* Disable CCNT if non-invasive debug*/
#define ARMV7_PMNC_N_SHIFT 11 /* Number of counters supported */
#define ARMV7_PMNC_N_MASK 0x1f
#define ARMV7_PMNC_MASK 0x3f /* Mask for writable bits */
사이클 관련 카운터를 읽을 때 사용되는 비트 마스크로 보인다.
armv7pmu_reset() 함수를 보니 인덱스 별로 armv7_pmnc_disable_counter() 함수를 호출해
카운터를 비활성화하는 코드가 보인다.
static void armv7pmu_reset(void *info)
{
struct arm_pmu *cpu_pmu = (struct arm_pmu *)info;
u32 idx, nb_cnt = cpu_pmu->num_events, val;
if (cpu_pmu->secure_access) {
asm volatile("mrc p15, 0, %0, c1, c1, 1" : "=r" (val));
val |= ARMV7_SDER_SUNIDEN;
asm volatile("mcr p15, 0, %0, c1, c1, 1" : : "r" (val));
}
/* The counter and interrupt enable registers are unknown at reset. */
for (idx = ARMV7_IDX_CYCLE_COUNTER; idx < nb_cnt; ++idx) {
armv7_pmnc_disable_counter(idx);
armv7_pmnc_disable_intens(idx);
}
/* Initialize & Reset PMNC: C and P bits */
armv7_pmnc_write(ARMV7_PMNC_P | ARMV7_PMNC_C);
}
이제 드디어 armv7pmu_read_counter() 함수가 보인다.
뭔가 ARM Instruction을 실행할 때의 사이클 정보를 얻는 함수로 보인다.
물론 이것도 나의 추정이다.
static inline u64 armv7pmu_read_counter(struct perf_event *event)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx;
u32 value = 0;
if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) {
pr_err("CPU%u reading wrong counter %d\n",
smp_processor_id(), idx);
} else if (idx == ARMV7_IDX_CYCLE_COUNTER) {
asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value));
} else {
armv7_pmnc_select_counter(idx);
asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (value));
}
return value;
}
위 코드에서 armv7_pmnc_counter_valid()/armv7_pmnc_select_counter() 함수가 보인다.
이 함수의 이름에 보이는 pmnc의 역할은 무엇인가?
ARM 스팩 문서 분석하기
ARM의 스팩 문서를 찾아보자.
c15, Performance Monitor Control Register (PMNC)
The purpose of the Performance Monitor Control Register is to control the operation of:
PMNC는 Performance Monitor Control Register 라고 말해준다.
이번에는 c9의 정체가 무엇인지 알아보자.
c9, Cycle Count Register
The purpose of the Cycle CouNT (CCNT) Register is to count the number of clock cycles since the register was reset. See bit [3] of the c9, Performance Monitor Control Register.
The CCNT Register is:
a read/write register common to Secure and Nonsecure states
accessible as determined by c9, User Enable Register.
...
To access the CCNT Register, read or write CP15 with:
MRC p15, 0, <Rd>, c9, c13, 0 ; Read CCNT Register
MCR p15, 0, <Rd>, c9, c13, 0 ; Write CCNT Register
The CCNT Register must be disabled before software can write to it. Any attempt by software to write to this register when enabled is Unpredictable.
정리하면 asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value)); 명령어는 'count the number of clock cycles'란 역할을 수행한다.
이번에는 asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (value)); 함수의 정체가 무엇인지 알아보자.
c9, Performance Monitor Count Registers
There are four Performance Monitor CouNT (PMCNT0-PMCNT3) Registers in the processor. The purpose of each PMCNT Register, as selected by the PMNXSEL Register, is to count instances of an event selected by the EVTSEL Register. Bits [31:0] of each PMCNT Register contain an event count.
...
To access the PMCNT Registers, read or write CP15 with:
MRC p15, 0, <Rd>, c9, c13, 2; Read PMCNT0-PMCNT3 Registers
MCR p15, 0, <Rd>, c9, c13, 2; Write PMCNT0-PMCNT3 Registers
PMCNT0-PMCNT3 레지스터를 읽는 역할을 수행한다.
패치 코드 작성
분석한 내용을 바탕으로 패치 코드를 하나 작성해서 컴파일을 해보자.
diff --git a/drivers/soc/bcm/raspberrypi-power.c b/drivers/soc/bcm/raspberrypi-power.c
index a78dfe0..e74409e 100644
--- a/drivers/soc/bcm/raspberrypi-power.c
+++ b/drivers/soc/bcm/raspberrypi-power.c
@@ -80,17 +80,32 @@ static int rpi_domain_on(struct generic_pm_domain *domain)
return rpi_firmware_set_power(rpi_domain, true);
}
+unsigned int rpi_get_cycle_counter_register(void)
+{
+ unsigned int result;
+ asm volatile ("MRC p15, 0, %0, c9, c13, 0\n\t": "=r" (result)::);
+
+ return result;
+}
+
+
static void rpi_common_init_power_domain(struct rpi_power_domains *rpi_domains,
int xlate_index, const char *name)
{
+ unsigned int clock_count;
struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index];
+ clock_count = rpi_get_cycle_counter_register();
+ printk("get current clock 1 = %ld \n" clock_count);
dom->fw = rpi_domains->fw;
dom->base.name = name;
dom->base.power_on = rpi_domain_on;
dom->base.power_off = rpi_domain_off;
+ clock_count = rpi_get_cycle_counter_register();
+ printk("get current clock 2 = %ld \n" clock_count);
+
/*
* Treat all power domains as off at boot.
*
'dom->fw = rpi_domains->fw;' ! 'dom->base.power_off = rpi_domain_off;' 구문 사이를
실행할 때의 ARM 프로세서의 사이클을 확인하는 코드이다.
위 코드를 반영하면 잘 동작할까? 잘 동작하지 않을 것 같다.
아래 함수를 적절히 호출해 PMNC 레지스터를 초기화시켜야 동작할 것 같다.
armv7_pmnc_disable_counter();
armv7_pmnc_enable_counter();
'시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리' 카테고리의 다른 글
[ARM] ARMv8(Aarch64) 아키텍처 기반 프로젝트가 대세인가? (0) | 2023.06.09 |
---|---|
[ARM] ARM 프로세서에서 말하는 프로그래밍 모델이란 (0) | 2023.06.09 |
[ARM] ARM사의 라이센스 방식: 소프트 매크로/하드 매크로 (0) | 2023.06.09 |
[ARM] const static 키워드로 변수를 선언: 코드 크기 최적화 (0) | 2023.06.09 |
기존 ARM 프로세서/아키텍처 책의 한계점 & 개선 포인트 (0) | 2023.06.09 |