Files
retroSoC/rtl/mini/top/rcu.sv
T
maksyuki fc898bc4ab feat: add PLL controller and related testbench functionality
- Removed legacy clock definitions and added a dynamic PLL timing template in retrosoc_pll.sdc.
- Introduced a new SystemVerilog testbench (pll_ctrl_tb.sv) for the PLL controller, including clock generation, register read/write tasks, and validation of PLL configuration and timing.
- Updated sysctrl_fault_tb.sv to include PLL controller interface and initialization.
- Enhanced memory map tests to verify PLL configuration and status offsets.
- Added tests for PLL controller dynamic reconfiguration and fallback to safe clock.
2026-07-20 22:12:17 +08:00

387 lines
11 KiB
Systemverilog

// Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
module rcu (
input logic ext_clk_i,
input logic aud_clk_i,
input logic ext_rst_n_i,
`ifdef HAVE_PLL
input logic xtal_clk_i,
`endif
pll_ctrl_if.rcu pll_ctrl,
output logic sys_clk_o,
output logic sys_rst_n_o,
output logic aud_rst_n_o,
output logic sys_clkdiv4_o
);
logic s_ext_clk_buf;
logic s_aud_clk_buf;
logic s_sys_clk;
logic s_ext_rst_n_sync;
logic s_sys_rst_n_sync;
logic s_aud_rst_n_sync;
logic [3:0] s_div_cnt_d, s_div_cnt_q;
logic s_sys_clkdiv4_d, s_sys_clkdiv4_q;
tc_clk_buf u_ext_clk_buf (
.clk_i(ext_clk_i),
.clk_o(s_ext_clk_buf)
);
tc_clk_buf u_aud_clk_buf (
.clk_i(aud_clk_i),
.clk_o(s_aud_clk_buf)
);
rst_sync #(
.STAGE(5)
) u_ext_rst_sync (
.clk_i (s_ext_clk_buf),
.rst_n_i(ext_rst_n_i),
.rst_n_o(s_ext_rst_n_sync)
);
`ifdef HAVE_PLL
logic s_xtal_clk_buf;
logic s_pll_clk;
logic s_pll_clk_buf;
logic s_pll_lock;
logic s_pll_capable;
logic [2:0] s_pll_sel;
logic s_pll_apply;
logic s_select_ext_clk;
tc_clk_buf u_xtal_buf (
.clk_i(xtal_clk_i),
.clk_o(s_xtal_clk_buf)
);
tc_pll u_tc_pll (
.fref_i (s_xtal_clk_buf),
.rst_n_i (s_ext_rst_n_sync),
.cfg_sel_i (s_pll_sel),
.cfg_apply_i (s_pll_apply),
.pll_capable_o(s_pll_capable),
.pll_lock_o (s_pll_lock),
.pll_clk_o (s_pll_clk)
);
tc_clk_buf u_pll_clk_buf (
.clk_i(s_pll_clk),
.clk_o(s_pll_clk_buf)
);
tc_clk_switch2 u_sys_clk_switch (
.clk0_i (s_pll_clk_buf),
.clk1_i (s_ext_clk_buf),
.clk_sel_i(s_select_ext_clk),
.clk_o (s_sys_clk)
);
pll_rcu_controller u_pll_rcu_controller (
.sys_clk_i (s_sys_clk),
.sys_rst_n_i (s_sys_rst_n_sync),
.ext_clk_i (s_ext_clk_buf),
.ext_rst_n_i (s_ext_rst_n_sync),
.pll_lock_i (s_pll_lock),
.pll_capable_i (s_pll_capable),
.pll_sel_o (s_pll_sel),
.pll_apply_o (s_pll_apply),
.select_ext_clk_o(s_select_ext_clk),
.pll_ctrl (pll_ctrl)
);
`else
logic s_no_pll_rsp_valid_d, s_no_pll_rsp_valid_q;
assign s_sys_clk = s_ext_clk_buf;
assign pll_ctrl.req_ready_i = ~s_no_pll_rsp_valid_q;
assign pll_ctrl.rsp_active_sel_i = '0;
assign pll_ctrl.rsp_active_valid_i = 1'b0;
assign pll_ctrl.rsp_safe_clk_i = 1'b1;
assign pll_ctrl.rsp_pll_lock_i = 1'b0;
assign pll_ctrl.rsp_error_i = 2'd1;
assign pll_ctrl.rsp_valid_i = s_no_pll_rsp_valid_q;
assign pll_ctrl.capable_i = 1'b0;
always_comb begin
s_no_pll_rsp_valid_d = s_no_pll_rsp_valid_q;
if (pll_ctrl.req_valid_o && pll_ctrl.req_ready_i) begin
s_no_pll_rsp_valid_d = 1'b1;
end
if (s_no_pll_rsp_valid_q && pll_ctrl.rsp_ready_o) begin
s_no_pll_rsp_valid_d = 1'b0;
end
end
dffr #(1) u_no_pll_rsp_valid_dffr (
s_ext_clk_buf,
s_ext_rst_n_sync,
s_no_pll_rsp_valid_d,
s_no_pll_rsp_valid_q
);
`endif
tc_clk_buf u_sys_clk_buf (
.clk_i(s_sys_clk),
.clk_o(sys_clk_o)
);
rst_sync #(
.STAGE(5)
) u_sys_rst_sync (
.clk_i (sys_clk_o),
.rst_n_i(ext_rst_n_i),
.rst_n_o(s_sys_rst_n_sync)
);
rst_sync #(
.STAGE(5)
) u_aud_rst_sync (
.clk_i (s_aud_clk_buf),
.rst_n_i(ext_rst_n_i),
.rst_n_o(s_aud_rst_n_sync)
);
assign sys_rst_n_o = s_sys_rst_n_sync;
assign aud_rst_n_o = s_aud_rst_n_sync;
assign sys_clkdiv4_o = s_sys_clkdiv4_q;
assign s_div_cnt_d = (s_div_cnt_q == 4'd1) ? '0 : s_div_cnt_q + 1'b1;
dffr #(4) u_div_cnt_dffr (
sys_clk_o,
sys_rst_n_o,
s_div_cnt_d,
s_div_cnt_q
);
assign s_sys_clkdiv4_d = (s_div_cnt_q == 4'd1) ? ~s_sys_clkdiv4_q : s_sys_clkdiv4_q;
dffr #(1) u_sys_clkdiv4_dffr (
sys_clk_o,
sys_rst_n_o,
s_sys_clkdiv4_d,
s_sys_clkdiv4_q
);
endmodule
module pll_rcu_controller #(
parameter int LOCK_TIMEOUT = 1024
) (
input logic sys_clk_i,
input logic sys_rst_n_i,
input logic ext_clk_i,
input logic ext_rst_n_i,
input logic pll_lock_i,
input logic pll_capable_i,
output logic [2:0] pll_sel_o,
output logic pll_apply_o,
output logic select_ext_clk_o,
pll_ctrl_if.rcu pll_ctrl
);
localparam logic [2:0] PLL_IDLE = 3'd0;
localparam logic [2:0] PLL_SAFE = 3'd1;
localparam logic [2:0] PLL_APPLY = 3'd2;
localparam logic [2:0] PLL_WAIT_LOCK = 3'd3;
localparam logic [2:0] PLL_SWITCH = 3'd4;
localparam logic [2:0] PLL_RESPOND = 3'd5;
logic [2:0] s_req_sel;
logic s_req_valid, s_req_ready;
logic [7:0] s_rsp_data;
logic s_rsp_valid, s_rsp_ready;
logic [7:0] s_rsp_sys_data;
logic s_rsp_sys_valid;
logic [2:0] s_state_d, s_state_q;
logic [2:0] s_pll_sel_d, s_pll_sel_q;
logic s_pll_apply_d, s_pll_apply_q;
logic s_select_ext_clk_d, s_select_ext_clk_q;
logic [15:0] s_lock_count_d, s_lock_count_q;
logic [2:0] s_active_sel_d, s_active_sel_q;
logic s_active_valid_d, s_active_valid_q;
logic s_safe_clk_d, s_safe_clk_q;
logic s_lock_d, s_lock_q;
logic [1:0] s_error_d, s_error_q;
logic s_rsp_valid_d, s_rsp_valid_q;
cdc_2phase #(
.DATA_WIDTH(3)
) u_pll_request_cdc (
.src_clk_i (sys_clk_i),
.src_rst_n_i(sys_rst_n_i),
.src_data_i (pll_ctrl.req_sel_o),
.src_valid_i(pll_ctrl.req_valid_o),
.src_ready_o(pll_ctrl.req_ready_i),
.dst_clk_i (ext_clk_i),
.dst_rst_n_i(ext_rst_n_i),
.dst_data_o (s_req_sel),
.dst_valid_o(s_req_valid),
.dst_ready_i(s_req_ready)
);
cdc_2phase #(
.DATA_WIDTH(8)
) u_pll_response_cdc (
.src_clk_i (ext_clk_i),
.src_rst_n_i(ext_rst_n_i),
.src_data_i (s_rsp_data),
.src_valid_i(s_rsp_valid_q),
.src_ready_o(s_rsp_ready),
.dst_clk_i (sys_clk_i),
.dst_rst_n_i(sys_rst_n_i),
.dst_data_o (s_rsp_sys_data),
.dst_valid_o(s_rsp_sys_valid),
.dst_ready_i(pll_ctrl.rsp_ready_o)
);
assign pll_ctrl.rsp_active_sel_i = s_rsp_sys_data[2:0];
assign pll_ctrl.rsp_active_valid_i = s_rsp_sys_data[3];
assign pll_ctrl.rsp_safe_clk_i = s_rsp_sys_data[4];
assign pll_ctrl.rsp_pll_lock_i = s_rsp_sys_data[5];
assign pll_ctrl.rsp_error_i = s_rsp_sys_data[7:6];
assign pll_ctrl.rsp_valid_i = s_rsp_sys_valid;
assign pll_ctrl.capable_i = pll_capable_i;
assign s_req_ready = s_state_q == PLL_IDLE;
assign s_rsp_data = {s_error_q, s_lock_q, s_safe_clk_q, s_active_valid_q, s_active_sel_q};
assign pll_sel_o = s_pll_sel_q;
assign pll_apply_o = s_pll_apply_q;
assign select_ext_clk_o = s_select_ext_clk_q;
always_comb begin
s_state_d = s_state_q;
s_pll_sel_d = s_pll_sel_q;
s_pll_apply_d = (s_state_q == PLL_APPLY) || (s_state_q == PLL_WAIT_LOCK);
s_select_ext_clk_d = s_select_ext_clk_q;
s_lock_count_d = s_lock_count_q;
s_active_sel_d = s_active_sel_q;
s_active_valid_d = s_active_valid_q;
s_safe_clk_d = s_safe_clk_q;
s_lock_d = s_lock_q;
s_error_d = s_error_q;
s_rsp_valid_d = s_rsp_valid_q;
unique case (s_state_q)
PLL_IDLE: begin
if (s_req_valid) begin
s_pll_sel_d = s_req_sel;
s_select_ext_clk_d = 1'b1;
s_error_d = 2'd0;
s_state_d = PLL_SAFE;
end
end
PLL_SAFE: begin
s_select_ext_clk_d = 1'b1;
s_safe_clk_d = 1'b1;
s_lock_d = 1'b0;
if (pll_capable_i) begin
s_state_d = PLL_APPLY;
end else begin
s_active_valid_d = 1'b0;
s_error_d = 2'd1;
s_state_d = PLL_RESPOND;
end
end
PLL_APPLY: begin
s_lock_count_d = '0;
s_state_d = PLL_WAIT_LOCK;
end
PLL_WAIT_LOCK: begin
if (pll_lock_i) begin
s_active_sel_d = s_pll_sel_q;
s_active_valid_d = 1'b1;
s_safe_clk_d = 1'b0;
s_lock_d = 1'b1;
s_error_d = 2'd0;
s_state_d = PLL_SWITCH;
end else if (s_lock_count_q == LOCK_TIMEOUT - 1) begin
s_active_valid_d = 1'b0;
s_safe_clk_d = 1'b1;
s_lock_d = 1'b0;
s_error_d = 2'd2;
s_state_d = PLL_RESPOND;
end else begin
s_lock_count_d = s_lock_count_q + 1'b1;
end
end
PLL_SWITCH: begin
s_select_ext_clk_d = 1'b0;
s_state_d = PLL_RESPOND;
end
PLL_RESPOND: begin
s_rsp_valid_d = 1'b1;
if (s_rsp_valid_q && s_rsp_ready) begin
s_rsp_valid_d = 1'b0;
s_state_d = PLL_IDLE;
end
end
default: begin
s_state_d = PLL_IDLE;
end
endcase
end
dffrc #(3, PLL_IDLE) u_state_dffrc (
ext_clk_i,
ext_rst_n_i,
s_state_d,
s_state_q
);
dffr #(3) u_pll_sel_dffr (
ext_clk_i,
ext_rst_n_i,
s_pll_sel_d,
s_pll_sel_q
);
dffr #(1) u_pll_apply_dffr (
ext_clk_i,
ext_rst_n_i,
s_pll_apply_d,
s_pll_apply_q
);
dffrc #(1, 1'b1) u_select_ext_clk_dffrc (
ext_clk_i,
ext_rst_n_i,
s_select_ext_clk_d,
s_select_ext_clk_q
);
dffr #(16) u_lock_count_dffr (
ext_clk_i,
ext_rst_n_i,
s_lock_count_d,
s_lock_count_q
);
dffr #(3) u_active_sel_dffr (
ext_clk_i,
ext_rst_n_i,
s_active_sel_d,
s_active_sel_q
);
dffr #(1) u_active_valid_dffr (
ext_clk_i,
ext_rst_n_i,
s_active_valid_d,
s_active_valid_q
);
dffrc #(1, 1'b1) u_safe_clk_dffrc (
ext_clk_i,
ext_rst_n_i,
s_safe_clk_d,
s_safe_clk_q
);
dffr #(1) u_lock_dffr (
ext_clk_i,
ext_rst_n_i,
s_lock_d,
s_lock_q
);
dffr #(2) u_error_dffr (
ext_clk_i,
ext_rst_n_i,
s_error_d,
s_error_q
);
dffr #(1) u_rsp_valid_dffr (
ext_clk_i,
ext_rst_n_i,
s_rsp_valid_d,
s_rsp_valid_q
);
endmodule