rhyde@gibson.ucr.edu (randy hyde) (04/23/91)
I noticed in Bruce's SCSI driver that it says that SCSI always transfer multiples of four bytes. Is this true? Is this why I'm having troubles when I'm attempting to transfer only 10 bytes for the read command? *** Randy Hyde
culberts@hplwbc.hpl.hp.com (Bruce Culbertson) (04/25/91)
> I noticed in Bruce's SCSI driver that it says that SCSI always transfer > multiples > of four bytes. Is this true? > *** Randy Hyde This is only an issue when tranferring from the SCSI device to the pc532 (a "read" phase of a SCSI command). Tranfers in the other direction are always the correct length. The SCSI controller's data register, from the CPU's point of view, is just like ordinary memory. When the CPU tries to read an address which is mapped to the data register, it first tries to find the value in the cache. The controller software has been carefully written to insure that the values are never found in the cache -- a read always results in a cache miss and, hence, the controller register actually gets read. On a cache miss, the CPU always fills an entire cache line (4 bytes), regardless of the size of the object wanted. Hence, groups of four bytes are always read from the controller. This does not mean that the SCSI device on the other side of the SCSI bus has to send four bytes. If the SCSI device sends, say, two bytes, then those two bytes plus two garbage bytes will be read by the CPU when it fills the cache line. Study the logic driving the CPU /RDY signal to see how this happens. To prevent SCSI read transfers from ending prematurely, movd instructions (more precisely, instructions operating on four byte objects) should be used to read the SCSI controller data register. This is explained in the comments in scsi_8490.c. Hence, you should provide a buffer which is a multiple of four bytes to receive a read transfer, even if you are expecting a smaller transfer. > Is this why I'm having troubles when I'm > attempting to transfer only 10 bytes for the read command? I doubt it. Lots of people have Minix configured to do extended read commands. I use the six byte (obsolete) read command on my obsolete disk. I just noticed that the monitor (as opposed to Minix) always transfers multiples of four bytes, even on the write phases. Shouldn't matter, though. The SCSI controller just stuffs the extra data into the proverbial bit-bucket. Bruce Culbertson
rhyde@ucrmath.ucr.edu (randy hyde) (04/26/91)
The program which follows is an attempt at a SCSI driver for the 8490 using the enhanced mode. It seems to be transmitting the command bytes just fine. However, after the device receives the command, the SCSI bus basically locks up until I send a reset command. After a few of these, it doesn't lock up, I get the "command complete" message back, but it didn't really do the command. I've tried using pseudo-DMA and the results were even worse-- it almost always locked up. I don't know if this is the DMA version or the polled version. They differ only in the Send routine. In any case, if someone can see an obvious error (I realize this is a long piece of code) I'd sure appreciate a pointer or two. Bruce-- I have a file called SCSI.C I got from somewhere. Is this code the same as SCSI_8490.C. Has this code ever changed? I know my disk drive works fine because Bruce's monitor accesses it just fine (well, after an initial BUSY warning when the thing comes up). According to my SCSI.C listing, the monitor code does not arbitrate for the bus, but my code does. Could this be a problem? .program x'30000 ; ; ; ; TimeOutDelay .equ 2000000 ; ; Absolute addresses: ; icu .equ x'fffffe00 scsi .equ x'30000000 scsidma .equ x'38000000 ; ; ; DP8490 Registers: ; dpODR .equ 0 dpCSD .equ 0 dpICR .equ 1 dpMR2 .equ 2 dpTCR .equ 3 dpSER .equ 4 dpCSB .equ 4 dpBSR .equ 5 dpSDS .equ 5 dpSDT .equ 6 dpIDR .equ 6 dpSDI .equ 7 dpRPI .equ 7 dpEMR .equ 7 dpISR .equ 7 dpIMR .equ 7 ; ; ; ph_odata .equ 0 ph_idata .equ 1 ph_cmd .equ 2 ph_stat .equ 3 ph_omsg .equ 6 ph_imsg .equ 7 ph_none .equ 8 ; ; ; ; .static ; SenseBuf .blkb 24 MsgBuf .blkb 4 StatBuf .blkb 4 ; ; ; CmdRetry .byte ; .endseg ; ; ;**************************************************************************** ; ; The program begins here: ; ; ; br StartTest:w br CheckCondition ; ; ; ; ; ; Tabular Data: ; ; MaskBits .byte x'ff, x'fe, x'fc, x'f8, x'f0, x'e0, x'c0, x'80 ; ; Read one block starting at block zero: ; SCSICmds .byte x'01, x'00, x'00, x'00, x'00, x'00, x'00 .byte x'00, x'01, x'00 ; ChkCond .byte x'03, x'00, x'00, x'00, 24, 0 ; ; ;***************************************************************************** ; ; uart .equ x'28000000 cr .equ 13 lf .equ 10 ; ; putc2 save [R1] movd uart, r1 putclp tbitb 2, 1(r1) bfc putclp movb r0, tos ;Delay for serial chip. movb tos, 3(r1) restore [r1] ret 0 ; ; putc bsr putc2:b cmpb cr, r0 bne NoLF:b movb lf, r0 bsr putc2:b movb cr, r0 NoLF ret 0 ; putcr save [r0] movb cr, r0 bsr putc restore [r0] ret 0 ; ; ; ; PRINT ROUTINE, PRINTS THE ZERO-TERMINATED STRING THAT ; FOLLOWS THE CXP INSTRUCTION. ; PRINT ENTER [R0,R1],0 ; RTNADRS .EQU 4(FP) ; ; ; Note: TOS looks something like: ; ; RETURN ADDRESS ; FRAME POINTER ; R0 ; R1 ; MOVD RTNADRS,R1 PRINTLP MOVB 0(R1),R0 CMPQB 0,R0 BEQ PRTDONE BSR PUTC ACBD 1,R1,PRINTLP ; prtdone addqd 1, r1 movd r1, rtnadrs exit [r0, r1] ret 0 ; ; PRNBLS MOVD R0,TOS LSHD R1,R0 BSR PRNIBBLE:B MOVD TOS,R0 ACBB 4,R1,PRNBLS ; PRNIBBLE ANDD X'0F,R0 MOVB CHRS [R0:B], R0 BR PUTC ; ; CHRS .BYTE '0123456789ABCDEF' ; ; ; ; ; DOPRBYTE SAVE [R0,R1] MOVQD -4,R1 BR FINISH:B ; DOPRWORD SAVE [R0,R1] MOVXBD -12,R1 BR FINISH:B ; DOPRDBL SAVE [R0,R1] MOVXBD -28,R1 FINISH BSR PRNBLS RESTORE [R0,R1] RET 0 ; ; ;**************************************************************************** ; ; ; Reset the SCSI chip: ; ResetSCSI save [r1,r7] bsr print .byte "Resetting SCSI Bus",cr,0 ; movd scsi, r7 movqb 0, dpMR2(r7) movqb 0, dpODR(R7) movb x'80, dpICR(r7) ;Hit the reset bit. movqb -1, r1 RstChipDelay acbb -1, r1, RstChipDelay movb x'0, dpICR(r7) ;Clear reset bit. movqw -1, r1 Rst2ChipDelay acbw -1, r1, Rst2ChipDelay restore [r1,r7] ret 0 ; ;**************************************************************************** ; ; ; Arbitrate- Arbitrates for the SCSI bus. R1(b) contains the SCSI id ; of the device we want to access. ; ; Arbitrate save [r0,r2,r3,r7] bsr print .byte "In Arbitration",cr,0 movd TimeOutDelay, r3 br DoArb:w ; ; Come here if we get a time-out during arbitration ; ResetArb bsr print .byte "Arbitration time-out",cr,0 bsr ResetSCSI movd TimeOutDelay, r3 ; ; ; DoArb movd scsi, r7 movqb 0, dpTCR(r7) ;During ARB, phase=0 ; ; ; Put the chip into enhanced mode: ; ; movb x'40, dpICR(r7) bicb 1, dpMR2(r7) ;Clear normal arb bit. ; ; Put out the scsi id of the disk drive (in R1(b)) ; movb r1, dpODR(r7) movb x'41, dpICR(r7) ;Enable data bus & enh mode. ; ; Arbitration Loop: ; ClearArb movqb 2, dpEMR(r7) ;Reset Ints, Turn off Arb movqb 0, dpEMR(r7) ;Finish reset Ints. movqb 1, dpEMR(r7) ;Enable EMR/ARB. ArbLoop movqb 7, dpEMR(r7) ;Enhanced Arb, Read ISR tbitb 0, dpISR(r7) ;Done Arbitration? bfs GotTheArb:b acbd -1, r3, ArbLoop br ResetArb ; GotTheArb tbitb 5, dpICR(r7) ;See if we lost arbitration. bfs ClearArb movqb 2, dpEMR(r7) ;Reset interrupt status and movqb 0, dpEMR(r7) ; ; See if we were given the highest priority ; movqd 0, r2 ;Find the bit position of ffsb r1, r2 ; our SCSI id. movzbd r2, r2 movb dpCSD(r7), r0 ;Mask out all the bits in the andb MaskBits[r2:b], r0 ; "winner's" ID below our ID. cmpb r1, r0 ;R1 contains the port we bne ClearArb ; requested. ; restore [r0,r2,r3,r7] ret 0 ; ; ;**************************************************************************** ; ; ; ; ConnectDevices- Arbitrates for the bus and connects us up with the disk ; drive. ; ConnectDevices movqd 1, r1 ;Disk drive SCSI ID (0) bsr Arbitrate ; bsr print .byte "Connecting Devices",cr,0 ; ; Clear ID match register so we don't trip over the ID we're about to put ; on the bus. ; movd scsi, r7 movqb 0, dpSER(r7) ; ; ; Output device ID or'd with our ID (Disk is zero, we're seven): ; movb x'1, dpODR(r7) ; ; Assert the SEL and DB lines and wait (at least) 1200 ns. ; movb x'45, dpICR(r7) ;Also enables DB. movqb 5, r0 SELDelay acbb -1, r0, SELDelay ; movd TimeOutDelay, r1 ;Timeout delay Wait4Busy movb dpCSB(r7), r0 ;BSY set? tbitb 6, r0 bfs GotAttn acbd -1, r1, Wait4Busy bsr print .byte "Select Timeout",cr,0 bsr resetSCSI br cdTimeOut:w ;Uh-oh ; ; ; GotAttn movb x'40, dpICR(r7) ;Remove SEL, Disable DB movqb 0, dpCSD(r7) ;Put zero into data out reg. ; movd TimeOutDelay, r1 Wait4Req tbitb 5, dpCSB(r7) ;See if REQ asserted. bfs GotReq:w acbd -1, r1, Wait4Req bsr print .byte "REQ Timeout",cr,0 bsr resetSCSI br cd2TimeOut:w ; GotReq movb dpCSB(r7), r0 lshb -2, r0 andb 7, r0 cmpqb ph_cmd, r0 beq CDNoErr:w bsr print .byte "Bad Phase",cr,0 br CDPhaseError:w ; cdNoErr movqd 0, r0 ;No Error br DoRtn:b ; cdTimeOut movqd 3, r0 br DoRtn:b ; cd2TimeOut movqd 2, r0 br DoRtn:b ; CDPhaseError movqd 1, r0 DoRtn ret 0 ; ; ;**************************************************************************** ; ; ; SendSCSI- transfers data from program to the SCSI device. ; R5- Points at buffer containing data to write. ; R6- Points into SCSI DMA area (x'3800_0000). ; ; SendSCSI bsr print .byte "Sending SCSI data",cr,0 ; movb x'41, dpICR(r7) movb 4, dpMR2(r7) movb x'80, dpEMR(r7) ll1 tbitb 4, dpBSR(r7) bfs GotInt3:w tbitb 5, dpCSB(r7) bfc ll1 ;Wait for REQ ; movb 0(r5), dpODR(r7) addqd 1, r5 movb x'51, dpICR(r7) ;Assert ACK ll3 tbitb 5, dpCSB(r7) bfs ll3 movb x'41, dpICR(r7) br ll1 ; GotInt3 movb dpBSR(r7), r0 ;Get current status movqb 2, dpEMR(r7) ;Reset interrupts. movqb 0, dpEMR(r7) movb x'40, dpICR(r7) ;Disable data bus. ret 0 ; movb x'41, dpICR(r7) ;Enable DB, clear SEL movb 6, dpMR2(r7) ;Monitor Busy, enable DMA movqb 0, dpSDS(r7) ;Start send DMA. movb x'80, dpEMR(r7) lpSendSCSI tbitb 4, dpBSR(r7) ;Interrupt yet? bfs GotInt:b movb 0(r5), 0(r6) addqw 1, r6 acbd 1, r5, lpSendSCSI ; GotInt movb dpBSR(r7), r0 ;Get current status movqb 2, dpEMR(r7) ;Reset interrupts. movqb 0, dpEMR(r7) bicb 2, dpMR2(r7) ;Clear DMA mode movb x'40, dpICR(r7) ;Disable data bus. ret 0 ; ; ; RcvSCSI- Transfers data from the device to this program. ; R5- Points at buffer to hold data. ; R6- Points at SCSI DMA area. ; ; Note: unlike the above, we *must* increment R6 each time to prevent ; the cache from kicking in and simply returning data from the ; cache rather than reading from the SCSI device. If I had any ; brains I'd simply disable the data cache at this point. ; RcvSCSI bsr print .byte "Receiving SCSI data",cr,0 ; lpRcvSCSI tbitb 4, dpBSR(r7) ;Interrupt yet? bfs GotInt2:b movb 0(r6), 0(r5) addqw 1, r6 acbd 1, r5, lpRcvSCSI ;Always taken ; GotInt2 ret 0 ; ; ;**************************************************************************** ; ; ; SCSILowLevel- Executes a low level SCSI command. ; Presumably, we've already selected the device at this point. ; ; On entry: ; ; R1- Points at command buffer ; R2- Points at data buffer ; R3- Points at Status buffer ; R4- Points at Msg buffer ; ; On Exit: ; ; R0 contains error code: ; 0 if no error. ; 1 if we need to reset the SCSI device ; 2 if we lost the busy signal. ; ; SCSILowLevel save [r5,r6] bsr print .byte "Starting SCSI command",cr,0 ; movqb ph_cmd, dpTCR(r7) ;Set up for cmd phase. ; ; ; The machine should be waiting for a command now: ; movd scsidma, r6 ;Point at SCSI DMA bank. MainSCSILp movb dpCSB(r7), r0 ;Get the current phase. lshb -2, r0 andb 7, r0 ;Strip phase bits. cmpqb ph_odata, r0 ;Data Out phase? beq DataOutPhase:w cmpqb ph_idata, r0 beq DataInPhase:w cmpqb ph_cmd, r0 beq CommandPhase:w cmpqb ph_stat, r0 beq StatusPhase:w ; cmpqb ph_omsg, r0 ;We're not sending any msgs for ; beq MsgOutPhase:w ; now so ignore this possibility. cmpqb ph_imsg, r0 beq MsgInPhase:w br PhaseError:w ;Bad phase at this point. ; ; ; If it's a command phase, load up the appropriate pointers and send bytes ; to the device until the phase changes: ; CommandPhase bsr print .byte "In Command Phase",cr,0 ; movd r1, r5 ;Point r5 at command array. movqb ph_cmd, dpTCR(r7) ;Set up for cmd phase. bsr SendSCSI:w ;Xmit the bytes. tbitb 6, r0 ;See if "lost busy" error bfs BSYError:w tbitb 3, r0 ;Make sure we had a phase mismatch. bfs RSTError:w ;Bad news if not phase mismatch. br MainSCSILp ; ; ; Come down here if we're reading data from the device. ; DataInPhase bsr print .byte "In Data In Phase",cr,0 ; movd r2, r5 ;Read data into data buffer. movqb ph_idata, dpTCR(r7) ;Set up for data in phase. movb x'40, dpICR(r7) ;Disable DB, clear SEL movb 6, dpMR2(r7) ;Monitor Busy, enable DMA movqb 4, dpEMR(r7) ;Start receive DMA. movqb 0, dpEMR(r7) bsr RcvSCSI:w ;Read the bytes. movb dpBSR(r7), r0 ;Get current status movqb 2, dpEMR(r7) ;Reset interrupts. movqb 0, dpEMR(r7) bicb 2, dpMR2(r7) ;Clear DMA mode tbitb 6, r0 ;See if busy error bfs BSYError:w tbitb 3, r0 ;Make sure we had a phase mismatch. bfs RSTError:w ;Bad news if not phase mismatch. br MainSCSILp ; ; ; ; Come down here if we're writing data to the device. ; DataOutPhase bsr print .byte "In Data Out Phase",cr,0 ; movd r2, r6 ;Write data from data buffer movqb ph_odata, dpTCR(r7) ;Set up for data out phase. bsr SendSCSI:w ;Xmit the bytes. movb dpBSR(r7), r0 ;Get current status movqb 2, dpEMR(r7) ;Reset interrupts. movqb 0, dpEMR(r7) bicb 2, dpMR2(r7) ;Clear DMA mode movb x'40, dpICR(r7) ;Disable data bus. tbitb 6, r0 ;See if busy error bfs BSYError:w tbitb 3, r0 ;Make sure we had a phase mismatch. bfs RSTError:w ;Bad news if not phase mismatch. br MainSCSILp ; ; ; Come down here if we're reading status data from the device. ; StatusPhase bsr print .byte "In Status Phase",cr,0 ; movqb ph_stat, dpTCR(r7) ;Set up for data in phase. movb x'40, dpICR(r7) ;Disable DB, clear SEL movb 4, dpMR2(r7) ;Monitor Busy, no DMA movqd 0, 0(r3) ;Clear previous status. ; movqd 0, r1 WaitStatus tbitb 5, dpCSB(r7) ;Wait for REQ bfc WaitStatus movb dpCSD(r7), 0(r3) ;Store into status buffer movb x'50, dpICR(r7) ;Assert ACK Wait2Status tbitb 5, dpCSB(r7) ;Wait for device to drop REQ bfs Wait2Status movb x'40, dpICR(r7) ;Drop ACK ; ; Short delay to let the chip get its wits about itself. ; movb 255, r0 StatusDelay acbb -1, r0, StatusDelay ; movb dpBSR(r7), r0 movqb 2, dpEMR(r7) ;Reset interrupts. movqb 0, dpEMR(r7) tbitb 2, r0 ;See if busy error bfs BSYError:w tbitb 3, r0 ;Make sure we had a phase mismatch. bfs RSTError:w ;Bad news if not phase mismatch. br MainSCSILoop ; ; ; ; ; Come down here if we're reading a message byte from the device. ; MsgInPhase bsr print .byte "In Message In Phase",cr,0 ; movqb ph_imsg, dpTCR(r7) ;Set up for msg in phase. movb x'40, dpICR(r7) ;Disable DB, clear SEL movb 4, dpMR2(r7) ;Monitor Busy, disable DMA movb x'80, dpEMR(r7) ;Allow phase change errors. ; WaitMsg tbitb 5, dpCSB(r7) ;Wait for REQ bfc WaitMsg movqd 0, 0(r4) movb dpCSD(r7), 0(r4) ;Store into message buffer movb x'50, dpICR(r7) ;Assert ACK Wait2Msg tbitb 5, dpCSB(r7) ;Wait for device to drop REQ bfs Wait2Msg movb x'40, dpICR(r7) ;Drop ACK ; ; Short delay to let the chip get its wits about itself. ; movb 255, r0 MsgDelay acbb -1, r0, MsgDelay ; tbitb 2, dpBSR(r7) ;Lost BSY signal interrupt. bfc RSTError:w ;Yikes! Who knows what happened ; movqb 2, dpEMR(r7) ;Reset interrupts. movqb 0, dpEMR(r7) ; ; A message is what normally terminates the SCSI operation, so this is where ; we exit this code. ; movqd 0, r0 ;Return no error. br ErrorRtn:w ; ; ; ; RSTError movqd 1, r0 ;Return "must reset" error. br ErrorRtn:b ; BSYError movqd 2, r0 ;Return "lost busy" error. ErrorRtn restore [r5,r6] ret 0 ; ; ; ; ; ; ; ;**************************************************************************** ; ; ; ; SCSI Test code: Main Program ; ; ; Select the dp8490 chip: ; StartTest movd icu, r7 andb x'7f, 20(r7) ;Select I/O, not port andb x'7f, 21(r7) ;Select as output. andb x'7f, 19(r7) ;Select dp8490 chip. ; ; ; TryAgain movqd 0, StatBuf bsr ConnectDevices cmpqb 1, r0 beq PhaseError:w cmpqb 2, r0 beq TimeOutErr:w cmpqb 3, r0 beq TimeOut2Err:w ; ; Set up and execute a read command: ; addr SCSICmds, r1 ;Point R1 at command movd x'10000, r2 ;Data buffer address addr StatBuf, r3 ;Status buffer address addr MsgBuf, r4 ;Message return buffer bsr SCSILowLevel ;Do the command cmpqb 1, r0 ;Need to reset device? beq StartTest cmpqb 2, r0 ;Lost BSY signal? beq GotBSYError:w ; bsr print .byte "Status = ",0 movd StatBuf, r0 bsr doprdbl bsr print .byte " Message = ",0 movd MsgBuf, r0 bsr doprdbl bsr putcr ; ; Well, we didn't get a direct error, check the status and see if it went okay. ; movzbd StatBuf, r0 lshb -1, r0 andb x'1f,r0 cmpqb 0, r0 beq GoodOperation:w cmpqb 1, r0 beq CheckCondition:w cmpqb 2, r0 beq GoodOperation:w cmpqb 4, r0 beq DeviceIsBusy:w cmpb r0, 8 beq GoodOperation:w cmpb r0, 10 beq GoodOperation:w br ReallyBadError:w ; ; GoodOperation movqd 0, r0 bpt ; ; If the device is busy, delay a short while and try again later. ; DeviceIsBusy bsr print .byte "Device is busy, trying again (in a moment)",cr,0 ; movd 6000000, r0 DIBLp acbd -1, r0, DIBLp br TryAgain ; ; ; If we got a Check Condition status, we've got to execute the Check Condition ; command to see what went wrong. ; CheckCondition bsr print .byte "Had Check Condition",cr,0 ; br TryAgain ; ; ; ;*************************************************************************** ; ; ; Errors: ; ; Select timeout error: ; ; ReallyBadError movd x'104, r0 bpt GotBSYError movd x'103, r0 bpt PhaseError movd x'102, r0 ;Unexpected phase error. bpt TimeOut2Err movd x'101, r0 bpt TimeOutErr movd x'100, r0 GenericErr bpt ; ; ; ; .ENDSEG