DE0-Nano Cистема на кристалле. Часть 2
Создание системы на кристалле. Часть 2

Порты очереди для записи подключаются к контроллеру АЦП, а порты для чтения к шине Avalon DMA, таким образом, когда контролер DMA будет запрашивать данные для чтения, они будут извлекаться из этой очереди. Для откладки процессов прямого доступа к памяти полезно сделать тестовый режим, когда вместо данных с АЦП в память пишутся последовательные числа, в этом случае легко выясняются ошибки с пропущенными данными или с буферами.
Для декодирования адреса регистра, используется следующий код:
Команда GENERATE используется для генерации нескольких копий одной конструкции, в данном случае создается декодер для одного регистра, а затем правило распространяется на PIO_REG_COUNT-1 регистров. Таким образом, значение единицы в i-том бите pio_addr_dc_i означает выбор i-ого регистра. Чтение и запись выполняется аналогичным образом, только вместо этого используется конструкция LOOP.
В контроллере используется 2 регистра:
–Регистр управления, адрес 0x0
Бит 0 – разрешение записи в очередь
Бит 1 – статус прерывания
Бит 2 – флаг пустой очереди
Бит 3 – флаг заполненной очереди
Бит 4 – включение тестового режима
Бит 5 – включение прерываний
–Регистр доступных слов, смещение адреса относительно базового 0x4 в байтах. Содержит количество доступных для чтения слов в очереди. Итоговый код ядра выглядит следующим образом:
Все три файла adc_stream.vhd, adc_stream_fifo.vhd и adc_stream_top.vhd нужно сохранить в одну папку.
Теперь, когда есть периферийный блок АЦП можно приступить к созданию системы на кристалле.
Для этого создается новый проект в Quartus II, после чего выбирается пункт File>>New>>SOPC Builder System

Для того, чтобы добавить контроллер АЦП в перечень доступных компонентов, необходимо выбрать пункт Project>>New component

Во вкладке HDL Files указываем путь к исходным VHDL файлам

Во вкладке Signals указывается соответствие между портами контроллера и функциями в шине Avalon. Export означает, что данный порт будет выведен на внешний вывод, а не подключен непосредственно к шине, это касается сигналов SPI.
Во вкладке Interfaces сигналы группируются в отдельные шины, в нашем случае их две для программного доступа, допускается чтение и запись, и вторая для режима DMA, где допускается только чтение.



Временные параметры шины PIO и вид диаграмм.


Описание и временные диаграммы второго интерфейса для DMA. Обратите внимание на значение поля Read Latency, здесь установлено значение 1, поскольку правильные данные из очереди появляются с задержкой в один такт. Вкладки HDL parameters и Library Info служат для определения параметров IP-ядра и информации о нём. После нажатия кнопки Finish будет сформирован .tcl скрипт, который использует SOPC Builder. Последнее, что осталось сделать, это настроить путь для поиска IP-компонентов. Это делается пунктом Tools>>Options


Здесь нужно указать путь, где находится .tcl скрипт и исходные .vhd файлы. После этого в списке компонентов появится контроллер АЦП.

