`timescale 1ns/1ps module wb_countdown_timer #( parameter WIDTH = 32, // counter width (<=32 makes bus mapping easy) parameter DIVIDER = 0 // optional prescaler: tick every 2^DIVIDER cycles )( input wire i_clk, input wire i_rst, output reg o_irq, input wire [31:0] i_wb_dat, output reg [31:0] o_wb_dat, input wire i_wb_we, input wire i_wb_cyc, input wire i_wb_stb, output wire o_wb_ack ); // One-cycle acknowledge on any valid WB access // (classic, zero-wait-state peripheral) assign o_wb_ack = i_wb_cyc & i_wb_stb; // Internal countdown and prescaler reg [WIDTH-1:0] counter; reg [DIVIDER:0] presc; // enough bits to count up to 2^DIVIDER-1 wire tick = (DIVIDER == 0) ? 1'b1 : (presc[DIVIDER] == 1'b1); // Readback: expose the current counter value always @(*) begin o_wb_dat = 32'd0; o_wb_dat[WIDTH-1:0] = counter; end // Main logic always @(posedge i_clk) begin if (i_rst) begin counter <= {WIDTH{1'b0}}; presc <= { (DIVIDER+1){1'b0} }; o_irq <= 1'b0; end else begin // Default prescaler behavior if (DIVIDER != 0) begin if (counter != 0 && !o_irq) presc <= presc + 1'b1; else presc <= { (DIVIDER+1){1'b0} }; end // Wishbone write: load counter and clear IRQ if (o_wb_ack && i_wb_we) begin counter <= i_wb_dat[WIDTH-1:0]; o_irq <= 1'b0; // reset prescaler on (re)start or stop presc <= { (DIVIDER+1){1'b0} }; end else begin // Countdown when running (counter>0), not already IRQ'd if (!o_irq && counter != 0) begin if (tick) begin if (counter == 1) begin counter <= {WIDTH{1'b0}}; o_irq <= 1'b1; // sticky until next write presc <= { (DIVIDER+1){1'b0} }; end else begin counter <= counter - 1'b1; end end end end end end endmodule