From d4e6244bbaa55732bf935a6163f230aa3edc7835 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Fri, 14 Oct 2016 12:26:45 +0100 Subject: [PATCH] Optimised register writing --- VM registers.xlsx | Bin 35545 -> 36055 bytes scope.py | 113 ++++++++++++-- streams.py | 5 +- vm.py | 370 ++++++++++++++++++++++++++-------------------- 4 files changed, 312 insertions(+), 176 deletions(-) diff --git a/VM registers.xlsx b/VM registers.xlsx index 9bf3218ea9802230aa79b48918e074f8d4efd48a..c4dbf42a7206e7760d9e4d8d89937b9c76e24d86 100644 GIT binary patch delta 9446 zcmZ8{WmFt(llIKukTAHrJ0!SUAh^3bg9mp8cXxLS?(PmDfuO=>Ib95XNH@^Mio zfKDLZN8JH$lZ{BmU@}gxZbj5BZtjMbqLT^a8K~7t-)?Fi@DcGQqx;S{^Pyr3q){Y* zxZ?`d{N+4ZRF>GJbD>}l^fhljOB6vf&3;|ni63t;IR#0yPA9B}US7?suRkClG;a{;qcHqqZa-gur(8Fo^G zi89y!$t60M`t*?m5J7iIJ)9lDD{m;E8PSSY|v zwHjjjF1;%-1$1MjeIlgOnm&lAktB&iVQ2RF%HchU+@;Iu1(%$~4koZm){QFW3%|62 ztv*Z%&qQO7=Z88UQO=lc`Ht9Uy0wv)k87}g>B9+m)GKuI8p_GVKPlChdSbp(VO=RL z2=gZ;6xmwJidU{JuOK@G8pL{ydy5fQ8+;PbsK0|t+JeV|Rx#~-t49C;1V{k@EC2xD zVaw_aF?KRHQ+08&vbS_*@vyVa)?QJd{EX%smkg!5Cnu0)Y8mCKLqZGqeqNXkx8#pi zAi%+H*Ezg8GrOaImAo-_u_vptW@_ZF7$^Vt{b_~j+Po{Ty3o^&bLOr-Av&&B7+Uni&m7W<$Od5u}MG zR>3kd&^*TkoCtvkQ|50N-%0a5%R~wn@;8$-BwOf-el3cyPGz@6A%8`G^<(Ub>7u!_ zVWwERYdFaG$(w!}W^~15AobCeC$T|?Lo?#0BFf=F-#w-QlM7+Se22q=5F{^6PixCUTdBMVX}3?IJE6vW0B!T>u9?%qw9`JuE{5qt4caR z`DYO8xg-*9h|ME)ml7Q?C+UaKXJR=Y%*xf4ueDbmQ8C(77+s>^=Us(Q^*UeE*^d&0 zRN{B@?pRSB45J+nIjQWz&m9wFaCyaR+0z=~YV4yZ26aCCk(p9W)M|rwJ$emEu*rIm zABFy;+kom@~=!qUNpr*4x-@jL?5Ke<>ni2lF3uZqT)5mYzdg0L8!7T)o?Ua(X z&yV`Of8uL6ZERnkw7C=t-35CtG|4C zduE!Gxn&;ksBI?aFx6;(=B3Q!BJ~q9I_e@+Uo_ zGRg-rprMgg!<%i{`PZOOP=sp5l)r30EM;42Kp0Ty*{r?OCs0-R9g*8iok@)%{Zl+G z+O|q_h0cY9>uUIfNZ!q|GjzKpY~y4 z?JlChzM1XYRTpFciZXwclR7!jc$gqQYTaV!opzhc%^*o%ItMWHPA;RL!gFDyw7~t1 z{G#$v;x$;;+NU!h%QC=_ADh-ByFgY-(qgj3IMDVUFc=M+~vmcXjYx z*#etT-Z7VRn@|<6In7~DoNrU%k)p4G6;;S^PW9rNF66U%!ZRsa9y&fhhxQP(I_|$O zZ6RT&uw1DKC3yLWjK*vZiG$x)l?7>jYk?`e+bmsj`(KceqJ`;%OfeYUGg zE;vDLKgEJz;CouPlt9-`y1v|5&NL<70BfB9U7L$ryOd}>77?LbpCDX3xZ2T&eC zue{x~Y^0JlNCD=w>bIPmd(tHuH5BXOQ=q7azbIs{X9M|GAeUG$Q~+){l~RY%r=G*3 z9b>=iMh!8*+5fVwUwx}mZ?8UZt9@K^<$G1_d3~^YD+z2?&fXFbYeC!B1!un>-txw9 zkE!Vjr_m&-u_a5}aLp9y+AOQ_L8;g>F8jKV?ZV3=s*#;Q7j)mWD|Q?`3q?JtA~Y(> zG>2((Jsj;TNq4XoPuP{7RPY)jz4SbjbP5_EvR#d0pkZms#fIa1Ja&>>%sWcf#5jx+ ziXC7Qx{2DrlKU?&T}Qk<1y5t+8UD)$qvO%ee(wWp`QM`bYP?)IH1+?|_lOuBQGY)K zsGqsXTy7qUGcr2&92y@we~{@c+^=x+lO+|rxk8yk`E=7oR#d8PqLx7SxH3HaJ(xwH z4HLgJl3S3F63W!!$zYn99TU+ynpj!5^?vY7qV}8c5Oo~k^oF8fjfi3IqKd^v(Cgz8 ztYTdAq5JuE*LUbMT%qduB&kP6^kHtx>)bHCRs(X%6Cvo%EP1EmrY5TCK%gx+T!LEugY~L2HpX+=cZ2{(R{NM!INt+j@JG`>C&QmjbvA-=&bOsOy0QI z!PmXuU&1FB1gWIlYqh`mo=$Erp-G$Y*p1nkm?+4+s~;_$aRGqRz@&B(5@@l$zVl9- zldq!s3ydsSyM4kYYVOv|!hWftDK}D(-+W5pSZD^rjy?60ej-8Kh3bpsi-Dgs0U2z< z+Gngu+p`+G%Y5Jsc_xnt%+p<~VP(lJyG$dGHq`pwGA9+c`e2;GK-ky%`NsF}WBH!B zXPZIrH#1^i>uw3{6NkLl8>pdB56zM6Um}V2&o~Q9Wed)^4no;^me&UmYzxbA8lBmc zQ7Ziq-scRV9Lp>YWZaHvYflmvzMyM$yjtCm5b)QinBB_{xt}%1eL5o-JdLShQjHbP zrgC@WTN+z3JJWSTj=s5FKMH8ujSY!+MPGBr+`Vvr5VtHUynV>Y z_Vv_YrpANUA}~BAp4e4hXoxshAoEfGlRCza>pM%e zKFe;F`UkLn?L!WOU@Npa6}r)ERI2@y>IF8&kx$%%jzm3WWQw|fqwspy`t6CuvpaUX z>>yi+I6g1AQ;2E3rqEVFK-u&MNd3EjcB1@4Z)@cIv!p#L^eC@3Wc=q+IiX93-?W1# zSUGjd#Jqm*d#iSCmD>%0YDhvKUP0bSxUkSQLidkNV#zV`4r=J1Sv;g4t69_RB}ZlS zYfyKTa;`PdgO%;O2TT3Wmf5(m^Hc^Qo_F`AnO$CUMPsf@G-}VYuOj-|mzZca;dw%9 ztj_BDV%QA!4T{y#LeKZShd08xuf@8i_H0SSlKMB5i#1C3qS)*b>BiXXFzJ3q9}fOt zpWRYjZM+Jq7nnjfoWn#nFjGkDFCy4X{|W;4r*GAWZ`XV(#727#P=k7C={7Fv-gu`y z>Ex3@p|7;vuih16^B3bfp4wz%8*|`D{;X!lhQ@#=va<`_TJIlf6dlnvMnyx-El%=y z^F0D3J-2tBR|kvZ!?#D854CHBP=)Sq>t8jW79KX6`JugsMJueDXN(U#`B25f62^HqUasWtX$6$?H^#mtAZNN2*J`k`>AAhewrVB<}%T zVLZ27%E#aH25XBbA4m7YU4q8N#+t2FL zn;Qe#QlNjehq7c~qpa8?5Mf(`mz*o1zNoX1pA3?~#b@v$4Cij-%{8Ht;wjnjk*Q=i zNar|8CH`TB&HKY7820*j8-2Sr@#gjA_H}nXxM$=AwQYJZX7CmegT4+0my6xpiUj2I zm_BlQm&&AGpair>J)XVN`>as=x#_`bcw9%ax zwBv5_x1E>IgT@<-{ho6-J2W+#Ar&>ZOFNql{%Sr`ML&N;l9)CsbCd^!Q3AFurNoLa zkao7c*@^BNe#{ns@lDI+?OgHe@QCc{g5oK7)DL*XU8+cwc=qE+(5|sJTyK6sR7q?I zFj6jwtt((odY~611d)Wk8)4-DYjQRu`Xf--7`+0JJ*dReg-+BLk^i77ysVz{78tKP zK<{C&BN?)(kY~e_EGSeexPOX2fmFAG!)Swe`;Zb7^sFfG$5Utq>e$?>b*@kqpnk#M z?9~1w`1+-vVtHs; z`7&~?K{G$mf4Gx=pe~dsH8hK@F;vL(gqpId|ATZ{Un+;d>PRU69Hfc zGBwDHig#40eh0v#DQB?phz&gp=wgP{%_Zh}0J z8$xMM{bMb~cVDxXQH*&QX$E0t}2DBA+SQm&6vkR)SX@ z%!tQBPU}tx=^>olP?vl)qY2USZzg|+mIG#c8xmQfOjJ$ttLD6!l)j6YmZWNsSuQeO zWa90n?ekUbH2b~eF8B5_oF~3?jLCTUG+=NPkCrh4 z;fF{+Z&eBECV~TUzc7SKY|I?q=6I!tW%*=DhJ2}OC=ZG5A?wF{;^_1_yP&19?KpdM=N^}D~CC|>VzdWkq1TlD59 zeYPx3*tGJNqPzuH4>IwM+brPmq=P}cg2<3~MU!8vkJo}IxGMtW^!Wq57@*+GW%9S* zFuEuj2gX>(T!L|Ye=^h?l-HcC#a~YAFLTG^7s=*>3^oD)V?h}v&rY4*81Axbn@*2+ zQr7-gpCHHKS1L_`LQT>mf( zAIX*6g<(JX8GhaRDeBIx(jB08ebz|lZN;{i<b>60=m6^ig-|OpU$pVY$;by;Qx0uiHMtpsFxlPL47b|NDpM!9k*wPjj0`$O}os zzO0<*{!?v7%j(d!zau#{ZE*`xf$t1iqcbuTU3V;9#*L*& zITBkt)HvI|v5@VZ`A^kr^G~0stfSr&5kkh|r|N<4Jc{`ybY06fdWQ&iZf4lkZv&!P z+C91x^3}(~Y*j?P^jSdQ`{2xGm~IwE{3|5R>3H%yB^W5)rt4?XN}tL_ikKBy)A50` zOJOU%B_{9r;B?6c&iEOX4*lar@;u2#xBL}zYH^_$c!H0+vk(Bx>Up_gio->rJXYq1 zF2l^v(|(FK61DOu7fpSIDnT~c8&V_iD#obVTHPG!S8Wz_6!M;M=?BVzGLjTCW1-QX zibbRPs&1kBl@r+-o6b6FhQ8WLI<=nb=d+>bH=GWl_~ps@PZ-tUSenilj!G6+v)}e? z=CyGi#r1B}Xb5T_w-exvJ&J*NydIil+_kgIo=Zo3b6A+8RO<8+dAZe}_12|I-1a68 z!zJQngTA(9gU#L??xci7I+h>$JK$X(AdSCbE(*$d=IN1HGf!04K6`twIV_O{k8hQ{ zSW5q>-o{I0+wl*ydiNPMf=XkoM_s)e8A1qfhe&XpD2mF^A6>c!1BU0zT$M$AmslW) zGLFmyQ^`f5a*j5H%DhvBSO#Yobgl)PmqR=jSY^f(2<{8t+@Ua^=XOFCI-y#6E7De= z^GVtS&!)1vq3e+BA|u+13(S}~drjoA`fyp5vseke<)r{yf0<8uAUyOmy}6nPNmN@~ z8pfh(uSbLnD(z}WQtE9G7b5l1R+&8|^GeEq&DyMla=doNdGgI&jQ< zSVR3_`}Fypf&=N(Yw;7TU=>rU%?clsyvHb?<`1_@lYYD-lfCm-$a8EsAHO&x8Ek#Y zPjTwsyb~MFPxqRkNL_xcmu+Z3f006i;pX0{O{=N67LBn|xJSXrMN70-nDFCF28rP@ zV6NRSjFH6#(FvFu#;@lTP=ewEjKqhT_5eE#6Fj zc?)X*YKaANyf(U4ASuNT>#e`P(zq9_Nc}xl8P%ejv(SEjCtwOSc`u67M^Hc2h%2J% zlUK$HKr`x>zqe<=f9+PmSb>uM%p^gSb!e35mc}_#c?JLVH=w+%lqJ6G?pO1j!o;Z} z-}P3Cy11@ZLK{-W0Z2U<1Z2YtGSp!S=GPwFM$zwCcm{P#%4ntW?L#xBPag$b`w0SH z5Ugf}rt|^wm%h1O0Fk~|dlRi{GrKgJliu1{31m>@l;Dla?`6dbQs~h2plQAV<^3C^ zb60Y2kJLqLX#P%#{f&g5FeH){;|WN&d6KY*yz8{H@kzL-r2W#h!y?QrwQ|>df@MH_tfygxvGAtTF9MbIgDiRx=AN^lALLa5A12&`uFV{<^hu!OLGt zum}SJ5E_cOdPmr0 zOo2uG0iUIPa4kn(i)Owg3zZ~Qc1fK{%zH;bvU0{BF0FJEP$foN+;0fzP2cJXHqb;H zEh-s(Eh76*Z#e5q2?Jr+2xqD3%sLM`euf?GHV&xioU)Z{!o#1}?~=U<8gGWGGiFPv zAy0g)CT|h|ye)BiTaqN+w{GI;5?-mHFn4}JOkN@ZDk-naz9G^YFT3F z38si-R5a8Eu>ok-Bz2D%4iEN@c9R$P#%$T~LmkQ-z6t=cmH4mJHk zGkV|u?+rR2bKALu3x;f(k!Tp&MDWQ~KAPcccqZPwYpin2kn4u=*CnHhoWLlXe_kpz z;XXEq9uhfad1(QF{q+TFkpT~%YlK=}8MkQl2vS`4h*~)XuUcT*r=q<3e#xeZX{*npCTgwwhnJbHpGpQU zC+N9)w=$ra3q<_AnQkT+!)4W=QkI#?bn^TXdo-t-w^Dz)b!kHxu!b^)*`qeUdJv=g-?s_?o_dmv9xsq0au48a8Q37KnaPCIf zwBB#v!;AFQD_AcTLlaqS;feRoOWV22g0gRFW`k~j=O zx7@aVHa%~x=7>`6eU&BdMsOcMe*l-u^o*JzS8hcq17M#Hpv4T|cOb%ScYhC3ZlBZK zr7ib~&WB@$wgF>2o(K!nIN~S*jYt7^u}Sy?j8zu$eBG$7m;RhiAZiIV6 zk09Qv^GCq#@IJZ?k~E4|3}R|IGlf=1Rc6Jp-p4j8CRtX`{52(RiyM2T%(7=k_al8h z`L50{^0WJCP-z9)GjF?&lrvEt&VxIyM9;U_qr!(Pmw{I-X-Xx-FDDSn53$>V(E%j# zDF&=djJVor#LGM`R+#xHLRLyCF=MdS0T9-4SglXQml9MqNFM_#(0?NM-{B3R092^70`?m2@bWfaq(CW(M0n9n6;QyJV1orn11#HCHg3%Kr;xM8HVF~PY)PS53Ut-Qe9ceH_Ph0^RxI{Y`7Cm38mVQ}6)+{LlHYbF3@vB+)mzet+&?TuNbhHlQN9mD9l2 zV`sO4-q2;r7y15U{MT3X*`v~*4?+gLdU%mHR!be9Goq5a+WnST5e$exW{tgKlRUs{AY%xPgc5lVAG*hEbs z7-XdoKo0`MuRW>pNAn> z(KC)<+(2jaQ4eN*>EwYVGhP+D=B)i_rxy~xj=}UQA zdq_QSX@Ota`fKgQb!Tk>ZCVOG&i`F1ELJ`Pa681xm9aZN)BgfCQw&Y9?Gu^fBV#-Y zko%{YqUnZwZS?EyR6r+W3%Vv(E5Mgw*qy*vR^;Qi`atZpad1@~8X~FV0ozn(280;* z99Wt#7m&eLAl-%L)5Bign7}47wOMVsj1G{xcj7fc@tzVSe|8{&W2i0>rA)?Q;)i@r zriOF^1pv~MxBqQB=6`I5z%6a-dH48d$-+rS5Nmbs3wKPpIQGzdOmtz8m!4;FdT(>Tz*aQ(^eWQ&SCQ^dn#La+m)?? zubQv^Z zEXsdP>>Y_)IVBI#kb2Z%+&v4`kP_BnG(*?*Jb76bc(36(UBcdSM3|E%*j-?10bxH@ z?fL;JI7}ds%wacAF#vWy<`^`V3YQ?0xVZJ7*IIfJ30i(~$iB!+! zeke4AyxzY88of9~<=}WO$}lAx`~2B~(9#t0f-@2Ga2~X5x}K2T8b&UtRN-1rw-vvQ ztI%C~wlkFXAkm~f&F&9yfm0_XoQghD#F3dmJ?+q{F_KkzS3oteSw}Pib#uCbPnfZ- zKu_1BZrT6leH&eTa?xrswZMpcdB-fJv~HY;_H=e*RPyL-q-3G6bQgq_6YF?j+yB|o=|Z0! zzq3ZR!zUd53iGL<2TD{i5_9Z63{(%hE&zt7%UhpTU@E zzV zbPO8G(GVRSPAKnmh+KedVD>LM?xXLn5DSeIcz~Uc%d}A1mm(>K-1z=pszoiPm37Ks zSy^q@CcW*T%f5kcbM}EC!1S$vT<3l=ZH@#u9te+ruy*b zO}IdF#hvB!5x?;yTX(wdrE{Y_>g!H6d$bfDHi}{m-Zpoc-DWLr){1kzbhXvA^`d@+ zF1}KK%yU-9ij@37hkN7I2CF5;B$~>`n2tS(YysC8sNG*>r139FVDY{&RC}<>c!A9( zopbUZGGM9C)`*LZF#Y``yamh~Dcfgj+`^`t&OC`@fzlYVH1H3}pH4i9YJt!gwA6oU z$c#%9k!UNwSappEL6HdgC|a;(c!tITB7|Yj7mY{}>FC3FjdE zztbIW10Mf5R+BQqMS;CZE8$8YZ0tmA?4*!Lv?P@X0^mrJT?84?|8GnI0C@jz(ZAOj zlB8}Df~3I+29P&b(nSP4(ZAmW5CE`w8%RR`U$W(aNgR=sME|&4?M>hlJOF_9zXYg5 VlAI$2KxLsx^^p|tb`k$D{|jbP5I+C_ delta 8941 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 W4^WSI5K?Ib^m{x5+B?WUjsFE&=rLme diff --git a/scope.py b/scope.py index 8d12787..157ec2a 100644 --- a/scope.py +++ b/scope.py @@ -1,29 +1,116 @@ import asyncio from streams import SerialStream +from vm import VirtualMachine -class Scope(object): +class Scope(VirtualMachine): + + @classmethod + async def connect(cls, stream=None): + scope = cls(stream if stream is not None else SerialStream()) + await scope.setup() + return scope def __init__(self, stream): - self._stream = stream + super(Scope, self).__init__(stream) - async def reset(self): - await self._stream.write(b'!') - await self._stream.readuntil(b'!') + async def setup(self): + await self.reset() + await self.issue_get_revision() + revision = (await self.read_reply()).decode('ascii') + if revision.startswith('BS0005'): + self.awg_clock_period = 25e-9 + self.awg_wavetable_size = 1024 + self.awg_sample_buffer_size = 1024 + self.awg_minimum_clock = 33 + self.awg_maximum_voltage = 3.3 - async def get_revision(self): - await self._stream.write(b'?') - assert await self._stream.readuntil(b'\r') == b'?\r' - revision = await self._stream.readuntil(b'\r') - return revision.decode('ascii').strip() + async def generate_waveform(self, frequency, waveform='sine', ratio=0.5, vpp=None, offset=0, min_samples=40, max_error=0.0001): + if vpp is None: + vpp = self.awg_maximum_voltage + best_width, best_params = None, None + clock = self.awg_minimum_clock + while True: + width = 1 / frequency / (clock * self.awg_clock_period) + if width <= self.awg_sample_buffer_size: + nwaves = int(self.awg_sample_buffer_size / width) + size = int(round(nwaves * width)) + width = size / nwaves + if width < min_samples: + break + actualf = 1 / (size / nwaves * clock * self.awg_clock_period) + if abs(frequency - actualf) / frequency < max_error and (best_width is None or width > best_width): + best_width, best_params = width, (size, nwaves, clock, actualf) + clock += 1 + if best_params is None: + raise ValueError("Unable to find appropriate solution to required frequency") + size, nwaves, clock, actualf = best_params + async with self.transaction(): + await self.set_registers(vrKitchenSinkB=VirtualMachine.KITCHENSINKB_WAVEFORM_GENERATOR_ENABLE) + await self.issue_configure_device_hardware() + await self.synthesize_wavetable(waveform, ratio) + await self.translate_wavetable(nwaves=nwaves, size=size, level=vpp/self.awg_maximum_voltage, offset=offset/self.awg_maximum_voltage) + await self.start_waveform_generator(clock=clock, modulo=size, mark=10, space=2, rest=0x7f00, option=0x8004) + return actualf + async def stop_generator(self): + await self.stop_waveform_generator() + async with self.transaction(): + await self.set_registers(vrKitchenSinkB=0) + await self.issue_configure_device_hardware() + + async def read_wavetable(self): + with self.transaction(): + self.set_registers(vpAddress=0, vpSize=self.awg_wavetable_size) + self.issue_wavetable_read() + return list(self.read_exactly(self.awg_wavetable_size)) + + async def write_wavetable(self, data): + if len(data) != self.awg_wavetable_size: + raise ValueError("Wavetable data must be {} samples".format(self.awg_wavetable_size)) + with self.transaction(): + self.set_registers(vpAddress=0, vpSize=1) + for byte in data: + self.wavetable_write(byte) + + async def synthesize_wavetable(self, waveform='sine', ratio=0.5): + mode = {'sine': 0, 'sawtooth': 1, 'exponential': 2, 'square': 3}[waveform.lower()] + async with self.transaction(): + await self.set_registers(vpCmd=0, vpMode=mode, vpRatio=ratio) + await self.issue_synthesize_wavetable() + + async def translate_wavetable(self, nwaves, size, level=1, offset=0, index=0, address=0): + async with self.transaction(): + await self.set_registers(vpCmd=0, vpMode=0, vpLevel=level, vpOffset=offset, + vpRatio=nwaves * self.awg_wavetable_size / size, + vpIndex=index, vpAddress=address, vpSize=size) + await self.issue_translate_wavetable() + + async def start_waveform_generator(self, clock, modulo, mark, space, rest, option): + async with self.transaction(): + await self.set_registers(vpCmd=2, vpMode=0, vpClock=clock, vpModulo=modulo, + vpMark=mark, vpSpace=space, vrRest=rest, vpOption=option) + await self.issue_control_waveform_generator() + + async def read_eeprom(self, address): + async with self.transaction(): + await self.set_registers(vrEepromAddress=address) + await self.issue_read_eeprom() + return int(await self.read_reply(), 16) + + async def write_eeprom(self, address, byte): + async with self.transaction(): + await self.set_registers(vrEepromAddress=address, vrEepromData=byte) + await self.issue_write_eeprom() + return int(await self.read_reply(), 16) async def main(): - s = Scope(SerialStream()) - await s.reset() - print(await s.get_revision()) + global s + s = await Scope.connect() + print(await s.generate_waveform(440*16, 'sawtooth')) + if __name__ == '__main__': asyncio.get_event_loop().run_until_complete(main()) diff --git a/streams.py b/streams.py index 8f60ec1..76f84c3 100644 --- a/streams.py +++ b/streams.py @@ -3,6 +3,7 @@ import asyncio import os import serial import serial.tools.list_ports +import time class SerialStream: @@ -36,7 +37,7 @@ class SerialStream: def _feed_data(self, data, future): n = self._connection.write(data) - print(''.format(repr(data[:n]))) + print('{:.3f} -> {}'.format(time.time(), repr(data[:n]))) future.set_result(n) self._loop.remove_writer(self._connection) @@ -76,7 +77,7 @@ class SerialStream: def _handle_data(self, n, future): data = self._connection.read(n if n is not None else self._connection.in_waiting) - print(''.format(repr(data))) + print('{:.3f} <- {}'.format(time.time(), repr(data))) future.set_result(data) self._loop.remove_reader(self._connection) diff --git a/vm.py b/vm.py index 3ecec3b..414ae44 100644 --- a/vm.py +++ b/vm.py @@ -23,75 +23,130 @@ class VirtualMachine: return False Registers = { - "vrTriggerLogic": (1, 0x05, '''Trigger Logic, one bit per channel (0 => Low, 1 => High)''', 'uint'), - "vrTriggerMask": (1, 0x06, '''Trigger Mask, one bit per channel (0 => Don’t Care, 1 => Active)''', 'uint'), - "vrSpockOption": (1, 0x07, '''Spock Option Register (see bit definition table for details)''', 'uint'), - "vrSampleAddress": (3, 0x08, '''Sample address (write) 24 bit''', 'uint'), - "vrSampleCounter": (3, 0x0b, '''Sample address (read) 24 bit''', 'uint'), - "vrTriggerIntro": (2, 0x32, '''Edge trigger intro filter counter (samples/2)''', 'uint'), - "vrTriggerOutro": (2, 0x34, '''Edge trigger outro filter counter (samples/2)''', 'uint'), - "vrTriggerValue": (2, 0x44, '''Digital (comparator) trigger (signed)''', 'int'), - "vrTriggerTime": (4, 0x40, '''Stopwatch trigger time (ticks)''', 'uint'), - "vrClockTicks": (2, 0x2e, '''Master Sample (clock) period (ticks)''', 'uint'), - "vrClockScale": (2, 0x14, '''Clock divide by N (low byte)''', 'uint'), - "vrTraceOption": (1, 0x20, '''Trace Mode Option bits''', 'uint'), - "vrTraceMode": (1, 0x21, '''Trace Mode (see Trace Mode Table)''', 'uint'), - "vrTraceIntro": (2, 0x26, '''Pre-trigger capture count (samples)''', 'uint'), - "vrTraceDelay": (4, 0x22, '''Delay period (uS)''', 'uint'), - "vrTraceOutro": (2, 0x2a, '''Post-trigger capture count (samples)''', 'uint'), - "vrTimeout": (2, 0x2c, '''Auto trace timeout (auto-ticks)''', 'uint'), - "vrPrelude": (2, 0x3a, '''Buffer prefill value''', 'uint'), - "vrBufferMode": (1, 0x31, '''Buffer mode''', 'uint'), - "vrDumpMode": (1, 0x1e, '''Dump mode''', 'uint'), - "vrDumpChan": (1, 0x30, '''Dump (buffer) Channel (0..127,128..254,255)''', 'uint'), - "vrDumpSend": (2, 0x18, '''Dump send (samples)''', 'uint'), - "vrDumpSkip": (2, 0x1a, '''Dump skip (samples)''', 'uint'), - "vrDumpCount": (2, 0x1c, '''Dump size (samples)''', 'uint'), - "vrDumpRepeat": (2, 0x16, '''Dump repeat (iterations)''', 'uint'), - "vrStreamIdent": (1, 0x36, '''Stream data token''', 'uint'), - "vrStampIdent": (1, 0x3c, '''Timestamp token''', 'uint'), - "vrAnalogEnable": (1, 0x37, '''Analog channel enable (bitmap)''', 'uint'), - "vrDigitalEnable": (1, 0x38, '''Digital channel enable (bitmap)''', 'uint'), - "vrSnoopEnable": (1, 0x39, '''Frequency (snoop) channel enable (bitmap)''', 'uint'), - "vpCmd": (1, 0x46, '''Command Vector''', 'uint'), - "vpMode": (1, 0x47, '''Operation Mode (per command)''', 'uint'), - "vpOption": (2, 0x48, '''Command Option (bits fields per command)''', 'uint'), - "vpSize": (2, 0x4a, '''Operation (unit/block) size''', 'uint'), - "vpIndex": (2, 0x4c, '''Operation index (eg, P Memory Page)''', 'uint'), - "vpAddress": (2, 0x4e, '''General purpose address''', 'uint'), - "vpClock": (2, 0x50, '''Sample (clock) period (ticks)''', 'uint'), - "vpModulo": (2, 0x52, '''Modulo Size (generic)''', 'uint'), - "vpLevel": (2, 0x54, '''Output (analog) attenuation (unsigned)''', 'uint'), - "vpOffset": (2, 0x56, '''Output (analog) offset (signed)''', 'int'), - "vpMask": (2, 0x58, '''Translate source modulo mask''', 'uint'), - "vpRatio": (4, 0x5a, '''Translate command ratio (phase step)''', 'uint'), - "vpMark": (2, 0x5e, '''Mark count/phase (ticks/step)''', 'uint'), - "vpSpace": (2, 0x60, '''Space count/phase (ticks/step)''', 'uint'), - "vpRise": (2, 0x82, '''Rising edge clock (channel 1) phase (ticks)''', 'uint'), - "vpFall": (2, 0x84, '''Falling edge clock (channel 1) phase (ticks)''', 'uint'), - "vpControl": (1, 0x86, '''Clock Control Register (channel 1)''', 'uint'), - "vpRise2": (2, 0x88, '''Rising edge clock (channel 2) phase (ticks)''', 'uint'), - "vpFall2": (2, 0x8a, '''Falling edge clock (channel 2) phase (ticks)''', 'uint'), - "vpControl2": (1, 0x8c, '''Clock Control Register (channel 2)''', 'uint'), - "vpRise3": (2, 0x8e, '''Rising edge clock (channel 3) phase (ticks)''', 'uint'), - "vpFall3": (2, 0x90, '''Falling edge clock (channel 3) phase (ticks)''', 'uint'), - "vpControl3": (1, 0x92, '''Clock Control Register (channel 3)''', 'uint'), - "vrEepromData": (1, 0x10, '''EE Data Register''', 'uint'), - "vrEepromAddress": (1, 0x11, '''EE Address Register''', 'uint'), - "vrConverterLo": (2, 0x64, '''VRB ADC Range Bottom (D Trace Mode)''', 'uint'), - "vrConverterHi": (2, 0x66, '''VRB ADC Range Top (D Trace Mode)''', 'uint'), - "vrTriggerLevel": (2, 0x68, '''Trigger Level (comparator, unsigned)''', 'uint'), - "vrLogicControl": (1, 0x74, '''Logic Control''', 'uint'), - "vrRest": (2, 0x78, '''DAC (rest) level''', 'uint'), - "vrKitchenSinkA": (1, 0x7b, '''Kitchen Sink Register A''', 'uint'), - "vrKitchenSinkB": (1, 0x7c, '''Kitchen Sink Register B''', 'uint'), + "vrTriggerLogic": (0x05, 'U8', "Trigger Logic, one bit per channel (0 => Low, 1 => High)"), + "vrTriggerMask": (0x06, 'U8', "Trigger Mask, one bit per channel (0 => Don’t Care, 1 => Active)"), + "vrSpockOption": (0x07, 'U8', "Spock Option Register (see bit definition table for details)"), + "vrSampleAddress": (0x08, 'U24', "Sample address (write) 24 bit"), + "vrSampleCounter": (0x0b, 'U24', "Sample address (read) 24 bit"), + "vrTriggerIntro": (0x32, 'U24', "Edge trigger intro filter counter (samples/2)"), + "vrTriggerOutro": (0x34, 'U16', "Edge trigger outro filter counter (samples/2)"), + "vrTriggerValue": (0x44, 'S16', "Digital (comparator) trigger (signed)"), + "vrTriggerTime": (0x40, 'U32', "Stopwatch trigger time (ticks)"), + "vrClockTicks": (0x2e, 'U16', "Master Sample (clock) period (ticks)"), + "vrClockScale": (0x14, 'U16', "Clock divide by N (low byte)"), + "vrTraceOption": (0x20, 'U8', "Trace Mode Option bits"), + "vrTraceMode": (0x21, 'U8', "Trace Mode (see Trace Mode Table)"), + "vrTraceIntro": (0x26, 'U16', "Pre-trigger capture count (samples)"), + "vrTraceDelay": (0x22, 'U32', "Delay period (uS)"), + "vrTraceOutro": (0x2a, 'U16', "Post-trigger capture count (samples)"), + "vrTimeout": (0x2c, 'U16', "Auto trace timeout (auto-ticks)"), + "vrPrelude": (0x3a, 'U16', "Buffer prefill value"), + "vrBufferMode": (0x31, 'U8', "Buffer mode"), + "vrDumpMode": (0x1e, 'U8', "Dump mode"), + "vrDumpChan": (0x30, 'U8', "Dump (buffer) Channel (0..127,128..254,255)"), + "vrDumpSend": (0x18, 'U16', "Dump send (samples)"), + "vrDumpSkip": (0x1a, 'U16', "Dump skip (samples)"), + "vrDumpCount": (0x1c, 'U16', "Dump size (samples)"), + "vrDumpRepeat": (0x16, 'U16', "Dump repeat (iterations)"), + "vrStreamIdent": (0x36, 'U8', "Stream data token"), + "vrStampIdent": (0x3c, 'U8', "Timestamp token"), + "vrAnalogEnable": (0x37, 'U8', "Analog channel enable (bitmap)"), + "vrDigitalEnable": (0x38, 'U8', "Digital channel enable (bitmap)"), + "vrSnoopEnable": (0x39, 'U8', "Frequency (snoop) channel enable (bitmap)"), + "vpCmd": (0x46, 'U8', "Command Vector"), + "vpMode": (0x47, 'U8', "Operation Mode (per command)"), + "vpOption": (0x48, 'U16', "Command Option (bits fields per command)"), + "vpSize": (0x4a, 'U16', "Operation (unit/block) size"), + "vpIndex": (0x4c, 'U16', "Operation index (eg, P Memory Page)"), + "vpAddress": (0x4e, 'U16', "General purpose address"), + "vpClock": (0x50, 'U16', "Sample (clock) period (ticks)"), + "vpModulo": (0x52, 'U16', "Modulo Size (generic)"), + "vpLevel": (0x54, 'U0.16', "Output (analog) attenuation (unsigned)"), + "vpOffset": (0x56, 'S1.15', "Output (analog) offset (signed)"), + "vpMask": (0x58, 'U16', "Translate source modulo mask"), + "vpRatio": (0x5a, 'U16.16', "Translate command ratio (phase step)"), + "vpMark": (0x5e, 'U16', "Mark count/phase (ticks/step)"), + "vpSpace": (0x60, 'U16', "Space count/phase (ticks/step)"), + "vpRise": (0x82, 'U16', "Rising edge clock (channel 1) phase (ticks)"), + "vpFall": (0x84, 'U16', "Falling edge clock (channel 1) phase (ticks)"), + "vpControl": (0x86, 'U8', "Clock Control Register (channel 1)"), + "vpRise2": (0x88, 'U16', "Rising edge clock (channel 2) phase (ticks)"), + "vpFall2": (0x8a, 'U16', "Falling edge clock (channel 2) phase (ticks)"), + "vpControl2": (0x8c, 'U8', "Clock Control Register (channel 2)"), + "vpRise3": (0x8e, 'U16', "Rising edge clock (channel 3) phase (ticks)"), + "vpFall3": (0x90, 'U16', "Falling edge clock (channel 3) phase (ticks)"), + "vpControl3": (0x92, 'U8', "Clock Control Register (channel 3)"), + "vrEepromData": (0x10, 'U8', "EE Data Register"), + "vrEepromAddress": (0x11, 'U8', "EE Address Register"), + "vrConverterLo": (0x64, 'U16', "VRB ADC Range Bottom (D Trace Mode)"), + "vrConverterHi": (0x66, 'U16', "VRB ADC Range Top (D Trace Mode)"), + "vrTriggerLevel": (0x68, 'U16', "Trigger Level (comparator, unsigned)"), + "vrLogicControl": (0x74, 'U8', "Logic Control"), + "vrRest": (0x78, 'U16', "DAC (rest) level"), + "vrKitchenSinkA": (0x7b, 'U8', "Kitchen Sink Register A"), + "vrKitchenSinkB": (0x7c, 'U8', "Kitchen Sink Register B"), + "vpMap0": (0x94, 'U8', "Peripheral Pin Select Channel 0"), + "vpMap1": (0x95, 'U8', "Peripheral Pin Select Channel 1"), + "vpMap2": (0x96, 'U8', "Peripheral Pin Select Channel 2"), + "vpMap3": (0x97, 'U8', "Peripheral Pin Select Channel 3"), + "vpMap4": (0x98, 'U8', "Peripheral Pin Select Channel 4"), + "vpMap5": (0x99, 'U8', "Peripheral Pin Select Channel 5"), + "vpMap6": (0x9a, 'U8', "Peripheral Pin Select Channel 6"), + "vpMap7": (0x9b, 'U8', "Peripheral Pin Select Channel 7"), + "vrMasterClockN": (0xf7, 'U8', "PLL prescale (DIV N)"), + "vrMasterClockM": (0xf8, 'U16', "PLL multiplier (MUL M)"), + "vrLedLevelRED": (0xfa, 'U8', "Red LED Intensity (VM10 only)"), + "vrLedLevelGRN": (0xfb, 'U8', "Green LED Intensity (VM10 only)"), + "vrLedLevelYEL": (0xfc, 'U8', "Yellow LED Intensity (VM10 only)"), + "vcBaudHost": (0xfe, 'U16', "baud rate (host side)"), } + TraceModes = { + "tmAnalog": 0, + "tmAnalogFast": 4, + "tmAnalogShot": 11, + "tmMixed": 1, + "tmMixedFast": 5, + "tmMixedShot": 12, + "tmLogic": 14, + "tmLogicFast": 15, + "tmLogicShot": 13, + "tmAnalogChop": 2, + "tmAnalogFastChop": 6, + "tmAnalogShotChop": 16, + "tmMixedChop": 3, + "tmMixedFastChop": 7, + "tmMixedShotChop": 17, + "tmMacro": 18, + "tmMacroChop": 19, + } + + BufferModes = { + "bmSingle": 0, + "bmChop": 1, + "bmDual": 2, + "bmChopDual": 3, + "bmMacro": 4, + "bmMacroChop": 5, + } + + SPOCKOPTION_TRIGGER_INVERT = 0x40 + SPOCKOPTION_TRIGGER_SOURCE_A = 0x00 + SPOCKOPTION_TRIGGER_SOURCE_B = 0x04 + SPOCKOPTION_TRIGGER_SWAP = 0x02 + SPOCKOPTION_TRIGGER_TYPE_SAMPLED_ANALOG = 0x00 + SPOCKOPTION_TRIGGER_TYPE_HARDWARE = 0x01 + + KITCHENSINKA_CHANNEL_A_COMPARATOR_ENABLE = 0x80 + KITCHENSINKA_CHANNEL_B_COMPARATOR_ENABLE = 0x40 + KITCHENSINKB_ANALOG_FILTER_ENABLE = 0x80 + KITCHENSINKB_WAVEFORM_GENERATOR_ENABLE = 0x40 + def __init__(self, stream): self._stream = stream self._transactions = [] - def new_transaction(self): + def transaction(self): return self.Transaction(self) async def issue(self, cmd): @@ -107,6 +162,8 @@ class VirtualMachine: return (await self.read_replies(1))[0] async def read_replies(self, n): + if self._transactions: + raise TypeError("Command transaction in progress") await self._stream.readuntil(b'\r') replies = [] for i in range(n): @@ -114,139 +171,130 @@ class VirtualMachine: return replies async def reset(self): + if self._transactions: + raise TypeError("Command transaction in progress") await self._stream.write(b'!') await self._stream.readuntil(b'!') - async def set_register(self, name, value): - width, base, desc, dtype = self.Registers[name] - bs = struct.pack(' r1 + 3: + cmd += '{:02x}@'.format(address) + r0 = r1 = address + else: + cmd += 'n' * (address - r1) + r1 = address + if byte != r0: + cmd += '[' if byte == 0 else '{:02x}'.format(byte) + r0 = byte + if cmd: + await self.issue(cmd + 's') async def get_register(self, name): - width, base, desc, dtype = self.Registers[name] + base, dtype, desc = self.Registers[name] await self.issue('{:02x}@p'.format(base)) - bs = [] + values = [] + width = self.calculate_width(dtype) for i in range(width): - bs.append(int(await self.read_reply(), 16)) + values.append(int(await self.read_reply(), 16)) if i < width-1: await self.issue(b'np') - for i in range(4 - width): - bs.append(0) - value = struct.unpack('') - async def configure_device_hardware(self): + async def issue_configure_device_hardware(self): await self.issue(b'U') - async def streaming_trace(self): + async def issue_streaming_trace(self): await self.issue(b'T') - async def triggered_trace(self): + async def issue_triggered_trace(self): await self.issue(b'D') - async def cancel_trace(self): + async def issue_cancel_trace(self): await self.issue(b'K') - async def sample_dump_csv(self): + async def issue_sample_dump_csv(self): await self.issue(b'S') - async def analog_dump_binary(self): + async def issue_analog_dump_binary(self): await self.issue(b'S') - async def read_wavetable(self, size=1024, address=0): - async with self.new_transaction(): - await self.set_registers(vpSize=size, vpAddress=address) - await self.issue(b'R') - return await self._stream.readexactly(size) + async def issue_wavetable_read(self): + await self.issue(b'R') - async def write_wavetable(self, data, address=0): - async with self.new_transaction(): - await self.set_registers(vpSize=1, vpAddress=address) - for byte in data: - await self.issue('{:02x}W'.format(byte)) + async def wavetable_write(self, byte): + await self.issue('{:02x}W'.format(byte)) - async def synthesize_wavetable(self, mode='sine', ratio=0.5): - mode = {'sine': 0, 'sawtooth': 1, 'exponential': 2, 'square': 3}[mode.lower()] - async with self.new_transaction(): - await self.set_registers(vpCmd=0, vpMode=mode, vpRatio=int(max(0, min(ratio, 1))*65535)) - await self.issue(b'Y') + async def issue_synthesize_wavetable(self): + await self.issue(b'Y') - async def translate_wavetable(self, ratio, level=1, offset=0, size=0, index=0, address=0): - async with self.new_transaction(): - await self.set_registers(vpCmd=0, vpMode=0, vpLevel=int(65535*level), vpOffset=int(65535*offset), vpRatio=ratio, - vpSize=size, vpIndex=index, vpAddress=address) - await self.issue(b'X') + async def issue_translate_wavetable(self): + await self.issue(b'X') - async def stop_waveform_generator(self): - async with self.new_transaction(): - await self.set_registers(vpCmd=1, vpMode=0) - await self.issue(b'Z') + async def issue_control_waveform_generator(self): + await self.issue(b'Z') - async def start_waveform_generator(self, clock, modulo, mark, space, rest, option): - async with self.new_transaction(): - await self.set_registers(vpCmd=2, vpMode=0, vpClock=clock, vpModulo=modulo, - vpMark=mark, vpSpace=space, vrRest=rest, vpOption=option) - await self.issue(b'Z') + async def issue_read_eeprom(self): + await self.issue(b'r') - async def start_clock_generator(self): - async with self.new_transaction(): - await self.set_registers(vpCmd=3, vpMode=0) - await self.issue(b'Z') - - async def read_eeprom(self, address): - async with self.new_transaction(): - await self.set_registers(vrEepromAddress=address) - await self.issue(b'r') - return int(await self.read_reply(), 16) - - async def write_eeprom(self, address, data): - async with self.new_transaction(): - await self.set_registers(vrEepromAddress=address, vrEepromData=data) - await self.issue(b'w') - return int(await self.read_reply(), 16) + async def issue_write_eeprom(self): + await self.issue(b'w') -async def main(): - from streams import SerialStream - vm = VirtualMachine(SerialStream()) - await vm.reset() - print(await vm.get_revision()) - print(await vm.get_register('vrConverterLo')) - print(await vm.get_register('vrTriggerLevel')) - print(await vm.get_register('vrConverterHi')) - await vm.set_register('vrTriggerLevel', 15000) - print(await vm.get_register('vrTriggerLevel')) - n = await vm.read_eeprom(0) - print(n) - print(await vm.write_eeprom(0, n+1)) - print(await vm.read_eeprom(0)) - async with vm.new_transaction(): - await vm.set_registers(vrKitchenSinkB=0x40) - await vm.configure_device_hardware() - await vm.synthesize_wavetable('sawtooth') - #data = await vm.read_wavetable() - #global array - #array = np.ndarray(buffer=data, shape=(len(data),), dtype='uint8') - #print(array) - await vm.translate_wavetable(671088) - await vm.start_waveform_generator(clock=40, modulo=1000, mark=10, space=1, rest=0x7f00, option=0x8004) - -if __name__ == '__main__': - asyncio.get_event_loop().run_until_complete(main()) -