From b99e5b8a6760cc208459ac1a0d40ecea06f6f317 Mon Sep 17 00:00:00 2001 From: Luke Wren Date: Fri, 20 Aug 2021 22:27:15 +0100 Subject: [PATCH] Convert timer to serial for smaller area. Rather untested --- hdl/peri/hazard3_riscv_timer.v | 74 +++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 18 deletions(-) diff --git a/hdl/peri/hazard3_riscv_timer.v b/hdl/peri/hazard3_riscv_timer.v index 99df9dd..5856fbf 100644 --- a/hdl/peri/hazard3_riscv_timer.v +++ b/hdl/peri/hazard3_riscv_timer.v @@ -39,8 +39,7 @@ module hazard3_riscv_timer ( output reg timer_irq ); -wire bus_write = pwrite && psel && penable; -wire bus_read = !pwrite && psel && penable; +wire bus_write = pwrite && psel && penable && pready; localparam W_ADDR = 8; localparam W_DATA = 32; @@ -74,46 +73,85 @@ always @ (posedge clk or negedge rst_n) else if (bus_write && paddr == ADDR_CTRL) ctrl_en <= pwdata[0]; -wire tick = ctrl_en && !dbg_halt && tick_nrz_prev != tick_nrz_sync; +wire tick = tick_nrz_prev != tick_nrz_sync; +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. + +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) - mtime <= mtime + 1'b1; - if (bus_write && paddr == ADDR_MTIME) + if (tick) begin + if (tick_and_increment) begin + // Serially increment mtime + {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; - if (bus_write && paddr == ADDR_MTIMEH) - mtime[63:32] <= pwdata; end end reg [63:0] mtimecmp; +reg mtimecmp_borrow; + +wire mtimecmp_borrow_next = (!mtimecmp[0] && (mtime[0] || mtimecmp_borrow)) || (mtime[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 - if (bus_write && paddr == ADDR_MTIMECMP) + // Serially subtract mtime from mtimecmp. If there is no borrow from + // bit 63 (i.e. if mtimecmp 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; - if (bus_write && paddr == ADDR_MTIMECMPH) - mtimecmp[63:32] <= pwdata; - timer_irq <= mtime >= mtimecmp; // oof end end always @ (*) begin case (paddr) - ADDR_CTRL: prdata = {{W_DATA-1{1'b0}}, 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 = {W_DATA{1'b0}}; + 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[63:32]; pready = serial_ctr == 6'h20; end + default: begin prdata = {W_DATA{1'b0}}; pready = 1'b1; end endcase end