123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308open!ImportopenSignaltypet={q:Signal.t;full:Signal.t;empty:Signal.t;nearly_full:Signal.t;nearly_empty:Signal.t;used:Signal.t}[@@derivingsexp_of]typecreate_fifo=?nearly_empty:int(** default is [1] **)->?nearly_full:int(** default is [depth-1] **)->?overflow_check:bool(** default is [true] *)->?reset:Signal.t(** default is [empty] **)->?underflow_check:bool(** default is [true] *)->?ram_attributes:Rtl_attribute.tlist(** default is blockram *)->?scope:Scope.t->unit->capacity:int->clock:Signal.t->clear:Signal.t->wr:Signal.t->d:Signal.t->rd:Signal.t->t(* Generates wbr memory with explicit collision detection to gurantee [wbr] behaviour.
Despite what's suggested by Vivado's BRAM documentation, [write_first] are not
respected, even in SDP RAM mode.
*)letram_wbr_safecapacity~write_port~read_port~ram_attributes=letopenSignalinletcollision=reg(Reg_spec.create~clock:write_port.write_clock())~enable:read_port.read_enable(write_port.write_enable&:read_port.read_enable&:(write_port.write_address==:read_port.read_address))inmux2collision(reg(Reg_spec.create~clock:write_port.write_clock())~enable:read_port.read_enablewrite_port.write_data)(ram_rbwcapacity~attributes:ram_attributes~write_port~read_port);;letcreate?(showahead=false)?(nearly_empty=1)?nearly_full?(overflow_check=true)?(reset=Signal.empty)?(underflow_check=true)?(ram_attributes=[Rtl_attribute.Vivado.Ram_style.block])?scope()~capacity:ram_capacity~clock~clear~wr~d~rd=let(--)=matchscopewith|Somescope->Scope.namingscope|None->(--)inifSignal.is_emptyclear&&Signal.is_emptyresetthenraise_s[%message"[Fifo.create] requires either a synchronous clear or asynchronous reset"];letreg_spec=Reg_spec.create~clock~clear~reset()inletreg?clear_to~enabled=reg(Reg_spec.overridereg_spec?clear_to)~enabledinletabits=address_bits_forram_capacityinletactual_capacity=(* to be consistent with Vivado's FIFO implementation, when instantiating a fwft FIFO,
it's actual capacity is added by one due to the additional register in the prefetch
buffer register.
*)ifshowaheadthenram_capacity+1elseram_capacityinletubits=num_bits_to_representactual_capacityin(* get nearly full/empty levels *)letnearly_full=matchnearly_fullwith|None->actual_capacity-1|Somex->xinletempty,full=wire1,wire1in(* safe rd/wr signals assuming fifo neither full or empty *)letrd=ifunderflow_checkthen(rd&:~:empty)--"RD_INT"elserdinletwr=ifoverflow_checkthen(wr&:~:full)--"WR_INT"elsewrin(* read or write, but not both *)letenable=rd^:wrin(* fill level of fifo *)letused=wireubitsinletused_next=mux2enable(mux2rd(used-:.1)(used+:.1))used(* read+write, or none *)inused<==reg~enable(used_next--"USED_NEXT");(* full empty flags *)empty<==reg~enable~clear_to:vdd(used_next==:.0);full<==reg~enable(used_next==:.actual_capacity);(* nearly full/empty flags *)letnearly_empty=reg~enable~clear_to:vdd(used_next<:.nearly_empty)inletnearly_full=reg~enable(used_next>=:.nearly_full)in(* read/write addresses within fifo *)letaddr_countenablename=leta=wireabitsinletan=mod_counter~max:(ram_capacity-1)aina<==reg~enablean;a--name,an--(name^"_NEXT")inletq=ifshowaheadthen(letused_is_one=reg~enable:(rd^:wr)(used_next==:.1)inletused_gt_one=reg~enable:(rd^:wr)(used_next>:.1)inletmemory=letwr=wr&:(used_gt_one|:(used_is_one&:~:rd))inletrd=rd&:used_gt_oneinletra,ra_n=addr_countrd"READ_ADDRESS"inletra=mux2rdra_nra--"RA"inletwa,_=addr_countwr"WRITE_ADDRESS"inram_wbr_safe~ram_attributesram_capacity~write_port:{write_clock=clock;write_enable=wr;write_address=wa;write_data=d}~read_port:{read_clock=clock;read_enable=vdd;read_address=ra}inletbypass_cond=empty&:wr|:(used_is_one&:wr&:rd)inmux2bypass_conddmemory|>reg~enable:(bypass_cond|:rd))else(letra,_=addr_countrd"READ_ADDRESS"inletwa,_=addr_countwr"WRITE_ADDRESS"inram_rbw~attributes:ram_attributesram_capacity~write_port:{write_clock=clock;write_enable=wr;write_address=wa;write_data=d}~read_port:{read_clock=clock;read_enable=rd;read_address=ra})in{q;full;empty;nearly_full;nearly_empty;used};;letcreate_classic_with_extra_reg?nearly_empty?nearly_full?overflow_check?reset?underflow_check?ram_attributes?scope()~capacity~clock~clear~wr~d~rd=letspec=Reg_spec.create~clock~clear()inletfifo_valid=wire1inletmiddle_valid=wire1inletfifo_rd_en=wire1inletempty=~:(fifo_valid|:middle_valid)inletwill_update_dout=~:empty&:rdinletwill_update_middle=fifo_valid&:(middle_valid==:will_update_dout)inletfifo=create~showahead:false?nearly_empty?nearly_full?overflow_check?reset?underflow_check?ram_attributes?scope()~capacity~clock~clear~wr~d~rd:fifo_rd_eninletmiddle_dout=regspec~enable:will_update_middlefifo.qinfifo_rd_en<==(~:(fifo.empty)&:~:(middle_valid&:fifo_valid));fifo_valid<==regspec~enable:(fifo_rd_en|:will_update_middle|:will_update_dout)fifo_rd_en;middle_valid<==regspec~enable:(will_update_middle|:will_update_dout)will_update_middle;{fifowithq=regspec~enable:will_update_dout(mux2middle_validmiddle_doutfifo.q);empty};;letcreate_showahead_from_classic?nearly_empty?nearly_full?overflow_check?reset?underflow_check?ram_attributes?scope()~capacity~clock~clear~wr~d~rd=letspec=Reg_spec.create~clock~clear()inletfifo_rd_en=wire1inletfifo=create~showahead:false?nearly_empty?nearly_full?overflow_check?reset?underflow_check?ram_attributes?scope()~capacity~clock~clear~wr~d~rd:fifo_rd_eninletdout_valid=regspec~enable:(fifo_rd_en|:rd)fifo_rd_eninletempty=~:dout_validinfifo_rd_en<==(~:(fifo.empty)&:(~:dout_valid|:rd));{fifowithempty};;letcreate_showahead_with_extra_reg?nearly_empty?nearly_full?overflow_check?reset?underflow_check?ram_attributes?scope()~capacity~clock~clear~wr~d~rd=letspec=Reg_spec.create~clock~clear()inletfifo_rd_en=wire1inletfifo=create~showahead:false?nearly_empty?nearly_full?overflow_check?reset?underflow_check?ram_attributes?scope()~capacity~clock~clear~wr~d~rd:fifo_rd_eninletfifo_valid=wire1inletmiddle_valid=wire1inletdout_valid=wire1inletwill_update_dout=middle_valid|:fifo_valid&:(rd|:~:dout_valid)inletwill_update_middle=fifo_valid&:(middle_valid==:will_update_dout)inletempty=~:dout_validinletmiddle_dout=regspec~enable:will_update_middlefifo.qinletdout=regspec~enable:will_update_dout(mux2middle_validmiddle_doutfifo.q)infifo_rd_en<==(~:(fifo.empty)&:~:(middle_valid&:dout_valid&:fifo_valid));fifo_valid<==regspec~enable:(fifo_rd_en|:will_update_middle|:will_update_dout)fifo_rd_en;middle_valid<==regspec~enable:(will_update_middle|:will_update_dout)will_update_middle;dout_valid<==regspec~enable:(will_update_dout|:rd)will_update_dout;{fifowithq=dout;empty};;