blob: 54df874e862d6f921af3a3fb14f9a85de52efc11 [file] [log] [blame]
//
// Copyright 2022 Tobias Strauch, Munich, Bavaria
//
// SPDX-FileCopyrightText: 2020 Efabless Corporation
//
// 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.
// SPDX-License-Identifier: Apache-2.0
//
// Simple calendar (ca) implementation.
// Goal is to have a working calendar first.
// Timing optimizations second.
// C-slow-retiming to enable multithreaded access not implemented yet.
// Calendar time bit width = 23;
//
module ca (clk_const,
rstn,
test_overflow,
ca_dbus_valid,
ca_dbus_ack,
ca_dbus_com,
ca_dbus_tid,
ca_dbus_data,
ca_time_valid,
ca_time_ack,
ca_time_data,
ca_match_valid,
ca_match_ack,
ca_command,
cubev_ca_wea,
cubev_ca_addra,
cubev_ca_dina,
cubev_ca_douta,
cubev_ca_addrb,
cubev_ca_doutb);
parameter CA_COM = 32'h80001000;
parameter CA_ET = 32'h80001004;
parameter CA_CT = 19'h01008;
input clk_const;
input rstn;
input test_overflow;
input ca_dbus_valid;
output ca_dbus_ack;
input ca_dbus_com;
input [2:0] ca_dbus_tid;
input [31:0] ca_dbus_data;
output ca_time_valid;
input ca_time_ack;
output [22:0] ca_time_data;
output ca_match_valid;
input ca_match_ack;
output [31:0] ca_command;
output cubev_ca_wea;
output [8:0] cubev_ca_addra;
output [31:0] cubev_ca_dina;
input [31:0] cubev_ca_douta;
output [8:0] cubev_ca_addrb;
input [31:0] cubev_ca_doutb;
//wire [32:0] ca_wr_douta;
reg ca_wr_wea;
reg [8:0] ca_wr_add;
reg [8:0] ca_wr_add_start;
reg ca_wr_add_start_marker;
reg [8:0] ca_wr_add_fill;
reg [8:0] ca_wr_add_fill_next;
reg ca_end_of_wr_list;
reg [8:0] ca_wr_add_ptr;
reg [32:0] ca_wr_dina;
//wire [32:0] ca_rd_doutb;
reg [5:0] ca_wr_fsm_state;
reg ca_ready;
reg [22:0] ca_insert_time;
reg [8:0] ca_rd_add;
reg [3:0] ca_rd_fsm_state;
//reg [2:0] ca_rd_delay;
reg ca_update_rd_add;
reg [31:0] ca_command;
reg ca_wr_sync_update;
reg ca_wr_sync_update_done;
reg ca_rd_doutb_31_23;
///////////////////////////////////////////////////
// handling time counter
// and transfer to user clock
///////////////////////////////////////////////////
//reg [22:0] ca_time;
reg [22:0] ca_time_const;
reg [22:0] ca_time_data;
reg ca_time_valid;
reg hs_ready_meta;
reg hs_valid;
always @ (posedge clk_const or negedge rstn)
if (~rstn) begin
ca_time_const <= 0;
ca_time_valid <= 0;
ca_time_data <= 0;
hs_valid <= 0;
hs_ready_meta <= 1;
end else begin
case (ca_time_valid)
0: if (hs_ready_meta) begin
hs_valid <= 1'b1;
ca_time_data <= ca_time_const;
ca_time_valid <= 1'b1;
end
1: if (!hs_ready_meta) begin
ca_time_valid <= 1'b0;
hs_valid <= 1'b0;
end
endcase
ca_time_const <= ca_time_const + 1;
hs_ready_meta <= ca_time_ack;
end
///////////////////////////////////////////////////
// handling write transfer
///////////////////////////////////////////////////
reg [1:0] hs_state_write_const;
reg ca_dbus_valid_meta;
reg ca_dbus_ack;
reg ca_wr_com_const;
reg ca_wr_et_const;
reg [31:0] hs_write_dbus_wr_data_const;
reg [2:0] hs_write_tid_wr0_const;
always @ (posedge clk_const or negedge rstn)
if (~rstn) begin
hs_state_write_const <= 0;
ca_wr_com_const <= 0;
ca_wr_et_const <= 0;
hs_write_dbus_wr_data_const <= 0;
hs_write_tid_wr0_const <= 0;
ca_dbus_ack <= 0;
ca_dbus_valid_meta <= 0;
end else begin
ca_wr_com_const <= 0;
ca_wr_et_const <= 0;
case (hs_state_write_const)
0: if (ca_dbus_valid_meta) begin
hs_state_write_const <= 1;
end
1: hs_state_write_const <= 2;
2: if (ca_ready) begin
hs_state_write_const <= 3;
ca_wr_com_const <= ca_dbus_com;
ca_wr_et_const <= !ca_dbus_com;
hs_write_dbus_wr_data_const <= ca_dbus_data;
hs_write_tid_wr0_const <= ca_dbus_tid;
ca_dbus_ack <= 1;
end
3: if (!ca_dbus_valid_meta) begin
hs_state_write_const <= 0;
ca_dbus_ack <= 0;
end
endcase
ca_dbus_valid_meta <= ca_dbus_valid;
end
///////////////////////////////////////////////////
// compare time
///////////////////////////////////////////////////
reg ca_insert_lesser_low;
reg ca_insert_lesser_high;
reg ca_insert_equal_high;
reg ca_compare_check;
always @ (posedge clk_const or negedge rstn)
if (~rstn) begin
ca_insert_lesser_low <= 0;
ca_insert_lesser_high <= 0;
ca_insert_equal_high <= 0;
ca_compare_check <= 0;
end else begin
if (test_overflow) begin
ca_insert_lesser_low <= ca_insert_time[5:0] < ca_wr_douta[5:0];
ca_insert_lesser_high <= ca_insert_time[11:6] < ca_wr_douta[11:6];
ca_insert_equal_high <= ca_insert_time[11:6] == ca_wr_douta[11:6];
ca_compare_check <= ca_insert_time[11:10] == ca_wr_douta[11:10];
if (((ca_wr_douta[11:10] == 2'h0) & (ca_insert_time[11:10] == 2'h3)) |
((ca_wr_douta[11:10] == 2'h1) & (ca_insert_time[11:10] == 2'h0)) |
((ca_wr_douta[11:10] == 2'h2) & (ca_insert_time[11:10] == 2'h1)) |
((ca_wr_douta[11:10] == 2'h3) & (ca_insert_time[11:10] == 2'h2)) ) begin
ca_insert_lesser_high <= 1'b1;
ca_compare_check <= 1'b1;
end
end else begin
ca_insert_lesser_low <= ca_insert_time[10:0] < ca_wr_douta[10:0];
ca_insert_lesser_high <= ca_insert_time[22:11] < ca_wr_douta[22:11];
ca_insert_equal_high <= ca_insert_time[22:11] == ca_wr_douta[22:11];
ca_compare_check <= ca_insert_time[22:21] == ca_wr_douta[22:21];
if (((ca_wr_douta[22:21] == 2'h0) & (ca_insert_time[22:21] == 2'h3)) |
((ca_wr_douta[22:21] == 2'h1) & (ca_insert_time[22:21] == 2'h0)) |
((ca_wr_douta[22:21] == 2'h2) & (ca_insert_time[22:21] == 2'h1)) |
((ca_wr_douta[22:21] == 2'h3) & (ca_insert_time[22:21] == 2'h2)) ) begin
ca_insert_lesser_high <= 1'b1;
ca_compare_check <= 1'b1;
end
end
end
wire ca_insert = ca_compare_check ?
(ca_insert_equal_high ? ca_insert_lesser_low : ca_insert_lesser_high) :
1'b0;
always @ (test_overflow or ca_wr_add_fill)
if ((test_overflow & (ca_wr_add_fill == 9'h02e)) |
//(!test_overflow & (ca_wr_add_fill == 510))) begin
(!test_overflow & (ca_wr_add_fill == 62))) begin
ca_wr_add_fill_next = 9'h010;
end else begin
ca_wr_add_fill_next = ca_wr_add_fill + 2;
end
///////////////////////////////////////////////////
// Central FSM
///////////////////////////////////////////////////
always @ (posedge clk_const or negedge rstn)
if (~rstn) begin
ca_wr_add <= 0;
ca_wr_wea <= 0;
ca_wr_dina <= 0;
ca_wr_add_start <= 0;
ca_wr_fsm_state <= 0;
ca_wr_add_start <= 0;
ca_wr_add_fill <= 0;
ca_wr_add_ptr <= 0;
ca_ready <= 0;
ca_insert_time <= 0;
ca_update_rd_add <= 0;
ca_wr_add_start_marker <= 0;
ca_end_of_wr_list <= 0;
ca_wr_sync_update_done <= 0;
end else begin
ca_wr_wea <= 1'b0;
case (ca_wr_fsm_state)
0: begin /////////////////////////////////////////////////// reset
ca_wr_wea <= 1'b1;
ca_wr_add_start <= 8'h010;
ca_wr_add_fill <= 8'h010;
//if (ca_wr_add == 511) begin
if (ca_wr_add == 63) begin
ca_wr_add <= 8'h010;
ca_wr_fsm_state <= 1;
ca_ready <= 1'b1;
end else
ca_wr_add <= ca_wr_add + 1;
end
1: begin /////////////////////////////////////////////////// wait
ca_ready <= 1'b1;
ca_wr_wea <= 1'b0;
ca_wr_add_start_marker <= 1'b0;
if (ca_wr_com_const) begin //////////////////////////////// handle COM write
ca_wr_add <= {6'h00, hs_write_tid_wr0_const};
ca_wr_dina <= {1'b0, hs_write_dbus_wr_data_const};
ca_wr_wea <= 1'b1;
end else if (ca_wr_et_const & ca_ready) begin // & ////////////////////////////// handle ET write when no match executed
ca_wr_add <= {6'h00, hs_write_tid_wr0_const}; //////////////////// read thread specific last command entry
ca_insert_time <= hs_write_dbus_wr_data_const[22:0];
ca_ready <= 1'b0;
ca_wr_fsm_state <= 2;
ca_wr_add_ptr <= ca_wr_add_start;
ca_wr_add_start_marker <= 1'b1;
end else if (ca_wr_sync_update) begin //////////////// clear entry and progress start ptr after match
ca_wr_add <= ca_wr_add_start;
ca_wr_dina[32:23] <= 0;
ca_wr_wea <= 1'b1;
if (ca_rd_doutb_31_23) //////////////////////////// read section points to start add
ca_wr_add_start <= ca_rd_doutb[31:23];
ca_wr_fsm_state <= 19;
ca_ready <= 1'b0;
end
end
2: begin ///////////////////////////////////////////////////
ca_wr_fsm_state <= 3;
end
3: begin ///////////////////////////////////////////////////
ca_wr_fsm_state <= 4;
ca_wr_add <= ca_wr_add_start;
end
4: begin /////////////////////////////////////////////////// write command at new entry
ca_wr_fsm_state <= 5;
ca_wr_add <= ca_wr_add_fill + 1;
ca_wr_dina <= ca_wr_douta; // command entry
ca_wr_wea <= 1'b1;
end
5: begin ///////////////////////////////////////////////////
ca_wr_fsm_state <= 6;
ca_wr_wea <= 1'b0;
ca_wr_add <= ca_wr_add_start;
end
6: begin /////////////////////////////////////////////////// make decision
if (!ca_wr_douta[32]) begin ////////////////////////// !ca_wr_douta[32]
ca_wr_add <= ca_wr_add_fill;
ca_wr_dina <= {10'h200, ca_insert_time};
ca_wr_wea <= 1'b1;
if (ca_wr_add_start_marker) begin /////// insert at start
ca_wr_add_start <= ca_wr_add_fill;
ca_update_rd_add <= 1'b1;
end
ca_wr_fsm_state <= 13;
ca_wr_add_fill <= ca_wr_add_fill_next;
end else begin
ca_wr_fsm_state <= 7;
ca_wr_dina <= {1'b1, ca_wr_add_ptr, ca_wr_douta[22:0]};
ca_wr_add_ptr <= ca_wr_add;
ca_wr_add <= ca_wr_douta[31:23];
end
end
7: begin /////////////////////////////////////////////////// check for insertion or appending
if (ca_end_of_wr_list & !ca_insert) begin
ca_wr_add <= ca_wr_add_ptr;
ca_wr_dina[31:23] <= ca_wr_add_fill;
ca_wr_wea <= 1'b1;
ca_wr_fsm_state <= 12;
ca_wr_add_ptr <= 0;
end else
if (ca_insert) begin ///////////////////////////////// insert
if (ca_wr_add_start_marker) begin /////// insert at start
ca_wr_dina <= {1'b1, ca_wr_add_start, ca_insert_time};
ca_wr_add <= ca_wr_add_fill;
ca_wr_wea <= 1'b1;
ca_wr_fsm_state <= 13;
ca_wr_add_start <= ca_wr_add_fill;
ca_update_rd_add <= 1'b1;
ca_wr_add_fill <= ca_wr_add_fill_next;
end else begin
ca_wr_add <= ca_wr_dina[31:23];
ca_wr_fsm_state <= 9;
end
end else begin /////////////////////////////////////// no insert, read next entry
ca_wr_fsm_state <= 8;
end
ca_wr_add_start_marker <= 1'b0;
end
8: begin /////////////////////////////////////////////////// delay due to read next entry
ca_wr_fsm_state <= 6;
end
9: begin ///////////////////////////////////////////////////
ca_wr_fsm_state <= 10;
end
10: begin ///////////////////////////////////////////////////
ca_wr_fsm_state <= 11;
end
11: begin //////////////////////////////////////////////////
ca_wr_dina <= {1'b1, ca_wr_add_fill, ca_wr_douta[22:0]};
ca_wr_wea <= 1'b1;
ca_wr_fsm_state <= 12;
end
12: begin //////////////////////////////////////////////////
ca_wr_add <= ca_wr_add_fill;
ca_wr_dina <= {1'b1, ca_wr_add_ptr, ca_insert_time};
ca_wr_wea <= 1'b1;
ca_wr_fsm_state <= 13;
ca_wr_add_fill <= ca_wr_add_fill_next;
end
13: begin //////////////////////////////////////////////////
ca_wr_add <= ca_wr_add_fill;
ca_wr_fsm_state <= 14;
ca_wr_wea <= 1'b0;
ca_update_rd_add <= 1'b0;
end
14: begin //////////////////////////////////////////////////
ca_wr_fsm_state <= 15;
end
15: begin //////////////////////////////////////////////////
ca_wr_fsm_state <= 16;
end
16: if (ca_wr_douta[32]) begin ////////////////////////// ca_wr_douta[32]
ca_wr_fsm_state <= 13;
ca_wr_add_fill <= ca_wr_add_fill_next;
end else begin
ca_wr_fsm_state <= 1;
ca_ready <= 1'b1;
end
19: begin //////////////////////////////////////////////////
ca_wr_add <= ca_wr_add_start;
ca_wr_wea <= 1'b0;
ca_wr_sync_update_done <= 1'b1;
if (!ca_wr_sync_update) begin
ca_wr_sync_update_done <= 1'b0;
ca_wr_fsm_state <= 1;
ca_ready <= 1'b1;
end
end
default : ;
endcase
ca_end_of_wr_list <= ca_wr_douta[31:23] == 0;
end
///////////////////////////////////////////////////
// memory handling
///////////////////////////////////////////////////
wire [32:0] ca_wr_dina_33 = {1'b0, ca_wr_dina[32], ca_wr_dina[30:0]};
wire [32:0] ca_wr_douta_33;
wire [32:0] ca_rd_doutb_33;
reg [32:0] ca_wr_douta;
reg [32:0] ca_rd_doutb;
always @ (posedge clk_const) begin
ca_wr_douta <= {ca_wr_douta_33[31], 1'b0, ca_wr_douta_33[30:0]};
ca_rd_doutb <= {ca_rd_doutb_33[31], 1'b0, ca_rd_doutb_33[30:0]};
end
assign cubev_ca_wea = ~ca_wr_wea;
assign cubev_ca_addra = ca_wr_add;
assign cubev_ca_dina = ca_wr_dina_33;
assign ca_wr_douta_33 = cubev_ca_douta;
assign cubev_ca_addrb = ca_rd_add;
assign ca_rd_doutb_33 = cubev_ca_doutb;
///////////////////////////////////////////////////
// get match
///////////////////////////////////////////////////
// test_overflow: late-bits: 8
// !test_overflow: late-bits: 12
reg ca_match_check;
reg [22:0] ca_time_diff;
reg time_dif_neg;
always @ (posedge clk_const or negedge rstn)
if (~rstn) begin
ca_match_check <= 0;
ca_time_diff <= 0;
time_dif_neg <= 0;
end else
begin
if (test_overflow) begin
time_dif_neg <= ca_time_diff[11:8] == 4'hf;
if ((ca_time_const[11:8] == 0) &
(ca_rd_doutb[11:8] == 4'hf)) ///////////////// overflow
ca_match_check <= 1'b0;
else
ca_match_check <= 1'b1;
ca_time_diff[11:0] <= ca_rd_doutb[11:0] - ca_time_const[11:0];
end else begin
time_dif_neg <= ca_time_diff[22:12] == 11'h7ff;
if ((ca_time_const[22:12] == 0) &
(ca_rd_doutb[22:12] == 11'h7ff)) ///////////////// overflow
ca_match_check <= 1'b0;
else
ca_match_check <= 1'b1;
ca_time_diff <= ca_rd_doutb[22:0] - ca_time_const;
end
end
wire ca_match_int = ca_rd_doutb_32 &
((ca_match_check) ?
((test_overflow & (ca_time_diff[11:8] == 4'hf)) |
(!test_overflow & (ca_time_diff[22:12] == 11'h7ff))) :
1'b1);
///////////////////////////////////////////////////
// handling memory read
///////////////////////////////////////////////////
reg ca_rd_doutb_32;
reg ca_match_req;
reg ca_end_of_rd_list;
always @ (posedge clk_const or negedge rstn)
if (~rstn) begin
ca_rd_add <= 0;
ca_rd_fsm_state <= 0;
ca_rd_doutb_32 <= 0;
ca_match_req <= 0;
ca_command <= 0;
ca_end_of_rd_list <= 0;
ca_wr_sync_update <= 0;
ca_rd_doutb_31_23 <= 0;
end else begin
ca_rd_doutb_32 <= ca_rd_doutb[32];
ca_match_req <= 0;
ca_rd_doutb_31_23 <= (ca_rd_doutb[31:23] != 0);
if (ca_update_rd_add) begin /////////////////////////////////// new entry before current one !!!
ca_rd_add <= ca_wr_add_start;
ca_rd_fsm_state <= 6;
end else
case (ca_rd_fsm_state)
0: begin /////////////////////////////////////////////////// reset
ca_rd_add <= 8'h010;
if (ca_ready)
ca_rd_fsm_state <= 1;
end
1: begin /////////////////////////////////////////////////// wait
if (ca_match_int &
!ca_wr_add_start_marker) begin
ca_rd_fsm_state <= 2;
ca_rd_add <= ca_wr_add_start + 1;
end
end
2: begin /////////////////////////////////////////////////// end clean bit
ca_rd_fsm_state <= 3;
ca_rd_add <= ca_wr_add_start;
end
3: begin
ca_rd_fsm_state <= 4;
end
4: begin
if (!ca_match_req)
ca_command <= ca_rd_doutb[31:0];
ca_match_req <= 1'b1;
if (ca_match_block_ack)
ca_rd_fsm_state <= 5;
end
5: begin
ca_wr_sync_update <= 1'b1;
if (ca_wr_sync_update_done) begin
ca_rd_fsm_state <= 6;
ca_rd_add <= ca_wr_add_start;
ca_wr_sync_update <= 1'b0;
end
end
6: begin
ca_rd_fsm_state <= 7;
end
7: begin
ca_rd_fsm_state <= 8;
end
8: if (!ca_wr_sync_update_done)
ca_rd_fsm_state <= 1;
default : ;
endcase
end
///////////////////////////////////////////////////
// FSM for async interface
///////////////////////////////////////////////////
reg ca_match_valid;
reg ca_match_ack_meta;
reg ca_match_block_ack;
reg [1:0] ca_match_hs_state;
always @ (posedge clk_const or negedge rstn)
if (~rstn) begin
ca_match_hs_state <= 0;
ca_match_valid <= 0;
ca_match_block_ack <= 0;
ca_match_ack_meta <= 0;
end else begin
ca_match_block_ack <= 0;
case (ca_match_hs_state)
0: if (ca_match_req) begin // <---
ca_match_valid <= 1'b1;
ca_match_hs_state <= 1; // <---
ca_match_block_ack <= 1;
end
1: if (ca_match_ack_meta) begin
ca_match_hs_state <= 2;
ca_match_valid <= 1'b0;
end
2: if (!ca_match_ack_meta) begin // <---
ca_match_hs_state <= 0;
end
endcase
ca_match_ack_meta <= ca_match_ack;
end
endmodule