MIPSfpga микропроцессор MIPS32 microAptiv описаный на языке Verilog для образовательных целей фирмы Imagination, который имеет кэш-память и блок управления памятью. Код процессора доступен пользователю (инструкция по скачиванию) и может использоваться для моделирования и реализации процессора на FPGA плате.

Данная статья является продолжением статьи о том как портировать MIPSfpga-plus на другие платы, и в ней будет описано как интегрировать периферию в систему MIPSfpga-plus на примере 16 кнопочной клавиатуры Digillent Pmod KYPD:



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

Интеграция встроенного в плату АЦП, а так же подключения дисплея от Nokia 5100 описана в следующей части моего tutorial:

Портирование MIPSfpga на другие платы и интеграция периферии в систему. Часть 3

Так же о том как начать работать с MIPSfpga написано в статье.

Архитектура MIPS является одной из первых архитектур компьютеров с сокращенным набором команд (RISC). Она появилась в результате исследований в Стэнфордском университете в 1981 году и революционно увеличила эффективность компьютерных архитектур. Коммерческое применение архитектуры MIPS началось в 1984 году фирмой 2 MIPSfpga: Начало работы Imagination Technologies v1.3, 1 марта, 2016 MIPS Computer Systems, которая в 2013 году была приобретена компанией Imagination Technologies.

Процессоры MIPS использовались в высокопроизводительных рабочих станциях Silicon Graphics, которые производились в 1980 и 1990 годах. Первым коммерчески успешным продуктом был процессор MIPS R3000, который имел пятистадийный конвейер. За ним последовал R4000, в котором были добавлены 64-разрядные команды, суперскалярный R8000, процессор с внеочередным выполнением команд R10000 и множество других высокопроизводительных ядер.

Со временем архитектура MIPS эволюционировала для применений в недорогих изделиях с малым энергопотреблением, таких как бытовые электронные устройства, сетевое оборудование, микроконтроллеры. Семейство M4K базируется на классической 32-разрядной архитектуре с пятистадийным конвейером. В семействе M14K добавлен набор 16-разрядных инструкций microMIPS, что позволяет уменьшить размер программ для встраиваемых приложений, в которых очень важна стоимость. В семействе microAptiv набор инструкций M14K расширен дополнительными инструкциями цифровой обработки сигналов. Ядро M14K существует в двух вариантах: микроконтроллерном (UC) и микропроцессорном (UP). Микропроцессорный вариант содержит кэш-память и поддерживает виртуальную память, что обеспечивает возможность запуска операционной системы, такой как Linux или Android. Возможно, вы знакомы с популярной линейкой микроконтроллеров PIC32 фирмы Microchip, которая основана на архитектуре M4K.

Процессор использует интерфейс памяти для связи с периферийными устройствами. То есть, это означает что данные записываются и считываются с подключенной периферии так же, как и с блока памяти RAM. Интеграция периферии в процессор осуществляется подключением к шине AHB-Lite (подробная документация). Подробней попробуем разобраться в процессе подключения.

Для начала нужно иметь понятие как будут проходить сигналы по шине AHB-Lite:


Видно, что процесс считывания данных с периферии осуществляется по сигналу HRDATA, передача данных производится по HWRITE с активным высоким уровнем на сигнале разрешения записи, выбор GPIO осуществляется выбором адреса на HADDR.

1. Подключение клавиатуры Digilent Pmod KYPD


Первым датчиком который был п��дключён это 16 кнопочный pmod KYPD (даташит).



В моем случае для подключения клавиатуры был использован интерфейс Pmod, но можно использовать любые другие пины вашей платы, но тогда в файле с ограничениями *.xdc нужно будет их прописать. К каким пинам подключать клавиатуру описано ниже в таблице.

Распиновка и сигналы клавиатуры описаны в таблице:
Пин Сигнал Назначение Пин Сигнал Назначение
1 Col4 4 колонка 7 ROW4 4 ряд
2 Col3 3 колонка 8 ROW3 3 ряд
3 Col2 2 колонка 9 ROW2 2 ряд
4 Col1 1 колонка 10 ROW1 1 ряд
5 GND контакт земли 11 GND контакт земли
6 GND контакт питания 12 VCC контакт питания

