// ============================================================================ // AXI-UART Bridge // ============================================================================ // // Copyright 2026 Henry Arthur Shulayev Barnes / Catalyst Neuromorphic Ltd // Company No. 17054540 — UK Patent Application No. 2602902.6 // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ============================================================================ module axi_uart_bridge #( parameter VERSION_ID = 32'hF2_02_03_10, parameter NUM_CORES = 16 )( input wire clk, input wire rst_n, input wire clk_neuro, input wire rst_neuro_n, input wire [31:0] s_axi_awaddr, input wire s_axi_awvalid, output reg s_axi_awready, input wire [31:0] s_axi_wdata, input wire [3:0] s_axi_wstrb, input wire s_axi_wvalid, output reg s_axi_wready, output reg [1:0] s_axi_bresp, output reg s_axi_bvalid, input wire s_axi_bready, input wire [31:0] s_axi_araddr, input wire s_axi_arvalid, output reg s_axi_arready, output reg [31:0] s_axi_rdata, output reg [1:0] s_axi_rresp, output reg s_axi_rvalid, input wire s_axi_rready, output reg [7:0] hi_rx_data, output reg hi_rx_valid, input wire [7:0] hi_tx_data, input wire hi_tx_valid, output wire hi_tx_ready ); localparam REG_TX_DATA = 3'd0; localparam REG_TX_STATUS = 3'd1; localparam REG_RX_DATA = 3'd2; localparam REG_RX_STATUS = 3'd3; localparam REG_CONTROL = 3'd4; localparam REG_VERSION = 3'd5; localparam REG_SCRATCH = 3'd6; localparam REG_CORE_COUNT = 3'd7; wire tx_wr_full; wire tx_rd_empty; wire [7:0] tx_rd_data; reg tx_rd_en; reg tx_wr_en; reg [7:0] tx_wr_data; async_fifo #(.DATA_WIDTH(8), .ADDR_BITS(5)) u_tx_fifo ( .wr_clk (clk), .wr_rst_n (rst_n), .wr_data (tx_wr_data), .wr_en (tx_wr_en), .wr_full (tx_wr_full), .rd_clk (clk_neuro), .rd_rst_n (rst_neuro_n), .rd_en (tx_rd_en), .rd_data (tx_rd_data), .rd_empty (tx_rd_empty) ); wire rx_wr_full; wire rx_rd_empty; wire [7:0] rx_rd_data; reg rx_rd_en; reg rx_wr_en; reg [7:0] rx_wr_data; async_fifo #(.DATA_WIDTH(8), .ADDR_BITS(5)) u_rx_fifo ( .wr_clk (clk_neuro), .wr_rst_n (rst_neuro_n), .wr_data (rx_wr_data), .wr_en (rx_wr_en), .wr_full (rx_wr_full), .rd_clk (clk), .rd_rst_n (rst_n), .rd_en (rx_rd_en), .rd_data (rx_rd_data), .rd_empty (rx_rd_empty) ); always @(posedge clk_neuro or negedge rst_neuro_n) begin if (!rst_neuro_n) begin hi_rx_data <= 8'd0; hi_rx_valid <= 1'b0; tx_rd_en <= 1'b0; end else begin hi_rx_valid <= 1'b0; tx_rd_en <= 1'b0; if (!tx_rd_empty && !hi_rx_valid) begin hi_rx_data <= tx_rd_data; hi_rx_valid <= 1'b1; tx_rd_en <= 1'b1; end end end reg [1:0] rx_holdoff; reg tx_ready_prev; wire internal_tx_ready = ~rx_wr_full & (rx_holdoff == 0); wire tx_ready_rising = internal_tx_ready & ~tx_ready_prev; wire do_rx_capture = hi_tx_valid & internal_tx_ready & ~tx_ready_rising; assign hi_tx_ready = internal_tx_ready; always @(posedge clk_neuro or negedge rst_neuro_n) begin if (!rst_neuro_n) begin rx_holdoff <= 2'd0; tx_ready_prev <= 1'b1; rx_wr_en <= 1'b0; rx_wr_data <= 8'd0; end else begin tx_ready_prev <= internal_tx_ready; rx_wr_en <= 1'b0; if (rx_holdoff != 0) rx_holdoff <= rx_holdoff - 1; if (do_rx_capture) begin rx_wr_data <= hi_tx_data; rx_wr_en <= 1'b1; rx_holdoff <= 2'd2; end end end reg [31:0] scratch_reg; localparam S_IDLE = 2'd0; localparam S_WRITE_RESP = 2'd1; localparam S_READ_RESP = 2'd2; reg [1:0] axi_state; reg [2:0] wr_reg_addr; reg [31:0] wr_data_reg; reg [2:0] rd_reg_addr; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin axi_state <= S_IDLE; s_axi_awready <= 1'b0; s_axi_wready <= 1'b0; s_axi_bvalid <= 1'b0; s_axi_bresp <= 2'b00; s_axi_arready <= 1'b0; s_axi_rvalid <= 1'b0; s_axi_rdata <= 32'd0; s_axi_rresp <= 2'b00; scratch_reg <= 32'd0; wr_reg_addr <= 3'd0; wr_data_reg <= 32'd0; rd_reg_addr <= 3'd0; tx_wr_en <= 1'b0; tx_wr_data <= 8'd0; rx_rd_en <= 1'b0; end else begin tx_wr_en <= 1'b0; rx_rd_en <= 1'b0; case (axi_state) S_IDLE: begin s_axi_bvalid <= 1'b0; s_axi_rvalid <= 1'b0; if (s_axi_awvalid && s_axi_wvalid) begin s_axi_awready <= 1'b1; s_axi_wready <= 1'b1; wr_reg_addr <= s_axi_awaddr[4:2]; wr_data_reg <= s_axi_wdata; axi_state <= S_WRITE_RESP; end else if (s_axi_arvalid) begin s_axi_arready <= 1'b1; rd_reg_addr <= s_axi_araddr[4:2]; axi_state <= S_READ_RESP; end end S_WRITE_RESP: begin s_axi_awready <= 1'b0; s_axi_wready <= 1'b0; if (!s_axi_bvalid) begin case (wr_reg_addr) REG_TX_DATA: begin if (!tx_wr_full) begin tx_wr_data <= wr_data_reg[7:0]; tx_wr_en <= 1'b1; end end REG_SCRATCH: scratch_reg <= wr_data_reg; default: ; endcase s_axi_bvalid <= 1'b1; s_axi_bresp <= 2'b00; end if (s_axi_bvalid && s_axi_bready) axi_state <= S_IDLE; end S_READ_RESP: begin s_axi_arready <= 1'b0; if (!s_axi_rvalid) begin case (rd_reg_addr) REG_TX_DATA: s_axi_rdata <= 32'd0; REG_TX_STATUS: s_axi_rdata <= {31'd0, ~tx_wr_full}; REG_RX_DATA: begin if (!rx_rd_empty) begin s_axi_rdata <= {24'd0, rx_rd_data}; rx_rd_en <= 1'b1; end else begin s_axi_rdata <= 32'd0; end end REG_RX_STATUS: s_axi_rdata <= {31'd0, ~rx_rd_empty}; REG_CONTROL: s_axi_rdata <= 32'd0; REG_VERSION: s_axi_rdata <= VERSION_ID; REG_SCRATCH: s_axi_rdata <= scratch_reg; REG_CORE_COUNT: s_axi_rdata <= NUM_CORES; endcase s_axi_rvalid <= 1'b1; s_axi_rresp <= 2'b00; end if (s_axi_rvalid && s_axi_rready) axi_state <= S_IDLE; end default: axi_state <= S_IDLE; endcase end end endmodule