// ============================================================================ // 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