-
Notifications
You must be signed in to change notification settings - Fork 487
/
timer.sv
154 lines (131 loc) · 5.05 KB
/
timer.sv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Example memory mapped timer
`include "prim_assert.sv"
module timer #(
// Bus data width (must be 32)
parameter int unsigned DataWidth = 32,
// Bus address width
parameter int unsigned AddressWidth = 32
) (
input logic clk_i,
input logic rst_ni,
// Bus interface
input logic timer_req_i,
input logic [AddressWidth-1:0] timer_addr_i,
input logic timer_we_i,
input logic [ DataWidth/8-1:0] timer_be_i,
input logic [ DataWidth-1:0] timer_wdata_i,
output logic timer_rvalid_o,
output logic [ DataWidth-1:0] timer_rdata_o,
output logic timer_err_o,
output logic timer_intr_o
);
// The timers are always 64 bits
localparam int unsigned TW = 64;
// Upper bits of address are decoded into timer_req_i
localparam int unsigned ADDR_OFFSET = 10; // 1kB
// Register map
localparam bit [9:0] MTIME_LOW = 0;
localparam bit [9:0] MTIME_HIGH = 4;
localparam bit [9:0] MTIMECMP_LOW = 8;
localparam bit [9:0] MTIMECMP_HIGH = 12;
logic timer_we;
logic mtime_we, mtimeh_we;
logic mtimecmp_we, mtimecmph_we;
logic [DataWidth-1:0] mtime_wdata, mtimeh_wdata;
logic [DataWidth-1:0] mtimecmp_wdata, mtimecmph_wdata;
logic [TW-1:0] mtime_q, mtime_d, mtime_inc;
logic [TW-1:0] mtimecmp_q, mtimecmp_d;
logic interrupt_q, interrupt_d;
logic error_q, error_d;
logic [DataWidth-1:0] rdata_q, rdata_d;
logic rvalid_q;
// Global write enable for all registers
assign timer_we = timer_req_i & timer_we_i;
// mtime increments every cycle
assign mtime_inc = mtime_q + 64'd1;
// Generate write data based on byte strobes
for (genvar b = 0; b < DataWidth / 8; b++) begin : gen_byte_wdata
assign mtime_wdata[(b*8)+:8] = timer_be_i[b] ? timer_wdata_i[b*8+:8] :
mtime_q[(b*8)+:8];
assign mtimeh_wdata[(b*8)+:8] = timer_be_i[b] ? timer_wdata_i[b*8+:8] :
mtime_q[DataWidth+(b*8)+:8];
assign mtimecmp_wdata[(b*8)+:8] = timer_be_i[b] ? timer_wdata_i[b*8+:8] :
mtimecmp_q[(b*8)+:8];
assign mtimecmph_wdata[(b*8)+:8] = timer_be_i[b] ? timer_wdata_i[b*8+:8] :
mtimecmp_q[DataWidth+(b*8)+:8];
end
// Generate write enables
assign mtime_we = timer_we & (timer_addr_i[ADDR_OFFSET-1:0] == MTIME_LOW);
assign mtimeh_we = timer_we & (timer_addr_i[ADDR_OFFSET-1:0] == MTIME_HIGH);
assign mtimecmp_we = timer_we & (timer_addr_i[ADDR_OFFSET-1:0] == MTIMECMP_LOW);
assign mtimecmph_we = timer_we & (timer_addr_i[ADDR_OFFSET-1:0] == MTIMECMP_HIGH);
// Generate next data
assign mtime_d = {(mtimeh_we ? mtimeh_wdata : mtime_inc[63:32]),
(mtime_we ? mtime_wdata : mtime_inc[31:0])};
assign mtimecmp_d = {(mtimecmph_we ? mtimecmph_wdata : mtimecmp_q[63:32]),
(mtimecmp_we ? mtimecmp_wdata : mtimecmp_q[31:0])};
// Generate registers
always_ff @(posedge clk_i or negedge rst_ni) begin
if (~rst_ni) begin
mtime_q <= 'b0;
end else begin
mtime_q <= mtime_d;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (~rst_ni) begin
mtimecmp_q <= 'b0;
end else if (mtimecmp_we | mtimecmph_we) begin
mtimecmp_q <= mtimecmp_d;
end
end
// interrupt remains set until mtimecmp is written
assign interrupt_d = ((mtime_q >= mtimecmp_q) | interrupt_q) & ~(mtimecmp_we | mtimecmph_we);
always_ff @(posedge clk_i or negedge rst_ni) begin
if (~rst_ni) begin
interrupt_q <= 'b0;
end else begin
interrupt_q <= interrupt_d;
end
end
assign timer_intr_o = interrupt_q;
// Read data
always_comb begin
rdata_d = 'b0;
error_d = 1'b0;
unique case (timer_addr_i[ADDR_OFFSET-1:0])
MTIME_LOW: rdata_d = mtime_q[31:0];
MTIME_HIGH: rdata_d = mtime_q[63:32];
MTIMECMP_LOW: rdata_d = mtimecmp_q[31:0];
MTIMECMP_HIGH: rdata_d = mtimecmp_q[63:32];
default: begin
rdata_d = 'b0;
// Error if no address matched
error_d = 1'b1;
end
endcase
end
// error_q and rdata_q are only valid when rvalid_q is high
always_ff @(posedge clk_i) begin
if (timer_req_i) begin
rdata_q <= rdata_d;
error_q <= error_d;
end
end
assign timer_rdata_o = rdata_q;
// Read data is always valid one cycle after a request
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
rvalid_q <= 1'b0;
end else begin
rvalid_q <= timer_req_i;
end
end
assign timer_rvalid_o = rvalid_q;
assign timer_err_o = error_q;
// Assertions
`ASSERT_INIT(param_legal, DataWidth == 32)
endmodule