/*****************************************************************************\ | Copyright (C) 2021-2022 Luke Wren | | SPDX-License-Identifier: Apache-2.0 | \*****************************************************************************/ // APB-to-APB asynchronous bridge for connecting DTM to DM, in case DTM is in // a different clock domain (e.g. running directly from crystal to get a // fixed baud reference) // // Note this module depends on the hazard3_sync_1bit module (a flop-chain // synchroniser) which should be reimplemented for your FPGA/process. `ifndef HAZARD3_REG_KEEP_ATTRIBUTE `define HAZARD3_REG_KEEP_ATTRIBUTE (* keep = 1'b1 *) `endif `default_nettype none module hazard3_apb_async_bridge #( parameter W_ADDR = 8, parameter W_DATA = 32, parameter N_SYNC_STAGES = 2 ) ( // Resets assumed to be synchronised externally input wire clk_src, input wire rst_n_src, input wire clk_dst, input wire rst_n_dst, // APB port from Transport Module input wire src_psel, input wire src_penable, input wire src_pwrite, input wire [W_ADDR-1:0] src_paddr, input wire [W_DATA-1:0] src_pwdata, output wire [W_DATA-1:0] src_prdata, output wire src_pready, output wire src_pslverr, // APB port to Debug Module output wire dst_psel, output wire dst_penable, output wire dst_pwrite, output wire [W_ADDR-1:0] dst_paddr, output wire [W_DATA-1:0] dst_pwdata, input wire [W_DATA-1:0] dst_prdata, input wire dst_pready, input wire dst_pslverr ); // ---------------------------------------------------------------------------- // Clock-crossing registers // We're using a modified req/ack handshake: // // - Initially both req and ack are low // - src asserts req high // - dst responds with ack high and begins transfer // - src deasserts req once it sees ack high // - dst deasserts ack once: // - transfer is complete *and* // - dst sees req deasserted // - Once src sees ack low, a new transfer can begin. // // A NRZI toggle handshake might be more appropriate, but can cause spurious // bus accesses when only one side of the link is reset. `HAZARD3_REG_KEEP_ATTRIBUTE reg src_req; wire dst_req; `HAZARD3_REG_KEEP_ATTRIBUTE reg dst_ack; wire src_ack; // Note the launch registers are not resettable. We maintain setup/hold on // launch-to-capture paths thanks to the req/ack handshake. A stray reset // could violate this. // // The req/ack logic itself can be reset safely because the receiving domain // is protected from metastability by a 2FF synchroniser. `HAZARD3_REG_KEEP_ATTRIBUTE reg [W_ADDR + W_DATA + 1 -1:0] src_paddr_pwdata_pwrite; // launch `HAZARD3_REG_KEEP_ATTRIBUTE reg [W_ADDR + W_DATA + 1 -1:0] dst_paddr_pwdata_pwrite; // capture `HAZARD3_REG_KEEP_ATTRIBUTE reg [W_DATA + 1 -1:0] dst_prdata_pslverr; // launch `HAZARD3_REG_KEEP_ATTRIBUTE reg [W_DATA + 1 -1:0] src_prdata_pslverr; // capture hazard3_sync_1bit #( .N_STAGES (N_SYNC_STAGES) ) sync_req ( .clk (clk_dst), .rst_n (rst_n_dst), .i (src_req), .o (dst_req) ); hazard3_sync_1bit #( .N_STAGES (N_SYNC_STAGES) ) sync_ack ( .clk (clk_src), .rst_n (rst_n_src), .i (dst_ack), .o (src_ack) ); // ---------------------------------------------------------------------------- // src state machine reg src_waiting_for_downstream; reg src_pready_r; always @ (posedge clk_src or negedge rst_n_src) begin if (!rst_n_src) begin src_req <= 1'b0; src_waiting_for_downstream <= 1'b0; src_prdata_pslverr <= {W_DATA + 1{1'b0}}; src_pready_r <= 1'b1; end else if (src_waiting_for_downstream) begin if (src_req && src_ack) begin // Request was acknowledged, so deassert. src_req <= 1'b0; end else if (!(src_req || src_ack)) begin // Downstream transfer has finished, data is valid. src_pready_r <= 1'b1; src_waiting_for_downstream <= 1'b0; // Note this assignment is cross-domain (but data has been stable // for duration of ack synchronisation delay): src_prdata_pslverr <= dst_prdata_pslverr; end end else begin // paddr, pwdata and pwrite are all valid during the setup phase, and // APB defines the setup phase to always last one cycle and proceed // to access phase. So, we can ignore penable, and pready is ignored. if (src_psel) begin src_pready_r <= 1'b0; src_req <= 1'b1; src_waiting_for_downstream <= 1'b1; end end end // Bus request launch register is not resettable always @ (posedge clk_src) begin if (src_psel && !src_waiting_for_downstream) src_paddr_pwdata_pwrite <= {src_paddr, src_pwdata, src_pwrite}; end assign {src_prdata, src_pslverr} = src_prdata_pslverr; assign src_pready = src_pready_r; // ---------------------------------------------------------------------------- // dst state machine wire dst_bus_finish = dst_penable && dst_pready; reg dst_psel_r; reg dst_penable_r; always @ (posedge clk_dst or negedge rst_n_dst) begin if (!rst_n_dst) begin dst_ack <= 1'b0; end else if (dst_req) begin dst_ack <= 1'b1; end else if (!dst_req && dst_ack && !dst_psel_r) begin dst_ack <= 1'b0; end end always @ (posedge clk_dst or negedge rst_n_dst) begin if (!rst_n_dst) begin dst_psel_r <= 1'b0; dst_penable_r <= 1'b0; end else if (dst_req && !dst_ack) begin dst_psel_r <= 1'b1; // Note this assignment is cross-domain. The src register has been // stable for the duration of the req sync delay. dst_paddr_pwdata_pwrite <= src_paddr_pwdata_pwrite; end else if (dst_psel_r && !dst_penable_r) begin dst_penable_r <= 1'b1; end else if (dst_bus_finish) begin dst_psel_r <= 1'b0; dst_penable_r <= 1'b0; end end // Bus response launch register is not resettable always @ (posedge clk_dst) begin if (dst_bus_finish) dst_prdata_pslverr <= {dst_prdata, dst_pslverr}; end assign dst_psel = dst_psel_r; assign dst_penable = dst_penable_r; assign {dst_paddr, dst_pwdata, dst_pwrite} = dst_paddr_pwdata_pwrite; endmodule `ifndef YOSYS `default_nettype wire `endif