up_clock_mon: Remove extra hold register

Currently the clock monitor features a hold register in the monitored clock
domain. This old register is used to store a instantaneous copy of the
counter register. The value in the old register is then transferred to the
monitoring domain. Since the counter is continuously counting it is not
possible to directly transfer it since that might result in inconsistent
data.

Instead stop the counter and hold the registers stable for a duration that
is long enough for the monitoring domain to correctly capture the value.
Once the value has been transferred the counter is reset and restarted for
the next iteration.

This allows to eliminate the hold register, which slightly reduces
utilization.

The externally visible behaviour is identical before and after the patch.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
main
Lars-Peter Clausen 2017-05-16 15:52:59 +02:00
parent 696305360c
commit 139876d28a
2 changed files with 62 additions and 49 deletions

View File

@ -38,76 +38,89 @@ module up_clock_mon (
// internal registers
reg [15:0] up_count = 'd0;
reg up_count_toggle = 'd0;
reg up_count_toggle_m1 = 'd0;
reg up_count_toggle_m2 = 'd0;
reg up_count_toggle_m3 = 'd0;
reg d_count_toggle_m1 = 'd0;
reg d_count_toggle_m2 = 'd0;
reg d_count_toggle_m3 = 'd0;
reg d_count_toggle = 'd0;
reg [31:0] d_count_hold = 'd0;
reg [15:0] up_count = 'd1;
reg up_count_run = 'd0;
reg up_count_running_m1 = 'd0;
reg up_count_running_m2 = 'd0;
reg up_count_running_m3 = 'd0;
reg d_count_run_m1 = 'd0;
reg d_count_run_m2 = 'd0;
reg d_count_run_m3 = 'd0;
reg [32:0] d_count = 'd0;
// internal signals
wire up_count_toggle_s;
wire d_count_toggle_s;
wire up_count_capture_s;
wire d_count_reset_s;
// processor reference
assign up_count_toggle_s = up_count_toggle_m3 ^ up_count_toggle_m2;
// Capture on the falling edge of running
assign up_count_capture_s = up_count_running_m3 == 1'b1 && up_count_running_m2 == 1'b0;
always @(negedge up_rstn or posedge up_clk) begin
if (up_rstn == 0) begin
up_count_running_m1 <= 1'b0;
up_count_running_m2 <= 1'b0;
up_count_running_m3 <= 1'b0;
end else begin
up_count_running_m1 <= d_count_run_m3;
up_count_running_m2 <= up_count_running_m1;
up_count_running_m3 <= up_count_running_m2;
end
end
always @(negedge up_rstn or posedge up_clk) begin
if (up_rstn == 0) begin
up_count <= 'd0;
up_count_toggle <= 'd0;
up_count_toggle_m1 <= 'd0;
up_count_toggle_m2 <= 'd0;
up_count_toggle_m3 <= 'd0;
up_d_count <= 'd0;
up_count_run <= 1'b0;
end else begin
if (up_count_running_m3 == 1'b0) begin
up_count_run <= 1'b1;
end else if (up_count == 'h00) begin
up_count_run <= 1'b0;
end
if (up_count_capture_s == 1'b1) begin
up_d_count <= d_count;
end
end
end
always @(posedge up_clk) begin
if (up_count_run == 1'b0) begin
up_count <= 'h01;
end else begin
up_count <= up_count + 1'b1;
if (up_count == 16'd0) begin
up_count_toggle <= ~up_count_toggle;
end
up_count_toggle_m1 <= d_count_toggle;
up_count_toggle_m2 <= up_count_toggle_m1;
up_count_toggle_m3 <= up_count_toggle_m2;
if (up_count_toggle_s == 1'b1) begin
up_d_count <= d_count_hold;
end
end
end
// device free running
assign d_count_toggle_s = d_count_toggle_m3 ^ d_count_toggle_m2;
// Reset on the rising edge of run
assign d_count_reset_s = d_count_run_m3 == 1'b0 && d_count_run_m2 == 1'b1;
always @(posedge d_clk or posedge d_rst) begin
if (d_rst == 1'b1) begin
d_count_toggle_m1 <= 'd0;
d_count_toggle_m2 <= 'd0;
d_count_toggle_m3 <= 'd0;
d_count_run_m1 <= 1'b0;
d_count_run_m2 <= 1'b0;
d_count_run_m3 <= 1'b0;
end else begin
d_count_toggle_m1 <= up_count_toggle;
d_count_toggle_m2 <= d_count_toggle_m1;
d_count_toggle_m3 <= d_count_toggle_m2;
d_count_run_m1 <= up_count_run;
d_count_run_m2 <= d_count_run_m1;
d_count_run_m3 <= d_count_run_m2;
end
end
always @(posedge d_clk) begin
if (d_count_toggle_s == 1'b1) begin
d_count_toggle <= ~d_count_toggle;
d_count_hold <= d_count[31:0];
end
if (d_count_toggle_s == 1'b1) begin
d_count <= 33'd1;
end else if (d_count[32] == 1'b0) begin
d_count <= d_count + 1'b1;
end else begin
d_count <= {33{1'b1}};
if (d_count_reset_s == 1'b1) begin
d_count <= 'h00;
end else if (d_count_run_m3 == 1'b1) begin
if (d_count[32] == 1'b0) begin
d_count <= d_count + 1'b1;
end else begin
d_count <= {33{1'b1}};
end
end
end

View File

@ -1,7 +1,7 @@
set_property ASYNC_REG TRUE [get_cells -hier -filter {name =~ *up_count_toggle_m*}]
set_property ASYNC_REG TRUE [get_cells -hier -filter {name =~ *d_count_toggle_m*}]
set_property ASYNC_REG true [get_cells -hier -filter {name =~ *up_count_running_m*}]
set_property ASYNC_REG true [get_cells -hier -filter {name =~ *d_count_run_m*}]
set_false_path -from [get_cells -hier -filter {name =~ *d_count_toggle_reg && IS_SEQUENTIAL}] -to [get_cells -hier -filter {name =~ *up_count_toggle_m1_reg && IS_SEQUENTIAL}]
set_false_path -from [get_cells -hier -filter {name =~ *up_count_toggle_reg && IS_SEQUENTIAL}] -to [get_cells -hier -filter {name =~ *d_count_toggle_m1_reg && IS_SEQUENTIAL}]
set_false_path -from [get_cells -hier -filter {name =~ *d_count_hold* && IS_SEQUENTIAL}] -to [get_cells -hier -filter {name =~ *up_d_count* && IS_SEQUENTIAL}]
set_false_path -from [get_cells -hier -filter {name =~ *d_count_run_m3_reg && IS_SEQUENTIAL}] -to [get_cells -hier -filter {name =~ *up_count_running_m1_reg && IS_SEQUENTIAL}]
set_false_path -from [get_cells -hier -filter {name =~ *up_count_run_reg && IS_SEQUENTIAL}] -to [get_cells -hier -filter {name =~ *d_count_run_m1_reg && IS_SEQUENTIAL}]
set_false_path -from [get_cells -hier -filter {name =~ *d_count* && IS_SEQUENTIAL}] -to [get_cells -hier -filter {name =~ *up_d_count* && IS_SEQUENTIAL}]