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,#0FFFFH70FFF6C7 MOVT R0,#07FFFH71FFF64F MOV R1,#0FFFFH71FFF6C1 MOVT R1,#01FFFH0100F04F??? MOV R1,#00000H0100F2C2 MOVT R1,#02000H21A2F64D MOV R1,#0DAA2H110FF6CC MOVT R1,#0C90FH5451F64E MOV R4,#0ED51H4487F2C6 MOVT R4,#06487H3733F24F MOV R7,#0F333H5704F2CB MOVT R7,#0B504H0700F04F??? MOV R7,#00000H0700F2C0 MOVT R7,#00000Hv0=инверсия 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 LLLLHL домножается до 2 и прибавляется к PC после конца команды, т.е. PC+4 (без округления до 4)все ветвления короткие:BEQ addr (==)%1101 0000 sLLL LLLLBNE addr (!=)%1101 0001 sLLL LLLLBCS 2BCC 3BMI 4BPL 5BVS 6BVC 7BHI 8BLS 9BGE a (>=)BLT b (<)BGT c (>)BLE d (<=)L домножается до 2 и прибавляется к PC+4!!! (без округления до 4)заранее не узнать, далеко ли надо прыгать вперёд - то есть в if/while надо длинный переход с коротким обходомпереход по регистру:BX Rr%0100 0111 0rrr r000BLX Rr%0100 0111 1rrr r000switch?SWI n%1101 1111 nnnn nnnnчто делает?в Миландре там svc #NMOVS R,r%0000 0000 00rr rRRRMOV R,r%0100 0110 Rrrr rRRRMVNS R,r%0100 0011 11rr rRRRLDR R,[r,N*4]%0110 1NNN NNrr rRRRSTR R,[r,N*4]%0110 0NNN NNrr rRRRADDS R,r,a%0001 100a aarr rRRRADDS R,r,N%0001 110N NNrr rRRRtodo грузить R0 в начале процедуры, а переменные адресовать относительно начала их блока (у этой процедуры). Если переменная слишком далеко, то адресовать через другой регистр (тогда уж присвоить его), а R0 не портить.А как же тогда вызовы? После вызовов надо опять грузить R0, желательно из той же константы, но если она далеко, то из новой такой же.todo сдвиги инлайн командамиtodo добавить недостающие команды типа REVKeil:Example 9. Placing literal poolsAREA Loadcon, CODE, READONLYENTRY ; Mark first instruction to executestartBL func1 ; Branch to first subroutineBL func2 ; Branch to second subroutinestopMOV r0, #0x18 ; angel_SWIreason_ReportExceptionLDR r1, =0x20026 ; ADP_Stopped_ApplicationExitSVC #0x123456 ; ARM semihosting (formerly SWI)func1LDR r0, =42 ; => MOV R0, #42LDR r1, =0x55555555 ; => LDR R1, [PC, #offset to; Literal Pool 1]LDR r2, =0xFFFFFFFF ; => MVN R2, #0BX lrLTORG ; Literal Pool 1 contains; literal Ox55555555func2LDR 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 reachBX lrLargeTableSPACE 4200 ; Starting at the current location,; clears a 4200 byte area of memory; to zeroEND ; Literal Pool 2 is emptyProcedure 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=SPR14=LR (link register, адрес возврата)R15=PCу нас сейчас не так: r0=rtemp, r1..r4 - регистры данных (r1=результат), параметры не передаются в регистрахr7=0xffr8=0x00r5,r6 свободны