mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
- 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.
387 lines
11 KiB
Systemverilog
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
|