Схема подключения клавиатуры очень простая:

Следующим шагом для интеграции клавиатуры к шине AHB-lite будет написание модуля на Verilog.
Клавиатура использует 4 ряда и столбца для создания массива з 16 тактовых кнопок.

На схеме показано что ряды подтянуты к питанию сопротивлениями в R=10к, это значит что путем приведения в действие линии столбцов на низкий логический уровень подается тактовый сигнал с определенной частотой. В момент когда активный сигнал на входе совпадёт с колонкой (col) на которой нажата кнопка на выходе рядка появится низкий уровень. Для реализации такого процесса нужно написать дешифратор (пример реализации модуля так же описан в данной статье).



Сам модуль имеет вид:

module kypd_decoder(
                input 		      i_clk,
                input 		      i_rst_n,
                input          [3:0]  i_row,

                output reg     [3:0]  o_col,
                output reg     [3:0]  o_number
              );
	  
                reg            [19:0] counter;
                reg            [3:0]  col;
                reg            [3:0]  row;
                
                        //  row col
parameter   ZERO       = 8'b11100111,
            ONE        = 8'b01110111,
            TWO        = 8'b01111011,
            THREE      = 8'b01111101,
            FOUR       = 8'b10110111,
            FIVE       = 8'b10111011,
            SIX        = 8'b10111101,
            SEVEN      = 8'b11010111,
            EIGHT      = 8'b11011011,
            NINE       = 8'b11011101,
            A          = 8'b01111110,
            B          = 8'b10111110,
            C          = 8'b11011110,
            D          = 8'b11101110,
            E          = 8'b11101101,
            F          = 8'b11101011;


always @(posedge i_clk or negedge i_rst_n)
	if (i_rst_n == 0)
	   counter <= 20'b0;	   
	else
	   counter <= counter + 1'b1;


