catalyst-n1 / rtl /chip_link.v
mrwabbit's picture
Initial upload: Catalyst N1 open source neuromorphic processor RTL
e4cdd5f verified
// ============================================================================
// Chip Link
// ============================================================================
//
// 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 chip_link #(
parameter CORE_ID_BITS = 7,
parameter NEURON_BITS = 10,
parameter DATA_WIDTH = 16,
parameter TX_DEPTH = 256,
parameter RX_DEPTH = 256
)(
input wire clk,
input wire rst_n,
input wire tx_push,
input wire [CORE_ID_BITS-1:0] tx_core,
input wire [NEURON_BITS-1:0] tx_neuron,
input wire [7:0] tx_payload,
output wire tx_full,
output wire [CORE_ID_BITS-1:0] rx_core,
output wire [NEURON_BITS-1:0] rx_neuron,
output wire signed [DATA_WIDTH-1:0] rx_current,
input wire rx_pop,
output wire rx_empty,
output reg [7:0] link_tx_data,
output reg link_tx_valid,
input wire link_tx_ready,
input wire [7:0] link_rx_data,
input wire link_rx_valid,
output wire link_rx_ready
);
localparam TX_PKT_W = CORE_ID_BITS + NEURON_BITS + 8;
reg [TX_PKT_W-1:0] tx_fifo [0:TX_DEPTH-1];
reg [8:0] tx_wr_ptr, tx_rd_ptr;
wire [8:0] tx_count = tx_wr_ptr - tx_rd_ptr;
wire tx_empty_i = (tx_wr_ptr == tx_rd_ptr);
assign tx_full = (tx_count >= TX_DEPTH);
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
tx_wr_ptr <= 0;
else if (tx_push && !tx_full) begin
tx_fifo[tx_wr_ptr[7:0]] <= {tx_core, tx_neuron, tx_payload};
tx_wr_ptr <= tx_wr_ptr + 1;
end
end
localparam TX_IDLE = 2'd0, TX_BYTE1 = 2'd1, TX_BYTE2 = 2'd2, TX_BYTE3 = 2'd3;
reg [1:0] tx_state;
reg [TX_PKT_W-1:0] tx_pkt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
tx_state <= TX_IDLE;
tx_rd_ptr <= 0;
link_tx_valid <= 0;
link_tx_data <= 0;
end else begin
link_tx_valid <= 0;
case (tx_state)
TX_IDLE: begin
if (!tx_empty_i && link_tx_ready) begin
tx_pkt <= tx_fifo[tx_rd_ptr[7:0]];
tx_rd_ptr <= tx_rd_ptr + 1;
link_tx_data <= 8'h80 | tx_fifo[tx_rd_ptr[7:0]][TX_PKT_W-1 -: CORE_ID_BITS];
link_tx_valid <= 1;
tx_state <= TX_BYTE1;
end
end
TX_BYTE1: begin
if (link_tx_ready) begin
link_tx_data <= tx_pkt[NEURON_BITS+7:10];
link_tx_valid <= 1;
tx_state <= TX_BYTE2;
end
end
TX_BYTE2: begin
if (link_tx_ready) begin
link_tx_data <= {tx_pkt[9:8], tx_pkt[7:2]};
link_tx_valid <= 1;
tx_state <= TX_BYTE3;
end
end
TX_BYTE3: begin
if (link_tx_ready) begin
link_tx_data <= {tx_pkt[1:0], 6'd0};
link_tx_valid <= 1;
tx_state <= TX_IDLE;
end
end
endcase
end
end
localparam RX_PKT_W = CORE_ID_BITS + NEURON_BITS + DATA_WIDTH;
localparam RX_IDLE = 2'd0, RX_BYTE1 = 2'd1, RX_BYTE2 = 2'd2, RX_BYTE3 = 2'd3;
reg [1:0] rx_state;
reg [CORE_ID_BITS-1:0] rx_pkt_core;
reg [NEURON_BITS-1:0] rx_pkt_neuron;
reg [7:0] rx_pkt_payload;
reg rx_push;
assign link_rx_ready = (rx_count < RX_DEPTH - 4);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rx_state <= RX_IDLE;
rx_push <= 0;
end else begin
rx_push <= 0;
case (rx_state)
RX_IDLE: begin
if (link_rx_valid && link_rx_data[7]) begin
rx_pkt_core <= link_rx_data[CORE_ID_BITS-1:0];
rx_state <= RX_BYTE1;
end
end
RX_BYTE1: begin
if (link_rx_valid) begin
rx_pkt_neuron[NEURON_BITS-1:2] <= link_rx_data;
rx_state <= RX_BYTE2;
end
end
RX_BYTE2: begin
if (link_rx_valid) begin
rx_pkt_neuron[1:0] <= link_rx_data[7:6];
rx_pkt_payload[7:2] <= link_rx_data[5:0];
rx_state <= RX_BYTE3;
end
end
RX_BYTE3: begin
if (link_rx_valid) begin
rx_pkt_payload[1:0] <= link_rx_data[7:6];
rx_push <= 1;
rx_state <= RX_IDLE;
end
end
endcase
end
end
reg [RX_PKT_W-1:0] rx_fifo [0:RX_DEPTH-1];
reg [8:0] rx_wr_ptr, rx_rd_ptr;
wire [8:0] rx_count = rx_wr_ptr - rx_rd_ptr;
assign rx_empty = (rx_wr_ptr == rx_rd_ptr);
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
rx_wr_ptr <= 0;
else if (rx_push && rx_count < RX_DEPTH) begin
rx_fifo[rx_wr_ptr[7:0]] <= {rx_pkt_core, rx_pkt_neuron,
{{(DATA_WIDTH-8){1'b0}}, rx_pkt_payload}};
rx_wr_ptr <= rx_wr_ptr + 1;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
rx_rd_ptr <= 0;
else if (rx_pop && !rx_empty)
rx_rd_ptr <= rx_rd_ptr + 1;
end
wire [RX_PKT_W-1:0] rx_top = rx_fifo[rx_rd_ptr[7:0]];
assign rx_core = rx_top[RX_PKT_W-1 -: CORE_ID_BITS];
assign rx_neuron = rx_top[DATA_WIDTH +: NEURON_BITS];
assign rx_current = rx_top[DATA_WIDTH-1:0];
endmodule