Реализация Avalon-MM Master в виде конечного автомата на VHDL

Автор: Бортников Анатолий Юрьевич, генеральный директор компании ООО «РСВ ЭЛЕКТРОНИКС», кандидат физ.- мат. наук.

Введение.

Шина Avalon-MM является одной из стандартных шин передачи данных, используемых в ПЛИС фирмы Intel. Использование этой шины в своих модулях для передачи данных существенно повышает их возможность повторного применения и повышает надежность проектов. Также упрощается интеграция модулей в проект с помощью Platform Designer.

Принципы работы шины Avalon-MM.

Шина Avalon-MM используется для передачи данных между модулями проекта. Она позволяет выполнять запись данных в модуль либо чтение данных из модуля обращаясь к нему как к блоку памяти.

На шине Avalon-MM существует определенная иерархия модулей. Обязательно должен быть контроллер — Master, который управляет всеми транзакциями передачи данных по шине. Остальные модули выступают в роли подчиненных — Slave. Каждый Slave на шине имеет определенный адрес или диапазон адресов, в которые Master может писать данные или читать из этих адресов. На рис. 1 представлен вариант конфигурации шины Avalon-MM.

yjkj08mgeuargexbikgrcdtlkbc.png

Рис 1. Вариант конфигурации шины Avalon-MM

В качестве мастера часто выступает процессор NIOS II либо может использоваться другой модуль в качестве процессора. Если использовать NIOS II то проблем с подключением к шине Avalon-MM не возникает. Так как он уже имеет встроенный интерфейс Avalon-MM Master. Если же есть необходимость использовать процессор без интерфейса Avalon-MM (например когда процессор пишется пользователем под конкретную задачу) то для его подключения к шине Avalon-MM требуется реализация интерфейса Avalon-MM Master.

Так как для управления периферией предполагается выполнение операций записи и чтения данных, рассмотрим минимально необходимый для этого набор сигналов интерфейса Avalon-MM Master, представленный в таблице 1.

Таблица 1. Сигналы шины Avalon-MM, минимальный набор

Название

разряды

направление

описание

address

1–32

выход

address представляет адрес байта независимо от ширины шины данных. Значение адреса должно быть выровнено по ширине шины данных. Для записи отдельных байтов с шины данных требуется использовать сигналы byteenable. Мастер всегда выставляет адрес независимо от ширины шины данных. Система межсоединений конвертирует этот адрес в адрес слов в адресном пространстве Slave-модуля.

read, read_n

1

выход

Сигнал запроса операции чтения. Не требуется, если Master никогда не инициализирует операцию чтения. Но если есть сигналы read/read_n то readdata обязательны.

readdata

8, 16, 32, 64, 128, 256, 512, 1024

вход

Сигналы для чтения данных.

write write_n

1

выход

Сигнал запроса операции записи. Не требуется, если Master никогда не инициализирует операцию записи данных. Но если есть write/write_n то сигналы writedata обязательны.

writedata

8, 16, 32, 64, 128, 256, 512, 1024

вход

Сигналы для передачи данных для записи в Slave. Если присутствуют writedata и readdata, то они должны быть одинаковой разрядности.

byteenable byteenable_n

1, 2, 4, 8, 16, 32, 64, 128

выход

Сигналы активизируют отдельные байты данных при передаче, если разрядность шины данных больше 8. Каждый бит в byteenable соответствует отдельному байту в шине данных как при чтении так и при записи. Единица в бите byteenable показывает, будет ли передан этот байт по шине данных. Остальные байты в шине данных должны игнорироваться Slave-ом. Если требуется передать больше чем 1 байт по шине данных, то все байты должны быть выставлены. Количество передаваемых байт должно быть равно степени 2. Доступны следующие варианты значения сигналов byteenable и количество передаваемых байт: 1111 — передача 4-х байтов (32 бита); 0011 — передача младших двух байт (16 бит); 1100 — передача старших двух байт (16 бит); 0001 — передача младшего (первого) байта; 0010 — передача второго байта; 0100 — передача третье байта; 1000 — передача четвертого байта;

waitrequest waitrequest_n

1

вход

Сигнал ожидания завершения транзакции. waitrequest выставляется Svale-ом, в случае, если он не может в данный момент принять входные данные или выдать данные для чтения. Master ожидает пока waitrequest не сбросится, чтобы завершить транзакцию. В это время Master не должен переключать сигналы на шине Avalon-MM.

