(* Runtime support for CPU internals *) (* Copyright (C) Florian Negele *) MODULE CPU; IMPORT SYSTEM; CONST StackSize* = 4096; CONST Quantum* = 100000; CONST CacheLineSize* = 32; CONST StackDisplacement* = 0; PROCEDURE Backoff-; CODE MOV R2, #0x100 loop: SUBS R2, R2, #1 BNE loop END Backoff; (* cpu control *) PROCEDURE Delay- (cycles: SIZE); CODE LDR R2, [FP, #cycles] delay: SUBS R2, R2, #1 BNE delay END Delay; PROCEDURE {NORETURN} Reset-; BEGIN {UNCOOPERATIVE, UNCHECKED} WriteWord (WDOG_CONTROL, LSH (0, CLKSEL) + LSH (0, CRV) + LSH (0248H, CKEY)); WriteWord (WDOG_MODE, LSH (1, WDEN) + LSH (1, RSTEN) + LSH (0ABCH, ZKEY)); Halt; END Reset; PROCEDURE {NORETURN} Halt-; CODE MRS R2, CPSR ORR R2, R2, #0b1100000 MSR CPSR_c, r2 WFI END Halt; PROCEDURE -SaveResult-; CODE STMDB SP!, {R0, R1} END SaveResult; PROCEDURE -RestoreResultAndReturn-; CODE LDMIA SP!, {R0, R1} ADD SP, FP, #4 LDMIA SP!, {FP, PC} END RestoreResultAndReturn; (* hardware registers *) CONST PSS_RST_CTRL* = 0F8000200H; SOFT_RST* = 0; CONST UART_RST_CTRL* = 0F8000228H; UART0_CPU1X_RST* = 0; UART1_CPU1X_RST* = 1; UART0_REF_RST* = 2; UART1_REF_RST* = 3; CONST UART_REF_CLK* = 50000000; CONST UART0* = 0E0000000H; UART1* = 0E0001000H; CONST Control_reg0* = 000H; RXRST* = 0; TXRST* = 1; RXEN* = 2; RXDIS* = 3; TXEN* = 4; TXDIS* = 5; CONST mode_reg0* = 004H; CHMOD* = 8; NBSTOP* = 6; PAR* = 3; CHRL* = 1; CLKS* = 0; CONST Intrpt_dis_reg0* = 00CH; CONST Baud_rate_gen_reg0* = 018H; CD* = 0; CONST Channel_sts_reg0* = 02CH; TXEMPTY* = 3; TXFULL* = 4; CONST Baud_rate_divider_reg0* = 034H; BDIV* = 0; CONST TX_RX_FIFO0* = 030H; FIFO* = 0; CONST Global_Timer_Counter_Register0* = 0F8F00200H; CONST Global_Timer_Counter_Register1* = 0F8F00204H; CONST WDOG_MODE* = 0F8005000H; WDEN* = 0; RSTEN* = 1; ZKEY* = 12; CONST WDOG_CONTROL* = 0F8005004H; CLKSEL* = 0; CRV* = 2; CKEY* = 14; PROCEDURE ReadWord- (register: ADDRESS): WORD; CODE LDR R2, [FP, #register] LDR R0, [R2, #0] END ReadWord; PROCEDURE ReadMask- (register: ADDRESS): SET; CODE LDR R2, [FP, #register] LDR R0, [R2, #0] END ReadMask; PROCEDURE WriteWord- (register: ADDRESS; value: ADDRESS); CODE LDR R2, [FP, #register] LDR R3, [FP, #value] STR R3, [R2, #0] END WriteWord; PROCEDURE WriteMask- (register: ADDRESS; value: SET); CODE LDR R2, [FP, #register] LDR R3, [FP, #value] STR R3, [R2, #0] END WriteMask; PROCEDURE Mask- (register: ADDRESS; value: SET); CODE LDR R2, [FP, #register] LDR R3, [FP, #value] LDR R4, [R2, #0] ORR R4, R4, R3 STR R4, [R2, #0] END Mask; PROCEDURE Unmask- (register: ADDRESS; value: SET); CODE LDR R2, [FP, #register] LDR R3, [FP, #value] LDR R4, [R2, #0] BIC R4, R4, R3 STR R4, [R2, #0] END Unmask; (* combined mask / unmask: clear mask and set value *) PROCEDURE MaskIn-(register: ADDRESS; mask, value: SET); CODE LDR R2, [FP, #register] LDR R3, [FP, #mask] LDR R4, [FP, #value] LDR R5, [R2, #0] BIC R5, R5, R3 ORR R5, R5, R4 STR R5, [R2, #0] END MaskIn; (* interrupt handling *) CONST Interrupts* = 7; CONST UndefinedInstruction* = 1; SoftwareInterrupt* = 2; PrefetchAbort* = 3; DataAbort* = 4; IRQ* = 5; FIQ* = 6; TYPE InterruptHandler* = PROCEDURE (index: SIZE); VAR handlers: ARRAY Interrupts OF InterruptHandler; PROCEDURE InstallInterrupt- (handler: InterruptHandler; index: SIZE): InterruptHandler; VAR previous: InterruptHandler; BEGIN {UNCOOPERATIVE, UNCHECKED} ASSERT (handler # NIL); ASSERT (index < Interrupts); REPEAT previous := CAS (handlers[index], NIL, NIL) UNTIL CAS (handlers[index], previous, handler) = previous; RETURN previous; END InstallInterrupt; PROCEDURE HandleInterrupt (index: SIZE); BEGIN {UNCOOPERATIVE, UNCHECKED} SYSTEM.SetActivity (NIL); IF handlers[index] # NIL THEN handlers[index] (index) ELSE HALT (1234) END; END HandleInterrupt; PROCEDURE DisableInterrupt- (index: SIZE); VAR previous: InterruptHandler; BEGIN {UNCOOPERATIVE, UNCHECKED} ASSERT (index < Interrupts); REPEAT previous := CAS (handlers[index], NIL, NIL) UNTIL CAS (handlers[index], previous, NIL) = previous; END DisableInterrupt; PROCEDURE Initialize-; CODE ADD R2, PC, #vector-$-8 MOV R3, #0 ADD R4, R3, #vector_end - vector copy: CMP R3, R4 BEQ vector_end LDR r5, [R2], #4 STR r5, [R3], #4 B copy vector: LDR PC, [PC, #header-$-8] LDR PC, [PC, #undefined_instruction-$-8] LDR PC, [PC, #software_interrupt-$-8] LDR PC, [PC, #prefetch_abort-$-8] LDR PC, [PC, #data_abort-$-8] MOV R0, R0 LDR PC, [PC, #irq-$-8] fiq: STMDB SP!, {R0, R1, R2, R3, R4, R5, R6, R7, LR} MOV R2, #UndefinedInstruction STR R2, [SP, #-4]! LDR R2, [PC, #handle-$-8] BLX R2 ADD SP, SP, #4 LDMIA SP!, {R0, R1, R2, R3, R4, R5, R6, R7, LR} SUBS PC, LR, #4 header: d32 0x8000 undefined_instruction: d32 UndefinedInstructionHandler software_interrupt: d32 SoftwareInterruptHandler prefetch_abort: d32 PrefetchAbortHandler data_abort: d32 DataAbortHandler irq: d32 IRQHandler handle: d32 HandleInterrupt vector_end: MOV R2, #0b10001 MSR CPSR_c, R2 MOV SP, #0x7000 MOV R2, #0b10010 MSR CPSR_c, R2 MOV SP, #0x6000 MOV R2, #0b10111 MSR CPSR_c, R2 MOV SP, #0x5000 MOV R2, #0b11011 MSR CPSR_c, R2 MOV SP, #0x4000 MOV R2, #0b10011 MSR CPSR_c, R2 END Initialize; PROCEDURE {NOPAF} UndefinedInstructionHandler; CODE STMDB SP!, {R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, LR} MOV R2, #UndefinedInstruction STR R2, [SP, #-4]! LDR R2, [PC, #handle-$-8] BLX R2 ADD SP, SP, #4 LDMIA SP!, {R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, LR} MOVS PC, LR handle: d32 HandleInterrupt END UndefinedInstructionHandler; PROCEDURE {NOPAF} SoftwareInterruptHandler; CODE STMDB SP!, {R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, LR} MOV R2, #SoftwareInterrupt STR R2, [SP, #-4]! LDR R2, [PC, #handle-$-8] BLX R2 ADD SP, SP, #4 LDMIA SP!, {R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, LR} MOVS PC, LR handle: d32 HandleInterrupt END SoftwareInterruptHandler; PROCEDURE {NOPAF} PrefetchAbortHandler; CODE STMDB SP!, {R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, LR} MOV R2, #PrefetchAbort STR R2, [SP, #-4]! LDR R2, [PC, #handle-$-8] BLX R2 ADD SP, SP, #4 LDMIA SP!, {R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, LR} SUBS PC, LR, #4 handle: d32 HandleInterrupt END PrefetchAbortHandler; PROCEDURE {NOPAF} DataAbortHandler; CODE STMDB SP!, {R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, LR} MOV R2, #DataAbort STR R2, [SP, #-4]! LDR R2, [PC, #handle-$-8] BLX R2 ADD SP, SP, #4 LDMIA SP!, {R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, LR} SUBS PC, LR, #4 handle: d32 HandleInterrupt END DataAbortHandler; PROCEDURE {NOPAF} IRQHandler; CODE STMDB SP!, {R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, LR} MOV R2, #IRQ STR R2, [SP, #-4]! LDR R2, [PC, #handle-$-8] BLX R2 ADD SP, SP, #4 LDMIA SP!, {R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, LR} SUBS PC, LR, #4 handle: d32 HandleInterrupt END IRQHandler; (* compiler intrinsics *) TYPE ULONGINT = LONGINT; (* alias to make distinction between signed and unsigned more clear *) TYPE UHUGEINT = HUGEINT; PROCEDURE DivS8*(left, right: SHORTINT): SHORTINT; VAR result, dummy: LONGINT; BEGIN {UNCOOPERATIVE, UNCHECKED} DivModS32(left, right, result, dummy); RETURN SHORTINT(result) END DivS8; PROCEDURE DivS16*(left, right: INTEGER): INTEGER; VAR result, dummy: LONGINT; BEGIN {UNCOOPERATIVE, UNCHECKED} DivModS32(left, right, result, dummy); RETURN INTEGER(result) END DivS16; PROCEDURE DivS32*(left, right: LONGINT): LONGINT; VAR result, dummy: LONGINT; BEGIN {UNCOOPERATIVE, UNCHECKED} DivModS32(left, right, result, dummy); RETURN result END DivS32; PROCEDURE DivU32*(left, right: ULONGINT): ULONGINT; VAR result, dummy: LONGINT; BEGIN {UNCOOPERATIVE, UNCHECKED} DivModU32(left, right, result, dummy); RETURN result END DivU32; PROCEDURE DivS64*(left, right: HUGEINT): HUGEINT; VAR result, dummy: HUGEINT; BEGIN {UNCOOPERATIVE, UNCHECKED} DivModS64(left, right, result, dummy); RETURN result END DivS64; PROCEDURE ModS8*(left, right: SHORTINT): SHORTINT; VAR result, dummy: LONGINT; BEGIN {UNCOOPERATIVE, UNCHECKED} DivModS32(left, right, dummy, result); RETURN SHORTINT(result) END ModS8; PROCEDURE ModS16*(left, right: INTEGER): INTEGER; VAR result, dummy: LONGINT; BEGIN {UNCOOPERATIVE, UNCHECKED} DivModS32(left, right, dummy, result); RETURN INTEGER(result) END ModS16; PROCEDURE ModS32*(left, right: LONGINT): LONGINT; VAR result, dummy: LONGINT; BEGIN {UNCOOPERATIVE, UNCHECKED} DivModS32(left, right, dummy, result); RETURN result END ModS32; PROCEDURE ModU32*(left, right: ULONGINT): ULONGINT; VAR result, dummy: LONGINT; BEGIN {UNCOOPERATIVE, UNCHECKED} DivModU32(left, right, dummy, result); RETURN result END ModU32; PROCEDURE ModS64*(left, right: HUGEINT): HUGEINT; VAR result, dummy: HUGEINT; BEGIN {UNCOOPERATIVE, UNCHECKED} DivModS64(left, right, dummy, result); RETURN result END ModS64; (* signed division and modulus - note: this implements the mathematical definition of DIV and MOD in contrast to the symmetric one *) PROCEDURE DivModS32(dividend, divisor: LONGINT; VAR quotient, remainder: LONGINT); BEGIN {UNCOOPERATIVE, UNCHECKED} ASSERT(divisor > 0); IF dividend >= 0 THEN DivModU32(dividend, divisor, quotient, remainder) ELSE dividend := -dividend; DivModU32(dividend, divisor, quotient, remainder); quotient := -quotient; IF remainder # 0 THEN DEC(quotient); remainder := divisor - remainder END END END DivModS32; (* Fast 32-bit unsigned integer division/modulo (author Alexey Morozov) *) PROCEDURE DivModU32(dividend, divisor: ULONGINT; VAR quotient, remainder: ULONGINT); CODE MOV R2, #0 ; quotient will be stored in R2 LDR R0, [FP,#dividend] ; R0 := dividend LDR R1, [FP,#divisor] ; R1 := divisor ; check for the case dividend < divisor CMP R0, R1 BLT Exit ; nothing to do than setting quotient to 0 and remainder to dividend (R0) CLZ R3, R0 ; R3 := clz(dividend) CLZ R4, R1 ; R4 := clz(divisor) SUB R3, R4, R3 ; R2 := clz(divisor) - clz(dividend) , R2 >= 0 LSL R1, R1, R3 ; scale divisor: divisor := LSH(divisor,clz(divisor)-clz(dividend)) Loop: CMP R0, R1 ADC R2, R2, R2 SUBCS R0, R0, R1 LSR R1, R1, #1 SUBS R3, R3, #1 BPL Loop ; R0 holds the remainder Exit: LDR R1, [FP,#quotient] ; R1 := address of quotient LDR R3, [FP,#remainder] ; R3 := address of remainder STR R2, [R1,#0] ; quotient := R1 STR R0, [R3,#0] ; remainder := R0 END DivModU32; (* signed division and modulus - note: this implements the mathematical definition of DIV and MOD in contrast to the symmetric one *) PROCEDURE DivModS64*(dividend, divisor: HUGEINT; VAR quotient, remainder: HUGEINT); BEGIN {UNCOOPERATIVE, UNCHECKED} ASSERT(divisor > 0); IF dividend >= 0 THEN DivModU64(dividend, divisor, quotient, remainder) ELSE dividend := -dividend; DivModU64(dividend, divisor, quotient, remainder); quotient := -quotient; IF remainder # 0 THEN DEC(quotient); remainder := divisor - remainder END END END DivModS64; (* Count leading zeros in a binary representation of a given 64-bit integer number *) PROCEDURE Clz64*(x: UHUGEINT): LONGINT; CODE ; high-half LDR R1, [FP,#x+4] CMP R1, #0 ; if high-half is zero count leading zeros of the low-half BEQ LowHalf CLZ R0, R1 B Exit ; low-half LowHalf: LDR R1, [FP,#x] CLZ R0, R1 ADD R0, R0, #32 ; add 32 zeros from the high-half Exit: END Clz64; (* Fast 64-bit unsigned integer division/modulo (Alexey Morozov) *) PROCEDURE DivModU64*(dividend, divisor: UHUGEINT; VAR quotient, remainder: UHUGEINT); VAR m: LONGINT; BEGIN {UNCOOPERATIVE, UNCHECKED} quotient := 0; IF dividend = 0 THEN remainder := 0; RETURN; END; IF dividend < divisor THEN remainder := dividend; RETURN; END; m := Clz64(divisor) - Clz64(dividend); ASSERT(m >= 0); divisor := LSH(divisor,m); WHILE m >= 0 DO quotient := LSH(quotient,1); IF dividend >= divisor THEN INC(quotient); DEC(dividend,divisor); END; divisor := LSH(divisor,-1); DEC(m); END; remainder := dividend; END DivModU64; END CPU.