From 25c12d6e0f8acb8635232f52c26068e041b33123 Mon Sep 17 00:00:00 2001 From: Michael McMaster Date: Mon, 5 Aug 2019 20:07:37 +1000 Subject: [PATCH] Slight improvements to data throughput, which may assist SCSI hosts with short timeouts --- CHANGELOG | 4 + STM32CubeMX/SCSI2SD-V6/Inc/stm32f2xx_it.h | 1 + STM32CubeMX/SCSI2SD-V6/Src/gpio.c | 8 +- STM32CubeMX/SCSI2SD-V6/Src/stm32f2xx_it.c | 1 + rtl/fpga_bitmap.o | Bin 32724 -> 32724 bytes src/firmware/config.c | 2 +- src/firmware/disk.c | 232 ++++----- src/firmware/scsi.c | 1 - src/firmware/scsi.h | 4 +- src/firmware/scsiPhy.c | 561 +++++++++++++--------- src/firmware/scsiPhy.h | 41 +- src/firmware/sd.h | 5 - 12 files changed, 476 insertions(+), 384 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f30692b8..a6955fff 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +20191009 6.2.7 + - Slight improvements to data throughput, which may assist SCSI hosts with + short timeouts. + 20190529 6.2.5 - Add scsi mode page 0 support - Fix SD card hotswap bug when the SCSI host is constantly polling diff --git a/STM32CubeMX/SCSI2SD-V6/Inc/stm32f2xx_it.h b/STM32CubeMX/SCSI2SD-V6/Inc/stm32f2xx_it.h index 9f13ad03..4c7075ab 100755 --- a/STM32CubeMX/SCSI2SD-V6/Inc/stm32f2xx_it.h +++ b/STM32CubeMX/SCSI2SD-V6/Inc/stm32f2xx_it.h @@ -46,6 +46,7 @@ /* Exported functions ------------------------------------------------------- */ void SysTick_Handler(void); +void EXTI3_IRQHandler(void); void EXTI4_IRQHandler(void); void SDIO_IRQHandler(void); void DMA2_Stream3_IRQHandler(void); diff --git a/STM32CubeMX/SCSI2SD-V6/Src/gpio.c b/STM32CubeMX/SCSI2SD-V6/Src/gpio.c index ba18a6d7..5682f257 100755 --- a/STM32CubeMX/SCSI2SD-V6/Src/gpio.c +++ b/STM32CubeMX/SCSI2SD-V6/Src/gpio.c @@ -69,11 +69,17 @@ void MX_GPIO_Init(void) __GPIOD_CLK_ENABLE(); /*Configure GPIO pins : PEPin PEPin PEPin PEPin */ - GPIO_InitStruct.Pin = FPGA_GPIO2_Pin|FPGA_GPIO3_Pin|UNUSED_PE5_Pin|UNUSED_PE6_Pin; + GPIO_InitStruct.Pin = FPGA_GPIO2_Pin|UNUSED_PE5_Pin|UNUSED_PE6_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLDOWN; HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); + /*Configure GPIO pin : PE3 */ + GPIO_InitStruct.Pin = FPGA_GPIO3_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); + /*Configure GPIO pin : PE4 */ GPIO_InitStruct.Pin = GPIO_PIN_4; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; diff --git a/STM32CubeMX/SCSI2SD-V6/Src/stm32f2xx_it.c b/STM32CubeMX/SCSI2SD-V6/Src/stm32f2xx_it.c index 3ed95bbb..efa2c5a5 100755 --- a/STM32CubeMX/SCSI2SD-V6/Src/stm32f2xx_it.c +++ b/STM32CubeMX/SCSI2SD-V6/Src/stm32f2xx_it.c @@ -72,6 +72,7 @@ void SysTick_Handler(void) /* please refer to the startup file (startup_stm32f2xx.s). */ /******************************************************************************/ + /** * @brief This function handles EXTI line4 interrupt. */ diff --git a/rtl/fpga_bitmap.o b/rtl/fpga_bitmap.o index 857b042a4710966e0f151d47775538b67d5c1497..e877e44b206e1382f9752496cd099314343dc659 100644 GIT binary patch literal 32724 zcmeHwdtgO!oA?Ds2e2}t(JI;ExlshBv{Z|NpI02A{;@B^#O+}7qn5n-VMk#SG51j z&HwDt>MPIsYW*wdFRD3fEByuLZ&bdb`@ca!G;X~@U-9j;929MmDB83-LX@XZ=xQN4 zIy&{=yKk)uEBC;y+ssgun}tE$r#}jy)|wG5X%H|Mu=FUp6f8B7CXYJ$A8a6SD_N!_ zjhT7Xt{PA=bhAV?S%^!ye0?w9Le*2yQb$1$^x%)Bw6554`z%IG6{>nypM4DjiAtL* zsKE2Co9YA4LfKVtv*HTx9@J?)6%f?~hXq(9sugW$QidZ&*#MSAGGSV3c2dS4$~%WS7A`a^@ioz%|MPYVhhTDwu^6w{1HQAaObYKQIxQhdDMuf!~(`g z^2ha7DCOSAFmR8kkeDTarEIjg*1#8fT{rQ-sW|R(K~o*M4EHfh*Ql0g^1gdP8*zF~ z3^W~WzyFRQgqBJcu|AEI4GAFSE${9XlF#lUk?lE|VOi)>!O zs6Ink4|As;;0E=Mn}I{%dq`1sH&5T{G+Ug0BBp{sMG}0sOHE*@0ubIlL!^KKNrn>H zQcCojThXa8;`f-)q6kA&97PnDPrhAF z6>h%MzSeKpg3dX~{~Uj8p0M~i5i;GyK9HlNZ@8r;P_36+n}M?}a5YM`IxQRuu+bA}=_LafA^Vr?R%Q zfX?lVnviDow**$etr73JTu?HS3>=#>k$2D=duXb_V~$!KfTNBsQy{3^u?s%vQA9T~ z{TND~%VlgnWF0>@3o+c7T%{2jI#^18czmW2ZOFbP5OEDux}X);yng+kQz%D#iVK4q z>hfrH7>)h;xTx{z2#Q0Z7PH6NxR+RFqWhq1vPI43(4`xpHdm6GxGU;9g2wh%9(nfX zqnU*ADK&8uq@2EQAzIFDnv|UIMlqyxNI-Y8cJiy52_en2?KV;7<{%}ApXrG7)fuwf zV`!Wi`1DJL7&@XI&uS`?gS+w&Z5%`*RX#Wqr-lU>>J&V6@@JV&Lz(J5=t7OKvDz;; z!ocEGvbi07{kBaEprP1HCh|}ukB0b~Y<1e%;AZYEo3J&DN@wI&Oh}SH{$m=2gB)Sd zg;Wt!SNIKR13aS9dI+t`qY01e`Q%-_eOHa?$a98hqNPLY@Mzay%IS>=20k^v8WYm3 znHZ4GS#dVDJ;AkplK&O6DAnRp2W5iv`<_F_LrNcy-;gnCqBF5YPe zqUJWngR3hZ!NA4zAa2|f$S93pJjf&QP_I=?Q~yQ0G*XsbrRp#?0}Eup1OFY>Xm0oE zj+Zf(63X3grX5$Ui!mV)4Q^o9w(ZX!t2OEfrKEws0g1*R#i=pQY18LM1LI5I&kXoR4tEBb1+Q zGFZ}mAf-JpIRAi6Jv4AmBy)2s8z50Pev{VGTf=y1MZl8#2iG;_q$E8Kx+L9WFLOD#rppx+X><9?W`|07tDiDF?bscl>-^1JQyzjGDIG# zeF5>(IKyW@+k|;QDn2FDx}sm(3vFb=1+uDm3VGHoxo4H$GY@yn8W?blG$4;hml*FN zUY<&#_P1t2Iy5yg_rcRV7urnWq9E3Ju?IhxL)s~IOSGg3^6JNoyG8EKjtYp@2^Dui z#;`!0-jO6)LZaS=l&3^|#?q)e<|z!EXmixG$&N=OFPB?Xt>JJ0vG;AmlIT1>`b3)3VQ1spB!e5)thsEO)wCAjb&m0& z?RWsmQST!i$ibk7pD;sMJW3rw@3?CWH|pR&rah%Q7HrJ145;J5eMa7x6)5=Scfg*Z z^k;uk@NDZl$krHaTgnuNe5{LAC~68mWD$yCt*T;D19xrz@gxC)fMwDVF;PE+EJcx&CW+_l{}i1> zXZ|$naY-_xXfdpxzZ@(*Dx;$8G=&sGl)}paRT*9(*?EA4LrFU81!mbRBN8kR2K(4d zp2!)y6`taWQVbwXtHClw{g>gUD2Y93B7_ktfK!o80WypT?ZmwR0?%>V8{R$aVlZ< zRFHGRGG(6*13p?qfkm+g&Pn4&66wPZl{(9`tBNZi@hIS6p3|+Gj(8H>ndGNa$?gb> zR>My%wzw^CYfVuP@xZU13$*gZCo@4s(EZM~fV}>chLol3?Ev>&grKPr&fJiK|G32< zDR12E4yAXegB4(3)CMF!h$=dr4GPK8oe@tvB0?%41wuiu!!)Q2 z8X3V6OBE7;`=@KPF>@b5YD8RD9%mjEbi#v>ie%Zjf;Qqgh4Yxs9ul;IO$(k|ASEmg zQS&Fxp(5W}7WbKgN|${v02fO~d^j&1@>tpXV7Z9ciGic_{avAXoQ9#w1s~Na$aaPH zNH@Cd+*=>Cc;Hm_sfk$;op;$53S`EM_-4TDJ$-I#5Z-A-LsN>1(>q$3-daKPCI$sG zS_G{Ed|rec>*%I8DcQ3vA?jBmh?HOwvm$<%;hnBo$88M^gJLODv;!_gbYk5Tip#|9 z*h)c^kvv#~A-F%Jl$2Ki?~yTgGLx;k7 zR!CneA!+M|?H(RGdUGd+9D-OKv1o8ST2*Qnd0rh` zoOAV_V*=P@{Jz6fqZxx|gJoL;SwSEIs%d7w1oGNNUa{@Q0=BY~0+A!7iI+}aJ#;D( zzO=(37_;oo(NZHE2_!HAGX?`2t$1?UXSIrY@U>b)i_0FHA6<@aMf-p?5I2zsIx+Fm z7vE;Hd5G%$#|ADRMu1HSxq+ulaO)jJ-flDawN|E>D`GVW42VB+ymYLle|;`!BD!JQ zE*>G}YZA&3nB^uP1DWqYqS`|p5>6k5;|7pQqi&0FRP3$ z7u`~W3VAz>ZBT=FNuNv1H;*VCMsX&= zhGUDp7c48GSokrFRk+Y44Nc?O8`%uwKIYQi4{NpqX zM(0;)Evl%j+q?dduOKCPem5l@TYm!AS2Vv1k5EWaxRsnLEy|GvveKG*nK$LAg_%2{|=e~qTq9O3428FJEL<9%^voBcmmxQn=pfHD?R>h4=uD0 zjtej|)5kqZ)Jro)MoZcu&Z1H*6tIqbPYhxnvBEh+6;`(quW;BkU>}q9A=K0BJG#7n zB+res87G1+;g7-6Q>a$2Ydr zH)6j)q8mf_hEe>LE;ZUYHHRF(;g&C$awT*&RN&S*u;fy%jg0548(<&^E3QR19YQ>^ zxs`*G1*pv3)~ZaqK$daolHRzx5Z;k7Sbj#Hs7Ku^LL3iqW|6=&bhAStF{RaCLVTEm zl+Xv=qoALM9gQEJ>|^3y8LVzd8cd$X+9Ydr_lnegKT%H=z{ji?59bu|Zy>}Y%Cbsx z1;D}p5*2v1NFTMhA*Y8&pafYX6De)_yDu^N^Yxn$=O+ffK?$lGf?AY5IN%usM^FO| zg%dBr7C5?i-;@yH4+2ZEvCp%eIl}HjPZ{5i`en+;)x%%U%YDc@`~3sc6j}gnu1NYX z+f;ys>ld5PGl;W%Na^+t1Qt0N(zqp*kDD39DrprS-NL~ zDj`zt5lCPWYc?;)(zq_UoIab;>M~;HFF66Yvs=bYTzH!+5A<5ANtFz`$)KFr{nuBxg1i~I!y-66`z69a_P`D6p@r#NS_XpDrEKg5Y^caS zPHS^3>VxLLn6}V3uTk5QEOi)ff^dJq&!1N|6%9WW(jjP+eIMpY(v9!zDm3|zS9LrNS#nR6HJ>u(~DJnCRbNY!It@jz4Khio34yI|l2 z>@^S1!Ek8uGm(rQ?lG}YWOS+PF#Kww^a9sb3VY5z==6)G*F6{NlHaYocuz)CL+t%! zM1Kl~{B(;41<(?qF`PyAF+Xj98W=>oHZ04|PAW9L20k4EdimJfv7=LSFM7;*ZV!~>Vq?^533m(@%SP$Z>@Pk2t@9A?(%2P$-_ zxi>VAeeHp9kh16W$nzVJd6MH6&T*T9$gb6KKP2Pp>**$n(b;oUL7a{aer#O9Dg8aH zNb3f@(ppUsrivR^Pm*}*GMLA+?)~9XPXnJ!)MAd8)b=hI#mx{9FWpEX&v8{HzikGt z3*(nOAFvkde-r9bLJe?rM}P7RR!Hx}3@lD`GTcbqS`fs=^rFNH-Zlf$nBQqHZ&|;y z5ijGmcjk&vN*devZ={ZmZ9j}%JQEtf465bppN&JyN;aJSVG0EL>9q#w!C90Om7{;CM^3ri&l?}68 z=10?1fG3C*c$J7cJ+bWz^T8y8m5Ynb=9)G0Q0P=!B`{vKCeBZwj=Z)Fm;dlZCeUkOWHC) zW499?GGV_+&kmi0{M0@a38j>D&)kb~O6ckgcHy3+NGtB<$HlH#PL31Zu*0*%90#ZK z3E@(Y!U5;CV>>E7pA9GCH%6hJB%9MQkqq+)P+A?rc`_M#C@ZIcInVZ zQuATcfHp5QxWasgohgB1aqv<#m+-^Ub`EMPP&9C7C<^+DJoF@~k)>7?Tka>#z$vfx z3_)^I^sw)Q%9N9z`KgDuU5lLxjG}m8dIau(ZuHo;PPo9y*Sured};V>1+Ja3lQkZi zzW%49J@_$IijGNa@#Q~+o(N!z4qkx02#toTdou?5)zG99n-WhsMwt#Bqrx7$Ab>Y|7a(k=SzZ0sr2G|XG}YylsVmx) z9oZ|LetZVnnBxf~8B>eXBO+~hlmYw1L>682Q2&7Q%e2qf3MzeGQ-s#Q7bR?BV9-qm z2a#xMI=q}}9=T%S4^yOpcbjX^2q&EXYM_+Q1}%jSta~v5QId!>KcstFe^^W{ zh7L=cN~~gq(DDk07DcUwAnYjm)pP&0MCqaRk?A=~b>jscPzZ^iY$3a3@{>=V<#CUT zH2{Tt^;Zo!-H~;;FzG0wuHLxF@trHs@8MnkziJlOzADU$akdg2d)2|qFphT~SqjFx zPVqetq_ABz)YU@cChV2+1--5L~lbmcr_NJ{A0a9rD~FG_@D6s1wK_Dh$3 zd2sbQv4O0ThZ{_fo7adrAk+{irH5*RDi;Z#dXbyr)1d;Z^IRSV#v=2zEb@lFdrmQFM{4rg@C zrviWSdkvLbr$pgLYjFp@*yvD7GsZ5Fc#$CRa^=rx`&WLa17iU+{7I9RB<AV zq3|Izc-fPIhIap4!g8NU@5VjHKIuJ z5L&Qxy5cUs+|$4u5~>(y$(a!!x=5ygAWK<^6gU_0Ygf-6epMx7!R0R+rY@m3vZonC zN-HfcIp7t*JU78?l5T#-!}lYgefXLU3bHQV>!D%bn5G0qCMZ>cYuVhXBOnl<;L>?L z70xm@WvdMtlrpD3lu)7ZS)ayBgPngqHSBo($}Zd?&zqlyEf}moKuWNzv;tlzUd&ls z92_5jWZI`g&I$=dI(f+fjJz!|H=v+d4Onav7>}%W?b%RR_sorZbfN@HoDY1gNiV|tSm+sz0i`Gb-xp+FZ}qSG zBL9l5bAdehw@4^L@G9d*mvr5FaL!!xLWp+m7&HSlkK(!EkgrBR$$Z^$cW6+`bO-vA zc=4b)@90meH4OxVmwxjd&&GSF(MvVf*TTD!o`Y=1c^_LTEDm=?vW5&Hq~orQkH9V6 zWCadKE|M3*1+{`M?->_pd|tKVuxBVSDC?>uVU}mERTPkXDC3bvSDt7NP^3mjx|O>t zV|FXYQL-2vQR^k#OZdQ{v{#j$kKu%?BILIx(5#5(*xETBkCN#=8l5ng^FCair;pEWY){&it#qWEVA5l`_e3@7`78p~R&rRuy7g~mZpoipb%HIV2 z)gI@ojsNTVe~aaRCezoS?4hZ?K%tgX%NMBbiludT6=fK&8Ic>6_t$mUg;A{a|0#R} zOKLDo^w(MCnqR{j`Tyq+4FliT+5bPbqF#`G!EZsJ^AfQnnvm`rAA7slUizo-qNJz8v$aa`+A!r;{H<|fka4X zV<{yS?Oqe}5Y1jg+$XmIN&w4%Sx(-1^&AxX(@u;};cavuRZY_B%6)AP3|NXb>_VSA zagWB`V#WO)3QB)OQ4}o`USOeb+^tD(JH-bJ**ZbvJ>&4Exn?$aSWZTRLKe@X3_=kb zO|0Z(*NOXWtdnAqF^>kz=8SS^*ln9iff+<9C7C7eo!l^mZya+|==a#1+DV{Sxmo0b zR50dvX34PVxzU4RLhFE!4GO?JC<8^*ydk2AH15GC+NYoji%%$mFU6a&QX4?nKt~bM*?5C*qYsOk47SNj`=nSKqJ!Osol?_WdDu*>EDT zwB~Pa!H{jN3dtUZWfP@721iOBV0{6`EWt%l%R?Is6$?z7z8|nINd=zyg#EXl`H22_%(s82^LP=M=~R2V6L^&+>h zgnHEc8ft=dX-7n9-Zd}^D;F%wQ7OK))Q2dtGU6a*HGMS0B2N!holF>-N0F{LYHL5V z(z|tvS$d?S64MjIc`mJ)j+~jD%u?2Tn!?qV)ioG6@KC{kwLy7``m{e5`sEm$)B%Rw za~l_<0w{FYZJO(==Vovf^VT=rhufe_7gu(5zT!}f>mFNfadu9CVtD)0j?zFMy(Edx z#@rJ&Y!XQES$SUbudx-v*G)-P85ME87gPY42O3IRT@@*xb%wfedkWNm$`}+@DYJ%< zyvJrCI@L7qfOSaKlO-oLZ%0asmm!TF?vtxB#<(6e^-&eLr^?&f29HP0Q9P4i`K%03 z_OAJi9|Y~~pU-|dZq1%1_$9ms^~d}Kxw4hcyq1)Fcv~g1HUQ?h=Tgnyf7tpKvm64Y zZ2vZXeBwlWj}vhmc`ar-0Vxp$Y1}ghON+ztQ1dI?6HSyt_g>G(4^_GZ3TnD^3a;YK z(}a&t;8azL&0~I3k=2~24{M@DQ~*t%p1&%F8&0r)zsScOEP3;5;$iaVCmQf85faC( zukf^2m zn5CsHa_7H*(4gvd(e8I~P>Dz$kmhIr!Q@>!pPcA{^U{*ar;ZQq-v*!X=!SEsg4b@h z6GRqcsKO(yC_+hv^BQcLN)@{7g8hd`74YJakB9`AyNJr z+h?6h)bQ<+oO$?{0M6jE=1pT;eiDyj1J+yW+Pah3Ndb9%gWz2 z=>_e0_}rD;v!31f1~g*tD>)S$tYw?7ZNsO~N-z9~AIMTdvxvDog8n_gH*sLi;0!)T zsBPh;v>qGZdDfFGGN;Pih?|AW*@>2 zD+*Vh4gw7gb54YXqom8|QZp$&N7MMKv!zzyFu6`KA;j_ykX%$u-fU9b_dt6Y<;-bM@X`T$|10!Ppa@BK9>uS$s;ZBa{E z3;dLhr+Sa(-WXd zCRs{`%41h?F)^dCS|0Pvk9=l{!xV>EwsLmsASW7uJFM@zPvC9Yj!GD_X=idJvs4+# zU|1?JNgvR-ik7OigypPW*3iP?FYnXZwPyCx7;xdrI`l(9&?N)%OZFmJHIJO**Ck8w zEU@G+{V?zla*4GMT?Y7A0>~cuCHK=NYM$BaSR21^W0vhe+jSCefQ&8=Va5b74=5-;x6Lf^DJLb&Nc4mQo_NJSuMNCp4F)cw z?>0cVLdv86=3uPVa6<}XxF?+d0kTh%{Wz&cwF_- zO=n2?XJy;Ebi7KfXxF=yGt(}{$3s?E=~|pPMS~d#boE7;oPoUXlNxBjYLCP*96>){ zf)qbR!W{@GRV9=gmU{ zkpwIm#w=l}k-#)PfTF%8>e0jvIDRGCdyWs^aK@)m$9&oY=RQmk0wPtGAuRjAG0Kl+g&>9#WJ+m)xq*-aUTwI>pV0i8NT&D4Fc_*=P4>Nf+i%n-sz1Z-?~@6hdX>oeb<$RjkXD249TsaR|-XMk$1n zaj^|(_@ztdOO~a1Z%V>$j2j4xYe<$A9k`)#$a)88EJK5z+N15W{fl*JP6XBba!gZC z#lh|jt*aIEG1%@RrYrk$$74*DfN`Jn+DA7}oo%L98f z0)2t*YuZ4W%niSoI9c;y_4pllq_OomRnRaCmwU$umR{Quh0aC=lKkMXQ%v6_@&`Y; zy9L8Q24Dc6 zvmoWi=uEBZy<#~^qpfkzFVv{*-4UcQ@K?KfED0Q`klrIXbc7yfv_NtTiJe+o84<@g%Tu{0T765yF2qy+mD-uxL` z!0c)LqmnAG_8rV|Rqv)h(rJ)C{@u?015q*z-*ISw!r8`HHXl{&!0!<8=(2?AjIJ9d zY*S0Y6Y@rViXB82%UtLJUK*9FQqr;}(Zrpr@H=JhQSPAEAfyYw^gLQ`t$Pnj@*Tu6;MgsZe2wwQ{fJfOl*qGsoYz8h+7C)E`87WK zcgAO?k`rX8WwjAb@8r+Is2gi~6L83H@gocLhFnCCbp_Y?J{Wy7ocg-hxMpZWJI4V28R=Zj@ zClY0b_MGc77`ZI&4DZC7z{f9oOk=0(Kl~c6)%^oxNDew91rW|r5|7YKixI3k?ob9x zeWanhf}%Y?_{iW3C^l`Q_E?wwv7F@~@euaoi3ZP(5RKoSb#09fkq8h!deG^p2lzA4 z&>1vg4_RY-(JH(;41tts$u1CsN@S{f1O-+H@_4TD3+R{sC*N!AnLC>LVx`N`yW;MCI2rJ0wS2= zYgXmof--n~K`dkN%$0OleC2AS{7U<4Rc55vHaPJ>)e5>D5*b+S(~!Yr=wMM?mSvTR zdoc4+amEMKq=5j)b)RTFH@A-%{>oM)LX5}8A6LYu{@h})+G8yySnxCDs?u<@m{VZs zI8~X-3{!y@*ni?L8D+P`9`lKjD*0Xv15O>i16u+(TElF%LZU7aC+pS zJHAw0;G=|^j&io5JuzIXBMGAdL7&RH*O2wI1kaG98ga?1oaLyY*r^BK4(8p3!`D4{=mwmmu_8Pd?AJ3G3{tT z556zig`#-}yOPEt;EzB>R3Yl4WQI8Ld5dcatT$-a8AcSQc6)8*+#!8XXIxI9 zdr(sv??NzeES8vr_nOgwP}9;3zI+YeMcEiQTo({e;71IjF$rI4W6uE~`Q8R<_@#}O zhvRNx@H$Ia6_|ge;uf7g?79xIMMlBDV|2 zc`C5%%UNw>mX8U3gOemf$PSgD0-R_>8<7M;%bm2SL>J+_umZnN?%mj8QVY=XF4Kiy z9uOT`BHx9Sj;^F1rnrxW&4=aFh9%~BmQ4j$hnT!~0Hi72D)BNufW=dpe+UWtEhT(b zB>V$pH5b^t9kG!Nz}5BO$#|uq{~79IfKdZz1}s^*&?T!9%0i}8jN`7=ti9ORV4r_?jisk~cmRFQgvF+i_#f9@k z67%{M4UGW=UYfnT6_sHrZ!&(XD8BnVVW>*wC4b(L;R(4PTtYa0Ju*+$Wf`}NPfEe8 zQmaSLRsw@vdQDRc<0%!9L*849inrgihyS6|G1@C?7=d>mB&tEDO&6rZ$Q>`4J)!a=`O{xk%}F1$22R<6_$v(A~td-NV^@`~7#o zN1@9qouuNSTQjstD{7ti?~U4nFQ$AMFeP;PZJb+c52+wn4B!4cQRaYBwTm(0sikLkb+X)>_q`Vod%Nt7#9Z6mxu}g+|Y^4y~eoew? z-+5+DIg{M^QC>)CuJ_>vNC}So{%H+P&F~H)Rh0Bl9@4mULyt~q0NXuici3W`_xKrF zL6>U=;~$}sM{Vw=qEYJ)F2d08+O?7BvVw8024_vJV~cD&LtG;=A*ql~oa#8Ns`IU8CSejg@e!z2|Mcm0iE=^o0FB}v}) zp3Y$Y9}sGCiiUpob`72qQa-wSI*1+&a+Dn0jMty?^AD7w$z651j@A>FM?C!%DKW=0 zhcL&B&b{8!ik|Jt+S9X?O1D=}D1-mW5y0f4n9T4`g~b|`d9sYTDi zi}4+w?Toz^2|{w*s|&*{4Rc(!;KR$K0*-Nfq(+1Eyw6Hl3hKE8Ay}Iy$#>@V5Sus z*wo|!Ix|}#55h+YOor6pt}c;TPtMaCe0tiwn>0^dvOk$siy(pbD~60JG!1_KLk*>c za~!`=2s`MM0rPCa?8%_5!)b8hKQ!7sb((+eC29Ch-|W2b$Y*y~fap}CKO?wM8f=_M zw7){afykYwK%i*uuWuyjSvOPe>6q^?QU>wy;LRItULiFfjsX_K`8-8S+i(*g!UJ!k z0FgBsRN~>7JvkWrN~Mjt^_K?I#zDKh%ly_ne%6YOK=E>>Oqctw0nA8e`u*Pu5fq)H zOOu$>67Ns6Vaaw2=qA-TpOkg8_%XL3t6Y9_>Bp#@yAmT7%`a0?HFv;Bv4A{U@U)Q` zW}-+t3_TghgYn_&MuxhP6uJY&8(1AtIkw zxV1!gw}pNQ+Tk5F&}6-C!%sdA z{(BVj48aZ3v8(u_EmV;X;2MGzD;jIc_!cyNpeb@; zQ5%j_pWBfNWr0Ze+_M-g&v{X@=wNT&sUfH%xP1(naPF={B^S6iZHxjl z=@Y!PGN8=1N>ktSYAS`4crZqhPO6PJq$^WkdE;9AM#KP{2hl8k^N>B-qv0FZ7Zfnw z>9ukLPL|wf^j%?oiKqlAg+hLb_b5p*WnAtf)I0w08vfx!IpT0*=$A=dkEj%PkQy}Y zYLRN_l-*7IDLm2de_5k>54q=oi}?}YHmrFGn+i)BeKd>_6+H$DU_!5mt_YAFdNn7ngbBpq{v;tGBv*vhsxjZjlWJqWs7Z31l z&5kqGbPJ4de85#std%Ez@(TWGOaqB0f(mvVkmwMDffr0daKk%h>AA&tu(7auo^wN( zL7;dBW%L zDYy5hJ^T=Zotl~&C)>WREmnyXAt?AQ_0Qz^=Sa4RWbDKE+HsRmx*qX4iH0;m*g?@b6o z7}j3Cg5=TbHCp6-D12_izj%v^nH0a_S&kU;$18A!FwQ%l)M}!!>oa9QFv=^@n~@???-7==>j3yv6P!6U=mxNa>gXB^zi8vNXA#SYXZfDRhZaS?_Ct(4s2#ty zcE~rwv+gX-Ms$Dx)k+uH$HO=FHQ45n4I;1XEurmBI@gC#ZriWdmYF$S(9ksqPj||) zkdF*_)&3%!H4HhwV74af&Po%ANZnp?Q3Sli%_5^gCl3fY;sZAWIfiq#>?Sup_K;^7 z{NiKJRY)~`x7xJ9`I3!!VBZ4O*l?cH-9;#feu7^h&$nstK6*DC4q3NvMU-;l`Snal zeeP&tI(!Yd?rrGXWhrwYn?FQN<2{!L^L=*cK7&`sj9HmPP26mnBsI!Sjc*0;wR-4| z#_fC*C>5J26SNKmCAaig=bek}WgbGT_y=x<2oF@CMDpwzR<~jgHmFdw5M=kIyqg zKz=#$gVX}@v4`BKr5t+ZbNA5*2w^{U$csP^?f%_5N9&yuZ-%HVgQLPKFnY}cQg4p()e>1HlTV{%E~B*j@#IVREj@Lnm8N(@FvZNYOEaPk>ZF3 zj%eWTQUmi5m5+phzf02}8T1hibZel8IFb)^>*7dxL<4`f8aR><{N0-V$iRn@_$?uh+e5zA-hY@by`tgDX_ro)Ip_P+ z8ZNzV4*s9!zmB^6>KR{0TsQNk%kgiJTzBL5=S-W9a?b3qtnaSPzGlX>In%xrU4N2@bg8stYu`sDxk~6(d)Tj_;^oRz+Qr zVDEwv-(Zc1Bw`$*L_`g1G>N%_o&`iSDC-TrL{Y|<1T+SQi$qkE-?#eAAm%1F_x_&e z`76}N?e40su2WTA{d&&e4aZG9UI=k6(p@OKU$4A;+W)-$I!mI;WTUck7u1sBJv}m^TfBWr1mzVauNq>a)46*cZzN( z#{a(~?fu+{v&_ND(r`zMh7?|8?3w!h(NP zaUh+al2_$G@;@l+1Ly~q^W=~eI^*wD=efe{Kx6+IFa2F#>=!3muib2sA5SI6aLJ3! zyLl=yOC3kf_k7Y^E_!=kzAiZXY2ni>mHLqXn`#XnuB(U(f@ zL9keWFL}rmwB)klipkJYLFCYm4qpRLm0xXpsj3h}U@N=%a%PRK>_)d4_w}VHD~gnM zN6vB<)*#6Pmz$|6XmnlYnRo_N#O1OnXHktu)$BiZ6?oVQCT7f_$&$*B*jvP6^D(z{1Yft5AGC`aQVWBASK*SQ|?Xfvw^c@Ct@m6d{&IOkVHfq;ZpH(HK@ z;f$c{qYoX_JB^(Wr_hL~*X(U~u`sKpZBhs*;Sa(fhz^;W;|M|3li@=Zib?!2D4TZ3 z1t`cLCcp{k_0IV=F86C`sc38j3?~{m=#5YCWCb-{j!R*?#~KdKP~!*16c_zJATM%H z<&}n|u$J{)Kt!=GweYM+b?(fiO-xOAeg)^OSvbE?G}Q8rU1AEJ)p$D|Yax*}R>OiX z;TZ#wK5Smun{>m`Uxelm9dkPx&a|3AY}gp7A@n%K<=VufAyNGCQ{kG76j`z~}RNt38c>XJ;z6afXcas!{*Kgn`IK9T5P8u?4)!dB!x!dPTdSk{#z zHgqv-RZcUJ+Uv&z0dlp_C5_wy%H6tR&m`>=5^Nh_X}>c_`LsXOmB>~>ydnDa^BfYj zB{>a*N_{zibDl4yEikf>lY6%*&hnjw$qpg`W&1}DoOtu)E~czI=lB^-u|1-e8A^?F zq2a>*DVPz6j5Zgqo}(l=J95n)E0GdEgO)^R-FyL@$$$|9@qKSzyvusV*~|kHV4$Ur zVp?CwCNM09c+3fp-T7(2=$ObUoS_dP@+;r4PqHQ4Rdm>xzXlXbk9cV}5amaTibE~A z(bu4P*HKu$ym5!ep(3E=z5W4ki-glI7a>yIw(w_@v$~9`?q@#e(^2QaXcsAUWyT zd0RDaO4Tmb5;1$`Gl9-jAMvey(3L^e*o;0c?f(&Z6~}ibl9teG>x#{oSz^%ur=0`g z!MXeT1%|5S3%i?`?)X>oBd95>kRAv{v8Ji8M^%@ymPyLp{NY`+8pXXGq1r*7dw}=* z6>I01s5pBMD$qS9HQ2bC8s2;^ma1=$aa{WF z>_c{SGR{+*r2o$GRWF>PWR97IcIQp1!PD++O+7#pTQ1v22IOM$Mm19F^xD*;q+v8* zpzQhsvQ!Ls@Muu61HO|G@Z)E&=!)#fjDm(>ATK)bMmX=uvbQy`(Ret6Gr#!lRRKjM zy#P!w2}v{8oE%#TZ=b)8X%>E8>v{0dyyd>Kcp1gc*=bM|Z#>jx9-;afOjhLg;h`Bd zy7^rt4b!ku+)>B*Y?$P_?&!IU5beD4)A1hNziz;1sgQuM*Dt^WiJ$i7t$;dq?^Xvn zIdd!P4bd!l5^M-T9vhs4Qsqredx_$@F2PE}xoe)<6h%J9Fc!i5p1BS&7%>#-Ab==dmR|nMpl`T2mUv@kB&6&_z zJl8otX_$>@Y#?qye8sA}ui8Z8^6R*wZ}oX<1-hqgdVE*NN5%4+K}z62R668Kin~%I zfRJazJHsb>s%N^v^i!5q6=e@Y3>c!Tz6jVxFQ?&mpZw_*K(jHSAHXXbX|ruo^ffy!+4BTQs)p4TCIRpf)OE-2w9t)jSC$r0tFN*sdz{v*&}vgL_1w-!IKd-Y+-Z>@nLpzzk#&ylEvE*0cE>tYK!46oxYiI2fAY3hf0 zrz;sl1sRWy9i-bxO*?5JBm-{SvL2~h_eHBkkyM2RKVeF+v0lC4KE{1A?>%)d&b3>6 zZk%2lau8j(6Qv?EV*yyNaW?jZNI^GOKT#fFxH`?d5Dy^?coPz6QSrl=FR5|qcEf(6 zmL=1@8~x(rw?2wWR@9~U)_tfI;`YohRzs9q#+yOFPk%&ka8>%HCAJ|a7j`{~CYAS% z*$XGi1&NGdM)BgXC&=@h_aCXtFb*%+6a(gyC;e7u=G7CHGZ9HJFV50cqeI}C=zxD?jV`=QS zF?uYBiSooXIMVnx4cR29e=zb(W9g>P!x-b+Vm)6N7c#2Y)>Xe!L|3<-b7L3Qmxc{H z`B5_AE$-2#L`~ym(?do~Ap>sUnvVP5sZ%6Zm&VZyF!_($D&X57*3oQn3L@{GzC#`1 zEEBUe*6ph48=ke=Q}uV(XgpS&iJ^>f4X2xQI!Eo1hy?qFE-~6DFOA)z@GxR7r%fohR&{$7Z z(1}@~3`#UE*@1Q3qwkK={ocS48D(+{qfk+ze>gR@50RQm z@Ub+FSshdde_O8`i%qf9|lNXdVoZ-B7_-9egY`j2|^CP?}^{iV+64@v>*p z@tEa;2+4qWR!<7)JN-G-x8D1Ulwvjr57LngR2HU(VZGzI=DDy=moD75ny5|$2E-8h z21^C(cvLPR`dL$Fg63#ch$Yy$#o^UQ@*{DaJa2xxfeep&w9)v6{^6mJm=BRzID^ZAkgTxR)KxEN3feR1AoWV|lsquL2dpVT1vU3JK+ zHSQ)2(8%JRGm(`MPkA9I@wQH(_~Ki6)8PL7TAAJ!Zmv1bQ8aYFB<_<~HL>>-+T-0Y%jj@h>(C`L4sD4I+Y#}I%{0s$? zW06=-czy?n!yfM9d&FsYULM4Uze3we;)$5(@cdFmMUvG$!Hs0{hLf⪌C%h}77L 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 s3T1A`bpGy!KjGky+X}K!qz>>M#f3Z+t<8 diff --git a/src/firmware/config.c b/src/firmware/config.c index 0ea55b2b..f9b288c8 100755 --- a/src/firmware/config.c +++ b/src/firmware/config.c @@ -37,7 +37,7 @@ #include -static const uint16_t FIRMWARE_VERSION = 0x0625; +static const uint16_t FIRMWARE_VERSION = 0x0627; // 1 flash row static const uint8_t DEFAULT_CONFIG[128] = diff --git a/src/firmware/disk.c b/src/firmware/disk.c index 961387fa..315db1c4 100755 --- a/src/firmware/disk.c +++ b/src/firmware/disk.c @@ -18,6 +18,8 @@ #include "stm32f2xx.h" +#include + // For SD write direct routines #include "sdio.h" #include "bsp_driver_sd.h" @@ -561,14 +563,17 @@ void scsiDiskPoll() int scsiActive __attribute__((unused)) = 0; // unused if DMA disabled int sdActive = 0; - uint32_t partialScsiChunk = 0; - - // Start reading from the SD card FIRST, because we change state and - // wait for SCSI signals - int dataInStarted = 0; + // It's highly unlikely that someone is going to use huge transfers + // per scsi command, but if they do it'll be slower than usual. + uint32_t totalScsiBytes = transfer.blocks * bytesPerSector; + int useSlowDataCount = totalScsiBytes >= SCSI_XFER_MAX; + if (!useSlowDataCount) + { + scsiSetDataCount(totalScsiBytes); + } while ((i < totalSDSectors) && - (!dataInStarted || likely(scsiDev.phase == DATA_IN)) && + likely(scsiDev.phase == DATA_IN) && likely(!scsiDev.resetFlag)) { int completedDmaSectors; @@ -588,12 +593,13 @@ void scsiDiskPoll() if (!sdActive && (prep - i < buffers) && - (prep < totalSDSectors)) + (prep < totalSDSectors) && + ((totalSDSectors - prep) >= sdPerScsi) && + (likely(!useSlowDataCount) || scsiPhyComplete())) { // Start an SD transfer if we have space. uint32_t startBuffer = prep % buffers; uint32_t sectors = totalSDSectors - prep; - uint32_t freeBuffers = buffers - (prep - i); uint32_t contiguousBuffers = buffers - startBuffer; @@ -603,6 +609,12 @@ void scsiDiskPoll() if (sectors > 128) sectors = 128; // 65536 DMA limit !! + // Round-down when we have odd sector sizes. + if (sdPerScsi != 1) + { + sectors = (sectors / sdPerScsi) * sdPerScsi; + } + for (int dodgy = 0; dodgy < sectors; dodgy++) { scsiDev.data[SD_SECTOR_SIZE * (startBuffer + dodgy) + 510] = 0xAA; @@ -613,6 +625,11 @@ void scsiDiskPoll() sdActive = sectors; + if (useSlowDataCount) + { + scsiSetDataCount((sectors / sdPerScsi) * bytesPerSector); + } + // Wait now that the SD card is busy // Chances are we've probably already waited sufficient time, // but it's hard to measure microseconds cheaply. So just wait @@ -624,26 +641,6 @@ void scsiDiskPoll() } } -#ifdef SCSI_FSMC_DMA - #error this code not updated for 256 max bytes in scsi fifo - if (scsiActive && scsiPhyComplete() && scsiWriteDMAPoll()) - { - scsiActive = 0; - i++; - scsiPhyFifoFlip(); - } - if (!scsiActive && ((prep - i) > 0)) - { - int dmaBytes = SD_SECTOR_SIZE; - if ((i % sdPerScsi) == (sdPerScsi - 1)) - { - dmaBytes = bytesPerSector % SD_SECTOR_SIZE; - if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE; - } - scsiWriteDMA(&scsiDev.data[SD_SECTOR_SIZE * (i % buffers)], dmaBytes); - scsiActive = 1; - } -#else if ((prep - i) > 0) { int dmaBytes = SD_SECTOR_SIZE; @@ -653,42 +650,11 @@ void scsiDiskPoll() if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE; } - // Manually unrolled loop for performance. - // -Os won't unroll this for us automatically, - // especially since scsiPhyTx does volatile stuff. - // Reduces bus utilisation by making the fsmc split - // 32bits into 2 16 bit writes. - - uint16_t* scsiDmaData = (uint16_t*) &(scsiDev.data[SD_SECTOR_SIZE * (i % buffers) + partialScsiChunk]); - - uint32_t chunk = ((dmaBytes - partialScsiChunk) > SCSI_FIFO_DEPTH) - ? SCSI_FIFO_DEPTH : (dmaBytes - partialScsiChunk); - - int k = 0; - for (; k + 4 < (chunk + 1) / 2; k += 4) - { - scsiPhyTx32(scsiDmaData[k], scsiDmaData[k+1]); - scsiPhyTx32(scsiDmaData[k+2], scsiDmaData[k+3]); - } - for (; k < (chunk + 1) / 2; ++k) - { - scsiPhyTx(scsiDmaData[k]); - } - while (!scsiPhyComplete() && !scsiDev.resetFlag) - { - __WFE(); // Wait for event - } - scsiPhyFifoFlip(); - scsiSetDataCount(chunk); + uint8_t* scsiDmaData = &(scsiDev.data[SD_SECTOR_SIZE * (i % buffers)]); + scsiWritePIO(scsiDmaData, dmaBytes); - partialScsiChunk += chunk; - if (partialScsiChunk == dmaBytes) - { - partialScsiChunk = 0; - ++i; - } + ++i; } -#endif } if (phaseChangeDelayUs > 0 && !scsiDev.resetFlag) // zero bytes ? @@ -699,13 +665,14 @@ void scsiDiskPoll() // We've finished transferring the data to the FPGA, now wait until it's // written to he SCSI bus. + __disable_irq(); while (!scsiPhyComplete() && likely(scsiDev.phase == DATA_IN) && likely(!scsiDev.resetFlag)) { - __WFE(); // Wait for event + __WFI(); } - + __enable_irq(); if (scsiDev.phase == DATA_IN) { @@ -727,22 +694,28 @@ void scsiDiskPoll() transfer.lba); int i = 0; int clearBSY = 0; + int extraSectors = 0; int parityError = 0; int enableParity = scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY; + uint32_t scsiSpeed = s2s_getScsiRateMBs(); + + uint32_t maxSectors = sizeof(scsiDev.data) / SD_SECTOR_SIZE; + + static_assert(SCSI_XFER_MAX >= sizeof(scsiDev.data), "Assumes SCSI_XFER_MAX >= sizeof(scsiDev.data)"); + + // Start reading and filling fifos as soon as possible. + scsiSetDataCount(transfer.blocks * bytesPerSector); + while ((i < totalSDSectors) && likely(scsiDev.phase == DATA_OUT) && - likely(!scsiDev.resetFlag) && - likely(!parityError || !enableParity)) + likely(!scsiDev.resetFlag)) + // KEEP GOING to ensure FIFOs are in a good state. + // likely(!parityError || !enableParity)) { - // Well, until we have some proper non-blocking SD code, we must - // do this in a half-duplex fashion. We need to write as much as - // possible in each SD card transaction. - uint32_t maxSectors = sizeof(scsiDev.data) / SD_SECTOR_SIZE; uint32_t rem = totalSDSectors - i; - uint32_t sectors = - rem < maxSectors ? rem : maxSectors; + uint32_t sectors = rem < maxSectors ? rem : maxSectors; if (bytesPerSector == SD_SECTOR_SIZE) { @@ -750,19 +723,20 @@ void scsiDiskPoll() // no flow control. This can be handled if a) the scsi interface // doesn't block and b) we read enough SCSI sectors first so that // the SD interface cannot catch up. + int prevExtraSectors = extraSectors; uint32_t totalBytes = sectors * SD_SECTOR_SIZE; - uint32_t readAheadBytes = sectors * SD_SECTOR_SIZE; + extraSectors = 0; + + int32_t readAheadBytes = totalBytes; uint32_t sdSpeed = s2s_getSdRateMBs() + (scsiDev.sdUnderrunCount / 2); - uint32_t scsiSpeed = s2s_getScsiRateMBs(); // if (have blind writes) if (scsiSpeed > 0 && scsiDev.sdUnderrunCount < 16) { // readAhead = sectors * (sd / scsi - 1 + 0.1); - readAheadBytes = totalBytes * sdSpeed / scsiSpeed - totalBytes + SCSI_FIFO_DEPTH; - if (readAheadBytes < SCSI_FIFO_DEPTH) - { - readAheadBytes = SCSI_FIFO_DEPTH; - } + readAheadBytes = totalBytes * sdSpeed / scsiSpeed - totalBytes; + + // Round up to nearest FIFO size. + readAheadBytes = ((readAheadBytes / SCSI_FIFO_DEPTH) + 1) * SCSI_FIFO_DEPTH; if (readAheadBytes > totalBytes) { @@ -770,60 +744,58 @@ void scsiDiskPoll() } } - uint32_t chunk = (readAheadBytes > SCSI_FIFO_DEPTH) ? SCSI_FIFO_DEPTH : readAheadBytes; - scsiSetDataCount(chunk); + uint32_t prevExtraBytes = prevExtraSectors * SD_SECTOR_SIZE; + uint32_t scsiBytesRead = prevExtraBytes; + readAheadBytes -= prevExtraBytes; // Must be signed! - uint32_t scsiBytesRead = 0; - while (scsiBytesRead < readAheadBytes) + if (readAheadBytes > 0) { - while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) - { - __WFE(); // Wait for event - } - parityError |= scsiParityError(); - scsiPhyFifoFlip(); - uint32_t nextChunk = ((totalBytes - scsiBytesRead - chunk) > SCSI_FIFO_DEPTH) - ? SCSI_FIFO_DEPTH : (totalBytes - scsiBytesRead - chunk); - - if (nextChunk > 0) scsiSetDataCount(nextChunk); - scsiReadPIO(&scsiDev.data[scsiBytesRead], chunk); - scsiBytesRead += chunk; - chunk = nextChunk; + scsiReadPIO( + &scsiDev.data[scsiBytesRead], + readAheadBytes, + &parityError); + scsiBytesRead += readAheadBytes; } HAL_SD_WriteBlocks_DMA(&hsd, (uint32_t*) (&scsiDev.data[0]), (i + sdLBA) * 512ll, SD_SECTOR_SIZE, sectors); - while (scsiBytesRead < totalBytes) + int underrun = 0; + if (scsiBytesRead < totalBytes && !scsiDev.resetFlag) { - while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) - { - __WFE(); // Wait for event - } - parityError |= scsiParityError(); - scsiPhyFifoFlip(); - uint32_t nextChunk = ((totalBytes - scsiBytesRead - chunk) > SCSI_FIFO_DEPTH) - ? SCSI_FIFO_DEPTH : (totalBytes - scsiBytesRead - chunk); - - if (nextChunk > 0) scsiSetDataCount(nextChunk); - scsiReadPIO(&scsiDev.data[scsiBytesRead], chunk); - scsiBytesRead += chunk; - chunk = nextChunk; + scsiReadPIO( + &scsiDev.data[scsiBytesRead], + totalBytes - readAheadBytes, + &parityError); + + // Oh dear, SD finished first. + underrun = hsd.DmaTransferCplt; + + scsiBytesRead += (totalBytes - readAheadBytes); } - // Oh dear, SD finished first. - int underrun = totalBytes > readAheadBytes && hsd.DmaTransferCplt; + if (!underrun && rem > sectors) + { + // We probably have some time to waste reading more here. + // While noting this is going to drop us down into + // half-duplex operation (hence why we read max / 4 only) + + extraSectors = rem - sectors > (maxSectors / 4) + ? (maxSectors / 4) + : rem - sectors; + + scsiReadPIO( + &scsiDev.data[0], + extraSectors * SD_SECTOR_SIZE, + &parityError); + } uint32_t dmaFinishTime = s2s_getTime_ms(); - while (!hsd.SdTransferCplt && + while ((!hsd.SdTransferCplt || + __HAL_SD_SDIO_GET_FLAG(&hsd, SDIO_FLAG_TXACT)) && s2s_elapsedTime_ms(dmaFinishTime) < 180) { // Wait while keeping BSY. } - while((__HAL_SD_SDIO_GET_FLAG(&hsd, SDIO_FLAG_TXACT)) && - s2s_elapsedTime_ms(dmaFinishTime) < 180) - { - // Wait for SD card while keeping BSY. - } if (i + sectors >= totalSDSectors && !underrun && @@ -842,14 +814,14 @@ void scsiDiskPoll() HAL_SD_CheckWriteOperation(&hsd, (uint32_t)SD_DATATIMEOUT); - if (underrun) + if (underrun && (!parityError || !enableParity)) { // Try again. Data is still in memory. sdTmpWrite(&scsiDev.data[0], i + sdLBA, sectors); scsiDev.sdUnderrunCount++; } - i += sectors; + i += sectors; } else { @@ -857,11 +829,7 @@ void scsiDiskPoll() // do this in a half-duplex fashion. We need to write as much as // possible in each SD card transaction. // use sg_dd from sg_utils3 tools to test. - uint32_t maxSectors = sizeof(scsiDev.data) / SD_SECTOR_SIZE; - uint32_t rem = totalSDSectors - i; - uint32_t sectors = rem < maxSectors ? rem : maxSectors; - int scsiSector; - for (scsiSector = i; scsiSector < i + sectors; ++scsiSector) + for (int scsiSector = i; scsiSector < i + sectors; ++scsiSector) { int dmaBytes = SD_SECTOR_SIZE; if ((scsiSector % sdPerScsi) == (sdPerScsi - 1)) @@ -869,9 +837,10 @@ void scsiDiskPoll() dmaBytes = bytesPerSector % SD_SECTOR_SIZE; if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE; } - scsiRead(&scsiDev.data[SD_SECTOR_SIZE * (scsiSector - i)], dmaBytes, &parityError); + + scsiReadPIO(&scsiDev.data[SD_SECTOR_SIZE * (scsiSector - i)], dmaBytes, &parityError); } - if (!parityError) + if (!parityError || !enableParity) { sdTmpWrite(&scsiDev.data[0], i + sdLBA, sectors); } @@ -879,6 +848,15 @@ void scsiDiskPoll() } } + // Should already be complete here as we've ready the FIFOs + // by now. Check anyway. + __disable_irq(); + while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) + { + __WFI(); + } + __enable_irq(); + if (clearBSY) { enter_BusFree(); diff --git a/src/firmware/scsi.c b/src/firmware/scsi.c index 4864426f..25f02839 100755 --- a/src/firmware/scsi.c +++ b/src/firmware/scsi.c @@ -303,7 +303,6 @@ static void process_Command() { scsiRead(scsiDev.cdb + 6, scsiDev.cdbLen - 6, &parityError); } - command = scsiDev.cdb[0]; // Prefer LUN's set by IDENTIFY messages for newer hosts. diff --git a/src/firmware/scsi.h b/src/firmware/scsi.h index 5480a6a5..cbfa9807 100755 --- a/src/firmware/scsi.h +++ b/src/firmware/scsi.h @@ -106,7 +106,9 @@ typedef struct typedef struct { // TODO reduce this buffer size and add a proper cache - uint8_t data[MAX_SECTOR_SIZE * 8]; // Must be aligned for DMA + // Must be aligned for DMA + // 65536 bytes is the DMA limit + uint8_t data[MAX_SECTOR_SIZE * 8]; TargetState targets[S2S_MAX_TARGETS]; TargetState* target; diff --git a/src/firmware/scsiPhy.c b/src/firmware/scsiPhy.c index deb67b25..9337b13c 100755 --- a/src/firmware/scsiPhy.c +++ b/src/firmware/scsiPhy.c @@ -30,7 +30,8 @@ static uint8_t asyncTimings[][4] = { /* Speed, Assert, Deskew, Hold, Glitch */ -{/*1.5MB/s*/ 28, 18, 13, 15}, +{/*1.5MB/s*/ 28, 18, 7, 15}, +//{/*1.5MB/s*/ 63, 31, 7, 15}, {/*3.3MB/s*/ 13, 6, 6, 13}, {/*5MB/s*/ 9, 6, 6, 6}, // 80ns {/*safe*/ 3, 6, 6, 6}, // Probably safe @@ -106,8 +107,6 @@ static DMA_HandleTypeDef fsmcToMem; volatile uint8_t scsiRxDMAComplete; volatile uint8_t scsiTxDMAComplete; -uint8_t scsiPhyFifoSel = 0; // global - // scsi IRQ handler is initialised by the STM32 HAL. Connected to // PE4 // Note: naming is important to ensure this function is listed in the @@ -120,15 +119,18 @@ void EXTI4_IRQHandler() // Clear interrupt flag __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4); - scsiDev.resetFlag = scsiDev.resetFlag || scsiStatusRST(); + uint8_t statusFlags = *SCSI_STS_SCSI; + + scsiDev.resetFlag = scsiDev.resetFlag || (statusFlags & 0x04); // selFlag is required for Philips P2000C which releases it after 600ns // without waiting for BSY. // Also required for some early Mac Plus roms - scsiDev.selFlag = *SCSI_STS_SELECTED; + if (statusFlags & 0x08) // Check SEL flag + { + scsiDev.selFlag = *SCSI_STS_SELECTED; + } } - - __SEV(); // Set event. See corresponding __WFE() calls. } static void assertFail() @@ -145,92 +147,215 @@ static void assertFail() void scsiSetDataCount(uint32_t count) { - *SCSI_DATA_CNT_HI = count >> 8; + *SCSI_DATA_CNT_HI = (count >> 16) & 0xff; + *SCSI_DATA_CNT_MID = (count >> 8) & 0xff; *SCSI_DATA_CNT_LO = count & 0xff; *SCSI_DATA_CNT_SET = 1; } +int scsiFifoReady(void) +{ + __NOP(); + HAL_GPIO_ReadPin(GPIOE, FPGA_GPIO3_Pin); + __NOP(); + return HAL_GPIO_ReadPin(GPIOE, FPGA_GPIO3_Pin) != 0; +} + uint8_t scsiReadByte(void) { -#if FIFODEBUG - if (!scsiPhyFifoAltEmpty()) { - // Force a lock-up. - assertFail(); - } -#endif scsiSetDataCount(1); + // Ready immediately. setDataCount resets fifos + while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) { - __WFE(); // Wait for event + __WFI(); // Wait for interrupt } - scsiPhyFifoFlip(); + __enable_irq(); + uint8_t val = scsiPhyRx(); // TODO scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read(); -#if FIFODEBUG - if (!scsiPhyFifoEmpty()) { - int j = 0; - uint8_t k __attribute((unused)); - while (!scsiPhyFifoEmpty()) { k = scsiPhyRx(); ++j; } - - // Force a lock-up. - assertFail(); - } -#endif return val; } void -scsiReadPIO(uint8_t* data, uint32_t count) +scsiReadPIO(uint8_t* data, uint32_t count, int* parityError) { uint16_t* fifoData = (uint16_t*)data; + uint32_t count16 = (count + 1) / 2; - for (int i = 0; i < (count + 1) / 2; ++i) + int i = 0; + while ((i < count16) && likely(!scsiDev.resetFlag)) { - fifoData[i] = scsiPhyRx(); // TODO ASSUMES LITTLE ENDIAN - } -} - -void -scsiReadDMA(uint8_t* data, uint32_t count) -{ - // Prepare DMA transfer - dmaInProgress = 1; - - scsiTxDMAComplete = 1; // TODO not used much - scsiRxDMAComplete = 0; // TODO not used much + // Wait until FIFO is full (or complete) + while (!scsiFifoReady() && likely(!scsiDev.resetFlag)) + { + // spin + } - HAL_DMA_Start( - &fsmcToMem, - (uint32_t) SCSI_FIFO_DATA, - (uint32_t) data, - (count + 1) / 2); -} + if (count16 - i >= SCSI_FIFO_DEPTH16) + { + uint32_t chunk16 = SCSI_FIFO_DEPTH16; -int -scsiReadDMAPoll() -{ - int complete = __HAL_DMA_GET_COUNTER(&fsmcToMem) == 0; - complete = complete && (HAL_DMA_PollForTransfer(&fsmcToMem, HAL_DMA_FULL_TRANSFER, 0xffffffff) == HAL_OK); - if (complete) - { - scsiTxDMAComplete = 1; // TODO MM FIX IRQ - scsiRxDMAComplete = 1; + // Let gcc unroll the loop as much as possible. + for (uint32_t k = 0; k + 128 <= chunk16; k += 128) + { + fifoData[i + k] = scsiPhyRx(); + fifoData[i + k + 1] = scsiPhyRx(); + fifoData[i + k + 2] = scsiPhyRx(); + fifoData[i + k + 3] = scsiPhyRx(); + fifoData[i + k + 4] = scsiPhyRx(); + fifoData[i + k + 5] = scsiPhyRx(); + fifoData[i + k + 6] = scsiPhyRx(); + fifoData[i + k + 7] = scsiPhyRx(); + fifoData[i + k + 8] = scsiPhyRx(); + fifoData[i + k + 9] = scsiPhyRx(); + fifoData[i + k + 10] = scsiPhyRx(); + fifoData[i + k + 11] = scsiPhyRx(); + fifoData[i + k + 12] = scsiPhyRx(); + fifoData[i + k + 13] = scsiPhyRx(); + fifoData[i + k + 14] = scsiPhyRx(); + fifoData[i + k + 15] = scsiPhyRx(); + fifoData[i + k + 16] = scsiPhyRx(); + fifoData[i + k + 17] = scsiPhyRx(); + fifoData[i + k + 18] = scsiPhyRx(); + fifoData[i + k + 19] = scsiPhyRx(); + fifoData[i + k + 20] = scsiPhyRx(); + fifoData[i + k + 21] = scsiPhyRx(); + fifoData[i + k + 22] = scsiPhyRx(); + fifoData[i + k + 23] = scsiPhyRx(); + fifoData[i + k + 24] = scsiPhyRx(); + fifoData[i + k + 25] = scsiPhyRx(); + fifoData[i + k + 26] = scsiPhyRx(); + fifoData[i + k + 27] = scsiPhyRx(); + fifoData[i + k + 28] = scsiPhyRx(); + fifoData[i + k + 29] = scsiPhyRx(); + fifoData[i + k + 30] = scsiPhyRx(); + fifoData[i + k + 31] = scsiPhyRx(); + fifoData[i + k + 32] = scsiPhyRx(); + fifoData[i + k + 33] = scsiPhyRx(); + fifoData[i + k + 34] = scsiPhyRx(); + fifoData[i + k + 35] = scsiPhyRx(); + fifoData[i + k + 36] = scsiPhyRx(); + fifoData[i + k + 37] = scsiPhyRx(); + fifoData[i + k + 38] = scsiPhyRx(); + fifoData[i + k + 39] = scsiPhyRx(); + fifoData[i + k + 40] = scsiPhyRx(); + fifoData[i + k + 41] = scsiPhyRx(); + fifoData[i + k + 42] = scsiPhyRx(); + fifoData[i + k + 43] = scsiPhyRx(); + fifoData[i + k + 44] = scsiPhyRx(); + fifoData[i + k + 45] = scsiPhyRx(); + fifoData[i + k + 46] = scsiPhyRx(); + fifoData[i + k + 47] = scsiPhyRx(); + fifoData[i + k + 48] = scsiPhyRx(); + fifoData[i + k + 49] = scsiPhyRx(); + fifoData[i + k + 50] = scsiPhyRx(); + fifoData[i + k + 51] = scsiPhyRx(); + fifoData[i + k + 52] = scsiPhyRx(); + fifoData[i + k + 53] = scsiPhyRx(); + fifoData[i + k + 54] = scsiPhyRx(); + fifoData[i + k + 55] = scsiPhyRx(); + fifoData[i + k + 56] = scsiPhyRx(); + fifoData[i + k + 57] = scsiPhyRx(); + fifoData[i + k + 58] = scsiPhyRx(); + fifoData[i + k + 59] = scsiPhyRx(); + fifoData[i + k + 60] = scsiPhyRx(); + fifoData[i + k + 61] = scsiPhyRx(); + fifoData[i + k + 62] = scsiPhyRx(); + fifoData[i + k + 63] = scsiPhyRx(); + fifoData[i + k + 64] = scsiPhyRx(); + fifoData[i + k + 65] = scsiPhyRx(); + fifoData[i + k + 66] = scsiPhyRx(); + fifoData[i + k + 67] = scsiPhyRx(); + fifoData[i + k + 68] = scsiPhyRx(); + fifoData[i + k + 69] = scsiPhyRx(); + fifoData[i + k + 70] = scsiPhyRx(); + fifoData[i + k + 71] = scsiPhyRx(); + fifoData[i + k + 72] = scsiPhyRx(); + fifoData[i + k + 73] = scsiPhyRx(); + fifoData[i + k + 74] = scsiPhyRx(); + fifoData[i + k + 75] = scsiPhyRx(); + fifoData[i + k + 76] = scsiPhyRx(); + fifoData[i + k + 77] = scsiPhyRx(); + fifoData[i + k + 78] = scsiPhyRx(); + fifoData[i + k + 79] = scsiPhyRx(); + fifoData[i + k + 80] = scsiPhyRx(); + fifoData[i + k + 81] = scsiPhyRx(); + fifoData[i + k + 82] = scsiPhyRx(); + fifoData[i + k + 83] = scsiPhyRx(); + fifoData[i + k + 84] = scsiPhyRx(); + fifoData[i + k + 85] = scsiPhyRx(); + fifoData[i + k + 86] = scsiPhyRx(); + fifoData[i + k + 87] = scsiPhyRx(); + fifoData[i + k + 88] = scsiPhyRx(); + fifoData[i + k + 89] = scsiPhyRx(); + fifoData[i + k + 90] = scsiPhyRx(); + fifoData[i + k + 91] = scsiPhyRx(); + fifoData[i + k + 92] = scsiPhyRx(); + fifoData[i + k + 93] = scsiPhyRx(); + fifoData[i + k + 94] = scsiPhyRx(); + fifoData[i + k + 95] = scsiPhyRx(); + fifoData[i + k + 96] = scsiPhyRx(); + fifoData[i + k + 97] = scsiPhyRx(); + fifoData[i + k + 98] = scsiPhyRx(); + fifoData[i + k + 99] = scsiPhyRx(); + fifoData[i + k + 100] = scsiPhyRx(); + fifoData[i + k + 101] = scsiPhyRx(); + fifoData[i + k + 102] = scsiPhyRx(); + fifoData[i + k + 103] = scsiPhyRx(); + fifoData[i + k + 104] = scsiPhyRx(); + fifoData[i + k + 105] = scsiPhyRx(); + fifoData[i + k + 106] = scsiPhyRx(); + fifoData[i + k + 107] = scsiPhyRx(); + fifoData[i + k + 108] = scsiPhyRx(); + fifoData[i + k + 109] = scsiPhyRx(); + fifoData[i + k + 110] = scsiPhyRx(); + fifoData[i + k + 111] = scsiPhyRx(); + fifoData[i + k + 112] = scsiPhyRx(); + fifoData[i + k + 113] = scsiPhyRx(); + fifoData[i + k + 114] = scsiPhyRx(); + fifoData[i + k + 115] = scsiPhyRx(); + fifoData[i + k + 116] = scsiPhyRx(); + fifoData[i + k + 117] = scsiPhyRx(); + fifoData[i + k + 118] = scsiPhyRx(); + fifoData[i + k + 119] = scsiPhyRx(); + fifoData[i + k + 120] = scsiPhyRx(); + fifoData[i + k + 121] = scsiPhyRx(); + fifoData[i + k + 122] = scsiPhyRx(); + fifoData[i + k + 123] = scsiPhyRx(); + fifoData[i + k + 124] = scsiPhyRx(); + fifoData[i + k + 125] = scsiPhyRx(); + fifoData[i + k + 126] = scsiPhyRx(); + fifoData[i + k + 127] = scsiPhyRx(); + } - dmaInProgress = 0; -#if 0 - // TODO MM scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read(); -#endif - return 1; + i += chunk16; + } + else + { + uint32_t chunk16 = count16 - i; + uint32_t k = 0; + for (; k + 4 <= chunk16; k += 4) + { + fifoData[i + k] = scsiPhyRx(); + fifoData[i + 1 + k] = scsiPhyRx(); + fifoData[i + 2 + k] = scsiPhyRx(); + fifoData[i + 3 + k] = scsiPhyRx(); + } + for (; k < chunk16; ++k) + { + fifoData[i + k] = scsiPhyRx(); + } + i += chunk16; + } } - else - { - return 0; - } + + *parityError |= scsiParityError(); } void @@ -239,208 +364,173 @@ scsiRead(uint8_t* data, uint32_t count, int* parityError) int i = 0; *parityError = 0; - - uint32_t chunk = ((count - i) > SCSI_FIFO_DEPTH) - ? SCSI_FIFO_DEPTH : (count - i); -#ifdef SCSI_FSMC_DMA - if (chunk >= 16) - { - // DMA is doing 32bit transfers. - chunk = chunk & 0xFFFFFFF8; - } -#endif - scsiSetDataCount(chunk); - while (i < count && likely(!scsiDev.resetFlag)) { - while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) - { - __WFE(); // Wait for event - } - *parityError |= scsiParityError(); - scsiPhyFifoFlip(); + uint32_t chunk = ((count - i) > SCSI_XFER_MAX) + ? SCSI_XFER_MAX : (count - i); + scsiSetDataCount(chunk); - uint32_t nextChunk = ((count - i - chunk) > SCSI_FIFO_DEPTH) - ? SCSI_FIFO_DEPTH : (count - i - chunk); -#ifdef SCSI_FSMC_DMA - if (nextChunk >= 16) - { - nextChunk = nextChunk & 0xFFFFFFF8; - } -#endif - if (nextChunk > 0) - { - scsiSetDataCount(nextChunk); - } + scsiReadPIO(data + i, chunk, parityError); -#ifdef SCSI_FSMC_DMA - if (chunk < 16) -#endif - { - scsiReadPIO(data + i, chunk); - } -#ifdef SCSI_FSMC_DMA - else + __disable_irq(); + while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) { - scsiReadDMA(data + i, chunk); - - while (!scsiReadDMAPoll() && likely(!scsiDev.resetFlag)) - { - }; + __WFI(); } -#endif - + __enable_irq(); i += chunk; - chunk = nextChunk; } -#if FIFODEBUG - if (!scsiPhyFifoEmpty() || !scsiPhyFifoAltEmpty()) { - int j = 0; - while (!scsiPhyFifoEmpty()) { scsiPhyRx(); ++j; } - scsiPhyFifoFlip(); - int k = 0; - while (!scsiPhyFifoEmpty()) { scsiPhyRx(); ++k; } - // Force a lock-up. - assertFail(); - } -#endif } void scsiWriteByte(uint8_t value) { -#if FIFODEBUG - if (!scsiPhyFifoEmpty()) { - // Force a lock-up. - assertFail(); - } -#endif - scsiPhyTx(value); - scsiPhyFifoFlip(); - scsiSetDataCount(1); + scsiPhyTx(value); + __disable_irq(); while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) { - __WFE(); // Wait for event - } - -#if FIFODEBUG - if (!scsiPhyFifoAltEmpty()) { - // Force a lock-up. - assertFail(); + __WFI(); } -#endif + __enable_irq(); } -static void +void scsiWritePIO(const uint8_t* data, uint32_t count) { uint16_t* fifoData = (uint16_t*)data; - for (int i = 0; i < (count + 1) / 2; ++i) + uint32_t count16 = (count + 1) / 2; + + int i = 0; + while ((i < count16) && likely(!scsiDev.resetFlag)) { - scsiPhyTx(fifoData[i]); - } -} + while (!scsiFifoReady() && likely(!scsiDev.resetFlag)) + { + // Spin + } -void -scsiWriteDMA(const uint8_t* data, uint32_t count) -{ - // Prepare DMA transfer - dmaInProgress = 1; + if (count16 - i >= SCSI_FIFO_DEPTH16) + { + uint32_t chunk16 = SCSI_FIFO_DEPTH16; - scsiTxDMAComplete = 0; - scsiRxDMAComplete = 1; + // Let gcc unroll the loop as much as possible. + for (uint32_t k = 0; k + 128 <= chunk16; k += 128) + { + scsiPhyTx32(fifoData[i + k], fifoData[i + k + 1]); + scsiPhyTx32(fifoData[i + 2 + k], fifoData[i + k + 3]); + scsiPhyTx32(fifoData[i + 4 + k], fifoData[i + k + 5]); + scsiPhyTx32(fifoData[i + 6 + k], fifoData[i + k + 7]); + scsiPhyTx32(fifoData[i + 8 + k], fifoData[i + k + 9]); + scsiPhyTx32(fifoData[i + 10 + k], fifoData[i + k + 11]); + scsiPhyTx32(fifoData[i + 12 + k], fifoData[i + k + 13]); + scsiPhyTx32(fifoData[i + 14 + k], fifoData[i + k + 15]); + scsiPhyTx32(fifoData[i + 16 + k], fifoData[i + k + 17]); + scsiPhyTx32(fifoData[i + 18 + k], fifoData[i + k + 19]); + scsiPhyTx32(fifoData[i + 20 + k], fifoData[i + k + 21]); + scsiPhyTx32(fifoData[i + 22 + k], fifoData[i + k + 23]); + scsiPhyTx32(fifoData[i + 24 + k], fifoData[i + k + 25]); + scsiPhyTx32(fifoData[i + 26 + k], fifoData[i + k + 27]); + scsiPhyTx32(fifoData[i + 28 + k], fifoData[i + k + 29]); + scsiPhyTx32(fifoData[i + 30 + k], fifoData[i + k + 31]); + + scsiPhyTx32(fifoData[i + 32 + k], fifoData[i + k + 33]); + scsiPhyTx32(fifoData[i + 34 + k], fifoData[i + k + 35]); + scsiPhyTx32(fifoData[i + 36 + k], fifoData[i + k + 37]); + scsiPhyTx32(fifoData[i + 38 + k], fifoData[i + k + 39]); + scsiPhyTx32(fifoData[i + 40 + k], fifoData[i + k + 41]); + scsiPhyTx32(fifoData[i + 42 + k], fifoData[i + k + 43]); + scsiPhyTx32(fifoData[i + 44 + k], fifoData[i + k + 45]); + scsiPhyTx32(fifoData[i + 46 + k], fifoData[i + k + 47]); + scsiPhyTx32(fifoData[i + 48 + k], fifoData[i + k + 49]); + scsiPhyTx32(fifoData[i + 50 + k], fifoData[i + k + 51]); + scsiPhyTx32(fifoData[i + 52 + k], fifoData[i + k + 53]); + scsiPhyTx32(fifoData[i + 54 + k], fifoData[i + k + 55]); + scsiPhyTx32(fifoData[i + 56 + k], fifoData[i + k + 57]); + scsiPhyTx32(fifoData[i + 58 + k], fifoData[i + k + 59]); + scsiPhyTx32(fifoData[i + 60 + k], fifoData[i + k + 61]); + scsiPhyTx32(fifoData[i + 62 + k], fifoData[i + k + 63]); + + scsiPhyTx32(fifoData[i + 64 + k], fifoData[i + k + 65]); + scsiPhyTx32(fifoData[i + 66 + k], fifoData[i + k + 67]); + scsiPhyTx32(fifoData[i + 68 + k], fifoData[i + k + 69]); + scsiPhyTx32(fifoData[i + 70 + k], fifoData[i + k + 71]); + scsiPhyTx32(fifoData[i + 72 + k], fifoData[i + k + 73]); + scsiPhyTx32(fifoData[i + 74 + k], fifoData[i + k + 75]); + scsiPhyTx32(fifoData[i + 76 + k], fifoData[i + k + 77]); + scsiPhyTx32(fifoData[i + 78 + k], fifoData[i + k + 79]); + scsiPhyTx32(fifoData[i + 80 + k], fifoData[i + k + 81]); + scsiPhyTx32(fifoData[i + 82 + k], fifoData[i + k + 83]); + scsiPhyTx32(fifoData[i + 84 + k], fifoData[i + k + 85]); + scsiPhyTx32(fifoData[i + 86 + k], fifoData[i + k + 87]); + scsiPhyTx32(fifoData[i + 88 + k], fifoData[i + k + 89]); + scsiPhyTx32(fifoData[i + 90 + k], fifoData[i + k + 91]); + scsiPhyTx32(fifoData[i + 92 + k], fifoData[i + k + 93]); + scsiPhyTx32(fifoData[i + 94 + k], fifoData[i + k + 95]); + + scsiPhyTx32(fifoData[i + 96 + k], fifoData[i + k + 97]); + scsiPhyTx32(fifoData[i + 98 + k], fifoData[i + k + 99]); + scsiPhyTx32(fifoData[i + 100 + k], fifoData[i + k + 101]); + scsiPhyTx32(fifoData[i + 102 + k], fifoData[i + k + 103]); + scsiPhyTx32(fifoData[i + 104 + k], fifoData[i + k + 105]); + scsiPhyTx32(fifoData[i + 106 + k], fifoData[i + k + 107]); + scsiPhyTx32(fifoData[i + 108 + k], fifoData[i + k + 109]); + scsiPhyTx32(fifoData[i + 110 + k], fifoData[i + k + 111]); + scsiPhyTx32(fifoData[i + 112 + k], fifoData[i + k + 113]); + scsiPhyTx32(fifoData[i + 114 + k], fifoData[i + k + 115]); + scsiPhyTx32(fifoData[i + 116 + k], fifoData[i + k + 117]); + scsiPhyTx32(fifoData[i + 118 + k], fifoData[i + k + 119]); + scsiPhyTx32(fifoData[i + 120 + k], fifoData[i + k + 121]); + scsiPhyTx32(fifoData[i + 122 + k], fifoData[i + k + 123]); + scsiPhyTx32(fifoData[i + 124 + k], fifoData[i + k + 125]); + scsiPhyTx32(fifoData[i + 126 + k], fifoData[i + k + 127]); - HAL_DMA_Start( - &memToFSMC, - (uint32_t) data, - (uint32_t) SCSI_FIFO_DATA, - count / 4); -} + } -int -scsiWriteDMAPoll() -{ - int complete = __HAL_DMA_GET_COUNTER(&memToFSMC) == 0; - complete = complete && (HAL_DMA_PollForTransfer(&memToFSMC, HAL_DMA_FULL_TRANSFER, 0xffffffff) == HAL_OK); - if (complete) - { - scsiTxDMAComplete = 1; // TODO MM FIX IRQ - scsiRxDMAComplete = 1; + i += chunk16; + } + else + { + uint32_t chunk16 = count16 - i; - dmaInProgress = 0; - return 1; - } - else - { - return 0; + uint32_t k = 0; + for (; k + 4 <= chunk16; k += 4) + { + scsiPhyTx32(fifoData[i + k], fifoData[i + k + 1]); + scsiPhyTx32(fifoData[i + k + 2], fifoData[i + k + 3]); + } + for (; k < chunk16; ++k) + { + scsiPhyTx(fifoData[i + k]); + } + i += chunk16; + } } } + void scsiWrite(const uint8_t* data, uint32_t count) { int i = 0; while (i < count && likely(!scsiDev.resetFlag)) { - uint32_t chunk = ((count - i) > SCSI_FIFO_DEPTH) - ? SCSI_FIFO_DEPTH : (count - i); - -#if FIFODEBUG - if (!scsiPhyFifoEmpty()) { - // Force a lock-up. - assertFail(); - } -#endif - -#ifdef SCSI_FSMC_DMA - if (chunk < 16) -#endif - { - scsiWritePIO(data + i, chunk); - } -#ifdef SCSI_FSMC_DMA - else - { - // DMA is doing 32bit transfers. - chunk = chunk & 0xFFFFFFF8; - scsiWriteDMA(data + i, chunk); + uint32_t chunk = ((count - i) > SCSI_XFER_MAX) + ? SCSI_XFER_MAX : (count - i); + scsiSetDataCount(chunk); - while (!scsiWriteDMAPoll() && likely(!scsiDev.resetFlag)) - { - } - } -#endif + scsiWritePIO(data + i, chunk); + __disable_irq(); while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) { - __WFE(); // Wait for event + __WFI(); } + __enable_irq(); -#if FIFODEBUG - if (!scsiPhyFifoAltEmpty()) { - // Force a lock-up. - assertFail(); - } -#endif - - scsiPhyFifoFlip(); - scsiSetDataCount(chunk); i += chunk; } - while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) - { - __WFE(); // Wait for event - } - -#if FIFODEBUG - if (!scsiPhyFifoAltEmpty()) { - // Force a lock-up. - assertFail(); - } -#endif } static inline void busSettleDelay(void) @@ -498,10 +588,6 @@ uint32_t scsiEnterPhaseImmediate(int newPhase) int oldPhase = *SCSI_CTRL_PHASE; - if (!scsiDev.resetFlag && (!scsiPhyFifoEmpty() || !scsiPhyFifoAltEmpty())) { - // Force a lock-up. - assertFail(); - } if (newPhase != oldPhase) { if ((newPhase == DATA_IN || newPhase == DATA_OUT) && @@ -639,8 +725,6 @@ void scsiPhyReset() *SCSI_CTRL_PHASE = 0x00; *SCSI_CTRL_BSY = 0x00; - scsiPhyFifoSel = 0; - *SCSI_FIFO_SEL = 0; *SCSI_CTRL_DBX = 0; *SCSI_CTRL_SYNC_OFFSET = 0; @@ -674,6 +758,31 @@ void scsiPhyReset() } #endif + // PIO Benchmark code + // Currently 16.7MB/s. + //#define PIO_BENCHMARK 1 + #ifdef PIO_BENCHMARK + while(1) + { + s2s_ledOn(); + + scsiEnterPhase(DATA_IN); // Need IO flag set for fifo ready flag + + // 100MB + for (int i = 0; i < (100LL * 1024 * 1024 / SCSI_FIFO_DEPTH); ++i) + { + scsiSetDataCount(1); // Resets fifos. + + // Shouldn't block + scsiDev.resetFlag = 0; + scsiWritePIO(&scsiDev.data[0], SCSI_FIFO_DEPTH); + } + s2s_ledOff(); + + for(int i = 0; i < 10; ++i) s2s_delay_ms(1000); + } + #endif + #ifdef SCSI_FREQ_TEST while(1) { @@ -749,8 +858,6 @@ void scsiPhyInit() *SCSI_CTRL_IDMASK = 0x00; // Reset in scsiPhyConfig *SCSI_CTRL_PHASE = 0x00; *SCSI_CTRL_BSY = 0x00; - scsiPhyFifoSel = 0; - *SCSI_FIFO_SEL = 0; *SCSI_CTRL_DBX = 0; *SCSI_CTRL_SYNC_OFFSET = 0; diff --git a/src/firmware/scsiPhy.h b/src/firmware/scsiPhy.h index 19f0aa6f..c2288db7 100755 --- a/src/firmware/scsiPhy.h +++ b/src/firmware/scsiPhy.h @@ -20,8 +20,8 @@ #define SCSI_CTRL_IDMASK ((volatile uint8_t*)0x60000000) #define SCSI_CTRL_PHASE ((volatile uint8_t*)0x60000002) #define SCSI_CTRL_BSY ((volatile uint8_t*)0x60000004) -#define SCSI_FIFO_SEL ((volatile uint8_t*)0x60000006) -#define SCSI_DATA_CNT_HI ((volatile uint8_t*)0x60000008) +#define SCSI_DATA_CNT_HI ((volatile uint8_t*)0x60000006) +#define SCSI_DATA_CNT_MID ((volatile uint8_t*)0x60000008) #define SCSI_DATA_CNT_LO ((volatile uint8_t*)0x6000000A) #define SCSI_DATA_CNT_SET ((volatile uint8_t*)0x6000000C) #define SCSI_CTRL_DBX ((volatile uint8_t*)0x6000000E) @@ -35,7 +35,7 @@ #define SCSI_CTRL_SEL_TIMING ((volatile uint8_t*)0x60000018) #define SCSI_STS_FIFO ((volatile uint8_t*)0x60000020) -#define SCSI_STS_ALTFIFO ((volatile uint8_t*)0x60000022) +// Obsolete #define SCSI_STS_ALTFIFO ((volatile uint8_t*)0x60000022) #define SCSI_STS_FIFO_COMPLETE ((volatile uint8_t*)0x60000024) #define SCSI_STS_SELECTED ((volatile uint8_t*)0x60000026) #define SCSI_STS_SCSI ((volatile uint8_t*)0x60000028) @@ -47,18 +47,17 @@ #define SCSI_STS_PARITY_ERR ((volatile uint8_t*)0x6000002C) #define SCSI_FIFO_DATA ((volatile uint16_t*)0x60000040) -#define SCSI_FIFO_DEPTH 256 +#define SCSI_FIFO_DEPTH 512 +#define SCSI_FIFO_DEPTH16 (SCSI_FIFO_DEPTH / 2) +#define SCSI_XFER_MAX 524288 -#define scsiPhyFifoFull() ((*SCSI_STS_FIFO & 0x01) == 0x01) -#define scsiPhyFifoEmpty() ((*SCSI_STS_FIFO & 0x02) == 0x02) -#define scsiPhyFifoAltEmpty() ((*SCSI_STS_ALTFIFO & 0x02) == 0x02) +// Check if FIFO is empty or full. +// Replaced with method due to delays +// #define scsiFifoReady() (HAL_GPIO_ReadPin(GPIOE, FPGA_GPIO3_Pin) != 0) -#define scsiPhyFifoFlip() \ -{\ - scsiPhyFifoSel ^= 1; \ - *SCSI_FIFO_SEL = scsiPhyFifoSel; \ -} +#define scsiPhyFifoFull() ((*SCSI_STS_FIFO & 0x01) != 0) +#define scsiPhyFifoEmpty() ((*SCSI_STS_FIFO & 0x02) != 0) #define scsiPhyTx(val) *SCSI_FIFO_DATA = (val) @@ -69,24 +68,23 @@ #define scsiPhyRx() *SCSI_FIFO_DATA #define scsiPhyComplete() ((*SCSI_STS_FIFO_COMPLETE & 0x01) == 0x01) -#define scsiStatusATN() ((*SCSI_STS_SCSI & 0x01) == 0x01) -#define scsiStatusBSY() ((*SCSI_STS_SCSI & 0x02) == 0x02) -#define scsiStatusRST() ((*SCSI_STS_SCSI & 0x04) == 0x04) -#define scsiStatusSEL() ((*SCSI_STS_SCSI & 0x08) == 0x08) -#define scsiStatusACK() ((*SCSI_STS_SCSI & 0x10) == 0x10) +#define scsiStatusATN() ((*SCSI_STS_SCSI & 0x01) != 0) +#define scsiStatusBSY() ((*SCSI_STS_SCSI & 0x02) != 0) +#define scsiStatusRST() ((*SCSI_STS_SCSI & 0x04) != 0) +#define scsiStatusSEL() ((*SCSI_STS_SCSI & 0x08) != 0) +#define scsiStatusACK() ((*SCSI_STS_SCSI & 0x10) != 0) -#define scsiParityError() ((*SCSI_STS_PARITY_ERR & 0x1) == 0x1) +#define scsiParityError() ((*SCSI_STS_PARITY_ERR & 0x1) != 0) // Disable DMA due to errate with the STM32F205 DMA2 controller when // concurrently transferring FSMC (with FIFO) and APB (ie. sdio) // peripherals. #undef SCSI_FSMC_DMA -extern uint8_t scsiPhyFifoSel; - void scsiPhyInit(void); void scsiPhyConfig(void); void scsiPhyReset(void); +int scsiFifoReady(void); void scsiEnterPhase(int phase); uint32_t scsiEnterPhaseImmediate(int phase); @@ -111,7 +109,8 @@ void scsiReadDMA(uint8_t* data, uint32_t count); int scsiReadDMAPoll(); // Low-level. -void scsiReadPIO(uint8_t* data, uint32_t count); +void scsiReadPIO(uint8_t* data, uint32_t count, int* parityError); +void scsiWritePIO(const uint8_t* data, uint32_t count); void scsiWriteDMA(const uint8_t* data, uint32_t count); int scsiWriteDMAPoll(); diff --git a/src/firmware/sd.h b/src/firmware/sd.h index 0a9d6328..b82f1192 100755 --- a/src/firmware/sd.h +++ b/src/firmware/sd.h @@ -34,14 +34,9 @@ extern SdDevice sdDev; int sdInit(void); -void sdWriteMultiSectorPrep(uint32_t sdLBA, uint32_t sdSectors); -void sdWriteMultiSectorDMA(uint8_t* outputBuffer); -int sdWriteSectorDMAPoll(); - void sdReadDMA(uint32_t lba, uint32_t sectors, uint8_t* outputBuffer); int sdReadDMAPoll(uint32_t remainingSectors); void sdCompleteTransfer(); -void sdPoll(); #endif -- 2.38.5