new timer
This commit is contained in:
@@ -1,74 +1,100 @@
|
||||
`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
|
||||
parameter address = 32'h00000000 // Base address of peripheral
|
||||
)(
|
||||
input wire i_clk,
|
||||
input wire i_rst,
|
||||
output reg o_irq,
|
||||
output wire o_irq,
|
||||
|
||||
input wire [31:0] i_wb_adr,
|
||||
input wire [31:0] i_wb_dat,
|
||||
output reg [31:0] o_wb_dat,
|
||||
input wire [3:0] i_wb_sel,
|
||||
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;
|
||||
// Registers
|
||||
reg [31:0] counter; // The actual counter. Generates an interrupt when it reaches 0
|
||||
reg [31:0] preload; // The value with which the counter gets loaded after it reaches 0. 0 to keep the timer off
|
||||
|
||||
// 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);
|
||||
reg wb_ack = 0;
|
||||
reg irq_fired = 0;
|
||||
reg counter_started = 0;
|
||||
reg counter_running = 0;
|
||||
reg prev_counter_running = 0;
|
||||
assign o_wb_ack = wb_ack;
|
||||
|
||||
// Readback: expose the current counter value
|
||||
always @(*) begin
|
||||
o_wb_dat = 32'd0;
|
||||
o_wb_dat[WIDTH-1:0] = counter;
|
||||
end
|
||||
assign o_irq = irq_fired;
|
||||
|
||||
// Main logic
|
||||
always @(posedge i_clk) begin
|
||||
if (i_rst) begin
|
||||
counter <= {WIDTH{1'b0}};
|
||||
presc <= { (DIVIDER+1){1'b0} };
|
||||
o_irq <= 1'b0;
|
||||
if(i_rst) begin
|
||||
counter <= 0;
|
||||
preload <= 0;
|
||||
wb_ack <= 0;
|
||||
o_wb_dat <= 0;
|
||||
irq_fired <= 0;
|
||||
counter_started <= 0;
|
||||
counter_running <= 0;
|
||||
prev_counter_running <= 0;
|
||||
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;
|
||||
prev_counter_running <= counter_running;
|
||||
counter_running <= counter>0;
|
||||
|
||||
// reset prescaler on (re)start or stop
|
||||
presc <= { (DIVIDER+1){1'b0} };
|
||||
if(!irq_fired && prev_counter_running && !counter_running)
|
||||
irq_fired <= 1'b1;
|
||||
if(counter>0 && counter_started)
|
||||
counter <= counter - 1;
|
||||
|
||||
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
|
||||
if(counter == 0 && preload>0 && counter_started)
|
||||
counter <= preload;
|
||||
|
||||
if(counter == 0 && preload == 0)
|
||||
counter_started <= 1'b0;
|
||||
|
||||
// Ack generation
|
||||
wb_ack <= i_wb_cyc & i_wb_stb & !wb_ack;
|
||||
|
||||
// Read cycle
|
||||
if(i_wb_cyc && i_wb_stb && !i_wb_we) begin
|
||||
if(i_wb_adr[3:0] == 4'b0000) begin
|
||||
if(i_wb_sel[0]) o_wb_dat[7:0] <= counter[7:0];
|
||||
if(i_wb_sel[1]) o_wb_dat[15:8] <= counter[15:8];
|
||||
if(i_wb_sel[2]) o_wb_dat[23:16] <= counter[23:16];
|
||||
if(i_wb_sel[3]) o_wb_dat[31:24] <= counter[31:24];
|
||||
end else if(i_wb_adr[3:0] == 4'b0100) begin
|
||||
if(i_wb_sel[0]) o_wb_dat[7:0] <= preload[7:0];
|
||||
if(i_wb_sel[1]) o_wb_dat[15:8] <= preload[15:8];
|
||||
if(i_wb_sel[2]) o_wb_dat[23:16] <= preload[23:16];
|
||||
if(i_wb_sel[3]) o_wb_dat[31:24] <= preload[31:24];
|
||||
end
|
||||
end
|
||||
|
||||
// write cycle
|
||||
if(i_wb_cyc && i_wb_stb && i_wb_we) begin
|
||||
if(i_wb_adr[3:0] == 4'b0000) begin
|
||||
if(i_wb_sel[0]) counter[7:0] <= i_wb_dat[7:0];
|
||||
if(i_wb_sel[1]) counter[15:8] <= i_wb_dat[15:8];
|
||||
if(i_wb_sel[2]) counter[23:16] <= i_wb_dat[23:16];
|
||||
if(i_wb_sel[3]) counter[31:24] <= i_wb_dat[31:24];
|
||||
counter_started <= 1'b1;
|
||||
end else if(i_wb_adr[3:0] == 4'b0100) begin
|
||||
if(i_wb_sel[0]) preload[7:0] <= i_wb_dat[7:0];
|
||||
if(i_wb_sel[1]) preload[15:8] <= i_wb_dat[15:8];
|
||||
if(i_wb_sel[2]) preload[23:16] <= i_wb_dat[23:16];
|
||||
if(i_wb_sel[3]) preload[31:24] <= i_wb_dat[31:24];
|
||||
counter_started <= 1'b1;
|
||||
end else if(i_wb_adr[3:0] == 4'b1000) begin
|
||||
// Any write to BASE+8 will ack the IRQ
|
||||
irq_fired <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
endmodule
|
||||
|
||||
Reference in New Issue
Block a user