diff --git a/hdl/peri/hazard3_riscv_timer.f b/hdl/peri/hazard3_riscv_timer.f deleted file mode 100644 index ef47462..0000000 --- a/hdl/peri/hazard3_riscv_timer.f +++ /dev/null @@ -1 +0,0 @@ -file hazard3_riscv_timer.v diff --git a/hdl/peri/hazard3_riscv_timer.v b/hdl/peri/hazard3_riscv_timer.v deleted file mode 100644 index 9b88596..0000000 --- a/hdl/peri/hazard3_riscv_timer.v +++ /dev/null @@ -1,164 +0,0 @@ -/*****************************************************************************\ -| Copyright (C) 2021-2022 Luke Wren | -| SPDX-License-Identifier: Apache-2.0 | -\*****************************************************************************/ - -// Implementation of standard RISC-V timer (mtime/mtimeh mtimecmp/mtimecmph) -// accessed over 32-bit data bus. -// -// 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. - -`default_nettype none - -module hazard3_riscv_timer ( - input wire clk, - input wire rst_n, - - input wire psel, - input wire penable, - input wire pwrite, - input wire [7:0] paddr, - input wire [31:0] pwdata, - output reg [31:0] prdata, - output reg pready, - output wire pslverr, - - input wire dbg_halt, - - input wire tick, - - output reg timer_irq -); - -localparam W_ADDR = 8; -localparam W_DATA = 32; - -localparam ADDR_CTRL = 8'h00; -localparam ADDR_MTIME = 8'h08; -localparam ADDR_MTIMEH = 8'h0c; -localparam ADDR_MTIMECMP = 8'h10; -localparam ADDR_MTIMECMPH = 8'h14; - -wire bus_write = pwrite && psel && penable && pready; - -reg ctrl_en; -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 - -wire tick_and_increment = ctrl_en && !dbg_halt && tick; - -// ---------------------------------------------------------------------------- -// 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; - -always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - serial_ctr <= 6'h00; - end else if (tick) begin - serial_ctr <= serial_ctr + 1'b1; - end -end - -reg [63:0] mtime; -reg mtime_carry; - -always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - mtime <= 64'h0; - mtime_carry <= 1'b1; - end else begin - if (tick) begin - if (tick_and_increment) begin - {mtime_carry, mtime[63]} <= mtime_carry + mtime[0]; - mtime[62:0] <= mtime[63:1]; - end else begin - // Still keep rotating the register, so writes can take place, - // and so we can continuously compare with mtimecmp. - mtime <= {mtime[0], mtime[63:1]}; - end - // Preload carry for increment - if (serial_ctr == 6'h3f) - mtime_carry <= 1'b1; - end - // Only the lower half is written; pready is driven so that the write - // occurs at the correct time and hence bit-alignment. - if (bus_write && (paddr == ADDR_MTIME || paddr == ADDR_MTIMEH)) - mtime[31:0] <= pwdata; - 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 mtimecmp_borrow; - -wire mtimecmp_borrow_next = - (!mtime[0] && (mtimecmp[0] || mtimecmp_borrow)) - || (mtimecmp[0] && mtimecmp_borrow); - -always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - mtimecmp <= 64'h0; - mtimecmp_borrow <= 1'b0; - timer_irq <= 1'b0; - end else begin - // Serially subtract mtimecmp from mtime. If there is no borrow from - // bit 63 (i.e. if mtime was greater or equal) then assert IRQ. - if (tick) begin - mtimecmp_borrow <= mtimecmp_borrow_next; - mtimecmp <= {mtimecmp[0], mtimecmp[63:1]}; - if (serial_ctr == 6'h3f) begin - mtimecmp_borrow <= 1'b0; - timer_irq <= !mtimecmp_borrow_next; - end - end - if (bus_write && (paddr == ADDR_MTIMECMP || paddr == ADDR_MTIMECMPH)) - mtimecmp[31:0] <= pwdata; - end -end - -// ---------------------------------------------------------------------------- -// Bus read mux - -// Only the lower half of each 64-bit counter register is exposed to the bus, -// using the serial counter to make sure the correct bits are aligned with -// the bus window at the point the bus access finishes. Note pready is only -// valid during the access phase (& is ignored during setup phase). - -always @ (*) case (paddr) - 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 - -`default_nettype wire