Clean up timer
This commit is contained in:
parent
9dd091b7b5
commit
e16ae06cb5
|
@ -1,2 +1 @@
|
||||||
file hazard3_riscv_timer.v
|
file hazard3_riscv_timer.v
|
||||||
file ../debug/cdc/hazard3_sync_1bit.v
|
|
||||||
|
|
|
@ -16,9 +16,19 @@
|
||||||
*********************************************************************/
|
*********************************************************************/
|
||||||
|
|
||||||
// Implementation of standard RISC-V timer (mtime/mtimeh mtimecmp/mtimecmph)
|
// Implementation of standard RISC-V timer (mtime/mtimeh mtimecmp/mtimecmph)
|
||||||
// accessed over 32-bit data bus. Ticks on both edges of tick_nrz, which is
|
// accessed over 32-bit data bus.
|
||||||
// provided by some external timebase, and is assumed to be asynchronous to
|
//
|
||||||
// clk. Nothing fancy, just a simple implementation of the spec.
|
// This is written for minimal area on FPGA -- in particular, it uses 64-bit
|
||||||
|
// serial increment and compare -- so is not the best solution for less
|
||||||
|
// resource-constrained platforms, because it can't count faster than once
|
||||||
|
// per 64 cycles, and bus accesses can be delayed for up to 63 timer ticks
|
||||||
|
// whilst the serial counter rotates to the correct bus alignment. The serial
|
||||||
|
// operations are also quite energy-intensive.
|
||||||
|
//
|
||||||
|
// Tie tick high for a 64-cycle timebase. tick must be free-running, i.e. must
|
||||||
|
// not be held low indefinitely, because this would also halt the serial
|
||||||
|
// mtimecmp comparison. To pause the timer due to an external event, assert
|
||||||
|
// dbg_halt high. To pause from software, write 0 to CTRL.EN.
|
||||||
|
|
||||||
module hazard3_riscv_timer (
|
module hazard3_riscv_timer (
|
||||||
input wire clk,
|
input wire clk,
|
||||||
|
@ -30,17 +40,16 @@ module hazard3_riscv_timer (
|
||||||
input wire [7:0] paddr,
|
input wire [7:0] paddr,
|
||||||
input wire [31:0] pwdata,
|
input wire [31:0] pwdata,
|
||||||
output reg [31:0] prdata,
|
output reg [31:0] prdata,
|
||||||
output wire pready,
|
output reg pready,
|
||||||
output wire pslverr,
|
output wire pslverr,
|
||||||
|
|
||||||
input wire dbg_halt,
|
input wire dbg_halt,
|
||||||
input wire tick_nrz,
|
|
||||||
|
input wire tick,
|
||||||
|
|
||||||
output reg timer_irq
|
output reg timer_irq
|
||||||
);
|
);
|
||||||
|
|
||||||
wire bus_write = pwrite && psel && penable && pready;
|
|
||||||
|
|
||||||
localparam W_ADDR = 8;
|
localparam W_ADDR = 8;
|
||||||
localparam W_DATA = 32;
|
localparam W_DATA = 32;
|
||||||
|
|
||||||
|
@ -50,34 +59,24 @@ localparam ADDR_MTIMEH = 8'h0c;
|
||||||
localparam ADDR_MTIMECMP = 8'h10;
|
localparam ADDR_MTIMECMP = 8'h10;
|
||||||
localparam ADDR_MTIMECMPH = 8'h14;
|
localparam ADDR_MTIMECMPH = 8'h14;
|
||||||
|
|
||||||
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)
|
|
||||||
if (!rst_n)
|
|
||||||
tick_nrz_prev <= 1'b0;
|
|
||||||
else
|
|
||||||
tick_nrz_prev <= tick_nrz_sync;
|
|
||||||
|
|
||||||
reg ctrl_en;
|
reg ctrl_en;
|
||||||
always @ (posedge clk or negedge rst_n)
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
if (!rst_n)
|
if (!rst_n) begin
|
||||||
ctrl_en <= 1'b1;
|
ctrl_en <= 1'b1;
|
||||||
else if (bus_write && paddr == ADDR_CTRL)
|
end else if (bus_write && paddr == ADDR_CTRL) begin
|
||||||
ctrl_en <= pwdata[0];
|
ctrl_en <= pwdata[0];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
wire tick = tick_nrz_prev != tick_nrz_sync;
|
wire bus_write = pwrite && psel && penable && pready;
|
||||||
wire tick_and_increment = ctrl_en && !dbg_halt && tick;
|
wire tick_and_increment = ctrl_en && !dbg_halt && tick;
|
||||||
|
|
||||||
// The 64-bit TIME and TIMECMP registers are processed serially, over the
|
// ----------------------------------------------------------------------------
|
||||||
// course of 64 cycles.
|
// mtime and serial increment
|
||||||
|
|
||||||
|
// Increment takes place over the course of 64 ticks. The mtime register is
|
||||||
|
// constantly rotating to bring a new serial bit into position 0.
|
||||||
|
|
||||||
reg [5:0] serial_ctr;
|
reg [5:0] serial_ctr;
|
||||||
|
|
||||||
|
@ -99,7 +98,6 @@ always @ (posedge clk or negedge rst_n) begin
|
||||||
end else begin
|
end else begin
|
||||||
if (tick) begin
|
if (tick) begin
|
||||||
if (tick_and_increment) begin
|
if (tick_and_increment) begin
|
||||||
// Serially increment mtime
|
|
||||||
{mtime_carry, mtime[63]} <= mtime_carry + mtime[0];
|
{mtime_carry, mtime[63]} <= mtime_carry + mtime[0];
|
||||||
mtime[62:0] <= mtime[63:1];
|
mtime[62:0] <= mtime[63:1];
|
||||||
end else begin
|
end else begin
|
||||||
|
@ -118,10 +116,20 @@ always @ (posedge clk or negedge rst_n) begin
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// mtimecmp and serial comparison
|
||||||
|
|
||||||
|
// The timer IRQ is only updated every 64 ticks, when we finish a new
|
||||||
|
// comparison. This is permitted by the RISC-V privileged spec: before
|
||||||
|
// returning, the IRQ handler should poll mtip until it sees the IRQ
|
||||||
|
// deassert.
|
||||||
|
|
||||||
reg [63:0] mtimecmp;
|
reg [63:0] mtimecmp;
|
||||||
reg mtimecmp_borrow;
|
reg mtimecmp_borrow;
|
||||||
|
|
||||||
wire mtimecmp_borrow_next = (!mtime[0] && (mtimecmp[0] || mtimecmp_borrow)) || (mtimecmp[0] && mtimecmp_borrow);
|
wire mtimecmp_borrow_next =
|
||||||
|
(!mtime[0] && (mtimecmp[0] || mtimecmp_borrow))
|
||||||
|
|| (mtimecmp[0] && mtimecmp_borrow);
|
||||||
|
|
||||||
always @ (posedge clk or negedge rst_n) begin
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
if (!rst_n) begin
|
if (!rst_n) begin
|
||||||
|
@ -144,15 +152,21 @@ always @ (posedge clk or negedge rst_n) begin
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
always @ (*) begin
|
// ----------------------------------------------------------------------------
|
||||||
case (paddr)
|
// Bus read mux
|
||||||
ADDR_CTRL: begin prdata = {{W_DATA-1{1'b0}}, ctrl_en}; pready = 1'b1; end
|
|
||||||
ADDR_MTIME: begin prdata = mtime[31:0]; pready = serial_ctr == 6'h00; end
|
// Only the lower half of each 64-bit counter register is exposed to the bus,
|
||||||
ADDR_MTIMEH: begin prdata = mtime[31:0]; pready = serial_ctr == 6'h20; end
|
// using the serial counter to make sure the correct bits are aligned with
|
||||||
ADDR_MTIMECMP: begin prdata = mtimecmp[31:0]; pready = serial_ctr == 6'h00; end
|
// the bus window at the point the bus access finishes. Note pready is only
|
||||||
ADDR_MTIMECMPH: begin prdata = mtimecmp[31:0]; pready = serial_ctr == 6'h20; end
|
// valid during the access phase (& is ignored during setup phase).
|
||||||
default: begin prdata = {W_DATA{1'b0}}; pready = 1'b1; end
|
|
||||||
endcase
|
always @ (*) case (paddr)
|
||||||
end
|
ADDR_CTRL: begin prdata = {{W_DATA-1{1'b0}}, ctrl_en}; pready = 1'b1; end
|
||||||
|
ADDR_MTIME: begin prdata = mtime[31:0]; pready = serial_ctr == 6'h00; end
|
||||||
|
ADDR_MTIMEH: begin prdata = mtime[31:0]; pready = serial_ctr == 6'h20; end
|
||||||
|
ADDR_MTIMECMP: begin prdata = mtimecmp[31:0]; pready = serial_ctr == 6'h00; end
|
||||||
|
ADDR_MTIMECMPH: begin prdata = mtimecmp[31:0]; pready = serial_ctr == 6'h20; end
|
||||||
|
default: begin prdata = {W_DATA{1'b0}}; pready = 1'b1; end
|
||||||
|
endcase
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
Loading…
Reference in New Issue