Как было упомянуто выше, все передачи данных инициализирует Master. Передачи данных осуществляется словами. Разрядность слов настраивается дискретными значениями в диапазоне от 8 бит до 1024 бит. Активные байты в передаваемых словах обозначаются маской в сигналах byteenable или byteenable_n.

Slave может приостановить передачу данных выставив в единицу сигнал waitrequest. Тогда, пока сигнал waitrequest равен 1, Master находится в режиме ожидания и не имеет права переключать сигналы интерфейса. Транзакция может завершиться только когда Slave сбросит сигнал waitrequest в 0 на рис. 2 представлены временные диаграммы записи и чтения данных.

ovfokxabdv1ppeoa5unfqe5hbke.png

Рис. 2 Временные диаграммы записи и чтения данных по шине Avalon-MM.

  1. Master начинает чтение данных выставляя сигналы address, byteenable и read. Slave выдает данные на readdata в течение следующего такта clk.

  2. Master считывает данные с readdata и сбрасывает сигнал read, заканчивая тем самым операцию чтения данных. И тут же начинает операцию записи данных выставляя сигналы address, byteenable, write и данные на writedata.

  3. Сигнал waitrequest не выставляется в единицу по следующему фронту clk, следовательно по этому фронту заканчивается операция записи. Master сбрасывает сигнал write.

  4. Master выставляет address, byteenable, writedata и write начиная следующую операцию записи. Тут же Slave поднимает сигнал waitrequest, сообщая, что он не готов в данный момент обрабатывать данные. Поэтому Master удерживает без изменения все сигналы шины Avalon-MM.

  5. waitrequest сброшен, и тут операция записи завершается. И сразу Master начинает следующую операцию чтения выставляя сигналы read, address, byteenable. Slave, в свою очередь, поднимает сигнал waitrequest, сообщая, что он занят выполнением другой операции и не может в данный момент выставить данные на выход readdata. На следующем такте Slave выставляет данные на шину readdata и сбрасывает сигнал waitrequest.

  6. waitreqiuest сброшен на следующем фронте сигнала clk, следовательно на этом фронте clk операция чтения завершается.

Реализация интерфейса Avalon-MM Master в виде конечного автомата

Если посмотреть внимательно на временные диаграммы записи чтения данных изображенных на рис. 2, то можно увидеть, что интерфейс при каждой транзакции проходит одни и те же состояния. Следовательно его можно реализовать в виде конечного автомата. Но сначала определим порты для нашего интерфейса Avalon-MM Master.

Порты интерфейса Avalon-MM Master

Представим наш интерфейс Avalon-MM Master как отдельный модуль, встраиваемый в какой-либо процессор или контроллер в следующем виде рис. 3.

lw8rds06wxsnzphsbmgkd6v3y7a.png

Рис. 3 Графическое обозначение модуля Avalon-MM Master.

Описание портов ввода-вывода на языке VHDL для компонента Avalon-MM Master, графическое представление которого изображено на рис. 3, приведено ниже в листинге 1.

Листинг 1: Описание портов ввода\вывода для Avalon-MM Master

PORT(
	nReset 				: IN STD_LOGIC;
	Clock 				: IN STD_LOGIC;
	
	------------ Processor Interface --------------------
	
	CPU_WrEn 			: IN STD_LOGIC;
	CPU_RdEn 			: IN STD_LOGIC;
	CPU_AddrIn 			: IN STD_LOGIC_VECTOR( 7 DOWNTO 0 );
	CPU_WrDataIn 		: IN STD_LOGIC_VECTOR( 31 DOWNTO 0 );
	CPU_ByteEnCode 		: IN STD_LOGIC_VECTOR( 3 DOWNTO 0 );
	CPU_Ready 			: OUT STD_LOGIC;
	CPU_RdDataOut 		: OUT STD_LOGIC_VECTOR( 31 DOWNTO 0 );
	
	---------- Avalon-MM Master interface --------------
	
	avm_readdata 		: IN STD_LOGIC_VECTOR( 31 DOWNTO 0 );
	avm_readdatavalid 	: IN STD_LOGIC;
	avm_address 		: OUT STD_LOGIC_VECTOR( 7 DOWNTO 0 );
	avm_byteenable 		: OUT STD_LOGIC_VECTOR( 3 DOWNTO 0 );
	avm_read 			: OUT STD_LOGIC;
	avm_write 			: OUT STD_LOGIC;
	avm_writedata 		: OUT STD_LOGIC_VECTOR( 31 DOWNTO 0 )
);

