Source code for hwtLib.mem.fifoAsync

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from hwt.code import If, Concat
from hwt.constraints import set_false_path, get_clock_of, set_max_delay
from hwt.hdl.types.bits import Bits
from hwt.interfaces.std import Clk, Rst_n, FifoWriter, FifoReader
from hwt.math import log2ceil, isPow2
from hwt.serializer.mode import serializeParamsUniq
from hwt.synthesizer.param import Param
from hwtLib.clocking.cdc import SignalCdcBuilder
from hwtLib.logic.cntrGray import binToGray
from hwtLib.mem.fifo import Fifo


[docs]@serializeParamsUniq class FifoAsync(Fifo): """ Asynchronous fifo using BRAM/LUT memory, based on: * https://github.com/ZipCPU/website/blob/master/examples/afifo.v * https://github.com/alexforencich/verilog-axis/blob/master/rtl/axis_async_fifo.v .. hwt-autodoc:: _example_FifoAsync """ def _config(self): Fifo._config(self) self.IN_FREQ = Param(int(100e6)) self.OUT_FREQ = Param(int(100e6)) def _declr(self): assert int(self.DEPTH) > 0, "FifoAsync is disabled in this case, do not use it entirely" assert isPow2(self.DEPTH), f"DEPTH has to be power of 2, is {self.DEPTH:d}" # pow 2 because of gray counter counters if self.EXPORT_SIZE or self.EXPORT_SPACE: raise NotImplementedError() self.dataIn_clk = Clk() self.dataIn_clk.FREQ = self.IN_FREQ self.dataOut_clk = Clk() self.dataOut_clk.FREQ = self.OUT_FREQ with self._paramsShared(): with self._associated(clk=self.dataIn_clk): self.dataIn_rst_n = Rst_n() with self._associated(rst=self.dataIn_rst_n): self.dataIn = FifoWriter() with self._associated(clk=self.dataOut_clk): self.dataOut_rst_n = Rst_n() with self._associated(rst=self.dataOut_rst_n): self.dataOut = FifoReader()._m() self.AW = log2ceil(self.DEPTH)
[docs] def _addr_reg_and_cdc(self, reg_name, clk_in, clk_out): """ Create a register for head/tail FIFO reader/writter position with gray encoded value propagated to other clock domain """ AW = self.AW gray_t = Bits(AW + 1) index_t = Bits(AW + 1) reg_bin = self._reg(reg_name + "_bin", index_t, def_val=0, **clk_in) reg_gray = self._reg(reg_name + "_gray", gray_t, def_val=0, **clk_in) cdc_builder = SignalCdcBuilder( reg_gray, (clk_in["clk"], clk_in["rst"]), (clk_out["clk"], clk_out["rst"]), reg_init_val=0) for _ in range(2): cdc_builder.add_out_reg() reg_gray(binToGray(reg_bin.next)) reg_gray_out_clk = cdc_builder.path[-1] # because vivado generates _replica of reg_bin and reworks the gray register set_max_delay(reg_bin, cdc_builder.path[1], cdc_builder.META_PERIOD_NS) return reg_bin, reg_gray, reg_gray_out_clk
def _impl(self): AW = self.AW din = self.dataIn dout = self.dataOut # store clock/resets to a tuples and dicts for later use clk_in = {"clk": self.dataIn_clk, "rst": self.dataIn_rst_n} clk_out = {"clk": self.dataOut_clk, "rst": self.dataOut_rst_n} # head/tail fifo pointers in gray/bin encoding with CDC r_bin, r_gray, r_gray_in_clk = self._addr_reg_and_cdc( "r", clk_out, clk_in) w_bin, w_gray, w_gray_out_clk = self._addr_reg_and_cdc( "w", clk_in, clk_out) w_full = self._reg("w_full", def_val=0, **clk_in) # wbin - rbin == 2^Nl https://zipcpu.com/blog/2018/07/06/afifo.html # first two bits inverted, rest equal w_full(w_gray.next._eq(Concat(~r_gray_in_clk[AW + 1:AW - 1], r_gray_in_clk[AW - 1:]))) din.wait(w_full) w_en = din.en & ~w_full If(w_en, w_bin(w_bin + 1) ) if self.DATA_WIDTH: memory_t = Bits(self.DATA_WIDTH)[self.DEPTH] memory = self._sig("memory", memory_t) If(clk_in["clk"]._onRisingEdge(), If(w_en, memory[w_bin[AW:]](din.data) ) ) r_empty = self._reg("r_empty", def_val=1, **clk_out) r_empty(r_gray.next._eq(w_gray_out_clk)) dout.wait(r_empty) r_en = dout.en & ~r_empty If(r_en, r_bin(r_bin + 1) ) if self.DATA_WIDTH: r_reg = self._reg("r_reg", dout.data._dtype, **clk_out) # set_false_path dataIn_clk -> r_reg set_false_path(get_clock_of(w_bin), r_reg) If(r_en, r_reg(memory[r_bin[AW:]]) ) dout.data(r_reg)
# dout.data(memory[r_bin[AW:]])
[docs]def _example_FifoAsync(): u = FifoAsync() u.DEPTH = 4 return u
if __name__ == "__main__": from hwt.synthesizer.utils import to_rtl_str u = _example_FifoAsync() print(to_rtl_str(u))