Timothy Chen | 23d3bb5 | 2022-05-17 17:04:33 -0700 | [diff] [blame] | 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 | // 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 | |
| 52 | module 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 Chen | f84f49a | 2022-08-25 19:21:10 -0700 | [diff] [blame] | 95 | |
| 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 Chen | 23d3bb5 | 2022-05-17 17:04:33 -0700 | [diff] [blame] | 102 | 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 Chen | 8c9f8ef | 2022-08-15 15:53:29 -0700 | [diff] [blame] | 162 | end else if (!dst_req && dst_lat_d) begin |
Timothy Chen | 23d3bb5 | 2022-05-17 17:04:33 -0700 | [diff] [blame] | 163 | id_q <= SelHwReq; |
| 164 | end else if (dst_lat_q) begin |
| 165 | id_q <= SelHwReq; |
| 166 | end |
| 167 | end |
| 168 | |
Timothy Chen | 8c9f8ef | 2022-08-15 15:53:29 -0700 | [diff] [blame] | 169 | // 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 Chen | f84f49a | 2022-08-25 19:21:10 -0700 | [diff] [blame] | 171 | `ASSERT(DstUpdateReqCheck_A, ##1 dst_update & !dst_req & !busy |=> id_q == SelHwReq, |
Timothy Chen | 8c9f8ef | 2022-08-15 15:53:29 -0700 | [diff] [blame] | 172 | 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 Chen | b20a33c | 2022-08-24 08:12:27 -0700 | [diff] [blame] | 175 | // 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 Chen | 8c9f8ef | 2022-08-15 15:53:29 -0700 | [diff] [blame] | 179 | clk_dst_i, !rst_dst_ni) |
| 180 | |
| 181 | |
Timothy Chen | 23d3bb5 | 2022-05-17 17:04:33 -0700 | [diff] [blame] | 182 | // 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 Chen | f84f49a | 2022-08-25 19:21:10 -0700 | [diff] [blame] | 205 | end else if (dst_update) begin |
Timothy Chen | 23d3bb5 | 2022-05-17 17:04:33 -0700 | [diff] [blame] | 206 | 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 Chen | 8c9f8ef | 2022-08-15 15:53:29 -0700 | [diff] [blame] | 247 | // once hardware makes an update request, we must eventually see an update pulse |
Timothy Chen | 19a07f8 | 2022-08-19 12:51:24 -0700 | [diff] [blame] | 248 | `ASSERT(ReqTimeout_A, $rose(id_q == SelHwReq) |-> s_eventually(src_update_o), |
Timothy Chen | 8c9f8ef | 2022-08-15 15:53:29 -0700 | [diff] [blame] | 249 | clk_src_i, !rst_src_ni) |
Canberk Topal | 0feb39a | 2022-08-02 17:03:14 +0100 | [diff] [blame] | 250 | |
Timothy Chen | 3452553 | 2022-08-10 16:59:56 -0700 | [diff] [blame] | 251 | `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 Chen | f84f49a | 2022-08-25 19:21:10 -0700 | [diff] [blame] | 258 | end else if (dst_update && !dst_req_o && !busy) begin |
Timothy Chen | 3452553 | 2022-08-10 16:59:56 -0700 | [diff] [blame] | 259 | async_flag <= 1'b1; |
| 260 | end |
| 261 | end |
| 262 | |
| 263 | // once hardware makes an update request, we must eventually see an update pulse |
Timothy Chen | 19a07f8 | 2022-08-19 12:51:24 -0700 | [diff] [blame] | 264 | `ASSERT(UpdateTimeout_A, $rose(async_flag) |-> s_eventually(src_update_o), |
Timothy Chen | 3452553 | 2022-08-10 16:59:56 -0700 | [diff] [blame] | 265 | clk_src_i, !rst_src_ni) |
| 266 | `endif |
| 267 | |
Timothy Chen | 23d3bb5 | 2022-05-17 17:04:33 -0700 | [diff] [blame] | 268 | 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 Chen | f84f49a | 2022-08-25 19:21:10 -0700 | [diff] [blame] | 290 | assign unused_sigs = |{dst_ds_i, dst_update}; |
Timothy Chen | 23d3bb5 | 2022-05-17 17:04:33 -0700 | [diff] [blame] | 291 | end |
| 292 | |
| 293 | |
Timothy Chen | 3452553 | 2022-08-10 16:59:56 -0700 | [diff] [blame] | 294 | |
Timothy Chen | 23d3bb5 | 2022-05-17 17:04:33 -0700 | [diff] [blame] | 295 | endmodule |