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