Согласно таблице 1, разрядность и шин данных avm_writedata и avm_readdata равны между собой. Разрядность сигналов avm_byteenable равна разрядность шин данных деленной на 8. Каждый бит в avm_byteenable ставится в соответствии байту на шине данных.

Разрядность шина адреса avm_address составляет в данном случае 8 бит. Разрядность шины адреса выбирается в зависимости от того, насколько большие области адресов памяти выделяются для компонентов периферии.

Если в проекте не используются модули встроенной памяти или контроллеры доступа к внешней памяти SDRAM, DDR-DDR3, то скорее всего большая разрядность шины адреса не потребуется.

Однако, если предполагается работа с перечисленными выше контроллера доступа к памяти, то может потребоваться достаточно большая разрядность шины адреса: 8, 16, 24, 32 бита.

Это связано с тем, что для модулей периферии выделяются относительно не большие объемы памяти — буквально несколько регистров. Ниже перечислены самые распространенные регистры, которые присутствуют практически в каждом периферийном модуле:

  • регистр записи данных;

  • регистр чтения данных

  • регистр управления и настройки

  • регистр статуса

Для модулей работы с памятью, в свою очередь, выделяются гораздо большие массивы памяти, что требует большей разрядности шины адреса.

Описание конечного автомата

На рис. 4 представлена диаграмма состояний конечного автомата интерфейса Avalon-MM Master.

oazsvgpffkcnwro4x-v9ka33wie.png

Рис. 4 Диаграмма состояний конечного автомата интерфейса Avalon-MM Master

После подачи питания конечный автомат попадает в состояние сброса FSM_RESET. Где сбрасываются в исходные состояния все внутренние переменные и сигналы шины Avalon-MM.

На следующем такте конечный автомат переходит в состояние ожидания FSM_IDLE. Это исходное состояние автомата для работы с шиной Avalon-MM. В этом состоянии автомат ожидает команду от процессора. В листинге 2 представлен исходный код, описывающий это состояние на VHDL.

Листинг 2. Исходный код реализации состояния FSM_IDLE

WHEN FSM_IDLE =>
	IF( CPU_WrEn = '1' ) THEN
		MasterAddr      <= CPU_AddrIn;
		MasterData      <= CPU_WrDataIn;
		FSM_MasterState <= FSM_WRITE;
	ELSIF( CPU_RdEn = '1' ) THEN
		MasterAddr      <= CPU_AddrIn;
		FSM_MasterState <= FSM_READ;
	ELSE
		MasterAddr <= ( OTHERS => '0' );
		MasterData <= ( OTHERS => '0' );
		avm_write  <= '0';
		avm_read   <= '0';
		avm_byteenable <= ( OTHERS => '0' );
		ReadyTmp <= '0';
	END IF;

По сигналам CPU_WrEn и CPU_RdEn происходит предварительное защелкивание адреса и данных с процессора во внутренние переменные конечного автомата.

При появлении единицы на линии CPU_RdEn, конечный автомат переходит в состояние FSM_READ — состояние чтения данных по шине Avalon-MM. Процессор должен вместе с единицей на линии CPU_RdEn выставить адрес на входные линии CPU_AddrIn, с которого он запрашивает чтение данных. И в это же время процессор должен задать код на шине CPU_ByteEnCode, чтобы определить, какие байты на шине данных будут активны.

В состоянии FSM_READ конечный автомат дублирует адрес на шину avm_address, повторяет код с CPU_ByteEnCode на линии avm_byteenable и выставляет единицу на линию avm_read, в соответствии с временными диаграммами на рис. 2. В листинге 3 показан исходный код этого состояния на VHDL

Листинг 3. Исходный код реализации состояния FSM_READ

WHEN FSM_READ =>
	avm_write 		<= '0';
	avm_read 		<= '1';
	avm_address 	<= MasterAddr;
	avm_byteenable 	<= CPU_ByteEnCode;
	FSM_MasterState <= FSM_ACK_READ;

На следующем такте конечный автомат переходит в состояние FSM_ACK_READ — подтверждение чтения данных. В этом состоянии автомат проверяет значение на линии avm_readdatavalid.

Если значение avm_readdatavalid равно единице, значит на линии avm_readdata доступны верные данные. В таком случае эти данные дублируются на выход CPU_RdDataOut и на линию CPU_Ready выставляется единица, как сигнал для процессора, что c интерфейса Avalon-MM Master доступны новые данные для чтения. На следующем такте автомат переходит в исходное состояние FSM_IDLE.

