Fix bug in timer core, where it misses clock cycles

Remove redundant timer state. This fixes a bug where the timer misses a
clock cycle every time the prescaler counter reaches 1. This means if
one uses a large prescaler, like 18E6, it is barely noticeable, but if
one have a low prescaler and a high timer value it becomes significant.
This also yields the running_* registers redundant, which are removed.

Add clarity to the readme.

Update the timer to default to values of one, for prescaler and timer
count.
This commit is contained in:
Daniel Jobson 2024-11-22 13:03:45 +01:00
parent 3d7a97ecbc
commit 6bdedf4f86
No known key found for this signature in database
GPG Key ID: 3707A9DBF4BB8F1A
4 changed files with 45 additions and 75 deletions

View File

@ -1 +1 @@
d5e4153187d1808b77535c9233a28073de7eeed4ead373efa9d7dd835833b03a application_fpga.bin 2a3928e8587c8d5d7eca6048c6448dc1d16d52a9a25d3ca5026bec3e7fb14ef3 application_fpga.bin

View File

@ -3,9 +3,11 @@ A simple timer with prescaler.
## Introduction ## Introduction
This core implements a simple timer with a prescaler. The prescaler This core implements a simple timer with a prescaler. The prescaler
allows measurement of time durations rather than cycles. If for can be used to divide the system clock frequency to yield a specific
example setting the prescaler to the clock frequency in Hertz, the amount of clock frequency ticks for each timer tick. This allows
timer will count seconds. measurement of time rather than cycles. If for example setting the
prescaler to the clock frequency in Hertz, the timer will count
seconds.
## API ## API
@ -27,11 +29,23 @@ The following addresses define the API for the timer:
## Details ## Details
The core consists of the timer_core module (in timer_core.v) and a top The core consists of the timer_core module (in timer_core.v) and a top
level wrapper, timer (in timer.v). The top level wrapper implements level wrapper, timer (in timer.v). The top level wrapper implements
the API, while the timer_core implements the actual timer the API, while the timer_core implements the timer functionality.
functionality.
The timer counter and the prescaler counter are both 32 bits. Both the
`prescaler` and the `timer` register can be set through the API above,
and should be a non-zero value, the defaults are one. These values
cannot be changed when the timer is running.
Bit zero of the `status` register shows if the timer is running, one
indicates a running timer.
Start the timer by writing 1 to bit zero of the `ctrl` register, write
1 to bit one to stop it.
Time elapsed can be calculated using:
```
time = clock_freq / (prescaler * timer)
```
The timer counter and the prescaler counter are both 32 bits.
When enabled the counter counts down one integer value per cycle.
The timer will stop when reaching final zero (given by prescaler times the initial value of the timer)
and the running flag will be lowered.

View File

