// ============================================================================ // Async Router // ============================================================================ // // 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. // ============================================================================ `timescale 1ns/1ps module async_router #( parameter PACKET_W = 34, parameter COORD_BITS = 4, parameter FIFO_DEPTH = 16, parameter FIFO_PTR_BITS = 4 )( input wire clk, input wire rst_n, input wire [COORD_BITS-1:0] my_x, input wire [COORD_BITS-1:0] my_y, input wire local_in_valid, output wire local_in_ready, input wire [PACKET_W-1:0] local_in_data, output wire local_out_valid, input wire local_out_ready, output wire [PACKET_W-1:0] local_out_data, input wire north_in_valid, output wire north_in_ready, input wire [PACKET_W-1:0] north_in_data, output wire north_out_valid, input wire north_out_ready, output wire [PACKET_W-1:0] north_out_data, input wire south_in_valid, output wire south_in_ready, input wire [PACKET_W-1:0] south_in_data, output wire south_out_valid, input wire south_out_ready, output wire [PACKET_W-1:0] south_out_data, input wire east_in_valid, output wire east_in_ready, input wire [PACKET_W-1:0] east_in_data, output wire east_out_valid, input wire east_out_ready, output wire [PACKET_W-1:0] east_out_data, input wire west_in_valid, output wire west_in_ready, input wire [PACKET_W-1:0] west_in_data, output wire west_out_valid, input wire west_out_ready, output wire [PACKET_W-1:0] west_out_data, output wire idle ); localparam P_LOCAL = 0, P_NORTH = 1, P_SOUTH = 2, P_EAST = 3, P_WEST = 4; localparam DX_MSB = PACKET_W - 1; localparam DX_LSB = PACKET_W - COORD_BITS; localparam DY_MSB = DX_LSB - 1; localparam DY_LSB = DX_LSB - COORD_BITS; wire [4:0] fifo_empty, fifo_full; wire [PACKET_W-1:0] fifo_head [0:4]; wire [4:0] fifo_push; reg [4:0] fifo_pop; assign fifo_push[P_LOCAL] = local_in_valid && !fifo_full[P_LOCAL]; assign fifo_push[P_NORTH] = north_in_valid && !fifo_full[P_NORTH]; assign fifo_push[P_SOUTH] = south_in_valid && !fifo_full[P_SOUTH]; assign fifo_push[P_EAST] = east_in_valid && !fifo_full[P_EAST]; assign fifo_push[P_WEST] = west_in_valid && !fifo_full[P_WEST]; assign local_in_ready = !fifo_full[P_LOCAL]; assign north_in_ready = !fifo_full[P_NORTH]; assign south_in_ready = !fifo_full[P_SOUTH]; assign east_in_ready = !fifo_full[P_EAST]; assign west_in_ready = !fifo_full[P_WEST]; wire [PACKET_W-1:0] in_data [0:4]; assign in_data[P_LOCAL] = local_in_data; assign in_data[P_NORTH] = north_in_data; assign in_data[P_SOUTH] = south_in_data; assign in_data[P_EAST] = east_in_data; assign in_data[P_WEST] = west_in_data; genvar gi; generate for (gi = 0; gi < 5; gi = gi + 1) begin : gen_fifo spike_fifo #( .ID_WIDTH (PACKET_W), .DEPTH (FIFO_DEPTH), .PTR_BITS (FIFO_PTR_BITS) ) input_fifo ( .clk (clk), .rst_n (rst_n), .push (fifo_push[gi]), .pop (fifo_pop[gi]), .clear (1'b0), .push_data (in_data[gi]), .pop_data (fifo_head[gi]), .empty (fifo_empty[gi]), .full (fifo_full[gi]) ); end endgenerate function [2:0] xy_route; input [COORD_BITS-1:0] dx, dy, cx, cy; begin if (dx > cx) xy_route = P_EAST; else if (dx < cx) xy_route = P_WEST; else if (dy > cy) xy_route = P_NORTH; else if (dy < cy) xy_route = P_SOUTH; else xy_route = P_LOCAL; end endfunction wire [2:0] head_route [0:4]; generate for (gi = 0; gi < 5; gi = gi + 1) begin : gen_route assign head_route[gi] = xy_route( fifo_head[gi][DX_MSB:DX_LSB], fifo_head[gi][DY_MSB:DY_LSB], my_x, my_y ); end endgenerate reg [4:0] out_valid_r; reg [PACKET_W-1:0] out_data_r [0:4]; wire [4:0] out_ready; assign out_ready[P_LOCAL] = local_out_ready; assign out_ready[P_NORTH] = north_out_ready; assign out_ready[P_SOUTH] = south_out_ready; assign out_ready[P_EAST] = east_out_ready; assign out_ready[P_WEST] = west_out_ready; assign local_out_valid = out_valid_r[P_LOCAL]; assign local_out_data = out_data_r[P_LOCAL]; assign north_out_valid = out_valid_r[P_NORTH]; assign north_out_data = out_data_r[P_NORTH]; assign south_out_valid = out_valid_r[P_SOUTH]; assign south_out_data = out_data_r[P_SOUTH]; assign east_out_valid = out_valid_r[P_EAST]; assign east_out_data = out_data_r[P_EAST]; assign west_out_valid = out_valid_r[P_WEST]; assign west_out_data = out_data_r[P_WEST]; reg [2:0] arb_ptr; reg [4:0] comb_grant; reg [4:0] comb_out_claim; always @(*) begin : grant_logic integer p, idx; comb_grant = 5'b0; comb_out_claim = 5'b0; for (p = 0; p < 5; p = p + 1) begin idx = arb_ptr + p; if (idx >= 5) idx = idx - 5; if (!fifo_empty[idx] && !comb_grant[idx]) begin if (!out_valid_r[head_route[idx]] && !comb_out_claim[head_route[idx]]) begin comb_grant[idx] = 1'b1; comb_out_claim[head_route[idx]] = 1'b1; end end end end always @(posedge clk or negedge rst_n) begin : seq_logic integer i; if (!rst_n) begin out_valid_r <= 5'b0; arb_ptr <= 3'd0; for (i = 0; i < 5; i = i + 1) out_data_r[i] <= {PACKET_W{1'b0}}; end else begin for (i = 0; i < 5; i = i + 1) if (out_valid_r[i] && out_ready[i]) out_valid_r[i] <= 1'b0; for (i = 0; i < 5; i = i + 1) begin if (comb_grant[i]) begin out_valid_r[head_route[i]] <= 1'b1; out_data_r[head_route[i]] <= fifo_head[i]; end end arb_ptr <= (arb_ptr == 3'd4) ? 3'd0 : arb_ptr + 3'd1; end end always @(*) fifo_pop = comb_grant; assign idle = (&fifo_empty) && !out_valid_r[P_NORTH] && !out_valid_r[P_SOUTH] && !out_valid_r[P_EAST] && !out_valid_r[P_WEST]; endmodule