In commercial SoC and FPGA designs, a hardware timer is a foundational infrastructure IP , it is essential for deterministic behavior, functional safety, power management, and long-term maintainability of complex systems.
-
Deterministic Time Reference
-
Hardware-Enforced Time Boundaries
-
Precise Periodic Event Generation
-
Low-Power and Idle-State Timekeeping
-
Offloading Timing Responsibility from Software
-
Peripheral Communication Timeouts
-
Operating System Scheduler Tick
-
Periodic Sampling and Control Systems
-
Safety and Supervision Logic
-
Non-Blocking Delay and Pacing
-
Hardware timers provide predictable timing unaffected by software variability.
-
Prevents deadlocks and uncontrolled execution paths in failure scenarios.
-
Eliminates fragile software delay loops and ad-hoc timing constructs.
-
Enables sleep-based designs instead of continuous polling.
-
Provides a reusable, standardized timing primitive across multiple designs and products.
The Timer IP supports multiple operating modes to accommodate a wide range of timing requirements in embedded and SoC designs.
- In one-shot mode, the timer counts down from a programmed load value to zero and asserts a timeout event once. After the timeout event
- In periodic mode, the timer automatically reloads the programmed load value upon reaching zero
- Both one-shot and periodic modes may optionally operate with a programmable prescaler
Timer IP (click to expand)
`timescale 1ns / 1ps
module timer_ip (
input wire clk,
input wire resetn,
// Bus interface
input wire sel, // timer selected
input wire wr_en, // write enable
input wire rd_en, // read enable
input wire [1:0] addr, // register select
input wire [31:0] wdata,
output reg [31:0] rdata,
// Hardware output
output wire timeout_o
);
// -------------------------------------------------
// Registers
// -------------------------------------------------
reg [31:0] ctrl_reg; // CTRL
reg [31:0] load_reg; // LOAD
reg [31:0] value_reg; // VALUE
reg timeout; // STATUS[0]
// Prescaler
reg [15:0] presc_cnt;
// CTRL fields
wire en = ctrl_reg[0];
wire mode = ctrl_reg[1]; // 0 = one-shot, 1 = periodic
wire presc_en = ctrl_reg[2];
wire [7:0] presc_div = ctrl_reg[15:8];
// -------------------------------------------------
// WRITE LOGIC (CTRL & LOAD only)
// -------------------------------------------------
always @(posedge clk) begin
if (!resetn) begin
ctrl_reg <= 32'b0;
load_reg <= 32'b0;
end else if (sel && wr_en) begin
case (addr)
2'b00: ctrl_reg <= wdata; // CTRL
2'b01: load_reg <= wdata; // LOAD
default: ;
endcase
end
end
// -------------------------------------------------
// TIMER CORE LOGIC (VALUE + TIMEOUT)
// -------------------------------------------------
always @(posedge clk) begin
if (!resetn) begin
value_reg <= 32'b0;
presc_cnt <= 16'b0;
timeout <= 1'b0;
end else begin
// STATUS W1C clear
if (sel && wr_en && addr == 2'b11 && wdata[0])
timeout <= 1'b0;
if (en) begin
// Prescaler tick
if (!presc_en || presc_cnt == presc_div) begin
presc_cnt <= 16'b0;
if (value_reg > 1) begin
value_reg <= value_reg - 1;
end else if (value_reg == 1) begin
timeout <= 1'b1;
value_reg <= mode ? load_reg : 32'b0;
end else begin
// value_reg == 0
value_reg <= load_reg;
end
end else begin
presc_cnt <= presc_cnt + 1;
end
end else begin
// EN = 0 → preload
value_reg <= load_reg;
presc_cnt <= 16'b0;
timeout <= 1'b0;
end
end
end
// -------------------------------------------------
// READ LOGIC (registered)
// -------------------------------------------------
always @(posedge clk) begin
if (!resetn) begin
rdata <= 32'b0;
end else if (sel && rd_en) begin
case (addr)
2'b00: rdata <= ctrl_reg; // CTRL
2'b01: rdata <= load_reg; // LOAD
2'b10: rdata <= value_reg; // VALUE
2'b11: rdata <= {31'b0, timeout}; // STATUS
default: rdata <= 32'b0;
endcase
end
end
// -------------------------------------------------
// Hardware output
// -------------------------------------------------
assign timeout_o = timeout;
endmodule
wire [31:0] timer_rdata;
wire timer_timeout;
timer_ip TIMER (
.clk (clk),
.resetn (resetn),
// Bus interface
.sel (timer_sel),
.wr_en (timer_wr_en),
.rd_en (timer_rd_en),
.addr (timer_addr),
.wdata (mem_wdata),
.rdata (timer_rdata),
// Hardware output
.timeout_o(timer_timeout)
);isIO = mem_addr[22];localparam TIMER_BASE_WADDR = 30'h00100010; // 0x00400040 >> 2
assign timer_sel =
isIO &&
(mem_wordaddr >= TIMER_BASE_WADDR) &&
(mem_wordaddr <= TIMER_BASE_WADDR + 3);| Offset | Register | Access | Description |
|---|---|---|---|
| 0x00 | CTRL | R/W | Control register (enable, mode, prescaler control) |
| 0x04 | LOAD | R/W | Load value (initial / reload count) |
| 0x08 | VALUE | R | Current counter value |
| 0x0C | STATUS | R/W1C | Timeout status (write-1-to-clear) |
Reset Value: 0x0000_0000
Access: Read / Write
| Bit(s) | Name | Description |
|---|---|---|
| [0] | EN | Timer enable. 1 = timer runs, 0 = timer stopped and preloaded |
| [1] | MODE | Operating mode: 0 = one-shot, 1 = periodic |
| [2] | PRESC_EN | Prescaler enable. 1 = prescaler active, 0 = bypass |
| [15:8] | PRESC_DIV | Prescaler divider value |
| [31:16] | RSVD | Reserved, read as 0 |
Behavior:
- When
EN=0, the counter is preloaded withLOAD. - When
EN=1, the timer counts based on the prescaler configuration. - MODE controls reload behavior after timeout.
create a firmware for test
cd Firmware
make timer_clear_test.bram.hexiverilog -g2012 -DBENCH -o sim.vvp riscv.v timer_ip.vvvp sim.vvpgtkwave soc.vcdyosys -p "
read_verilog riscv.v timer_ip.v
synth_ice40 -top SOC -json soc.json
"make pnricepack soc.asc soc.binsudo iceprog soc.bin
| Timer IP Signal | SoC Signal | FPGA Pin | Board Connection | Purpose |
|---|---|---|---|---|
| timeout_o | LEDS[0] | 39 | On-board LED0 | Visual timeout indication |
set_io LEDS[0] 39
TIMER_LOADED
TIMER_DECREMENT & TIMEOUT_HIGH
TIMEOUT_RESET

- Hardware validation of the Timer IP showing correct timeout behavior.
- The timer counts down to zero, asserts the timeout signal, and drives the on-board LED high, confirming proper timer operation and SoC-level integration.
LED_rgb.mp4
TIMER LOADED
TIMER DECREMENT
TIMER RELOADED
TIMER READBACK
TIMEOUT_TOGGLE

TIMER_LOADED
TIMER_DECREMENT & TIMEOUT_HIGH
TIMEOUT_RESET
LEDS
