Для каждой из наших FPGA плат Марсоход, Марсоход2, Марсоход3 мы всегда делали первый проект очень простым.
Это всегда было что-то вроде "моргания светодиодами". Как правило в таком проекте нет ничего особенного, однако, польза простого проекта очевидна:
- в нем легко разобраться даже новичку;
- его легко исправлять, наращивая функциональность проекта, с ним просто экспериментировать;
- в нем уже сделаны все важные назначения сигналов проекта на конкретные выводы микросхемы ПЛИС.
Конечно, для новой платы Марсоход2RPI тоже нужен такой простой проект.
Так сделаем его!
Код проекта на github: https://github.com/marsohod4you/m2rpi_first
Топ модуль проекта напишем на Verilog. В нем будет описан сам модуль и его сигналы: входной сигнал OSC из генератора 100МГц на плате, три кнопки [2:0]KEY, разъемы [27:0]GPIO_A и [27:0]GPIO_B и сигналы разъем, ведущего к Raspberry Pi3:
module m2rpi(
input wire OSC,
input wire [2:0]KEY,
output wire [3:0]LED,
inout wire [27:0]GPIO_A,
inout wire [27:0]GPIO_B,
//Raspberry GPIO pins
input wire GPIO0,
input wire GPIO1,
input wire GPIO2,
input wire GPIO3,
input wire GPIO4,
input wire GPIO5,
input wire GPIO6,
input wire GPIO7,
input wire GPIO8,
input wire GPIO9,
input wire GPIO10,
input wire GPIO11,
input wire GPIO12,
input wire GPIO13,
input wire GPIO14, //Serial RX
output wire GPIO15, //Serial TX
input wire GPIO16,
input wire GPIO17,
input wire GPIO18,
input wire GPIO19,
input wire GPIO20,
input wire GPIO21,
input wire GPIO22,
input wire GPIO23,
input wire GPIO24,
input wire GPIO25,
input wire GPIO26,
input wire GPIO27
);
Из среды САПР Quartus Prime создаю нужный мне PLL через меню Tools => IP Catalog и так далее..
Экземпляр PLL вставляю в текст моей "программы":
wire w_clk;
wire w_clk1;
wire w_locked;
pll my_pll_inst(
.inclk0(OSC ),
.c0( w_clk ),
.c1( w_clk1 ),
.locked( w_locked )
);
Вот ниже двоичный счетчик, который будет увеличиваться по каждому такту рабочей частоты w_clk, которая идет из PLL
По нажатию на плате кнопки KEY[0] счетчик будет сбрасываться, а по нажатию кнопки KEY[1] будет останавливаться счет.
reg [31:0]counter;
always @( posedge w_clk )
begin
if( KEY[0]==1'b0 )
counter <= 0;
else
if( KEY[1]==1'b1 )
counter <= counter+1;
end
Чтобы хоть как-то усложнить проект, я вставлю в него экземпляр модуля приемника последовательного порта. Плата Марсоход2RPI подключается к Raspberry Pi3 специальным переходником. Сигнал GPIO14 с разъема Raspberry может действовать как линия передачи данных в нашу плату. Прием данных осуществляется на стандартной скорости 115200бод, то есть бит в секунду. Параметры передачи 8Бит данных, без четности и один стоп бит.
wire [7:0]rx_byte;
wire w_rbyte_ready;
serial my_serial_inst(
.reset( ~w_locked ),
.clk100( w_clk ),
.rx( GPIO14 ),
.rx_byte( rx_byte ),
.rbyte_ready( w_rbyte_ready )
);
Тут еще добавлю немного хитрой логики.
Можно было бы конечно, просто отправлять принятый байт назад в распберри, но какой в этом смысл? Да для этого вообще принимать не нужно было бы, можно было бы просто соединить внутри моего ПЛИС проекта сигналы RX и TX и возвращался бы принятый байт. Нет, я же хочу, чтобы плата с ПЛИС была некоторым центром обработки данных. Чтобы Raspberry передавала в ПЛИС что-то для рассчета, ПЛИС бы считала и отправляла назад.
Поскольку это все таки первый простой проект, то весь рассчет будет очень примитивным: байт принимается, к нему прибавляется единица и он отправляется назад. Конечно, это скорее всего никому не нужно, по крайней мере демонстрирует идею "сопроцессора ПЛИС" к Raspberry.
Демонстрировать эту "обработку данных" проще всего если подключить потом к последовательному порту программу терминала. Буду нажимать клавишу "1", а будет отображаться "2". Нажму "А", но появится "В".
//registered delay of w_rbyte_ready impulse
reg [1:0]r_rbyte_ready;
always @( posedge w_clk )
r_rbyte_ready <= { r_rbyte_ready[0], w_rbyte_ready };
//fix received serial byte into register
reg [7:0]r_rx_byte;
always @( posedge w_clk )
if( w_rbyte_ready )
r_rx_byte <= rx_byte;
//modify received byte +1 and fix into register
reg [7:0]r_rx_byte_1;
always @( posedge w_clk )
if( r_rbyte_ready[0] )
r_rx_byte_1 <= r_rx_byte+1'b1;
А вот и экземпляр модуля передатчика последовательного порта. Он отправляет уже модифицированный байт в распберри через линию GPIO15.
//serial send to raspberry modified byte
tx_serial my_tx_serial_inst(
.reset( ~w_locked ),
.clk100( w_clk ),
.sbyte( r_rx_byte_1 ),
.send( r_rbyte_ready[1] ),
.tx( GPIO15 ), //raspberry serial TX
.busy()
);
Ну и в конце еще немного хитрости в мой простой проект. На светодиоде я буду отображать четыре бита двоичного счетчика, но какие именно четыре бита - это будет определяться принятым из последовательного порта байтом.
Здесь анализируются только два младших бита принятого байта. Если они разны нулю, то на светодиодах будет самый быстрый счет, если они равны 2'b11, то самый медленный. Я буду в последовательный порт отправлять символы с клавиатуры 0, 1, 2, 3. Им соответствуют коды 0x30, 0x31, 0x32, 0x33 и моргание светодиодов от этого будет быстрое или медленное.
assign LED = r_rx_byte[1:0]==2'b00 ? counter[27:24] :
r_rx_byte[1:0]==2'b01 ? counter[26:23] :
r_rx_byte[1:0]==2'b10 ? counter[25:22] : counter[24:21];
endmodule
Если такой проект откомпилировать в среде Quartus и загрузить в ПЛИС платы Марсоход2RPI, то уже можно сразу убедиться в его работоспособности: сразу должны моргать светодиоды отображая двоичный счет. По нажатию кнопок платы счетчик либо будет сбрасываться или счет будет приостанавливаться.
Теперь перейдем к работе с последовательным портом в распбери. Казалось бы нет ничего проще.. но однако же и тут могут ожидать некоторые препятствия.
Во-первых, последовательный порт уже может быть под управлением других программ, например, им может владеть serial-getty.
Во-вторых, многие выводы GPIO разъема распбери имеют различную функциональность, те же самые выводы разъема TX/RX могут быть использованы просто как GPIO. Как все это отконфигурировать? Вот простая инструкция:
- Запустите raspi-config и в пункте меню Advanced => Serial выбирайте Disable. После этого потребуется перезагрузить распберри. После перезагрузки убедитесь, что файл /boot/cmdline.txt выглядит как-то вот так:
dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
но не вот так:
dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
Не должно быть упоминания ttyAMA0.
- Далее убедитесь, что файл /boot/config.txt содержит строку
enable_uart=1
Если ее нет, то придется отредактировать /boot/config.txt и добавить туда эту строку enable_uart=1
- Нужно убедиться, что сервис getty не использует последовательный порт. Команда
>ps aux | grep tty
выведет список всех работающих процессов, у которых в командной строке встречается слово tty. Проверьте, чтоб getty не использовало ttyAMA0 или ttyS0 (это еще зависит какая у вас распберри, может Raspberry Pi2, на ней почему-то появляется порт ttyS0).
Выключить сервис getty можно командой
>sudo systemctl mask Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра.
- можно убедиться, что все готово к работе командой
>gpio readall
Эта команда в Raspberry вообще полезна, отображает состояния всех пинов на разъеме:
+-----+-----+---------+------+---+-Model B2-+---+------+---------+-----+-----+
| BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM |
+-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
| | | 3.3v | | | 1 || 2 | | | 5v | | |
| 2 | 8 | SDA.1 | IN | 1 | 3 || 4 | | | 5V | | |
| 3 | 9 | SCL.1 | IN | 1 | 5 || 6 | | | 0v | | |
| 4 | 7 | GPIO. 7 | IN | 1 | 7 || 8 | 1 | ALT0 | TxD | 15 | 14 |
| | | 0v | | | 9 || 10 | 1 | ALT0 | RxD | 16 | 15 |
| 17 | 0 | GPIO. 0 | IN | 0 | 11 || 12 | 0 | IN | GPIO. 1 | 1 | 18 |
| 27 | 2 | GPIO. 2 | IN | 0 | 13 || 14 | | | 0v | | |
| 22 | 3 | GPIO. 3 | IN | 0 | 15 || 16 | 0 | IN | GPIO. 4 | 4 | 23 |
| | | 3.3v | | | 17 || 18 | 0 | IN | GPIO. 5 | 5 | 24 |
| 10 | 12 | MOSI | IN | 0 | 19 || 20 | | | 0v | | |
| 9 | 13 | MISO | IN | 0 | 21 || 22 | 0 | IN | GPIO. 6 | 6 | 25 |
| 11 | 14 | SCLK | IN | 0 | 23 || 24 | 1 | IN | CE0 | 10 | 8 |
| | | 0v | | | 25 || 26 | 1 | IN | CE1 | 11 | 7 |
+-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
| 28 | 17 | GPIO.17 | IN | 0 | 51 || 52 | 0 | IN | GPIO.18 | 18 | 29 |
| 30 | 19 | GPIO.19 | IN | 0 | 53 || 54 | 0 | IN | GPIO.20 | 20 | 31 |
+-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
| BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM |
+-----+-----+---------+------+---+-Model B2-+---+------+---------+-----+-----+
Нужно убедиться, что выводы соответствующие RxD и TxD находятся в режиме ALT0.
Режим работы отдельного контакта GPIO Raspberry Pi3 можно менять командой:
>gpio mode 14 ALT0
>gpio mode 15 ALT0
Вот теперь, когда последовательный порт сконфигурирован, можно пробовать посылать символы или байты в порт в плату ПЛИС Марсоход2RPI.
Демонстрация:
Я использую программу Putty. Открываю порт ttyAMA0 на скорости 115200 и набираю текст.
Как и ожидалось, при нажатии на клавишу "1" терминал показывает "2"!
Значит проект в ПЛИС успешно принимает байты, модифицирует их и отправляет назад.
Подробнее...