132 lines
5.0 KiB
VHDL
132 lines
5.0 KiB
VHDL
-- 32bit prescaled Timer
|
|
--
|
|
-- Original Author unknown.
|
|
-- Updated for ZPU SoC use: Philip Smart, 2019.
|
|
--
|
|
-- The FreeBSD license
|
|
--
|
|
-- Redistribution and use in source and binary forms, with or without
|
|
-- modification, are permitted provided that the following conditions
|
|
-- are met:
|
|
--
|
|
-- 1. Redistributions of source code must retain the above copyright
|
|
-- notice, this list of conditions and the following disclaimer.
|
|
-- 2. Redistributions in binary form must reproduce the above
|
|
-- copyright notice, this list of conditions and the following
|
|
-- disclaimer in the documentation and/or other materials
|
|
-- provided with the distribution.
|
|
--
|
|
-- THIS SOFTWARE IS PROVIDED BY THE ZPU PROJECT ``AS IS'' AND ANY
|
|
-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
-- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
-- ZPU PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
-- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
--
|
|
-- The views and conclusions contained in the software and documentation
|
|
-- are those of the authors and should not be interpreted as representing
|
|
-- official policies, either expressed or implied, of the ZPU Project.
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use IEEE.numeric_std.ALL;
|
|
|
|
library work;
|
|
|
|
-- Timer controller module
|
|
|
|
entity timer_controller is
|
|
generic(
|
|
prescale : integer := 1; -- Prescale incoming clock
|
|
timers : integer := 0 -- This is a power of 2, so zero means 1 counter, 4 means 16 counters...
|
|
);
|
|
port (
|
|
clk : in std_logic;
|
|
reset : in std_logic; -- active low
|
|
|
|
reg_addr_in : in std_logic_vector(7 downto 0); -- from host CPU
|
|
reg_data_in : in std_logic_vector(31 downto 0);
|
|
reg_rw : in std_logic;
|
|
reg_req : in std_logic;
|
|
|
|
ticks : out std_logic_vector(2**timers-1 downto 0)
|
|
);
|
|
end entity;
|
|
|
|
architecture rtl of timer_controller is
|
|
constant prescale_adj : integer := prescale-1;
|
|
signal prescale_counter : unsigned(15 downto 0);
|
|
signal prescaled_tick : std_logic;
|
|
type timer_counters is array (2**timers-1 downto 0) of unsigned(31 downto 0);
|
|
signal timer_counter : timer_counters;
|
|
signal timer_limit : timer_counters;
|
|
signal timer_enabled : std_logic_vector(2**timers-1 downto 0);
|
|
signal timer_index : unsigned(7 downto 0);
|
|
begin
|
|
|
|
-- Prescaled tick
|
|
process(clk, reset)
|
|
begin
|
|
if reset='0' then
|
|
prescale_counter <=(others => '0');
|
|
elsif rising_edge(clk) then
|
|
prescaled_tick <='0';
|
|
prescale_counter <=prescale_counter-1;
|
|
if prescale_counter=X"00" then
|
|
prescaled_tick <='1';
|
|
prescale_counter <=to_unsigned(prescale_adj,16);
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- The timers proper;
|
|
process(clk,reset)
|
|
begin
|
|
if reset='0' then
|
|
for I in 0 to (2**timers-1) loop
|
|
timer_counter(I) <= (others => '0');
|
|
end loop;
|
|
elsif rising_edge(clk) then
|
|
ticks <= (others => '0');
|
|
if prescaled_tick='1' then
|
|
for I in 0 to (2**timers-1) loop
|
|
if timer_enabled(I)='1' then
|
|
timer_counter(I) <=timer_counter(I)-1;
|
|
if timer_counter(I)=X"00000000" then
|
|
timer_counter(I)<=timer_limit(I);
|
|
ticks(I) <='1';
|
|
end if;
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- Handle CPU access to hardware registers
|
|
process(clk,reset)
|
|
begin
|
|
if reset='0' then
|
|
timer_enabled <= (others => '0');
|
|
elsif rising_edge(clk) then
|
|
if reg_req='1' and reg_rw='0' then -- Write access
|
|
case reg_addr_in is
|
|
when X"00" =>
|
|
timer_enabled <= reg_data_in(2**timers-1 downto 0);
|
|
when X"04" =>
|
|
timer_index <= unsigned(reg_data_in(7 downto 0));
|
|
when X"08" =>
|
|
timer_limit(to_integer(timer_index(timers downto 0)))<= unsigned(reg_data_in(31 downto 0));
|
|
when others =>
|
|
null;
|
|
end case;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
end architecture;
|