Выполнение транзакций на шине PCI. Реализация на VHDL

в 18:32, , рубрики: Железо, Программинг микроконтроллеров

Не так давно я спрашивал о механизме опроса PCI-устройств. После я устроился на работу, доделал тестовое задание, а спрашивал я именно о нем, и благополучно забыл о нем. Но недавно выдали новый проект и пришлось все вспомнить, заодно и решил написать сюда.

Транзакций на шине PCI достаточно много, в данном топике будет описаны только следующие:

  • Конфигурационные транзакции
  • Транзакции ввода/вывода
  • Транзакции обращения к памяти


При осуществлении транзакций возможно 2 варианта:

  • Когда ведущим устройством является южный мост
  • Когда ведущим устройством является устройство, подключенное к шине PCI

Как уже сложилось, при рассмотрении шины PCI, ведущее устройство я буду называть мастером(Master), ведомое — таргетом (Target).
В данной статье рассматриваются только транзакции, когда мастером является южный мост, так как транзакции, когда мастером является устройство, подключенное к шине PCI заслуживает отдельной статьи.

И так, для работы с шиной, нам понадобятся следующие сигналы:

entity main is
Port (	
	clk	    : in	std_logic;
	AD	: inout	std_logic_vector(31 downto 0);
	IDSEL	: in	std_logic;
	CBE	: in	std_logic_vector(3 downto 0);
	FRAME	: in	std_logic;			  
	IRDY	: in	std_logic;
	TRDY	: inout	std_logic;
	STOP	: inout	std_logic;
	PAR	: inout	std_logic;
	RST	: in	std_logic;
	DEVSEL	: inout	std_logic
 );
end main;

clk (Clock) — обеспечивает синхронизацию всех транзакций на PCI, а также является входным для каждого PCI — устройства.
AD (Address and Data) — мультиплексирования шина адреса и данных.
IDSEL (Initialization Device Select) — выбор устройства инициализации, используется для выбора кристалла при транзакциях чтения конфигурации и записи.
CBE (Bus Command and Byte Enables) — команды шины и разрешение байта.
FRAME (Frame) — сигнал выдаётся мастером в начале транзакции и определяет её длительность.
IRDY (Initiator Ready) — сигнал готовности мастера. Он свидетельствует о готовности мастера завершить текущую фазу данных.
TRDY (Target Ready) — сигнал готовности таргета, свидетельствующий о готовности таргета завершить текущую фазу данных.
STOP (Stop) — этот сигнал выдаётся таргетом, если он хочет остановить текущую транзакцию.
PAR (Parity) — контроль четности по линиям AD и CBE.
RST(Reset) — cигнал сброса. Является асинхронным.
DEVSEL (Device Select) — сигнал выбора устройства.

Перед началом работы с любым устройством его нужно инициализировать. Поэтому рассмотрим особенности выполнения конфигурационных транзакций.

Конфигурационные транзакции. Общие сведения.

Вид на конфигурационные транзакции со стороны ОС
Для генерации конфигурационных транзакций PCI на ПК используются обращения к двум портам ввода-вывода, носящим имена CONFIG_ADDRESS и CONFIG_DATA, имеющим адреса 0CF8h и 0CFCh соответственно и входящим в состав моста Host–PCI, через который шина PCI прямо или косвенно соединяется с процессором.
Порт CONFIG_ADDRESS имеет размер двойное слово и доступен только как единое целое. Обращения меньшего размера по принадлежащим ему адресам передаются на шину PCI как обычные транзакции ввода-вывода. Этот порт доступен для чтения и записи и имеет следующий формат:
Выполнение транзакций на шине PCI. Реализация на VHDL
Когда необходимо выполнить конфигурационную транзакцию, в этот порт записывается адрес регистра конфигурационного пространства PCI, состоящий из номеров шины (разряды 23–16), устройства (15–11), функции (10–8) и собственно регистра (7–2). Биты 1 и 0 должны всегда содержать нули, а старший бит должен содержать единицу, разрешая тем самым выполнение конфигурационной транзакции. Разряды 30–24 зарезервированы и должны содержать нули.