Если же значение avm_readdatavalid равно нулю, то на линию CPU_Ready выставляется ноль и на следующем такте автомат переходит также в исходное состояние FSM_IDLE.

В листинге 4 представлен исходный код реализации состояния FSM_ACK_READ на VHDL.

Листинг 4. Исхjдный код реализации состояния FSM_ACK_READ

WHEN FSM_ACK_READ =>
	IF( avm_readdatavalid = '1' ) THEN
		ReadyTmp 		<= '1';
		RdDataOutTmp 	<= avm_readdata;
		FSM_MasterState <= FSM_IDLE;
	ELSE
		FSM_MasterState <= FSM_IDLE;
		ReadyTmp 		<= '0';
		RdDataOutTmp 	<= ( OTHERS => '0' );
	END IF;

Из состояние FSM_IDLE конечный автомат по единице на входной линии CPU_WrEn переходит в состояние FSM_WRITE. В этом состоянии дублируется значение адреса с линии CPU_AddrIn и код с линии CPU_ByteEnCode на выходные линии avm_address и avm_byteenable соответственно. И выставляется единица на линию avm_write. Исходный код реализации этого состояния представлен в листинге 5.

Листинг 5: Исходный код реализации состояния FSM_WRITE.

WHEN FSM_WRITE =>
	avm_write 		<= '1';
	avm_read 		<= '0';
	avm_address 	<= MasterAddr;
	avm_byteenable 	<= CPU_ByteEnCode;
	avm_writedata 	<= MasterData;
	FSM_MasterState <= FSM_ACK_WRITE;

На следующем такте автомат переходит в состояние FSM_ACK_WRITE, в котором сбрасывается в 0 сигнал на линии avm_write. Это приводит к завершению операции записи данных в периферийный модуль. Исходный код реализации состояния FSM_ACK_WRITE представлен в листинге 7.

Листинг 7. Исходный код реализации состояния FSM_ACK_WRITE.

WHEN FSM_ACK_WRITE =>
	avm_write 		<= '0';
	FSM_MasterState <= FSM_IDLE;

На следующем такте автомат переходит в исходное состояние FSM_IDLE для ожидания новой команды от процессора.

Обработка waitrequest

Конечно не всегда периферийный модуль может за один такт успеть защелкнуть записываемые в него данные либо успеть выдать на следующем такте запрашиваемые в него данные. На этот случай шина Avalon-MM имеет сигнал waitrequest.

Сигнал waitrequest управляется интерфейсом Avalon-MM Slave на периферийном модуле (см. рис. 2). Если периферийный модуль не может в данный момент отреагировать на запрос от Avalon-MM Master, то он выставляет 1 на линию waitrequest. Это приводит к тому, что Avalon-MM Master должен удерживать все сигналы на линии Avalon-MM в текущем состоянии, пока периферийный модуль не сбросить waitrequest в 0. И только тогда Avalon-MM Master сможет завершить текущую операцию записи или чтения.

Обработку сигнала waitrequest лучше всего добавить в состояния FSM_ACK_READ и FSM_ACK_WRITE. Так как в этих состояниях автомат уже выставил все сигналы на шине Avalon-MM, то тут нам только потребуется проверять значения сигнала waitrequest. Пока он равен 1, автомат должен находиться в текущем состоянии, а как только waitrequest будет сброшен в 0, автомат сможет завершить текущую операцию и перейти в состояние FSM_IDLE. Исходные коды реализации состояний FSM_ACK_READ и FSM_ACK_WRITE с обработкой сигнала waitrequest представлены в листинге 8 и 9 соответственно.

Листинг 8. Исходный код реализации сосотяния FSM_ACK_READ с обработкой сигнала waitrequest.

WHEN FSM_ACK_READ =>
	IF( avm_readdatavalid = '1' ) THEN
		ReadyTmp 		<= '1';
		RdDataOutTmp 	<= avm_readdata;
		avm_read 		<= '0';
		FSM_MasterState <= FSM_IDLE;
	ELSE
		ReadyTmp 		<= '0';
		RdDataOutTmp 	<= ( OTHERS => '0' );
		IF( avm_waitrequest = '0' ) THEN
			IF (wait_cycle > 0) THEN
				wait_cycle <= wait_cycle - 1;
			ELSE
				FSM_MasterState <= FSM_IDLE;
			END IF;
		ELSE
			IF( AckCounter > 0 )THEN
				AckCounter := ( AckCounter - 1 );
			ELSE
				FSM_MasterState <= FSM_IDLE;
			END IF;
		END IF;
	END IF;

