library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.spi_pkg.all;
entity spi_clkgen is
generic (
DIVISORS : natural_vector := (2, 4, 6, 8, 16, 32, 64, 128, 256);
DIVISORS_LOG2 : natural := 3
);
port (
clk_i : in std_logic;
rst_in : in std_logic;
start_i : in std_logic;
-- next_data_i : in std_logic;
div_sel_i : in std_logic_vector(DIVISORS_LOG2 - 1 downto 0);
clock_polarity_i : in std_logic;
clock_phase_i : in std_logic;
sck_o : out std_logic;
sck_mask_i : in std_logic;
counter_overflow_o : out std_logic;
sample_data_o : out std_logic;
change_data_o : out std_logic);
end entity spi_clkgen;
architecture a1 of spi_clkgen is
constant MAX : natural := get_max_natural(DIVISORS);
type state_t is (IDLE, CLK_GEN, SCK_GEN);
signal running : std_logic;
signal selected_divisor : natural range 0 to MAX := 1;
signal changing : std_logic;
signal curr_state : state_t;
signal next_state : state_t;
signal curr_counter : integer range 0 to MAX - 1;
signal next_counter : integer range 0 to MAX - 1;
signal curr_sck : std_logic;
signal next_sck : std_logic;
begin -- architecture a1
set_data: process (clk_i) is
begin -- process set_data
if rising_edge(clk_i) then -- rising clock edge
if rst_in = '0' then -- synchronous reset (active low)
curr_sck <= '0';
curr_counter <= 0;
curr_state <= IDLE;
else
curr_sck <= next_sck;
curr_counter <= next_counter;
curr_state <= next_state;
end if;
end if;
end process set_data;
state: process (all) is
begin -- process state
next_state <= curr_state;
running <= '0';
sample_data_o <= '0';
change_data_o <= '0';
case curr_state is
when IDLE =>
if start_i = '1' then
if sck_mask_i = '1' then
next_state <= SCK_GEN;
change_data_o <= not clock_phase_i;
else
next_state <= CLK_GEN;
end if;
end if;
when CLK_GEN =>
running <= '1';
if start_i = '0' then
next_state <= IDLE;
elsif sck_mask_i = '1' then
next_state <= SCK_GEN;
end if;
when SCK_GEN =>
running <= '1';
if changing = '1' then
if curr_sck = clock_phase_i then
sample_data_o <= '1';
else
change_data_o <= '1';
end if;
end if;
if start_i = '0' and ((changing = '1' and curr_sck /= '0') or (curr_sck = '0')) then
next_state <= IDLE;
sample_data_o <= '0';
change_data_o <= '0';
elsif sck_mask_i = '0' and ((changing = '1' and curr_sck /= '0') or (curr_sck = '0')) then
next_state <= CLK_GEN;
sample_data_o <= '0';
change_data_o <= '0';
end if;
when others => null;
end case;
end process state;
selected_divisor <= DIVISORS(to_integer(unsigned(div_sel_i)));
changing <= '1' when curr_counter = 0 and running = '1' and curr_state = SCK_GEN else '0';
next_counter <= selected_divisor - 2 when changing = '1' else
0 when curr_counter = 0 else
curr_counter - 1 when running = '1' else
selected_divisor - 1;
-- sample_data_o <= '1' when curr_sck = clock_phase_i and changing = '1' else '0';
-- change_data_o <= '1' when curr_sck /= clock_phase_i and changing = '1' else
-- '1' when clock_phase_i = '0' and start_i = '1' and running = '0' else
-- -- '1' when next_data_i = '1' else
-- '0';
next_sck <= '0' when curr_state /= SCK_GEN else
not curr_sck when changing = '1'
else curr_sck when running = '1' else
'0';
sck_o <= clock_polarity_i when curr_state /= SCK_GEN else
curr_sck when clock_polarity_i = '0' else
not curr_sck;
counter_overflow_o <= '1' when running = '1' and curr_counter = 0 else '0';
end architecture a1;