catalyst-n1 / rtl /axi_uart_bridge.v
mrwabbit's picture
Initial upload: Catalyst N1 open source neuromorphic processor RTL
e4cdd5f verified
// ============================================================================
// 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