Прерывания в Nios II Altera
Прерывания в Nios II Altera, в частности внешние, совсем не похожи на прерывания современного микропроцессора или микроконтрлера. Не похожи в первую очередь потому, что в простейшем варианте любое прерывание - это переход по одному и тому же вектору. Далее программно вычисляется флаг перехода и осуществляется вход в обработчик. Все это занимает огромное количество времени доходящее до 1000 тактов.
Выход из ситуации - применение ядра аппаратного контроллера прерываний которое входит в библиотеку Nios II. Но это уже тема другого поста. В этом посте о том как создать простейший модуль с внешним прерыванием и простейший софт с его обработчиком. Все построено на базе все той же DE0 Board.
Итак для того, чтобы создать модуль с внешним прерыванием необходимо в SOPC создать новый компонент Avalon-MM Slave With Interupt, сгенерровать HDL файл а затем в него добавить пользовательские сигналы. В результате получится компонент который в SOPC подключается с одной стороны к шине Avalon-MM Slave, а с другой к внутренним и внешним пользовательским сигналам FPGA. Последовательно как это делается.
В SOPC File->New Component затем Templates->Add Typical Avalon-MM Slave With Interupt
Далее небходимо создать HDL шаблон компонента и сохранить файл под любым нужным именем. Компонент пока можно не сохранять поскольку HDL файл компонента необходимо еще пополнить пользовательскими сигналами со стороны FPGA.
Теперь файл шаблона можно откорректировать добавив в него пользовательские сигналы типа Exports, примерно так:
--------------------------------------------------------------------
-- Project : ramtest
--------------------------------------------------------------------
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 ramtest is
port (
-- Avalon Slave
clk : in std_logic := '0'; -- clock.clk
reset_n : in std_logic := '0'; -- .reset_n
avs_s0_address : in std_logic_vector(1 downto 0) := (others => '0'); -- s0.address
avs_s0_read_n : in std_logic := '0'; -- .read_n
avs_s0_readdata : out std_logic_vector(15 downto 0); -- .readdata
avs_s0_write_n : in std_logic := '0'; -- .write_n
avs_s0_writedata : in std_logic_vector(15 downto 0) := (others => '0'); -- .writedata
avs_s0_byteenable : in std_logic_vector(1 downto 0) := (others => '0');
ins_irq0_irq : out std_logic;
-- RAM Exports
-- Вспомогательные сигналы (тест загрузки и чтения RAM)
avs_s0_read_ext : out std_logic; --
avs_s0_irq_ext : out std_logic;
ram_irq : in std_logic := '0'; -- внешний старт прерывания
ram_load : out std_logic; -- старт загрузки RAM (для теста)
ram_wr : out std_logic; -- строб записи RAM
cnt_out : out std_logic; --
en_irq_out : out std_logic; --
ram_data_in : out std_logic_vector(15 downto 0) -- входные данные RAM (для теста)
);
end entity ramtest;
architecture rtl of ramtest is
--------------------------------------------------------------------
-- компонент двухпортовая RAM
component ramresult
generic
(
DATA_WIDTH : integer := 16;
ADDR_WIDTH : integer := 2
);
port
(
rclk : in std_logic;
wclk : in std_logic;
raddr : in std_logic_vector (0 to ADDR_WIDTH - 1);
waddr : in std_logic_vector (0 to ADDR_WIDTH - 1);
data : in std_logic_vector((DATA_WIDTH-1) downto 0);
we : in std_logic := '1';
q : out std_logic_vector((DATA_WIDTH -1) downto 0)
);
end component;
-- сигналы загрузки и теста чтения RAM
signal cnt : std_logic_vector(3 downto 0) := X"0"; -- делитель
signal s_data_in : std_logic_vector(15 downto 0) := X"0000"; -- данные RAM (вход)
signal s_data_out : std_logic_vector(15 downto 0); -- данные RAM (выход)
signal s_rd : std_logic; -- клок чтения из RAM
signal s_wr : std_logic; -- клок записи в RAM
signal s_addr_wr_ram : std_logic_vector(1 downto 0); -- адрес записи
signal en_loadcnt : std_logic := '0'; -- разрешение загрузки в RAM
signal s_lrcnt : std_logic_vector(5 downto 0) := "000000"; -- счетчик
signal s_ramload : std_logic := '1'; --
signal s_ramread : std_logic := '1'; --
signal en_wrrd : std_logic := '1'; --
-- сигналы для шины Avalon
signal avs_s0_address_reg : std_logic_vector(1 downto 0);
signal avs_s0_we : std_logic;
signal avs_s0_read_n_ext_reg : std_logic;
-- сигналы для IRQ
signal irq_cnt : std_logic_vector(31 downto 0); --
signal s_irq_t : std_logic; --
signal en_irq : std_logic; --
signal s_irq_d : std_logic; --
signal s_irq_n : std_logic; --
signal s_irq_h : std_logic; --
begin
uramresult : ramresult
generic map (16, 2)
port map (rclk => s_rd, wclk => s_wr, raddr => avs_s0_address_reg,
waddr => s_addr_wr_ram, data => s_data_in, we => avs_s0_we, q => s_data_out);
--------------------------------------------------------------------
-- Вспомогательные сигналы
--------------------------------------------------------------------
--
process(clk)
begin
if (rising_edge(clk)) then
cnt <= cnt + 1;
end if;
end process;
cnt_out <= cnt(0);
-- сигналы для загрузи RAM
process(cnt(0))
begin
if (rising_edge(cnt(0))) then
if(en_wrrd = '1') then
s_lrcnt <= s_lrcnt + 1;
end if;
if (s_lrcnt = "000011") then
s_ramload <= '0';
elsif (s_lrcnt = "000101") then
s_ramload <= '1';
elsif (s_lrcnt = "001011") then
s_ramread <= '0';
elsif (s_lrcnt = "001101") then
s_ramread <= '1';
elsif (s_lrcnt = "101101") then
en_wrrd <= '0';
end if;
end if;
end process;
--------------------------------------------------------------------
-- Заполнение RAM
--------------------------------------------------------------------
-- старт стоп перебора адресов RAM
process(cnt(0))
begin
if (falling_edge(cnt(0))) then
if(s_ramload = '0') then
en_loadcnt <= '1';
end if;
if(en_loadcnt = '1') then
s_addr_wr_ram <= s_addr_wr_ram + 1;
else
s_addr_wr_ram <= (others => '0');
end if;
if(s_addr_wr_ram = "11") then
en_loadcnt <= '0';
end if;
end if;
end process;
-- на RAM запись
s_wr <= not((cnt(0) and en_loadcnt) and (not clk));
ram_wr <= s_wr;
ram_load <= en_loadcnt;
-- загрузка RAM
process(cnt(0))
begin
if (rising_edge(cnt(0))) then
if(en_loadcnt = '1') then
if(s_addr_wr_ram = "00") then
s_data_in <= X"3132";
elsif(s_addr_wr_ram = "01") then
s_data_in <= X"3334";
elsif(s_addr_wr_ram = "10") then
s_data_in <= X"3536";
elsif(s_addr_wr_ram = "11") then
s_data_in <= X"3738";
end if;
end if;
end if;
end process;
ram_data_in <= s_data_in;
--------------------------------------------------------------------
-- Avalon slave register read logic
--------------------------------------------------------------------
-- из Avalon MM Slave
avs_s0_read_n_ext_reg <= avs_s0_read_n;
avs_s0_read_ext <= avs_s0_read_n_ext_reg;
-- на RAM адрес из Avalon MM Slave
avs_s0_address_reg <= avs_s0_address;
-- данные на Avalon MM Slave из RAM
avs_s0_readdata <= s_data_out;
-- сигнал чтения из Avalon MM Slave на RAM
s_rd <= (not clk and not avs_s0_read_n);
-- сигнал выбор кристалла из Avalon MM Slave на RAM
avs_s0_we <= avs_s0_read_n;
s_irq_t <= not ram_irq and en_irq;
-- прерывания
process(clk)
begin
if (reset_n = '0') then
en_irq <= '1';
irq_cnt <= (others => '0');
s_irq_h <= '0';
else if (rising_edge(clk)) then
if(s_irq_t = '1') then
s_irq_d <= '1';
else
s_irq_d <= '0';
end if;
if(s_irq_d = '1') then
en_irq <= '0';
s_irq_h <= '1';
end if;
if (en_irq = '0') then
irq_cnt <= irq_cnt + 1;
s_irq_n <= '1';
end if;
if(avs_s0_address_reg = "11" and avs_s0_read_n_ext_reg = '0') then
s_irq_h <= '0';
elsif (irq_cnt = "00000001111111111111111111111111") then
en_irq <= '1';
irq_cnt <= (others => '0');
end if;
end if;
end if;
end process;
ins_irq0_irq <= s_irq_h;
en_irq_out <= en_irq;
-- прерывание длина (внешний тест)
avs_s0_irq_ext <= s_irq_h;
end architecture rtl; -- of ramtest
Далее из файла можно создать компонент и сохранить его под любым нужным имененем. В данном проекте создан компонент ramtest имеющий в своем составе шину Avalon-MM Slave With Interupt со стороны процессора Nios II и двухпортовую пользовательскую память со стороны FPGA. На базе данного компонента и построена логика работы внешнего прерывания. Сигнал ram_irq соединен с внешней кнопкой на DE0. При ее нажатии модуль ramtest формирует уровень прерывания на ins_irq0_irq который сбрасывается программно после входа в подпрограмму обработчик. Конфигурация системы двольно проста:
Как все работает? После загрузки системы в FPGA, в двухпортовую RAM записываются 4 слова данных 0x3132 0x3334 0x3536 0x3738, которые программно из Nios II через шину Avalon-MM Slave в цикле читаются из памяти и передаются по rs232 плюс текст "Test Success". В сою очередь если нажать на кнопку KN2 DE0 Board то будет сгенерировано внешнее прерывание, выполнится подпрограмма обратчик.
Обработчик сбросит тригер формирующий прерывание путем чтения данных из памяти по одному из адресов, считает все оставшиеся данные и передаст их по rs232 плюс дополнительно текст "Irq Success". Таким бразом будет осуществлен визуальный контроль работы прерываний в Nios II. А для пущей убедительности можно воспользоваться готовым проектом NiosIrq_Quartus90 в котором реализовано все вышесказанное.
Похожий проект выполнен и на базе DE0_nano Board от Terasic в Quartus11.0. Скриншет работы теста на консоли Nios II ниже по тексту.
- Прерывания в Nios II Quartus 9.0 - тестовый проект NiosIrq_Quartus90
- Прерывания в Nios II Quartus 11.0 - тестовый проект NiosIrqNano_Quartus110
Электроника :
- Техника электроника (11)
- Полезная электроника (4)
- Электроника для всех (5)
- Техника для дома (6)
- Cхемотехника ПЛИС (11)
- Пректирование PCAD (4)
Программирование :
- Микроконтроллеры (9)
- ПЛИС VHDL Verilog (29)
- C++ Builder (7)
- Visual Studio C++ C# (7)
- Java programming (7)
- Matlab programming (4)
Сайтостроение :
- Сайтостроение HTML (5)
- Сайтостроение PHP (8)
- PHP CMS на файлах (3)
- Web инструменты (9)
- Полезное вебмастеру (11)
- SEO раскрутка сайта (4)
- PHP скрипты (3)
Реклама :
Книги и учебники :
- Шаблоны сайтов (6)
- Книги и учебники (2)
Компьютер и интернет :
Поиск по сайту :
Реклама :
Облако меток :
Бесплатная подписка :
Статистика :
- Популярность (3)
- Посещаемость (3)
- Поисковые запросы (3)