always @(posedge i_clk or negedge i_rst_n)
	if (i_rst_n == 1'b0) begin
	   
	   o_col <= 4'b1110;
	   col 	 <= 4'b1110;
	   row   <= 4'b1111;
	
	end else if (!counter) begin
    
       o_col <= {o_col [0], o_col [3:1]};
       col   <= o_col;
       row   <= i_row;
	
	end

always @(posedge i_clk or negedge i_rst_n)
	if (i_rst_n == 0)
		o_number <= 4'b0;
	else
		case ({row, col})
	
			ZERO:    o_number <= 4'h0;
			
			ONE:     o_number <= 4'h1;
			
			TWO:     o_number <= 4'h2;
			
			THREE:   o_number <= 4'h3;
			
			FOUR:    o_number <= 4'h4;
			
			FIVE:    o_number <= 4'h5;
			
			SIX:     o_number <= 4'h6;
			
			SEVEN:   o_number <= 4'h7;
			
			EIGHT:	 o_number <= 4'h8;
			
			NINE:	 o_number <= 4'h9;
			
			A:	 o_number <= 4'hA;
			
			B:	 o_number <= 4'hB;
			
			C:	 o_number <= 4'hC;
			
			D:       o_number <= 4'hD;
			
			E:       o_number <= 4'hE;
			
			F:       o_number <= 4'hF;
	
	
		endcase
		
endmodule

Схема модуля в Vivado будет иметь вид:



Если кратко, то добавить модуль дешифратора в проект Vivado нужно так: Add Sources → Add or create design sources → Next → Create File → (написать имя файла) → Ok → Ok → Yes. Создан пустой Verilog файл, после создания файла, нужно его найти в иерархии системы MIPSfpga-plus, написать код дешифратора и сохранить. Более подробное описание как добавить модуль в проект и просто как работать с Vivado описано в предыдущей моей статье:

Портирование MIPSfpga на другие платы и интеграция периферии в систему. Часть 1

Теперь приступим к подключению входов и выходов к шине AHB-Lite и физическим выходам нашей платы.

Иерархия mipsfpga_ahb имеет вид:



Для начала в директории Verilog Header mfp_ahb_lite_matrix_config.vh пропишем с помощью директивы `define название периферии которую будем подключать. Для этого найдем строку с идентификатором добавления в систему датчика освещённости (подробнее о датчике описано в предыдущей статье):

Строку нужно закомментировать, это позволит выключить все строки которые связаны с кодом подключения датчика освещённости к шине AHB-Lite:

//`define MFP_DEMO_LIGHT_SENSOR

Пропишем и расскоментируем строку для нащей периферии:

`define MFP_PMOD_KYPD

Откроем «mfp_system» найдем строки подключения екземпляра датчика освещенности:

`ifdef MFP_DEMO_LIGHT_SENSOR

И рядом добавим екземпляр своего модуля дешифратора:

`ifdef MFP_PMOD_KYPD
      kypd_decoder kypd_decoder
                      (                  
                          .i_clk    (   SI_ClkIn         ),
                          .i_rst_n  (   KEY_0            ),
                          .o_col    (   KYPD_DATA [3:0]  ),
                          .i_row    (   KYPD_DATA [7:4]  ),   
                          .o_number (   KYPD_OUT         )
                      );
`endif

Сигналы модуля дешифратора нужно подключить к екземпляру шины mfp_ahb_lite_matrix_with_loader (где прописывать нужные строчки можно смотреть по примеру интеграции датчика освещённости с помощью поиска по модулю MFP_DEMO_LIGHT_SENSOR):

`ifdef MFP_PMOD_KYPD
            .KYPD_OUT    (    KYPD_OUT      ),    
`endif

Для соединения экземпляров модуля дешифратора и шины добавим сигнал типа wire:

`ifdef MFP_PMOD_KYPD
            wire [3:0] KYPD_OUT;
`endif

После подключения нашего модуля дешифратора к шине перейдём в «mfp_ahb_lite_matrix_with_loader» который находится по иерархии ниже модуля «mfp_system» и добавим порт ввода/вывода:

`ifdef MFP_PMOD_KYPD
             input [3:0] KYPD_OUT,
`endif

Так же добавим эти сигналы в екземпляр «mfp_ahb_lite_matrix»:

`ifdef MFP_PMOD_KYPD
            .KYPD_OUT    (    KYPD_OUT      ),    
`endif

Те же действия проделаем в «mfp_ahb_lite_matrix» который находится по иерархии ниже модуля «mfp_ahb_lite_matrix_with_loader» и добавим порт ввода/вывода:

`ifdef MFP_PMOD_KYPD
             input [3:0] KYPD_OUT,
`endif

Так же добавим эти сигналы в екземпляр «mfp_ahb_gpio_slave»:

`ifdef MFP_PMOD_KYPD
            .KYPD_OUT    (    KYPD_OUT      ),    
`endif

Перейдем в модуль «mfp_ahb_gpio_slave», это именно тот блок (GPIO) в который мы так рвались, добавим порт ввода/вывода:

`ifdef MFP_PMOD_KYPD
             input [3:0] KYPD_OUT,
`endif

Теперь следует изменить модуль «mfp_ahb_gpio_slave» так, чтобы он обнаруживал адрес ввода/вывода с отображением в память (которые мы ещё определим) и записывал данные (HWDATA) в соответствующий обнаруженному адресу регистр:

`ifdef MFP_PMOD_KYPD
            `MFP_PMOD_KYPD_IONUM  : HRDATA <= { 28'b0,    KYPD_OUT };
`endif

Чтобы указать адрес по которому мы будем обращаться, откроем заголовочный файл «mfp_ahb_lite_matrix_config.vh» и добавим:

`ifdef MFP_PMOD_KYPD
`define MFP_PMOD_KYPD_ADDR          32'h1f800018
`endif

Модуль GPIO использует младшие биты адреса, чтобы определить, с каким периферийным устройством следует осуществлять операцию чтения или записи информации.

`ifdef MFP_PMOD_KYPD            
`define MFP_PMOD_KYPD_IONUM         4'h6
`endif

Вернемся назад по иерархии в модуль «mfp_system» и добавим порты ввода/вывода:

     `ifdef MFP_PMOD_KYPD
     inout [7:0] KYPD_DATA,
     input KEY_0,
    `endif

Стоит заметить что это уже не совсем те порты которые мы подключали к шине. Перейдем в топ модуль оболочку (у меня «cmoda7») и добавим в екземпляр «mfp_system» строчки:

        `ifdef MFP_PMOD_KYPD
    	.KYPD_DATA		  (     JA           ),
	.KEY_0                    (     ~ i_btn1     ),
        `endif

Таким образом мы добавили к JA[7:0] выводам модуль нашего декодера и соединили декодер с процессором по шине AHB-Lite.

В нашем случае порты ввода/вывода JA в модуле оболочке и в файле ограничений (у меня «cmoda7.xdc») добавлять не потребуется так как они уже использовались для датчика освещённости. Но в д��угих случаях такие действия потребуются, потому для понимания я просто покажу эти строки:

module cmoda7
(
...
...
...
   inout  [ 7:0] JA
);

cmoda7.xdc
## Pmod Header JA
set_property -dict {PACKAGE_PIN G17 IOSTANDARD LVCMOS33} [get_ports {JA[0]}]
set_property -dict {PACKAGE_PIN G19 IOSTANDARD LVCMOS33} [get_ports {JA[1]}]
set_property -dict {PACKAGE_PIN N18 IOSTANDARD LVCMOS33} [get_ports {JA[2]}]
set_property -dict {PACKAGE_PIN L18 IOSTANDARD LVCMOS33} [get_ports {JA[3]}]
set_property -dict {PACKAGE_PIN H17 IOSTANDARD LVCMOS33} [get_ports {JA[4]}]
set_property -dict {PACKAGE_PIN H19 IOSTANDARD LVCMOS33} [get_ports {JA[5]}]
set_property -dict {PACKAGE_PIN J19 IOSTANDARD LVCMOS33} [get_ports {JA[6]}]
set_property -dict {PACKAGE_PIN K18 IOSTANDARD LVCMOS33} [get_ports {JA[7]}]


И если мы вспомним в «mfp_ahb_lite_matrix_config.vh» мы закомментировали строку:

//`define MFP_DEMO_LIGHT_SENSOR

тем самым при синтезе системы весь код прописаный между

`ifdef MFP_DEMO_LIGHT_SENSOR
...
`endif 

будет игнорироваться, и конфликтов использования портов не будет.

Мы можем посмотреть схему созданой системы MIPSfpga-plus со встроенным дешифратором для нашей клавиатуры, для этого откройте во вкладке RTL Analysys → Open Elaborated Design → Schematic. Здесь отображается вся схема системы, чтобы проверить правильность подключёного модуля дешифратора желательно пройтись по RTL Netlist и проверить все контакты.

Теперь можно сгенерировать bitstream файл (.bit) и загрузить в FPGA.

Напишем простую программу для взаимодействия клавиатуры и процессора.

Для загрузки кода в систему нужно перейти в папку скачаного mipsfpga plus → github → mipsfpga-plus → programs → 01_pmod_kypd откроем «mfp_memory_mapped_registers.h»

#define MFP_PMOD_KYPD_ADDR      0xBF800018
и
#define MFP_PMOD_KYPD           (* (volatile unsigned *) MFP_PMOD_KYPD_ADDR     )

далее откроем main.c и напишем пару строк для демонстрации:

#include "mfp_memory_mapped_registers.h"

int main ()
{
    int n = 0;

    for (;;)
    {
        MFP_7_SEGMENT_HEX = MFP_PMOD_KYPD;
    }

    return 0;
}

После в папке находим скрипт который компилирует код:

02_compile_and_link

Генерируем motorola_s_record файл:

08_generate_motorola_s_record_file

Проверяем к какому СОМ порту подключен USB UART преобразователь:

11_check_which_com_port_is_used

Изменяем файл 12_upload_to_the_board_using_uart:

set a=7 
mode com%a% baud=115200 parity=n data=8 stop=1 to=off xon=off odsr=off octs=off dtr=off rts=off idsr=off type program.rec >\.\COM%a%

где а – номер СОМ порта, к которому подключен USB UART преобразователь. И загружаем программу:

12_upload_to_the_board_using_uart

Результат:


В следующей части расскажу как добавить в MIPSfpga встроеный в cmoda7 АПЦ, и LCD дисплей от Nokia 5100.

Портирование MIPSfpga на другие платы и интеграция периферии в систему. Часть 3