DE0-Nano Cистема на кристалле. Часть 2

Блог им. katatonia67
Создание системы на кристалле. Часть 2


Порты очереди для записи подключаются к контроллеру АЦП, а порты для чтения к шине 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 комментариев

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.