LPC2378 MCI driver

Download pre-release library modules and new examples to use with Astrobe for LPC2000. Forum members can also upload their own source code examples.
Post Reply
Alexander Shiryaev
Posts: 12
Joined: Mon Apr 04, 2011 7:20 pm
Location: Russia
Contact:

LPC2378 MCI driver

Post by Alexander Shiryaev » Tue May 08, 2012 5:56 pm

MCI.def:

Code: Select all

DEFINITION MODULE MCI;

	CONST
		(* card types *)
			cardTypeUnknown* = 0;
			cardTypeMMC* = 1;
			cardTypeSD* = 2;
			cardTypeSDHC* = 3;

		writeBlocksAdr* = 7FD00000H;
		writeBlocksLen* = 8192; (* B *)
		readBlocksAdr* = 7FD00000H;
		readBlocksLen* = 8192; (* B *)

		blockLen* = 512; (* B *)

		(* err codes *)
			errOk* = 0;
			errNotInTrans* = 80;
			errTxUnderrun* = 200;
			errRxOverrun* = 205;

	VAR
		cardType*: INTEGER;
		nBlocks*: INTEGER;
		rate*: INTEGER; (* Hz *)
		err*: INTEGER;
		serialNo*: INTEGER;

	PROCEDURE Init* (MCIPWRActiveHigh: BOOLEAN; VAR ok: BOOLEAN);

	PROCEDURE* Busy* (): BOOLEAN;

	PROCEDURE StartWrite* (blockN, blocks: INTEGER; adr: INTEGER; VAR ok: BOOLEAN);

	PROCEDURE StartRead* (blockN, blocks: INTEGER; VAR ok: BOOLEAN);

END MCI.
MCI.mod:

Code: Select all

