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/ram.sv
|
||||
../rtl/perips/rom.sv
|
||||
../rtl/perips/timer.sv
|
||||
../rtl/perips/machine_timer.sv
|
||||
../rtl/perips/rvic.sv
|
||||
../rtl/perips/uart/uart_reg_pkg.sv
|
||||
../rtl/perips/uart/uart_reg_top.sv
|
||||
|
@ -45,6 +43,10 @@
|
|||
../rtl/perips/uart/uart_rx.sv
|
||||
../rtl/perips/uart/uart_top.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_master_sel.sv
|
||||
|
|
|
@ -34,15 +34,12 @@
|
|||
// GPIO
|
||||
`define GPIO_ADDR_MASK ~32'hffff
|
||||
`define GPIO_ADDR_BASE 32'h30000000
|
||||
// Timer
|
||||
`define TIMER_ADDR_MASK ~32'hffff
|
||||
`define TIMER_ADDR_BASE 32'h40000000
|
||||
// Timer0
|
||||
`define TIMER0_ADDR_MASK ~32'hffff
|
||||
`define TIMER0_ADDR_BASE 32'h40000000
|
||||
// UART0
|
||||
`define UART0_ADDR_MASK ~32'hffff
|
||||
`define UART0_ADDR_BASE 32'h50000000
|
||||
// Machine Timer
|
||||
`define MTIMER_ADDR_MASK ~32'hffff
|
||||
`define MTIMER_ADDR_BASE 32'hA0000000
|
||||
// Interrupt controller
|
||||
`define RVIC_ADDR_MASK ~32'hffff
|
||||
`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 Ram = 1;
|
||||
localparam int JtagDevice = 2;
|
||||
localparam int Mtimer = 3;
|
||||
localparam int Timer0 = 3;
|
||||
localparam int Gpio = 4;
|
||||
localparam int Uart0 = 5;
|
||||
localparam int Rvic = 6;
|
||||
|
@ -105,7 +105,7 @@ module tinyriscv_soc_top #(
|
|||
wire int_req;
|
||||
wire[7:0] int_id;
|
||||
|
||||
wire mtimer_irq;
|
||||
wire timer0_irq;
|
||||
wire uart0_irq;
|
||||
|
||||
wire[1:0] io_in;
|
||||
|
@ -114,7 +114,7 @@ module tinyriscv_soc_top #(
|
|||
|
||||
always @ (*) begin
|
||||
irq_src = 32'h0;
|
||||
irq_src[0] = mtimer_irq;
|
||||
irq_src[0] = timer0_irq;
|
||||
irq_src[1] = uart0_irq;
|
||||
end
|
||||
|
||||
|
@ -187,18 +187,19 @@ module tinyriscv_soc_top #(
|
|||
.data_o (slave_rdata[Ram])
|
||||
);
|
||||
|
||||
assign slave_addr_mask[Mtimer] = `MTIMER_ADDR_MASK;
|
||||
assign slave_addr_base[Mtimer] = `MTIMER_ADDR_BASE;
|
||||
// 3.机器定时器模块
|
||||
machine_timer u_machine_timer(
|
||||
.clk (clk),
|
||||
.rst_n (ndmreset_n),
|
||||
.addr_i (slave_addr[Mtimer]),
|
||||
.data_i (slave_wdata[Mtimer]),
|
||||
.sel_i (slave_be[Mtimer]),
|
||||
.we_i (slave_we[Mtimer]),
|
||||
.data_o (slave_rdata[Mtimer]),
|
||||
.irq_o (mtimer_irq)
|
||||
assign slave_addr_mask[Timer0] = `TIMER0_ADDR_MASK;
|
||||
assign slave_addr_base[Timer0] = `TIMER0_ADDR_BASE;
|
||||
// 3.定时器0模块
|
||||
timer_top timer0(
|
||||
.clk_i (clk),
|
||||
.rst_ni (ndmreset_n),
|
||||
.irq_o (timer0_irq),
|
||||
.req_i (slave_req[Timer0]),
|
||||
.we_i (slave_we[Timer0]),
|
||||
.be_i (slave_be[Timer0]),
|
||||
.addr_i (slave_addr[Timer0]),
|
||||
.data_i (slave_wdata[Timer0]),
|
||||
.data_o (slave_rdata[Timer0])
|
||||
);
|
||||
|
||||
// IO0
|
||||
|
|
Loading…
Reference in New Issue