name: "Asynchronous Dual-Clock FIFO"
category: "Algorithmic Hardware Primitives"
complexity: "Medium"
description: |
  Implement an asynchronous FIFO that transfers data from a write clock
  domain to an independent read clock domain.

  The FIFO depth is 8 entries and each entry is 16 bits wide. Data must be
  returned in first-in, first-out order.

  WRITE INTERFACE:
  - A write is accepted on a rising edge of `wr_clk` when both `wr_en` is
    high and `wr_full` is low.
  - On an accepted write, the value of `wr_data` is appended to the FIFO.
  - If `wr_en` is high while `wr_full` is high, no write is accepted and the
    FIFO contents must not change.

  READ INTERFACE:
  - A read is accepted on a rising edge of `rd_clk` when both `rd_en` is high
    and `rd_empty` is low.
  - On an accepted read, the oldest unread word must be removed from the FIFO.
  - `rd_data` must present the word returned by that accepted read
    immediately after the accepting edge.
  - If `rd_en` is high while `rd_empty` is high, no read is accepted and the
    FIFO contents must not change.
  - When no read is accepted on a given `rd_clk` edge, `rd_data` is
    don't-care and will not be checked.

  ORDERING:
  - The sequence of values observed on successful reads must exactly match the
    sequence of previously accepted writes.
  - Reads and writes may occur on unrelated clocks and in overlapping time
    windows, but the externally visible behaviour must still match a FIFO with
    8-word storage capacity.

  FLAG BEHAVIOUR:
  - `wr_full` is observed in the write clock domain and must prevent any write
    that would cause the number of stored unread words to exceed 8.
  - `rd_empty` is observed in the read clock domain and must prevent any read
    when no unread words are available.
  - After a successful write that fills the final free entry, `wr_full` must
    be high immediately after that `wr_clk` edge.
  - After a successful read that removes the final unread word, `rd_empty`
    must be high immediately after that `rd_clk` edge.
  - After a successful read creates free space, `wr_full` may remain high for
    up to 2 additional rising edges of `wr_clk` before deasserting.
  - After a successful write makes data available to read, `rd_empty` may
    remain high for up to 2 additional rising edges of `rd_clk` before
    deasserting.
  - Aside from those bounded cross-domain visibility delays, the flags must
    reflect whether a write or read can safely be accepted in their local
    domains.

  RESET:
  - `wr_rst` and `rd_rst` are synchronous active-high resets for their
    respective clock domains.
  - The benchmark will only assert and deassert `wr_rst` and `rd_rst`
    together.
  - While reset is asserted, no writes or reads are accepted.
  - After reset has been observed in both domains, the FIFO must be empty.
  - After reset is released, `wr_full` must be low, `rd_empty` must be high,
    and no previously written data may remain available.

interface:
  module_name: "async_dual_clock_fifo"
  clocked: true
  ports:
    - name: "wr_clk"
      type: "input"
      width: "1"
      description: "Write-domain clock"
    - name: "wr_rst"
      type: "input"
      width: "1"
      description: "Synchronous active-high reset for the write domain"
    - name: "wr_en"
      type: "input"
      width: "1"
      description: "Write request; sampled on rising edges of wr_clk"
    - name: "wr_data"
      type: "input"
      width: "16"
      description: "Write-domain input data"
    - name: "wr_full"
      type: "output"
      width: "1"
      description: "High when the FIFO cannot safely accept another write"
    - name: "rd_clk"
      type: "input"
      width: "1"
      description: "Read-domain clock"
    - name: "rd_rst"
      type: "input"
      width: "1"
      description: "Synchronous active-high reset for the read domain"
    - name: "rd_en"
      type: "input"
      width: "1"
      description: "Read request; sampled on rising edges of rd_clk"
    - name: "rd_data"
      type: "output"
      width: "16"
      description: "Data returned by an accepted read"
    - name: "rd_empty"
      type: "output"
      width: "1"
      description: "High when no unread data is currently available to read"

target_platform:
  device_family: "AMD Xilinx Zynq-7000"

tasks:
  - name: "Area-Efficient Async Dual-Clock FIFO"
    constraints:
      max_lut: 140
      max_ff: 80
      max_bram: 0
      max_dsp: 0
      min_frequency: 180
    objective:
      goal: "minimise"
      target: "LUT"
