Rev 206 | Blame | Compare with Previous | Last modification | View Log | Download
1986ВЕ1Т
Код можно исполнять только из ПЗУ и области ОЗУ 0x20100000 (16K), из внешнего ОЗУ (с адреса, например, 0x60000000, но не 0x50000000).
Адрес перехода должен быть нечётным, иначе hardfault.
Чтение слов только выровненному адресу, иначе hardfault.
Система команд Thumb1 с небольшими дополнениями (используем только Thumb1).
После сброса читает PC=[4]
Для работы с внешней памятью надо настроить много регистров.
Внешняя память ОЗУ размером 1М видна с адресов 0x50000000, 0x60000000 и др.
Старший бит адреса (A19) перекрывает кнопку RIGHT.
При побайтовой записи путает чётные-нечётные байты.
Не работает в режиме LOW8 (с любыми LOW16, ENDIAN).
В режиме LOW16 дублирует пары байт, так работать нельзя.
В режиме ENDIAN процессор меняет 0..3<->3..0, так что после этого уже путаются чётные-нечётные слова, что тоже бесполезно.
Как исправить:
надо вынуть перемычки PC9-BE0, PC10-BE1, PC11-BE2, PC12-BE3
и установить провода PC9-BE1, PC10-BE0, PC11-BE3, PC12-BE2.
После этого всё правильно читается по байтам и по словам.
Можно поставить память больше, т.к. CS на A20,A21 мы выставляем вручную.
На отладочной плате A19 пересекается с кнопкой RIGHT, но теоретически можно сделать проводок от кнопки на другую ножку (поставил на PF2 вместо A31).
С проводами при CPU_CLK = 96 МГц (настройка из примера) получился делитель WAIT_STATE минимум "7" (2+4+2), т.е. 12 МГц (>83 нс). Даже при "4" (2+2+1) работает только половина теста памяти (с проводами).
При CPU_CLK = 128 МГц (как получить 144 МГц из 8 МГц? надо другой осциллятор HSE?) делитель WAIT_STATE минимум "8" (3+4+2), т.е. 14.2 МГц (>70 нс) - видимо, при "6" (2+3+2) и "7" (2+4+2) неудачные растактовки.
Сама микросхема памяти имеет "Время выборки по адресу и сигналу nСЕ1
и CE2 не более 30 нс", "Время цикла считывания информации мин 30 нс", "Время цикла записи информации мин 30 нс".
По доке на микросхему ОЗУ и при чтении, и при записи не нужна задержка адреса после снятия управляющих сигналов.
Минимальная работающая ручная растактовка 2+4+0, т.е. 21.3 МГц (47 нс). Не работает 1+4+0, т.е. 25.6 МГц (39 нс).
Для сравнения, скорость встроенного ПЗУ: 16 байт за 40 нс (странно, что работает, когда в EEPROM_CMD Delay=0!!!).
Кэшируется ли доступ к внешнему ОЗУ? В документации на 1986ВЕ1Т упоминаются только настройки кэширования и буферизации для DMA.
В J-Em v2 с Фитоном можно установить только 2 брякпойнта и только в ПЗУ. Поэтому точки останова ставим либо вручную в программе, либо вручную в окне дизасма или дампа.
для ARM thumb понадобится таблица констант, без которой никак
в thumb нет способа присваивания по частям!!!
на ВЕ9x есть (TODO!):
70FFF64F MOV R0,#0FFFFH
70FFF6C7 MOVT R0,#07FFFH
71FFF64F MOV R1,#0FFFFH
71FFF6C1 MOVT R1,#01FFFH
0100F04F??? MOV R1,#00000H
0100F2C2 MOVT R1,#02000H
21A2F64D MOV R1,#0DAA2H
110FF6CC MOVT R1,#0C90FH
5451F64E MOV R4,#0ED51H
4487F2C6 MOVT R4,#06487H
3733F24F MOV R7,#0F333H
5704F2CB MOVT R7,#0B504H
0700F04F??? MOV R7,#00000H
0700F2C0 MOVT R7,#00000H
v0=инверсия HHHH?
%0hhh RRRR LLLL LLLL, 1111 0hi0 0100 HHHH = MOV RR,HhLL
%0hhh RRRR LLLL LLLL, 1111 0hi0 1100 HHHH = MOVT RR,ThLL
^bit12
более того, в некоторых арифметических операциях есть ограничение на байтовые константы (ASR/LSL до 31, двухадресный ADD/SUB до 7, а MOV,CMP и одноадресный ADD/SUB до 0xff)
AND,ORR,EOR только в регистрах двухадресно
или делать все константы через ld:jr:dw? предсказывается идеально, в кэш ложится идеально, только одна лишняя команда и надо учитывать выравнивание на 4 (.ALIGN 2 на выходе выравнивает на 4, можно прямо его и использовать, только тогда надо уникальную метку для джампа!)
LDR Rr,[PC,#0xNN] NN = 0x000...0x3fc (кратно 4) только вперёд!!! интересно, от какого PC
%0100 1rrr NNNN NNNN
на адресе 062a команда LDR Rr,[PC,#0x00] читает из 062c (там лежит 44ff,42ba, в регистр попадает 42ba44ff)
на адресе 061c команда LDR Rr,[PC,#0x00] читает из 0620 (там лежит d00f,07d9, в регистр попадает 07d9d00f)
в PUSH можно указать список регистров (только R0..R7 и LR):
PUSH {R0,R2,R7,LR}
%1011 010L 7654 3210
в POP можно указать список регистров (только R0..R7 и PC):
POP {R0,R2,R7,PC}
%1011 110P 7654 3210
короткий переход (-800..+800):
B addr
%1110 0sLL LLLL LLLL (S с дополнением до 2)
L домножается до 2 и прибавляется к PC+4!!! (без округления до 4)
длинный переход (-400000..+400000):
BL addr
%1111 0sHH HHHH HHHH
%1111 1LLL LLLL LLLL
HL домножается до 2 и прибавляется к PC после конца команды, т.е. PC+4 (без округления до 4)
все ветвления короткие:
BEQ addr (==)
%1101 0000 sLLL LLLL
BNE addr (!=)
%1101 0001 sLLL LLLL
BCS 2
BCC 3
BMI 4
BPL 5
BVS 6
BVC 7
BHI 8
BLS 9
BGE a (>=)
BLT b (<)
BGT c (>)
BLE d (<=)
L домножается до 2 и прибавляется к PC+4!!! (без округления до 4)
заранее не узнать, далеко ли надо прыгать вперёд - то есть в if/while надо длинный переход с коротким обходом
переход по регистру:
BX Rr
%0100 0111 0rrr r000
BLX Rr
%0100 0111 1rrr r000
switch?
SWI n
%1101 1111 nnnn nnnn
что делает?
в Миландре там svc #N
MOVS R,r
%0000 0000 00rr rRRR
MOV R,r
%0100 0110 Rrrr rRRR
MVNS R,r
%0100 0011 11rr rRRR
LDR R,[r,N*4]
%0110 1NNN NNrr rRRR
STR R,[r,N*4]
%0110 0NNN NNrr rRRR
ADDS R,r,a
%0001 100a aarr rRRR
ADDS R,r,N
%0001 110N NNrr rRRR
todo грузить R0 в начале процедуры, а переменные адресовать относительно начала их блока (у этой процедуры). Если переменная слишком далеко, то адресовать через другой регистр (тогда уж присвоить его), а R0 не портить.
А как же тогда вызовы? После вызовов надо опять грузить R0, желательно из той же константы, но если она далеко, то из новой такой же.
todo сдвиги инлайн командами
todo добавить недостающие команды типа REV
Keil:
Example 9. Placing literal pools
AREA Loadcon, CODE, READONLY
ENTRY ; Mark first instruction to execute
start
BL func1 ; Branch to first subroutine
BL func2 ; Branch to second subroutine
stop
MOV r0, #0x18 ; angel_SWIreason_ReportException
LDR r1, =0x20026 ; ADP_Stopped_ApplicationExit
SVC #0x123456 ; ARM semihosting (formerly SWI)
func1
LDR r0, =42 ; => MOV R0, #42
LDR r1, =0x55555555 ; => LDR R1, [PC, #offset to
; Literal Pool 1]
LDR r2, =0xFFFFFFFF ; => MVN R2, #0
BX lr
LTORG ; Literal Pool 1 contains
; literal Ox55555555
func2
LDR r3, =0x55555555 ; => LDR R3, [PC, #offset to
; Literal Pool 1]
; LDR r4, =0x66666666 ; If this is uncommented it
; fails, because Literal Pool 2
; is out of reach
BX lr
LargeTable
SPACE 4200 ; Starting at the current location,
; clears a 4200 byte area of memory
; to zero
END ; Literal Pool 2 is empty
Procedure Call Standard for the ARM Architecture (AAPCS).
4 Using Assembly and Intrinsics in C or C++ Code 4.3 Calling assembly functions from C and C++
The AAPCS describes a contract between caller functions and callee functions. For example, for integer or pointer types, it specifies that:
- Registers R0-R3 pass argument values to the callee function, with subsequent arguments passed on the stack.
- Register R0 passes the result value back to the caller function.
- Caller functions must preserve R0-R3 and R12, because these registers are allowed to be corrupted by the callee function.
- Callee functions must preserve R4-R11 and LR, because these registers are not allowed to be corrupted by the callee function.
R13=SP
R14=LR (link register, адрес возврата)
R15=PC
у нас сейчас не так: r0=rtemp, r1..r4 - регистры данных (r1=результат), параметры не передаются в регистрах
r7=0xff
r8=0x00
r5,r6 свободны