Собственно генерация конфигурационной транзакции происходит при чтении или записи порта CONFIG_DATA, когда в CONFIG_ADDRESS был записан адрес с установленным старшим битом и номером шины, соответствующим шине, подключенной к мосту Host–PCI, или любой шине PCI, лежащей ниже этой шины и соединённой с ней через один или несколько мостов PCI–PCI (допустимый диапазон номеров шин задаётся мосту Host–PCI в процессе его настройки). Доступ к порту CONFIG_DATA должен иметь размер, равный размеру считываемого или записываемого конфигурационного регистра, адрес которого находится в CONFIG_ADDRESS.
Если номер шины, заданный в CONFIG_ADDRESS, совпадает с номером шины, подключённой непосредственно к мосту Host–PCI, генерируется конфигурационная транзакция с адресом типа 0, причём номер устройства, находящийхся в разрядах 15–11 порта CONFIG_ADDRESS, используется для выдачи одного из сигналов IDSEL, которые и служат для выбора конкретного устройства. Кроме того, декодированный номер устройства (один единичный и остальные нулевые биты) в фазе адреса конфигурационной транзакции передаётся в разрядах 31–11 адреса.
Если адрес в CONFIG_ADDRESS указывает не ту шину, которая непосредственно подключена к мосту Host–PCI, последний генерирует конфигурационную транзакцию с адресом типа 1. Она будет обработана мостом PCI–PCI, который опознает содержащийся в адресе номер шины. Этот мост либо выполнит конфигурационную транзакцию с адресом типа 0 (если адресуемое устройство подключено к шине, прямо подсоединённой к этому мосту), либо сгенерирует транзакцию с адресом типа 1, обеспечив тем самым её прохождение через следующий мост. Длина этой цепочки теоретически ограничена только разрядностью поля, отведённого под номер шины (8 бит).

Если при выполнении транзакции выяснится, что адресуемого конфигурационного регистра не существует (указан номер несуществующей шины, устройства, функции или регистра), то операция записи не возымеет никаких действий, а операция чтения вернёт процессору значение, содержащее единицы в каждом разряде
Выполнение транзакций на шине PCI. Реализация на VHDL
Формат адреса для транзакции типа 1.
Выполнение транзакций на шине PCI. Реализация на VHDL
Формат адреса для транзакции типа 0.

