Friday, February 14, 2014

use VHDL to write a counter with more functions(2)

--Selector[1:0] | Boundary enable | Output [N: 0]
----------------------------------------------------------------
--00              0 or 1            0
--01              0                 Increment count by 7 from Boundary to 255 every half second and stop
--10              0                 Decrement count by 9 from 255 to Boundary every half second and stop
--11              0                 Increment count by 11 from Boundary to 255 then decrement by 15 to  Boundary every half second and stop
--01              1                 Increment count by 8 from 1 to Boundary every half second and stop
--10              1                 Decrement count by 13 from Boundary to 0 every half second and stop
--11              1                 Increment count by 14  from 1 to Boundary then decrement by 6 to  0 every half second and stop

-----------------------------------------------------------------
--clk dividor
-----------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;

entity clk_div is
generic (
    N: integer := 12499999
);
port (
    clk     : in  std_logic;
    rst_n   : in  std_logic;
    clk_out : out std_logic
);
end entity clk_div;

architecture behavioral of clk_div is
signal clk_cnt     : integer range 0 to N;
signal clk_out_tmp : std_logic;
begin
--counter for half second
process(clk, rst_n)
begin
    if(rst_n = '0')then
        clk_cnt <= 0;
        clk_out_tmp <= '0';
    elsif(clk'event and clk = '1')  then
        --when counter reaches to half second,
        --need to reset the counter to 0
        if(clk_cnt = N) then
            clk_cnt <= 0;
            clk_out_tmp <= not (clk_out_tmp);
        else
            clk_cnt <= clk_cnt + 1;
            clk_out_tmp <= clk_out_tmp;
        end if;
    end if;
end process;

clk_out <= clk_out_tmp;

end behavioral;

-----------------------------------------------------------------
--kernel counter
-----------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;

entity kernel_counter is
generic (
    CNT_LEN : integer := 8
);
port (
    clk_in        : in  std_logic;
    rst_n         : in  std_logic;
    cnt_restart   : in  std_logic;
    cnt_type      : in  std_logic;
    cnt_inc       : in  std_logic_vector(CNT_LEN-1 downto 0);
    cnt_st_data   : in  std_logic_vector(CNT_LEN-1 downto 0);
    cnt_end_data  : in  std_logic_vector(CNT_LEN-1 downto 0);
    cnt_out       : out std_logic_vector(CNT_LEN-1 downto 0);
    cnt_done      : out std_logic
);
end entity kernel_counter;

architecture behavioral of kernel_counter is
signal cnt_end_data_new : std_logic_vector(CNT_LEN-1 downto 0);
signal cnt              : std_logic_vector(CNT_LEN-1 downto 0);
signal cnt_end          : std_logic_vector(CNT_LEN-1 downto 0);
type   cnt_status is (IDLE, LOAD, INC, DEC, DONE);
signal cnt_stat         : cnt_status;
signal cnt_stat_nxt     : cnt_status;
--signal cnt_type_ff1     : std_logic;

begin

process(clk_in, rst_n)
begin
    if(rst_n = '0') then
        cnt_end_data_new <= x"00";
    elsif(rising_edge(clk_in)) then
        if(cnt_stat = LOAD) then
            cnt_end_data_new <= cnt_end_data;
        end if;
    end if;
end process;

--when counter increase or decrease, get the last data that the counter can reach when counting
--process(cnt_restart, cnt_type_ff1, cnt_end_data, cnt_inc)
process(clk_in, rst_n)
begin
    if(rst_n = '0') then
        cnt_end <= x"00";
    elsif(rising_edge(clk_in)) then
        if(cnt_stat = LOAD) then
            if(cnt_type = '0') then
                cnt_end <= cnt_end_data - cnt_inc - cnt_inc;
            else
                cnt_end <= cnt_end_data + cnt_inc + cnt_inc;
            end if;
        end if;
    end if;
end process;

--counter state machine
--counter to count how many half seconds
process(cnt_stat, cnt_type, cnt, cnt_end, cnt_restart)
begin
    case cnt_stat is
        when IDLE =>
            if(cnt_restart = '1') then
                cnt_stat_nxt <= LOAD;
            else
                cnt_stat_nxt <= IDLE;
            end if;
        when LOAD =>
            if(cnt_restart = '1') then
                cnt_stat_nxt <= LOAD;
            elsif(cnt_type = '0') then
                cnt_stat_nxt <= INC;
            else
                cnt_stat_nxt <= DEC;
            end if;
        when INC =>
            if(cnt_restart = '1') then
                cnt_stat_nxt <= LOAD;
            elsif(cnt >= cnt_end) then
                cnt_stat_nxt <= DONE;
            else
                cnt_stat_nxt <= INC;
            end if;
        when DEC =>
            if(cnt_restart = '1') then
                cnt_stat_nxt <= LOAD;
            elsif(cnt <= cnt_end) then
                cnt_stat_nxt <= DONE;
            else
                cnt_stat_nxt <= DEC;
            end if;
        when DONE =>
            if(cnt_restart = '1') then
                cnt_stat_nxt <= LOAD;
            else
                cnt_stat_nxt <= IDLE;
            end if;
        when others =>
            cnt_stat_nxt <= cnt_stat;
    end case;
end process;

process(clk_in, rst_n)
begin
    if(rst_n = '0') then
        cnt_stat <= IDLE;
    elsif(rising_edge(clk_in)) then
        cnt_stat <= cnt_stat_nxt;
    end if;
end process;

--cnt done
process(clk_in, rst_n)
begin
    if(rst_n = '0') then
        cnt_done <= '0';
    elsif(rising_edge(clk_in)) then
        if(cnt_stat = DONE) then
            cnt_done <= '1';
        else
            cnt_done <= '0';
        end if;
    end if;
end process;

--one half second count
process(clk_in, rst_n)
begin
    if(rst_n = '0') then
        cnt <= (others => '0');
    elsif(rising_edge(clk_in)) then
        if(cnt_stat = LOAD) then
            cnt <= cnt_st_data;
        elsif(cnt_stat = INC) then
            cnt <= cnt + cnt_inc;
        elsif(cnt_stat = DEC) then
            cnt <= cnt - cnt_inc;
        elsif(cnt_stat = DONE) then
            cnt <= cnt_end_data_new;
        end if;
    end if;
end process;

--send data out
cnt_out <= cnt;

end behavioral;

-----------------------------------------------------------------
--counter
-----------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;

entity counter is
port (
    clk_in        : in  std_logic;
    rst_n         : in  std_logic;
    selector      : in  std_logic_vector(1 downto 0);
    boundary_en   : in  std_logic;
    boundary_data : in  std_logic_vector(7 downto 0);
    data          : out std_logic_vector(7 downto 0)
);
end entity counter;

architecture behavioral of counter is
--signal declaration in architecture
signal selector_ff1            : std_logic_vector(1 downto 0);
signal selector_ff2            : std_logic_vector(1 downto 0);
signal selector_ff3            : std_logic_vector(1 downto 0);
signal selector_trig           : std_logic_vector(3 downto 0);
signal selector_trig_for_rvs   : std_logic_vector(3 downto 0);
signal boundary_en_ff1         : std_logic;
signal boundary_en_ff2         : std_logic;
signal boundary_en_ff3         : std_logic;
signal boundary_en_trig        : std_logic;
signal boundary_data_ff1       : std_logic_vector(7 downto 0);
signal boundary_data_ff2       : std_logic_vector(7 downto 0);
signal boundary_data_ff3       : std_logic_vector(7 downto 0);
signal boundary_data_trig      : std_logic;
signal cnt_restart             : std_logic;
signal reverse                 : std_logic;
signal reverse_ff1             : std_logic;
signal reverse_trig            : std_logic;
signal reverse_nxt             : std_logic;
signal cnt_type                : std_logic;
signal cnt_inc                 : std_logic_vector(7 downto 0);
signal cnt_st_data             : std_logic_vector(7 downto 0);
signal cnt_end_data            : std_logic_vector(7 downto 0);
signal cnt_out                 : std_logic_vector(7 downto 0);
signal cnt_inc_end             : std_logic_vector(7 downto 0);
signal cnt_en_trig             : std_logic;
signal cnt_dis_trig            : std_logic;
signal cnt_type_1_trig         : std_logic;
signal cnt_done                : std_logic;

constant select01_boundary0_inc : std_logic_vector(7 downto 0) := x"07" ;
constant select01_boundary1_inc : std_logic_vector(7 downto 0) := x"08" ;
constant select10_boundary0_dec : std_logic_vector(7 downto 0) := x"09" ;
constant select10_boundary1_dec : std_logic_vector(7 downto 0) := x"0d" ;
constant select11_boundary0_inc : std_logic_vector(7 downto 0) := x"0b" ;
constant select11_boundary1_inc : std_logic_vector(7 downto 0) := x"0e" ;
constant select11_boundary0_dec : std_logic_vector(7 downto 0) := x"0f" ;
constant select11_boundary1_dec : std_logic_vector(7 downto 0) := x"06" ;

--function to convert boolean to std_logic
function to_stdulogic(V: Boolean) return std_ulogic is
begin
    if V then
        return '1';
    else
        return '0';
    end if;
end to_stdulogic;

--kernel counter
component kernel_counter is
generic (
    CNT_LEN : integer := 8
);
port (
    clk_in        : in  std_logic;
    rst_n         : in  std_logic;
    cnt_restart   : in  std_logic;
    cnt_type      : in  std_logic;
    cnt_inc       : in  std_logic_vector(CNT_LEN-1 downto 0);
    cnt_st_data   : in  std_logic_vector(CNT_LEN-1 downto 0);
    cnt_end_data  : in  std_logic_vector(CNT_LEN-1 downto 0);
    cnt_out       : out std_logic_vector(CNT_LEN-1 downto 0);
    cnt_done      : out std_logic
);
end component;

--begin of architecture
begin

--instance kernel counter
u_kernel_counter: kernel_counter generic map (
    CNT_LEN => 8
) port map (
    clk_in        => clk_in       ,
    rst_n         => rst_n        ,
    cnt_restart   => cnt_restart  ,
    cnt_type      => cnt_type     ,
    cnt_inc       => cnt_inc      ,
    cnt_st_data   => cnt_st_data  ,
    cnt_end_data  => cnt_end_data ,
    cnt_out       => cnt_out      ,
    cnt_done      => cnt_done
);

--solve metastability issue, so 2 flip-flops are needed
--sample the change of selector, so 3 flip-flops are needed
--so add 3 flip-flops
process(clk_in, rst_n)
begin
    if(rst_n = '0') then
        selector_ff1      <= b"00" ;
        selector_ff2      <= b"00" ;
        selector_ff3      <= b"00" ;
        boundary_en_ff1   <= '0'   ;
        boundary_en_ff2   <= '0'   ;
        boundary_en_ff3   <= '0'   ;
        boundary_data_ff1 <= x"00" ;
        boundary_data_ff2 <= x"00" ;
        boundary_data_ff3 <= x"00" ;
    elsif(clk_in'event and clk_in = '1') then
        selector_ff1      <= selector          ;
        selector_ff2      <= selector_ff1      ;
        selector_ff3      <= selector_ff2      ;
        boundary_en_ff1   <= boundary_en       ;
        boundary_en_ff2   <= boundary_en_ff1   ;
        boundary_en_ff3   <= boundary_en_ff2   ;
        boundary_data_ff1 <= boundary_data     ;
        boundary_data_ff2 <= boundary_data_ff1 ;
        boundary_data_ff3 <= boundary_data_ff2 ;
    end if;
end process;

boundary_data_trig <= to_stdulogic(boundary_data_ff2 /= boundary_data_ff3);
boundary_en_trig <= to_stdulogic(boundary_en_ff2 /= boundary_en_ff3);

--sample the change of selector
--use each bit to represent one selector
selector_trig_for_rvs(0) <= to_stdulogic(
                        ((selector_ff2 = b"00") and (selector_ff3 /= b"00")) or
                        (((boundary_data_trig = '1') or (boundary_en_trig = '1')) and (selector_ff2 = b"00"))
                    );
selector_trig_for_rvs(1) <= to_stdulogic(
                        ((selector_ff2 = b"01") and (selector_ff3 /= b"01")) or
                        (((boundary_data_trig = '1') or (boundary_en_trig = '1')) and (selector_ff2 = b"01"))
                    );
selector_trig_for_rvs(2) <= to_stdulogic(
                        ((selector_ff2 = b"10") and (selector_ff3 /= b"10")) or
                        (((boundary_data_trig = '1') or (boundary_en_trig = '1')) and (selector_ff2 = b"10"))
                    );
selector_trig_for_rvs(3) <= to_stdulogic(
                        ((selector_ff2 = b"11") and (selector_ff3 /= b"11")) or
                        (((boundary_data_trig = '1') or (boundary_en_trig = '1')) and (selector_ff2 = b"11"))
                    );

selector_trig <= to_stdulogic(
                        ((selector_ff2 = b"11") and (selector_ff3 /= b"11")) or
                        (((boundary_data_trig = '1') or (boundary_en_trig = '1') or (reverse_trig = '1')) and (selector_ff2 = b"11"))
                    ) & selector_trig_for_rvs(2) & selector_trig_for_rvs(1) & selector_trig_for_rvs(0);

--when selector is change, calculate the next data displayed on screen
process(clk_in, rst_n)
begin
    if(rst_n = '0') then
        cnt_st_data <= x"00";
    elsif(rising_edge(clk_in)) then
        case selector_trig is
            when b"0001" =>
                cnt_st_data <= x"00";
            when b"0010" =>
                if(boundary_en_ff2 = '0') then
                    cnt_st_data <= boundary_data_ff2;
                else
                    cnt_st_data <= x"01";
                end if;
            when b"0100" =>
                if(boundary_en_ff2 = '0') then
                    cnt_st_data <= x"ff";
                else
                    cnt_st_data <= boundary_data_ff2;
                end if;
            when b"1000" =>
                if(boundary_en_ff2 = '0') then
                    if(reverse = '1') then
                        cnt_st_data <= x"ff" - select11_boundary0_dec;
                    else
                        cnt_st_data <= boundary_data_ff2;
                    end if;
                else
                    if(reverse = '1') then
                        cnt_st_data <= boundary_data_ff2 - select11_boundary1_dec;
                    else
                        cnt_st_data <= x"01";
                    end if;
                end if;
            when others =>
                cnt_st_data <= cnt_st_data;
        end case;
    end if;
end process;

--when counter cannot counter but has not reached the last data, calculate the last data to display
process(clk_in, rst_n)
begin
    if(rst_n = '0') then
        cnt_end_data <= x"00";
    elsif(rising_edge(clk_in)) then
        case selector_trig is
            when b"0001" =>
                cnt_end_data <= x"00";
            when b"0010" =>
                if(boundary_en_ff2 = '0') then
                    cnt_end_data <= x"ff";
                else
                    cnt_end_data <= boundary_data_ff2;
                end if;
            when b"0100" =>
                if(boundary_en_ff2 = '0') then
                    cnt_end_data <= boundary_data_ff2;
                else
                    cnt_end_data <= x"00";
                end if;
            when b"1000" =>
                if(boundary_en_ff2 = '0') then
                    if(reverse = '1') then
                        cnt_end_data <= boundary_data_ff2;
                    else
                        cnt_end_data <= x"ff";
                    end if;
                else
                    if(reverse = '1') then
                        cnt_end_data <= x"00";
                    else
                        cnt_end_data <= boundary_data_ff2;
                    end if;
                end if;
            when others =>
                cnt_end_data <= cnt_end_data;
        end case;
    end if;
end process;

--counter increment or decrement for each clock
process(clk_in, rst_n)
begin
    if(rst_n = '0') then
        cnt_inc <= x"00";
    elsif(rising_edge(clk_in)) then
        case selector_trig is
            when b"0001" =>
                cnt_inc  <= x"00";
            when b"0010" =>
                if(boundary_en_ff2 = '0') then
                    cnt_inc <= select01_boundary0_inc;
                else
                    cnt_inc <= select01_boundary1_inc;
                end if;
            when b"0100" =>
                if(boundary_en_ff2 = '0') then
                    cnt_inc <= select10_boundary0_dec;
                else
                    cnt_inc <= select10_boundary1_dec;
                end if;
            when b"1000" =>
                if(boundary_en_ff2 = '0') then
                    if(reverse = '1') then
                        cnt_inc <= select11_boundary0_dec;
                    else
                        cnt_inc <= select11_boundary0_inc;
                    end if;
                else
                    if(reverse = '1') then
                        cnt_inc <= select11_boundary1_dec;
                    else
                        cnt_inc <= select11_boundary1_inc;
                    end if;
                end if;
            when others =>
                cnt_inc  <= cnt_inc;
        end case;
    end if;
end process;

--when counter increase in mode 3, get the last data that the counter can reach when counting
process(clk_in, rst_n)
begin
    if(rst_n = '0') then
        cnt_inc_end <= x"00";
    elsif(rising_edge(clk_in)) then
        case selector_trig is
            when b"1000" =>
                if(boundary_en_ff2 = '0') then
                    cnt_inc_end <= x"ff" - select11_boundary0_inc;
                else
                    cnt_inc_end <= boundary_data_ff2 - select11_boundary1_inc;
                end if;
            when others =>
                cnt_inc_end <= cnt_inc_end;
        end case;
    end if;
end process;

--signal to control whether it is an increment or decrement
cnt_type_1_trig <= to_stdulogic((selector_ff2 = "11" and (reverse_trig = '1' or reverse = '1')) or (selector_ff2 = "10"));
--process(cnt_type_1_trig)
--begin
--    if(cnt_type_1_trig = '1') then
--        cnt_type <= '1';
--    else
--        cnt_type <= '0';
--    end if;
--end process;
process(clk_in, rst_n)
begin
    if(rst_n = '0') then
        cnt_type <= '0';
    elsif(clk_in'event and clk_in = '1') then
        if(cnt_type_1_trig = '1') then
            cnt_type <= '1';
        else
            cnt_type <= '0';
        end if;
    end if;
end process;

--when in mode3, reverse will be changed to 1 when counter reach to the last data when increasing
process(clk_in, rst_n)
begin
    if(rst_n = '0') then
        reverse     <= '0';
    elsif(clk_in'event and clk_in = '1') then
        if(selector_trig_for_rvs /= x"0") then
            reverse <= '0';
        elsif(selector_ff2 = b"11") then
            if(reverse = '0') then
                if(cnt_out >= cnt_inc_end) then
                    reverse <= '1';
                else
                    reverse <= reverse;
                end if;
            else
                reverse <= reverse;
            end if;
        else
            reverse <= '0';
        end if;
    end if;
end process;

--delay reverse one cycle
process(clk_in, rst_n)
begin
    if(rst_n = '0') then
        reverse_ff1 <= '0';
    elsif(clk_in'event and clk_in = '1') then
        reverse_ff1 <= reverse;
    end if;
end process;

--find a reverse change in mode3
reverse_trig <= reverse and (not reverse_ff1);
--signal to trigger kernel counter to restart
process(clk_in, rst_n)
begin
    if(rst_n = '0') then
        cnt_restart  <= '0';
    elsif(clk_in'event and clk_in = '1') then
        cnt_restart  <= selector_trig(0) or selector_trig(1) or selector_trig(2) or selector_trig(3);
    end if;
end process;

--send the counter data out
data <= cnt_out;

end behavioral;
--end of architecture

-----------------------------------------------------------------
--convert to segment
-----------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;

entity hex2segment is
generic (
    M: integer := 7
);
port (
    hex     : in std_logic_vector(3 downto 0);
    segment : out std_logic_vector(M-1 downto 0)
);
end entity hex2segment;

architecture behavioral of hex2segment is

begin

SEGMENT_WITHOUT_DP: if M=7 generate
process(hex)
begin
    case hex is
        --decode from hex to segment
        when x"0" => segment <= b"1000000";
        when x"1" => segment <= b"1111001";
        when x"2" => segment <= b"0100100";
        when x"3" => segment <= b"0110000";
        when x"4" => segment <= b"0011001";
        when x"5" => segment <= b"0010010";
        when x"6" => segment <= b"0000010";
        when x"7" => segment <= b"1111000";
        when x"8" => segment <= b"0000000";
        when x"9" => segment <= b"0010000";
        when x"a" => segment <= b"0001000";
        when x"b" => segment <= b"0000011";
        when x"c" => segment <= b"1000110";
        when x"d" => segment <= b"0100001";
        when x"e" => segment <= b"0000110";
        when x"f" => segment <= b"0001110";
        --when in selector 1, counter should add 1 after each half second
        --for other unknown selectors, set counter to 0
        when others => segment <= b"1000000";
    end case;
end process;
end generate SEGMENT_WITHOUT_DP;

SEGMENT_WITH_DP: if M=8 generate
process(hex)
begin
    case hex is
        --decode from hex to segment
        when x"0" => segment <= b"11000000";
        when x"1" => segment <= b"11111001";
        when x"2" => segment <= b"10100100";
        when x"3" => segment <= b"10110000";
        when x"4" => segment <= b"10011001";
        when x"5" => segment <= b"10010010";
        when x"6" => segment <= b"10000010";
        when x"7" => segment <= b"11111000";
        when x"8" => segment <= b"10000000";
        when x"9" => segment <= b"10010000";
        when x"a" => segment <= b"10001000";
        when x"b" => segment <= b"10000011";
        when x"c" => segment <= b"11000110";
        when x"d" => segment <= b"10100001";
        when x"e" => segment <= b"10000110";
        when x"f" => segment <= b"10001110";
        --when in selector 1, counter should add 1 after each half second
        --for other unknown selectors, set counter to 0
        when others => segment <= b"11000000";
    end case;
end process;

end generate SEGMENT_WITH_DP;

end behavioral;

-----------------------------------------------------------------
--top
-----------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;

entity top is
generic (
    M: integer := 7;
    N: integer := 12499999
);
port (
    clk           : in  std_logic;
    rst_n         : in  std_logic;
    selector      : in  std_logic_vector(1 downto 0);
    boundary_en   : in  std_logic;
    boundary_data : in  std_logic_vector(7 downto 0);
    data          : out std_logic_vector(7 downto 0);
    segment0      : out std_logic_vector(M-1 downto 0);
    segment1      : out std_logic_vector(M-1 downto 0)
);
end entity top;

architecture behavioral of top is
--signal declaration in architecture
signal clk_out  : std_logic;
signal data_tmp : std_logic_vector(7 downto 0);
signal hex      : std_logic_vector(11 downto 0);

component clk_div is
generic (
    N: integer := 12499999
);
port (
    clk     : in  std_logic;
    rst_n   : in  std_logic;
    clk_out : out std_logic
);
end component;

component counter is
port (
    clk_in        : in  std_logic;
    rst_n         : in  std_logic;
    selector      : in  std_logic_vector(1 downto 0);
    boundary_en   : in  std_logic;
    boundary_data : in  std_logic_vector(7 downto 0);
    data          : out std_logic_vector(7 downto 0)
);
end component;

component hex2segment is
generic (
    M: integer := 7
);
port (
    hex     : in std_logic_vector(3 downto 0);
    segment : out std_logic_vector(M-1 downto 0)
);
end component;

--begin of architecture
begin

--instance clock dividor
u_clk_div: clk_div generic map (
    N => N
) PORT MAP (
    clk     => clk     ,
    rst_n   => rst_n   ,
    clk_out => clk_out
);

--instance counter
u_counter: counter PORT MAP (
    clk_in        => clk_out       ,
    rst_n         => rst_n         ,
    selector      => selector      ,
    boundary_en   => boundary_en   ,
    boundary_data => boundary_data ,
    data          => data_tmp    
);

data <= data_tmp;

--instance segment0
u_hex2segment_0: hex2segment generic map (
    M => M
) PORT MAP (
    hex     => data_tmp(3 downto 0),
    segment => segment0
);

--instance segment1
u_hex2segment_1: hex2segment generic map (
    M => M
) PORT MAP (
    hex     => data_tmp(7 downto 4),
    segment => segment1
);

end behavioral;
--end of architecture

No comments:

Post a Comment