123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652(* API representing basic Xilinx primitives *)open!Importopen!Xilinx_intfmoduletypeS=S(* algebra for building LUT equations *)moduleLutEqn=structtypet=|Gnd|Vdd|Inputofint64|Andoft*t|Oroft*t|Xoroft*t|Notoftleti0=Input0Lleti1=Input1Lleti2=Input2Lleti3=Input3Lleti4=Input4Lleti5=Input5Lletgnd=Gndletvdd=Vddlet(&:)ab=And(a,b)let(|:)ab=Or(a,b)let(^:)ab=Xor(a,b)let(~:)a=Notalet(<>:)ab=a^:blet(==:)ab=~:(a<>:b)let(>>.)ab=Int64.shift_right_logicala(Int64.to_int_exnb)let(<<.)ab=Int64.shift_lefta(Int64.to_int_exnb)let(&.)=Int64.logandlet(|.)=Int64.logorlet(^.)=Int64.logxorlet(~.)=Int64.lognotlet(+.)=Int64.addletevalnv=letn=Int64.of_intninletrecevaln=function|Gnd->0L|Vdd->1L|Inputa->n>>.a&.1L|And(a,b)->evalna&.evalnb|Or(a,b)->evalna|.evalnb|Xor(a,b)->evalna^.evalnb|Nota->~.(evalna)&.1Linletrecevalnmw=ifInt64.equalm(1L<<.n)thenwelseevaln(m+.1L)(evalmv<<.m|.w)inevaln0L0L;;end(* Hardcaml implementation of Xilinx API *)moduleHardcaml_api=structopenSignalletlutvsel=letn=1lslwidthselinletrecbuildi=ifi=nthen[]else(letd=ifnot(Int64.equal(Int64.logandv(Int64.shift_left1Li))0L)thenvddelsegndind::build(i+1))inmuxsel(build0);;letmuxcycidisel=mux2selcidiletinva=~:aletxorcycili=ci^:liletmuxf5fts=mux2stfletmuxf6fts=mux2stfletmuxf7fts=mux2stfletmuxf8fts=mux2stfletfdcecceclrd=reg(Reg_spec.override(Reg_spec.create()~clock:c)~reset:clr~reset_to:gnd)~enable:ced;;letfdpeccepred=reg(Reg_spec.override(Reg_spec.create()~clock:c)~reset:pre~reset_to:vdd)~enable:ced;;letmult_andab=a&:bletram1sadclkwe=memory(1lslwidtha)~write_port:{write_clock=clk;write_enable=we;write_address=a;write_data=d}~read_address:a;;end(* unisim based implementation of Xilinx API *)moduleUnisim=structopenSignalletinva=(Instantiation.create()~name:"INV"~inputs:["I",a]~outputs:["O",1])#o"O";;letlutvsel=letw=widthselinletw'=Int.to_stringwinletinit=Int64.to_stringv|>Bits.of_decimal_string~width:(1lslw)|>Bits.reverse|>Bits.to_stringin(Instantiation.create()~name:("LUT"^w')~parameters:[Parameter.create~name:"INIT"~value:(Stringinit)]~inputs:(List.mapi(bits_lsbsel)~f:(funib->"I"^Int.to_stringi,b))~outputs:["O",1])#o"O";;letmuxcycidisel=(Instantiation.create()~name:"MUXCY"~inputs:["CI",ci;"DI",di;"S",sel]~outputs:["O",1])#o"O";;letxorcycili=(Instantiation.create()~name:"XORCY"~inputs:["CI",ci;"LI",li]~outputs:["O",1])#o"O";;letmuxf5fts=(Instantiation.create()~name:"MUXF5"~inputs:["I0",f;"I1",t;"S",s]~outputs:["O",1])#o"O";;letmuxf6fts=(Instantiation.create()~name:"MUXF6"~inputs:["I0",f;"I1",t;"S",s]~outputs:["O",1])#o"O";;letmuxf7fts=(Instantiation.create()~name:"MUXF7"~inputs:["I0",f;"I1",t;"S",s]~outputs:["O",1])#o"O";;letmuxf8fts=(Instantiation.create()~name:"MUXF8"~inputs:["I0",f;"I1",t;"S",s]~outputs:["O",1])#o"O";;letfdcecceclrd=(Instantiation.create()~name:"FDCE"~parameters:[Parameter.create~name:"INIT"~value:(String"0")]~inputs:["C",c;"CE",ce;"CLR",clr;"D",d]~outputs:["Q",1])#o"Q";;letfdpeccepred=(Instantiation.create()~name:"FDPE"~parameters:[Parameter.create~name:"INIT"~value:(String"1")]~inputs:["C",c;"CE",ce;"D",d;"PRE",pre]~outputs:["Q",1])#o"Q";;letmult_andab=(Instantiation.create()~name:"MULT_AND"~inputs:["I0",a;"I1",b]~outputs:["LO",1])#o"LO";;letram1sadclkwe=letwidth=widthainletsize=1lslwidthinleta=List.mapi(bits_lsba)~f:(funis->"A"^Int.to_stringi,s)in(Instantiation.create()~name:("RAM"^Int.to_stringsize^"X1S")~parameters:[Parameter.create~name:"INIT"~value:(String(Constant.of_int~width:size0|>Constant.to_binary_string))]~inputs:(["D",d;"WE",we;"WCLK",clk]@a)~outputs:["Q",1])#o"Q";;endmoduletypeT=TwithmoduleLutEqn:=LutEqn(* max lut size. *)moduletypeLutSize=sigvalmax_lut:intendmoduleLut4=structletmax_lut=4endmoduleLut6=structletmax_lut=6end(* Build API of Xilinx primitives *)moduleXMake(X:S)(L:LutSize)=structmoduleS=SignalopenSopenLutEqnopenLletx_lutfs=leti=eval(widths)finX.lutis;;letx_mapfl=letw=List.hd_exnl|>widthinletlutn=x_lutf(List.mapl~f:(funs->selectsnn)|>concat_lsb)inletrecbuildn=ifn=wthen[]elselutn::build(n+1)inconcat_lsb(build0);;letx_andab=x_map(i0&:i1)[a;b]letx_orab=x_map(i0|:i1)[a;b]letx_xorab=x_map(i0^:i1)[a;b]letx_nota=bits_msba|>List.map~f:X.inv|>concat_msbletinputsn=List.take[i0;i1;i2;i3;i4;i5]nletreduce_inputsopn=letargs=inputsninList.reduce_exnargs~f:(funacca->opacca);;letrecx_reduce_carryinvopmux_dincarry_ina=letn=minmax_lut(widtha)inletop'=reduce_inputsopninletop'=ifinvthen~:op'elseop'inletlut=x_lutop'(selecta(n-1)0)inletcarry_out=X.muxcycarry_inmux_dinlutinifn=widthathencarry_outelsex_reduce_carryinvopmux_dincarry_out(selecta(widtha-1)n);;letx_and_reducea=x_reduce_carryfalse(&:)S.gndS.vddaletx_or_reducea=x_reduce_carrytrue(|:)S.vddS.gndaletrecx_reduce_treeopa=letreclevela=letn=minmax_lut(widtha)inletop'=reduce_inputsopninletlut=x_lutop'(selecta(n-1)0)inifn=widthathenlutelselevel(selecta(widtha-1)n)@:lutinifwidtha=1thenaelsex_reduce_treeop(levela);;letx_add_carryopcab=letlutcab=leto=x_lutop(a@:b)inlets=X.xorcycoinletc=X.muxcycboinc,sinletr,c=List.fold2_exn(bits_lsba)(bits_lsbb)~init:([],c)~f:(fun(r,c)ab->letc,s=lutcabins::r,c)inc,concat_msbr;;letx_addab=snd(x_add_carry(i0^:i1)S.gndab)letx_subab=snd(x_add_carry~:(i0^:i1)S.vddba)letx_mux_add_carryopcx(a,a')b=letlutopxc(a,a')b=leto=x_lutop(x@:b@:a'@:a)inlets=X.xorcycoinletc=X.muxcycboinc,sinletzipab=List.map2_exnab~f:(funab->a,b)inletr,c=List.fold2_exn(zip(bits_lsba)(bits_lsba'))(bits_lsbb)~init:([],c)~f:(fun(r,c)(a,a')b->letc,s=lutopxc(a,a')bins::r,c)inc,concat_msbr;;letx_mux_addx(a,a')b=letadd_lut_op=(i0&:i3|:(i1&:~:i3))^:i2insnd(x_mux_add_carryadd_lut_opS.gndx(a,a')b);;letx_mux_subxa(b,b')=letsub_lut_op=~:((i0&:i3|:(i1&:~:i3))^:i2)insnd(x_mux_add_carrysub_lut_opS.vddx(b,b')a);;letx_eqab=letreceql=matchlwith|[]->[]|a::b::t->~:(a^:b)::eqt|_->failwith"x_eq expecting even length list"inleteql=List.fold(eql)~init:vdd~f:(&:)inleteq_lutab=matchwidthawith|1->x_lut(eq[i0;i1])(b@:a)|2->x_lut(eq[i0;i2;i1;i3])(b@:a)|3->x_lut(eq[i0;i3;i1;i4;i2;i5])(b@:a)|_->failwith"x_eq invalid signal width"inletsize=max_lut/2inletrecmkab=assert(widtha=widthb);ifwidtha<=sizethen[eq_lutab]elseeq_lut(selecta(size-1)0)(selectb(size-1)0)::mk(selecta(widtha-1)size)(selectb(widthb-1)size)inletc=mkabinList.foldc~init:S.vdd~f:(funcinc->X.muxcycinS.gndc);;letx_ltab=fst(x_add_carry~:(i0^:i1)S.vddba)(* muxes - Lut6 version doesnt work... *)(* basic lut4/6 structures *)letx_lut4_mux2seld0d1=x_lut(~:i0&:i1|:(i0&:i2))(d1@:d0@:sel)letx_lut6_mux4seld0d1d2d3=x_lut(~:i1&:~:i0&:i2|:(~:i1&:i0&:i3)|:(i1&:~:i0&:i4)|:(i1&:i0&:i5))(d3@:d2@:d1@:d0@:sel);;letsplitnd=letrecfmdl=ifn=mthenList.revl,delse(matchdwith|[]->List.revl,[]|h::t->f(m+1)t(h::l))inf0d[];;letx_mux_2sddef=matchdwith|[]->def|[d]->x_lut4_mux2sddef|[d0;d1]->x_lut4_mux2sd0d1|_->failwith"x_mux2";;letx_mux_4sddef=matchdwith|[]->def|[d]->x_lut4_mux2sddef|[d0;d1]->x_lut4_mux2sd0d1|[d0;d1;d2]->x_lut6_mux4sd0d1d2def|[d0;d1;d2;d3]->x_lut6_mux4sd0d1d2d3|_->failwith"x_mux4";;letrecx_mux_nnmfsddef=ifn<=4&&max_lut>=6thenx_mux_4sddefelseifn<=2thenx_mux_2sddefelse(leta,b=split(n/2)din(List.hd_exnmf)(msbs)(x_mux_n(n/2)(List.tl_exnmf)(lsbss)adef)(x_mux_n(n/2)(List.tl_exnmf)(lsbss)bdef));;letmuxfnn=letfmsd0d1=ifUid.equal(uidd0)(uidd1)thend0elsemd0d1sinletd=matchnwith|5->assertfalse|4->[X.muxf8;X.muxf7;X.muxf6;X.muxf5]|3->[X.muxf7;X.muxf6;X.muxf5]|2->[X.muxf6;X.muxf5]|1->[X.muxf5]|_->[]inList.mapd~f;;(* This assumes that all arch's have muxf5/6/7/8, but they dont. V5 seems to only have
muxf7/8 ??? *)letx_mux_bitsd=letl_max,l_off=ifmax_lut>=6then6,2else5,1inletdef=List.hd_exn(List.revd)inletrecbuildsd=letl=widthsinletl=minl_maxlinletn=1lsllinletmuxfn=muxfn(l-l_off)inletrecbuild2sd=matchdwith|[]->[]|_->leta,b=split(1lsll)dinx_mux_nnmuxfnsadef::build2sbinletd=build2(selects(l-1)0)dinifl=widthsthenList.hd_exndelsebuild(selects(widths-1)l)dinbuildsd;;letx_muxsd=letw=width(List.hd_exnd)inletrecmux_bitsi=ifi=wthen[]else(letd=List.mapd~f:(funs->bitsi)inx_mux_bitsd::mux_bits(i+1))inmux_bits0|>List.rev|>Signal.concat_msb;;(* multiplier *)letx_mulsignab=letout_width=widtha+widthbinletexa=ifsignthenmsbaelseS.gndinletx_mul_luta0a1b0b1carry=leto=x_lut((i0&:i1)^:(i2&:i3))(b0@:a1@:b1@:a0)inleta=X.mult_anda0b1inletc=X.muxcycarryaoinlets=X.xorcycarryoinc,sinletx_mul_2ab=leta1=concat_msb[exa;exa;a]|>bits_lsbinleta0=concat_msb[exa;a;S.gnd]|>bits_lsbinletrecbuilda0a1b0b1c=matcha0,a1with|[],[]->[]|[a0],[a1]->[snd(x_mul_luta0a1b0b1c)]|a0::a0t,a1::a1t->letc,s=x_mul_luta0a1b0b1cins::builda0ta1tb0b1c|_->failwith"x_mul_2"inbuilda0a1(bitb0)(bitb1)S.gnd|>concat_lsbinletx_mul_1ab=leta=concat_msb[exa;exa;a]inx_anda(repeatb(widtha))inletrecbuild_productsiab=matchwidthbwith|1->[i,x_mul_1ab]|2->[i,x_mul_2a(selectb10)]|_->(i,x_mul_2a(selectb10))::build_products(i+2)a(msbs(msbsb))inletrecadder_treepp=letrecadder'levelpp=matchppwith|[]->[]|[(i,p)]->[i,p@:zerolevel]|(_,p0)::(i1,p1)::tl->(i1,x_add(repeat(exp0)level@:p0)(p1@:zerolevel))::adder'leveltlinmatchppwith|[]->failwith"adder_tree"|[a]->a|(i0,_)::(i1,_)::_->adder_tree(adder'(i1-i0)pp)inselect(snd(adder_tree(build_products0ab)))(out_width-1)0;;letx_muluab=x_mulfalseabletx_mulsab=(* note; use x_mux_sub below instead *)matchwidthbwith|0->failwith"x_muls 'b' is empty"|1->letz=zero(widtha+widthb)inx_muxb[z;x_subz(msba@:a)]|_->letm=x_multruea(lsbsb)inx_sub(msbm@:m)(x_mux(msbb)[zero(widtha+widthb);msba@:a@:zero(widthb-1)]);;end(* Generate full Comb.S API for Xilinx primitives *)moduleXComb(Synth:T)=structtypet=Signal.tinclude(Signal:Comb.Primitiveswithtypet:=t)let(&:)=Synth.x_andlet(|:)=Synth.x_orlet(^:)=Synth.x_xorlet(~:)=Synth.x_notlet(+:)=Synth.x_addlet(-:)=Synth.x_sublet(==:)=Synth.x_eqlet(<:)=Synth.x_ltlet(*:)=Synth.x_mululet(*+)=Synth.x_mulsletmux=Synth.x_muxendmoduleXSynthesizeComb(X:S)(L:LutSize)=Transform.MakeCombTransform(XComb(XMake(X)(L)))moduleXSynthesize(X:S)(L:LutSize)=structmoduleC=Comb.Make(XComb(XMake(X)(L)))openCmoduleT=Transform.MakeCombTransform(C)lettransformfind(signal:Signal.t)=matchsignalwith|Reg{register=r;d;_}->letfind_uidx=Signal.uidx|>findinletr={rwith(* note; level constants are copied *)reg_clock=find_uidr.reg_clock;reg_reset=find_uidr.reg_reset;reg_reset_value=find_uidr.reg_reset_value;reg_clear=find_uidr.reg_clear;reg_clear_value=find_uidr.reg_clear_value;reg_enable=find_uidr.reg_enable}inletvreseti=ifis_emptyr.reg_reset_valuethenfalseelse(letc=Signal.const_valuer.reg_reset_value|>Bits.to_bstrinChar.equalc.[i]'1')(* note; not [w-i-1] because we map backwards... *)inletreset=ifis_emptyr.reg_resetthengndelse(matchr.reg_reset_edgewith|Falling->~:(r.reg_reset)|Rising->r.reg_reset)inletclear=ifis_emptyr.reg_clearthengndelse(matchr.reg_clear_levelwith|Low->~:(r.reg_clear)|High->r.reg_clear)inletclk=matchr.reg_clock_edgewith|Falling->~:(r.reg_clock)|Rising->r.reg_clockinletd=find_uiddinletena,d=ifis_emptyr.reg_clearthenr.reg_enable,delseifis_emptyr.reg_clear_valuethenr.reg_enable|:clear,mux2clear(zero(widthsignal))delser.reg_enable|:clear,mux2clearr.reg_clear_valuedinList.mapi(bits_msbd)~f:(funid->ifvresetithenX.fdpeclkenaresetdelseX.fdceclkenaresetd)|>concat_msb|_->T.transformfindsignal;;end