Порты очереди для записи подключаются к контроллеру АЦП, а порты для чтения к шине Avalon DMA, таким образом, когда контролер DMA будет запрашивать данные для чтения, они будут извлекаться из этой очереди. Для откладки процессов прямого доступа к памяти полезно сделать тестовый режим, когда вместо данных с АЦП в память пишутся последовательные числа, в этом случае легко выясняются ошибки с пропущенными данными или с буферами.
Для декодирования адреса регистра, используется следующий код:
pio_addr_dc_gen: for i in 0 to PIO_REG_COUNT-1
generate
pio_addr_dc_i(i) <= '1' when pio_address = std_logic_vector(to_unsigned(i, pio_address'length)) else '0';
end generate pio_addr_dc_gen;
Команда GENERATE используется для генерации нескольких копий одной конструкции, в данном случае создается декодер для одного регистра, а затем правило распространяется на PIO_REG_COUNT-1 регистров. Таким образом, значение единицы в i-том бите pio_addr_dc_i означает выбор i-ого регистра. Чтение и запись выполняется аналогичным образом, только вместо этого используется конструкция LOOP.
В контроллере используется 2 регистра:
–Регистр управления, адрес 0x0
Бит 0 – разрешение записи в очередь
Бит 1 – статус прерывания
Бит 2 – флаг пустой очереди
Бит 3 – флаг заполненной очереди
Бит 4 – включение тестового режима
Бит 5 – включение прерываний
–Регистр доступных слов, смещение адреса относительно базового 0x4 в байтах. Содержит количество доступных для чтения слов в очереди. Итоговый код ядра выглядит следующим образом:
library ieee;
library work;
use ieee.std_logic_1164.ALL;
use ieee.numeric_std.ALL;
entity adc_stream_top is
generic
(
PIO_REG_COUNT: integer :=4 --количество программно доступных регистров
);
port
(
clk: in std_logic; --тактовая частота шины Avalon
reset: in std_logic; --сброс
--интерфейс spi
miso: in std_logic;
mosi: out std_logic;
cs: out std_logic;
scl: out std_logic;
--сигналы шины avalon
pio_address: in std_logic_vector(7 downto 0); --адрес регистра
pio_read: in std_logic; --запрос на чтение
pio_readdata:out std_logic_vector(31 downto 0); --данные из регистра
pio_write: in std_logic; --запрос на запись
pio_writedata: in std_logic_vector(31 downto 0); --данные для записи в регистр
pio_irq: out std_logic; --сигнал прерывания
--Интерфейс для DMA канала
dma_read: in std_logic; --запрос на чтение данных
dma_readdata: out std_logic_vector(31 downto 0) --данные из очереди
);
end entity adc_stream_top;
architecture arc of adc_stream_top is
--тип для 32 разрядных регистров
type registers_t is array(PIO_REG_COUNT-1 downto 0) of std_logic_vector(31 downto 0);
--внутренние сигналы от контролллера АЦП
signal adc_sample_i: std_logic_vector(15 downto 0); --сэмпл данных
signal adc_new_sample_i: std_logic; --строб нового сэмпла
--------------------------------------------------
--сигналы от очереди
signal fifo_data_in_i: std_logic_vector(15 downto 0); --входные данные для очереди
signal wrreq_i: std_logic; --разрешения записи
signal rdempty_i: std_logic; --очередь пуста
signal wrfull_i: std_logic; --очередь заполнена
signal rdusedw_i: std_logic_vector(7 downto 0); --количество доступных для чтения слов
--------------------------------------------------
--pio регистры
signal pio_registers_i: registers_t; --программно доступные регистры
signal pio_addr_dc_i: std_logic_vector(PIO_REG_COUNT-1 downto 0); --выход декодера адреса
--Биты управления
signal adc_test_mode_i: std_logic; --тестовый режим
signal adc_test_mode_data_i: unsigned(15 downto 0); --данные в тестовом режиме
signal adc_irq_en: std_logic; --сигнал включения прерывания
begin
--прервывание если очередь заполнена наполовину и бит прерывания установлен
pio_irq <= rdusedw_i(7) and adc_irq_en;
--модуль контроллера АЦП
adc_stream_i: entity work.adc_stream
generic map (
DIV_CLK => 8
)
port map
(
clk => clk,
reset => reset,
convert => wrreq_i,
channel => "000",
adc_sample => adc_sample_i,
adc_new_sample => adc_new_sample_i,
miso => miso,
mosi => mosi,
cs => cs,
scl => scl
);
--Очередь
adc_stream_fifo_i : entity work.adc_stream_fifo
port map
(
aclr => reset,
data => fifo_data_in_i,
rdclk => clk,
rdreq => dma_read,
wrclk => adc_new_sample_i,
wrreq => wrreq_i,
q => dma_readdata,
rdempty => rdempty_i,
wrfull => wrfull_i,
rdusedw => rdusedw_i
);
--Генерация данных в тестовом режиме---------------------------
process
begin
wait until rising_edge(adc_new_sample_i);
if adc_test_mode_i = '0' then
fifo_data_in_i <= adc_sample_i;
adc_test_mode_data_i <= (others => '0');
else
fifo_data_in_i <= std_logic_vector(adc_test_mode_data_i);
adc_test_mode_data_i <= adc_test_mode_data_i + 1;
end if;
end process;
---------------------------------------------------------------
--pio процесс чтения и записи----------------------------------
process
begin
wait until rising_edge(clk);
if reset = '1' then
--Очистка регистров при сбросе
pio_reg_clr: for i in 0 to PIO_REG_COUNT-1
loop
pio_registers_i(i) <= (others => '0');
end loop pio_reg_clr;
else
--чтение регистров
pio_read_gen: for i in 0 to PIO_REG_COUNT-1
loop
if (pio_addr_dc_i(i) and pio_read) = '1' then pio_readdata <= pio_registers_i(i); end if;
end loop pio_read_gen;
--запись в регистры
pio_write_gen: for i in 0 to PIO_REG_COUNT-1
loop
if (pio_addr_dc_i(i) and pio_write) = '1' then pio_registers_i(i)<= pio_writedata; end if;
end loop pio_write_gen;
wrreq_i <= pio_registers_i(0)(0);
adc_test_mode_i <= pio_registers_i(0)(4);
adc_irq_en <= pio_registers_i(0)(5);
pio_registers_i(0)(1) <= rdusedw_i(7);
pio_registers_i(0)(2) <= rdempty_i;
pio_registers_i(0)(3) <= wrfull_i;
pio_registers_i(1)(31 downto 0) <= x"000000"&rdusedw_i;
end if;
end process;
--декодер адреса
pio_addr_dc_gen: for i in 0 to PIO_REG_COUNT-1
generate
pio_addr_dc_i(i) <= '1' when pio_address = std_logic_vector(to_unsigned(i, pio_address'length)) else '0';
end generate pio_addr_dc_gen;
end architecture arc;
Все три файла adc_stream.vhd, adc_stream_fifo.vhd и adc_stream_top.vhd нужно сохранить в одну папку.
Теперь, когда есть периферийный блок АЦП можно приступить к созданию системы на кристалле.
Для этого создается новый проект в Quartus II, после чего выбирается пункт File>>New>>SOPC Builder System

Для того, чтобы добавить контроллер АЦП в перечень доступных компонентов, необходимо выбрать пункт Project>>New component

Во вкладке HDL Files указываем путь к исходным VHDL файлам

Во вкладке Signals указывается соответствие между портами контроллера и функциями в шине Avalon. Export означает, что данный порт будет выведен на внешний вывод, а не подключен непосредственно к шине, это касается сигналов SPI.

Во вкладке Interfaces сигналы группируются в отдельные шины, в нашем случае их две для программного доступа, допускается чтение и запись, и вторая для режима DMA, где допускается только чтение.



Временные параметры шины PIO и вид диаграмм.


Описание и временные диаграммы второго интерфейса для DMA. Обратите внимание на значение поля Read Latency, здесь установлено значение 1, поскольку правильные данные из очереди появляются с задержкой в один такт. Вкладки HDL parameters и Library Info служат для определения параметров IP-ядра и информации о нём. После нажатия кнопки Finish будет сформирован .tcl скрипт, который использует SOPC Builder. Последнее, что осталось сделать, это настроить путь для поиска IP-компонентов. Это делается пунктом Tools>>Options


Здесь нужно указать путь, где находится .tcl скрипт и исходные .vhd файлы. После этого в списке компонентов появится контроллер АЦП.

0 комментариев