[clkmgr / top] Add clock divider step down to support lc_ctrl transition
Signed-off-by: Timothy Chen <timothytim@google.com>
[clkmgr] updates per review comments
Signed-off-by: Timothy Chen <timothytim@google.com>
[clkmgr] update waiver
Signed-off-by: Timothy Chen <timothytim@google.com>
[top] Auto generate files
Signed-off-by: Timothy Chen <timothytim@google.com>
[top] Auto generate
Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/ip/clkmgr/data/clkmgr.hjson.tpl b/hw/ip/clkmgr/data/clkmgr.hjson.tpl
index 1ee888e..cddbb91 100644
--- a/hw/ip/clkmgr/data/clkmgr.hjson.tpl
+++ b/hw/ip/clkmgr/data/clkmgr.hjson.tpl
@@ -48,6 +48,20 @@
package: "clkmgr_pkg",
},
+ { struct: "lc_tx",
+ type: "uni",
+ name: "ast_clk_bypass_ack",
+ act: "rcv",
+ package: "lc_ctrl_pkg",
+ },
+
+ { struct: "lc_tx",
+ type: "uni",
+ name: "lc_clk_bypass_ack",
+ act: "req",
+ package: "lc_ctrl_pkg",
+ },
+
// All clock inputs
% for src in srcs:
{ struct: "logic",
diff --git a/hw/ip/clkmgr/data/clkmgr.sv.tpl b/hw/ip/clkmgr/data/clkmgr.sv.tpl
index 36e3d5a..03c708f 100644
--- a/hw/ip/clkmgr/data/clkmgr.sv.tpl
+++ b/hw/ip/clkmgr/data/clkmgr.sv.tpl
@@ -46,6 +46,10 @@
// idle hints
input [${len(hint_clks)-1}:0] idle_i,
+ // clock bypass control
+ input lc_ctrl_pkg::lc_tx_t ast_clk_bypass_ack_i,
+ output lc_ctrl_pkg::lc_tx_t lc_clk_bypass_ack_o,
+
// clock output interface
% for intf in export_clks:
output clkmgr_${intf}_out_t clocks_${intf}_o,
@@ -74,6 +78,17 @@
////////////////////////////////////////////////////
// Divided clocks
////////////////////////////////////////////////////
+
+ lc_ctrl_pkg::lc_tx_t step_down_req;
+ logic [${len(div_srcs)-1}:0] step_down_acks;
+
+ prim_lc_sync u_rcv (
+ .clk_i,
+ .rst_ni,
+ .lc_en_i(ast_clk_bypass_ack_i),
+ .lc_en_o(step_down_req)
+ );
+
% for src in div_srcs:
logic clk_${src['name']}_i;
% endfor
@@ -84,11 +99,19 @@
) u_${src['name']}_div (
.clk_i(clk_${src['src']}_i),
.rst_ni(rst_${src['src']}_ni),
+ .step_down_req_i(step_down_req == lc_ctrl_pkg::On),
+ .step_down_ack_o(step_down_acks[${loop.index}]),
.test_en_i(scanmode_i),
.clk_o(clk_${src['name']}_i)
);
% endfor
+ prim_lc_sender u_send (
+ .clk_i,
+ .rst_ni,
+ .lc_en_i(&step_down_acks ? lc_ctrl_pkg::On : lc_ctrl_pkg::Off),
+ .lc_en_o(lc_clk_bypass_ack_o)
+ );
////////////////////////////////////////////////////
// Feed through clocks
diff --git a/hw/ip/prim/lint/prim_clock_div.waiver b/hw/ip/prim/lint/prim_clock_div.waiver
index bd70fff..c9b4a73 100644
--- a/hw/ip/prim/lint/prim_clock_div.waiver
+++ b/hw/ip/prim/lint/prim_clock_div.waiver
@@ -3,3 +3,6 @@
# SPDX-License-Identifier: Apache-2.0
#
# waiver file for prim_clock_div
+
+waive -rules DUAL_EDGE_CLOCK -location {prim_clock_div.sv} -regexp {.*} \
+ -comment "The clock switch signal is synchronized on negative edge to ensure it is away from any transition"
diff --git a/hw/ip/prim/prim_clock_div.core b/hw/ip/prim/prim_clock_div.core
index 7bd9bfb..ab031d7 100644
--- a/hw/ip/prim/prim_clock_div.core
+++ b/hw/ip/prim/prim_clock_div.core
@@ -16,7 +16,16 @@
- rtl/prim_clock_div.sv
file_type: systemVerilogSource
+ files_ascentlint_waiver:
+ depend:
+ # common waivers
+ - lowrisc:lint:common
+ files:
+ - lint/prim_clock_div.waiver
+ file_type: waiver
+
targets:
default:
filesets:
+ - tool_ascentlint ? (files_ascentlint_waiver)
- files_rtl
diff --git a/hw/ip/prim/rtl/prim_clock_div.sv b/hw/ip/prim/rtl/prim_clock_div.sv
index 35dbbba..838d146 100644
--- a/hw/ip/prim/rtl/prim_clock_div.sv
+++ b/hw/ip/prim/rtl/prim_clock_div.sv
@@ -10,11 +10,17 @@
) (
input clk_i,
input rst_ni,
+ input step_down_req_i, // step down divisor by 2x
+ output logic step_down_ack_o, // step down acknowledge
input test_en_i,
output logic clk_o
);
+ // Only even divide is supported at the moment
+ // For odd divide we need to introduce more parameters to control duty cycle
+ `ASSERT_INIT(DivEven_A, (Divisor % 2) == 0)
+
logic clk_int;
if (Divisor == 2) begin : gen_div2
@@ -38,28 +44,56 @@
.clk_no(q_n)
);
- assign clk_int = q_p;
+ logic step_down_nq;
+ always_ff @(negedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ step_down_nq <= 1'b0;
+ end else begin
+ step_down_nq <= step_down_req_i;
+ end
+ end
+
+ // make sure selection point is away from both edges
+ prim_clock_mux2 #(
+ .NoFpgaBufG(1'b1)
+ ) u_step_down_mux (
+ .clk0_i(q_p),
+ .clk1_i(clk_i),
+ .sel_i(step_down_nq),
+ .clk_o(clk_int)
+ );
+
+ assign step_down_ack_o = step_down_nq;
end else begin : gen_div
- // Only even divide is supported at the moment
- // For odd divide we need to introduce more parameters to control duty cycle
- `ASSERT_INIT(DivEven_A, (Divisor % 2) == 0)
localparam int ToggleCnt = Divisor / 2;
localparam int CntWidth = $clog2(ToggleCnt);
logic [CntWidth-1:0] cnt;
+ logic [CntWidth-1:0] limit;
+
+ assign limit = !step_down_req_i ? ToggleCnt - 1 :
+ (ToggleCnt / 2) == 2 ? '0 : (ToggleCnt / 2) - 1;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
cnt <= '0;
clk_int <= ResetValue;
- end else if (cnt == ToggleCnt-1) begin
+ end else if (cnt >= limit) begin
cnt <= '0;
clk_int <= ~clk_o;
end else begin
cnt <= cnt + 1'b1;
end
end
+
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ step_down_ack_o <= 1'b0;
+ end else begin
+ step_down_ack_o <= step_down_req_i;
+ end
+ end
end
// when in scanmode, bypass the dividers completely
diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
index b73e62a..d573698 100644
--- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
+++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
@@ -1684,6 +1684,9 @@
default: lc_ctrl_pkg::Off
package: lc_ctrl_pkg
inst_name: lc_ctrl
+ width: 1
+ top_type: broadcast
+ top_signame: lc_ctrl_lc_clk_byp_ack
index: -1
}
{
@@ -2541,6 +2544,27 @@
index: -1
}
{
+ struct: lc_tx
+ type: uni
+ name: ast_clk_bypass_ack
+ act: rcv
+ package: lc_ctrl_pkg
+ inst_name: clkmgr
+ index: -1
+ }
+ {
+ struct: lc_tx
+ type: uni
+ name: lc_clk_bypass_ack
+ act: req
+ package: lc_ctrl_pkg
+ inst_name: clkmgr
+ width: 1
+ default: ""
+ top_signame: lc_ctrl_lc_clk_byp_ack
+ index: -1
+ }
+ {
struct: logic
type: uni
name: clk_main
@@ -5641,6 +5665,10 @@
otp_ctrl.lc_check_byp_en
]
lc_ctrl.lc_clk_byp_req: []
+ lc_ctrl.lc_clk_byp_ack:
+ [
+ clkmgr.lc_clk_bypass_ack
+ ]
lc_ctrl.lc_creator_seed_sw_rw_en:
[
otp_ctrl.lc_creator_seed_sw_rw_en
@@ -9163,6 +9191,9 @@
default: lc_ctrl_pkg::Off
package: lc_ctrl_pkg
inst_name: lc_ctrl
+ width: 1
+ top_type: broadcast
+ top_signame: lc_ctrl_lc_clk_byp_ack
index: -1
}
{
@@ -9677,6 +9708,27 @@
index: -1
}
{
+ struct: lc_tx
+ type: uni
+ name: ast_clk_bypass_ack
+ act: rcv
+ package: lc_ctrl_pkg
+ inst_name: clkmgr
+ index: -1
+ }
+ {
+ struct: lc_tx
+ type: uni
+ name: lc_clk_bypass_ack
+ act: req
+ package: lc_ctrl_pkg
+ inst_name: clkmgr
+ width: 1
+ default: ""
+ top_signame: lc_ctrl_lc_clk_byp_ack
+ index: -1
+ }
+ {
struct: logic
type: uni
name: clk_main
@@ -11875,6 +11927,14 @@
{
package: lc_ctrl_pkg
struct: lc_tx
+ signame: lc_ctrl_lc_clk_byp_ack
+ width: 1
+ type: uni
+ default: lc_ctrl_pkg::Off
+ }
+ {
+ package: lc_ctrl_pkg
+ struct: lc_tx
signame: lc_ctrl_lc_creator_seed_sw_rw_en
width: 1
type: uni
diff --git a/hw/top_earlgrey/data/top_earlgrey.hjson b/hw/top_earlgrey/data/top_earlgrey.hjson
index a5432ac..c474cba 100755
--- a/hw/top_earlgrey/data/top_earlgrey.hjson
+++ b/hw/top_earlgrey/data/top_earlgrey.hjson
@@ -641,8 +641,8 @@
'lc_ctrl.lc_check_byp_en' : ['otp_ctrl.lc_check_byp_en'],
// TODO: OTP Clock bypass signal going from LC to AST/clkmgr
- 'lc_ctrl.lc_clk_byp_req' : [],
- //'lc_ctrl.lc_clk_byp_ack' : [],
+ 'lc_ctrl.lc_clk_byp_req' : [],
+ 'lc_ctrl.lc_clk_byp_ack' : ['clkmgr.lc_clk_bypass_ack'],
// LC access control signal broadcast
'lc_ctrl.lc_creator_seed_sw_rw_en' : ['otp_ctrl.lc_creator_seed_sw_rw_en',
diff --git a/hw/top_earlgrey/ip/clkmgr/clkmgr.core b/hw/top_earlgrey/ip/clkmgr/clkmgr.core
index dbaa2f6..b3067b4 100644
--- a/hw/top_earlgrey/ip/clkmgr/clkmgr.core
+++ b/hw/top_earlgrey/ip/clkmgr/clkmgr.core
@@ -13,6 +13,7 @@
- lowrisc:prim:clock_gating
- lowrisc:prim:clock_buf
- lowrisc:prim:clock_div
+ - lowrisc:ip:lc_ctrl_pkg
- lowrisc:ip:pwrmgr_pkg
- lowrisc:systems:clkmgr_pkg
files:
diff --git a/hw/top_earlgrey/ip/clkmgr/data/autogen/clkmgr.hjson b/hw/top_earlgrey/ip/clkmgr/data/autogen/clkmgr.hjson
index b840cd2..214722c 100644
--- a/hw/top_earlgrey/ip/clkmgr/data/autogen/clkmgr.hjson
+++ b/hw/top_earlgrey/ip/clkmgr/data/autogen/clkmgr.hjson
@@ -46,6 +46,20 @@
package: "clkmgr_pkg",
},
+ { struct: "lc_tx",
+ type: "uni",
+ name: "ast_clk_bypass_ack",
+ act: "rcv",
+ package: "lc_ctrl_pkg",
+ },
+
+ { struct: "lc_tx",
+ type: "uni",
+ name: "lc_clk_bypass_ack",
+ act: "req",
+ package: "lc_ctrl_pkg",
+ },
+
// All clock inputs
{ struct: "logic",
type: "uni",
diff --git a/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr.sv b/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr.sv
index e5d5414..fcb74bd 100644
--- a/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr.sv
+++ b/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr.sv
@@ -49,6 +49,10 @@
// idle hints
input [3:0] idle_i,
+ // clock bypass control
+ input lc_ctrl_pkg::lc_tx_t ast_clk_bypass_ack_i,
+ output lc_ctrl_pkg::lc_tx_t lc_clk_bypass_ack_o,
+
// clock output interface
output clkmgr_ast_out_t clocks_ast_o,
output clkmgr_out_t clocks_o
@@ -75,6 +79,17 @@
////////////////////////////////////////////////////
// Divided clocks
////////////////////////////////////////////////////
+
+ lc_ctrl_pkg::lc_tx_t step_down_req;
+ logic [1:0] step_down_acks;
+
+ prim_lc_sync u_rcv (
+ .clk_i,
+ .rst_ni,
+ .lc_en_i(ast_clk_bypass_ack_i),
+ .lc_en_o(step_down_req)
+ );
+
logic clk_io_div2_i;
logic clk_io_div4_i;
@@ -83,6 +98,8 @@
) u_io_div2_div (
.clk_i(clk_io_i),
.rst_ni(rst_io_ni),
+ .step_down_req_i(step_down_req == lc_ctrl_pkg::On),
+ .step_down_ack_o(step_down_acks[0]),
.test_en_i(scanmode_i),
.clk_o(clk_io_div2_i)
);
@@ -91,10 +108,18 @@
) u_io_div4_div (
.clk_i(clk_io_i),
.rst_ni(rst_io_ni),
+ .step_down_req_i(step_down_req == lc_ctrl_pkg::On),
+ .step_down_ack_o(step_down_acks[1]),
.test_en_i(scanmode_i),
.clk_o(clk_io_div4_i)
);
+ prim_lc_sender u_send (
+ .clk_i,
+ .rst_ni,
+ .lc_en_i(&step_down_acks ? lc_ctrl_pkg::On : lc_ctrl_pkg::Off),
+ .lc_en_o(lc_clk_bypass_ack_o)
+ );
////////////////////////////////////////////////////
// Feed through clocks
diff --git a/hw/top_earlgrey/lint/top_earlgrey.waiver b/hw/top_earlgrey/lint/top_earlgrey.waiver
index 3762ff5..5420f35 100644
--- a/hw/top_earlgrey/lint/top_earlgrey.waiver
+++ b/hw/top_earlgrey/lint/top_earlgrey.waiver
@@ -6,7 +6,7 @@
# dedicated reset drivers / muxes
set_reset_drivers prim_clock_mux2 prim_flop_2sync prim_flop
-set_clock_drivers prim_clock_buf
+set_clock_drivers prim_clock_buf prim_clock_mux2
# All leaf resets have a reset multiplex
waive -rules RESET_MUX -location {top_earlgrey.sv} -regexp {Asynchronous reset .*rstmgr_resets\.rst.* is driven by a multiplexer} \
diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
index 2279807..a67f802 100644
--- a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
+++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
@@ -304,6 +304,7 @@
lc_ctrl_pkg::lc_tx_t lc_ctrl_lc_escalate_en;
lc_ctrl_pkg::lc_tx_t lc_ctrl_lc_check_byp_en;
lc_ctrl_pkg::lc_tx_t lc_ctrl_lc_clk_byp_req;
+ lc_ctrl_pkg::lc_tx_t lc_ctrl_lc_clk_byp_ack;
lc_ctrl_pkg::lc_tx_t lc_ctrl_lc_creator_seed_sw_rw_en;
lc_ctrl_pkg::lc_tx_t lc_ctrl_lc_owner_seed_sw_rw_en;
lc_ctrl_pkg::lc_tx_t lc_ctrl_lc_iso_part_sw_rd_en;
@@ -924,7 +925,7 @@
.lc_keymgr_en_o(),
.lc_escalate_en_o(lc_ctrl_lc_escalate_en),
.lc_clk_byp_req_o(lc_ctrl_lc_clk_byp_req),
- .lc_clk_byp_ack_i(lc_ctrl_pkg::Off),
+ .lc_clk_byp_ack_i(lc_ctrl_lc_clk_byp_ack),
.lc_flash_rma_req_o(flash_ctrl_rma_req),
.lc_flash_rma_seed_o(flash_ctrl_rma_seed),
.lc_flash_rma_ack_i(flash_ctrl_rma_ack),
@@ -1045,6 +1046,8 @@
// Inter-module signals
.clocks_o(clkmgr_clocks),
+ .ast_clk_bypass_ack_i(lc_ctrl_pkg::LC_TX_DEFAULT),
+ .lc_clk_bypass_ack_o(lc_ctrl_lc_clk_byp_ack),
.clk_main_i(clk_main_i),
.clk_io_i(clk_io_i),
.clk_usb_i(clk_usb_i),