// afifo: the asynchronous FIFO of R6.4 with the reset handling of R6.5. DEPTH words of WIDTH bits, // written on wclk and read on rclk. The three switches build broken versions for the bench; with // all three at 0 it is the design the brief asks for. module afifo #( parameter int DEPTH = 8, WIDTH = 8, parameter bit BINARY = 0, // switch: binary pointers on the crossing wires parameter bit NO_READY = 0, // switch: flags that do not wait for both sides to be out of reset parameter bit COMB_CROSS = 0 // switch: the Gray coder sits in front of the synchronizer ) ( input logic wclk, wrst_n, // write domain; wrst_n may be released at any time input logic write_en, input logic [WIDTH-1:0] write_data, output logic full, // 1: do not write (also during reset) input logic rclk, rrst_n, // read domain input logic read_en, output logic read_valid, // 1 for one cycle after a read is taken output logic [WIDTH-1:0] read_data, output logic empty, // 1: nothing to read (also during reset) output logic [$clog2(DEPTH):0] read_level // words the read side can see ); localparam int AW = $clog2(DEPTH); function automatic logic [AW:0] to_wire(input logic [AW:0] b); return BINARY ? b : b ^ (b >> 1); endfunction function automatic logic [AW:0] from_wire(input logic [AW:0] w); logic [AW:0] b; for (int i = 0; i <= AW; i++) b[i] = BINARY ? w[i] : ^(w >> i); return b; endfunction // Each side releases its own reset pin through two flops of its own clock (R6.5). logic wr_q1, wrst_s, rr_q1, rrst_s; always_ff @(posedge wclk or negedge wrst_n) if (!wrst_n) begin wr_q1 <= 1'b0; wrst_s <= 1'b0; end else begin wr_q1 <= 1'b1; wrst_s <= wr_q1; end always_ff @(posedge rclk or negedge rrst_n) if (!rrst_n) begin rr_q1 <= 1'b0; rrst_s <= 1'b0; end else begin rr_q1 <= 1'b1; rrst_s <= rr_q1; end // Each side raises an "up" flag after its release and hears the other's through two flops. logic w_up, r_up, w_rup1, w_rup2, r_wup1, r_wup2; always_ff @(posedge wclk or negedge wrst_s) if (!wrst_s) begin w_up <= 1'b0; w_rup1 <= 1'b0; w_rup2 <= 1'b0; end else begin w_up <= 1'b1; w_rup1 <= r_up; w_rup2 <= w_rup1; end always_ff @(posedge rclk or negedge rrst_s) if (!rrst_s) begin r_up <= 1'b0; r_wup1 <= 1'b0; r_wup2 <= 1'b0; end else begin r_up <= 1'b1; r_wup1 <= w_up; r_wup2 <= r_wup1; end wire w_ready = NO_READY || (w_up && w_rup2); wire r_ready = NO_READY || (r_up && r_wup2); logic [WIDTH-1:0] mem [DEPTH]; logic [AW:0] wbin_q, wptr_q, rbin_q, rptr_q; // word counts, and the registered copies logic [AW:0] wq1_rptr, wq2_rptr; // the read pointer after one and two write-clock flops logic [AW:0] rq1_wptr, rq2_wptr; // the write pointer after one and two read-clock flops logic full_q, empty_q; // What actually crosses: the registered copy, or (COMB_CROSS) the coder's output. logic [AW:0] wptr_x, rptr_x; assign wptr_x = COMB_CROSS ? to_wire(wbin_q) : wptr_q; assign rptr_x = COMB_CROSS ? to_wire(rbin_q) : rptr_q; // Write domain. assign full = full_q || !w_ready; logic take_w; logic [AW:0] wbin_d, rbin_seen; assign take_w = write_en && !full; assign wbin_d = wbin_q + (AW+1)'(take_w); assign rbin_seen = from_wire(wq2_rptr); always_ff @(posedge wclk or negedge wrst_s) if (!wrst_s) begin wbin_q <= '0; wptr_q <= '0; wq1_rptr <= '0; wq2_rptr <= '0; full_q <= 1'b0; end else begin wbin_q <= wbin_d; wptr_q <= to_wire(wbin_d); wq1_rptr <= rptr_x; wq2_rptr <= wq1_rptr; full_q <= (wbin_d[AW] != rbin_seen[AW]) && (wbin_d[AW-1:0] == rbin_seen[AW-1:0]); end always_ff @(posedge wclk) if (take_w) mem[wbin_q[AW-1:0]] <= write_data; // Read domain. assign empty = empty_q || !r_ready; logic take_r; logic [AW:0] rbin_d, wbin_seen; assign take_r = read_en && !empty; assign rbin_d = rbin_q + (AW+1)'(take_r); assign wbin_seen = from_wire(rq2_wptr); always_ff @(posedge rclk or negedge rrst_s) if (!rrst_s) begin rbin_q <= '0; rptr_q <= '0; rq1_wptr <= '0; rq2_wptr <= '0; empty_q <= 1'b1; read_level <= '0; read_valid <= 1'b0; read_data <= '0; end else begin rbin_q <= rbin_d; rptr_q <= to_wire(rbin_d); rq1_wptr <= wptr_x; rq2_wptr <= rq1_wptr; empty_q <= (rbin_d == wbin_seen); read_level <= wbin_seen - rbin_d; read_valid <= take_r; if (take_r) read_data <= mem[rbin_q[AW-1:0]]; end endmodule