blob: 17374532f376f3bc9e0a259f50a761273f6620d5 [file] [log] [blame]
Timothy Chen23d3bb52022-05-17 17:04:33 -07001// 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// Component handling register CDC
6
7`include "prim_assert.sv"
8
9// There are three handling scenarios.
10// 1. The register can only be updated by software.
11// 2. The register can be updated by both software and hardware.
12// 3. The register can only be updated by hardware.
13//
14// For the first scenario, hardware updates are completely ignored.
15// The software facing register (`src_q` in prim_reg_cdc) simply reflects
16// the value affected by software. Since there is no possibility the
17// register value can change otherwise, there is no need to sample or
18// do any other coordination between the two domains. In this case,
19// we use the gen_passthru block below.
20//
21// For the second scenario, one of 4 things can happen:
22// 1. A software update without conflict
23// 2. A hardware update without conflict
24// 3. A software update is initiated when a hardware update is in-flight
25// 4. A hardware update is initiated when a software update is in-flight
26//
27// For the first case, it behaves similarly to the gen_passthru scenario.
28//
29// For the second case, the hardware update indication and update value are
30// captured, and the intent to change is synchronized back to the software
31// domain. While this happens, other hardware updates are ignored. Any hardware
32// change during the update is then detected as a difference between the
33// transit register `dst_qs_o` and the current hardware register value `dst_qs_i`.
34// When this change is observed after the current handshake completes, another
35// handshake event is generated to bring the latest hardware value over to the
36// software domain.
37//
38// For the third case, if a hardware update event is already in progress, the
39// software event is held and not acknowledged. Once the hardware event completes,
40// then the software event proceeds through its normal updating process.
41//
42// For the forth case, if a hardware update event is received while a software
43// update is in progress, the hardware update is ignored, and the logic behaves
44// similarly to the second case. Specifically, after the software update completes,
45// a delta is observed between the transit register and the current hardware value,
46// and a new handshake event is generated.
47//
48// The thrid scenario can be folded into the second scenario. The only difference
49// is that of the 4 cases identified, only case 2 can happen since there is never a
50// software initiated update.
51
52module prim_reg_cdc_arb #(
53 parameter int DataWidth = 32,
54 parameter logic [DataWidth-1:0] ResetVal = 32'h0,
55 parameter bit DstWrReq = 0
56) (
57 input clk_src_i,
58 input rst_src_ni,
59 input clk_dst_i,
60 input rst_dst_ni,
61 // destination side acknowledging a software transaction
62 output logic src_ack_o,
63 // destination side requesting a source side update after
64 // after hw update
65 output logic src_update_o,
66 // input request from prim_reg_cdc
67 input dst_req_i,
68 // output request to prim_subreg
69 output logic dst_req_o,
70 input dst_update_i,
71 // ds allows us to sample the destination domain register
72 // one cycle earlier instead of waiting for it to be reflected
73 // in the qs.
74 // This is important because a genearl use case is that interrupts
75 // are captured alongside payloads from the destination domain into
76 // the source domain. If we rely on `qs` only, then it is very likely
77 // that the software observed value will be behind the interrupt
78 // assertion. If the destination clock is very slow, this can seem
79 // an error on the part of the hardware.
80 input [DataWidth-1:0] dst_ds_i,
81 input [DataWidth-1:0] dst_qs_i,
82 output logic [DataWidth-1:0] dst_qs_o
83);
84
85 typedef enum logic {
86 SelSwReq,
87 SelHwReq
88 } req_sel_e;
89
90 typedef enum logic [1:0] {
91 StIdle,
92 StWait
93 } state_e;
94
Timothy Chenf84f49a2022-08-25 19:21:10 -070095
96 // Only honor the incoming destinate update request if the incoming
97 // value is actually different from what is already completed in the
98 // handshake
99 logic dst_update;
100 assign dst_update = dst_update_i & (dst_qs_o != dst_ds_i);
101
Timothy Chen23d3bb52022-05-17 17:04:33 -0700102 if (DstWrReq) begin : gen_wr_req
103 logic dst_lat_q;
104 logic dst_lat_d;
105 logic dst_update_req;
106 logic dst_update_ack;
107 req_sel_e id_q;
108
109 state_e state_q, state_d;
110 // Make sure to indent the following later
111 always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin
112 if (!rst_dst_ni) begin
113 state_q <= StIdle;
114 end else begin
115 state_q <= state_d;
116 end
117 end
118
119 logic busy;
120 logic dst_req_q, dst_req;
121 always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin
122 if (!rst_dst_ni) begin
123 dst_req_q <= '0;
124 end else if (dst_req_q && dst_lat_d) begin
125 // if request is held, when the transaction starts,
126 // automatically clear.
127 // dst_lat_d is safe to used here because dst_req_q, if set,
128 // always has priority over other hardware based events.
129 dst_req_q <= '0;
130 end else if (dst_req_i && !dst_req_q && busy) begin
131 // if destination request arrives when a handshake event
132 // is already ongoing, hold on to request and send later
133 dst_req_q <= 1'b1;
134 end
135 end
136 assign dst_req = dst_req_q | dst_req_i;
137
138 // Hold data at the beginning of a transaction
139 always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin
140 if (!rst_dst_ni) begin
141 dst_qs_o <= ResetVal;
142 end else if (dst_lat_d) begin
143 dst_qs_o <= dst_ds_i;
144 end else if (dst_lat_q) begin
145 dst_qs_o <= dst_qs_i;
146 end
147 end
148
149 // Which type of transaction is being ack'd back?
150 // 0 - software initiated request
151 // 1 - hardware initiated request
152 // The id information is used by prim_reg_cdc to disambiguate
153 // simultaneous updates from software and hardware.
154 // See scenario 2 case 3 for an example of how this is handled.
155 always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin
156 if (!rst_dst_ni) begin
157 id_q <= SelSwReq;
158 end else if (dst_update_req && dst_update_ack) begin
159 id_q <= SelSwReq;
160 end else if (dst_req && dst_lat_d) begin
161 id_q <= SelSwReq;
Timothy Chen8c9f8ef2022-08-15 15:53:29 -0700162 end else if (!dst_req && dst_lat_d) begin
Timothy Chen23d3bb52022-05-17 17:04:33 -0700163 id_q <= SelHwReq;
164 end else if (dst_lat_q) begin
165 id_q <= SelHwReq;
166 end
167 end
168
Timothy Chen8c9f8ef2022-08-15 15:53:29 -0700169 // if a destination update is received when the system is idle and there is no
170 // software side request, hw update must be selected.
Timothy Chenf84f49a2022-08-25 19:21:10 -0700171 `ASSERT(DstUpdateReqCheck_A, ##1 dst_update & !dst_req & !busy |=> id_q == SelHwReq,
Timothy Chen8c9f8ef2022-08-15 15:53:29 -0700172 clk_dst_i, !rst_dst_ni)
173
174 // if hw select was chosen, then it must be the case there was a destination update
Timothy Chenb20a33c2022-08-24 08:12:27 -0700175 // indication or there was a difference between the transit register and the
176 // latest incoming value.
177 `ASSERT(HwIdSelCheck_A, $rose(id_q == SelHwReq) |-> $past(dst_update_i, 1) ||
178 $past(dst_lat_q, 1),
Timothy Chen8c9f8ef2022-08-15 15:53:29 -0700179 clk_dst_i, !rst_dst_ni)
180
181
Timothy Chen23d3bb52022-05-17 17:04:33 -0700182 // send out prim_subreg request only when proceeding
183 // with software request
184 assign dst_req_o = ~busy & dst_req;
185
186 logic dst_hold_req;
187 always_comb begin
188 state_d = state_q;
189 dst_hold_req = '0;
190
191 // depending on when the request is received, we
192 // may latch d or q.
193 dst_lat_q = '0;
194 dst_lat_d = '0;
195
196 busy = 1'b1;
197
198 unique case (state_q)
199 StIdle: begin
200 busy = '0;
201 if (dst_req) begin
202 // there's a software issued request for change
203 state_d = StWait;
204 dst_lat_d = 1'b1;
Timothy Chenf84f49a2022-08-25 19:21:10 -0700205 end else if (dst_update) begin
Timothy Chen23d3bb52022-05-17 17:04:33 -0700206 state_d = StWait;
207 dst_lat_d = 1'b1;
208 end else if (dst_qs_o != dst_qs_i) begin
209 // there's a direct destination update
210 // that was blocked by an ongoing transaction
211 state_d = StWait;
212 dst_lat_q = 1'b1;
213 end
214 end
215
216 StWait: begin
217 dst_hold_req = 1'b1;
218 if (dst_update_ack) begin
219 state_d = StIdle;
220 end
221 end
222
223 default: begin
224 state_d = StIdle;
225 end
226 endcase // unique case (state_q)
227 end // always_comb
228
229 assign dst_update_req = dst_hold_req | dst_lat_d | dst_lat_q;
230 logic src_req;
231 prim_sync_reqack u_dst_update_sync (
232 .clk_src_i(clk_dst_i),
233 .rst_src_ni(rst_dst_ni),
234 .clk_dst_i(clk_src_i),
235 .rst_dst_ni(rst_src_ni),
236 .req_chk_i(1'b1),
237 .src_req_i(dst_update_req),
238 .src_ack_o(dst_update_ack),
239 .dst_req_o(src_req),
240 // immediate ack
241 .dst_ack_i(src_req)
242 );
243
244 assign src_ack_o = src_req & (id_q == SelSwReq);
245 assign src_update_o = src_req & (id_q == SelHwReq);
246
Timothy Chen8c9f8ef2022-08-15 15:53:29 -0700247 // once hardware makes an update request, we must eventually see an update pulse
Timothy Chen19a07f82022-08-19 12:51:24 -0700248 `ASSERT(ReqTimeout_A, $rose(id_q == SelHwReq) |-> s_eventually(src_update_o),
Timothy Chen8c9f8ef2022-08-15 15:53:29 -0700249 clk_src_i, !rst_src_ni)
Canberk Topal0feb39a2022-08-02 17:03:14 +0100250
Timothy Chen34525532022-08-10 16:59:56 -0700251 `ifdef INC_ASSERT
252 logic async_flag;
253 always_ff @(posedge clk_dst_i or negedge rst_dst_ni or posedge src_update_o) begin
254 if (!rst_dst_ni) begin
255 async_flag <= '0;
256 end else if (src_update_o) begin
257 async_flag <= '0;
Timothy Chenf84f49a2022-08-25 19:21:10 -0700258 end else if (dst_update && !dst_req_o && !busy) begin
Timothy Chen34525532022-08-10 16:59:56 -0700259 async_flag <= 1'b1;
260 end
261 end
262
263 // once hardware makes an update request, we must eventually see an update pulse
Timothy Chen19a07f82022-08-19 12:51:24 -0700264 `ASSERT(UpdateTimeout_A, $rose(async_flag) |-> s_eventually(src_update_o),
Timothy Chen34525532022-08-10 16:59:56 -0700265 clk_src_i, !rst_src_ni)
266 `endif
267
Timothy Chen23d3bb52022-05-17 17:04:33 -0700268 end else begin : gen_passthru
269 // when there is no possibility of conflicting HW transactions,
270 // we can assume that dst_qs_i will only ever take on the value
271 // that is directly related to the transaction. As a result,
272 // there is no need to latch further, and the end destination
273 // can in fact be used as the holding register.
274 assign dst_qs_o = dst_qs_i;
275 assign dst_req_o = dst_req_i;
276
277 // since there are no hw transactions, src_update_o is always '0
278 assign src_update_o = '0;
279
280 prim_pulse_sync u_dst_to_src_ack (
281 .clk_src_i(clk_dst_i),
282 .rst_src_ni(rst_dst_ni),
283 .clk_dst_i(clk_src_i),
284 .rst_dst_ni(rst_src_ni),
285 .src_pulse_i(dst_req_i),
286 .dst_pulse_o(src_ack_o)
287 );
288
289 logic unused_sigs;
Timothy Chenf84f49a2022-08-25 19:21:10 -0700290 assign unused_sigs = |{dst_ds_i, dst_update};
Timothy Chen23d3bb52022-05-17 17:04:33 -0700291 end
292
293
Timothy Chen34525532022-08-10 16:59:56 -0700294
Timothy Chen23d3bb52022-05-17 17:04:33 -0700295endmodule