@ -30,18 +30,13 @@ module timer_core (
//---------------------------------------------------------------- //----------------------------------------------------------------
// Internal constant and parameter definitions. // Internal constant and parameter definitions.
//---------------------------------------------------------------- //----------------------------------------------------------------
localparam CTRL_IDLE = 2'h0; localparam CTRL_IDLE = 1'h0;
localparam CTRL_PRESCALER = 2'h1; localparam CTRL_RUNNING = 1'h1;
localparam CTRL_TIMER = 2'h2;
//---------------------------------------------------------------- //----------------------------------------------------------------
// Registers including update variables and write enable. // Registers including update variables and write enable.
//---------------------------------------------------------------- //----------------------------------------------------------------
reg running_reg;
reg running_new;
reg running_we;
reg [31 : 0] prescaler_reg; reg [31 : 0] prescaler_reg;
reg [31 : 0] prescaler_new; reg [31 : 0] prescaler_new;
reg prescaler_we; reg prescaler_we;
@ -54,8 +49,8 @@ module timer_core (
reg timer_set; reg timer_set;
reg timer_dec; reg timer_dec;
reg [ 1 : 0] core_ctrl_reg; reg core_ctrl_reg;
reg [ 1 : 0] core_ctrl_new; reg core_ctrl_new;
reg core_ctrl_we; reg core_ctrl_we;
@ -63,7 +58,7 @@ module timer_core (
// Concurrent connectivity for ports etc. // Concurrent connectivity for ports etc.
//---------------------------------------------------------------- //----------------------------------------------------------------
assign curr_timer = timer_reg; assign curr_timer = timer_reg;
assign running = running_reg; assign running = core_ctrl_reg;
//---------------------------------------------------------------- //----------------------------------------------------------------
@ -71,16 +66,11 @@ module timer_core (
//---------------------------------------------------------------- //----------------------------------------------------------------
always @(posedge clk) begin : reg_update always @(posedge clk) begin : reg_update
if (!reset_n) begin if (!reset_n) begin
running_reg <= 1'h0;
prescaler_reg <= 32'h0; prescaler_reg <= 32'h0;
timer_reg <= 32'h0; timer_reg <= 32'h0;
core_ctrl_reg <= CTRL_IDLE; core_ctrl_reg <= CTRL_IDLE;
end end
else begin else begin
if (running_we) begin
running_reg <= running_new;
end
if (prescaler_we) begin if (prescaler_we) begin
prescaler_reg <= prescaler_new; prescaler_reg <= prescaler_new;
end end
@ -136,8 +126,6 @@ module timer_core (
// Core control FSM. // Core control FSM.
//---------------------------------------------------------------- //----------------------------------------------------------------
always @* begin : core_ctrl always @* begin : core_ctrl
running_new = 1'h0;
running_we = 1'h0;
prescaler_set = 1'h0; prescaler_set = 1'h0;
prescaler_dec = 1'h0; prescaler_dec = 1'h0;
timer_set = 1'h0; timer_set = 1'h0;
@ -148,73 +136,38 @@ module timer_core (
case (core_ctrl_reg) case (core_ctrl_reg)
CTRL_IDLE: begin CTRL_IDLE: begin
if (start) begin if (start) begin
running_new = 1'h1;
running_we = 1'h1;
prescaler_set = 1'h1; prescaler_set = 1'h1;
timer_set = 1'h1; timer_set = 1'h1;
if (prescaler_init == 0) begin core_ctrl_new = CTRL_RUNNING;
core_ctrl_new = CTRL_TIMER;
core_ctrl_we = 1'h1;
end
else begin
core_ctrl_new = CTRL_PRESCALER;
core_ctrl_we = 1'h1; core_ctrl_we = 1'h1;
end end
end end
end
CTRL_PRESCALER: begin CTRL_RUNNING: begin
if (stop) begin if (stop) begin
running_new = 1'h0;
running_we = 1'h1;
core_ctrl_new = CTRL_IDLE; core_ctrl_new = CTRL_IDLE;
core_ctrl_we = 1'h1; core_ctrl_we = 1'h1;
end end
else begin else begin
if (prescaler_reg == 1) begin if (prescaler_reg[31 : 1] == 0) begin // Check prescaler_reg <= 1
core_ctrl_new = CTRL_TIMER; if (timer_reg[31 : 1] == 0) begin // Check timer_reg <= 1
core_ctrl_new = CTRL_IDLE;
core_ctrl_we = 1'h1; core_ctrl_we = 1'h1;
end end
else begin
prescaler_set = 1'h1;
timer_dec = 1'h1;
end
end
else begin else begin
prescaler_dec = 1'h1; prescaler_dec = 1'h1;
end end
end end
end end
CTRL_TIMER: begin
if (stop) begin
running_new = 1'h0;
running_we = 1'h1;
core_ctrl_new = CTRL_IDLE;
core_ctrl_we = 1'h1;
end
else begin
if (timer_reg == 1) begin
running_new = 1'h0;
running_we = 1'h1;
core_ctrl_new = CTRL_IDLE;
core_ctrl_we = 1'h1;
end
else begin
timer_dec = 1'h1;
if (prescaler_init > 0) begin
prescaler_set = 1'h1;
core_ctrl_new = CTRL_PRESCALER;
core_ctrl_we = 1'h1;
end
end
end
end
default: begin default: begin
end end
endcase // case (core_ctrl_reg) endcase // case (core_ctrl_reg)

View File

@ -182,6 +182,9 @@ module tb_timer_core ();
//---------------------------------------------------------------- //----------------------------------------------------------------
// test1() // test1()
//
// Set prescaler and timer and count until the timer returns expired.
// Check so the clock cycles passed adds up to timer * prescaler.
//---------------------------------------------------------------- //----------------------------------------------------------------
task test1; task test1;
begin begin