MODULE MCI;

	(*
		A. V. Shiryaev, 2012.05, 2012.06

		LPC2378/LPC2388
		MCI
		DMA channel 1

		Supported card types:
			MMC, [micro]SD, [micro]SDHC
			3.3 V

		Tested cards:
			"Kingston, MMC+, 512 M"
			"Samsung, microSD, 2 G"
			"Transcend, microSDHC, Class 10, 8 G"
			"Transcend, SDHC, Class 10, 32 G"
			"KingMax, microSDHC, Class 6, 32 G"

		References:
			NXP Code Bundle (LPC23xx)
			Keil RL FlashFS (MCB2300_SD_File/MCI_LPC23xx)
			OpenBSD sdmmc kernel driver (src/sys/dev/sdmmc)
			http://www.sdmmc.org/:
				Part_1_Physical_Layer_Simplified_Specification_Ver_3.01_Final_100518.pdf

		TODO:
			SDXC:
				...
				1 TB limit
			DMA:
				DMAMove: transfer size???
			? STOP_TRANSMISSION if resp # TRANS if s0 IN {1,4}
	*)

	IMPORT SYSTEM, LPC2378, LPC;

	CONST
		(* card types *)
			cardTypeUnknown* = 0;
			cardTypeMMC* = 1;
			cardTypeSD* = 2;
			cardTypeSDHC* = 3;

		respTypeNone = 0;
		respTypeShort = 1;
		respTypeLong = 2;

		(* DMA mode *)
			M2P = 1;
			P2M = 2;

		dmaAdr0 = 7FD00000H;
		dmaLen0 = 8192;

		writeBlocksAdr* = dmaAdr0;
		writeBlocksLen* = dmaLen0;
		readBlocksAdr* = dmaAdr0;
		readBlocksLen* = dmaLen0;

		(* from OpenBSD src/sys/dev/sdmmc/sdmmcreg.h, v 1.4 2009/01/09 *)
			(* MMC command; response *)
				MMCGOIDLESTATE = 0; (* R0 *) (* no resp *)
				MMCSENDOPCOND = 1; (* R3 *) (* short resp, ignCRC, ignRespCmd *)
				MMCALLSENDCID = 2; (* R2 *) (* long resp, ignRespCmd *)
				MMCSETRELATIVEADDR = 3; (* R1 *) (* short resp *)
				MMCSELECTCARD = 7; (* R1 *) (* short resp *)
				MMCSENDCSD = 9; (* R2 *) (* long resp, ignRespCmd *)
				MMCSTOPTRANSMISSION = 12; (* R1B *) (* short resp, ignCRC *)
				MMCSENDSTATUS = 13; (* R1 *) (* short resp *)
				MMCSETBLOCKLEN = 16; (* R1 *) (* short resp *)
				MMCREADBLOCKSINGLE = 17; (* R1 *) (* short resp *)
				MMCREADBLOCKMULTIPLE = 18; (* R1 *)
				MMCSETBLOCKCOUNT = 23; (* R1 *)
				MMCWRITEBLOCKSINGLE = 24; (* R1 *) (* short resp *)
				MMCWRITEBLOCKMULTIPLE = 25; (* R1 *)
				MMCAPPCMD = 55; (* R1 *) (* short resp *)
			(* SD command; response *)
				SDSENDRELATIVEADDR = 3; (* R6 *) (* short resp *)
				SDSENDIFCOND = 8; (* R7 *) (* short resp *)
			(* SD application command; response *)
				SDAPPSETBUSWIDTH = 6; (* R1 *) (* short resp *)
				SDAPPOPCOND = 41; (* R3 *) (* short resp, ignCRC, ignRespCmd *)

		(* MCIStatus bits *)
			CmdCrcFail = 0; (* Command response received (CRC check failed) *)
			DataCrcFail = 1; (* Data block sent/received (CRC check failed) *)
			CmdTimeOut = 2; (* Command response timeout *)
			DataTimeOut = 3; (* Data timeout *)
			TxUnderrun = 4; (* Transmit FIFO underrun error *)
			RxOverrun = 5; (* Receive FIFO overrun error *)
			CmdRespEnd = 6; (* Command response received (CRC check passed) *)
			CmdSent = 7; (* Command sent (no response required) *)
			DataEnd = 8; (* Data end (data counter is zero) *)
			StartBitErr = 9; (* Start bit not detected on all data signals in wide bus mode *)
			DataBlockEnd = 10; (* Data block sent/received (CRC check passed) *)

			CmdActive = 11; (* Command transfer in progress *)
			TxActive = 12; (* Data transmit in progress *)
			RxActive = 13; (* Data receive in progress *)
			TxFifoHalfEmpty = 14; (* Transmit FIFO half empty *)
			RxFifoHalfFull = 15; (* Receive FIFO half full *)
			TxFifoFull = 16; (* Transmit FIFO full *)
			RxFifoFull = 17; (* Receive FIFO full *)
			TxFifoEmpty = 18; (* Transmit FIFO empty *)
			RxFifoEmpty = 19; (* Receive FIFO empty *)
			TxDataAvbl = 20; (* Data available in transmit FIFO *)
			RxDataAvbl = 21; (* Data available in receive FIFO *)

		MCIClearMask = {0..10};

		(* from NXP code bundle *)
			OCRINDEX = 00FF8000H; (* 2.7 -- 3.6 V *)

		blockLenN = 9; (* >= 2 *)
		blockLen* = 512; (* 2^blockLenN;
			readBlocksLen MOD blockLen = 0; readBlocksLen >= blockLen *)

		(* err codes *)
			errOk* = 0;
			errNotInTrans* = 80;
			errTxUnderrun* = 200;
			errRxOverrun* = 205;

	VAR
		cardType*: INTEGER;
		CardRCA: INTEGER;

		nBlocks*,
		MCLK: INTEGER;
		rate*: INTEGER; (* Hz *)

		(* info *)
			serialNo*: INTEGER;

		s0: INTEGER;
			(*
				0: idle
				1: get status resp
					2: get read block resp
						3: wait read block
							[ 3 { 3 } 7 ]
								0
				4: get status resp
					5: get write block resp
						6: wait write block
							[ 6 { 6 } 8 ]
								0
				intermediate:
					7: get stop resp (multiple blocks read)
						0
					8: get stop resp (multiple blocks write)
			*)
			s1: INTEGER; (* start block number to read/write *)
			s2: INTEGER; (* number of blocks to read/write *)
			err*: INTEGER;

	PROCEDURE* SendCmd0 (cmd, arg, respType: INTEGER);
		VAR x: SET;
	BEGIN
		ASSERT(cmd >= 0, 100);
		ASSERT(cmd <= 63, 101); (* 6 bits *)

		x := SYSTEM.VAL(SET, cmd) + {10};
		CASE respType OF respTypeNone:
		| respTypeShort:
			x := x + {6}
		| respTypeLong:
			x := x + {6,7}
		END;

		SYSTEM.PUT(LPC2378.MCIArgument, arg);
		SYSTEM.PUT(LPC2378.MCICommand, x)
	END SendCmd0;

	(* to call from InterruptsHandlerMCI *)

		PROCEDURE StartSendReadBlock (blockN: INTEGER; multiple: BOOLEAN);
			VAR cmd, arg: INTEGER;
		BEGIN
			IF multiple THEN
				cmd := MMCREADBLOCKMULTIPLE
			ELSE
				cmd := MMCREADBLOCKSINGLE
			END;

			CASE cardType OF cardTypeMMC,cardTypeSD:
				arg := blockN * blockLen
			| cardTypeSDHC:
				arg := blockN
			END;

			SendCmd0(cmd, arg, respTypeShort)
		END StartSendReadBlock;

		PROCEDURE StartSendWriteBlock (blockN: INTEGER; multiple: BOOLEAN);
			VAR cmd, arg: INTEGER;
		BEGIN
			IF multiple THEN
				cmd := MMCWRITEBLOCKMULTIPLE
			ELSE
				cmd := MMCWRITEBLOCKSINGLE
			END;

			CASE cardType OF cardTypeMMC,cardTypeSD:
				arg := blockN * blockLen
			| cardTypeSDHC:
				arg := blockN
			END;

			SendCmd0(cmd, arg, respTypeShort)
		END StartSendWriteBlock;

		PROCEDURE StartSendStop;
		BEGIN
			(* XXX: arg: RCA? see OpenBSD srcs.; 0 works *)
			SendCmd0(MMCSTOPTRANSMISSION, 0, respTypeShort)
		END StartSendStop;

		PROCEDURE* DMAMove (channel, mode: INTEGER);
			CONST
				MBS = 4; (* : 32 *)
				(* MBS = 2; (* : 8 *) *)
			VAR x: SET;
		BEGIN
			(*
				USB RAM is used for test.
				Please note, Ethernet has its own SRAM, but GPDMA can't access
				that. GPDMA can access USB SRAM and IRAM. Ethernet DMA controller can
				access both IRAM and Ethernet SRAM.
			*)
			CASE channel OF 0:
				SYSTEM.PUT(LPC2378.DMACIntTCClear, {0});
				SYSTEM.PUT(LPC2378.DMACIntERRClear, {0});
				CASE mode OF M2P:
					(* Ch0 set for M2P transfer from mempry to MCI FIFO. *)
						SYSTEM.PUT(LPC2378.DMACC0SrcAddr, writeBlocksAdr);
						SYSTEM.PUT(LPC2378.DMACC0DestAddr, LPC2378.MCIFIFO);
					(* The burst size is set to 8, the size is 8 bit too. *)
						SYSTEM.PUT(LPC2378.DMACC0Control,
							blockLen MOD 1000H (* XXX *)
							+ LSL(MBS, 12) (* Source burst size *)
							+ LSL(2, 15) (* Destination burst size: 8 *)
							+ LSL(2, 18) (* Source transfer width: 32 bit *)
							+ LSL(2, 21) (* Destination transfer width: 32 bit *)
							+ LSL(1, 26) (* Source increment *)
							+ LSL(1, 31) (* Terminal count interrupt enable *)
						)
				| P2M:
					(* Ch0 set for P2M transfer from MCI FIFO to memory. *)
						SYSTEM.PUT(LPC2378.DMACC0SrcAddr, LPC2378.MCIFIFO);
						SYSTEM.PUT(LPC2378.DMACC0DestAddr, readBlocksAdr);
					(* The burst size is set to 8, the size is 8 bit too. *)
						SYSTEM.PUT(LPC2378.DMACC0Control,
							blockLen MOD 1000H (* XXX *)
							+ LSL(2, 12) (* Source burst size: 8 *)
							+ LSL(MBS, 15) (* Destination burst size *)
							+ LSL(2, 18) (* Source transfer width: 32 bit *)
							+ LSL(2, 21) (* Destination transfer width: 32 bit *)
							+ LSL(1, 27) (* Destination increment *)
							+ LSL(1, 31) (* Terminal count interrupt enable *)
						)
				END
			| 1:
				SYSTEM.PUT(LPC2378.DMACIntTCClear, {1});
				SYSTEM.PUT(LPC2378.DMACIntERRClear, {1});
				CASE mode OF M2P:
					(* Ch1 set for M2P transfer from memory to MCI FIFO. *)
						SYSTEM.PUT(LPC2378.DMACC1SrcAddr, writeBlocksAdr);
						SYSTEM.PUT(LPC2378.DMACC1DestAddr, LPC2378.MCIFIFO);
					(* The burst size is set to 8, the size is 8 bit too. *)
						SYSTEM.PUT(LPC2378.DMACC1Control,
							blockLen MOD 1000H (* XXX *)
							+ LSL(MBS, 12) (* Source burst size *)
							+ LSL(2, 15) (* Destination burst size: 8 *)
							+ LSL(2, 18) (* Source transfer width: 32 bit *)
							+ LSL(2, 21) (* Destination transfer width: 32 bit *)
							+ LSL(1, 26) (* Source increment *)
							+ LSL(1, 31) (* Terminal count interrupt enable *)
						)
				| P2M:
					(* Ch1 set for P2M transfer from MCI_FIFO to memory. *)
						SYSTEM.PUT(LPC2378.DMACC1SrcAddr, LPC2378.MCIFIFO);
						SYSTEM.PUT(LPC2378.DMACC1DestAddr, readBlocksAdr);
					(* The burst size is set to 8, the size is 8 bit too. *)
						SYSTEM.PUT(LPC2378.DMACC1Control,
							blockLen MOD 1000H (* XXX *)
							+ LSL(2, 12) (* Source burst size: 8 *)
							+ LSL(MBS, 15) (* Destination burst size *)
							+ LSL(2, 18) (* Source transfer width: 32 bit *)
							+ LSL(2, 21) (* Destination transfer width: 32 bit *)
							+ LSL(1, 27) (* Destination increment *)
							+ LSL(1, 31) (* Terminal count interrupt enable *)
						)
				END
			END;

			SYSTEM.PUT(LPC2378.DMACConfiguration, {0}) (* Enable DMA channels, little endian *)
		END DMAMove;

		PROCEDURE StartReadBlocksTransfer (blocks: INTEGER);
			VAR DataCtrl: INTEGER;
		BEGIN
			(* XXX: by TAAC *)
				SYSTEM.PUT(LPC2378.MCIDataTimer, blocks * 100 * (rate DIV 1000)); (* ~ 100 ms per block *)

			SYSTEM.PUT(LPC2378.MCIDataLength, blockLen * blocks);

			DMAMove(1, P2M);
			SYSTEM.PUT(LPC2378.DMACC1Config,
				10001H (* Enable, Lock *)
				+ LSL(4, 1) (* Source peripherial: SD/MMC *)
				+ LSL(6, 11) (* Flow control: peripherial to memory *)
				+ LSL(3, 14) (* IE, ITC *)
			);

			DataCtrl := 1 + 2 + 8 (* enable block data transfer from card to controller, DMA enabled *)
				+ LSL(blockLenN, 4);

			SYSTEM.PUT(LPC2378.MCIDataCtrl, DataCtrl)
		END StartReadBlocksTransfer;

		PROCEDURE StartWriteBlocksTransfer (blocks: INTEGER);
			VAR DataCtrl: INTEGER;
		BEGIN
			(* XXX: by TAAC & R2W *)
				SYSTEM.PUT(LPC2378.MCIDataTimer, blocks * 300 * (rate DIV 1000)); (* ~300 ms per block *)

			SYSTEM.PUT(LPC2378.MCIDataLength, blockLen * blocks);

			DMAMove(1, M2P);
			SYSTEM.PUT(LPC2378.DMACC1Config,
				10001H (* Enable, Lock *)
				+ LSL(4, 6) (* Destination peripherial: SD/MMC *)
				+ LSL(5, 11) (* Flow control: memory to peripherial *)
				+ LSL(3, 14) (* IE, ITC *)
			);
			(* Write, block transfer, DMA, and data length *)
			DataCtrl := 1 + 8 + LSL(blockLenN, 4);

			SYSTEM.PUT(LPC2378.MCIDataCtrl, DataCtrl)
		END StartWriteBlocksTransfer;

		PROCEDURE* Err0 (n: INTEGER);
		BEGIN
			err := n;
			s0 := 0
		END Err0;

		PROCEDURE* Err (n: INTEGER);
		BEGIN
			err := n;
			s0 := 0;

			SYSTEM.PUT(LPC2378.MCIDataCtrl, 0)
		END Err;

		PROCEDURE E0 (e: INTEGER);
		BEGIN
			IF s0 = 3 THEN (* wait read block(s) *)
				IF s2 = 1 THEN
					Err(e)
				ELSE (* s2 > 1 *)
					err := e + 1; (* may be overwritten if STOPTRANSMISSION fail *)
					s0 := 7;
					StartSendStop
				END
			ELSIF s0 = 6 THEN (* wait write block(s) *)
				IF s2 = 1 THEN
					Err(e + 2)
				ELSE (* s2 > 1 *)
					err := e + 3; (* may be overwritten if STOPTRANSMISSION fail *)
					s0 := 8;
					StartSendStop
				END
			ELSE
				Err(e + 4)
			END
		END E0;

	PROCEDURE InterruptsHandlerMCI [4]; (* 4 for IRQ or FIQ *)
		VAR x: SET;
			resp: SET;
			y: INTEGER;
	BEGIN
		SYSTEM.GET(LPC2378.VICIRQStatus, x);
		IF 24 IN x THEN
			SYSTEM.GET(LPC2378.MCIStatus, x);
			SYSTEM.PUT(LPC2378.MCIClear, x * MCIClearMask);

			IF s0 = 0 THEN
				Err(1)
			ELSIF x * {CmdCrcFail,DataCrcFail,CmdTimeOut,DataTimeOut,TxUnderrun,RxOverrun,StartBitErr,CmdSent} # {} THEN
				IF CmdCrcFail IN x THEN (* Command response received (CRC check failed) *)
					IF s0 IN {7,8} THEN (* get stop resp => ignore CRC *)
						s0 := 0;
						SYSTEM.PUT(LPC2378.MCIDataCtrl, 0)
						(* XXX
							SYSTEM.PUT(LPC2378.MCICommand, 0);
							SYSTEM.PUT(LPC2378.MCIArgument, -1)
						*)
						(* XXX: check cmd and resp *)
					ELSE
						E0(5)
					END
				ELSIF DataCrcFail IN x THEN (* Data block sent/received (CRC check failed) *)
					E0(10)
				ELSIF CmdTimeOut IN x THEN (* Command response timeout *)
					IF s0 IN {1,2,4,5,7,8} THEN
						(* XXX:
							SYSTEM.PUT(LPC2378.MCICommand, 0);
							SYSTEM.PUT(LPC2378.MCIArgument, -1)
						*)
						Err(15)
						(*
							case of card re-insertion (s0 IN {1,4})
						*)
					ELSE
						E0(20)
					END
				ELSIF DataTimeOut IN x THEN (* Data timeout *)
					E0(25)
				ELSIF TxUnderrun IN x THEN (* Transmit FIFO underrun error *)
					IF s0 = 6 THEN
						(*
							2012.06.19
							this may occur if some LPC2378 peripherial touched
							(such as REPEAT UNTIL SYSTEM.BIT(LPC.U0LSR, 5) etc.)
							when Busy() after StartWrite;
							send STOP in any case, because card not in TRANS state here
						*)
						SYSTEM.PUT(LPC2378.MCIDataCtrl, 0);
						err := errTxUnderrun; (* may be overwritten if STOPTRANSMISSION fail *)
						s0 := 8;
						StartSendStop
					ELSE
						E0(30)
					END
				ELSIF RxOverrun IN x THEN (* Receive FIFO overrun error *)
					IF s0 = 3 THEN
						(*
							2012.06.19
							this may occur if some LPC2378 peripherial touched
							(such as REPEAT UNTIL SYSTEM.BIT(LPC.U0LSR, 5) etc.)
							when Busy() after StartRead;
							send STOP in any case, because card not in TRANS state here
						*)
						SYSTEM.PUT(LPC2378.MCIDataCtrl, 0); (* required too *)
						err := errRxOverrun; (* may be overwritten if STOPTRANSMISSION fail *)
						s0 := 7;
						StartSendStop
					ELSE
						E0(35)
					END
				ELSIF StartBitErr IN x THEN (* Start bit not detected on all data signals in wide bus mode *)
					E0(40)
				ELSIF CmdSent IN x THEN (* Command sent (no response required) *)
					E0(45) (* not requested *)
				END
			ELSE
				IF CmdRespEnd IN x THEN (* Command response received (CRC check passed) *)
					SYSTEM.GET(LPC2378.MCIRespCmd, y); y := y MOD 64;
					SYSTEM.GET(LPC2378.MCIResponse0, resp);
					IF s0 IN {1,2,4,5} THEN (* get status/read/status/write resp *)
						IF resp * {8..11} = {8,11} THEN (* status: ready, TRAN *)
							CASE s0 OF 1:
								IF y = MMCSENDSTATUS THEN
									s0 := 2; StartSendReadBlock(s1, s2 > 1)
								ELSE
									Err(50)
								END
							| 2:
								IF s2 = 1 THEN
									IF y = MMCREADBLOCKSINGLE THEN
										s0 := 3; StartReadBlocksTransfer(s2)
									ELSE
										Err(55)
									END
								ELSE (* s2 > 1 *)
									IF y = MMCREADBLOCKMULTIPLE THEN
										s0 := 3; StartReadBlocksTransfer(s2)
									ELSE
										Err(60)
									END
								END
							| 4:
								IF y = MMCSENDSTATUS THEN
									s0 := 5; StartSendWriteBlock(s1, s2 > 1)
								ELSE
									Err(65)
								END
							| 5:
								IF s2 = 1 THEN
									IF y = MMCWRITEBLOCKSINGLE THEN
										s0 := 6; StartWriteBlocksTransfer(1)
									ELSE
										Err(70)
									END
								ELSE (* s2 > 1 *)
									IF y = MMCWRITEBLOCKMULTIPLE THEN
										s0 := 6; StartWriteBlocksTransfer(s2)
									ELSE
										Err(75)
									END
								END
							END
						ELSIF s0 IN {1,4} THEN (* get status resp (before r/w) *)
							IF y = MMCSENDSTATUS THEN
								Err0(errNotInTrans)
							ELSE
								Err(85)
							END
						ELSE
							Err0(90)
						END
					ELSIF s0 IN {7,8} THEN (* get stop resp *)
						IF y = MMCSTOPTRANSMISSION THEN
							IF s0 = 7 THEN
								IF 8 IN resp THEN
									s0 := 0
								ELSE
									Err(95)
								END
							ELSE (* s0 = 8 *)
								(* XXX: check in PRG state?... *)
								s0 := 0
							END
						ELSE
							Err(105)
						END
					ELSE
						E0(110)
					END
				END;
				(*
				IF DataBlockEnd IN x THEN (* Data block sent/received (CRC check passed) *)
				END;
				*)
				IF s0 # 0 THEN
					IF DataEnd IN x THEN (* Data end (data counter is zero) *)
						IF s0 = 3 THEN (* wait read block(s) *)
							IF s2 = 1 THEN
								s0 := 0
							ELSE (* s2 > 1 *)
								s0 := 7;
								StartSendStop
							END
						ELSIF s0 = 6 THEN (* wait write block(s) *)
							IF s2 = 1 THEN
								s0 := 0
							ELSE (* s2 > 1 *)
								s0 := 8;
								StartSendStop
							END
						ELSE
							Err(115)
						END
					END
				END
			END

		(* ELSE ASSERT(x = {}) *) (* spurious interrupt? *) (* FIXME *)
		END;
		SYSTEM.PUT(LPC2378.VICAddress, 0)
	END InterruptsHandlerMCI;

	PROCEDURE* InitDMA;
		CONST
			(* PCONP bits *)
				PCGPDMA = 29;
		VAR x: SET;
	BEGIN
		(* GPDMA *)
			SYSTEM.GET(LPC2378.PCONP, x);
			SYSTEM.PUT(LPC2378.PCONP, x + {PCGPDMA});

			(* clear all interrupts on channel 1 *)
				SYSTEM.PUT(LPC2378.DMACIntTCClear, {1});
				SYSTEM.PUT(LPC2378.DMACIntERRClear, {1});

			SYSTEM.PUT(LPC2378.DMACConfiguration, {0}); (* Enable DMA channels, little endian *)
			REPEAT UNTIL SYSTEM.BIT(LPC2378.DMACConfiguration, 0)
	END InitDMA;

	PROCEDURE* DecodeCID (CID: ARRAY OF SET; VAR serialNo: INTEGER);
	BEGIN
		CASE cardType OF cardTypeMMC:
			serialNo := LSL(SYSTEM.VAL(INTEGER, CID[2]), 16) + LSR(SYSTEM.VAL(INTEGER, CID[3]), 16)
		| cardTypeSD,cardTypeSDHC:
			serialNo := LSL(SYSTEM.VAL(INTEGER, CID[2]), 8) + LSR(SYSTEM.VAL(INTEGER, CID[3]), 24)
		END
	END DecodeCID;

	PROCEDURE DecodeCSD (CSD: ARRAY OF SET; VAR READBLLEN, WRITEBLLEN, nBlocks, rate: INTEGER);
		VAR CSDSTRUCTURE: INTEGER;
			CSIZE, CSIZEMULT: INTEGER;

		PROCEDURE* DecodeTRANSPEED (TRANSPEED: INTEGER): INTEGER;
			VAR x: INTEGER;
		BEGIN
			CASE TRANSPEED MOD 8 OF 0: x := 100000
			| 1: x := 1000000
			| 2: x := 10000000
			| 3: x := 100000000
			END;

			CASE TRANSPEED DIV 8 MOD 16 OF 1:
			| 2: x := (x * 6) DIV 5
			| 3: x := (x * 13) DIV 10
			| 4: x := (x * 3) DIV 2
			| 5: x := x * 2
			| 6: x := (x * 5) DIV 2
			| 7: x := x * 3
			| 8: x := (x * 7) DIV 2
			| 9: x := x * 4
			| 10: x := (x * 9) DIV 2
			| 11: x := x * 5
			| 12: x := (x * 11) DIV 2
			| 13: x := x * 6
			| 14: x := x * 7
			| 15: x := x * 8
			END
		RETURN x
		END DecodeTRANSPEED;

	BEGIN
		rate := DecodeTRANSPEED(SYSTEM.VAL(INTEGER, CSD[0]) MOD 256);

		READBLLEN := LSR(SYSTEM.VAL(INTEGER, CSD[1]), 16) MOD 16;
		WRITEBLLEN := LSR(SYSTEM.VAL(INTEGER, CSD[3]), 22) MOD 16;

		CASE cardType OF cardTypeMMC:
		| cardTypeSD,cardTypeSDHC:
			CSDSTRUCTURE := LSR(SYSTEM.VAL(INTEGER, CSD[0]), 30);
			CASE cardType OF cardTypeSD:
				ASSERT(CSDSTRUCTURE = 0, 105); (* 1.0 *)
				ASSERT(READBLLEN IN {9,10,11}, 106) (* 512 B | 1024 B | 2048 B *)
			| cardTypeSDHC:
				ASSERT(CSDSTRUCTURE = 1, 107); (* 2.0 *)
				ASSERT(READBLLEN = 9, 108) (* 512 B *)
			END;
			ASSERT(READBLLEN = WRITEBLLEN, 109)
		END;

		CASE cardType OF cardTypeMMC,cardTypeSD:
			CSIZE := (LSL(SYSTEM.VAL(INTEGER, CSD[1]), 2) + LSR(SYSTEM.VAL(INTEGER, CSD[2]), 30)) MOD 4096;
			CSIZEMULT := LSR(SYSTEM.VAL(INTEGER, CSD[2]), 15) MOD 8;
		| cardTypeSDHC:
			CSIZE := LSL(SYSTEM.VAL(INTEGER, CSD[1]) MOD 64, 16) + LSR(SYSTEM.VAL(INTEGER, CSD[2]), 16);
			(* (cardType = cardTypeSDXC) = (CSIZE >= 65535) *)
			CSIZEMULT := 8
		END;

		nBlocks := LSL(CSIZE + 1, CSIZEMULT + 2)
	END DecodeCSD;

	PROCEDURE Delay0 (n: INTEGER);
		VAR i: INTEGER;
	BEGIN
		i := 0; WHILE i < n DO INC(i) END
	END Delay0;

	PROCEDURE Init* (MCIPWRActiveHigh: BOOLEAN; VAR ok: BOOLEAN);
		CONST
			(* PCONP bits *)
				PCSDC = 28;
		VAR x: SET;

		PROCEDURE SetMCIClock (MCLCLK: INTEGER);
			CONST
				(* MCIClock bits *)
					PwrSave = 9;
				(* extra = {}; *)
				extra = {PwrSave}; (* as in Keil MCI_LPC23xx.c *)
			VAR clkDiv: INTEGER;
				x: SET;
		BEGIN
			ASSERT(MCLCLK > 0, 110);

			clkDiv := MCLK DIV (2 * MCLCLK) - 1;
			ASSERT(clkDiv >= 0, 111);
			ASSERT(clkDiv <= 255, 112);

			SYSTEM.GET(LPC2378.MCIClock, x);
			SYSTEM.PUT(LPC2378.MCIClock, x * {9,10,11} + {8} + extra + SYSTEM.VAL(SET, clkDiv));
			Delay0(10H) (* delay 3MCLK + 2PCLK before next write *)
		END SetMCIClock;

		PROCEDURE Configure0;
			VAR ok: BOOLEAN;
				READBLLEN, WRITEBLLEN: INTEGER;
				CID, CSD: ARRAY 4 OF SET;

			PROCEDURE SetOpenDrainMode (mode: BOOLEAN);
				VAR x: SET;
			BEGIN
				SYSTEM.GET(LPC2378.MCIPower, x);
				IF mode THEN
					SYSTEM.PUT(LPC2378.MCIPower, x + {6}) (* Set Open Drain output control for MMC *)
				ELSE (* PUSHPULLMODE *)
					SYSTEM.PUT(LPC2378.MCIPower, x - {6}) (* Clear Open Drain output control for SD *)
				END;
				Delay0(3000H)
			END SetOpenDrainMode;

			PROCEDURE Command (cmd, arg, respType: INTEGER; VAR status: SET; VAR resp: ARRAY OF SET);

				PROCEDURE WaitResp0 (cmd, respType: INTEGER; VAR status: SET; VAR resp: ARRAY OF SET);
					VAR y: INTEGER;

					(* ignore CRC errors *)
					PROCEDURE* Cmd0 (cmd: INTEGER): BOOLEAN;
					RETURN (cmd = MMCSENDOPCOND) OR (cmd = SDAPPOPCOND) OR (cmd = MMCSTOPTRANSMISSION)
					END Cmd0;

					(* ignore RespCmd *)
					PROCEDURE* Cmd1 (cmd: INTEGER): BOOLEAN;
					RETURN (cmd = MMCSENDOPCOND) OR (cmd = SDAPPOPCOND) OR (cmd = MMCALLSENDCID) OR (cmd = MMCSENDCSD)
					END Cmd1;

				BEGIN
					CASE respType OF respTypeNone:
						(* wait until command finished *)
							REPEAT UNTIL ~SYSTEM.BIT(LPC2378.MCIStatus, CmdActive);
						SYSTEM.PUT(LPC2378.MCIClear, MCIClearMask);
						status := {}
					| respTypeShort,respTypeLong:
						REPEAT
							SYSTEM.GET(LPC2378.MCIStatus, status)
						UNTIL status * {CmdTimeOut,CmdCrcFail,CmdRespEnd} # {};

						SYSTEM.PUT(LPC2378.MCIClear, status * MCIClearMask);

						IF CmdTimeOut IN status THEN
							SYSTEM.PUT(LPC2378.MCICommand, 0);
							SYSTEM.PUT(LPC2378.MCIArgument, -1)
						ELSIF (CmdCrcFail IN status) & ~Cmd0(cmd) THEN
						ELSE (* (CmdRespEnd IN status) OR (CmdCrcFail IN status) & Cmd0(cmd) THEN *)
							IF CmdCrcFail IN status THEN
								SYSTEM.PUT(LPC2378.MCICommand, 0);
								SYSTEM.PUT(LPC2378.MCIArgument, -1)
							END;

							SYSTEM.GET(LPC2378.MCIRespCmd, y);
							IF (y MOD 64 = cmd) OR Cmd1(cmd) THEN
								SYSTEM.GET(LPC2378.MCIResponse0, resp[0]);
								IF respType = respTypeLong THEN
									SYSTEM.GET(LPC2378.MCIResponse1, resp[1]);
									SYSTEM.GET(LPC2378.MCIResponse2, resp[2]);
									SYSTEM.GET(LPC2378.MCIResponse3, resp[3])
								END;
								status := {}
							ELSE
								status := {0..31} (* invalid resp *)
							END
						END
					END
				END WaitResp0;

			BEGIN
				ASSERT( ~SYSTEM.BIT(LPC2378.MCIStatus, CmdActive), 113 );

				SendCmd0(cmd, arg, respType);
				WaitResp0(cmd, respType, status, resp)
			END Command;

			PROCEDURE SendACMD (i: INTEGER): BOOLEAN;
				CONST
					ACMDENABLE = 5;
				VAR ok: BOOLEAN;
					arg: INTEGER;
					status: SET; resp: ARRAY 4 OF SET;
			BEGIN
				ASSERT(i > 0, 114);

				CASE cardType OF cardTypeUnknown(*ok*),cardTypeMMC:
					arg := 0
				| cardTypeSD,cardTypeSDHC:
					arg := CardRCA
				END;

				ok := FALSE;
				REPEAT
					Command(MMCAPPCMD, arg, respTypeShort, status, resp);
					IF (status = {}) & (ACMDENABLE IN resp[0]) THEN
						ok := TRUE
					ELSE
						Delay0(20H);
						DEC(i)
					END
				UNTIL (i = 0) OR ok
			RETURN ok
			END SendACMD;

			PROCEDURE DetectCardType (): INTEGER;
				VAR res: INTEGER;
					isVer20: BOOLEAN;

				(* CMD0, very first command *)
				PROCEDURE GoIdleState (): BOOLEAN;
					VAR ok: BOOLEAN;
						i: INTEGER;
						status: SET; resp: ARRAY 4 OF SET;
				BEGIN
					i := 20H;
					REPEAT
						Command(MMCGOIDLESTATE, 0, respTypeNone, status, resp);
						DEC(i)
					UNTIL (i = 0) OR (status = {})
				RETURN status = {}
				END GoIdleState;

				(* CMD8 *)
				PROCEDURE SendIfCmd (i: INTEGER): BOOLEAN;
					VAR ok: BOOLEAN;
						status: SET;
						resp: ARRAY 4 OF SET;
				BEGIN
					ASSERT(i > 0, 115);
					REPEAT
						(* Send CMD8 command repeatedly until the response is back correctly *)
						Command(SDSENDIFCOND, 1AAH, respTypeShort, status, resp);
						ok := (status = {}) & (resp[0] * {0..11} = SYSTEM.VAL(SET, 1AAH));
						IF ~ok THEN
							Delay0(20H)
						END;
						DEC(i)
					UNTIL (i = 0) OR ok
				RETURN ok
				END SendIfCmd;

				(* CMD1, for MMC *)
				PROCEDURE SendOPCond (i: INTEGER): BOOLEAN;
					VAR ok: BOOLEAN;
						status: SET; resp: ARRAY 4 OF SET;
				BEGIN
					ASSERT(i > 0, 116);
					ok := FALSE;
					REPEAT
						Command(MMCSENDOPCOND, OCRINDEX, respTypeShort, status, resp);
						IF ~(CmdTimeOut IN status) & (31 IN resp[0]) THEN
							ok := TRUE
						ELSE
							Delay0(20H);
							DEC(i)
						END
					UNTIL (i = 0) OR ok
				RETURN ok
				END SendOPCond;

				(*
					res:
						0: fail
						1: ok (SD)
						2: ok (SDHC)
				*)
				PROCEDURE SendACMDOPCond (i: INTEGER; isVer20: BOOLEAN): INTEGER;
					VAR useHCS: INTEGER;
						res: INTEGER;
						status: SET; resp: ARRAY 4 OF SET;
				BEGIN
					ASSERT(i > 0, 117);

					IF isVer20 THEN
						useHCS := 40000000H
					ELSE
						useHCS := 0
					END;

					res := 0;
					REPEAT
						IF SendACMD(20) THEN
							Command(SDAPPOPCOND, OCRINDEX + useHCS, respTypeShort, status, resp);
							IF ~(CmdTimeOut IN status) & (31 IN resp[0]) & (resp[0] # {0..31}) THEN
								(* response is back and correct. *)
								(* if the card is V2.0, check if the card is SDHC *)
								IF isVer20 & (30 IN resp[0]) THEN
									res := 2
								ELSE
									res := 1
								END
							END
						END;
						IF res = 0 THEN
							Delay0(20H);
							DEC(i)
						END
					UNTIL (i = 0) OR (res # 0)
				RETURN res
				END SendACMDOPCond;

			BEGIN
				res := cardTypeUnknown;
				IF GoIdleState() THEN
					isVer20 := FALSE;

					(* Start sending CMD8, and check if this is SD card V2.0 *)
					IF SendIfCmd(5) THEN
						isVer20 := TRUE
					ELSE
						(* If this is not SD V2.0, check if it is MMC card *)
						SetOpenDrainMode(TRUE);
						IF SendOPCond(200H) THEN
							res := cardTypeMMC
						ELSE
							SetOpenDrainMode(FALSE)
						END
					END;

					IF res = cardTypeUnknown THEN
						res := SendACMDOPCond(200H, isVer20);
						CASE res OF 0:
							res := cardTypeUnknown
						| 1:
							res := cardTypeSD
						| 2:
							res := cardTypeSDHC
						END
					END
				END
			RETURN res
			END DetectCardType;

			(* CMD2, after CMD1 (MMC) or ACMD41 (SD) *)
			PROCEDURE ReadCID (VAR resp: ARRAY OF SET; VAR ok: BOOLEAN);
				VAR i: INTEGER;
					status: SET;
			BEGIN
				i := 50;
				ok := FALSE;
				REPEAT
					Command(MMCALLSENDCID, 0, respTypeLong, status, resp);
					IF ~(CmdTimeOut IN status) THEN
						ok := TRUE
					ELSE
						Delay0(20H);
						DEC(i)
					END
				UNTIL (i = 0) OR ok
			END ReadCID;

			(* CMD9, after CMD3 *)
			PROCEDURE ReadCSD (VAR resp: ARRAY OF SET; VAR ok: BOOLEAN);
				VAR i: INTEGER;
					arg: INTEGER;
					status: SET;
			BEGIN
				CASE cardType OF cardTypeMMC:
					arg := 10000H
				| cardTypeSD,cardTypeSDHC:
					arg := CardRCA
				END;

				i := 20H;
				ok := FALSE;
				REPEAT
					Command(MMCSENDCSD, arg, respTypeLong, status, resp);
					IF status = {} THEN
						ok := TRUE
					ELSE
						Delay0(20H);
						DEC(i)
					END
				UNTIL (i = 0) OR ok
			END ReadCSD;

			(* CMD3, after CMD2 *)
			PROCEDURE SetAddress (VAR ok: BOOLEAN);
				VAR i: INTEGER;
					arg: INTEGER;
					status: SET; resp: ARRAY 4 OF SET;
			BEGIN
				CASE cardType OF cardTypeMMC:
					arg := 10000H
				| cardTypeSD,cardTypeSDHC:
					arg := 0
				END;

				i := 20H;
				ok := FALSE;
				REPEAT
					Command(MMCSETRELATIVEADDR, arg, respTypeShort, status, resp);
					IF ~(CmdTimeOut IN status) & (resp[0] * {8..11} = {8,10}) THEN
						CardRCA := SYSTEM.VAL(INTEGER, resp[0] * {16..31});
						ok := TRUE
					ELSE
						Delay0(20H);
						DEC(i)
					END
				UNTIL (i = 0) OR ok
			END SetAddress;

			(* CMD7, after CMD9 *)
			PROCEDURE SelectCard (VAR ok: BOOLEAN);
				VAR i: INTEGER;
					arg: INTEGER;
					status: SET; resp: ARRAY 4 OF SET;
			BEGIN
				CASE cardType OF cardTypeMMC:
					arg := 10000H
				| cardTypeSD,cardTypeSDHC:
					arg := CardRCA
				END;

				i := 20H;
				ok := FALSE;
				REPEAT
					Command(MMCSELECTCARD, arg, respTypeShort, status, resp);
					IF (status = {}) OR (resp[0] * {8..11} = {8,9,10}) THEN
						ok := TRUE
					ELSE
						Delay0(20H);
						DEC(i)
					END
				UNTIL (i = 0) OR ok
			END SelectCard;

			(* ACMD6 *)
			PROCEDURE SDSetBusWidth (bw4bit: BOOLEAN; VAR ok: BOOLEAN);
				VAR x: SET;
					i: INTEGER;
					arg: INTEGER;
					status: SET; resp: ARRAY 4 OF SET;
			BEGIN
				ASSERT(cardType IN {cardTypeSD,cardTypeSDHC}, 118);

				Delay0(10H); (* delay 3MCLK + 2PCLK *)

				SYSTEM.GET(LPC2378.MCIClock, x);
				IF bw4bit THEN (* 4 bit bus *)
					SYSTEM.PUT(LPC2378.MCIClock, x + {11});
					arg := 10
				ELSE (* 1 bit bus *)
					SYSTEM.PUT(LPC2378.MCIClock, x - {11});
					arg := 0
				END;

				i := 20H;
				ok := FALSE;
				REPEAT
					IF SendACMD(20) THEN
						Command(SDAPPSETBUSWIDTH, arg, respTypeShort, status, resp);
						IF (status = {}) & (resp[0] * {8..11} = {8,11}) THEN
							ok := TRUE
						END
					END;
					IF ~ok THEN
						Delay0(20H);
						DEC(i)
					END
				UNTIL (i = 0) OR ok
			END SDSetBusWidth;

			(* CMD16, after CMD7 *)
			(* blockLen, B *)
			PROCEDURE SetBlockLen (blockLen: INTEGER; VAR ok: BOOLEAN);
				VAR i: INTEGER;
					status: SET; resp: ARRAY 4 OF SET;
			BEGIN
				ASSERT(blockLen > 0, 119);
				(* ASSERT(blockLen MOD 4 = 0, 120); *)

				i := 20H;
				ok := FALSE;
				REPEAT
					Command(MMCSETBLOCKLEN, blockLen, respTypeShort, status, resp);
					IF (status = {}) & (resp[0] * {8..11} = {8,11}) THEN
						ok := TRUE
					ELSE
						Delay0(20H);
						DEC(i)
					END
				UNTIL (i = 0) OR ok
			END SetBlockLen;

		BEGIN
			cardType := DetectCardType();
			IF cardType IN {cardTypeMMC,cardTypeSD,cardTypeSDHC} THEN
				ReadCID(CID, ok); (* CMD2 *)
				ASSERT(ok, 131);
				DecodeCID(CID, serialNo);

				SetAddress(ok); (* CMD3 *)
				ASSERT(ok, 132);

				ReadCSD(CSD, ok); (* CMD9 *)
				ASSERT(ok, 133);
				DecodeCSD(CSD, READBLLEN, WRITEBLLEN, nBlocks, rate);
				ASSERT(rate >= 400000, 134);
				IF rate > 25000000 THEN rate := 25000000 END;
				ASSERT(READBLLEN = WRITEBLLEN, 135);

				IF READBLLEN # blockLenN THEN
					ASSERT(READBLLEN > blockLenN, 136);
					nBlocks := nBlocks * LSL(1, READBLLEN - blockLenN)
				END;
				ASSERT(nBlocks > 0, 139); (* 1 TB limit *)

				IF cardType = cardTypeMMC THEN (* Disable Open Drain mode for MMC *)
					SetOpenDrainMode(FALSE)
				END;

				SelectCard(ok); (* CMD7 *)
				ASSERT(ok, 140);

				SetMCIClock(rate);

				CASE cardType OF cardTypeMMC:
				| cardTypeSD,cardTypeSDHC:
					SDSetBusWidth(TRUE, ok);
					ASSERT(ok, 141)
				END;

				SetBlockLen(blockLen, ok); (* CMD16 *)
				ASSERT(ok, 142)
			END
		END Configure0;

	BEGIN
		SYSTEM.PUT(LPC2378.VICIntEnClr, {24}); (* disable MCI interrupts *)

		(* MCI *)
			cardType := cardTypeUnknown;
			serialNo := 0;
			CardRCA := 0;
			s0 := 0; err := 0;

			nBlocks := 0;
			serialNo := 0;

			SYSTEM.GET(LPC2378.PCONP, x);
			SYSTEM.PUT(LPC2378.PCONP, x + {PCSDC});

			SYSTEM.GET(LPC2378.PCLKSEL1, x);
			SYSTEM.PUT(LPC2378.PCLKSEL1, x + {24} - {25}); (* MCLK = CCLK *)
			MCLK := LPC.CCLK;

			SYSTEM.PUT(LPC2378.MCIClock, 0);

			SYSTEM.PUT(LPC2378.MCIPower, 0);
			Delay0(1000H);

			(* disable interrupts *)
				SYSTEM.PUT(LPC2378.MCIMask0, {});
				SYSTEM.PUT(LPC2378.MCIMask1, {});

			(* PINs *)
				SYSTEM.GET(LPC2378.PINSEL1, x);
				SYSTEM.PUT(LPC2378.PINSEL1, x + {7,9,11,13} - {6,8,10,12}); (* P0.19 -- P0.22: MCI function *)

				SYSTEM.GET(LPC2378.PINSEL4, x);
				SYSTEM.PUT(LPC2378.PINSEL4, x + {23,25,27} - {22,24,26}); (* P2.11 -- P2.13: MCI function *)

				(* PINMODE not touched *)

				(* MCIPWR Active Level *)
					SYSTEM.GET(LPC2378.SCS, x);
					IF MCIPWRActiveHigh THEN
						SYSTEM.PUT(LPC2378.SCS, x + {3}) (* MCIPWR pin is high *)
					ELSE
						SYSTEM.PUT(LPC2378.SCS, x - {3}) (* MCIPWR pin is low *)
					END;

			SYSTEM.PUT(LPC2378.MCICommand, 0);
			SYSTEM.PUT(LPC2378.MCIDataCtrl, 0);
			SYSTEM.PUT(LPC2378.MCIClear, MCIClearMask);

			SYSTEM.PUT(LPC2378.MCIPower, {1}); (* power up *)
			REPEAT UNTIL SYSTEM.BIT(LPC2378.MCIPower, 1);
			Delay0(100H);
			(*
				During identification phase, the clock should be less than
				400Khz. Once we pass this phase, the normal clock can be set up
				to 25Mhz on SD card and 20Mhz on MMC card.
			*)
			rate := 400000;
			SetMCIClock(rate);
			SYSTEM.PUT(LPC2378.MCIPower, {0,1});
			Delay0(2000H);

			(* Configure the Vectored Interrupt Controller *)
				(* Use IRQ not FIQ for MCI *)
					SYSTEM.GET(LPC2378.VICIntSelect, x);
					SYSTEM.PUT(LPC2378.VICIntSelect, x - {24});
				(* Install handler *)
					SYSTEM.PUT(LPC2378.VICVectAddr24, SYSTEM.ADR(InterruptsHandlerMCI));

		Configure0;

		CASE cardType OF cardTypeMMC,cardTypeSD,cardTypeSDHC:
			InitDMA;

			SYSTEM.GET(LPC2378.MCIStatus, x);
			ASSERT(x * MCIClearMask = {}, 143);

			SYSTEM.PUT(LPC2378.MCIMask0, MCIClearMask);
			SYSTEM.PUT(LPC2378.MCIMask1, MCIClearMask);

			(* Enable MCI interrupt *)
				SYSTEM.PUT(LPC2378.VICIntEnable, {24});

			ok := TRUE
		| cardTypeUnknown:
			ok := FALSE
		END
	END Init;

	PROCEDURE StartSendStatus;
		VAR arg: INTEGER;
	BEGIN
		CASE cardType OF cardTypeMMC:
			arg := 10000H
		| cardTypeSD,cardTypeSDHC:
			arg := CardRCA
		END;

		SendCmd0(MMCSENDSTATUS, arg, respTypeShort)
	END StartSendStatus;

	PROCEDURE* Copy4 (srcAdr, dstAdr: INTEGER; n: INTEGER);
		VAR x: INTEGER;
	BEGIN
		ASSERT(n > 0, 144);
		REPEAT
			SYSTEM.GET(srcAdr, x);
			SYSTEM.PUT(dstAdr, x);
			INC(srcAdr, 4);
			INC(dstAdr, 4);
			DEC(n)
		UNTIL n = 0
	END Copy4;

	PROCEDURE* Busy* (): BOOLEAN;
	(* CRITICAL *)
		RETURN s0 # 0
	END Busy;

	PROCEDURE StartWrite* (blockN, blocks: INTEGER; adr: INTEGER; VAR ok: BOOLEAN);
	BEGIN
		ASSERT(blockN >= 0, 145); (* 1 TB limit *)
		ASSERT(blocks > 0);
		ASSERT(blocks <= writeBlocksLen DIV blockLen);
		ASSERT(blockN <= nBlocks - blocks, 146); (* 1 TB limit *)
		ASSERT(adr MOD 4 = 0, 147);

		IF ~Busy() THEN
			IF adr # writeBlocksAdr THEN
				Copy4(adr, writeBlocksAdr, blocks * (blockLen DIV 4))
			END;

			err := 0;
			s0 := 4;
			s1 := blockN;
			s2 := blocks;
			StartSendStatus;

			ok := TRUE
		ELSE
			ok := FALSE
		END
	END StartWrite;

	PROCEDURE StartRead* (blockN, blocks: INTEGER; VAR ok: BOOLEAN);
	BEGIN
		ASSERT(blockN >= 0, 148); (* 1 TB limit *)
		ASSERT(blocks > 0);
		ASSERT(blocks <= readBlocksLen DIV blockLen);
		ASSERT(blockN <= nBlocks - blocks, 149); (* 1 TB limit *)

		IF ~Busy() THEN
			err := 0;
			s0 := 1;
			s1 := blockN;
			s2 := blocks;
			StartSendStatus;

			ok := TRUE
		ELSE
			ok := FALSE
		END
	END StartRead;

END MCI.
mci_algo.png
Example: write block 0, then read.

Code: Select all

PROCEDURE Test1;
	CONST MCIPWRActiveHigh = FALSE; (* see your circuity for details *)
	VAR ok: BOOLEAN;
		i: INTEGER;
		x: CHAR;
BEGIN
	MCI.Init(MCIPWRActiveHigh, ok);
	ASSERT(ok); (* card not present, card type unknown or circuity problem *)

	(* fill 1 data block in RAM *)
		i := 0;
		WHILE i < MCI.blockLen DO
			SYSTEM.PUT(MCI.writeBlocksAdr + i, 0X);
			INC(i)
		END;

	(* write 1 data block from RAM to card *)
		MCI.StartWrite(0 (* block index *), 1 (* number of blocks *), MCI.writeBlocksAdr, ok);
		ASSERT(ok); (* ~MCI.Busy() before StartWrite *)
		REPEAT UNTIL ~MCI.Busy(); (* wait until operation complete *)
		ASSERT(MCI.err = MCI.errOk); (* # MCI.errNotInTrans, because this is very first write operation *)
			(* notInTrans means write in progress *)
			(* handle MCI.err when ~MCI.Busy() only *)

	(* wait write complete, then read 1 data block from card to RAM *)
		REPEAT
			MCI.StartRead(0 (* block index *), 1 (* number of blocks *), ok);
			ASSERT(ok); (* ~MCI.Busy() before MCI.StartRead *)
			REPEAT UNTIL ~MCI.Busy(); (* wait until operation complete *)
			ASSERT((MCI.err = MCI.errOk) OR (MCI.err = MCI.errNotInTrans))
		UNTIL MCI.err = MCI.errOk;

	(* check read block in RAM *)
		i := 0;
		WHILE i < MCI.blockLen DO
			SYSTEM.GET(MCI.readBlocksAdr + i, x);
			ASSERT(x = 0X)
		END
END Test1.
Easy, blocking read and write operations:

Code: Select all

MODULE EasyMCI;

	(*
		A. V. Shiryaev, 2012.05, 2012.06

		Easy, blocking MCI read/write operations
	*)

	IMPORT MCI;

	PROCEDURE Write* (blockN, blocks: INTEGER; adr: INTEGER; VAR res: INTEGER);
		VAR ok: BOOLEAN;
	BEGIN
		REPEAT
			MCI.StartWrite(blockN, blocks, adr, ok);
			IF ok THEN (* ~MCI.Busy() before MCI.StartWrite *)
				REPEAT UNTIL ~MCI.Busy()
			END
		UNTIL ok & (MCI.err # MCI.errNotInTrans);
		res := MCI.err
	END Write;

	PROCEDURE Read* (blockN, blocks: INTEGER; VAR res: INTEGER);
		VAR ok: BOOLEAN;
	BEGIN
		REPEAT
			MCI.StartRead(blockN, blocks, ok);
			IF ok THEN (* ~MCI.Busy() before MCI.StartRead *)
				REPEAT UNTIL ~MCI.Busy()
			END
		UNTIL ok & (MCI.err # MCI.errNotInTrans);
		res := MCI.err
	END Read;

END EasyMCI.
You do not have the required permissions to view the files attached to this post.
Last edited by Alexander Shiryaev on Thu Jun 21, 2012 6:20 pm, edited 8 times in total.

Alexander Shiryaev
Posts: 12
Joined: Mon Apr 04, 2011 7:20 pm
Location: Russia
Contact:

Re: LPC2378 MCI driver

Post by Alexander Shiryaev » Sun Jun 10, 2012 9:04 am

There is some mistakes. I forgot to put the brackets :(

MCI.mod:

Code: Select all

SYSTEM.PUT(LPC2378.MCIDataTimer, blocks * 100 * rate DIV 1000)
=>

Code: Select all

SYSTEM.PUT(LPC2378.MCIDataTimer, blocks * 100 * (rate DIV 1000))

Code: Select all

SYSTEM.PUT(LPC2378.MCIDataTimer, blocks * 300 * rate DIV 1000)
=>

Code: Select all

SYSTEM.PUT(LPC2378.MCIDataTimer, blocks * 300 * (rate DIV 1000))

Code: Select all

Copy4(adr, writeBlocksAdr, blocks * blockLen DIV 4)
=>

Code: Select all

Copy4(adr, writeBlocksAdr, blocks * (blockLen DIV 4))
First post updated.

Alexander Shiryaev
Posts: 12
Joined: Mon Apr 04, 2011 7:20 pm
Location: Russia
Contact:

Re: LPC2378 MCI driver

Post by Alexander Shiryaev » Tue Jun 19, 2012 10:03 pm

Some refinement.

DMA controller has some "feature".
On intensive peripherial access (such as REPEAT UNTIL SYSTEM.BIT(LPC.U0LSR, 5), Timer.MSecDelay etc.) after StartWrite/StartRead (i.e. when MCI.Busy()), DMA controller does not have time to work. This leads to MCI errors txUnderrun/rxOverrun.
In these cases, the card not in TRANS state. STOP command should be sent.

=> NOTE: Module consumers should not query peripherials when MCI.Busy(), or handle txUnderrun/rxOverrun errors (repeat operation if occur).

Also converted to Oberon-2011.

Code: Select all

--- MCI.mod.old	Mon Jun 18 13:00:37 2012
+++ MCI.mod.new	Wed Jun 20 01:29:57 2012
@@ -120,6 +120,8 @@
 		(* err codes *)
 			errOk* = 0;
 			errNotInTrans* = 80;
+			errTxUnderrun* = 200;
+			errRxOverrun* = 205;
 
 	VAR
 		cardType*: INTEGER;
@@ -154,7 +156,7 @@
 			s2: INTEGER; (* number of blocks to read/write *)
 			err*: INTEGER;
 
-	PROCEDURE* SendCmd0 (CONST cmd, arg, respType: INTEGER);
+	PROCEDURE* SendCmd0 (cmd, arg, respType: INTEGER);
 		VAR x: SET;
 	BEGIN
 		ASSERT(cmd >= 0, 100);
@@ -174,7 +176,7 @@
 
 	(* to call from InterruptsHandlerMCI *)
 
-		PROCEDURE StartSendReadBlock (CONST blockN: INTEGER; CONST multiple: BOOLEAN);
+		PROCEDURE StartSendReadBlock (blockN: INTEGER; multiple: BOOLEAN);
 			VAR cmd, arg: INTEGER;
 		BEGIN
 			IF multiple THEN
@@ -192,7 +194,7 @@
 			SendCmd0(cmd, arg, respTypeShort)
 		END StartSendReadBlock;
 
-		PROCEDURE StartSendWriteBlock (CONST blockN: INTEGER; CONST multiple: BOOLEAN);
+		PROCEDURE StartSendWriteBlock (blockN: INTEGER; multiple: BOOLEAN);
 			VAR cmd, arg: INTEGER;
 		BEGIN
 			IF multiple THEN
@@ -216,7 +218,7 @@
 			SendCmd0(MMCSTOPTRANSMISSION, 0, respTypeShort)
 		END StartSendStop;
 
-		PROCEDURE* DMAMove (CONST channel, mode: INTEGER);
+		PROCEDURE* DMAMove (channel, mode: INTEGER);
 			CONST
 				MBS = 4; (* : 32 *)
 				(* MBS = 2; (* : 8 *) *)
@@ -297,7 +299,7 @@
 			SYSTEM.PUT(LPC2378.DMACConfiguration, {0}) (* Enable DMA channels, little endian *)
 		END DMAMove;
 
-		PROCEDURE StartReadBlocksTransfer (CONST blocks: INTEGER);
+		PROCEDURE StartReadBlocksTransfer (blocks: INTEGER);
 			VAR DataCtrl: INTEGER;
 		BEGIN
 			(* XXX: by TAAC *)
@@ -319,7 +321,7 @@
 			SYSTEM.PUT(LPC2378.MCIDataCtrl, DataCtrl)
 		END StartReadBlocksTransfer;
 
-		PROCEDURE StartWriteBlocksTransfer (CONST blocks: INTEGER);
+		PROCEDURE StartWriteBlocksTransfer (blocks: INTEGER);
 			VAR DataCtrl: INTEGER;
 		BEGIN
 			(* XXX: by TAAC & R2W *)
@@ -340,13 +342,13 @@
 			SYSTEM.PUT(LPC2378.MCIDataCtrl, DataCtrl)
 		END StartWriteBlocksTransfer;
 
-		PROCEDURE* Err0 (CONST n: INTEGER);
+		PROCEDURE* Err0 (n: INTEGER);
 		BEGIN
 			err := n;
 			s0 := 0
 		END Err0;
 
-		PROCEDURE* Err (CONST n: INTEGER);
+		PROCEDURE* Err (n: INTEGER);
 		BEGIN
 			err := n;
 			s0 := 0;
@@ -354,7 +356,7 @@
 			SYSTEM.PUT(LPC2378.MCIDataCtrl, 0)
 		END Err;
 
-		PROCEDURE E0 (CONST e: INTEGER);
+		PROCEDURE E0 (e: INTEGER);
 		BEGIN
 			IF s0 = 3 THEN (* wait read block(s) *)
 				IF s2 = 1 THEN
@@ -420,9 +422,37 @@
 				ELSIF DataTimeOut IN x THEN (* Data timeout *)
 					E0(25)
 				ELSIF TxUnderrun IN x THEN (* Transmit FIFO underrun error *)
-					E0(30)
+					IF s0 = 6 THEN
+						(*
+							2012.06.19
+							this may occur if some LPC2378 peripherial touched
+							(such as REPEAT UNTIL SYSTEM.BIT(LPC.U0LSR, 5) etc.)
+							when Busy() after StartWrite;
+							send STOP in any case, because card not in TRANS state here
+						*)
+						SYSTEM.PUT(LPC2378.MCIDataCtrl, 0);
+						err := errTxUnderrun; (* may be overwritten if STOPTRANSMISSION fail *)
+						s0 := 8;
+						StartSendStop
+					ELSE
+						E0(30)
+					END
 				ELSIF RxOverrun IN x THEN (* Receive FIFO overrun error *)
-					E0(35)
+					IF s0 = 3 THEN
+						(*
+							2012.06.19
+							this may occur if some LPC2378 peripherial touched
+							(such as REPEAT UNTIL SYSTEM.BIT(LPC.U0LSR, 5) etc.)
+							when Busy() after StartRead;
+							send STOP in any case, because card not in TRANS state here
+						*)
+						SYSTEM.PUT(LPC2378.MCIDataCtrl, 0); (* required too *)
+						err := errRxOverrun; (* may be overwritten if STOPTRANSMISSION fail *)
+						s0 := 7;
+						StartSendStop
+					ELSE
+						E0(35)
+					END
 				ELSIF StartBitErr IN x THEN (* Start bit not detected on all data signals in wide bus mode *)
 					E0(40)
 				ELSIF CmdSent IN x THEN (* Command sent (no response required) *)
@@ -553,7 +583,7 @@
 			REPEAT UNTIL SYSTEM.BIT(LPC2378.DMACConfiguration, 0)
 	END InitDMA;
 
-	PROCEDURE* DecodeCID (CONST CID: ARRAY OF SET; VAR serialNo: INTEGER);
+	PROCEDURE* DecodeCID (CID: ARRAY OF SET; VAR serialNo: INTEGER);
 	BEGIN
 		CASE cardType OF cardTypeMMC:
 			serialNo := LSL(SYSTEM.VAL(INTEGER, CID[2]), 16) + LSR(SYSTEM.VAL(INTEGER, CID[3]), 16)
@@ -562,11 +592,11 @@
 		END
 	END DecodeCID;
 
-	PROCEDURE DecodeCSD (CONST CSD: ARRAY OF SET; VAR READBLLEN, WRITEBLLEN, nBlocks, rate: INTEGER);
+	PROCEDURE DecodeCSD (CSD: ARRAY OF SET; VAR READBLLEN, WRITEBLLEN, nBlocks, rate: INTEGER);
 		VAR CSDSTRUCTURE: INTEGER;
 			CSIZE, CSIZEMULT: INTEGER;
 
-		PROCEDURE* DecodeTRANSPEED (CONST TRANSPEED: INTEGER): INTEGER;
+		PROCEDURE* DecodeTRANSPEED (TRANSPEED: INTEGER): INTEGER;
 			VAR x: INTEGER;
 		BEGIN
 			CASE TRANSPEED MOD 8 OF 0: x := 100000
@@ -625,19 +655,19 @@
 		nBlocks := LSL(CSIZE + 1, CSIZEMULT + 2)
 	END DecodeCSD;
 
-	PROCEDURE Delay0 (CONST n: INTEGER);
+	PROCEDURE Delay0 (n: INTEGER);
 		VAR i: INTEGER;
 	BEGIN
 		i := 0; WHILE i < n DO INC(i) END
 	END Delay0;
 
-	PROCEDURE Init* (CONST MCIPWRActiveHigh: BOOLEAN; VAR ok: BOOLEAN);
+	PROCEDURE Init* (MCIPWRActiveHigh: BOOLEAN; VAR ok: BOOLEAN);
 		CONST
 			(* PCONP bits *)
 				PCSDC = 28;
 		VAR x: SET;
 
-		PROCEDURE SetMCIClock (CONST MCLCLK: INTEGER);
+		PROCEDURE SetMCIClock (MCLCLK: INTEGER);
 			CONST
 				(* MCIClock bits *)
 					PwrSave = 9;
@@ -662,7 +692,7 @@
 				READBLLEN, WRITEBLLEN: INTEGER;
 				CID, CSD: ARRAY 4 OF SET;
 
-			PROCEDURE SetOpenDrainMode (CONST mode: BOOLEAN);
+			PROCEDURE SetOpenDrainMode (mode: BOOLEAN);
 				VAR x: SET;
 			BEGIN
 				SYSTEM.GET(LPC2378.MCIPower, x);
@@ -674,18 +704,18 @@
 				Delay0(3000H)
 			END SetOpenDrainMode;
 
-			PROCEDURE Command (CONST cmd, arg, respType: INTEGER; VAR status: SET; VAR resp: ARRAY OF SET);
+			PROCEDURE Command (cmd, arg, respType: INTEGER; VAR status: SET; VAR resp: ARRAY OF SET);
 
-				PROCEDURE WaitResp0 (CONST cmd, respType: INTEGER; VAR status: SET; VAR resp: ARRAY OF SET);
+				PROCEDURE WaitResp0 (cmd, respType: INTEGER; VAR status: SET; VAR resp: ARRAY OF SET);
 					VAR y: INTEGER;
 
 					(* ignore CRC errors *)
-					PROCEDURE* Cmd0 (CONST cmd: INTEGER): BOOLEAN;
+					PROCEDURE* Cmd0 (cmd: INTEGER): BOOLEAN;
 					RETURN (cmd = MMCSENDOPCOND) OR (cmd = SDAPPOPCOND) OR (cmd = MMCSTOPTRANSMISSION)
 					END Cmd0;
 
 					(* ignore RespCmd *)
-					PROCEDURE* Cmd1 (CONST cmd: INTEGER): BOOLEAN;
+					PROCEDURE* Cmd1 (cmd: INTEGER): BOOLEAN;
 					RETURN (cmd = MMCSENDOPCOND) OR (cmd = SDAPPOPCOND) OR (cmd = MMCALLSENDCID) OR (cmd = MMCSENDCSD)
 					END Cmd1;
 
@@ -825,7 +855,7 @@
 						1: ok (SD)
 						2: ok (SDHC)
 				*)
-				PROCEDURE SendACMDOPCond (i: INTEGER; CONST isVer20: BOOLEAN): INTEGER;
+				PROCEDURE SendACMDOPCond (i: INTEGER; isVer20: BOOLEAN): INTEGER;
 					VAR useHCS: INTEGER;
 						res: INTEGER;
 						status: SET; resp: ARRAY 4 OF SET;
@@ -987,7 +1017,7 @@
 			END SelectCard;
 
 			(* ACMD6 *)
-			PROCEDURE SDSetBusWidth (CONST bw4bit: BOOLEAN; VAR ok: BOOLEAN);
+			PROCEDURE SDSetBusWidth (bw4bit: BOOLEAN; VAR ok: BOOLEAN);
 				VAR x: SET;
 					i: INTEGER;
 					arg: INTEGER;
@@ -1024,7 +1054,7 @@
 
 			(* CMD16, after CMD7 *)
 			(* blockLen, B *)
-			PROCEDURE SetBlockLen (CONST blockLen: INTEGER; VAR ok: BOOLEAN);
+			PROCEDURE SetBlockLen (blockLen: INTEGER; VAR ok: BOOLEAN);
 				VAR i: INTEGER;
 					status: SET; resp: ARRAY 4 OF SET;
 			BEGIN
@@ -1206,7 +1236,7 @@
 		RETURN s0 # 0
 	END Busy;
 
-	PROCEDURE StartWrite* (CONST blockN, blocks: INTEGER; CONST adr: INTEGER; VAR ok: BOOLEAN);
+	PROCEDURE StartWrite* (blockN, blocks: INTEGER; adr: INTEGER; VAR ok: BOOLEAN);
 	BEGIN
 		ASSERT(blockN >= 0, 145); (* 1 TB limit *)
 		ASSERT(blocks > 0);
@@ -1231,7 +1261,7 @@
 		END
 	END StartWrite;
 
-	PROCEDURE StartRead* (CONST blockN, blocks: INTEGER; VAR ok: BOOLEAN);
+	PROCEDURE StartRead* (blockN, blocks: INTEGER; VAR ok: BOOLEAN);
 	BEGIN
 		ASSERT(blockN >= 0, 148); (* 1 TB limit *)
 		ASSERT(blocks > 0);
First post updated.

Alexander Shiryaev
Posts: 12
Joined: Mon Apr 04, 2011 7:20 pm
Location: Russia
Contact:

Re: LPC2378 MCI driver

Post by Alexander Shiryaev » Wed Jun 20, 2012 6:03 pm

Sample program: check declared card capacity correspond to real.

CardFakeTest.mod:

Code: Select all

MODULE CardFakeTest;

	(*
		A. V. Shiryaev, 2012.06

		NOTE:
			information stored on the card will be lost!
	*)

	IMPORT Main, SYSTEM, LPC, MCI, Out, Timer, CheckCard;

	VAR
		res: INTEGER;
		s0: INTEGER; (* 0 -- fast check, 1 -- complete check *)

	PROCEDURE MCIInit (): BOOLEAN;
		VAR ok: BOOLEAN;
	BEGIN
		Out.String("Try MCI.Init(FALSE): ");
		MCI.Init(FALSE, ok);
		IF ok THEN
			Out.String("OK."); Out.Ln
		ELSE
			Out.String("FAIL."); Out.Ln;
			Out.String("Try MCI.Init(TRUE): ");
			MCI.Init(TRUE, ok);
			IF ok THEN
				ASSERT(MCI.blockLen MOD 4 = 0);
				Out.String("OK."); Out.Ln
			ELSE
				Out.String("FAIL."); Out.Ln
			END
		END
	RETURN ok
	END MCIInit;

	(* 0: 0%; 10000: 100% *)
	PROCEDURE OutProgress (progress: INTEGER);
		VAR t: INTEGER;
	BEGIN
		Out.Int(progress DIV 100, 0);
		Out.Char(".");
		t := progress MOD 100;
		IF t < 10 THEN Out.Char("0") END;
		Out.Int(t, 0)
	END OutProgress;

	PROCEDURE Do;

		PROCEDURE Init;
		BEGIN
			SYSTEM.PUT(LPC.T0TCR, {1}); (* disable and reset *)
			SYSTEM.PUT(LPC.T0PR, 0); (* prescaler *)
			SYSTEM.PUT(LPC.T0TCR, {0}); (* enable *)

			CheckCard.Init(LPC.T0TC)
		END Init;

		PROCEDURE MsgReceived (id, x: INTEGER);
			VAR
				phase: INTEGER;
					(*
						0 -- fast mode W, weight ~ 1
						1 -- fast mode R, weight ~ 1
						2 -- complete mode W, weight ~ MCI.blockLen DIV 4
						3 -- complete mode R, weight ~ MCI.blockLen DIV 4
					*)
		BEGIN
			IF id = CheckCard.msgCheckPassed THEN
				Out.String("Check PASSED.");
				res := 1
			ELSIF id = CheckCard.msgCheckFailed THEN
				Out.String("Check FAILED!");
				res := -1
			ELSIF id = CheckCard.msgWriteError THEN
				Out.String("Write error: "); Out.Int(x, 0);
				res := -2
			ELSIF id = CheckCard.msgReadError THEN
				Out.String("Read error: "); Out.Int(x, 0);
				res := -3
			ELSIF id = CheckCard.msgStatProgress THEN
				CASE s0 OF 0: phase := 0
				| 1: phase := 2
				END;
				IF x < 10000 THEN
					Out.String("W: ")
				ELSE
					Out.String("R: ");
					INC(phase);
					x := x - 10000
				END;
				OutProgress(x);
				CASE phase OF 0:
					x := x DIV (2 + MCI.blockLen DIV 2)
				| 1:
					x := (x + 10000) DIV (2 + MCI.blockLen DIV 2)
				| 2:
					x := (x * (MCI.blockLen DIV 4) + 20000) DIV (2 + MCI.blockLen DIV 2)
				| 3:
					x := ((x + 10000) * (MCI.blockLen DIV 4) + 20000) DIV (2 + MCI.blockLen DIV 2)
				END;
				Out.String("% done (total ~"); OutProgress(x);
					Out.String("% done)")
			ELSIF id = CheckCard.msgStatWBps THEN
				Out.String("W: "); Out.Int(x, 0); Out.String(" Bps")
			ELSIF id = CheckCard.msgStatRBps THEN
				Out.String("R: "); Out.Int(x, 0); Out.String(" Bps")
			ELSIF id = CheckCard.msgTimeOut THEN
				Out.String("Error: Time out."); Out.Ln;
				res := -4
			ELSE
				ASSERT(FALSE)
			END;
			Out.Ln
		END MsgReceived;

	BEGIN
		IF MCIInit() THEN
			Init;

			(* Do fast check *)
				Out.String("Fast check. Please wait..."); Out.Ln;
				CheckCard.Start(TRUE);
				s0 := 0;
				res := 0;
				REPEAT
					CheckCard.Process(MsgReceived)
				UNTIL res # 0;

			IF res = 1 (* Fast check passed *) THEN
				(* Do complete check *)
					Out.String("Complete check. Please wait..."); Out.Ln;
					CheckCard.Start(FALSE);
					s0 := 1;
					res := 0;
					REPEAT
						CheckCard.Process(MsgReceived)
					UNTIL res # 0
			END
		ELSE
			Out.String("Card not present?"); Out.Ln
		END
	END Do;

BEGIN
	Timer.MSecDelay(1000);

	Out.String("Hello"); Out.Ln;

	Do;

	Out.String("All done."); Out.Ln
END CardFakeTest.
CheckCard.mod:

Code: Select all

MODULE CheckCard;

	(*
		A. V. Shiryaev, 2012.06
	*)

	IMPORT SYSTEM, MCI, LPC;

	CONST
		msgCheckPassed* = 0; msgCheckFailed* = 1;
		msgWriteError* = 2; msgReadError* = 3;
		msgStatProgress* = 4;
			msgStatWBps* = 5; msgStatRBps* = 6;
		msgTimeOut* = 7;

	TYPE
		MsgHandler* = PROCEDURE (id, par: INTEGER);

	VAR
		s0: INTEGER;
			(*
				0: stopped
				1: await ~MCI.Busy()
				2: W
				3: R
			*)
		s1: INTEGER; (* s0 IN {2,3}: operations done *)
		bpop: INTEGER; (* s0 IN {2,3}: blocks per operation *)
		ops: INTEGER; (* s0 IN {2,3}: number of operations *)
		rz: INTEGER; (* s0 IN {2,3}: pseudo-random generator state *)
		fastMode: BOOLEAN;

		(* statistics *)
			tcAdr: INTEGER; (* TC register address *)

			t0, wdt: INTEGER;
			bpsRc: REAL; bpsRcAssigned: BOOLEAN;
			s1o: INTEGER;

	PROCEDURE* CalcBlocksPerOp (blocksLen: INTEGER): INTEGER;
		VAR n: INTEGER;
	BEGIN
		n := blocksLen DIV MCI.blockLen;
		WHILE MCI.nBlocks MOD n # 0 DO
			n := n DIV 2
		END
	RETURN n
	END CalcBlocksPerOp;

	PROCEDURE* Fill (n: INTEGER);
		CONST a = 16807; m = 2147483647; q = m DIV a; r = m MOD a;
		VAR adr: INTEGER;
			gamma: INTEGER;
	BEGIN
		adr := MCI.writeBlocksAdr;
		REPEAT
			(* Generate next pseudo-random number *)
				gamma := a * (rz MOD q) - r * (rz DIV q);
				IF gamma > 0 THEN rz := gamma ELSE rz := gamma + m END;

			SYSTEM.PUT(adr, rz);
			IF fastMode THEN
				adr := adr + MCI.blockLen;
				DEC(n, MCI.blockLen DIV 4)
			ELSE
				INC(adr, 4);
				DEC(n)
			END
		UNTIL n = 0
	END Fill;

	PROCEDURE* Check (n: INTEGER): BOOLEAN;
		CONST a = 16807; m = 2147483647; q = m DIV a; r = m MOD a;
		VAR x1: INTEGER;
			adr: INTEGER;
			gamma: INTEGER;
	BEGIN
		adr := MCI.readBlocksAdr;
		REPEAT
			(* Generate next pseudo-random number *)
				gamma := a * (rz MOD q) - r * (rz DIV q);
				IF gamma > 0 THEN rz := gamma ELSE rz := gamma + m END;

			SYSTEM.GET(adr, x1);
			IF fastMode THEN
				adr := adr + MCI.blockLen;
				DEC(n, MCI.blockLen DIV 4)
			ELSE
				INC(adr, 4);
				DEC(n)
			END
		UNTIL (n = 0) OR (rz # x1)
	RETURN rz = x1
	END Check;

	PROCEDURE StartW (t: INTEGER);
		VAR ok: BOOLEAN;
	BEGIN
		ASSERT(s0 = 1 (* await ~MCI.Busy() *));
		ASSERT(~MCI.Busy());

		bpop := CalcBlocksPerOp(MCI.writeBlocksLen);
		ops := MCI.nBlocks DIV bpop;

		ASSERT(ops > 0);

		t0 := t;
		bpsRcAssigned := FALSE;
		s1o := 0;

		s1 := 0;
		rz := 0;

		Fill(bpop * (MCI.blockLen DIV 4));
		MCI.StartWrite(s1 * bpop, bpop, MCI.writeBlocksAdr, ok);
		ASSERT(ok);

		s0 := 2 (* W *)
	END StartW;

	PROCEDURE StartR (t: INTEGER);
		VAR ok: BOOLEAN;
	BEGIN
		ASSERT(s0 = 2 (* W *));
		ASSERT(~MCI.Busy());

		bpop := CalcBlocksPerOp(MCI.readBlocksLen);
		ops := MCI.nBlocks DIV bpop;

		ASSERT(ops > 0);

		t0 := t;
		bpsRcAssigned := FALSE;
		s1o := 0;

		s1 := 0;
		rz := 0;

		MCI.StartRead(s1 * bpop, bpop, ok);
		ASSERT(ok);

		s0 := 3 (* R *)
	END StartR;

	PROCEDURE* Init* (tcadr: INTEGER);
	BEGIN
		tcAdr := tcadr
	END Init;

	PROCEDURE Start* (fastmode: BOOLEAN);
		VAR t: INTEGER;
	BEGIN
		fastMode := fastmode;
		s0 := 1; (* await ~MCI.Busy() *)
		SYSTEM.GET(tcAdr, t);
		wdt := t; (* update wdt *)
		IF ~MCI.Busy() THEN
			StartW(t)
		END
	END Start;

	PROCEDURE* Stop*;
	BEGIN
		s0 := 0 (* stopped *)
	END Stop;

	PROCEDURE StatTask (p: MsgHandler; t: INTEGER);
		CONST a = 0.25;
		VAR dt: INTEGER;
			dOps, progress: INTEGER;
			bps: REAL;
			id: INTEGER;
	BEGIN
		ASSERT(s1 < ops);

		dt := t - t0;
		IF dt >= LPC.PCLK (* 1 sec *) THEN
			t0 := t;

			dOps := s1 - s1o;
			s1o := s1;

			(* update bpsRc *)
				bps := FLT(dOps * bpop * MCI.blockLen) * FLT(LPC.PCLK) / FLT(dt);

				IF bpsRcAssigned THEN
					bpsRc := bpsRc + a * (bps - bpsRc)
				ELSE
					bpsRc := bps;
					bpsRcAssigned := TRUE
				END;

			(* progress *)
				progress := FLOOR(10000.0 * FLT(s1) / FLT(ops));
				IF s0 = 2 (* W *) THEN
					p(msgStatProgress, progress)
				ELSIF s0 = 3 (* R *) THEN
					p(msgStatProgress, 10000 + progress)
				ELSE
					ASSERT(FALSE)
				END;

			(* Bps *)
				IF s0 = 2 (* W *) THEN
					id := msgStatWBps
				ELSIF s0 = 3 (* R *) THEN
					id := msgStatRBps
				ELSE
					ASSERT(FALSE)
				END;
				p(id, FLOOR(bpsRc))
		END
	END StatTask;

	PROCEDURE CheckWdt (p: MsgHandler; t: INTEGER);
		VAR dt: INTEGER;
	BEGIN
		dt := t - wdt;
		IF (dt < 0) OR (dt >= LPC.PCLK (* 1 sec *)) THEN
			s0 := 0; (* stopped *)
			p(msgTimeOut, 0)
		END
	END CheckWdt;

	PROCEDURE Process* (p: MsgHandler);
		VAR ok: BOOLEAN;
			t: INTEGER;
	BEGIN
		(* t task *)
			CASE s0 OF 0: (* stopped *)
			| 1,2,3: (* await ~busy, W, R *)
				SYSTEM.GET(tcAdr, t) (* update t *)
			END;

		(* main task *)
			CASE s0 OF 0: (* stopped *)
			| 1: (* await ~busy *)
				IF ~MCI.Busy() THEN
					StartW(t)
				END
			| 2: (* W *)
				IF ~MCI.Busy() THEN
					IF MCI.err = MCI.errOk THEN
						wdt := t; (* update wdt *)
						INC(s1);
						IF s1 = ops THEN
							StartR(t);
							p(msgStatProgress, 10000)
						ELSE
							StatTask(p, t); (* before StartWrite! *)
							Fill(bpop * (MCI.blockLen DIV 4));
							MCI.StartWrite(s1 * bpop, bpop, MCI.writeBlocksAdr, ok);
							ASSERT(ok)
						END
					ELSIF MCI.err = MCI.errNotInTrans THEN
						MCI.StartWrite(s1 * bpop, bpop, MCI.writeBlocksAdr, ok);
						ASSERT(ok)
					ELSE (* stop *)
						s0 := 0; (* stopped *)
						p(msgWriteError, MCI.err)
					END
				END
			| 3: (* R *)
				IF ~MCI.Busy() THEN
					IF MCI.err = MCI.errOk THEN
						wdt := t; (* update wdt *)
						IF Check(bpop * (MCI.blockLen DIV 4)) THEN
							INC(s1);
							IF s1 = ops THEN (* stop *)
								s0 := 0; (* stopped *)
								p(msgCheckPassed, 0)
							ELSE
								StatTask(p, t); (* before StartRead! *)
								MCI.StartRead(s1 * bpop, bpop, ok);
								ASSERT(ok)
							END
						ELSE (* stop *)
							s0 := 0; (* stopped *)
							p(msgCheckFailed, s1)
						END
					ELSIF MCI.err = MCI.errNotInTrans THEN
						MCI.StartRead(s1 * bpop, bpop, ok);
						ASSERT(ok)
					ELSE (* stop *)
						s0 := 0; (* stopped *)
						p(msgReadError, MCI.err)
					END
				END
			END;

		(* watchdog task *)
			CASE s0 OF 0: (* stopped *)
			| 1,2,3: (* await ~busy, W, R *)
				CheckWdt(p, t)
			END
	END Process;

END CheckCard.

Post Reply