143 lines
3.4 KiB
Verilog
143 lines
3.4 KiB
Verilog
/*****************************************************************************\
|
|
| Copyright (C) 2022 Luke Wren |
|
|
| SPDX-License-Identifier: Apache-2.0 |
|
|
\*****************************************************************************/
|
|
|
|
`default_nettype none
|
|
|
|
// Basic implementation of standard 64-bit RISC-V timer with 32-bit APB.
|
|
|
|
// TICK_IS_NRZ = 1: tick is an NRZ signal that is asynchronous to clk.
|
|
// TICK_IS_NRZ = 0: tick is a level-sensitive signal that is synchronous to clk.
|
|
|
|
module hazard3_riscv_timer #(
|
|
parameter TICK_IS_NRZ = 0
|
|
) (
|
|
input wire clk,
|
|
input wire rst_n,
|
|
|
|
input wire [15:0] paddr,
|
|
input wire psel,
|
|
input wire penable,
|
|
input wire pwrite,
|
|
input wire [31:0] pwdata,
|
|
output reg [31:0] prdata,
|
|
output wire pready,
|
|
output wire pslverr,
|
|
|
|
input wire dbg_halt,
|
|
input wire tick,
|
|
|
|
output reg timer_irq
|
|
);
|
|
|
|
localparam ADDR_CTRL = 16'h0000;
|
|
localparam ADDR_MTIME = 16'h0008;
|
|
localparam ADDR_MTIMEH = 16'h000c;
|
|
localparam ADDR_MTIMECMP = 16'h0010;
|
|
localparam ADDR_MTIMECMPH = 16'h0014;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Timer tick logic
|
|
|
|
wire tick_event;
|
|
|
|
generate
|
|
if (TICK_IS_NRZ) begin: edge_detect
|
|
|
|
wire tick_nrz_sync;
|
|
|
|
hazard3_sync_1bit tick_sync_u (
|
|
.clk (clk),
|
|
.rst_n (rst_n),
|
|
.i (tick_nrz),
|
|
.o (tick_nrz_sync)
|
|
);
|
|
|
|
reg tick_nrz_prev;
|
|
always @ (posedge clk or negedge rst_n) begin
|
|
if (!rst_n) begin
|
|
tick_nrz_prev <= 1'b0;
|
|
end else begin
|
|
tick_nrz_prev <= tick_nrz_sync;
|
|
end
|
|
end
|
|
|
|
assign tick_event = tick_nrz_sync ^ tick_nrz_sync_prev;
|
|
|
|
end else begin: no_edge_detect
|
|
|
|
assign tick_event = tick;
|
|
|
|
end
|
|
endgenerate
|
|
|
|
reg ctrl_en;
|
|
wire tick_now = tick_event && ctrl_en && !dbg_halt;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Counter registers
|
|
|
|
wire bus_write = pwrite && psel && penable;
|
|
wire bus_read = !pwrite && psel && penable;
|
|
|
|
always @ (posedge clk or negedge rst_n) begin
|
|
if (!rst_n) begin
|
|
ctrl_en <= 1'b1;
|
|
end else if (bus_write && paddr == ADDR_CTRL) begin
|
|
ctrl_en <= pwdata[0];
|
|
end
|
|
end
|
|
|
|
reg [63:0] mtime;
|
|
|
|
always @ (posedge clk or negedge rst_n) begin
|
|
if (!rst_n) begin
|
|
mtime <= 64'h0;
|
|
end else begin
|
|
if (tick_now)
|
|
mtime <= mtime + 1'b1;
|
|
if (bus_write && paddr == ADDR_MTIME)
|
|
mtime[31:0] <= pwdata;
|
|
if (bus_write && paddr == ADDR_MTIMEH)
|
|
mtime[63:32] <= pwdata;
|
|
end
|
|
end
|
|
|
|
// mtimecmp is stored inverted for minor LUT savings on iCE40
|
|
reg [63:0] mtimecmp;
|
|
wire [64:0] cmp_diff = {1'b0, mtime} + {1'b0, mtimecmp} + 65'd1;
|
|
|
|
always @ (posedge clk or negedge rst_n) begin
|
|
if (!rst_n) begin
|
|
mtimecmp <= 64'h0;
|
|
timer_irq <= 1'b0;
|
|
end else begin
|
|
if (bus_write && paddr == ADDR_MTIMECMP)
|
|
mtimecmp[31:0] <= ~pwdata;
|
|
if (bus_write && paddr == ADDR_MTIMECMPH)
|
|
mtimecmp[63:32] <= ~pwdata;
|
|
timer_irq <= cmp_diff[64];
|
|
end
|
|
end
|
|
|
|
always @ (*) begin
|
|
case (paddr)
|
|
ADDR_CTRL: prdata = {31'h0, ctrl_en};
|
|
ADDR_MTIME: prdata = mtime[31:0];
|
|
ADDR_MTIMEH: prdata = mtime[63:32];
|
|
ADDR_MTIMECMP: prdata = ~mtimecmp[31:0];
|
|
ADDR_MTIMECMPH: prdata = ~mtimecmp[63:32];
|
|
default: prdata = 32'h0;
|
|
endcase
|
|
end
|
|
|
|
assign pready = 1'b1;
|
|
assign pslverr = 1'b0;
|
|
|
|
endmodule
|
|
|
|
`ifndef YOSYS
|
|
`default_nettype none
|
|
`endif
|