rtl: perips: rewrite timer module
Signed-off-by: liangkangnan <liangkangnan@163.com>pull/4/head
parent
f6c8a046c7
commit
64041b4d2b
|
@ -36,8 +36,6 @@
|
||||||
../rtl/perips/gpio.sv
|
../rtl/perips/gpio.sv
|
||||||
../rtl/perips/ram.sv
|
../rtl/perips/ram.sv
|
||||||
../rtl/perips/rom.sv
|
../rtl/perips/rom.sv
|
||||||
../rtl/perips/timer.sv
|
|
||||||
../rtl/perips/machine_timer.sv
|
|
||||||
../rtl/perips/rvic.sv
|
../rtl/perips/rvic.sv
|
||||||
../rtl/perips/uart/uart_reg_pkg.sv
|
../rtl/perips/uart/uart_reg_pkg.sv
|
||||||
../rtl/perips/uart/uart_reg_top.sv
|
../rtl/perips/uart/uart_reg_top.sv
|
||||||
|
@ -45,6 +43,10 @@
|
||||||
../rtl/perips/uart/uart_rx.sv
|
../rtl/perips/uart/uart_rx.sv
|
||||||
../rtl/perips/uart/uart_top.sv
|
../rtl/perips/uart/uart_top.sv
|
||||||
../rtl/perips/uart/uart_tx.sv
|
../rtl/perips/uart/uart_tx.sv
|
||||||
|
../rtl/perips/timer/timer_reg_pkg.sv
|
||||||
|
../rtl/perips/timer/timer_reg_top.sv
|
||||||
|
../rtl/perips/timer/timer_core.sv
|
||||||
|
../rtl/perips/timer/timer_top.sv
|
||||||
|
|
||||||
../rtl/sys_bus/obi_interconnect.sv
|
../rtl/sys_bus/obi_interconnect.sv
|
||||||
../rtl/sys_bus/obi_interconnect_master_sel.sv
|
../rtl/sys_bus/obi_interconnect_master_sel.sv
|
||||||
|
|
|
@ -34,15 +34,12 @@
|
||||||
// GPIO
|
// GPIO
|
||||||
`define GPIO_ADDR_MASK ~32'hffff
|
`define GPIO_ADDR_MASK ~32'hffff
|
||||||
`define GPIO_ADDR_BASE 32'h30000000
|
`define GPIO_ADDR_BASE 32'h30000000
|
||||||
// Timer
|
// Timer0
|
||||||
`define TIMER_ADDR_MASK ~32'hffff
|
`define TIMER0_ADDR_MASK ~32'hffff
|
||||||
`define TIMER_ADDR_BASE 32'h40000000
|
`define TIMER0_ADDR_BASE 32'h40000000
|
||||||
// UART0
|
// UART0
|
||||||
`define UART0_ADDR_MASK ~32'hffff
|
`define UART0_ADDR_MASK ~32'hffff
|
||||||
`define UART0_ADDR_BASE 32'h50000000
|
`define UART0_ADDR_BASE 32'h50000000
|
||||||
// Machine Timer
|
|
||||||
`define MTIMER_ADDR_MASK ~32'hffff
|
|
||||||
`define MTIMER_ADDR_BASE 32'hA0000000
|
|
||||||
// Interrupt controller
|
// Interrupt controller
|
||||||
`define RVIC_ADDR_MASK ~32'hffff
|
`define RVIC_ADDR_MASK ~32'hffff
|
||||||
`define RVIC_ADDR_BASE 32'hD0000000
|
`define RVIC_ADDR_BASE 32'hD0000000
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2021 Blue Liang, liangkangnan@163.com
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
module machine_timer(
|
|
||||||
|
|
||||||
input wire clk,
|
|
||||||
input wire rst_n,
|
|
||||||
input wire[31:0] addr_i,
|
|
||||||
input wire[31:0] data_i,
|
|
||||||
input wire[3:0] sel_i,
|
|
||||||
input wire we_i,
|
|
||||||
output wire[31:0] data_o,
|
|
||||||
output wire irq_o
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
localparam mtime_ctrl_reg = 4'h0;
|
|
||||||
localparam mtime_cmp_reg = 4'h4;
|
|
||||||
localparam mtime_count_reg = 4'h8;
|
|
||||||
|
|
||||||
localparam start = 0;
|
|
||||||
localparam irq_pending = 1;
|
|
||||||
|
|
||||||
reg[31:0] mtime_ctrl_d, mtime_ctrl_q;
|
|
||||||
reg[31:0] mtime_cmp_d, mtime_cmp_q;
|
|
||||||
reg[31:0] mtime_count_q;
|
|
||||||
reg[31:0] data_d, data_q;
|
|
||||||
|
|
||||||
wire[3:0] rw_addr = addr_i[3:0];
|
|
||||||
wire w0 = we_i & sel_i[0];
|
|
||||||
wire w1 = we_i & sel_i[1];
|
|
||||||
wire w2 = we_i & sel_i[2];
|
|
||||||
wire w3 = we_i & sel_i[3];
|
|
||||||
|
|
||||||
// write
|
|
||||||
always @ (*) begin
|
|
||||||
mtime_cmp_d = mtime_cmp_q;
|
|
||||||
mtime_ctrl_d = mtime_ctrl_q;
|
|
||||||
|
|
||||||
case (rw_addr)
|
|
||||||
mtime_ctrl_reg: begin
|
|
||||||
if (w0) begin
|
|
||||||
mtime_ctrl_d[0] = data_i[0];
|
|
||||||
mtime_ctrl_d[irq_pending] = mtime_ctrl_q & (~data_i[irq_pending]);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
mtime_cmp_reg: begin
|
|
||||||
if (w0) mtime_cmp_d[7:0] = data_i[7:0];
|
|
||||||
if (w1) mtime_cmp_d[15:8] = data_i[15:8];
|
|
||||||
if (w2) mtime_cmp_d[23:16] = data_i[23:16];
|
|
||||||
if (w3) mtime_cmp_d[31:24] = data_i[31:24];
|
|
||||||
end
|
|
||||||
|
|
||||||
default:;
|
|
||||||
endcase
|
|
||||||
|
|
||||||
if (mtime_count_q == mtime_cmp_q) begin
|
|
||||||
mtime_ctrl_d[irq_pending] = 1'b1;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
assign irq_o = mtime_ctrl_q[irq_pending] & mtime_ctrl_q[start];
|
|
||||||
|
|
||||||
// read
|
|
||||||
always @ (*) begin
|
|
||||||
data_d = data_q;
|
|
||||||
|
|
||||||
case (rw_addr)
|
|
||||||
mtime_ctrl_reg: data_d = mtime_ctrl_q;
|
|
||||||
mtime_cmp_reg: data_d = mtime_cmp_q;
|
|
||||||
mtime_count_reg: data_d = mtime_count_q;
|
|
||||||
default:;
|
|
||||||
endcase
|
|
||||||
end
|
|
||||||
|
|
||||||
assign data_o = data_q;
|
|
||||||
|
|
||||||
always @ (posedge clk or negedge rst_n) begin
|
|
||||||
if (!rst_n) begin
|
|
||||||
mtime_ctrl_q <= 32'h0;
|
|
||||||
mtime_cmp_q <= 32'h0;
|
|
||||||
data_q <= 32'h0;
|
|
||||||
end else begin
|
|
||||||
mtime_ctrl_q <= mtime_ctrl_d;
|
|
||||||
mtime_cmp_q <= mtime_cmp_d;
|
|
||||||
data_q <= data_d;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
always @ (posedge clk or negedge rst_n) begin
|
|
||||||
if (!rst_n) begin
|
|
||||||
mtime_count_q <= 32'h0;
|
|
||||||
end else begin
|
|
||||||
if (mtime_ctrl_q[start]) begin
|
|
||||||
mtime_count_q <= mtime_count_q + 1'b1;
|
|
||||||
if (mtime_count_q == mtime_cmp_q) begin
|
|
||||||
mtime_count_q <= 32'h0;
|
|
||||||
end
|
|
||||||
end else begin
|
|
||||||
mtime_count_q <= 32'h0;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
endmodule
|
|
|
@ -1,151 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 Blue Liang, liangkangnan@163.com
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
`include "../core/defines.sv"
|
|
||||||
|
|
||||||
// 32位向上计数定时器模块
|
|
||||||
module timer(
|
|
||||||
|
|
||||||
input wire clk,
|
|
||||||
input wire rst_n,
|
|
||||||
input wire[31:0] addr_i,
|
|
||||||
input wire[31:0] data_i,
|
|
||||||
input wire[3:0] sel_i,
|
|
||||||
input wire we_i,
|
|
||||||
output wire[31:0] data_o,
|
|
||||||
output wire int_sig_o
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
// 寄存器(偏移)地址
|
|
||||||
localparam REG_CTRL = 4'h0;
|
|
||||||
localparam REG_COUNT = 4'h4;
|
|
||||||
localparam REG_VALUE = 4'h8;
|
|
||||||
|
|
||||||
// 定时器控制寄存器,可读可写
|
|
||||||
// bit[0]: 定时器使能
|
|
||||||
// bit[1]: 定时器中断使能
|
|
||||||
// bit[2]: 定时器中断pending标志,写1清零
|
|
||||||
reg[31:0] timer_ctrl;
|
|
||||||
|
|
||||||
// 定时器当前计数值寄存器, 只读
|
|
||||||
reg[31:0] timer_count;
|
|
||||||
|
|
||||||
// 定时器溢出值寄存器,当定时器计数值达到该值时产生pending,可读可写
|
|
||||||
reg[31:0] timer_value;
|
|
||||||
|
|
||||||
wire wen = we_i & req_valid_i;
|
|
||||||
wire ren = (~we_i) & req_valid_i;
|
|
||||||
wire timer_en = (timer_ctrl[0] == 1'b1);
|
|
||||||
wire timer_int_en = (timer_ctrl[1] == 1'b1);
|
|
||||||
wire timer_expired = (timer_count >= timer_value);
|
|
||||||
wire write_reg_ctrl_en = wen & (addr_i[3:0] == REG_CTRL);
|
|
||||||
wire write_reg_value_en = wen & (addr_i[3:0] == REG_VALUE);
|
|
||||||
|
|
||||||
// 计数
|
|
||||||
always @ (posedge clk or negedge rst_n) begin
|
|
||||||
if (!rst_n) begin
|
|
||||||
timer_count <= 32'h0;
|
|
||||||
end else begin
|
|
||||||
if (timer_en) begin
|
|
||||||
if (timer_expired) begin
|
|
||||||
timer_count <= 32'h0;
|
|
||||||
end else begin
|
|
||||||
timer_count <= timer_count + 1'b1;
|
|
||||||
end
|
|
||||||
end else begin
|
|
||||||
timer_count <= 32'h0;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
reg int_sig_r;
|
|
||||||
// 产生中断信号
|
|
||||||
always @ (posedge clk or negedge rst_n) begin
|
|
||||||
if (!rst_n) begin
|
|
||||||
int_sig_r <= 1'b0;
|
|
||||||
end else begin
|
|
||||||
if (write_reg_ctrl_en & (data_i[2] == 1'b1)) begin
|
|
||||||
int_sig_r <= 1'b0;
|
|
||||||
end else if (timer_int_en & timer_en & timer_expired) begin
|
|
||||||
int_sig_r <= 1'b1;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
assign int_sig_o = int_sig_r;
|
|
||||||
|
|
||||||
// 写timer_ctrl
|
|
||||||
always @ (posedge clk or negedge rst_n) begin
|
|
||||||
if (!rst_n) begin
|
|
||||||
timer_ctrl <= 32'h0;
|
|
||||||
end else begin
|
|
||||||
if (write_reg_ctrl_en) begin
|
|
||||||
if (sel_i[0]) begin
|
|
||||||
timer_ctrl[7:0] <= {data_i[7:3], timer_ctrl[2] & (~data_i[2]), data_i[1:0]};
|
|
||||||
end
|
|
||||||
end else begin
|
|
||||||
if (timer_expired) begin
|
|
||||||
timer_ctrl[0] <= 1'b0;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
// 写timer_value
|
|
||||||
always @ (posedge clk or negedge rst_n) begin
|
|
||||||
if (!rst_n) begin
|
|
||||||
timer_value <= 32'h0;
|
|
||||||
end else begin
|
|
||||||
if (write_reg_value_en) begin
|
|
||||||
if (sel_i[0]) begin
|
|
||||||
timer_value[7:0] <= data_i[7:0];
|
|
||||||
end
|
|
||||||
if (sel_i[1]) begin
|
|
||||||
timer_value[15:8] <= data_i[15:8];
|
|
||||||
end
|
|
||||||
if (sel_i[2]) begin
|
|
||||||
timer_value[23:16] <= data_i[23:16];
|
|
||||||
end
|
|
||||||
if (sel_i[3]) begin
|
|
||||||
timer_value[31:24] <= data_i[31:24];
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
reg[31:0] data_r;
|
|
||||||
// 读寄存器
|
|
||||||
always @ (posedge clk or negedge rst_n) begin
|
|
||||||
if (!rst_n) begin
|
|
||||||
data_r <= 32'h0;
|
|
||||||
end else begin
|
|
||||||
if (ren) begin
|
|
||||||
case (addr_i[3:0])
|
|
||||||
REG_VALUE: data_r <= timer_value;
|
|
||||||
REG_CTRL: data_r <= timer_ctrl;
|
|
||||||
REG_COUNT: data_r <= timer_count;
|
|
||||||
default: data_r <= 32'h0;
|
|
||||||
endcase
|
|
||||||
end else begin
|
|
||||||
data_r <= 32'h0;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
assign data_o = data_r;
|
|
||||||
|
|
||||||
endmodule
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright lowRISC contributors.
|
||||||
|
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
{ name: "timer",
|
||||||
|
clocking: [{clock: "clk_i", reset: "rst_ni"}],
|
||||||
|
bus_interfaces: [
|
||||||
|
{ protocol: "tlul", direction: "device" }
|
||||||
|
],
|
||||||
|
regwidth: "32",
|
||||||
|
registers: [
|
||||||
|
{ name: "CTRL",
|
||||||
|
desc: "Timer control register",
|
||||||
|
swaccess: "rw",
|
||||||
|
hwaccess: "hrw",
|
||||||
|
hwqe: "true",
|
||||||
|
fields: [
|
||||||
|
{ bits: "0",
|
||||||
|
name: "EN",
|
||||||
|
desc: "Timer enable(start)",
|
||||||
|
}
|
||||||
|
{ bits: "1",
|
||||||
|
name: "INT_EN",
|
||||||
|
desc: "Timer interrupt enable",
|
||||||
|
}
|
||||||
|
{ bits: "2",
|
||||||
|
name: "INT_PENDING",
|
||||||
|
swaccess: "rw1c",
|
||||||
|
desc: "Timer interrupt pending",
|
||||||
|
}
|
||||||
|
{ bits: "3",
|
||||||
|
name: "MODE",
|
||||||
|
desc: "Timer mode",
|
||||||
|
}
|
||||||
|
{ bits: "31:8",
|
||||||
|
name: "CLK_DIV",
|
||||||
|
desc: "Timer clock divider count",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
{ name: "VALUE",
|
||||||
|
desc: "Timer expired value register",
|
||||||
|
swaccess: "rw",
|
||||||
|
hwaccess: "hro",
|
||||||
|
fields: [
|
||||||
|
{ bits: "31:0" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
{ name: "COUNT",
|
||||||
|
desc: "Timer current count register",
|
||||||
|
swaccess: "ro",
|
||||||
|
hwaccess: "hrw",
|
||||||
|
hwext: "true",
|
||||||
|
fields: [
|
||||||
|
{ bits: "31:0" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 Blue Liang, liangkangnan@163.com
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module timer_core (
|
||||||
|
input logic clk_i,
|
||||||
|
input logic rst_ni,
|
||||||
|
|
||||||
|
output logic irq_o,
|
||||||
|
|
||||||
|
input logic reg_we_i,
|
||||||
|
input logic reg_re_i,
|
||||||
|
input logic [31:0] reg_wdata_i,
|
||||||
|
input logic [ 3:0] reg_be_i,
|
||||||
|
input logic [31:0] reg_addr_i,
|
||||||
|
output logic [31:0] reg_rdata_o
|
||||||
|
);
|
||||||
|
|
||||||
|
import timer_reg_pkg::*;
|
||||||
|
|
||||||
|
timer_reg_pkg::timer_reg2hw_t reg2hw;
|
||||||
|
timer_reg_pkg::timer_hw2reg_t hw2reg;
|
||||||
|
|
||||||
|
logic start;
|
||||||
|
logic int_en;
|
||||||
|
logic int_pending;
|
||||||
|
logic mode;
|
||||||
|
logic [23:0] div_cont;
|
||||||
|
logic [31:0] count_q;
|
||||||
|
logic [31:0] value;
|
||||||
|
logic tick;
|
||||||
|
|
||||||
|
assign start = reg2hw.ctrl.en.q;
|
||||||
|
assign int_en = reg2hw.ctrl.int_en.q;
|
||||||
|
assign int_pending = reg2hw.ctrl.int_pending.q;
|
||||||
|
assign mode = reg2hw.ctrl.mode.q;
|
||||||
|
assign div_cont = reg2hw.ctrl.clk_div.q;
|
||||||
|
assign value = reg2hw.value.q;
|
||||||
|
|
||||||
|
// 当前计数值
|
||||||
|
assign hw2reg.count.d = count_q;
|
||||||
|
assign hw2reg.ctrl.int_pending.d = 1'b1;
|
||||||
|
assign hw2reg.ctrl.int_pending.de = (count_q == value) && int_en && start;
|
||||||
|
|
||||||
|
assign hw2reg.ctrl.en.d = 1'b0;
|
||||||
|
assign hw2reg.ctrl.en.de = (count_q == value) && (mode == 1'b0) && start;
|
||||||
|
|
||||||
|
assign irq_o = int_pending;
|
||||||
|
|
||||||
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||||
|
if (!rst_ni) begin
|
||||||
|
count_q <= 32'h0;
|
||||||
|
end else begin
|
||||||
|
if (start) begin
|
||||||
|
if (tick) begin
|
||||||
|
count_q <= count_q + 1'b1;
|
||||||
|
if (count_q == value) begin
|
||||||
|
count_q <= 32'h0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end else begin
|
||||||
|
count_q <= 32'h0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
clk_div #(
|
||||||
|
.RATIO_WIDTH(24)
|
||||||
|
) u_clk_div (
|
||||||
|
.clk_i(clk_i),
|
||||||
|
.rst_ni(rst_ni || (~(count_q == value))),
|
||||||
|
.en_i(start),
|
||||||
|
.ratio_i(div_cont),
|
||||||
|
.clk_o(tick)
|
||||||
|
);
|
||||||
|
|
||||||
|
timer_reg_top u_timer_reg_top (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
.reg2hw (reg2hw),
|
||||||
|
.hw2reg (hw2reg),
|
||||||
|
.reg_we (reg_we_i),
|
||||||
|
.reg_re (reg_re_i),
|
||||||
|
.reg_wdata (reg_wdata_i),
|
||||||
|
.reg_be (reg_be_i),
|
||||||
|
.reg_addr (reg_addr_i),
|
||||||
|
.reg_rdata (reg_rdata_o)
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright lowRISC contributors.
|
||||||
|
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// Register Package auto-generated by `reggen` containing data structure
|
||||||
|
|
||||||
|
package timer_reg_pkg;
|
||||||
|
|
||||||
|
// Address widths within the block
|
||||||
|
parameter int BlockAw = 4;
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// Typedefs for registers //
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
struct packed {
|
||||||
|
logic q;
|
||||||
|
logic qe;
|
||||||
|
} en;
|
||||||
|
struct packed {
|
||||||
|
logic q;
|
||||||
|
logic qe;
|
||||||
|
} int_en;
|
||||||
|
struct packed {
|
||||||
|
logic q;
|
||||||
|
logic qe;
|
||||||
|
} int_pending;
|
||||||
|
struct packed {
|
||||||
|
logic q;
|
||||||
|
logic qe;
|
||||||
|
} mode;
|
||||||
|
struct packed {
|
||||||
|
logic [23:0] q;
|
||||||
|
logic qe;
|
||||||
|
} clk_div;
|
||||||
|
} timer_reg2hw_ctrl_reg_t;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
logic [31:0] q;
|
||||||
|
} timer_reg2hw_value_reg_t;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
logic [31:0] q;
|
||||||
|
} timer_reg2hw_count_reg_t;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
struct packed {
|
||||||
|
logic d;
|
||||||
|
logic de;
|
||||||
|
} en;
|
||||||
|
struct packed {
|
||||||
|
logic d;
|
||||||
|
logic de;
|
||||||
|
} int_en;
|
||||||
|
struct packed {
|
||||||
|
logic d;
|
||||||
|
logic de;
|
||||||
|
} int_pending;
|
||||||
|
struct packed {
|
||||||
|
logic d;
|
||||||
|
logic de;
|
||||||
|
} mode;
|
||||||
|
struct packed {
|
||||||
|
logic [23:0] d;
|
||||||
|
logic de;
|
||||||
|
} clk_div;
|
||||||
|
} timer_hw2reg_ctrl_reg_t;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
logic [31:0] d;
|
||||||
|
} timer_hw2reg_count_reg_t;
|
||||||
|
|
||||||
|
// Register -> HW type
|
||||||
|
typedef struct packed {
|
||||||
|
timer_reg2hw_ctrl_reg_t ctrl; // [96:64]
|
||||||
|
timer_reg2hw_value_reg_t value; // [63:32]
|
||||||
|
timer_reg2hw_count_reg_t count; // [31:0]
|
||||||
|
} timer_reg2hw_t;
|
||||||
|
|
||||||
|
// HW -> register type
|
||||||
|
typedef struct packed {
|
||||||
|
timer_hw2reg_ctrl_reg_t ctrl; // [64:32]
|
||||||
|
timer_hw2reg_count_reg_t count; // [31:0]
|
||||||
|
} timer_hw2reg_t;
|
||||||
|
|
||||||
|
// Register offsets
|
||||||
|
parameter logic [BlockAw-1:0] TIMER_CTRL_OFFSET = 4'h0;
|
||||||
|
parameter logic [BlockAw-1:0] TIMER_VALUE_OFFSET = 4'h4;
|
||||||
|
parameter logic [BlockAw-1:0] TIMER_COUNT_OFFSET = 4'h8;
|
||||||
|
|
||||||
|
// Reset values for hwext registers and their fields
|
||||||
|
parameter logic [31:0] TIMER_COUNT_RESVAL = 32'h0;
|
||||||
|
|
||||||
|
// Register index
|
||||||
|
typedef enum int {
|
||||||
|
TIMER_CTRL,
|
||||||
|
TIMER_VALUE,
|
||||||
|
TIMER_COUNT
|
||||||
|
} timer_id_e;
|
||||||
|
|
||||||
|
// Register width information to check illegal writes
|
||||||
|
parameter logic [3:0] TIMER_PERMIT [3] = '{
|
||||||
|
4'b1111, // index[0] TIMER_CTRL
|
||||||
|
4'b1111, // index[1] TIMER_VALUE
|
||||||
|
4'b1111 // index[2] TIMER_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
endpackage
|
||||||
|
|
|
@ -0,0 +1,303 @@
|
||||||
|
// Copyright lowRISC contributors.
|
||||||
|
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// Register Top module auto-generated by `reggen`
|
||||||
|
|
||||||
|
|
||||||
|
module timer_reg_top (
|
||||||
|
input logic clk_i,
|
||||||
|
input logic rst_ni,
|
||||||
|
|
||||||
|
// To HW
|
||||||
|
output timer_reg_pkg::timer_reg2hw_t reg2hw, // Write
|
||||||
|
input timer_reg_pkg::timer_hw2reg_t hw2reg, // Read
|
||||||
|
|
||||||
|
input logic reg_we,
|
||||||
|
input logic reg_re,
|
||||||
|
input logic [31:0] reg_wdata,
|
||||||
|
input logic [ 3:0] reg_be,
|
||||||
|
input logic [31:0] reg_addr,
|
||||||
|
output logic [31:0] reg_rdata
|
||||||
|
);
|
||||||
|
|
||||||
|
import timer_reg_pkg::* ;
|
||||||
|
|
||||||
|
localparam int AW = 4;
|
||||||
|
localparam int DW = 32;
|
||||||
|
localparam int DBW = DW/8; // Byte Width
|
||||||
|
|
||||||
|
logic reg_error;
|
||||||
|
logic addrmiss, wr_err;
|
||||||
|
|
||||||
|
logic [DW-1:0] reg_rdata_next;
|
||||||
|
|
||||||
|
assign reg_rdata = reg_rdata_next;
|
||||||
|
assign reg_error = wr_err;
|
||||||
|
|
||||||
|
// Define SW related signals
|
||||||
|
// Format: <reg>_<field>_{wd|we|qs}
|
||||||
|
// or <reg>_{wd|we|qs} if field == 1 or 0
|
||||||
|
logic ctrl_we;
|
||||||
|
logic ctrl_en_qs;
|
||||||
|
logic ctrl_en_wd;
|
||||||
|
logic ctrl_int_en_qs;
|
||||||
|
logic ctrl_int_en_wd;
|
||||||
|
logic ctrl_int_pending_qs;
|
||||||
|
logic ctrl_int_pending_wd;
|
||||||
|
logic ctrl_mode_qs;
|
||||||
|
logic ctrl_mode_wd;
|
||||||
|
logic [23:0] ctrl_clk_div_qs;
|
||||||
|
logic [23:0] ctrl_clk_div_wd;
|
||||||
|
logic value_we;
|
||||||
|
logic [31:0] value_qs;
|
||||||
|
logic [31:0] value_wd;
|
||||||
|
logic count_re;
|
||||||
|
logic [31:0] count_qs;
|
||||||
|
|
||||||
|
// Register instances
|
||||||
|
// R[ctrl]: V(False)
|
||||||
|
|
||||||
|
// F[en]: 0:0
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (1),
|
||||||
|
.SWACCESS("RW"),
|
||||||
|
.RESVAL (1'h0)
|
||||||
|
) u_ctrl_en (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (ctrl_we),
|
||||||
|
.wd (ctrl_en_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.ctrl.en.de),
|
||||||
|
.d (hw2reg.ctrl.en.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (reg2hw.ctrl.en.qe),
|
||||||
|
.q (reg2hw.ctrl.en.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (ctrl_en_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// F[int_en]: 1:1
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (1),
|
||||||
|
.SWACCESS("RW"),
|
||||||
|
.RESVAL (1'h0)
|
||||||
|
) u_ctrl_int_en (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (ctrl_we),
|
||||||
|
.wd (ctrl_int_en_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.ctrl.int_en.de),
|
||||||
|
.d (hw2reg.ctrl.int_en.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (reg2hw.ctrl.int_en.qe),
|
||||||
|
.q (reg2hw.ctrl.int_en.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (ctrl_int_en_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// F[int_pending]: 2:2
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (1),
|
||||||
|
.SWACCESS("W1C"),
|
||||||
|
.RESVAL (1'h0)
|
||||||
|
) u_ctrl_int_pending (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (ctrl_we),
|
||||||
|
.wd (ctrl_int_pending_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.ctrl.int_pending.de),
|
||||||
|
.d (hw2reg.ctrl.int_pending.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (reg2hw.ctrl.int_pending.qe),
|
||||||
|
.q (reg2hw.ctrl.int_pending.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (ctrl_int_pending_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// F[mode]: 3:3
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (1),
|
||||||
|
.SWACCESS("RW"),
|
||||||
|
.RESVAL (1'h0)
|
||||||
|
) u_ctrl_mode (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (ctrl_we),
|
||||||
|
.wd (ctrl_mode_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.ctrl.mode.de),
|
||||||
|
.d (hw2reg.ctrl.mode.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (reg2hw.ctrl.mode.qe),
|
||||||
|
.q (reg2hw.ctrl.mode.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (ctrl_mode_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// F[clk_div]: 31:8
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (24),
|
||||||
|
.SWACCESS("RW"),
|
||||||
|
.RESVAL (24'h0)
|
||||||
|
) u_ctrl_clk_div (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (ctrl_we),
|
||||||
|
.wd (ctrl_clk_div_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.ctrl.clk_div.de),
|
||||||
|
.d (hw2reg.ctrl.clk_div.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (reg2hw.ctrl.clk_div.qe),
|
||||||
|
.q (reg2hw.ctrl.clk_div.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (ctrl_clk_div_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// R[value]: V(False)
|
||||||
|
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (32),
|
||||||
|
.SWACCESS("RW"),
|
||||||
|
.RESVAL (32'h0)
|
||||||
|
) u_value (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (value_we),
|
||||||
|
.wd (value_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (1'b0),
|
||||||
|
.d ('0),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (),
|
||||||
|
.q (reg2hw.value.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (value_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// R[count]: V(True)
|
||||||
|
|
||||||
|
prim_subreg_ext #(
|
||||||
|
.DW (32)
|
||||||
|
) u_count (
|
||||||
|
.re (count_re),
|
||||||
|
.we (1'b0),
|
||||||
|
.wd ('0),
|
||||||
|
.d (hw2reg.count.d),
|
||||||
|
.qre (),
|
||||||
|
.qe (),
|
||||||
|
.q (reg2hw.count.q),
|
||||||
|
.qs (count_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
logic [2:0] addr_hit;
|
||||||
|
always_comb begin
|
||||||
|
addr_hit = '0;
|
||||||
|
addr_hit[0] = (reg_addr == TIMER_CTRL_OFFSET);
|
||||||
|
addr_hit[1] = (reg_addr == TIMER_VALUE_OFFSET);
|
||||||
|
addr_hit[2] = (reg_addr == TIMER_COUNT_OFFSET);
|
||||||
|
end
|
||||||
|
|
||||||
|
assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ;
|
||||||
|
|
||||||
|
// Check sub-word write is permitted
|
||||||
|
always_comb begin
|
||||||
|
wr_err = (reg_we &
|
||||||
|
((addr_hit[0] & (|(TIMER_PERMIT[0] & ~reg_be))) |
|
||||||
|
(addr_hit[1] & (|(TIMER_PERMIT[1] & ~reg_be))) |
|
||||||
|
(addr_hit[2] & (|(TIMER_PERMIT[2] & ~reg_be)))));
|
||||||
|
end
|
||||||
|
|
||||||
|
assign ctrl_we = addr_hit[0] & reg_we & !reg_error;
|
||||||
|
|
||||||
|
assign ctrl_en_wd = reg_wdata[0];
|
||||||
|
|
||||||
|
assign ctrl_int_en_wd = reg_wdata[1];
|
||||||
|
|
||||||
|
assign ctrl_int_pending_wd = reg_wdata[2];
|
||||||
|
|
||||||
|
assign ctrl_mode_wd = reg_wdata[3];
|
||||||
|
|
||||||
|
assign ctrl_clk_div_wd = reg_wdata[31:8];
|
||||||
|
assign value_we = addr_hit[1] & reg_we & !reg_error;
|
||||||
|
|
||||||
|
assign value_wd = reg_wdata[31:0];
|
||||||
|
assign count_re = addr_hit[2] & reg_re & !reg_error;
|
||||||
|
|
||||||
|
// Read data return
|
||||||
|
always_comb begin
|
||||||
|
reg_rdata_next = '0;
|
||||||
|
unique case (1'b1)
|
||||||
|
addr_hit[0]: begin
|
||||||
|
reg_rdata_next[0] = ctrl_en_qs;
|
||||||
|
reg_rdata_next[1] = ctrl_int_en_qs;
|
||||||
|
reg_rdata_next[2] = ctrl_int_pending_qs;
|
||||||
|
reg_rdata_next[3] = ctrl_mode_qs;
|
||||||
|
reg_rdata_next[31:8] = ctrl_clk_div_qs;
|
||||||
|
end
|
||||||
|
|
||||||
|
addr_hit[1]: begin
|
||||||
|
reg_rdata_next[31:0] = value_qs;
|
||||||
|
end
|
||||||
|
|
||||||
|
addr_hit[2]: begin
|
||||||
|
reg_rdata_next[31:0] = count_qs;
|
||||||
|
end
|
||||||
|
|
||||||
|
default: begin
|
||||||
|
reg_rdata_next = '1;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
// Unused signal tieoff
|
||||||
|
|
||||||
|
// wdata / byte enable are not always fully used
|
||||||
|
// add a blanket unused statement to handle lint waivers
|
||||||
|
logic unused_wdata;
|
||||||
|
logic unused_be;
|
||||||
|
assign unused_wdata = ^reg_wdata;
|
||||||
|
assign unused_be = ^reg_be;
|
||||||
|
|
||||||
|
endmodule
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 Blue Liang, liangkangnan@163.com
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module timer_top (
|
||||||
|
input logic clk_i,
|
||||||
|
input logic rst_ni,
|
||||||
|
|
||||||
|
output logic irq_o,
|
||||||
|
|
||||||
|
// OBI总线接口信号
|
||||||
|
input logic req_i,
|
||||||
|
input logic we_i,
|
||||||
|
input logic [ 3:0] be_i,
|
||||||
|
input logic [31:0] addr_i,
|
||||||
|
input logic [31:0] data_i,
|
||||||
|
output logic [31:0] data_o
|
||||||
|
);
|
||||||
|
|
||||||
|
logic re;
|
||||||
|
logic we;
|
||||||
|
logic [31:0] addr;
|
||||||
|
logic [31:0] reg_rdata;
|
||||||
|
|
||||||
|
// 读信号
|
||||||
|
assign re = req_i & (!we_i);
|
||||||
|
// 写信号
|
||||||
|
assign we = req_i & we_i;
|
||||||
|
// 去掉基地址
|
||||||
|
assign addr = {16'h0, addr_i[15:0]};
|
||||||
|
|
||||||
|
timer_core u_timer_core (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
.irq_o (irq_o),
|
||||||
|
.reg_we_i (we),
|
||||||
|
.reg_re_i (re),
|
||||||
|
.reg_wdata_i(data_i),
|
||||||
|
.reg_be_i (be_i),
|
||||||
|
.reg_addr_i (addr),
|
||||||
|
.reg_rdata_o(reg_rdata)
|
||||||
|
);
|
||||||
|
|
||||||
|
always_ff @(posedge clk_i) begin
|
||||||
|
data_o <= reg_rdata;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
|
@ -55,7 +55,7 @@ module tinyriscv_soc_top #(
|
||||||
localparam int Rom = 0;
|
localparam int Rom = 0;
|
||||||
localparam int Ram = 1;
|
localparam int Ram = 1;
|
||||||
localparam int JtagDevice = 2;
|
localparam int JtagDevice = 2;
|
||||||
localparam int Mtimer = 3;
|
localparam int Timer0 = 3;
|
||||||
localparam int Gpio = 4;
|
localparam int Gpio = 4;
|
||||||
localparam int Uart0 = 5;
|
localparam int Uart0 = 5;
|
||||||
localparam int Rvic = 6;
|
localparam int Rvic = 6;
|
||||||
|
@ -105,7 +105,7 @@ module tinyriscv_soc_top #(
|
||||||
wire int_req;
|
wire int_req;
|
||||||
wire[7:0] int_id;
|
wire[7:0] int_id;
|
||||||
|
|
||||||
wire mtimer_irq;
|
wire timer0_irq;
|
||||||
wire uart0_irq;
|
wire uart0_irq;
|
||||||
|
|
||||||
wire[1:0] io_in;
|
wire[1:0] io_in;
|
||||||
|
@ -114,7 +114,7 @@ module tinyriscv_soc_top #(
|
||||||
|
|
||||||
always @ (*) begin
|
always @ (*) begin
|
||||||
irq_src = 32'h0;
|
irq_src = 32'h0;
|
||||||
irq_src[0] = mtimer_irq;
|
irq_src[0] = timer0_irq;
|
||||||
irq_src[1] = uart0_irq;
|
irq_src[1] = uart0_irq;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -187,18 +187,19 @@ module tinyriscv_soc_top #(
|
||||||
.data_o (slave_rdata[Ram])
|
.data_o (slave_rdata[Ram])
|
||||||
);
|
);
|
||||||
|
|
||||||
assign slave_addr_mask[Mtimer] = `MTIMER_ADDR_MASK;
|
assign slave_addr_mask[Timer0] = `TIMER0_ADDR_MASK;
|
||||||
assign slave_addr_base[Mtimer] = `MTIMER_ADDR_BASE;
|
assign slave_addr_base[Timer0] = `TIMER0_ADDR_BASE;
|
||||||
// 3.机器定时器模块
|
// 3.定时器0模块
|
||||||
machine_timer u_machine_timer(
|
timer_top timer0(
|
||||||
.clk (clk),
|
.clk_i (clk),
|
||||||
.rst_n (ndmreset_n),
|
.rst_ni (ndmreset_n),
|
||||||
.addr_i (slave_addr[Mtimer]),
|
.irq_o (timer0_irq),
|
||||||
.data_i (slave_wdata[Mtimer]),
|
.req_i (slave_req[Timer0]),
|
||||||
.sel_i (slave_be[Mtimer]),
|
.we_i (slave_we[Timer0]),
|
||||||
.we_i (slave_we[Mtimer]),
|
.be_i (slave_be[Timer0]),
|
||||||
.data_o (slave_rdata[Mtimer]),
|
.addr_i (slave_addr[Timer0]),
|
||||||
.irq_o (mtimer_irq)
|
.data_i (slave_wdata[Timer0]),
|
||||||
|
.data_o (slave_rdata[Timer0])
|
||||||
);
|
);
|
||||||
|
|
||||||
// IO0
|
// IO0
|
||||||
|
|
Loading…
Reference in New Issue