Листинг 9. Исходный код реализации сосотяния FSM_ACK_WRITE с обработкой сигнала waitrequest.

WHEN FSM_ACK_WRITE =>
	IF( avm_waitrequest = '0' ) THEN
		avm_write		<= '0';
		FSM_MasterState <= FSM_IDLE;
	ELSE
		IF( AckCounter > 0 )THEN
			AckCounter := ( AckCounter - 1 );
		ELSE
			avm_write 		<= '0';
			FSM_MasterState <= FSM_IDLE;
		END IF;
	END IF;

Нужно отметить, что в условие проверки сигнала waitrequest полезно добавить счетчик-таймер, по истечению которого, если периферийный модуль так и не сбросит сигнал waitrequest в 0, то автомат вернется в исходное состояние FSM_IDLE. Таким образом возможное зависание периферийного модуля не приведет к зависанию интерфейса Avalon-MM Master.

В нашем случае мы добавили счетчик AckCounter, который считает от 15 до 0. В состоянии FSM_IDLE значение этого счетчика выставляется равное 15.

Так же нужно отметить, что в состояние FSM_ACK_READ была добавилена задержка в 1 такт на счетчике wait_cycle. Это было сделано для того, чтобы автомат задержался в состоянии FSM_ACK_READ на 1 такт дольше, чтобы дождаться появления 1 на линии avm_readdatavalid.

Отладка конечного автомата Avalon-MM Master

Для отладки работы конечного автомата был написан testbench, достаточно простой. Все окружение, кроме тестируемого интерфейса Avalon-MM Master состояло из конечного автомата, который эмулировал команды процессора — FSM_CPU и модуля встроенной памяти On-Chip Memory с интерфейсом Avalon-MM Slave.

Автомат процессора последовательно задавал команды на запись и чтение данных. Встроенная память была изначально проинициализирована тестовыми данными.

На рис. 5 представлены временные диаграммы работы компонента Avalon-MM Master с встроенной памятью через шину Avalon-MM.

8iddhmpuzb7xaell-cknl50jfoo.png

Рис. 5 временные диаграммы работы компонента Avalon-MM Master с встроенной памятью через шину Avalon-MM (приставка TB_ обозначает сигналы из testbench)

Когда процессор начинает операцию записи, он выставляет на линии TB_CPU_WrEn, TB_CPU_AddrIN, TB_CPU_WrDataIn соответсвенно сигнал записи, адрес ячейки (0xAD) и данные, которые требуется записать в память (0xDADA0505).

В свою очередь автомат интерфейса Avalon-MM Master переходит в состояние FSM_WRITE и выставляет на шину Avalon-MM сигнал TB_avm_write равный 1 и дублирует адрес и данные на линии TB_avm_address и TB_avm_writedata соответвтенно.

На следующем такте On-Chip Memory выдает 1 на линию TB_waitrequest, сигнализируя о процессе записи данных в память. После чего процедура записи заканчивается. Автомат интерфейса Avalon-MM Master переходит в состояние FSM_IDLE.

Для начала операции чтения процессор выставляет на линии TB_CPU_RdEn интерфейса Avalon-MM Master единицу и на линии TB_CPU_AddrIN адрес ячейки (0xBB) для чтения данных.

Автомат интерфейса Avalon-MM Master переходит в состояние FSM_READ и выставляет единицу на линии TB_avm_read, а так же дублирует адрес ячейки на шину адреса TB_avm_address и выставляет код активных байтов (0xF) на линии TB_avm_byteenable.

На следующем такте On-Chip Memory выдает 1 на линию TB_waitrequest, сигнализируя о процессе чтения данных из памяти. И в следюущий такт считаные данные выставыляются на линию TB_avm_readdata (0×1A2B3C4D) и одновременно с этим выставляется единица на линию TB_avm_readdatavalid, сигнализирующая, что выставлены валидные данные. После чего автомат интерфеса Avalon-MM Master выставляет единицу на линию TB_CPU_Ready, показывая что доступны новые данные для процессора и дублирует данные с линии TB_avm_readdata на выход TB_CPU_RdDataOut. После чего операция чтения завершается.

Заключение

В данной статье был рассмотрен вариант реализации конечного автомата для интерфейса Avalon-MM Master. Данная реализация позволяет встраивать этот интерфейс в свои компоненты, что приводит к повышению степени их повторного использования и упрощает проектирование систем на ПЛИС с использованием инструмента Platform Designer.

© Habrahabr.ru