Buenas, os pongo el codigo VHDL del componente que controla una matriz 2D de LEDs, resumiendo es un componente con puertos para conectarse a la CPU NiosII usando el bus Avalon (Addr,DataRd,DataWR,Wr,Rd,CS,Clk ,nReset) y los puertos de salida de control de la matriz 2D LED ( LedRowPut,LedColOut). Instanciando varios componentes de este tipo podremos controlar la matriz 3D ( cubo).
El problema es que consume muchos recursos de la FPGA ya que la matriz la sintetiza con registros, estoy viendo como usar la memoria interna que llevan los Cyclone de Altera para reducir el uso de registros al minimo.
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
USE ieee.std_logic_signed.all;
--USE ieee.numeric_std.all;
---------------------------------------------------
--------------------------------------------------
-- LED 2D Matrix driver
-- COL : num. columnas ( 5 cols.).
-- ROW : num. filas ( 5 red + 5 green = 10 rows. )
-- Freq refresco : 100 Hz ( 10 ms )
-- Valores LED : 64
-- Freq. PWM : 64 * ROW @ 10 ms -> ROW(10) => 64000 hz.
--------------------------------------------------
--------------------------------------------------
ENTITY shifter_LED IS
GENERIC(
COL_NUMBER : in natural := 8; -- Max 16
ROW_NUMBER : in natural := 16; -- Max 16
CLK_DIVIDER : in natural := 780 -- 50 Mhz / 780 -> 64000 Hz
);
PORT (
-- Avalon interface
Clk : IN STD_LOGIC;
nReset : IN STD_LOGIC;
CS : IN STD_LOGIC;
Addr : IN STD_LOGIC_VECTOR(1 downto 0);
Wr : IN STD_LOGIC;
DataWr : IN STD_LOGIC_VECTOR(15 downto 0);
Rd : IN STD_LOGIC;
DataRd : OUT STD_LOGIC_VECTOR(15 downto 0);
--MatrixPWMClk : IN std_logic;
-- LED Outputs ( 5 x 5 x 2 colors)
LedRowOut : OUT STD_LOGIC_VECTOR(ROW_NUMBER-1 downto 0);
LedColOut : OUT STD_LOGIC_VECTOR(COL_NUMBER-1 downto 0)
) ;
END shifter_LED;
ARCHITECTURE rtl OF shifter_LED IS
CONSTANT COL_BITS : integer := 3; -- 2^COL_BITS <= COL_NUMBER
CONSTANT ROW_BITS : integer := 4; -- 2^ROW_BITS <= ROW_NUMBER
-- Matrix types
subtype t_LED_value is integer range 0 to 127;
CONSTANT VAL_BITS : integer := 7; -- 2^VAL_BITS < t_LED_value'high
type t_LED_matrix is array (0 to ROW_NUMBER-1,0 to COL_NUMBER-1) of t_LED_value;
-- LED Matrix
signal matrix : t_LED_matrix;
-- Registers
signal control_reg: STD_LOGIC_VECTOR ( 7 DOWNTO 0);
--signal data_reg : STD_LOGIC_VECTOR (15 DOWNTO 0);
signal addr_reg : STD_LOGIC_VECTOR (15 DOWNTO 0);
signal MatrixPWMClk : std_logic;
SIGNAL led_row_out: STD_LOGIC_VECTOR (ROW_NUMBER-1 DOWNTO 0);
SIGNAL scanner : UNSIGNED(COL_NUMBER-1 DOWNTO 0);
BEGIN
LedRowOut <= led_row_out;
LedColOut <= STD_LOGIC_VECTOR(scanner);
process (clk,nReset)
variable row : integer range 0 to ROW_NUMBER-1;
variable col : integer range 0 to COL_NUMBER-1;
begin
if (nReset = '0') then
matrix <= (others => (others => 0));
control_reg <= (others => '0');
col := 0;
row := 0;
elsif rising_edge(Clk) then
if CS = '1' and Wr = '1' then
case Addr is
-- Control register
when "00" =>
control_reg <= DataWr(7 DOWNTO 0);
-- Address register
when "01" =>
addr_reg <= DataWr;
-- WARN: No hay chequeo de rangos...
col := conv_integer( unsigned(addr_reg ( 8+COL_BITS-1 DOWNTO 8 )));
row := conv_integer( unsigned(addr_reg ( ROW_BITS-1 DOWNTO 0 )));
-- Data register
when "10" =>
-- WARN: No hay chequeo de rangos...
matrix(row,col) <= conv_integer( unsigned(DataWr( VAL_BITS-1 DOWNTO 0 )));
-- Auto-incremento Row->Col
if (row = ROW_NUMBER-1)
then
row := 0;
if (col = COL_NUMBER-1)
then
col := 0;
else
col := col + 1;
end if;
else
row := row + 1;
end if;
addr_reg( 7 DOWNTO 0 ) <= STD_LOGIC_VECTOR(conv_unsigned(row,8));
addr_reg( 15 DOWNTO 8 ) <= STD_LOGIC_VECTOR(conv_unsigned(col,8));
when others => null;
end case;
end if;
end if;
end process;
-- Generate internal Clock
process_Clock:
process(Clk, nReset)
variable cntClock : integer range 0 to CLK_DIVIDER+1;
begin
if (nReset = '0') then
MatrixPWMClk <= '0';
cntClock := 0;
elsif rising_edge(Clk) then
if(cntClock > CLK_DIVIDER) then
MatrixPWMClk <= not MatrixPWMClk;
cntClock := 0;
else
cntClock := cntClock +1 ;
end if;
end if;
end process process_Clock;
-- Shift data
process_GeneratePWM:
process(MatrixPWMClk, nReset)
variable cntCol : integer range 0 to COL_NUMBER := 0;
variable valPWM : t_LED_value := 0;
begin
if (nReset = '0') then
led_row_out <= (others => '0');
scanner <= conv_unsigned(1,COL_NUMBER);
cntCol := 0;
valPWM := 1;
elsif rising_edge(MatrixPWMClk) then
-- Incrementamos valor del PWM
-- Si llegamos al maximo reiniciamos
if (valPWM = t_LED_value'high)
then
valPWM := 1;
-- Siguiente columna
cntCol := cntCol +1;
if (cntCol < COL_NUMBER) then
scanner(COL_NUMBER-1 downto 1) <= scanner(COL_NUMBER-2 downto 0);
scanner(0) <= scanner(COL_NUMBER-1);
else
scanner <= conv_unsigned(1,COL_NUMBER);
cntCol := 0;
end if;
else
valPWM := valPWM + 1;
end if;
-- Activamos salida LED solo si el valor es mayor que valPWM actual.
for row in 0 to ROW_NUMBER-1
loop
if ( matrix(row,cntCol) >= valPWM)
then
led_row_out(row) <= '1';
else
led_row_out(row) <= '0';
end if;
end loop;
end if;
end process process_GeneratePWM;
-- Asynchronous Read
process_Read:
process(Addr,control_reg,addr_reg,CS,Rd,matrix)
--variable row : integer range 0 to ROW_NUMBER-1;
--variable col : integer range 0 to COL_NUMBER-1;
begin
--if CS = '1' and Rd = '1' then
case Addr is
when "00" => DataRd <= "00000000" & control_reg;
when "01" => DataRd <= addr_reg;
--when "10" =>
-- WARN: No hay chequeo de rangos...
--col := conv_integer( unsigned(addr_reg ( 8+COL_BITS-1 DOWNTO 8 )));
--row := conv_integer( unsigned(addr_reg ( ROW_BITS-1 DOWNTO 0 )));
--DataRd <= STD_LOGIC_VECTOR(conv_unsigned(matrix(row,col),16));
when others => DataRd <= (others => '1');
end case;
--end if;
end process process_Read ;
END rtl;