Convert timer to serial for smaller area. Rather untested
This commit is contained in:
		
							parent
							
								
									4aba165166
								
							
						
					
					
						commit
						b99e5b8a67
					
				|  | @ -39,8 +39,7 @@ module hazard3_riscv_timer ( | ||||||
| 	output reg                timer_irq | 	output reg                timer_irq | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| wire bus_write = pwrite && psel && penable; | wire bus_write = pwrite && psel && penable && pready; | ||||||
| wire bus_read = !pwrite && psel && penable; |  | ||||||
| 
 | 
 | ||||||
| localparam W_ADDR = 8; | localparam W_ADDR = 8; | ||||||
| localparam W_DATA = 32; | localparam W_DATA = 32; | ||||||
|  | @ -74,46 +73,85 @@ always @ (posedge clk or negedge rst_n) | ||||||
| 	else if (bus_write && paddr == ADDR_CTRL) | 	else if (bus_write && paddr == ADDR_CTRL) | ||||||
| 		ctrl_en <= pwdata[0]; | 		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 [63:0] mtime; | ||||||
|  | reg        mtime_carry; | ||||||
| 
 | 
 | ||||||
| always @ (posedge clk or negedge rst_n) begin | always @ (posedge clk or negedge rst_n) begin | ||||||
| 	if (!rst_n) begin | 	if (!rst_n) begin | ||||||
| 		mtime <= 64'h0; | 		mtime <= 64'h0; | ||||||
|  | 		mtime_carry <= 1'b1; | ||||||
| 	end else begin | 	end else begin | ||||||
| 		if (tick) | 		if (tick) begin | ||||||
| 			mtime <= mtime + 1'b1; | 			if (tick_and_increment) begin | ||||||
| 		if (bus_write && paddr == ADDR_MTIME) | 				// 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; | 			mtime[31:0] <= pwdata; | ||||||
| 		if (bus_write && paddr == ADDR_MTIMEH) |  | ||||||
| 			mtime[63:32] <= pwdata; |  | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| reg [63:0] mtimecmp; | 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 | always @ (posedge clk or negedge rst_n) begin | ||||||
| 	if (!rst_n) begin | 	if (!rst_n) begin | ||||||
| 		mtimecmp <= 64'h0; | 		mtimecmp <= 64'h0; | ||||||
|  | 		mtimecmp_borrow <= 1'b0; | ||||||
| 		timer_irq <= 1'b0; | 		timer_irq <= 1'b0; | ||||||
| 	end else begin | 	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; | 			mtimecmp[31:0] <= pwdata; | ||||||
| 		if (bus_write && paddr == ADDR_MTIMECMPH) |  | ||||||
| 			mtimecmp[63:32] <= pwdata; |  | ||||||
| 		timer_irq <= mtime >= mtimecmp; // oof |  | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| always @ (*) begin | always @ (*) begin | ||||||
| 	case (paddr) | 	case (paddr) | ||||||
| 	ADDR_CTRL:      prdata = {{W_DATA-1{1'b0}}, ctrl_en}; | 	ADDR_CTRL:      begin  prdata = {{W_DATA-1{1'b0}}, ctrl_en}; pready = 1'b1;                end | ||||||
| 	ADDR_MTIME:     prdata = mtime[31:0]; | 	ADDR_MTIME:     begin  prdata = mtime[31:0];                 pready = serial_ctr == 6'h00; end | ||||||
| 	ADDR_MTIMEH:    prdata = mtime[63:32]; | 	ADDR_MTIMEH:    begin  prdata = mtime[31:0];                 pready = serial_ctr == 6'h20; end | ||||||
| 	ADDR_MTIMECMP:  prdata = mtimecmp[31:0]; | 	ADDR_MTIMECMP:  begin  prdata = mtimecmp[31:0];              pready = serial_ctr == 6'h00; end | ||||||
| 	ADDR_MTIMECMPH: prdata = mtimecmp[63:32]; | 	ADDR_MTIMECMPH: begin  prdata = mtimecmp[63:32];             pready = serial_ctr == 6'h20; end | ||||||
| 	default:        prdata = {W_DATA{1'b0}}; | 	default:        begin  prdata = {W_DATA{1'b0}};              pready = 1'b1;                end | ||||||
| 	endcase | 	endcase | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue