|  | `default_nettype none | 
|  | /* Simple 32-bit counter-timer for Caravel. */ | 
|  |  | 
|  | /* Counter acts as high 32 bits of a 64-bit counter | 
|  | * when chained with the other counter | 
|  | */ | 
|  |  | 
|  | module counter_timer_high_wb # ( | 
|  | parameter BASE_ADR = 32'h2400_0000, | 
|  | parameter CONFIG = 8'h00, | 
|  | parameter VALUE  = 8'h04, | 
|  | parameter DATA   = 8'h08 | 
|  | ) ( | 
|  | input wb_clk_i, | 
|  | input wb_rst_i, | 
|  | input [31:0] wb_adr_i, | 
|  | input [31:0] wb_dat_i, | 
|  | input [3:0] wb_sel_i, | 
|  | input wb_we_i, | 
|  | input wb_cyc_i, | 
|  | input wb_stb_i, | 
|  |  | 
|  | output wb_ack_o, | 
|  | output [31:0] wb_dat_o, | 
|  | input enable_in, | 
|  | input stop_in, | 
|  | input strobe, | 
|  | input is_offset, | 
|  | output stop_out, | 
|  | output enable_out, | 
|  | output irq | 
|  | ); | 
|  | wire [31:0] counter_timer_reg_cfg_do; | 
|  | wire [31:0] counter_timer_reg_val_do; | 
|  | wire [31:0] counter_timer_reg_dat_do; | 
|  |  | 
|  | wire resetn = ~wb_rst_i; | 
|  | wire valid = wb_stb_i && wb_cyc_i; | 
|  | wire counter_timer_reg_cfg_sel = valid && (wb_adr_i == (BASE_ADR | CONFIG)); | 
|  | wire counter_timer_reg_val_sel = valid && (wb_adr_i == (BASE_ADR | VALUE)); | 
|  | wire counter_timer_reg_dat_sel = valid && (wb_adr_i == (BASE_ADR | DATA)); | 
|  |  | 
|  | wire reg_cfg_we = (counter_timer_reg_cfg_sel) ? | 
|  | (wb_sel_i[0] & {wb_we_i}): 1'b0; | 
|  | wire [3:0] reg_val_we = (counter_timer_reg_val_sel) ? | 
|  | (wb_sel_i & {4{wb_we_i}}): 4'b0000; | 
|  | wire [3:0] reg_dat_we = (counter_timer_reg_dat_sel) ? | 
|  | (wb_sel_i & {4{wb_we_i}}): 4'b0000; | 
|  |  | 
|  | wire [31:0] mem_wdata = wb_dat_i; | 
|  | wire reg_dat_re = counter_timer_reg_dat_sel && !wb_sel_i && ~wb_we_i; | 
|  |  | 
|  | assign wb_dat_o = (counter_timer_reg_cfg_sel) ? counter_timer_reg_cfg_do : | 
|  | (counter_timer_reg_val_sel) ? counter_timer_reg_val_do : | 
|  | counter_timer_reg_dat_do; | 
|  | assign wb_ack_o = counter_timer_reg_cfg_sel || counter_timer_reg_val_sel || | 
|  | counter_timer_reg_dat_sel; | 
|  |  | 
|  | counter_timer_high counter_timer_high_inst ( | 
|  | .resetn(resetn), | 
|  | .clkin(wb_clk_i), | 
|  | .reg_val_we(reg_val_we), | 
|  | .reg_val_di(mem_wdata), | 
|  | .reg_val_do(counter_timer_reg_val_do), | 
|  | .reg_cfg_we(reg_cfg_we), | 
|  | .reg_cfg_di(mem_wdata), | 
|  | .reg_cfg_do(counter_timer_reg_cfg_do), | 
|  | .reg_dat_we(reg_dat_we), | 
|  | .reg_dat_di(mem_wdata), | 
|  | .reg_dat_do(counter_timer_reg_dat_do), | 
|  | .enable_in(enable_in), | 
|  | .stop_in(stop_in), | 
|  | .is_offset(is_offset), | 
|  | .stop_out(stop_out), | 
|  | .strobe(strobe), | 
|  | .enable_out(enable_out), | 
|  | .irq_out(irq) | 
|  | ); | 
|  |  | 
|  | endmodule | 
|  |  | 
|  | module counter_timer_high ( | 
|  | input resetn, | 
|  | input clkin, | 
|  |  | 
|  | input  [3:0]  reg_val_we, | 
|  | input  [31:0] reg_val_di, | 
|  | output [31:0] reg_val_do, | 
|  |  | 
|  | input 	  reg_cfg_we, | 
|  | input  [31:0] reg_cfg_di, | 
|  | output [31:0] reg_cfg_do, | 
|  |  | 
|  | input  [3:0]  reg_dat_we, | 
|  | input  [31:0] reg_dat_di, | 
|  | output [31:0] reg_dat_do, | 
|  | input	  stop_in, | 
|  | input	  enable_in, | 
|  | input	  is_offset, | 
|  | input	  strobe, | 
|  | output	  stop_out, | 
|  | output	  enable_out, | 
|  | output	  irq_out | 
|  | ); | 
|  |  | 
|  | reg [31:0] value_cur; | 
|  | reg [31:0] value_reset; | 
|  | reg	   irq_out; | 
|  | wire	   enable_in;		// Enable from chained counter | 
|  | wire	   strobe;		// Count strobe from low word counter | 
|  | wire	   enable_out;		// Enable to chained counter (sync) | 
|  | reg	   stop_out;		// Stop signal to low word counter | 
|  |  | 
|  | wire [31:0] value_cur_plus;	// Next value, on up-count | 
|  | wire [31:0] value_cur_minus;	// Next value, on down-count | 
|  | wire [31:0] value_check_plus;	// Value to check for stop condition during up count | 
|  | wire	    loc_enable;		// Local enable | 
|  |  | 
|  | reg enable;	// Enable (start) the counter/timer | 
|  | reg lastenable;	// Previous state of enable (catch rising/falling edge) | 
|  | reg oneshot;	// Set oneshot (1) mode or continuous (0) mode | 
|  | reg updown;	// Count up (1) or down (0) | 
|  | reg irq_ena;	// Enable interrupt on timeout | 
|  | reg chain;	// Chain to a secondary timer | 
|  |  | 
|  | // Configuration register | 
|  |  | 
|  | assign reg_cfg_do = {27'd0, irq_ena, chain, updown, oneshot, enable}; | 
|  |  | 
|  | always @(posedge clkin or negedge resetn) begin | 
|  | if (resetn == 1'b0) begin | 
|  | enable <= 1'b0; | 
|  | oneshot <= 1'b0; | 
|  | updown <= 1'b0; | 
|  | chain <= 1'b0; | 
|  | irq_ena <= 1'b0; | 
|  | end else begin | 
|  | if (reg_cfg_we) begin | 
|  | enable <= reg_cfg_di[0]; | 
|  | oneshot <= reg_cfg_di[1]; | 
|  | updown <= reg_cfg_di[2]; | 
|  | chain <= reg_cfg_di[3]; | 
|  | irq_ena <= reg_cfg_di[4]; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | // Counter/timer reset value register | 
|  |  | 
|  | assign reg_val_do = value_reset; | 
|  |  | 
|  | always @(posedge clkin or negedge resetn) begin | 
|  | if (resetn == 1'b0) begin | 
|  | value_reset <= 32'd0; | 
|  | end else begin | 
|  | if (reg_val_we[3]) value_reset[31:24] <= reg_val_di[31:24]; | 
|  | if (reg_val_we[2]) value_reset[23:16] <= reg_val_di[23:16]; | 
|  | if (reg_val_we[1]) value_reset[15:8] <= reg_val_di[15:8]; | 
|  | if (reg_val_we[0]) value_reset[7:0] <= reg_val_di[7:0]; | 
|  | end | 
|  | end | 
|  |  | 
|  | assign reg_dat_do = value_cur; | 
|  |  | 
|  | // Counter/timer current value register and timer implementation | 
|  |  | 
|  | assign value_cur_plus = value_cur + 1; | 
|  | assign value_cur_minus = value_cur - 1; | 
|  |  | 
|  | assign value_check_plus = (is_offset) ? value_cur_plus : value_cur; | 
|  |  | 
|  | assign enable_out = enable; | 
|  | assign loc_enable = (chain == 1'b1) ? (enable && enable_in) : enable; | 
|  |  | 
|  | // When acting as the high 32 bit word of a 64-bit chained counter: | 
|  | // | 
|  | // It counts when the low 32-bit counter strobes (strobe == 1). | 
|  | // It sets "stop_out" and stops on the stop condition. | 
|  |  | 
|  | always @(posedge clkin or negedge resetn) begin | 
|  | if (resetn == 1'b0) begin | 
|  | value_cur <= 32'd0; | 
|  | stop_out <= 1'b0; | 
|  | irq_out <= 1'b0; | 
|  | lastenable <= 1'b0; | 
|  | end else begin | 
|  | lastenable <= loc_enable; | 
|  |  | 
|  | if (reg_dat_we != 4'b0000) begin | 
|  | if (reg_dat_we[3] == 1'b1) value_cur[31:24] <= reg_dat_di[31:24]; | 
|  | if (reg_dat_we[2] == 1'b1) value_cur[23:16] <= reg_dat_di[23:16]; | 
|  | if (reg_dat_we[1] == 1'b1) value_cur[15:8] <= reg_dat_di[15:8]; | 
|  | if (reg_dat_we[0] == 1'b1) value_cur[7:0] <= reg_dat_di[7:0]; | 
|  |  | 
|  | end else if (loc_enable == 1'b1) begin | 
|  | /* IRQ signals one cycle after stop, if IRQ is enabled */ | 
|  | irq_out <= (irq_ena) ? stop_out : 1'b0; | 
|  |  | 
|  | if (updown == 1'b1) begin | 
|  | if (lastenable == 1'b0) begin | 
|  | value_cur <= 32'd0; | 
|  | stop_out <= 1'b0; | 
|  | end else if (chain) begin | 
|  | // Chained counter behavior | 
|  | if (value_check_plus == value_reset) begin | 
|  | stop_out <= 1'b1; | 
|  | end | 
|  | if (stop_in == 1'b1) begin	// lower word counter stopped | 
|  | if (oneshot != 1'b1) begin | 
|  | value_cur <= 32'd0;	// reset count | 
|  | stop_out <= 1'b0;	// no longer stopped | 
|  | end else if (strobe == 1'b1) begin | 
|  | value_cur <= value_cur_plus; | 
|  | end | 
|  | end else if (strobe == 1'b1) begin | 
|  | value_cur <= value_cur_plus; | 
|  | end | 
|  | end else begin | 
|  | // Standalone counter behavior | 
|  | if (value_cur == value_reset) begin | 
|  | if (oneshot != 1'b1) begin | 
|  | value_cur <= 32'd0; | 
|  | stop_out <= 1'b0; | 
|  | end else begin | 
|  | stop_out <= 1'b1; | 
|  | end | 
|  | end else begin | 
|  | if (value_cur_plus == 32'd0) begin | 
|  | stop_out <= 1'b1; | 
|  | end else begin | 
|  | stop_out <= 1'b0; | 
|  | end | 
|  | value_cur <= value_cur_plus;	// count up | 
|  | end | 
|  | end | 
|  | end else begin | 
|  | if (lastenable == 1'b0) begin | 
|  | value_cur <= value_reset; | 
|  | stop_out <= 1'b0; | 
|  | end else if (chain) begin | 
|  | // Chained counter behavior | 
|  | if (value_cur == 32'd0) begin | 
|  | stop_out <= 1'b1; | 
|  | end | 
|  | if (stop_in == 1'b1) begin	// lower word counter stopped | 
|  | if (oneshot != 1'b1) begin | 
|  | value_cur <= value_reset;	// reset count | 
|  | stop_out <= 1'b0;		// no longer stopped | 
|  | end | 
|  | end else if (strobe == 1'b1) begin | 
|  | value_cur <= value_cur_minus;	// count down | 
|  | end | 
|  | end else begin | 
|  | // Standalone counter behavior | 
|  | if (value_cur == 32'd0) begin | 
|  | if (oneshot != 1'b1) begin | 
|  | value_cur <= value_reset; | 
|  | stop_out <= 1'b0; | 
|  | end else begin | 
|  | stop_out <= 1'b1; | 
|  | end | 
|  | end else begin | 
|  | if (value_cur_minus == 32'd0) begin | 
|  | stop_out <= 1'b1; | 
|  | end else begin | 
|  | stop_out <= 1'b0; | 
|  | end | 
|  | value_cur <= value_cur_minus;	// count down | 
|  | end | 
|  | end | 
|  | end | 
|  | end else begin | 
|  | stop_out <= 1'b0; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | endmodule | 
|  | `default_nettype wire |