Счетчики PMU в ARM11
Я программирую распри pi model b ARM1176 bare metal
(в сборке и c
). Мне нужно рассчитать тактовые циклы, используемые для выполнения кода сборки.
Я использую следующий код для счетчика PMU:
mov r0,#1
MCR p15, 0, r0, c15, c12, 0 ; Write Performance Monitor Control Register
/* Reset Cycle Counter */
mov r0,#5
MCR p15, 0, r0, c15, c12, 0 ; Write Performance Monitor Control Register
/* Meaure */
MRC p15, 0, r0, c15, c12, 1 @ Read Cycle Counter Register
<MY CODES>
MRC p15, 0, r1, c15, c12, 1 @ Read Cycle Counter Register
Из этого, если у меня есть
add r3,#3
вместо моего кода я получаю r1=8
а также r0=0
, что кажется правильным, так как arm11 имеет 8 этапов конвейера и для его выполнения требуется 8 тактов.
Но когда я добавляю больше инструкций, я получаю смешные результаты, такие как
add r3,#3
add r4,#1
r0=0,r1=97/96/94 (the result of r1 should also be constant!!!)
я использую uart
увидеть результаты реестров на миником.
1 ответ
Хорошо, видеть то же самое, это очень интересно.
@ nop
.globl test
test:
mov r0,#1
MCR p15, 0, r0, c15, c12, 0
mov r0,#5
MCR p15, 0, r0, c15, c12, 0
MRC p15, 0, r0, c15, c12, 1
add r3,#3
add r2,#1
MRC p15, 0, r1, c15, c12, 1
sub r0,r1,r0
bx lr
Я вызываю это из C, поэтому, если я загляну с r4 в тестируемом коде, мне придется сохранить его в стеке, так что перепутано с r2. Без строки add r2 возвращаемое значение равнялось 8, а для строки add r2 возвращаемое значение было 0x68, а затем 0x65. Обратите внимание, что это на пи ноль. Так что некоторые часы немного быстрее твоих.
Помните, что это исходит от драма, а драм мучительно медленный. Так что вы можете увидеть что-то из этого.
Начальное выравнивание кода:
00008024 <test>:
8024: e3a00001 mov r0, #1
8028: ee0f0f1c mcr 15, 0, r0, cr15, cr12, {0}
802c: e3a00005 mov r0, #5
8030: ee0f0f1c mcr 15, 0, r0, cr15, cr12, {0}
8034: ee1f0f3c mrc 15, 0, r0, cr15, cr12, {1}
8038: e2833003 add r3, r3, #3
803c: e2822001 add r2, r2, #1
8040: ee1f1f3c mrc 15, 0, r1, cr15, cr12, {1}
8044: e0410000 sub r0, r1, r0
8048: e12fff1e bx lr
Да, если я раскомментирую nop перед тестом.globl и закомментирую add r2, в качестве тестируемого кода у меня будет только add r3, но nop подталкивает выравнивание всего блока кода. с добавлением r3 и без nop я получаю 8 отсчетов при добавлении r3 и nop я получаю 0x67 отсчетов.
Так что я думаю, что это всего лишь пример измерения выборки. Я не включил кеш руки, но может быть более глубокий кеш, или MMU, или другой, так как этот оперативный диск разделен между рукой и графическим процессором.
Если я сделаю еще один шаг и раскомментирую, то у nop есть как add r3, так и add r2, это 0x69. или в основном на одном уровне или чуть длиннее, чем одна инструкция, поэтому мы принудительно принесли туда.
так что в моем случае, если я добавлю больше nops, чтобы начальное чтение счетчика было выровнено по границе 8 слов, и у меня есть две измеряемые инструкции
00008030 <test>:
8030: e3a00001 mov r0, #1
8034: ee0f0f1c mcr 15, 0, r0, cr15, cr12, {0}
8038: e3a00005 mov r0, #5
803c: ee0f0f1c mcr 15, 0, r0, cr15, cr12, {0}
8040: ee1f0f3c mrc 15, 0, r0, cr15, cr12, {1}
8044: e2833003 add r3, r3, #3
8048: e2822001 add r2, r2, #1
804c: ee1f1f3c mrc 15, 0, r1, cr15, cr12, {1}
8050: e0410000 sub r0, r1, r0
8054: e12fff1e bx lr
Я получаю счет 8. Я положил третью инструкцию там добавить r3 и два добавить r2s. все еще счет 8.
Если я вернусь к этому, где хотя бы часть этого находится в другой строке извлечения.
00008024 <test>:
8024: e3a00001 mov r0, #1
8028: ee0f0f1c mcr 15, 0, r0, cr15, cr12, {0}
802c: e3a00005 mov r0, #5
8030: ee0f0f1c mcr 15, 0, r0, cr15, cr12, {0}
8034: ee1f0f3c mrc 15, 0, r0, cr15, cr12, {1}
8038: e2833003 add r3, r3, #3
803c: e2822001 add r2, r2, #1
8040: ee1f1f3c mrc 15, 0, r1, cr15, cr12, {1}
8044: e0410000 sub r0, r1, r0
8048: e12fff1e bx lr
И я делаю три запуска, ничего не меняя, а затем включаю кэш l1 (инструкция) и делаю еще три запуска, которые я получаю
00000068
0000001D
0000001D
0000001F
00000008
00000008
Так что я думаю, что вы имеете дело с медленным драмом, извлекающим строки, отсутствующими в кеше и и попадающими в них, и получающимися в результате выборками из строк кэша.
Если вы ожидали увидеть количество часов, необходимое для выполнения команды, которую вы не хотите, у вас не будет нулевой памяти состояния ожидания, если вы не сможете сохранить весь тестируемый код в кэше l1.
Я не думаю, что на чипе Sram можно использовать для такого рода вещей для этой фишки / платы, вы в конечном итоге попадете в драм, и этот драм передается в gpu. Таким образом, в основном время выполнения программы не должно быть детерминированным, и, как и в случае с вашим компьютером или телефоном, процессор не является узким местом, в течение долгого времени он сидел без дела, ожидая получения данных или инструкций.