Clean up timer
This commit is contained in:
		
							parent
							
								
									9dd091b7b5
								
							
						
					
					
						commit
						e16ae06cb5
					
				|  | @ -1,2 +1 @@ | |||
| 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) | ||||
| // accessed over 32-bit data bus. Ticks on both edges of tick_nrz, which is | ||||
| // provided by some external timebase, and is assumed to be asynchronous to | ||||
| // clk. Nothing fancy, just a simple implementation of the spec. | ||||
| // 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. | ||||
| 
 | ||||
| module hazard3_riscv_timer ( | ||||
| 	input  wire               clk, | ||||
|  | @ -30,17 +40,16 @@ module hazard3_riscv_timer ( | |||
| 	input  wire [7:0]         paddr, | ||||
| 	input  wire [31:0]        pwdata, | ||||
| 	output reg  [31:0]        prdata, | ||||
| 	output wire               pready, | ||||
| 	output reg                pready, | ||||
| 	output wire               pslverr, | ||||
| 
 | ||||
| 	input  wire               dbg_halt, | ||||
| 	input  wire               tick_nrz, | ||||
| 
 | ||||
| 	input  wire               tick, | ||||
| 
 | ||||
| 	output reg                timer_irq | ||||
| ); | ||||
| 
 | ||||
| wire bus_write = pwrite && psel && penable && pready; | ||||
| 
 | ||||
| localparam W_ADDR = 8; | ||||
| localparam W_DATA = 32; | ||||
| 
 | ||||
|  | @ -50,34 +59,24 @@ localparam ADDR_MTIMEH    = 8'h0c; | |||
| localparam ADDR_MTIMECMP  = 8'h10; | ||||
| 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; | ||||
| always @ (posedge clk or negedge rst_n) | ||||
| 	if (!rst_n) | ||||
| always @ (posedge clk or negedge rst_n) begin | ||||
| 	if (!rst_n) begin | ||||
| 		ctrl_en <= 1'b1; | ||||
| 	else if (bus_write && paddr == ADDR_CTRL) | ||||
| 	end else if (bus_write && paddr == ADDR_CTRL) begin | ||||
| 		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; | ||||
| 
 | ||||
| // 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; | ||||
| 
 | ||||
|  | @ -99,7 +98,6 @@ always @ (posedge clk or negedge rst_n) begin | |||
| 	end else begin | ||||
| 		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 | ||||
|  | @ -118,10 +116,20 @@ always @ (posedge clk or negedge rst_n) begin | |||
| 	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); | ||||
| 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 | ||||
|  | @ -144,15 +152,21 @@ always @ (posedge clk or negedge rst_n) begin | |||
| 	end | ||||
| end | ||||
| 
 | ||||
| always @ (*) begin | ||||
| 	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 | ||||
| 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 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue