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