Формат регистра конфигурации:
Выполнение транзакций на шине PCI. Реализация на VHDL
Минимальный набор регистров:

  • Vendor ID — поле идентифицирует изготовителя устройства. Запрещено использовать значение 0xFFFF.
  • Device ID — поле идентифицирует конкретный вид устройства.
  • Revision ID — дополнение к идентификатору устройства. Может быть равно нулю.
  • Header Type — Для многофункциональных устройств. Если 7ой бит равен 0, то устройство является однофункцальным, иначе — многофункциональное.
  • Class Code — доступен только для чтения. Используется для идентификации общего функционального назначения устройства. Старший байт (адрес 0Bh) определяет базовый класс, средний — подкласс, младший — программный интерфейс (если он стандартизован).
  • Subsystem ID, Subsystem Vendor ID — задаются производителем. Только для чтения. Хранят идентификаторы, позволяющие точно идентифицировать карты и устройства (в системе могут быть установлены
    несколько карт с совпадающими идентификаторами устройства и производителя (Device ID и Vendor ID).
  • BAR0 — BAR5 — описывают области памяти и портов ввода-вывода.

Для областей памяти и портов описания различаются:

  • Бит 0 = 0 — признак памяти. Размером не более 2 Гбайт
  • Бит 0 = 1 — признак области портов. Размером до 256 байт.

Размер областей вычисляется следующим образом. В BAR записывается 0xFFFFFFFF. Далее, из BAR считывается значение, и вычитается из 0xFFFFFFFF. Результат и есть размер области. Единица в младшем бите не учитывается.

Общий алгоритм выполнения транзакций

Мастер выставляет на шине AD адрес устройства, на шине CBE выполняемую команду, устанавливает сигнал FRAME в 0 и сигнал IRDY в 0. Далее, мастер ждет от таргета — выставления им сигналов TRDY и DEVSEL. Так же, таргет выставляет на шину AD запрашиваемые данные. Данные считаются валидными, когда FRAME, IRDY и DEVSEL равны уровню логического нуля.

Реализация

Для обращения к выводам ПЛИС потребуются специальные компоненты: буферы ввода/вывода.
Так, для шины AD подключение будет выглядеть следующим образом:

signal AD_I:			std_logic_vector (AD'range);
signal AD_O:			std_logic_vector (AD'range);
signal AD_T:			std_logic;

AD_BUF:
for iCount in AD'low to AD'high generate
      begin
        
	IOBUF_AD : IOBUF
	generic map 
	(
	DRIVE => 12,
	IOSTANDARD => "PCI33_3",
	SLEW => "SLOW")
	port map (
		O => AD_I(iCount),
		IO => AD(iCount),
		I => AD_O(iCount),
		T => AD_T
	);
end generate;

Где,

  • O — выход буфера.
  • IO — вход/выход буфера, непосредственно подключается к выводу ПЛИС.
  • I — выход буфера.
  • T — управление входом, уровень единицы — вход, уровень нуля — выход.

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

Как я уже писал выше, при начале транзации, когда на шине AD выставлен адрес, всегда сигнал FRAME равен нулю. Ниже приведен код, который формирует сигнал AdrPhASE, во время действия которого нужно защелкнуть шину адреса и шину команд для последующей работы.

signal AdrPhASE:		std_logic;
signal FRAME_D:	std_logic;
signal Addres:			std_logic_vector(AD_I'range);
signal Command:	std_logic_vector(CBE'range);
signal bCfgTr:			boolean;

process (clk_I, RST_I) begin
	if (RST_I = '1') then
		if (rising_edge(clk_I)) then
			FRAME_D <= FRAME_I after cTCQ;
		end if;
	else FRAME_D <= '1' after cTCQ;
	end if;
end process;

AdrPhASE <= not FRAME_I and FRAME_D;

process (clk_I, RST_I) begin
	if (RST_I = '0') then
		Address	<= (others => '0') after cTCQ;
		Command	<= (others => '0') after cTCQ;
		bCfgTr <= false after cTCQ;
	elsif (rising_edge(clk_I)) then
		if (AdrPhASE = '1') then
			Address	<= AD_I	 after cTCQ;
			Command	<= CBE_I after cTCQ;
			bCfgTr	<= (IDSEL_I = '1') after cTCQ;
		end if;
	end if;
end process;

Далее, работу всего устройства можно описать с помощью автомата.

Код автомата

type TSM_PCI_T is		(sIDLE, sDECODE, sCFG_READ, sCFG_WRITE, sIO_READ, sIO_WRITE, sMEM_READ, sMEM_WRITE);
signal smPCI_T: 		TSM_PCI_T;

process(clk_I, RST_I) begin
	if (RST_I = '0') then
		smPCI_T <= sIDLE after cTCQ;
	elsif (rising_edge(clk_I)) then
		case (smPCI_T) is
			when sIDLE	=>	if (AdrPhASE = '1') then smPCI_T <= sDECODE after cTCQ;	end if;
			when sDECODE	=>	if (bCfgTr and Address(10 downto 8) = b"000" and Command(3 downto 1) = b"101")  then
							if (Command(0) = '0') then smPCI_T <= sCFG_READ	after cTCQ;
								else smPCI_T <= sCFG_WRITE after cTCQ; end if;
						elsif (Command(3 downto 1)= b"001") and (Addres(31 downto 8) = BAR0(31 downto 8))then	
							if (Command(0) = '0') then smPCI_T <= sIO_READ after cTCQ;
							else smPCI_T <= sIO_WRITE after cTCQ;	end if;
						elsif (Command(3 downto 1) = b"011") and (Addres(31 downto 16) = BAR1(31 downto 16)) then
							if (Command(0) = '0') then smPCI_T <= sMEM_READ	after cTCQ;
								else smPCI_T <= sMEM_WRITE after cTCQ; end if;
						else smPCI_T <= sIDLE after cTCQ; 
						end if;
			when sCFG_READ	=>	if (IRDY_I = '0') then	smPCI_T <= sIDLE after cTCQ;  end if;  	
			when sCFG_WRITE	=>	if (IRDY_I = '0') then	smPCI_T <= sIDLE after cTCQ;  end if;  	
			when sIO_WRITE	=>	if (IRDY_I = '0') then	smPCI_T <= sIDLE after cTCQ;  end if;  	
			when sIO_READ	=>	if (IRDY_I = '0') then	smPCI_T <= sIDLE after cTCQ;  end if;  	
			when sMEM_READ	=>	if (IRDY_I = '0') then	smPCI_T <= sIDLE after cTCQ;  end if;
			when sMEM_WRITE	=>	if (IRDY_I = '0') then	smPCI_T	<= sIDLE after cTCQ;  end if;
			when others		=>	smPCI_T <= sIDLE		after cTCQ;
		end case;
	end if;
end process; 

Для понимания выше написанного, приведу возможные команды, передаваемые по шине CBE.

  • 0010 I/O Read
  • 0011 I/O Write
  • 0110 Memory Read
  • 0111 Memory Write
  • 1010 Configuration Read
  • 1011 Configuration Write

Каждой команде соответствует свое состояние автомата. Переход в него зависит от текущего состояния шины CBE и шины AD для транзакций обращения к памяти и портам ввода-вывода. Выход в начальное состояние осуществляется по приходу сигнала IRDY от мастера.

Чтение конфигурации

Как было описано выше, для обработки устройством используются транзакции типа 0. Так как устройство однофункциональное, то номер функции — 000, который проверятся в управляющем автомате. В зависимости от номера регистра (биты 7..0 шины AD) на шину AD выдается нужный регистра, согласно рисунку выше.

Код чтения конфигурации

signal CfgRData:			std_logic_vector(31 downto 0):=x"00000000";
signal CommandReg:			std_logic_vector(15 downto 0) := x"0000";
signal StatusReg:			std_logic_vector(15 downto 0) := x"0200";
signal LatencyTimer:			std_logic_vector(7 downto 0) := x"00";
signal CacheLineSize:			std_logic_vector(7 downto 0) := x"00";
signal BAR0:					std_logic_vector(31 downto 0) := x"00000001";
signal BAR1:					std_logic_vector(31 downto 0) := x"00000000";
signal InterruptLine:			std_logic_vector(7 downto 0);
process (clk_I) begin
	if (rising_edge(clk_I)) then
		case (Address(7 downto 0)) is 
			when x"00"	=> CfgRData <= x"00017788" ; --Device ID and Vendor ID
			when x"04"	=> CfgRData <= StatusReg & CommandReg; --Status Register, Command Register
			when x"08"	=> CfgRData <= x"10000001"; -- Class Code and Revision ID
			when x"0C"	=> CfgRData <= x"0000" & LatencyTimer & CacheLineSize; -- BIST, Header Type(bit 7 = 0, single, bits 6-0 = 0, type0), Latency Timer(for masters), Cache Line Size (bit 2 in 1)
			when x"10"	=> CfgRData <= BAR0; -- Base Adress 0 (Register IO address decoder)
			when x"14"	=> CfgRData <= BAR1; -- Base Adress 1
			when x"28"	=> CfgRData <= x"00000000"; -- CarfdBus CIS Pointer
			when x"2C"	=> CfgRData <= x"00017788"; -- Subsystem ID, Subsystem Vendor ID
			when x"30"	=> CfgRData <= x"00000000"; -- Expanxion Rom Base Address
			when x"34"	=> CfgRData <= x"00000000"; -- Reserved, Capabilitis Pointer
			when x"38"	=> CfgRData <= x"00000000";	-- Reserved
			when x"3C"	=> CfgRData <= x"004001" & InterruptLine;	-- Max_Lat(only bus master), Min_Gnt, Interrupt Pin, Interrupt Line
			when others	=> CfgRData <= (others => '0');
		end case;	
	end if;
end process; 

Так выгядит чтение конфигурации в симуляторе:
Выполнение транзакций на шине PCI. Реализация на VHDL
Полное изображение

Запись конфигурации

На шине AD выставляет адрес регистра для записи, а в следующем такте выставляются данные, которые нужно записать. В BAR0 биты 7..0 являются read-only, в BAR1 биты 15..0 являются read-only. Поэтому адресов ввода/вывода 256, адресов памяти 4 294 967 296.

Код записи конфигурации

process(clk_I, RST_I) begin
	if(RST_I = '0')then
		CommandReg <= x"0000" after cTCQ;
		StatusReg <= x"0200" after cTCQ;
		LatencyTimer <= x"00" after cTCQ;
		CacheLineSize <= x"00" after cTCQ;
		BAR0 <= x"00000001" after cTCQ;
		BAR1 <= x"00000000" after cTCQ;
	elsif(rising_edge(clk_I)) then
		if (smPCI_T = sCFG_WRITE) then
			case(Address(7 downto 0)) is 
				when x"04"	=>	if (CBE_I(1) = '0') then	CommandReg(15 downto 8) <= AD_I(15 downto 8)	after cTCQ; end if;
								if (CBE_I(0) = '0') then	CommandReg(7 downto 0) <= AD_I(7 downto 0)		after cTCQ; end if;
				when x"0C"	=>	if (CBE_I(1) = '0') then	LatencyTimer <= AD_I(15 downto 8)				after cTCQ; end if;
								if (CBE_I(0) = '0') then	CacheLineSize <= AD_I(7 downto 0)				after cTCQ; end if;
				when x"10"	=>	if (CBE_I(3) = '0')	then 	BAR0(31 downto 24) <= AD_I(31 downto 24)		after cTCQ; end if;
								if (CBE_I(2) = '0')	then 	BAR0(23 downto 16) <= AD_I(23 downto 16)		after cTCQ; end if;
								if (CBE_I(1) = '0')	then 	BAR0(15 downto 8) <= AD_I(15 downto 8)			after cTCQ; end if;
				when x"14"	=>	if (CBE_I(3) = '0')	then 	BAR1(31 downto 24) <= AD_I(31 downto 24)		after cTCQ; end if;
								if (CBE_I(2) = '0')	then 	BAR1(23 downto 16) <= AD_I(23 downto 16)		after cTCQ; end if;
				when x"3C"	=>	if (CBE_I(0) = '0') then	InterruptLine <= AD_I(7 downto 0)				after cTCQ; end if;
				when others	=>	null;
			end case;
		end if;
	end if;
end process; 

Запись в порт

На шине AD выставляется номер регистра для записи, в следующем такте выставляются данные, которые нужно записать.
Приведем пример только для записи одного регистра, остальные записываются аналогично.

signal IOReg0:			std_logic_vector (31 downto 0);

process(clk_I, RST_I) begin
	if(RST_I = '0') then
		IOReg0 <= x"00000000" after cTCQ;
	elsif (rising_edge(clk_I)) then
		if (smPCI_T = sIO_WRITE and Address(7 downto 0) = x"00") then
			if (CBE_I(0) = '0') then	IOReg0( 7 downto  0) <= AD_I( 7 downto  0) after cTCQ; end if;
			if (CBE_I(1) = '0') then IOReg0(15 downto  8) <= AD_I(15 downto  8) after cTCQ; end if;
			if (CBE_I(2) = '0') then IOReg0(23 downto 16) <= AD_I(23 downto 16) after cTCQ; end if;
			if (CBE_I(3) = '0') then IOReg0(31 downto 24) <= AD_I(31 downto 24) after cTCQ; end if;
		end if;
	end if;
end process;

Чтение порта

На шине AD выставляется номер регистра, который нужно прочитать. Затем устройство выдает на шину AD запрашиваемые данные.

Код чтения порта

signal IORDate:			std_logic_vector (31 downto 0);

process (clk_I, RST_I) begin
	if (RST_I = '0') then
		IORDate <= x"00000000";
	elsif (rising_edge(clk_I)) then
			case (Address(7 downto 0)) is
				when x"00" =>	IORDate <= IOReg0 after cTCQ;
				when x"04" =>	IORDate <= IOReg1 after cTCQ;
				when x"08" =>	IORDate <= IOReg2 after cTCQ;
				when x"0C" =>	IORDate <= IOReg3 after cTCQ;
				when x"10" =>	IORDate <= IOReg4 after cTCQ;
				when x"14" =>	IORDate <= IOReg5 after cTCQ;
				when x"18" =>	IORDate <= IOReg6 after cTCQ;
				when x"1C" =>	IORDate <= IOReg7 after cTCQ;
				when x"20" =>	IORDate <= IOReg8 after cTCQ;
				when x"24" =>	IORDate <= IOReg9 after cTCQ;
				when others =>	IORDate <= (others => '0');
			end case;
	end if;
end process; 

Так выглядит запись и чтение порта ввода-вывода:
Выполнение транзакций на шине PCI. Реализация на VHDL
Полное изображение

Запись и чтение памяти

На шине AD выставляется адрес, по которому нужно записать данные, а в следующем такте сами данные. При чтении на шину AD выставляет адрес для чтения, затем на шину AD таргет выставляет сами данные.
Данные пишуется в RAM в порт А, читаются из порта B.

Код обращения к памяти

signal RamWrEn:		std_logic;
signal RamOutputDate:			std_logic_vector (31 downto 0);
signal RamInputDate:			std_logic_vector (31 downto 0);
signal RamRst:				std_logic := '0';

RAMB16_S36_S36_inst : RAMB16_S36_S36
port map (
	DOA	=> open,						-- Port A 32-bit Data Output
	DOB	=> RamOutputDate,			-- Port B 32-bit Data Output
	DOPA	=> open,					-- Port A 4-bit Parity Output
	DOPB	=> open,					-- Port B 4-bit Parity Output
	ADDRA	=> Address(8 downto 0),	-- Port A 9-bit Address Input
	ADDRB	=> Address(8 downto 0),	-- Port B 9-bit Address Input
	CLKA	=> clk_I,					-- Port A Clock
	CLKB	=> clk_I,					-- Port B Clock
	DIA	=> RamInputDate,			-- Port A 32-bit Data Input
	DIB	=> x"00000000",			-- Port B 32-bit Data Input
	DIPA	=> x"0",					-- Port A 4-bit parity Input
	DIPB	=> x"0",					-- Port-B 4-bit parity Input
	ENA	=> '1',					-- Port A RAM Enable Input
	ENB	=> '1',					-- PortB RAM Enable Input
	SSRA	=> '0',					-- Port A Synchronous Set/Reset Input
	SSRB	=> '0',					-- Port B Synchronous Set/Reset Input
	WEA	=> RamWrEn,		-- Port A Write Enable Input
	WEB	=> '0'					-- Port B Write Enable Input
);

process(clk_I) begin
	if (rising_edge(clk_I)) then
		if (RST_I = '1') then
			RamRst <= '0';
		else
			RamRst <= '1';
		end if;
	end if;
end process;

process(clk_I, RST_I) begin
	if(RST_I = '0') then
		RamInputDate <= (others => '0') after cTCQ;
		RamWrEn <= '0' after cTCQ;
	elsif (rising_edge(clk_I)) then
		if (smPCI_T = sMEM_WRITE) then
			if (CBE_I(0) = '0') then RamInputDate(7  downto  0) <= AD_I( 7 downto  0) after cTCQ; end if;
			if (CBE_I(1) = '0') then RamInputDate(15 downto  8) <= AD_I(15 downto  8) after cTCQ; end if;
			if (CBE_I(2) = '0') then RamInputDate(23 downto 16) <= AD_I(23 downto 16) after cTCQ; end if;
			if (CBE_I(3) = '0') then RamInputDate(31 downto 24) <= AD_I(31 downto 24) after cTCQ; end if;
			RamWrEn <= '1' after cTCQ;
		else 
			RamWrEn <= '0' after cTCQ;
		end if;
	end if;
end process; 

Так выглядит запись и чтение памяти в симуляторе:
Выполнение транзакций на шине PCI. Реализация на VHDL
Полное изображение

Данные на шину AD выводятся следующим образом. В зависимости от состояния автомата, к выходному буферу подключается соответствующий регистр.

process (clk_I, RST_I) begin
	if (RST_I = '0') then
		AD_O <= (others => '0') after cTCQ;
	elsif (rising_edge(clk_I)) then
		if (smPCI_T = sCFG_READ) then
			AD_O <= CfgRData after cTCQ;
		elsif (smPCI_T = sIO_READ) then
			AD_O <= IORDate after cTCQ;
		elsif (smPCI_T = sMEM_READ) then
			AD_O <= RamOutputDate after cTCQ;
		end if;
	end if;
end process;

Сигнал разрешения выдачи данных на шину AD формируется следующим образом:

process (clk_I, RST_I) begin
	if (RST_I = '0') then
		AD_T <= '1' after cTCQ;
	elsif (rising_edge(clk_I)) then
		AD_T <= not b2l(smPCI_T = sCFG_READ or smPCI_T = sIO_READ or smPCI_T = sMEM_READ) after cTCQ;
	end if;
end process;

Заключение

В заключение хочу сказать, что выполнение транзакций на шине PCI не так сложно как кажется. Разработанная прошивка была залита в ПЛИС. Плата с ПЛИС вставлена в PCI слот и был включен компьютер. Система нашла плату и запросила драйвера на нее.
Выполнение транзакций на шине PCI. Реализация на VHDL
Выполнение транзакций на шине PCI. Реализация на VHDL
Работает! :)

Сам проект rghost.ru/46686218. Открывать xilinx ise 14.2.

Автор: Fenja

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js