blob: ea946f00d91a16b15bbd78f5ae0d52458b4698e4 [file] [log] [blame]
// 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
`default_nettype none
/* Simple 32-bit counter-timer for Caravel. */
/* Counter acts as high 32 bits of a 64-bit counter
* when chained with the other counter
*/
module counter_timer_high_wb # (
parameter BASE_ADR = 32'h2400_0000,
parameter CONFIG = 8'h00,
parameter VALUE = 8'h04,
parameter DATA = 8'h08
) (
input wb_clk_i,
input wb_rst_i,
input [31:0] wb_adr_i,
input [31:0] wb_dat_i,
input [3:0] wb_sel_i,
input wb_we_i,
input wb_cyc_i,
input wb_stb_i,
output wb_ack_o,
output [31:0] wb_dat_o,
input enable_in,
input stop_in,
input strobe,
input is_offset,
output stop_out,
output enable_out,
output irq
);
wire [31:0] counter_timer_reg_cfg_do;
wire [31:0] counter_timer_reg_val_do;
wire [31:0] counter_timer_reg_dat_do;
wire resetn = ~wb_rst_i;
wire valid = wb_stb_i && wb_cyc_i;
wire counter_timer_reg_cfg_sel = valid && (wb_adr_i == (BASE_ADR | CONFIG));
wire counter_timer_reg_val_sel = valid && (wb_adr_i == (BASE_ADR | VALUE));
wire counter_timer_reg_dat_sel = valid && (wb_adr_i == (BASE_ADR | DATA));
wire reg_cfg_we = (counter_timer_reg_cfg_sel) ?
(wb_sel_i[0] & {wb_we_i}): 1'b0;
wire [3:0] reg_val_we = (counter_timer_reg_val_sel) ?
(wb_sel_i & {4{wb_we_i}}): 4'b0000;
wire [3:0] reg_dat_we = (counter_timer_reg_dat_sel) ?
(wb_sel_i & {4{wb_we_i}}): 4'b0000;
wire [31:0] mem_wdata = wb_dat_i;
wire reg_dat_re = counter_timer_reg_dat_sel && !wb_sel_i && ~wb_we_i;
assign wb_dat_o = (counter_timer_reg_cfg_sel) ? counter_timer_reg_cfg_do :
(counter_timer_reg_val_sel) ? counter_timer_reg_val_do :
counter_timer_reg_dat_do;
assign wb_ack_o = counter_timer_reg_cfg_sel || counter_timer_reg_val_sel ||
counter_timer_reg_dat_sel;
counter_timer_high counter_timer_high_inst (
.resetn(resetn),
.clkin(wb_clk_i),
.reg_val_we(reg_val_we),
.reg_val_di(mem_wdata),
.reg_val_do(counter_timer_reg_val_do),
.reg_cfg_we(reg_cfg_we),
.reg_cfg_di(mem_wdata),
.reg_cfg_do(counter_timer_reg_cfg_do),
.reg_dat_we(reg_dat_we),
.reg_dat_di(mem_wdata),
.reg_dat_do(counter_timer_reg_dat_do),
.enable_in(enable_in),
.stop_in(stop_in),
.is_offset(is_offset),
.stop_out(stop_out),
.strobe(strobe),
.enable_out(enable_out),
.irq_out(irq)
);
endmodule
module counter_timer_high (
input resetn,
input clkin,
input [3:0] reg_val_we,
input [31:0] reg_val_di,
output [31:0] reg_val_do,
input reg_cfg_we,
input [31:0] reg_cfg_di,
output [31:0] reg_cfg_do,
input [3:0] reg_dat_we,
input [31:0] reg_dat_di,
output [31:0] reg_dat_do,
input stop_in,
input enable_in,
input is_offset,
input strobe,
output stop_out,
output enable_out,
output irq_out
);
reg [31:0] value_cur;
reg [31:0] value_reset;
reg irq_out;
wire enable_in; // Enable from chained counter
wire strobe; // Count strobe from low word counter
wire enable_out; // Enable to chained counter (sync)
reg stop_out; // Stop signal to low word counter
wire [31:0] value_cur_plus; // Next value, on up-count
wire [31:0] value_cur_minus; // Next value, on down-count
wire [31:0] value_check_plus; // Value to check for stop condition during up count
wire loc_enable; // Local enable
reg enable; // Enable (start) the counter/timer
reg lastenable; // Previous state of enable (catch rising/falling edge)
reg oneshot; // Set oneshot (1) mode or continuous (0) mode
reg updown; // Count up (1) or down (0)
reg irq_ena; // Enable interrupt on timeout
reg chain; // Chain to a secondary timer
// Configuration register
assign reg_cfg_do = {27'd0, irq_ena, chain, updown, oneshot, enable};
always @(posedge clkin or negedge resetn) begin
if (resetn == 1'b0) begin
enable <= 1'b0;
oneshot <= 1'b0;
updown <= 1'b0;
chain <= 1'b0;
irq_ena <= 1'b0;
end else begin
if (reg_cfg_we) begin
enable <= reg_cfg_di[0];
oneshot <= reg_cfg_di[1];
updown <= reg_cfg_di[2];
chain <= reg_cfg_di[3];
irq_ena <= reg_cfg_di[4];
end
end
end
// Counter/timer reset value register
assign reg_val_do = value_reset;
always @(posedge clkin or negedge resetn) begin
if (resetn == 1'b0) begin
value_reset <= 32'd0;
end else begin
if (reg_val_we[3]) value_reset[31:24] <= reg_val_di[31:24];
if (reg_val_we[2]) value_reset[23:16] <= reg_val_di[23:16];
if (reg_val_we[1]) value_reset[15:8] <= reg_val_di[15:8];
if (reg_val_we[0]) value_reset[7:0] <= reg_val_di[7:0];
end
end
assign reg_dat_do = value_cur;
// Counter/timer current value register and timer implementation
assign value_cur_plus = value_cur + 1;
assign value_cur_minus = value_cur - 1;
assign value_check_plus = (is_offset) ? value_cur_plus : value_cur;
assign enable_out = enable;
assign loc_enable = (chain == 1'b1) ? (enable && enable_in) : enable;
// When acting as the high 32 bit word of a 64-bit chained counter:
//
// It counts when the low 32-bit counter strobes (strobe == 1).
// It sets "stop_out" and stops on the stop condition.
always @(posedge clkin or negedge resetn) begin
if (resetn == 1'b0) begin
value_cur <= 32'd0;
stop_out <= 1'b0;
irq_out <= 1'b0;
lastenable <= 1'b0;
end else begin
lastenable <= loc_enable;
if (reg_dat_we != 4'b0000) begin
if (reg_dat_we[3] == 1'b1) value_cur[31:24] <= reg_dat_di[31:24];
if (reg_dat_we[2] == 1'b1) value_cur[23:16] <= reg_dat_di[23:16];
if (reg_dat_we[1] == 1'b1) value_cur[15:8] <= reg_dat_di[15:8];
if (reg_dat_we[0] == 1'b1) value_cur[7:0] <= reg_dat_di[7:0];
end else if (loc_enable == 1'b1) begin
/* IRQ signals one cycle after stop, if IRQ is enabled */
irq_out <= (irq_ena) ? stop_out : 1'b0;
if (updown == 1'b1) begin
if (lastenable == 1'b0) begin
value_cur <= 32'd0;
stop_out <= 1'b0;
end else if (chain) begin
// Chained counter behavior
if (value_check_plus == value_reset) begin
stop_out <= 1'b1;
end
if (stop_in == 1'b1) begin // lower word counter stopped
if (oneshot != 1'b1) begin
value_cur <= 32'd0; // reset count
stop_out <= 1'b0; // no longer stopped
end else if (strobe == 1'b1) begin
value_cur <= value_cur_plus;
end
end else if (strobe == 1'b1) begin
value_cur <= value_cur_plus;
end
end else begin
// Standalone counter behavior
if (value_cur == value_reset) begin
if (oneshot != 1'b1) begin
value_cur <= 32'd0;
stop_out <= 1'b0;
end else begin
stop_out <= 1'b1;
end
end else begin
if (value_cur_plus == 32'd0) begin
stop_out <= 1'b1;
end else begin
stop_out <= 1'b0;
end
value_cur <= value_cur_plus; // count up
end
end
end else begin
if (lastenable == 1'b0) begin
value_cur <= value_reset;
stop_out <= 1'b0;
end else if (chain) begin
// Chained counter behavior
if (value_cur == 32'd0) begin
stop_out <= 1'b1;
end
if (stop_in == 1'b1) begin // lower word counter stopped
if (oneshot != 1'b1) begin
value_cur <= value_reset; // reset count
stop_out <= 1'b0; // no longer stopped
end
end else if (strobe == 1'b1) begin
value_cur <= value_cur_minus; // count down
end
end else begin
// Standalone counter behavior
if (value_cur == 32'd0) begin
if (oneshot != 1'b1) begin
value_cur <= value_reset;
stop_out <= 1'b0;
end else begin
stop_out <= 1'b1;
end
end else begin
if (value_cur_minus == 32'd0) begin
stop_out <= 1'b1;
end else begin
stop_out <= 1'b0;
end
value_cur <= value_cur_minus; // count down
end
end
end
end else begin
stop_out <= 1'b0;
end
end
end
endmodule
`default_nettype wire