2022-04-07 18:46:57 +02:00

370 lines
13 KiB
VHDL

-- altera vhdl_input_version vhdl_2008
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
LIBRARY altera_mf;
use altera_mf.altera_mf_components.all;
entity LCDController is
port(
clk : in std_logic;
rst_n : in std_logic;
-- Avalon slave interface
avalon_slave_address : in std_logic_vector (3 downto 0);
avalon_slave_write : in std_logic;
avalon_slave_writedata : in std_logic_vector(31 downto 0);
avalon_slave_read : in std_logic;
avalon_slave_readdata : out std_logic_vector(31 downto 0);
avalon_slave_waitrequest : out std_logic;
av_irq : out std_logic;
-- Avalon master interface
avalon_master_waitreq : in std_logic;
avalon_master_readdata : in std_logic_vector(31 downto 0);
avalon_master_readdatavalid : in std_logic;
avalon_master_address : out std_logic_vector(31 downto 0);
avalon_master_burstcount : out std_logic_vector(4 downto 0);
avalon_master_read : out std_logic;
-- LT24 conduit interface
lt24_rd_n : out std_logic;
lt24_wr_n : out std_logic;
lt24_rs : out std_logic;
lt24_cs_n : out std_logic;
lt24_data : out std_logic_vector(15 downto 0);
lt24_reset_n : out std_logic;
lt24_lcd_on : out std_logic
);
end LCDController;
architecture comp of LCDController is
-- Type definitions
type State is (S_READY, S_REFRESHING);
type IRQ_CTRL is record
enabled : boolean;
clear_on_refresh : boolean;
irq0_active : std_logic;
end record IRQ_CTRL;
-- Generic constants
constant F_CLK : natural := 50*10**6;
constant ZERO_ADDR : std_logic_vector(31 downto 0) := (others => '0');
constant MAX_H : natural := 240;
constant MAX_W : natural := 320;
constant N_PIXELS : natural := MAX_H * MAX_W;
constant PIXEL_WIDTH : natural := 16;
constant AVM_BURST_LEN : natural := 256;
constant IRQ_CTRL_DEFAULT : IRQ_CTRL := (
enabled => true,
clear_on_refresh => true,
irq0_active => '0'
);
-- Avalon slave programmable interface constants
constant A_WRITEREG : natural := 0;
constant A_WRITEDATA : natural := 1;
constant A_WRITEBASE : natural := 2;
constant A_REFRESH : natural := 3;
constant A_SETENABLED : natural := 4;
constant A_SETHEIGHT : natural := 5;
constant A_SETWIDTH : natural := 6;
constant A_WRITEIRQ : natural := 7;
constant A_SETIRQ : natural := 8;
constant A_CLEARIRQ : natural := 9;
constant A_ISBUSY : natural := 10;
constant LAST_avalon_slave_ADDR : natural := A_ISBUSY;
signal avalon_slave_address_int : integer range 0 to LAST_avalon_slave_ADDR;
-- FIFO configuration parameters
constant FIFO_N_ALMOST_EMPTY : natural := 32;
constant FIFO_WIDTH : natural := 32;
constant FIFO_SIZE : natural := AVM_BURST_LEN + FIFO_N_ALMOST_EMPTY;
constant PIXELS_PER_WORD : natural := FIFO_WIDTH / PIXEL_WIDTH;
constant NWORDS_MAX : natural := N_PIXELS / PIXELS_PER_WORD;
-- FIFO signals
signal fifo_almost_empty : std_logic;
signal fifo_wr_req : std_logic;
signal fifo_data_in, fifo_data_out : std_logic_vector(FIFO_WIDTH - 1 downto 0);
signal fifo_rd_req : std_logic;
signal fifo_empty : std_logic;
-- Inferred registers
signal baseaddr_reg, baseaddr_next : std_logic_vector(31 downto 0);
signal state_reg, state_next : State;
signal lcd_on_reg, lcd_on_next : std_logic := '1';
signal lcd_rst_reg, lcd_rst_next : std_logic := '0';
signal width_reg, width_next : integer range 0 to MAX_W;
signal height_reg, height_next : integer range 0 to MAX_H;
signal irq_ctrl_reg, irq_ctrl_next : IRQ_CTRL;
-- LCD Driver interfacing signals
signal lcd_cmd_en : std_logic;
signal lcd_cmd_dcx : std_logic;
signal lcd_cmd_data : std_logic_vector(7 downto 0);
signal lcd_busy : std_logic;
signal lcd_req : std_logic;
signal lcd_data : std_logic_vector(15 downto 0);
signal lcd_empty : std_logic;
-- LCDController state signals
signal refresh : std_logic;
-- # of words required by the fifo to gather all pixels in an image based on
-- current width and height values
signal av_nwords : std_logic_vector(integer(ceil(log2(real(NWORDS_MAX)))) downto 0);
function writeIRQCtrl(v : in std_logic_vector(2 downto 0)) return IRQ_CTRL is
variable res : IRQ_CTRL;
begin
res.enabled := v(0) = '1';
res.clear_on_refresh := v(1) = '1';
res.irq0_active := v(2);
return res;
end writeIRQCtrl;
function setIRQCtrl(current: in IRQ_CTRL; v : in std_logic_vector(2 downto 0)) return IRQ_CTRL is
variable res : IRQ_CTRL;
begin
res := current;
res.enabled := current.enabled or v(0) = '1';
res.clear_on_refresh := current.clear_on_refresh or v(1) = '1';
res.irq0_active := current.irq0_active or v(2);
return res;
end setIRQCtrl;
function clearIRQCtrl(current: in IRQ_CTRL; v : in std_logic_vector(2 downto 0)) return IRQ_CTRL is
variable res : IRQ_CTRL;
begin
res := current;
res.enabled := current.enabled and not v(0) = '1';
res.clear_on_refresh := current.clear_on_refresh and not v(1) = '1';
res.irq0_active := current.irq0_active and not v(2);
return res;
end clearIRQCtrl;
--Component declaration, required for altera library IP
component scfifo
generic (
almost_empty_value : natural;
lpm_numwords : natural;
lpm_width : natural;
lpm_widthu : natural
);
port(
aclr : in std_logic;
almost_empty : out std_logic;
clock : in std_logic;
data : in std_logic_vector(lpm_width-1 downto 0);
empty : out std_logic;
q : out std_logic_vector(lpm_width-1 downto 0);
rdreq : in std_logic;
sclr : in std_logic;
wrreq : in std_logic
);
end component;
begin
-- ENTITY LCDDriver:
lcddriver_ent : entity work.LCDDriver
generic map (
F_CLK => F_CLK
)
port map (
clk => clk,
rst_n => rst_n,
data_in => lcd_data,
empty_in => lcd_empty,
refresh_in => refresh,
cmd_en_in => lcd_cmd_en,
cmd_dcx_in => lcd_cmd_dcx,
cmd_data_in => lcd_cmd_data,
data_out => lt24_data,
rd_n => lt24_rd_n,
wr_n => lt24_wr_n,
rs => lt24_rs,
cs_n => lt24_cs_n,
lcd_rdreq => lcd_req,
busy => lcd_busy
);
-- ENTITY PixTrans:
pixtrans_ent : entity work.PixTrans
generic map (
MAX_H => MAX_H,
MAX_W => MAX_W
)
port map (
clk => clk,
rst_n => rst_n,
lcd_req => lcd_req,
data => lcd_data,
empty => lcd_empty,
fifo_req => fifo_rd_req,
fifo_q => fifo_data_out,
fifo_empty => fifo_empty,
w => width_reg,
h => height_reg
);
-- ENTITY Avalon master:
avalon_master_ent : entity work.LCDAvalonMaster
generic map (
NWORDS_MAX => NWORDS_MAX
)
port map (
clk => clk,
rst_n => rst_n,
waitreq => avalon_master_waitreq,
readdata => avalon_master_readdata,
readdatavalid => avalon_master_readdatavalid,
address => avalon_master_address,
burstcount => avalon_master_burstcount,
am_read => avalon_master_read,
refresh => refresh,
fifo_almost_empty => fifo_almost_empty,
fifo_data => fifo_data_in,
fifo_wr_req => fifo_wr_req,
baseaddress => baseaddr_reg,
nwords => av_nwords
);
-- ENTITY FIFO
fifo_ent : scfifo
generic map (
lpm_widthu => 9,
lpm_width => FIFO_WIDTH,
lpm_numwords => FIFO_SIZE,
almost_empty_value => FIFO_N_ALMOST_EMPTY
)
port map (
clock => clk,
data => fifo_data_in,
q => fifo_data_out,
wrreq => fifo_wr_req,
rdreq => fifo_rd_req,
empty => fifo_empty,
almost_empty => fifo_almost_empty,
sclr => not rst_n,
aclr => '0'
);
-- PROCESS Avalon slave interface
avalon_slave_address_int <= to_integer(unsigned(avalon_slave_address));
process(all) begin
state_next <= state_reg;
height_next <= height_reg;
width_next <= width_reg;
irq_ctrl_next <= irq_ctrl_reg;
refresh <= '0';
lcd_cmd_en <= '0';
lcd_cmd_dcx <= '0';
baseaddr_next <= baseaddr_reg;
lcd_on_next <= lcd_on_reg;
lcd_rst_next <= lcd_rst_reg;
avalon_slave_readdata <= (others => 'Z');
-- Avalon slave interface
if avalon_slave_write = '1' then
case avalon_slave_address_int is
when A_WRITEREG =>
lcd_cmd_en <= '1';
lcd_cmd_dcx <= '0';
when A_WRITEDATA =>
lcd_cmd_en <= '1';
lcd_cmd_dcx <= '1';
when A_WRITEBASE =>
baseaddr_next <= avalon_slave_writedata;
when A_REFRESH =>
if state_reg = S_READY then
refresh <= '1';
state_next <= S_REFRESHING;
if irq_ctrl_reg.clear_on_refresh then
irq_ctrl_next.irq0_active <= '0';
end if;
end if;
when A_SETENABLED =>
lcd_on_next <= avalon_slave_writedata(0);
lcd_rst_next <= avalon_slave_writedata(1);
when A_SETHEIGHT =>
height_next <= to_integer(unsigned(avalon_slave_writedata));
when A_SETWIDTH =>
width_next <= to_integer(unsigned(avalon_slave_writedata));
when A_WRITEIRQ =>
irq_ctrl_next <= writeIRQCtrl(avalon_slave_writedata(2 downto 0));
when A_SETIRQ =>
irq_ctrl_next <= setIRQCtrl(irq_ctrl_reg, avalon_slave_writedata(2 downto 0));
when A_CLEARIRQ =>
irq_ctrl_next <= clearIRQCtrl(irq_ctrl_reg, avalon_slave_writedata(2 downto 0));
when others => null;
end case;
end if;
if avalon_slave_read = '1' then
case avalon_slave_address_int is
when A_ISBUSY =>
avalon_slave_readdata <= (avalon_slave_readdata'left downto 1 => '0') & lcd_busy;
when others =>
null;
end case;
end if;
-- LCD Controller state handling
if (state_reg = S_REFRESHING) and lcd_busy = '0' then
state_next <= S_READY;
if irq_ctrl_reg.enabled then
irq_ctrl_next.irq0_active <= '1'; -- register that we are now finished
end if;
end if;
end process;
av_irq <= irq_ctrl_reg.irq0_active;
-- LCDController top level signals and functional units
lcd_cmd_data <= avalon_slave_writedata(7 downto 0);
av_nwords <= std_logic_vector(to_unsigned((height_reg * width_reg) / 2, av_nwords'length));
-- Output logic
lt24_reset_n <= not lcd_rst_reg;
lt24_lcd_on <= lcd_on_reg;
avalon_slave_waitrequest <= '1' when (lcd_busy='1' or state_reg /= S_READY) else '0';
-- Clocking process/register inferrence
process(clk) begin
if rising_edge(clk) then
if rst_n = '0' then
baseaddr_reg <= (others => '0');
state_reg <= S_READY;
height_reg <= MAX_H;
width_reg <= MAX_W;
irq_ctrl_reg <= IRQ_CTRL_DEFAULT;
lcd_on_reg <= '1';
else
state_reg <= state_next;
baseaddr_reg <= baseaddr_next;
height_reg <= height_next;
width_reg <= width_next;
irq_ctrl_reg <= irq_ctrl_next;
lcd_on_reg <= lcd_on_next;
lcd_rst_reg <= lcd_rst_next;
end if;
end if;
end process;
end comp;