370 lines
13 KiB
VHDL
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; |