def fifo(rst, clk, full, we, din, empty, re, dout, afull=None, aempty=None, afull_th=None, aempty_th=None, ovf=None, udf=None, count=None, count_max=None, depth=None, width=None): """ Synchronous FIFO Input interface: full, we, din Output interface: empty, re, dout It s possible to set din and dout to None. Then the fifo width will be 0 and the fifo will contain no storage. Extra interface: afull (o) - almost full flag, asserted when the number of empty cells <= afull_th aempty (o) - almost empty flag, asserted when the number of full cells <= aempty_th afull_th (i) - almost full threshold, in terms of fifo cells; signal or constant; Optional, default depth/2 aempty_th (i) - almost empty threshold, in terms of fifo cells; signal or constant; Optional, default depth/2 count (o) - number of occupied fifo cells count_max (o) - max number of occupied fifo cells reached since the last reset ovf (o) - overflow flag, set at the first write in a full fifo, cleared at reset udf (o) - underflow flag, set at the first read from an empty fifo, cleared at reset Parameters: depth - fifo depth, must be >= 1; if not set or set to `None` default value 2 is used width - data width in bits, must be >= 0; if not set or set to `None` the `din` width is used """ if (width == None): width = 0 if din is not None: width = len(din) if (depth == None): depth = 2 full_flg = Signal(bool(1)) empty_flg = Signal(bool(1)) we_safe = Signal(bool(0)) re_safe = Signal(bool(0)) rd_ptr = Signal(intbv(0, min=0, max=depth)) rd_ptr_new = Signal(intbv(0, min=0, max=depth)) wr_ptr = Signal(intbv(0, min=0, max=depth)) wr_ptr_new = Signal(intbv(0, min=0, max=depth)) @always_comb def safe_read_write(): full.next = full_flg empty.next = empty_flg we_safe.next = we and not full_flg re_safe.next = re and not empty_flg #=========================================================================== # Write, Read, Full, Empty #=========================================================================== @always_comb def ptrs_new(): rd_ptr_new.next = ((rd_ptr + 1) % depth) wr_ptr_new.next = ((wr_ptr + 1) % depth) @always(clk.posedge) def state_main(): if (rst): wr_ptr.next = 0 rd_ptr.next = 0 full_flg.next = 0 empty_flg.next = 1 else: # Write pointer if (we_safe): wr_ptr.next = wr_ptr_new # Read pointer if (re_safe): rd_ptr.next = rd_ptr_new # Empty flag if (we_safe): empty_flg.next = 0 elif (re_safe and (rd_ptr_new == wr_ptr)): empty_flg.next = 1 # Full flag if (re_safe): full_flg.next = 0 elif (we_safe and (wr_ptr_new == rd_ptr)): full_flg.next = 1 #=========================================================================== # Count, CountMax #=========================================================================== ''' Count ''' if (count != None) or (count_max != None) or (afull != None) or (aempty != None): count_r = Signal(intbv(0, min=0, max=depth+1)) count_new = Signal(intbv(0, min=-1, max=depth+2)) if (count != None): assert count.max > depth @always_comb def count_out(): count.next = count_r @always_comb def count_comb(): if (we_safe and not re_safe): count_new.next = count_r + 1 elif (not we_safe and re_safe): count_new.next = count_r - 1 else: count_new.next = count_r @always(clk.posedge) def count_proc(): if (rst): count_r.next = 0 else: count_r.next = count_new ''' Count max ''' if (count_max != None): assert count_max.max > depth count_max_r = Signal(intbv(0, min=0,max=count_max.max)) @always(clk.posedge) def count_max_proc(): if (rst): count_max_r.next = 0 else: if (count_max_r < count_new): count_max_r.next = count_new @always_comb def count_max_out(): count_max.next = count_max_r #=========================================================================== # AlmostFull, AlmostEmpty #=========================================================================== ''' AlmostFull flag ''' if (afull != None): if (afull_th == None): afull_th = depth//2 @always(clk.posedge) def afull_proc(): if (rst): afull.next = 0 else: afull.next = (count_new >= depth-afull_th) ''' AlmostEmpty flag ''' if (aempty != None): if (aempty_th == None): aempty_th = depth//2 @always(clk.posedge) def aempty_proc(): if (rst): aempty.next = 1 else: aempty.next = (count_new <= aempty_th) #=========================================================================== # Overflow, Underflow #=========================================================================== ''' Overflow flag ''' if (ovf != None): @always(clk.posedge) def ovf_proc(): if (rst): ovf.next = 0 else: if (we and full_flg ): ovf.next = 1 ''' Underflow flag ''' if (udf != None): @always(clk.posedge) def udf_proc(): if (rst): udf.next = 0 else: if (re and empty_flg): udf.next = 1 if width>0: #=========================================================================== # Memory instance #=========================================================================== mem_we = Signal(bool(0)) mem_addrw = Signal(intbv(0, min=0, max=depth)) mem_addrr = Signal(intbv(0, min=0, max=depth)) mem_di = Signal(intbv(0)[width:0]) mem_do = Signal(intbv(0)[width:0]) # RAM: Simple-Dual-Port, Asynchronous read mem = ram_sdp_ar( clk = clk, we = mem_we, addrw = mem_addrw, addrr = mem_addrr, di = mem_di, do = mem_do ) @always_comb def mem_connect(): mem_we.next = we_safe mem_addrw.next = wr_ptr mem_addrr.next = rd_ptr mem_di.next = din dout.next = mem_do return instances()
def fifo_async(wrst, rrst, wclk, rclk, wfull, we, wdata, rempty, re, rdata, depth=None, width=None): """ Asynchronous FIFO Implements the design described in: Clifford E. Cummings, "Simulation and Synthesis Techniques for Asynchronous FIFO Design," SNUG 2002 (Synopsys Users Group Conference, San Jose, CA, 2002) User Papers, March 2002, Section TB2, 2nd paper. Also available at www.sunburst-design.com/papers Write side interface: wrst - reset wclk - clock wfull - full flag, immediate set on 'write', delayed clear on 'read' due to clock domain synchronization we - write enable wdata - write data Read side interface rrst - reset cclk - clock rempty - empty flag, immediate set on 'read', delayed clear on 'write' due to clock domain synchronization re - read enable rdata - read data Parameters depth - fifo depth. If not set, default 4 is used. Must be >=4. Must be power of 2 width - data width. If not set, data with equals len(wdata). Can be [0,1,2,3...) It is possible to instantiate a fifo with data width 0 (no data) if width=0 or width=None and wdata=None """ if width == None: width = 0 if wdata is not None: width = len(wdata) if depth == None: depth = 4 assert depth >= 4, "Fifo_async parameter 'depth' must be >= 4 , detected depth={}".format(depth) assert (depth & (depth - 1)) == 0, "Fifo_async parameter 'depth' must be 2**n, detected depth={}".format(depth) full_flg = Signal(bool(1)) empty_flg = Signal(bool(1)) full_val = Signal(bool(1)) empty_val = Signal(bool(1)) we_safe = Signal(bool(0)) re_safe = Signal(bool(0)) rd_ptr = Signal(intbv(0, min=0, max=depth)) wr_ptr = Signal(intbv(0, min=0, max=depth)) WIDTH = len(rd_ptr) rd_ptr_bin = Signal(intbv(0)[WIDTH + 1 :]) rd_ptr_bin_new = Signal(intbv(0)[WIDTH + 1 :]) rd_ptr_gray = Signal(intbv(0)[WIDTH + 1 :]) rd_ptr_gray_new = Signal(intbv(0)[WIDTH + 1 :]) rd_ptr_gray_sync1 = Signal(intbv(0)[WIDTH + 1 :]) rd_ptr_gray_sync2 = Signal(intbv(0)[WIDTH + 1 :]) wr_ptr_bin = Signal(intbv(0)[WIDTH + 1 :]) wr_ptr_bin_new = Signal(intbv(0)[WIDTH + 1 :]) wr_ptr_gray = Signal(intbv(0)[WIDTH + 1 :]) wr_ptr_gray_new = Signal(intbv(0)[WIDTH + 1 :]) wr_ptr_gray_sync1 = Signal(intbv(0)[WIDTH + 1 :]) wr_ptr_gray_sync2 = Signal(intbv(0)[WIDTH + 1 :]) @always_comb def safe_read_write(): wfull.next = full_flg rempty.next = empty_flg we_safe.next = we and not full_flg re_safe.next = re and not empty_flg @always(wclk.posedge) def sync_r2w(): """ Read-domain to write-domain synchronizer """ if wrst: rd_ptr_gray_sync1.next = 0 rd_ptr_gray_sync2.next = 0 else: rd_ptr_gray_sync1.next = rd_ptr_gray rd_ptr_gray_sync2.next = rd_ptr_gray_sync1 @always(rclk.posedge) def sync_w2r(): """ Write-domain to read-domain synchronizer """ if rrst: wr_ptr_gray_sync1.next = 0 wr_ptr_gray_sync2.next = 0 else: wr_ptr_gray_sync1.next = wr_ptr_gray wr_ptr_gray_sync2.next = wr_ptr_gray_sync1 @always_comb def bin_comb(): wr_ptr_bin_new.next = wr_ptr_bin rd_ptr_bin_new.next = rd_ptr_bin if we_safe: wr_ptr_bin_new.next = (wr_ptr_bin + 1) % wr_ptr_bin.max if re_safe: rd_ptr_bin_new.next = (rd_ptr_bin + 1) % rd_ptr_bin.max @always_comb def gray_comb(): wr_ptr_gray_new.next = (wr_ptr_bin_new >> 1) ^ wr_ptr_bin_new rd_ptr_gray_new.next = (rd_ptr_bin_new >> 1) ^ rd_ptr_bin_new @always_comb def full_empty_comb(): empty_val.next = rd_ptr_gray_new == wr_ptr_gray_sync2 full_val.next = ( (wr_ptr_gray_new[WIDTH] != rd_ptr_gray_sync2[WIDTH]) and (wr_ptr_gray_new[WIDTH - 1] != rd_ptr_gray_sync2[WIDTH - 1]) and (wr_ptr_gray_new[WIDTH - 1 :] == rd_ptr_gray_sync2[WIDTH - 1 :]) ) @always(wclk.posedge) def wptr_proc(): if wrst: wr_ptr_bin.next = 0 wr_ptr_gray.next = 0 full_flg.next = 0 else: wr_ptr_bin.next = wr_ptr_bin_new wr_ptr_gray.next = wr_ptr_gray_new full_flg.next = full_val @always(rclk.posedge) def rptr_proc(): if rrst: rd_ptr_bin.next = 0 rd_ptr_gray.next = 0 empty_flg.next = 1 else: rd_ptr_bin.next = rd_ptr_bin_new rd_ptr_gray.next = rd_ptr_gray_new empty_flg.next = empty_val if width > 0: # =========================================================================== # Memory instance # =========================================================================== mem_we = Signal(bool(0)) mem_addrw = Signal(intbv(0, min=0, max=depth)) mem_addrr = Signal(intbv(0, min=0, max=depth)) mem_di = Signal(intbv(0)[width:0]) mem_do = Signal(intbv(0)[width:0]) # RAM: Simple-Dual-Port, Asynchronous read mem = ram_sdp_ar(clk=wclk, we=mem_we, addrw=mem_addrw, addrr=mem_addrr, di=mem_di, do=mem_do) @always_comb def mem_connect(): mem_we.next = we_safe mem_addrw.next = wr_ptr_bin[WIDTH:] mem_addrr.next = rd_ptr_bin[WIDTH:] mem_di.next = wdata rdata.next = mem_do return instances()
def fifo_speculative(rst, clk, full, we, din, empty, re, dout, wr_commit=None, wr_discard=None, rd_commit=None, rd_discard=None, afull=None, aempty=None, count=None, afull_th=None, aempty_th=None, ovf=None, udf=None, count_max=None, depth=None, width=None): """ Speculative FIFO Input interface: full, we, din, wr_commit, wr_discard Output interface: empty, re, dout, rd_commit, rd_discard we (i) - writes data speculatively (the data can be later removed from the fifo) wr_commit (i) - accept the speculatively written data (can be read) wr_discard (i) - remove the speculatively written data at once full (o) - asserted when there are no empty cells to write data speculatively If we is asserted together with the wr_commit or wr_discard, the data being written is affected by the commit/discard command. If wr_commit and wr_discard are asserted simultaneously, the wr_discard wins. re (i) - reads data speculatively (the data can be later restored) rd_commit (i) - removes the speculatively read data at once rd_discard (i) - restores the speculatively read data (will be read again) empty (o) - asserted when there are no committed date to be read If re is asserted together with the rd_commit or rd_discards, the data being read is affected by the commit/discard command. If rd_commit and rd_discard are asserted simultaneously, the rd_discard wins. Note: This fifo can be Full and Empty at the same time - all fifo cells are occupied by data that are either speculatively written or speculatively read Extra interface: afull (o) - almost full flag, asserted when the number of empty cells <= afull_th aempty (o) - almost empty flag, asserted when the number of full cells <= aempty_th afull_th (i) - almost full threshold, in terms of fifo cells; Optional, default depth/2 aempty_th (i) - almost empty threshold, in terms of fifo cells; Optional, default depth/2 count (o) - number of occupied fifo cells, committed and not committed (speculatively written/read) count_max (o) - max number of occupied fifo cells reached since the last reset ovf (o) - overflow flag, set at the first write in a full fifo, cleared at reset udf (o) - underflow flag, set at the first read from an empty fifo, cleared at reset """ if (width == None): width = len(din) if (depth == None): depth = 2 assert ((wr_commit != None) and (wr_discard != None)) or ((wr_commit == None) and (wr_discard == None)), "SFIFO ERROR: Interface signals wr_commit and wr_discard must be either both used or both left unused" assert ((rd_commit != None) and (rd_discard != None)) or ((rd_commit == None) and (rd_discard == None)), "SFIFO ERROR: Interface signals rd_commit and rd_discard must be either both used or both left unused" wr_commit = wr_commit if (wr_commit != None) else Signal(bool(1)) wr_discard = wr_discard if (wr_discard != None) else Signal(bool(0)) rd_commit = rd_commit if (rd_commit != None) else Signal(bool(1)) rd_discard = rd_discard if (rd_discard != None) else Signal(bool(0)) full_flg = Signal(bool(1)) empty_flg = Signal(bool(1)) we_safe = Signal(bool(0)) re_safe = Signal(bool(0)) swr_non0 = Signal(bool(0)) srd_non0 = Signal(bool(0)) wr_commit_non0 = Signal(bool(0)) rd_commit_non0 = Signal(bool(0)) wr_discard_non0 = Signal(bool(0)) rd_discard_non0 = Signal(bool(0)) # Almost full/empty and their thresholds afull_flg = Signal(bool(0)) aempty_flg = Signal(bool(0)) if (afull_th == None): afull_th = depth//2 if (aempty_th == None): aempty_th = depth//2 afull = afull if (afull != None) else Signal(bool(0)) aempty = aempty if (aempty != None) else Signal(bool(0)) count = count if (count != None) else Signal(intbv(0, min=0, max=depth + 1)) ovf = ovf if (ovf != None) else Signal(bool(0)) udf = udf if (udf != None) else Signal(bool(0)) count_max = count_max if (count_max != None) else Signal(intbv(0, min=0, max=depth+1)) rd_ptr = Signal(intbv(0, min=0, max=depth)) wr_ptr = Signal(intbv(0, min=0, max=depth)) srd_ptr = Signal(intbv(0, min=0, max=depth)) srd_ptr_new = Signal(intbv(0, min=0, max=depth)) swr_ptr = Signal(intbv(0, min=0, max=depth)) swr_ptr_new = Signal(intbv(0, min=0, max=depth)) # Data written in the fifo (committed and not committed) data_count = Signal(intbv(0, min=0, max=depth + 1)) data_count_max = Signal(intbv(0, min=0, max=depth + 1)) @always_comb def out_assign(): full.next = full_flg empty.next = empty_flg afull.next = afull_flg aempty.next = aempty_flg count.next = data_count count_max.next = data_count_max @always_comb def safe_read_write(): we_safe.next = we and not full_flg re_safe.next = re and not empty_flg @always_comb def non_zero_commits(): wr_commit_non0.next = wr_commit and (swr_non0 or we_safe) and not wr_discard rd_commit_non0.next = rd_commit and (srd_non0 or re_safe) and not rd_discard # wr_discard_non0.next = wr_discard and (swr_non0 or we_safe) rd_discard_non0.next = rd_discard and (srd_non0 or re_safe) """ ---------------------------- """ """ - Write, Read, Full, Empty - """ """ ---------------------------- """ @always_comb def sptrs_new(): """ Next value for the speculative pointers """ if (wr_discard): swr_ptr_new.next = wr_ptr elif (we_safe): swr_ptr_new.next = ((swr_ptr + 1) % depth) else: swr_ptr_new.next = swr_ptr if (rd_discard): srd_ptr_new.next = rd_ptr elif (re_safe): srd_ptr_new.next = ((srd_ptr + 1) % depth) else: srd_ptr_new.next = srd_ptr @always(clk.posedge) def state_main(): if (rst): wr_ptr.next = 0 rd_ptr.next = 0 swr_ptr.next = 0 srd_ptr.next = 0 swr_non0.next = 0 srd_non0.next = 0 full_flg.next = 0 empty_flg.next = 1 else: # Speculative Write pointer swr_ptr.next = swr_ptr_new # Speculative Read pointer srd_ptr.next = srd_ptr_new # Write pointer if (wr_commit): wr_ptr.next = swr_ptr_new # Read pointer if (rd_commit): rd_ptr.next = srd_ptr_new # Empty flag if (wr_commit_non0 or rd_discard_non0): empty_flg.next = 0 elif (re_safe and (srd_ptr_new == wr_ptr)): empty_flg.next = 1 # Full flag if (rd_commit_non0 or wr_discard_non0): full_flg.next = 0 elif (we_safe and (swr_ptr_new == rd_ptr)): full_flg.next = 1 # swr_non0 flag if (wr_commit or wr_discard): swr_non0.next = 0 elif (we_safe): swr_non0.next = 1 # srd_non0 flag if (rd_commit or rd_discard): srd_non0.next = 0 elif (re_safe): srd_non0.next = 1 """ rd srd wr swr rd ^ ^ v v ^ |#########################|%%%%%%%%%%%%%%|*************************|..............| |<------ srd_count ------>|<------------>|<------ swr_count ------>|<------------>| |<--------------- wr_count ------------->|<--------------- rd_count ------------->| |<----------------------------------- depth ------------------------------------->| """ """ ------------------------------------ """ """ - Almost_Full, Almost_Empty, Count - """ """ ------------------------------------ """ # count of the speculatively written data swr_count = Signal(intbv(0, min=0, max=depth + 1)) # count of the speculatively read data srd_count = Signal(intbv(0, min=0, max=depth + 1)) # count of the write committed data wr_count = Signal(intbv(0, min=0, max=depth + 1)) # count of the not write committed positions rd_count = Signal(intbv(0, min=0, max=depth + 1)) @always(clk.posedge) def state_extra(): if (rst): swr_count.next = 0 srd_count.next = 0 wr_count.next = 0 rd_count.next = depth data_count.next = 0 afull_flg.next = 0 aempty_flg.next = 1 data_count_max.next = 0 else: swr_count_new = 0 if (not wr_commit and not wr_discard): swr_count_new = int((swr_count + 1) if (we_safe) else swr_count) swr_count.next = swr_count_new srd_count_new = 0 if (not rd_commit and not rd_discard): srd_count_new = int((srd_count + 1 if (re_safe) else srd_count)) srd_count.next = srd_count_new wr_add_new = 0 if (wr_commit and not wr_discard): wr_add_new = int((swr_count + 1) if (we_safe) else swr_count) rd_add_new = 0 if (rd_commit and not rd_discard): rd_add_new = int((srd_count + 1) if (re_safe) else srd_count) wr_count_new = wr_count + wr_add_new - rd_add_new rd_count_new = rd_count - wr_add_new + rd_add_new wr_count.next = wr_count_new rd_count.next = rd_count_new aempty_flg.next = ((wr_count_new - srd_count_new) <= aempty_th) afull_flg.next = ((rd_count_new - swr_count_new) <= afull_th ) # Count data in the fifo data_count_new = wr_count_new + swr_count_new data_count.next = data_count_new if (data_count_max < data_count_new): data_count_max.next = data_count_new ''' Overflow flag ''' if (ovf != None): @always(clk.posedge) def ovf_proc(): if (rst): ovf.next = 0 else: if (we and full_flg ): ovf.next = 1 ''' Underflow flag ''' if (udf != None): @always(clk.posedge) def udf_proc(): if (rst): udf.next = 0 else: if (re and empty_flg): udf.next = 1 """ ------------------- """ """ - Memory instance - """ """ ------------------- """ mem_we = Signal(bool(0)) mem_addrw = Signal(intbv(0, min=0, max=depth)) mem_addrr = Signal(intbv(0, min=0, max=depth)) mem_di = Signal(intbv(0)[width:0]) mem_do = Signal(intbv(0)[width:0]) # RAM: Simple-Dual-Port, Asynchronous Read mem = ram_sdp_ar( clk = clk, we = mem_we, addrw = mem_addrw, addrr = mem_addrr, di = mem_di, do = mem_do ) @always_comb def mem_connect(): mem_we.next = we_safe mem_addrw.next = swr_ptr mem_addrr.next = srd_ptr mem_di.next = din dout.next = mem_do return instances()