../src/lowrisc_prim_all_0.1/rtl/prim_fifo_async.sv Cov: 100%
1: // Copyright lowRISC contributors.
2: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3: // SPDX-License-Identifier: Apache-2.0
4: //
5: // Generic asynchronous fifo for use in a variety of devices.
6:
7: `include "prim_assert.sv"
8:
9: module prim_fifo_async #(
10: parameter int unsigned Width = 16,
11: parameter int unsigned Depth = 3,
12: localparam int unsigned DepthW = $clog2(Depth+1) // derived parameter representing [0..Depth]
13: ) (
14: // write port
15: input clk_wr_i,
16: input rst_wr_ni,
17: input wvalid,
18: output wready,
19: input [Width-1:0] wdata,
20: output [DepthW-1:0] wdepth,
21:
22: // read port
23: input clk_rd_i,
24: input rst_rd_ni,
25: output rvalid,
26: input rready,
27: output [Width-1:0] rdata,
28: output [DepthW-1:0] rdepth
29: );
30:
31: `ASSERT_INIT(paramCheckDepth, Depth >= 3)
32:
33: localparam int unsigned PTRV_W = $clog2(Depth);
34: localparam logic [PTRV_W-1:0] DepthMinus1 = PTRV_W'(Depth - 1);
35: localparam int unsigned PTR_WIDTH = PTRV_W+1;
36:
37: logic [PTR_WIDTH-1:0] fifo_wptr, fifo_rptr;
38: logic [PTR_WIDTH-1:0] fifo_wptr_sync_combi, fifo_rptr_sync;
39: logic [PTR_WIDTH-1:0] fifo_wptr_gray_sync, fifo_rptr_gray_sync;
40: logic [PTR_WIDTH-1:0] fifo_wptr_gray, fifo_rptr_gray;
41: logic fifo_incr_wptr, fifo_incr_rptr, empty;
42:
43: logic full_wclk, full_rclk;
44:
45: assign wready = !full_wclk;
46: assign rvalid = !empty;
47:
48: // create the write and read pointers
49:
50: assign fifo_incr_wptr = wvalid & wready;
51: assign fifo_incr_rptr = rvalid & rready;
52:
53: ///////////////////
54: // write pointer //
55: ///////////////////
56:
57: always_ff @(posedge clk_wr_i or negedge rst_wr_ni)
58: if (!rst_wr_ni) begin
59: fifo_wptr <= {(PTR_WIDTH){1'b0}};
60: end else if (fifo_incr_wptr) begin
61: if (fifo_wptr[PTR_WIDTH-2:0] == DepthMinus1) begin
62: fifo_wptr <= {~fifo_wptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}};
63: end else begin
64: fifo_wptr <= fifo_wptr + {{(PTR_WIDTH-1){1'b0}},1'b1};
65: end
66: end
67:
68: // gray-coded version
69: always_ff @(posedge clk_wr_i or negedge rst_wr_ni)
70: if (!rst_wr_ni) begin
71: fifo_wptr_gray <= {(PTR_WIDTH){1'b0}};
72: end else if (fifo_incr_wptr) begin
73: if (fifo_wptr[PTR_WIDTH-2:0] == DepthMinus1) begin
74: fifo_wptr_gray <= dec2gray({~fifo_wptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}});
75: end else begin
76: fifo_wptr_gray <= dec2gray(fifo_wptr + {{(PTR_WIDTH-1){1'b0}},1'b1});
77: end
78: end
79:
80: prim_flop_2sync #(.Width(PTR_WIDTH)) sync_wptr (
81: .clk_i (clk_rd_i),
82: .rst_ni (rst_rd_ni),
83: .d (fifo_wptr_gray),
84: .q (fifo_wptr_gray_sync));
85:
86: assign fifo_wptr_sync_combi = gray2dec(fifo_wptr_gray_sync);
87:
88: //////////////////
89: // read pointer //
90: //////////////////
91:
92: always_ff @(posedge clk_rd_i or negedge rst_rd_ni)
93: if (!rst_rd_ni) begin
94: fifo_rptr <= {(PTR_WIDTH){1'b0}};
95: end else if (fifo_incr_rptr) begin
96: if (fifo_rptr[PTR_WIDTH-2:0] == DepthMinus1) begin
97: fifo_rptr <= {~fifo_rptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}};
98: end else begin
99: fifo_rptr <= fifo_rptr + {{(PTR_WIDTH-1){1'b0}},1'b1};
100: end
101: end
102:
103: // gray-coded version
104: always_ff @(posedge clk_rd_i or negedge rst_rd_ni)
105: if (!rst_rd_ni) begin
106: fifo_rptr_gray <= {(PTR_WIDTH){1'b0}};
107: end else if (fifo_incr_rptr) begin
108: if (fifo_rptr[PTR_WIDTH-2:0] == DepthMinus1) begin
109: fifo_rptr_gray <= dec2gray({~fifo_rptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}});
110: end else begin
111: fifo_rptr_gray <= dec2gray(fifo_rptr + {{(PTR_WIDTH-1){1'b0}},1'b1});
112: end
113: end
114:
115: prim_flop_2sync #(.Width(PTR_WIDTH)) sync_rptr (
116: .clk_i (clk_wr_i),
117: .rst_ni (rst_wr_ni),
118: .d (fifo_rptr_gray),
119: .q (fifo_rptr_gray_sync));
120:
121: always_ff @(posedge clk_wr_i or negedge rst_wr_ni)
122: if (!rst_wr_ni) begin
123: fifo_rptr_sync <= {PTR_WIDTH{1'b0}};
124: end else begin
125: fifo_rptr_sync <= gray2dec(fifo_rptr_gray_sync);
126: end
127:
128: //////////////////
129: // empty / full //
130: //////////////////
131:
132: assign full_wclk = (fifo_wptr == (fifo_rptr_sync ^ {1'b1,{(PTR_WIDTH-1){1'b0}}}));
133: assign full_rclk = (fifo_wptr_sync_combi == (fifo_rptr ^ {1'b1,{(PTR_WIDTH-1){1'b0}}}));
134:
135: // Current depth in the write clock side
136: logic wptr_msb;
137: logic rptr_sync_msb;
138: logic [PTRV_W-1:0] wptr_value;
139: logic [PTRV_W-1:0] rptr_sync_value;
140: assign wptr_msb = fifo_wptr[PTR_WIDTH-1];
141: assign rptr_sync_msb = fifo_rptr_sync[PTR_WIDTH-1];
142: assign wptr_value = fifo_wptr[0+:PTRV_W];
143: assign rptr_sync_value = fifo_rptr_sync[0+:PTRV_W];
144: assign wdepth = (full_wclk) ? DepthW'(Depth) :
145: (wptr_msb == rptr_sync_msb) ? DepthW'(wptr_value) - DepthW'(rptr_sync_value) :
146: (DepthW'(Depth) - DepthW'(rptr_sync_value) + DepthW'(wptr_value)) ;
147:
148: // Same again in the read clock side
149: assign empty = (fifo_wptr_sync_combi == fifo_rptr);
150: logic rptr_msb;
151: logic wptr_sync_msb;
152: logic [PTRV_W-1:0] rptr_value;
153: logic [PTRV_W-1:0] wptr_sync_value;
154: assign wptr_sync_msb = fifo_wptr_sync_combi[PTR_WIDTH-1];
155: assign rptr_msb = fifo_rptr[PTR_WIDTH-1];
156: assign wptr_sync_value = fifo_wptr_sync_combi[0+:PTRV_W];
157: assign rptr_value = fifo_rptr[0+:PTRV_W];
158: assign rdepth = (full_rclk) ? DepthW'(Depth) :
159: (wptr_sync_msb == rptr_msb) ? DepthW'(wptr_sync_value) - DepthW'(rptr_value) :
160: (DepthW'(Depth) - DepthW'(rptr_value) + DepthW'(wptr_sync_value)) ;
161:
162: /////////////
163: // storage //
164: /////////////
165:
166: logic [Width-1:0] storage [Depth];
167:
168: always_ff @(posedge clk_wr_i)
169: if (fifo_incr_wptr) begin
170: storage[fifo_wptr[PTR_WIDTH-2:0]] <= wdata;
171: end
172:
173: assign rdata = storage[fifo_rptr[PTR_WIDTH-2:0]];
174:
175: // gray code conversion functions. algorithm walks up from 0..N-1
176: // then flips the upper bit and walks down from N-1 to 0.
177:
178: function automatic [PTR_WIDTH-1:0] dec2gray(input logic [PTR_WIDTH-1:0] decval);
179: logic [PTR_WIDTH-1:0] decval_sub;
180: logic [PTR_WIDTH-2:0] decval_in;
181: logic unused_decval_msb;
182:
183: decval_sub = Depth - {1'b0,decval[PTR_WIDTH-2:0]} - 1'b1;
184:
185: {unused_decval_msb, decval_in} = decval[PTR_WIDTH-1] ? decval_sub : decval;
186: // Was done in two assigns for low bits and top bit
187: // but that generates a (bogus) verilator warning, so do in one assign
188: dec2gray = {decval[PTR_WIDTH-1],
189: {1'b0,decval_in[PTR_WIDTH-2:1]} ^ decval_in[PTR_WIDTH-2:0]};
190: endfunction
191:
192: function automatic [PTR_WIDTH-1:0] gray2dec(input logic [PTR_WIDTH-1:0] grayval);
193: logic [PTR_WIDTH-2:0] dec_tmp, dec_tmp_sub;
194: logic unused_decsub_msb;
195:
196: dec_tmp[PTR_WIDTH-2] = grayval[PTR_WIDTH-2];
197: for (int i = PTR_WIDTH-3; i >= 0; i--)
198: dec_tmp[i] = dec_tmp[i+1]^grayval[i];
199: {unused_decsub_msb, dec_tmp_sub} = Depth - {1'b0,dec_tmp} - 1'b1;
200: if (grayval[PTR_WIDTH-1])
201: gray2dec = {1'b1,dec_tmp_sub};
202: else
203: gray2dec = {1'b0,dec_tmp};
204: endfunction
205:
206: // TODO: assertions on full, empty, gray transitions
207:
208: endmodule
209: