2
0
Эх сурвалжийг харах

Added basic support for cooperative kernel on Zynq devices
(missing support for identity mapped memory and interrupt controller)

git-svn-id: https://svn.inf.ethz.ch/svn/lecturers/a2/trunk@6566 8c9fc860-2736-0410-a75d-ab315db34111

eth.negelef 9 жил өмнө
parent
commit
58a166d0c8

+ 20 - 1
source/Release.Tool

@@ -23,6 +23,11 @@
 #	Release.Build --build --path="./" A2RPiCooperative ~
 #	Release.Build --build --path="./" A2RPiCooperative ~
 #	StaticLinker.Link --fileName=kernel.img --extension=Gof --displacement=8000H --path="./" Trace CPU Runtime Counters Processors Queues BaseTypes Timer Activities ExclusiveBlocks HeapManager Interrupts Environment Mutexes Machine Heaps Modules GarbageCollector Objects Kernel ~
 #	StaticLinker.Link --fileName=kernel.img --extension=Gof --displacement=8000H --path="./" Trace CPU Runtime Counters Processors Queues BaseTypes Timer Activities ExclusiveBlocks HeapManager Interrupts Environment Mutexes Machine Heaps Modules GarbageCollector Objects Kernel ~
 #
 #
+# A2 Zynq Cooperative
+#
+#	Release.Build --build --path="./" A2ZynqCooperative ~
+#	StaticLinker.Link --fileName=kernel.bin --extension=Gof --displacement=100000H --path="./" Trace CPU Runtime Counters Processors Queues BaseTypes Timer Activities ExclusiveBlocks HeapManager Interrupts Environment Mutexes Machine Heaps Modules GarbageCollector Objects Kernel ~
+#
 # WINAOS
 # WINAOS
 #
 #
 # There are two different variants of WinAOS:
 # There are two different variants of WinAOS:
@@ -139,6 +144,17 @@ BUILDS
 		EXCLUDEPACKAGES "System Drivers Compiler ApplicationsMini Applications GuiApplicationsMini GuiApplications Fun Testing Education Build EFI Contributions Oberon OberonGadgets OberonApplications OberonDocumentation OberonVoyager OberonAnts"
 		EXCLUDEPACKAGES "System Drivers Compiler ApplicationsMini Applications GuiApplicationsMini GuiApplications Fun Testing Education Build EFI Contributions Oberon OberonGadgets OberonApplications OberonDocumentation OberonVoyager OberonAnts"
 		DISABLED "FALSE"
 		DISABLED "FALSE"
 	}
 	}
