Made a number of modifications to the counter-timer to correctly pipeline
the 64-bit counter, including synchronizing the enables. There are still
two issues, one of which causes the testbench to fail, which have not been
solved.
diff --git a/verilog/dv/caravel/mgmt_soc/timer/timer.c b/verilog/dv/caravel/mgmt_soc/timer/timer.c
index 4b49f4f..c3a8dc4 100644
--- a/verilog/dv/caravel/mgmt_soc/timer/timer.c
+++ b/verilog/dv/caravel/mgmt_soc/timer/timer.c
@@ -74,7 +74,8 @@
/* 0 = timer enable (1 = enabled, 0 = disabled) */
/* 1 = one-shot mode (1 = oneshot, 0 = continuous) */
/* 2 = up/down (1 = count up, 0 = count down) */
- /* 3 = IRQ enable (1 = enabled, 0 = disabled) */
+ /* 3 = chain (1 = enabled, 0 = disabled) */
+ /* 4 = IRQ enable (1 = enabled, 0 = disabled) */
reg_timer0_config = 3; /* Enabled, one-shot, down count */
diff --git a/verilog/dv/caravel/mgmt_soc/timer2/timer2.c b/verilog/dv/caravel/mgmt_soc/timer2/timer2.c
index 2994082..a8c65e0 100644
--- a/verilog/dv/caravel/mgmt_soc/timer2/timer2.c
+++ b/verilog/dv/caravel/mgmt_soc/timer2/timer2.c
@@ -117,8 +117,81 @@
value = reg_timer1_data;
reg_mprj_datal = value; // Put count value on GPIO
}
-
+
+ reg_mprj_datah = 0x05; /* Check value in testbench */
+
+ /* Now, set up chained 64 bit timer. Check count-up */
+ /* value and count-down value crossing the 32-bit */
+ /* boundary. */
+
+ /* First disable both counters, and set the "chained" */
+ /* property so that enable/disable will be synchronized */
+
+ reg_timer1_config = 8; /* Disabled, chained */
+ reg_timer0_config = 8; /* Disabled, chained */
+
+ /* Configure timer for a chained single-shot countdown. */
+ /* Count start = 0x0000000100001000, end = 0x0 */
+
+ reg_timer1_value = 0x00000055;
+ reg_timer0_value = 0x00001000;
+
+ /* Timer configuration bits: */
+ /* 0 = timer enable (1 = enabled, 0 = disabled) */
+ /* 1 = one-shot mode (1 = oneshot, 0 = continuous) */
+ /* 2 = up/down (1 = count up, 0 = count down) */
+ /* 3 = chain (1 = enabled, 0 = disabled) */
+ /* 4 = IRQ enable (1 = enabled, 0 = disabled) */
+
+ reg_timer1_config = 11; /* Enabled, one-shot, down count, chained */
+ reg_timer0_config = 11; /* Enabled, one-shot, down count, chained */
+
+ for (i = 0; i < 1; i++) {
+ value = reg_timer1_data;
+ reg_mprj_datal = value; // Put count value on GPIO
+ }
+
+ reg_mprj_datah = 0x06; /* Check value in testbench */
+
+ // Skip to the end. . .
+ reg_timer1_data = 0x00000000;
+ reg_timer0_data = 0x00000200;
+
+ for (i = 0; i < 4; i++) {
+ value = reg_timer0_data;
+ reg_mprj_datal = value; // Put count value on GPIO
+ }
+
+ reg_mprj_datah = 0x07; /* Check value in testbench */
+
+ reg_timer1_config = 14; /* Disabled, one-shot, up count, chained */
+ reg_timer0_config = 14; /* Disabled, one-shot, up count, chained */
+
+ reg_timer1_value = 0x00000002;
+ reg_timer0_value = 0x00000000;
+
+ reg_timer1_config = 15; /* Enabled, one-shot, up count, chained */
+ reg_timer0_config = 15; /* Enabled, one-shot, up count, chained */
+
+ for (i = 0; i < 1; i++) {
+ value = reg_timer0_data;
+ reg_mprj_datal = value; // Put count value on GPIO
+ }
+
+ reg_mprj_datah = 0x08; /* Check value in testbench */
+
+ // Skip to the end. . .
+ /* Count 0x00000001ffffff00 to 0x0000000200000000 and stop */
+
+ reg_timer1_data = 0x00000001; // Set value (will be reset)
+ reg_timer0_data = 0xffffff00; // Set value (will be reset)
+
+ for (i = 0; i < 4; i++) {
+ value = reg_timer1_data;
+ reg_mprj_datal = value; // Put timer1 count value on GPIO
+ }
+
/* Present end marker (see testbench verilog) */
- reg_mprj_datah = 0x05;
+ reg_mprj_datah = 0x10;
}
diff --git a/verilog/dv/caravel/mgmt_soc/timer2/timer2_tb.v b/verilog/dv/caravel/mgmt_soc/timer2/timer2_tb.v
index 39e06cc..30b54f1 100644
--- a/verilog/dv/caravel/mgmt_soc/timer2/timer2_tb.v
+++ b/verilog/dv/caravel/mgmt_soc/timer2/timer2_tb.v
@@ -40,13 +40,13 @@
$dumpvars(0, timer2_tb);
// Repeat cycles of 1000 clock edges as needed to complete testbench
- repeat (50) begin
+ repeat (60) begin
repeat (1000) @(posedge clock);
$display("+1000 cycles");
end
$display("%c[1;31m",27);
$display ("Monitor: Timeout, Test GPIO (RTL) Failed");
- $display("%c[0m",27);
+ $display("%c[0m",27);
$finish;
end
@@ -99,6 +99,34 @@
$finish;
end
+ wait(checkbits == 6'h06);
+ $display(" countbits = %x (should be 0x0054)", countbits);
+ if(countbits !== 32'h0054) begin
+ $display("Monitor: Test Timer2 (RTL) Failed");
+ $finish;
+ end
+
+ wait(checkbits == 6'h07);
+ $display(" countbits = %x (should be 0x0000)", countbits);
+ if(countbits !== 32'h0000) begin
+ $display("Monitor: Test Timer2 (RTL) Failed");
+ $finish;
+ end
+
+ wait(checkbits == 6'h08);
+ $display(" countbits = %x (should be 0x0218)", countbits);
+ if(countbits !== 32'h0218) begin
+ $display("Monitor: Test Timer2 (RTL) Failed");
+ $finish;
+ end
+
+ wait(checkbits == 6'h10);
+ $display(" countbits = %x (should be 0x0002)", countbits);
+ if(countbits !== 32'h0002) begin
+ $display("Monitor: Test Timer2 (RTL) Failed");
+ $finish;
+ end
+
$display("Monitor: Test Timer2 (RTL) Passed");
$finish;
end
diff --git a/verilog/rtl/counter_timer.v b/verilog/rtl/counter_timer.v
deleted file mode 100755
index f569182..0000000
--- a/verilog/rtl/counter_timer.v
+++ /dev/null
@@ -1,192 +0,0 @@
-/* Simple 32-bit counter-timer for Caravel. */
-
-module counter_timer_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 strobe_in,
- 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,
- output strobe_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 counter_timer_inst (
- .resetn(resetn),
- .clkin(wb_clk_i),
- .strobe_in(strobe_in),
- .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),
- .strobe_out(strobe_out),
- .irq_out(irq)
- );
-
-endmodule
-
-module counter_timer (
- input resetn,
- input clkin,
- input strobe_in,
-
- 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,
- output strobe_out,
- output irq_out
-);
-
-reg [31:0] value_cur;
-reg [31:0] value_reset;
-reg strobe_out;
-wire irq_out;
-
-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;
- lastenable <= 1'b0;
- oneshot <= 1'b0;
- updown <= 1'b0;
- chain <= 1'b0;
- irq_ena <= 1'b0;
- end else begin
- lastenable <= enable;
- 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 irq_out = (irq_ena) ? strobe_out : 1'b0;
-
-always @(posedge clkin or negedge resetn) begin
- if (resetn == 1'b0) begin
- value_cur <= 32'd0;
- strobe_out <= 1'b0;
- end else begin
- 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 (enable == 1'b1) begin
- if (updown == 1'b1) begin
- if (lastenable == 1'b0) begin
- value_cur <= 32'd0;
- end else if (value_cur == value_reset) begin
- if (oneshot != 1'b1) begin
- value_cur <= 32'd0;
- end
- strobe_out <= 1'b1;
- end else begin
- if ((chain == 1'b0) || ((chain == 1'b1) && (strobe_in == 1'b1))) begin
- value_cur <= value_cur + 1; // count up
- strobe_out <= 1'b0;
- end
- end
- end else begin
- if (lastenable == 1'b0) begin
- value_cur <= value_reset;
- end else if (value_cur == 32'd0) begin
- if (oneshot != 1'b1) begin
- value_cur <= value_reset;
- end
- strobe_out <= 1'b1;
- end else begin
- if ((chain == 1'b0) || ((chain == 1'b1) && (strobe_in == 1'b1))) begin
- value_cur <= value_cur - 1; // count down
- strobe_out <= 1'b0;
- end
- end
- end
- end else begin
- strobe_out <= 1'b0;
- end
- end
-end
-
-endmodule
diff --git a/verilog/rtl/counter_timer_high.v b/verilog/rtl/counter_timer_high.v
new file mode 100755
index 0000000..76db8b9
--- /dev/null
+++ b/verilog/rtl/counter_timer_high.v
@@ -0,0 +1,277 @@
+/* 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
diff --git a/verilog/rtl/counter_timer_low.v b/verilog/rtl/counter_timer_low.v
new file mode 100755
index 0000000..66989d2
--- /dev/null
+++ b/verilog/rtl/counter_timer_low.v
@@ -0,0 +1,309 @@
+/* Simple 32-bit counter-timer for Caravel. */
+
+/* Counter acts as low 32 bits of a 64-bit counter
+ * when chained with the other counter.
+ */
+
+module counter_timer_low_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 stop_in,
+ input enable_in,
+ output strobe,
+ output 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_low counter_timer_low_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),
+ .stop_in(stop_in),
+ .strobe(strobe),
+ .is_offset(is_offset),
+ .enable_in(enable_in),
+ .stop_out(stop_out),
+ .enable_out(enable_out),
+ .irq_out(irq)
+ );
+
+endmodule
+
+module counter_timer_low (
+ 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,
+ output strobe,
+ output enable_out,
+ output stop_out,
+ output is_offset,
+ output irq_out
+);
+
+reg [31:0] value_cur;
+reg [31:0] value_reset;
+reg irq_out;
+wire stop_in; // High 32 bits counter has stopped
+reg strobe; // Strobe to high 32 bits counter; occurs
+ // one cycle before actual timeout and
+ // irq signal.
+reg stop_out; // Stop condition flag
+
+wire [31:0] value_cur_plus; // Next value, on up-count
+wire [31:0] value_cur_minus; // Next value, on down-count
+wire is_offset;
+
+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 loc_enable = enable_in && enable;
+assign loc_enable = (chain == 1'b1) ? (enable && enable_in) : enable;
+assign enable_out = enable;
+
+// If counting up and the stop condition on the low 32 bits is zero,
+// then signal to the high word counter that the stop condition of the
+// high word must be adjusted by one, since it will roll over at the same
+// time and must be signaled early.
+
+assign is_offset = ((updown == 1'b1) && (value_reset == 0));
+
+// When acting as low 32-bit word of a 64-bit chained counter:
+// It sets the output strobe on the stop condition, one cycle early.
+// It stops on the stop condition if "stop_in" is high.
+
+always @(posedge clkin or negedge resetn) begin
+ if (resetn == 1'b0) begin
+ value_cur <= 32'd0;
+ strobe <= 1'b0;
+ 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_out, 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;
+ strobe <= 1'b0;
+ stop_out <= 1'b0;
+ end else if (chain == 1'b1) begin
+ // Rollover strobe (2 clock cycles advanced)
+ if (value_cur == -1) begin
+ strobe <= 1'b1;
+ end else begin
+ strobe <= 1'b0;
+ end
+
+ // Chained counter behavior
+ if ((stop_in == 1'b1) && (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 ((stop_in == 1'b1) && (value_cur_plus == value_reset)) begin
+ stop_out <= 1'b1;
+ end else begin
+ stop_out <= 1'b0;
+ end
+ value_cur <= value_cur_plus; // count up
+ end
+ end else begin
+
+ // Single 32-bit 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 == value_reset) 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;
+ strobe <= 1'b0;
+ end else if (chain == 1'b1) begin
+ // Rollover strobe (2 clock cycles advanced)
+ if (value_cur == 2) begin
+ strobe <= 1'b1;
+ end else begin
+ strobe <= 1'b0;
+ end
+
+ // Chained counter behavior
+ if ((stop_in == 1'b1) && (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 ((stop_in == 1'b1) && (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 else begin
+
+ // Single 32-bit 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
+ strobe <= 1'b0;
+ end
+ end
+end
+
+endmodule
diff --git a/verilog/rtl/mgmt_soc.v b/verilog/rtl/mgmt_soc.v
index 3a8825e..67a2c17 100644
--- a/verilog/rtl/mgmt_soc.v
+++ b/verilog/rtl/mgmt_soc.v
@@ -33,7 +33,8 @@
`include "spimemio.v"
`include "simpleuart.v"
`include "simple_spi_master.v"
-`include "counter_timer.v"
+`include "counter_timer_high.v"
+`include "counter_timer_low.v"
`include "wb_intercon.v"
`include "mem_wb.v"
`include "gpio_wb.v"
@@ -481,14 +482,16 @@
.irq(irq_spi_master)
);
- wire strobe_counter_timer0, strobe_counter_timer1;
+ wire counter_timer_strobe, counter_timer_offset;
+ wire counter_timer0_enable, counter_timer1_enable;
+ wire counter_timer0_stop, counter_timer1_stop;
// Wishbone Counter-timer 0
wire counter_timer0_stb_i;
wire counter_timer0_ack_o;
wire [31:0] counter_timer0_dat_o;
- counter_timer_wb #(
+ counter_timer_low_wb #(
.BASE_ADR(COUNTER_TIMER0_BASE_ADR),
.CONFIG(COUNTER_TIMER0_CONFIG),
.VALUE(COUNTER_TIMER0_VALUE),
@@ -497,7 +500,6 @@
// Wishbone Interface
.wb_clk_i(wb_clk_i),
.wb_rst_i(wb_rst_i),
- .strobe_in(strobe_counter_timer1),
.wb_adr_i(cpu_adr_o),
.wb_dat_i(cpu_dat_o),
@@ -508,7 +510,13 @@
.wb_stb_i(counter_timer0_stb_i),
.wb_ack_o(counter_timer0_ack_o),
.wb_dat_o(counter_timer0_dat_o),
- .strobe_out(strobe_counter_timer0),
+
+ .enable_in(counter_timer1_enable),
+ .stop_in(counter_timer1_stop),
+ .strobe(counter_timer_strobe),
+ .is_offset(counter_timer_offset),
+ .enable_out(counter_timer0_enable),
+ .stop_out(counter_timer0_stop),
.irq(irq_counter_timer0)
);
@@ -517,7 +525,7 @@
wire counter_timer1_ack_o;
wire [31:0] counter_timer1_dat_o;
- counter_timer_wb #(
+ counter_timer_high_wb #(
.BASE_ADR(COUNTER_TIMER1_BASE_ADR),
.CONFIG(COUNTER_TIMER1_CONFIG),
.VALUE(COUNTER_TIMER1_VALUE),
@@ -526,7 +534,6 @@
// Wishbone Interface
.wb_clk_i(wb_clk_i),
.wb_rst_i(wb_rst_i),
- .strobe_in(strobe_counter_timer0),
.wb_adr_i(cpu_adr_o),
.wb_dat_i(cpu_dat_o),
@@ -537,7 +544,13 @@
.wb_stb_i(counter_timer1_stb_i),
.wb_ack_o(counter_timer1_ack_o),
.wb_dat_o(counter_timer1_dat_o),
- .strobe_out(strobe_counter_timer1),
+
+ .enable_in(counter_timer0_enable),
+ .strobe(counter_timer_strobe),
+ .stop_in(counter_timer0_stop),
+ .is_offset(counter_timer_offset),
+ .enable_out(counter_timer1_enable),
+ .stop_out(counter_timer1_stop),
.irq(irq_counter_timer1)
);