SummerCart64/fw/rtl/sd/sd_clk.v
2021-02-06 19:35:50 +01:00

108 lines
2.5 KiB
Verilog

module sd_clk (
input i_clk,
input i_reset,
output reg o_sd_clk,
input [1:0] i_sd_clk_config,
output reg o_sd_clk_strobe_rising,
output reg o_sd_clk_strobe_falling
);
// Clock configuration values
localparam [1:0] SD_CLK_CONFIG_STOP = 2'd0;
localparam [1:0] SD_CLK_CONFIG_DIV_256 = 2'd1;
localparam [1:0] SD_CLK_CONFIG_DIV_4 = 2'd2;
localparam [1:0] SD_CLK_CONFIG_DIV_2 = 2'd3;
// Clock configuration change detection
reg [1:0] r_prev_sd_clk_config;
wire w_sd_clk_config_changed = r_prev_sd_clk_config != i_sd_clk_config;
always @(posedge i_clk) begin
if (i_reset) begin
r_prev_sd_clk_config <= SD_CLK_CONFIG_STOP;
end else begin
r_prev_sd_clk_config <= i_sd_clk_config;
end
end
// Clock divider
reg [7:0] r_sd_clk_counter;
always @(posedge i_clk) begin
if (i_reset || w_sd_clk_config_changed) begin
r_sd_clk_counter <= 8'd0;
end else begin
r_sd_clk_counter <= r_sd_clk_counter + 1'd1;
end
end
// Clock divider selector
reg r_selected_sd_clk;
always @(*) begin
case (r_prev_sd_clk_config)
SD_CLK_CONFIG_STOP:
r_selected_sd_clk = 1'b0;
SD_CLK_CONFIG_DIV_256:
r_selected_sd_clk = r_sd_clk_counter[7];
SD_CLK_CONFIG_DIV_4:
r_selected_sd_clk = r_sd_clk_counter[1];
SD_CLK_CONFIG_DIV_2:
r_selected_sd_clk = r_sd_clk_counter[0];
endcase
end
// Clock strobe generation
reg r_prev_selected_sd_clk;
always @(posedge i_clk) begin
o_sd_clk_strobe_rising <= 1'b0;
o_sd_clk_strobe_falling <= 1'b0;
if (i_reset) begin
r_prev_selected_sd_clk <= 1'b0;
end else begin
r_prev_selected_sd_clk <= r_selected_sd_clk;
if (!r_prev_selected_sd_clk && r_selected_sd_clk) begin
o_sd_clk_strobe_rising <= 1'b1;
end
if (r_prev_selected_sd_clk && !r_selected_sd_clk) begin
o_sd_clk_strobe_falling <= 1'b1;
end
end
end
// SD clock generation
always @(posedge i_clk) begin
if (i_reset) begin
o_sd_clk <= 1'b0;
end else begin
if (o_sd_clk_strobe_rising) begin
o_sd_clk <= 1'b1;
end
if (o_sd_clk_strobe_falling) begin
o_sd_clk <= 1'b0;
end
end
end
endmodule