+	A2ZynqCooperative {
+		INCLUDE "NATIVE ARM ZYNQ COOP ZYNQCOOP"
+		COMPILER "Compiler.Compile"
+		COMPILEOPTIONS "--cooperative --objectFile=Generic --newObjectFile --traceModule=Trace --mergeSections"
+		TARGET "ARM"
+		EXTENSION "Gof"
+		SYMBOLEXTENSION "Sym"
+		PATH "AOS:"
+		EXCLUDEPACKAGES "System Drivers Compiler ApplicationsMini Applications GuiApplicationsMini GuiApplications Fun Testing Education Build EFI Contributions Oberon OberonGadgets OberonApplications OberonDocumentation OberonVoyager OberonAnts"
+		DISABLED "FALSE"
+	}
 	A2NewObjectFile
 	A2NewObjectFile
 	{
 	{
 		INCLUDE "NATIVE NATIVEGEN I386 I386GEN UNCOOP"
 		INCLUDE "NATIVE NATIVEGEN I386 I386GEN UNCOOP"
@@ -280,6 +296,7 @@ PACKAGE Kernel ARCHIVE "Kernel.zip" SOURCE "KernelSrc.zip" DESCRIPTION "A2 Kerne
 	UNCOOP { Runtime.Mod }
 	UNCOOP { Runtime.Mod }
 	I386COOP { I386.CPU.Mod }
 	I386COOP { I386.CPU.Mod }
 	RPICOOP { RPI.CPU.Mod }
 	RPICOOP { RPI.CPU.Mod }
+	ZYNQCOOP { Zynq.CPU.Mod }
 
 
 	# Low-level trace output
 	# Low-level trace output
 	Trace.Mod
 	Trace.Mod
@@ -289,9 +306,11 @@ PACKAGE Kernel ARCHIVE "Kernel.zip" SOURCE "KernelSrc.zip" DESCRIPTION "A2 Kerne
 	NATIVEI386COOP { I386.APIC.Mod APIC.Processors.Mod BIOS.ACPI.Mod ACPI.Timer.Mod}
 	NATIVEI386COOP { I386.APIC.Mod APIC.Processors.Mod BIOS.ACPI.Mod ACPI.Timer.Mod}
 	WINCOOP { Coop.Win32.Kernel32.Mod Win32.Processors.Mod  Win32.Timer.Mod}
 	WINCOOP { Coop.Win32.Kernel32.Mod Win32.Processors.Mod  Win32.Timer.Mod}
 	RPICOOP { RPI.Processors.Mod RPI.Timer.Mod }
 	RPICOOP { RPI.Processors.Mod RPI.Timer.Mod }
+	ZYNQCOOP { Zynq.Processors.Mod Zynq.Timer.Mod }
 	COOP { Queues.Mod BaseTypes.Mod Activities.Mod ExclusiveBlocks.Mod Interrupts.Mod Coop.Runtime.Mod }
 	COOP { Queues.Mod BaseTypes.Mod Activities.Mod ExclusiveBlocks.Mod Interrupts.Mod Coop.Runtime.Mod }
 	NATIVECOOP { HeapManager.Mod BIOS.Environment.Mod }
 	NATIVECOOP { HeapManager.Mod BIOS.Environment.Mod }
 	RPICOOP { HeapManager.Mod RPI.Environment.Mod }
 	RPICOOP { HeapManager.Mod RPI.Environment.Mod }
+	ZYNQCOOP { HeapManager.Mod Zynq.Environment.Mod }
 	WINCOOP { Win32.Environment.Mod }
 	WINCOOP { Win32.Environment.Mod }
 
 
 	UNIX { Unix.Glue.Mod }
 	UNIX { Unix.Glue.Mod }
@@ -306,7 +325,7 @@ PACKAGE Kernel ARCHIVE "Kernel.zip" SOURCE "KernelSrc.zip" DESCRIPTION "A2 Kerne
 	WINORIG, WINGEN {Win32.Machine.Mod }
 	WINORIG, WINGEN {Win32.Machine.Mod }
 	COOP { Mutexes.Mod }
 	COOP { Mutexes.Mod }
 	I386COOP { Coop.I386.Machine.Mod }
 	I386COOP { Coop.I386.Machine.Mod }
-	RPICOOP { Coop.ARM.Machine.Mod }
+	RPICOOP, ZYNQCOOP { Coop.ARM.Machine.Mod }
 
 
 	UNIX { Unix.I386.Machine.Mod }
 	UNIX { Unix.I386.Machine.Mod }
 
 

+ 471 - 0
source/Zynq.CPU.Mod

@@ -0,0 +1,471 @@
+(* 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.

+ 142 - 0
source/Zynq.Environment.Mod

@@ -0,0 +1,142 @@
+(* Zynq environment *)
+(* Copyright (C) Florian Negele *)
+
+MODULE Environment;
+
+IMPORT SYSTEM, Activities, CPU, HeapManager, Interrupts, Trace, Processors, Timer;
+
+CONST Running* = 0; ShuttingDown* = 1; Rebooting* = 2;
+
+VAR memory: SIZE;
+VAR heap: HeapManager.Heap;
+VAR frequency: Timer.Counter;
+VAR status* := Running: WORD;
+
+PROCEDURE {NORETURN} Abort-;
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	IF SYSTEM.GetActivity () # NIL THEN Activities.TerminateCurrentActivity END;
+	Activities.TerminateCurrentActivity;
+END Abort;
+
+PROCEDURE Allocate- (size: SIZE): ADDRESS;
+VAR result, address: ADDRESS;
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	result := HeapManager.Allocate (size, heap);
+	IF result = NIL THEN RETURN NIL END;
+	FOR address := result TO result + size - 1 DO SYSTEM.PUT8 (address, 0) END;
+	RETURN result;
+END Allocate;
+
+PROCEDURE Deallocate- (address: ADDRESS);
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	HeapManager.Deallocate (address, heap);
+END Deallocate;
+
+PROCEDURE Write- (character: CHAR);
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	WHILE CPU.TXFULL IN CPU.ReadMask (CPU.UART1 + CPU.Channel_sts_reg0) DO END;
+	CPU.WriteWord (CPU.UART1 + CPU.TX_RX_FIFO0, LSH (ORD (character), CPU.FIFO));
+END Write;
+
+PROCEDURE Flush-;
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	REPEAT UNTIL CPU.TXEMPTY IN CPU.ReadMask (CPU.UART1 + CPU.Channel_sts_reg0);
+END Flush;
+
+PROCEDURE GetString- (CONST name: ARRAY OF CHAR; VAR result: ARRAY OF CHAR);
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	result[0] := 0X
+END GetString;
+
+PROCEDURE Clock- (): LONGINT;
+BEGIN RETURN Timer.GetCounter () DIV frequency;
+END Clock;
+
+PROCEDURE Sleep- (milliseconds: LONGINT);
+VAR clock: Timer.Counter;
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	ASSERT (milliseconds >= 0);
+	clock := Timer.GetCounter () + milliseconds * frequency;
+	WHILE Timer.GetCounter () - clock < 0 DO Activities.Switch END;
+END Sleep;
+
+PROCEDURE Shutdown*;
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	IF CAS (status, Running, ShuttingDown) # Running THEN RETURN END;
+	Trace.StringLn ("system: shutting down...");
+END Shutdown;
+
+PROCEDURE Reboot*;
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	Shutdown;
+	ASSERT (CAS (status, ShuttingDown, Rebooting) = ShuttingDown);
+END Reboot;
+
+PROCEDURE {NORETURN} Exit- (status: WORD);
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	Trace.String ("system: ");
+	IF status = Rebooting THEN Trace.StringLn ("rebooting..."); CPU.Reset END;
+	Trace.StringLn ("ready for power off or restart"); Flush; CPU.Halt;
+END Exit;
+
+PROCEDURE InitTrace;
+CONST BaudRate = 115200;
+CONST BDIV = 6; CD = CPU.UART_REF_CLK DIV BaudRate DIV (BDIV + 1);
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	CPU.WriteMask (CPU.UART_RST_CTRL, {CPU.UART1_REF_RST, CPU.UART1_CPU1X_RST});
+	CPU.WriteMask (CPU.UART1 + CPU.mode_reg0, {CPU.PAR + 2});
+	CPU.WriteMask (CPU.UART1 + CPU.Intrpt_dis_reg0, {0..12});
+(*	commented out to reuse UART settings from bootloader
+	CPU.WriteMask (CPU.UART1 + CPU.Control_reg0, {CPU.RXDIS, CPU.TXDIS});
+	CPU.WriteWord (CPU.UART1 + CPU.Baud_rate_gen_reg0, LSH (CD, CPU.CD));
+	CPU.WriteWord (CPU.UART1 + CPU.Baud_rate_divider_reg0, LSH (BDIV, CPU.BDIV));
+	CPU.WriteMask (CPU.UART1 + CPU.Control_reg0, {CPU.RXRST, CPU.TXRST});
+	CPU.WriteMask (CPU.UART1 + CPU.Control_reg0, {CPU.RXEN, CPU.TXEN});
+*)
+	Trace.Init; Trace.Char := Write;
+END InitTrace;
+
+PROCEDURE InitMemory;
+CONST MemorySize = 512 * 1024 * 1024;
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	HeapManager.Initialize (heap, ADDRESS OF KernelEnd, MemorySize);
+	memory := MemorySize - ADDRESS OF KernelEnd;
+END InitMemory;
+
+PROCEDURE Initialize-;
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	CPU.Initialize; InitTrace; InitMemory;
+	frequency := Timer.GetFrequency () DIV 1000;
+END Initialize;
+
+PROCEDURE Terminate-;
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	Interrupts.Terminate;
+END Terminate;
+
+PROCEDURE {NOPAF, INITIAL, FIXED(100000H)} KernelBegin;
+CODE
+	; initialize SP
+	MOV	SP, #0x8000
+	MRC	P15, 0, R0, C0, C0, 5
+	AND	R0, R0, #0x1
+	SUB	SP, SP, R0, LSL #13
+
+	; filter CPU
+	CMP	R0, #0
+	BEQ	skip
+	WFE
+	B		@Processors.Boot
+skip:
+END KernelBegin;
+
+PROCEDURE {NOPAF, FINAL, ALIGNED(32)} KernelEnd;
+CODE
+END KernelEnd;
+
+BEGIN {UNCHECKED}
+	Trace.String ("Version "); Trace.String (SYSTEM.Date); Trace.String (" (");
+	Trace.Int (memory DIV (1024 * 1024), 0); Trace.String (" MB RAM, GC, ");
+	Trace.Int (Processors.count, 0); Trace.String (" CPU");
+	IF Processors.count > 1 THEN Trace.Char ('s') END; Trace.Char (')'); Trace.Ln;
+END Environment.

+ 70 - 0
source/Zynq.Processors.Mod

@@ -0,0 +1,70 @@
+(* Runtime support for Zynq *)
+(* Copyright (C) Florian Negele *)
+
+(** The Processors module represents all logical processors of the system. *)
+MODULE Processors;
+
+IMPORT SYSTEM, Counters, CPU;
+
+(** Indicates the maximal number of logical processors that are supported by the system. *)
+CONST Maximum* = 2 + CPU.Interrupts;
+
+(** Holds the actual number of processors in the system. *)
+VAR count-: SIZE;
+
+VAR running: Counters.AlignedCounter; (* counts the number of application processors currently running *)
+
+(** Returns the unique index of the processor executing this procedure call. *)
+PROCEDURE GetCurrentIndex- EXTERN "Activities.GetCurrentProcessorIndex" (): SIZE;
+
+(** Suspends the execution of the current processor. *)
+(** A suspended processor must be resumed by a call to the Processors.ResumeAnyProcessor procedure. *)
+(** @topic Scheduling *)
+PROCEDURE SuspendCurrentProcessor-;
+CODE
+	WFE
+END SuspendCurrentProcessor;
+
+(** Resumes the execution of a single suspended processor. *)
+(** @topic Scheduling *)
+PROCEDURE ResumeAllProcessors-;
+CODE
+	SEV
+END ResumeAllProcessors;
+
+(** Starts the execution of all available processors. *)
+(** @topic Scheduling *)
+PROCEDURE StartAll-;
+CODE
+	SEV
+END StartAll;
+
+PROCEDURE {NORETURN, NOPAF} Boot;
+PROCEDURE Idle EXTERN "Activities.Idle";
+PROCEDURE Execute EXTERN "Activities.Execute" (procedure: PROCEDURE);
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	Counters.Inc (running);
+	SuspendCurrentProcessor;
+	SuspendCurrentProcessor;
+	Execute (Idle);
+	Counters.Dec (running);
+	CPU.Halt;
+END Boot;
+
+(** Initializes the module by enumerating all available processors. *)
+(** @topic Runtime Call *)
+PROCEDURE Initialize-;
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	ResumeAllProcessors;
+	REPEAT UNTIL Counters.Read (running) = 1;
+	count := 2;
+END Initialize;
+
+(** Terminates the module and waits for all other processors to stop their execution. *)
+(** @topic Runtime Call *)
+PROCEDURE Terminate-;
+BEGIN {UNCOOPERATIVE, UNCHECKED}
+	REPEAT UNTIL Counters.Read (running) = 0;
+END Terminate;
+
+END Processors.

+ 18 - 0
source/Zynq.Timer.Mod

@@ -0,0 +1,18 @@
+(* Runtime support for high precision timer *)
+(* Copyright (C) Florian Negele *)
+
+MODULE Timer;
+
+IMPORT CPU;
+
+TYPE Counter* = LONGINT;
+
+PROCEDURE GetCounter- (): Counter;
+BEGIN {UNCOOPERATIVE, UNCHECKED} RETURN CPU.ReadWord (CPU.Global_Timer_Counter_Register0);
+END GetCounter;
+
+PROCEDURE GetFrequency- (): Counter;
+BEGIN {UNCOOPERATIVE, UNCHECKED} RETURN 400000000;
+END GetFrequency;
+
+END Timer.