[clkmgr] Allow software to control clock stepdown
- Fixes #8027
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 0ec3605..949ff1c 100644
--- a/hw/ip/clkmgr/data/clkmgr.hjson.tpl
+++ b/hw/ip/clkmgr/data/clkmgr.hjson.tpl
@@ -125,8 +125,8 @@
registers: [
- { name: "EXTCLK_SEL_REGWEN",
- desc: "External clock select write enable",
+ { name: "EXTCLK_CTRL_REGWEN",
+ desc: "External clock control write enable",
swaccess: "rw0c",
hwaccess: "none",
fields: [
@@ -134,24 +134,24 @@
name: "EN",
resval: "1"
desc: '''
- When 1, the value of !!EXTCLK_SEL can be set. When 0, writes to !!EXTCLK_SEL have no
+ When 1, the value of !!EXTCLK_CTRL can be set. When 0, writes to !!EXTCLK_CTRL have no
effect.
'''
},
]
},
- { name: "EXTCLK_SEL",
+ { name: "EXTCLK_CTRL",
desc: '''
Select external clock
''',
- regwen: "EXTCLK_SEL_REGWEN",
+ regwen: "EXTCLK_CTRL_REGWEN",
swaccess: "rw",
hwaccess: "hro",
fields: [
{
bits: "3:0",
- name: "VAL",
+ name: "SEL",
desc: '''
A value of b1010 selects external clock as clock for the system.
While this register can always be programmed, it only takes effect when the system is in
@@ -160,6 +160,17 @@
All other values are invalid and keep clocks on internal sources.
'''
resval: "0x5"
+ },
+ {
+ bits: "7:4",
+ name: "STEP_DOWN",
+ desc: '''
+ A value of b1010 steps down the clock dividers by a factor of 2 if the !!EXTCLK_CTRL.SEL
+ field is also set to b1010.
+
+ All other values have no effect.
+ '''
+ resval: "0x5"
}
]
},
diff --git a/hw/ip/clkmgr/data/clkmgr.sv.tpl b/hw/ip/clkmgr/data/clkmgr.sv.tpl
index 6efe895..f4601d8 100644
--- a/hw/ip/clkmgr/data/clkmgr.sv.tpl
+++ b/hw/ip/clkmgr/data/clkmgr.sv.tpl
@@ -168,7 +168,8 @@
.clk_i,
.rst_ni,
.en_i(lc_dft_en_i),
- .byp_req(lc_tx_t'(reg2hw.extclk_sel.q)),
+ .byp_req_i(lc_tx_t'(reg2hw.extclk_ctrl.sel.q)),
+ .step_down_req_i(lc_tx_t'(reg2hw.extclk_ctrl.step_down.q)),
.ast_clk_byp_req_o,
.ast_clk_byp_ack_i,
.lc_clk_byp_req_i,
diff --git a/hw/ip/clkmgr/doc/_index.md b/hw/ip/clkmgr/doc/_index.md
index f056fc9..ec60bad 100644
--- a/hw/ip/clkmgr/doc/_index.md
+++ b/hw/ip/clkmgr/doc/_index.md
@@ -189,18 +189,18 @@
Software request for external clocks is not always valid.
Software is only able to request for external clocks when dft functions are [allowed]({{< relref "hw/ip/lc_ctrl/doc/_index.md#dft_en" >}}).
+When software requests the external clock switch, it also has the option to request whether the clock divider should be stepped down by a factor of 2.
+
When the life cycle controller requests external clock, a request signal `lc_clk_byp_req_i` is sent from `lc_ctrl` to `clkmgr`.
`clkmgr` then forwards the request to `ast` through `ast_clk_byp_req_o`, which performs the actual clock switch.
-When the clock switch is complete, the life cycle controller is acknowledged through `lc_clk_byp_ack_o`.
+When the clock switch is complete, the clock dividers are stepped down by a factor of 2 and the life cycle controller is acknowledged through `lc_clk_byp_ack_o`.
-When software requests external clock, the register bit {{< regref "EXTCLK_SEL" >}} is written.
+When software requests external clock, the register bit {{< regref "EXTCLK_CTRL" >}} is written.
If dft functions are allowed, the `clkmgr` sends a request signal `ast_clk_byp_req_o` to `ast`.
-In both cases, when the clock switch is complete, the internal dividers of the `clkmgr` are stepped down by 2x.
-A divide-by-4 clock becomes divide-by-2 clock , and a divide-by-2 becomes a divide-by-1 clock.
-The step down function will be made more flexible in the future as it is highly dependent on the ratio of internal to external clock ratios.
-However, given currently known requirements, a blanket 2x step down is sufficient.
+When the divider is stepped down, a divide-by-4 clock becomes divide-by-2 clock , and a divide-by-2 becomes a divide-by-1 clock.
+This allows external connection to be either nominal frequencies or nominal divided-by-2.
### Clock Frequency Measurements
diff --git a/hw/ip/clkmgr/rtl/clkmgr_byp.sv b/hw/ip/clkmgr/rtl/clkmgr_byp.sv
index a802d07..713bf5a 100644
--- a/hw/ip/clkmgr/rtl/clkmgr_byp.sv
+++ b/hw/ip/clkmgr/rtl/clkmgr_byp.sv
@@ -10,7 +10,8 @@
input clk_i,
input rst_ni,
input lc_tx_t en_i,
- input lc_tx_t byp_req,
+ input lc_tx_t byp_req_i,
+ input lc_tx_t step_down_req_i,
output lc_tx_t ast_clk_byp_req_o,
input lc_tx_t ast_clk_byp_ack_i,
input lc_tx_t lc_clk_byp_req_i,
@@ -26,7 +27,7 @@
// Generate qualified reg clk bypass request
for (genvar i = 0; i < $bits(lc_tx_t); i++) begin : gen_clk_byp
prim_buf u_buf (
- .in_i(on_val[i] ? byp_req[i] & en_i[i] : byp_req[i] | en_i[i]),
+ .in_i(on_val[i] ? byp_req_i[i] & en_i[i] : byp_req_i[i] | en_i[i]),
.out_o(reg_clk_byp_req[i])
);
end
@@ -48,13 +49,20 @@
.lc_en_o(ast_clk_byp_req_o)
);
+ lc_tx_t ast_clk_byp_ack;
prim_lc_sync u_rcv (
.clk_i,
.rst_ni,
.lc_en_i(ast_clk_byp_ack_i),
- .lc_en_o(step_down_req_o)
+ .lc_en_o(ast_clk_byp_ack)
);
+ // if switch request came from software, let software dictate whether to step down
+ assign step_down_req_o =
+ lc_clk_byp_req_i == lc_ctrl_pkg::On ? ast_clk_byp_ack :
+ reg_clk_byp_req == lc_ctrl_pkg::On ? ast_clk_byp_ack & step_down_req_i :
+ lc_ctrl_pkg::Off;
+
// only ack the lc_ctrl if it made a request.
prim_lc_sender u_send (
.clk_i,
diff --git a/hw/top_earlgrey/ip/clkmgr/data/autogen/clkmgr.hjson b/hw/top_earlgrey/ip/clkmgr/data/autogen/clkmgr.hjson
index e25b1b0..b8bb167 100644
--- a/hw/top_earlgrey/ip/clkmgr/data/autogen/clkmgr.hjson
+++ b/hw/top_earlgrey/ip/clkmgr/data/autogen/clkmgr.hjson
@@ -142,8 +142,8 @@
registers: [
- { name: "EXTCLK_SEL_REGWEN",
- desc: "External clock select write enable",
+ { name: "EXTCLK_CTRL_REGWEN",
+ desc: "External clock control write enable",
swaccess: "rw0c",
hwaccess: "none",
fields: [
@@ -151,24 +151,24 @@
name: "EN",
resval: "1"
desc: '''
- When 1, the value of !!EXTCLK_SEL can be set. When 0, writes to !!EXTCLK_SEL have no
+ When 1, the value of !!EXTCLK_CTRL can be set. When 0, writes to !!EXTCLK_CTRL have no
effect.
'''
},
]
},
- { name: "EXTCLK_SEL",
+ { name: "EXTCLK_CTRL",
desc: '''
Select external clock
''',
- regwen: "EXTCLK_SEL_REGWEN",
+ regwen: "EXTCLK_CTRL_REGWEN",
swaccess: "rw",
hwaccess: "hro",
fields: [
{
bits: "3:0",
- name: "VAL",
+ name: "SEL",
desc: '''
A value of b1010 selects external clock as clock for the system.
While this register can always be programmed, it only takes effect when the system is in
@@ -177,6 +177,17 @@
All other values are invalid and keep clocks on internal sources.
'''
resval: "0x5"
+ },
+ {
+ bits: "7:4",
+ name: "STEP_DOWN",
+ desc: '''
+ A value of b1010 steps down the clock dividers by a factor of 2 if the !!EXTCLK_CTRL.SEL
+ field is also set to b1010.
+
+ All other values have no effect.
+ '''
+ resval: "0x5"
}
]
},
diff --git a/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr.sv b/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr.sv
index 0b6c77b..ff1e875 100644
--- a/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr.sv
+++ b/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr.sv
@@ -193,7 +193,8 @@
.clk_i,
.rst_ni,
.en_i(lc_dft_en_i),
- .byp_req(lc_tx_t'(reg2hw.extclk_sel.q)),
+ .byp_req_i(lc_tx_t'(reg2hw.extclk_ctrl.sel.q)),
+ .step_down_req_i(lc_tx_t'(reg2hw.extclk_ctrl.step_down.q)),
.ast_clk_byp_req_o,
.ast_clk_byp_ack_i,
.lc_clk_byp_req_i,
diff --git a/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr_reg_pkg.sv b/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr_reg_pkg.sv
index 874419e..b98d22e 100644
--- a/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr_reg_pkg.sv
+++ b/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr_reg_pkg.sv
@@ -29,8 +29,13 @@
} clkmgr_reg2hw_alert_test_reg_t;
typedef struct packed {
- logic [3:0] q;
- } clkmgr_reg2hw_extclk_sel_reg_t;
+ struct packed {
+ logic [3:0] q;
+ } sel;
+ struct packed {
+ logic [3:0] q;
+ } step_down;
+ } clkmgr_reg2hw_extclk_ctrl_reg_t;
typedef struct packed {
logic q;
@@ -204,8 +209,8 @@
// Register -> HW type
typedef struct packed {
- clkmgr_reg2hw_alert_test_reg_t alert_test; // [120:117]
- clkmgr_reg2hw_extclk_sel_reg_t extclk_sel; // [116:113]
+ clkmgr_reg2hw_alert_test_reg_t alert_test; // [124:121]
+ clkmgr_reg2hw_extclk_ctrl_reg_t extclk_ctrl; // [120:113]
clkmgr_reg2hw_jitter_enable_reg_t jitter_enable; // [112:112]
clkmgr_reg2hw_clk_enables_reg_t clk_enables; // [111:108]
clkmgr_reg2hw_clk_hints_reg_t clk_hints; // [107:103]
@@ -227,8 +232,8 @@
// Register offsets
parameter logic [BlockAw-1:0] CLKMGR_ALERT_TEST_OFFSET = 6'h 0;
- parameter logic [BlockAw-1:0] CLKMGR_EXTCLK_SEL_REGWEN_OFFSET = 6'h 4;
- parameter logic [BlockAw-1:0] CLKMGR_EXTCLK_SEL_OFFSET = 6'h 8;
+ parameter logic [BlockAw-1:0] CLKMGR_EXTCLK_CTRL_REGWEN_OFFSET = 6'h 4;
+ parameter logic [BlockAw-1:0] CLKMGR_EXTCLK_CTRL_OFFSET = 6'h 8;
parameter logic [BlockAw-1:0] CLKMGR_JITTER_ENABLE_OFFSET = 6'h c;
parameter logic [BlockAw-1:0] CLKMGR_CLK_ENABLES_OFFSET = 6'h 10;
parameter logic [BlockAw-1:0] CLKMGR_CLK_HINTS_OFFSET = 6'h 14;
@@ -250,8 +255,8 @@
// Register index
typedef enum int {
CLKMGR_ALERT_TEST,
- CLKMGR_EXTCLK_SEL_REGWEN,
- CLKMGR_EXTCLK_SEL,
+ CLKMGR_EXTCLK_CTRL_REGWEN,
+ CLKMGR_EXTCLK_CTRL,
CLKMGR_JITTER_ENABLE,
CLKMGR_CLK_ENABLES,
CLKMGR_CLK_HINTS,
@@ -269,8 +274,8 @@
// Register width information to check illegal writes
parameter logic [3:0] CLKMGR_PERMIT [15] = '{
4'b 0001, // index[ 0] CLKMGR_ALERT_TEST
- 4'b 0001, // index[ 1] CLKMGR_EXTCLK_SEL_REGWEN
- 4'b 0001, // index[ 2] CLKMGR_EXTCLK_SEL
+ 4'b 0001, // index[ 1] CLKMGR_EXTCLK_CTRL_REGWEN
+ 4'b 0001, // index[ 2] CLKMGR_EXTCLK_CTRL
4'b 0001, // index[ 3] CLKMGR_JITTER_ENABLE
4'b 0001, // index[ 4] CLKMGR_CLK_ENABLES
4'b 0001, // index[ 5] CLKMGR_CLK_HINTS
diff --git a/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr_reg_top.sv b/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr_reg_top.sv
index 3d4b1bd..24effbe 100644
--- a/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr_reg_top.sv
+++ b/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr_reg_top.sv
@@ -111,12 +111,14 @@
logic alert_test_we;
logic alert_test_recov_fault_wd;
logic alert_test_fatal_fault_wd;
- logic extclk_sel_regwen_we;
- logic extclk_sel_regwen_qs;
- logic extclk_sel_regwen_wd;
- logic extclk_sel_we;
- logic [3:0] extclk_sel_qs;
- logic [3:0] extclk_sel_wd;
+ logic extclk_ctrl_regwen_we;
+ logic extclk_ctrl_regwen_qs;
+ logic extclk_ctrl_regwen_wd;
+ logic extclk_ctrl_we;
+ logic [3:0] extclk_ctrl_sel_qs;
+ logic [3:0] extclk_ctrl_sel_wd;
+ logic [3:0] extclk_ctrl_step_down_qs;
+ logic [3:0] extclk_ctrl_step_down_wd;
logic jitter_enable_we;
logic jitter_enable_qs;
logic jitter_enable_wd;
@@ -227,18 +229,18 @@
);
- // R[extclk_sel_regwen]: V(False)
+ // R[extclk_ctrl_regwen]: V(False)
prim_subreg #(
.DW (1),
.SwAccess(prim_subreg_pkg::SwAccessW0C),
.RESVAL (1'h1)
- ) u_extclk_sel_regwen (
+ ) u_extclk_ctrl_regwen (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
- .we (extclk_sel_regwen_we),
- .wd (extclk_sel_regwen_wd),
+ .we (extclk_ctrl_regwen_we),
+ .wd (extclk_ctrl_regwen_wd),
// from internal hardware
.de (1'b0),
@@ -249,22 +251,23 @@
.q (),
// to register interface (read)
- .qs (extclk_sel_regwen_qs)
+ .qs (extclk_ctrl_regwen_qs)
);
- // R[extclk_sel]: V(False)
+ // R[extclk_ctrl]: V(False)
+ // F[sel]: 3:0
prim_subreg #(
.DW (4),
.SwAccess(prim_subreg_pkg::SwAccessRW),
.RESVAL (4'h5)
- ) u_extclk_sel (
+ ) u_extclk_ctrl_sel (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
- .we (extclk_sel_we & extclk_sel_regwen_qs),
- .wd (extclk_sel_wd),
+ .we (extclk_ctrl_we & extclk_ctrl_regwen_qs),
+ .wd (extclk_ctrl_sel_wd),
// from internal hardware
.de (1'b0),
@@ -272,10 +275,35 @@
// to internal hardware
.qe (),
- .q (reg2hw.extclk_sel.q),
+ .q (reg2hw.extclk_ctrl.sel.q),
// to register interface (read)
- .qs (extclk_sel_qs)
+ .qs (extclk_ctrl_sel_qs)
+ );
+
+ // F[step_down]: 7:4
+ prim_subreg #(
+ .DW (4),
+ .SwAccess(prim_subreg_pkg::SwAccessRW),
+ .RESVAL (4'h5)
+ ) u_extclk_ctrl_step_down (
+ .clk_i (clk_i),
+ .rst_ni (rst_ni),
+
+ // from register interface
+ .we (extclk_ctrl_we & extclk_ctrl_regwen_qs),
+ .wd (extclk_ctrl_step_down_wd),
+
+ // from internal hardware
+ .de (1'b0),
+ .d ('0),
+
+ // to internal hardware
+ .qe (),
+ .q (reg2hw.extclk_ctrl.step_down.q),
+
+ // to register interface (read)
+ .qs (extclk_ctrl_step_down_qs)
);
@@ -1230,8 +1258,8 @@
always_comb begin
addr_hit = '0;
addr_hit[ 0] = (reg_addr == CLKMGR_ALERT_TEST_OFFSET);
- addr_hit[ 1] = (reg_addr == CLKMGR_EXTCLK_SEL_REGWEN_OFFSET);
- addr_hit[ 2] = (reg_addr == CLKMGR_EXTCLK_SEL_OFFSET);
+ addr_hit[ 1] = (reg_addr == CLKMGR_EXTCLK_CTRL_REGWEN_OFFSET);
+ addr_hit[ 2] = (reg_addr == CLKMGR_EXTCLK_CTRL_OFFSET);
addr_hit[ 3] = (reg_addr == CLKMGR_JITTER_ENABLE_OFFSET);
addr_hit[ 4] = (reg_addr == CLKMGR_CLK_ENABLES_OFFSET);
addr_hit[ 5] = (reg_addr == CLKMGR_CLK_HINTS_OFFSET);
@@ -1272,12 +1300,14 @@
assign alert_test_recov_fault_wd = reg_wdata[0];
assign alert_test_fatal_fault_wd = reg_wdata[1];
- assign extclk_sel_regwen_we = addr_hit[1] & reg_we & !reg_error;
+ assign extclk_ctrl_regwen_we = addr_hit[1] & reg_we & !reg_error;
- assign extclk_sel_regwen_wd = reg_wdata[0];
- assign extclk_sel_we = addr_hit[2] & reg_we & !reg_error;
+ assign extclk_ctrl_regwen_wd = reg_wdata[0];
+ assign extclk_ctrl_we = addr_hit[2] & reg_we & !reg_error;
- assign extclk_sel_wd = reg_wdata[3:0];
+ assign extclk_ctrl_sel_wd = reg_wdata[3:0];
+
+ assign extclk_ctrl_step_down_wd = reg_wdata[7:4];
assign jitter_enable_we = addr_hit[3] & reg_we & !reg_error;
assign jitter_enable_wd = reg_wdata[0];
@@ -1361,11 +1391,12 @@
end
addr_hit[1]: begin
- reg_rdata_next[0] = extclk_sel_regwen_qs;
+ reg_rdata_next[0] = extclk_ctrl_regwen_qs;
end
addr_hit[2]: begin
- reg_rdata_next[3:0] = extclk_sel_qs;
+ reg_rdata_next[3:0] = extclk_ctrl_sel_qs;
+ reg_rdata_next[7:4] = extclk_ctrl_step_down_qs;
end
addr_hit[3]: begin