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

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

В даной статье будет описано на примере Digilent cmodA7 как портировать процессор MIPSfpga-plus на другие платы.



На сегодняшний день MIPSFPGA портирован на популярные платы таких фирм как ALTERA и Xilinx, среди них Basys 3, Nexys4 ddr, и другие (полный список находится на github). Такие платы наиболее популярны среди разработчиков на FPGA. Цена на такие платы довольно не маленькая, да и загружаются программы в ядро MIPSfpga с использованием интерфейса EJTAG и адаптера Bus Blaster ценой около 50$. Адаптер Bus Blaster получает команды по высокоскоростному кабелю USB 2.0 и преобразует их в последовательный протокол EJTAG, это позволяет загружать программы в ядро MIPSfpga и управлять отладкой программ, которые на нем выполняются. Проблема с относительно дорогим Bus Blaster была решена введением в систему MIPSfpga ряда улучшений. Улучшеный вариант системи MIPSfpga, названый MIPSfpga-plus включает в себя такие новые функции:

— Возможность загрузки программного обеспечения с использованием USB-to-UART коннектора ценой в $ 5 FTDI вместо $ 50 Bus Blaster, который иногда не так уж и легко достать.

— Возможность изменять тактовую частоту на лету с 50 или 25 МГц до 1 Гц (один цикл в секунду) для наблюдения за работой процессора в режиме реального времени, включая промахи в кэш-памяти и перенаправления конвеера.

— Пример интеграции датчика освещенности с протоколом SPI.

— Небольшая последовательность инициализации программного обеспечения, которая вписывается в 1 КБ вместо 32 КБ памяти, что позволяет переносить MIPSfpga на более широкий выбор плат FPGA без использования внешней памяти. Реализация UART описана в статье: MIPSfpga и UART.

Создание проекта в Xilinx Vivado 2016.4 (64 bit) с MIPSfpga-plus


Перед тем как добавлять различную периферию в MIPSfpga+ нужно загрузить и запустить систему на FPGA. Некоторые шаги данной инструкции можно будет пропустить если у вас есть в наличии плата из списка ниже:
Портированые платы
basys3
de0
de0_cv
de0_nano
de1
de10_lite
de2_115nexys4
nexys4_ddr

На эти платы MIPSfpga-plus уже портирован и скачать его можно по ссылке:

github.com/MIPSfpga/mipsfpga-plus/tree/master/boards

В другом случае, если у вас отличная от приведённых в списке FPGA плата, портировать на неё процессор не составит большого труда. Для этого написана следующая инструкция которая приводится на примере моей платы cmodA7.

1. Для начала нужно получить пакет c системой MIPSFPGA. Подробная инструкция о том как это сделать:

mipsfpga-download-instruction

Также вам потребуется скачать актуальную версию расширения MIPSfpga-plus:

Mipsfpga-plus

2. Для плат фирмы Xilinx используется программное обеспечение Vivado, актуальную версию можно скачать по ссылке:

Download Vivado

3. Теперь давайте создадим новый проект в Vivado:



Выбираем где будет хранится созданный проект:



Жмем далее:



Выбираем RTL проект и жмем далее:



Теперь добавим файлы системы оригинального MIPSfpga:





А так же MIPSfpga-plus которые находятся в архиве скачаном с mipsfpga-plus:



Добавим в проект модуль uart о котором ранее шла речь:



Пропускаем создание IP и файлов констрейнов создадим их позже:





Далее нужно либо выбрать микросхему либо добавить вашу плату.

Плата Digilent cmod A7 была выбрана в связи с ее не большой ценой и наличием АЦП которое мы потом используем. Чтобы выбрать FPGA схему нужно прочитать на вашу плату документацию, на cmodA7 размещен чип xc7a35tcpg236-1:



Другим способом является добавление файла вашей платы в библиотеку Vivado, этот способ лучше тем что, во первых, запомнить название платы удобней чем чипа, во вторых если вы в будущем захотите использовать block designer в Vivado, то у вас будет дополнительный инструментарий для удобной работы с интерфейсами платы, IP ядрами, и т.д. Скачать их можно на GitHub, сохранить эти файлы нужно в ~\Vivado\2015.1\data\boards\ (актуально для Vivado 2015.1 и новее).





Вот мы и создали проект с MIPSfpga-plus.

Инструкция по портированию на примере платы Digilent CmodA7 на базе микросхемы Artix-7


Если вкратце то на борту платы cmodA7 находится чип семейства Artix-7, имеет 20800 LUT, 41600 FF, 225 KB блочной памяти, имеется 48 пинов, среди которых 2 является выходами АЦП, также имеется usb-uart преобразователь, Quad-SPI Flash, и JTAG, 2 тактовые кнопки, а также 5 светодиодов 3 из которых это RGB (даташит).

Чтобы перенести систему MIPSfpga на плату cmodA7, следует:

Шаг 1. Написать модуль-оболочку, который устанавливает соответствие между вводами/выводами MIPSfpga и вводами/выводами платы cmodA7.
Шаг 2. Уменьшить объем памяти системы MIPSfpga для соответствия плате cmodA7.
Шаг 3. Добавить файл ограничений, который устанавливает соответствие между вводами/выводами платы и выводами корпуса FPGA.

Шаг 1. Добавим новый файл оболочки cmoda7.v
















Установим cmoda7.v топ модулем в иерархии системы.



Так как на плате cmoda7 нет переключателей которые можно было бы использовать для понижения частоты процессора (эта модификация появилась в MIPSfpga-plus), этот модуль просто не был включен в модуль оболочку. Так же можно исключить выход EJTAG к которому подключается BUS Blaster.

Для начала нужно добавить заголовочный файл конфигурации шины AHB_lite.

`include "mfp_ahb_lite_matrix_config.vh"

Усовершенствованная высокопроизводительная шина (Advanced High-performance Bus, AHB) является открытым интерфейсом, который используется во многих микропроцессорных системах, особенно во встроенных. Шина AHB облегчает организацию соединения нескольких устройств. AHB-Lite является упрощенной версией AHB с одним ведущим устройством.



Эта конфигурация имеет одно ведущее устройство, процессор MIPSfpga, и три ведомых: RAM0, RAM1 и GPIO, которые представляют собой (соответственно) два блока ОЗУ и модуль доступа к устройствам ввода/вывода платы. Мы как раз и будем работать с блоком GPIO и писать модули взаимодействия с периферией.

Пропишем основные порты ввода-вывода:

module cmoda7
              (
                  input               i_clk,
                  input               i_btn0, 
                  input               i_btn1,
                  input               RsRx,

                  output [ 6:0]       seg,   
                  output              dp,
                  output [ 3:0]       an,
                  output              led0_r,
                  output              led0_g,
                  output              led0_b,

                  inout  [ 7:0]       JA
                  
               );

i_clk — сигнал тактового генератора платы частотой (для cmodA7 12 МГц).
i_btn0 — кнопка используется для перезагрузки процессора.
i_btn1 — n/c.
RsRx — сигнал приема по uart.
seg, dp, an, — контакты для подключения семисегментного 4 разрядного индикатора.
led_r, led_g, led_b, — выводы размещённого на плате RGB Led.
JA — Pmod интерфейс.

Основными сигналами являются сигнал тактовой частоты и reset (i_btn0) остальные для подключения периферии.



Основной целью модуля cmoda7.v является создание экземпляра системы MIPSfpga-plus (mipsfpga_sys) и ее соединение с вводами/выводами платы.


    wire clock;
    wire reset = i_btn0;
    
    wire display_clock;
    wire [7:0] anodes;

    wire [`MFP_N_BUTTONS           - 1:0] IO_Buttons;
    wire [`MFP_7_SEGMENT_HEX_WIDTH - 1:0] IO_7_SegmentHEX;
    
    assign IO_Buttons  = { { `MFP_N_BUTTONS  -  2 { 1'b0 } } , i_btn0, i_btn1 };

mfp_system mfp_system
    (
        .SI_ClkIn         ( clock            ), 
        .SI_Reset         ( reset            ),
             
        .HADDR            (                  ),
        .HRDATA           (                  ),
        .HWDATA           (                  ),
        .HWRITE           (                  ),

        .EJ_TRST_N_probe  (                  ),
        .EJ_TDI           (                  ),
        .EJ_TDO           (                  ),
        .EJ_TMS           (                  ),
        .EJ_TCK           (                  ),
        .SI_ColdReset     (                  ),
        .EJ_DINT          (   1'b0           ),

        .IO_Switches      (                  ),
        .IO_Buttons       ( IO_Buttons       ),
        .IO_RedLEDs       (                  ),
        .IO_GreenLEDs     (                  ), 
        .IO_7_SegmentHEX  ( IO_7_SegmentHEX  ),
        
        `ifdef MFP_DEMO_LIGHT_SENSOR
        .SPI_CS           (   JA [0]         ),
        .SPI_SCK          (   JA [3]         ),
        .SPI_SDO          (   JA [2]         ),
        `endif
        
        .UART_RX          ( RsRx             ),
        .UART_TX          (                  )
    );

`ifdef MFP_DEMO_LIGHT_SENSOR
    assign JA [1] = 1'b0;
`endif

MFP_DEMO_LIGHT_SENSOR будем использовать при тестировании портированой системы. Как видим на выходе системы mfp_system будет 32-х разрядная шина IO_7_SegmentHEX которую мы выводим в модуль дешифратора 4-х разрядного 7 сегментного индикатора, подключим в оболочку модуль екземпляра «mfp_multi_digit_display»:

mfp_multi_digit_display multi_digit_display
    (
        .clock          (   display_clock   ),
        .resetn         ( ~ reset           ),
        .number         (   IO_7_SegmentHEX ),

        .seven_segments (   seg             ),
        .dot            (   dp              ),
        .anodes         (   an              )
    );

При создании дешифратора стоит узнать является ли индикатор с общим анодом или катодом.
Код дешифратора семисегментного индикатора с общим анодом имеет такой вид:

module mfp_single_digit_seven_segment_display
(
    input      [3:0] digit,
    output reg [6:0] seven_segments
);

    always @*
        case (digit)
        'h0: seven_segments = 'b1000000;  // a b c d e f g
        'h1: seven_segments = 'b1111001;
        'h2: seven_segments = 'b0100100;  //   --a--
        'h3: seven_segments = 'b0110000;  //  |     |
        'h4: seven_segments = 'b0011001;  //  f     b
        'h5: seven_segments = 'b0010010;  //  |     |
        'h6: seven_segments = 'b0000010;  //   --g--
        'h7: seven_segments = 'b1111000;  //  |     |
        'h8: seven_segments = 'b0000000;  //  e     c
        'h9: seven_segments = 'b0011000;  //  |     |
        'ha: seven_segments = 'b0001000;  //   --d-- 
        'hb: seven_segments = 'b0000011;
        'hc: seven_segments = 'b1000110;
        'hd: seven_segments = 'b0100001;
        'he: seven_segments = 'b0000110;
        'hf: seven_segments = 'b0001110;
        endcase

endmodule

//--------------------------------------------------------------------

module mfp_multi_digit_display
(
    input             clock,
    input             resetn,
    input      [31:0] number,

    output reg [ 6:0] seven_segments,
    output reg        dot,
    output reg [ 7:0] anodes
);

    function [6:0] bcd_to_seg (input [3:0] bcd);

        case (bcd)
        'h0: bcd_to_seg = 'b1000000;  // a b c d e f g
        'h1: bcd_to_seg = 'b1111001;
        'h2: bcd_to_seg = 'b0100100;  //   --a--
        'h3: bcd_to_seg = 'b0110000;  //  |     |
        'h4: bcd_to_seg = 'b0011001;  //  f     b
        'h5: bcd_to_seg = 'b0010010;  //  |     |
        'h6: bcd_to_seg = 'b0000010;  //   --g--
        'h7: bcd_to_seg = 'b1111000;  //  |     |
        'h8: bcd_to_seg = 'b0000000;  //  e     c
        'h9: bcd_to_seg = 'b0010000;  //  |     |
        'ha: bcd_to_seg = 'b0001000;  //   --d-- 
        'hb: bcd_to_seg = 'b0000011;
        'hc: bcd_to_seg = 'b1000110;
        'hd: bcd_to_seg = 'b0100001;
        'he: bcd_to_seg = 'b0000110;
        'hf: bcd_to_seg = 'b0001110;
        endcase

    endfunction

    reg [2:0] i;

    always @ (posedge clock or negedge resetn)
    begin
        if (! resetn)
        begin
            seven_segments <=   bcd_to_seg (0);
            dot            <= 0;
            anodes         <= 8'b00000001;

            i <= 0;
        end
        else
        begin
            seven_segments <=  bcd_to_seg (number [i * 4 +: 4]);
            dot            <= 0;
            anodes         <= (1 << i);

            i <= i + 1;
        end
    end

endmodule

Как можно увидеть по каждому переднему фронту clock в seven_segments выводится соответствующий сигнал bcd_to_seg, а переключение между разрядами индикатора производится с помощью переключения активного сигнала на соответствующих анодах (либо катодах).

Так как на вход процессора поступает сигнал в частотой в 50 МГц который мы создадим далее, если ми будем с такой частотой менять значения на индикаторе наш глаз не будет замечать эти изменения(для нормального отображения значений на 7 сегментном индикаторе необходима частота приблизительно в 763 Гц). Для этого подключим экземпляр делителя частоты:

   mfp_clock_divider_50_MHz_to_763_Hz mfp_clock_divider_50_MHz_to_763_Hz
        (clock, display_clock);

В исходных файлах делителя частоты с 50 MГц в 763 Гц нет. Поэтому мы добавим его в файл mfp_clock_dividers.v:



module mfp_clock_divider_50_MHz_to_763_Hz
(
    input  clki,
    output clko
);

    mfp_clock_divider
    # (.DIV_POW_SLOWEST (16))
    mfp_clock_divider
    (
        .clki    ( clki ),
        .sel_lo  ( 1'b1 ),
        .sel_mid ( 1'b0 ),
        .clko    ( clko )
    );

endmodule

На вход системы поступают сигналы reset и clock, RsRx, IO_Buttons. Но у нас на входе топ модуля сигнал i_clk, а не clock. Дело в том что на плате присутствует тактовый генератор в 12 МГц, а процессор в FPGA может работать на частотах 50 МГц и выше, а что делать если у вас другая плата с другой тактовой частотой (например Nexys 4 DDR имеет тактовую частоту 100 МГц). Решается проблема созданием IP ядра Clocking Wizard (PLL), для этого откроем IP каталог:



Откроем Clocking Wizard:



Вкладку board оставляем как есть, либо настраиваем нужные нам входы и переходим на вкладку clocking options можно выбрать либо MMCM имеет более широкий диапазон входных частот 10...800МГц, чем PLL 19...800МГц, поскольку на cmoda7 всего 12 МГц выбираем MMCM, но для других плат можно выбирать другие параметры:



Во вкладке output clocks устанавливаем частоту 50 МГц (с частотой можно и нужно экспериментировать, так на других FPGA могут быть другие характеристики):



Также убираем reset и locked:



Port renaming, MMCM setting и Summary смотрим и оставляем как есть, жмем ОК, ОК, в следующем окне выбираем generate:

image

Последним штрихом сделаем непрерывное присваивание для RGB Led, поскольку как видно из схемы в плавающем состоянии они будут тускло светится, а мы хотим что бы они были выключены:

image

assign led0_r = 1'b1;
assign led0_g = 1'b1;
assign led0_b = 1'b1;

В конце напишем:

endmodule

Модуль оболочка создан, перейдем к изменению размера памяти.

Шаг 2. Уменьшаем объем памяти системы MIPSfpga для соответствия плате cmoda7


Плата cmoda7 имеет 225 Кбайт блоковой памяти. Таким образом, два блока памяти (128 Кбайт ОЗУ сброса и 256 Кбайт ОЗУ программ) не помещаются на плате cmoda7. Так как, загрузочный код может уместиться в 32 Кбайт, и можно ограничить потребности программ объемом 64 Кбайт. Таким образом, общая необходимая память (32 Kбайт + 64 Kбайт = 96 Kбайт) соответствует объему памяти платы cmoda7. Оставшиеся 225-96 = 129 Kбайт памяти могут быть использованы для других нужд системы MIPSfpga, например, в качестве кэш-памяти.

Уменьшить объем памяти можно, изменив размер памяти, заявленный в заголовочном файле Verilog который мы подключали в файл оболочку «mfp_ahb_lite_matrix_config.vh». Открываем файл mipsfpga_ahb_const.vh. Адреса ОЗУ сброса (или ОЗУ загрузки) состоят из 13 бит. Таким образом, объем ОЗУ сброса равен 213 32-битных слов = 215 байт = 32 Kбайт.



`define H_RAM_RESET_ADDR_WIDTH  13 

Адреса ОЗУ программ состоят из 14 бит. Таким образом, объем ОЗУ программ равен 214 32-битных слов = 216 байт = 64 Kбайт.

`define H_RAM_ADDR_WIDTH  14

Шаг 3. Создаем файл ограничений cmoda7


Теперь создадим «cmoda7.xdc» который устанавливает соответствие между внешними сигналами модуля-оболочки и выводами корпуса FPGA платы cmoda7 (xdc файл с полным списком выводов для cmoda7):






## Clock signal 12 MHz
set_property -dict {PACKAGE_PIN L17 IOSTANDARD LVCMOS33} [get_ports i_clk]
create_clock -period 83.330 -name sys_clk_pin -waveform {0.000 41.660} -add [get_ports i_clk]

## Buttons
set_property -dict {PACKAGE_PIN A18 IOSTANDARD LVCMOS33} [get_ports i_btn0]
set_property -dict {PACKAGE_PIN B18 IOSTANDARD LVCMOS33} [get_ports i_btn1]

## LEDs
set_property -dict {PACKAGE_PIN B17 IOSTANDARD LVCMOS33} [get_ports led0_b]
set_property -dict {PACKAGE_PIN B16 IOSTANDARD LVCMOS33} [get_ports led0_g]
set_property -dict {PACKAGE_PIN C17 IOSTANDARD LVCMOS33} [get_ports led0_r]

## 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]}]

## GPIO Pins 1 - 6   7_segment_ind
set_property -dict {PACKAGE_PIN M3 IOSTANDARD LVCMOS33} [get_ports {seg[1]}]
set_property -dict {PACKAGE_PIN L3 IOSTANDARD LVCMOS33} [get_ports {an[1]} ]
set_property -dict {PACKAGE_PIN A16 IOSTANDARD LVCMOS33}[get_ports {an[2]} ]
set_property -dict {PACKAGE_PIN K3 IOSTANDARD LVCMOS33} [get_ports {seg[5]}]
set_property -dict {PACKAGE_PIN C15 IOSTANDARD LVCMOS33}[get_ports {seg[0]}]
set_property -dict {PACKAGE_PIN H1 IOSTANDARD LVCMOS33} [get_ports {an[3]} ]

## GPIO Pins 43 - 48  7_segment_ind
set_property -dict {PACKAGE_PIN U3 IOSTANDARD LVCMOS33} [get_ports {seg[3]}]
set_property -dict {PACKAGE_PIN W6 IOSTANDARD LVCMOS33} [get_ports {seg[4]}]
set_property -dict {PACKAGE_PIN U7 IOSTANDARD LVCMOS33} [get_ports dp    ]
set_property -dict {PACKAGE_PIN W7 IOSTANDARD LVCMOS33} [get_ports {seg[2]}]
set_property -dict {PACKAGE_PIN U8 IOSTANDARD LVCMOS33} [get_ports {seg[6]}]
set_property -dict {PACKAGE_PIN V8 IOSTANDARD LVCMOS33} [get_ports {an[0]} ]

## UART
#set_property -dict { PACKAGE_PIN J18   IOSTANDARD LVCMOS33 } [get_ports { uart_rxd_out }]; #IO_L7N_T1_D10_14 Sch=uart_rxd_out
set_property -dict {PACKAGE_PIN J17 IOSTANDARD LVCMOS33} [get_ports RsRx]

Например, следующая строка устанавливает соответствие между входом i_clk и выводом корпуса FPGA L17, на который поступает тактовый сигнал с частотой 12 МГц с платы cmoda7.

set_property -dict {PACKAGE_PIN L17 IOSTANDARD LVCMOS33} [get_ports i_clk]
create_clock -period 83.330 -name sys_clk_pin -waveform {0.000 41.660} -add [get_ports i_clk]

Так же в файл ограничений нужно добавить следующие выходные временные ограничения для сигналов:

# I/O virtual clock
create_clock -period 83.330 -name "clk_virt"

# tsu/th constraints
set_input_delay -clock "clk_virt" -min -add_delay 0.000 [get_ports i_btn0]
set_input_delay -clock "clk_virt" -max -add_delay 10.000 [get_ports i_btn0]
set_input_delay -clock "clk_virt" -min -add_delay 0.000 [get_ports i_btn1]
set_input_delay -clock "clk_virt" -max -add_delay 10.000 [get_ports i_btn1]
set_input_delay -clock "clk_virt" -min -add_delay 0.000 [get_ports led0_b]
set_input_delay -clock "clk_virt" -max -add_delay 10.000 [get_ports led0_b]
set_input_delay -clock "clk_virt" -min -add_delay 0.000 [get_ports led0_g]
set_input_delay -clock "clk_virt" -max -add_delay 10.000 [get_ports led0_g]
set_input_delay -clock "clk_virt" -min -add_delay 0.000 [get_ports led0_r]
set_input_delay -clock "clk_virt" -max -add_delay 10.000 [get_ports led0_r]

## PMOD ALS
set_output_delay -clock "clk_virt" -min -add_delay 0.000 [get_ports {JA[0]}]
set_output_delay -clock "clk_virt" -max -add_delay 10.000 [get_ports {JA[0]}]
set_output_delay -clock "clk_virt" -min -add_delay 0.000 [get_ports {JA[1]}]
set_output_delay -clock "clk_virt" -max -add_delay 10.000 [get_ports {JA[1]}]
set_input_delay -clock "clk_virt" -min -add_delay 0.000 [get_ports {JA[2]}]
set_input_delay -clock "clk_virt" -max -add_delay 10.000 [get_ports {JA[2]}]
set_output_delay -clock "clk_virt" -min -add_delay 0.000 [get_ports {JA[3]}]
set_output_delay -clock "clk_virt" -max -add_delay 10.000 [get_ports {JA[3]}]

set_input_delay -clock "clk_virt" -min -add_delay 0.000 [get_ports {seg[*]}]
set_input_delay -clock "clk_virt" -max -add_delay 10.000 [get_ports {seg[*]}]
set_input_delay -clock "clk_virt" -min -add_delay 0.000 [get_ports {an[*]}]
set_input_delay -clock "clk_virt" -max -add_delay 10.000 [get_ports {an[*]}]
set_input_delay -clock "clk_virt" -min -add_delay 0.000 [get_ports dp]
set_input_delay -clock "clk_virt" -max -add_delay 10.000 [get_ports dp]
set_output_delay -clock "clk_virt" -min -add_delay 0.000 [get_ports RsRx]
set_output_delay -clock "clk_virt" -max -add_delay 10.000 [get_ports RsRx]

Тест MIPSfpga


Мы можем протестировать портированый на нашу плату процессор, для этого нужно зайти в файл конфигурации «mfp_ahb_lite_matrix_config.vh» и расскоментируем строку:

`define MFP_DEMO_LIGHT_SENSOR



Таким образом мы подключили написанный на Verilog модуль интерфейса SPI к шине AHB Lite для соединения с датчиком освещённости PmodALS фирмы Digilent.

Модуль SPI
module mfp_pmod_als_spi_receiver
(
    input             clock,
    input             reset_n,
    output            cs,
    output            sck,
    input             sdo,
    output reg [15:0] value
);

    reg [21:0] cnt;
    reg [15:0] shift;

    always @ (posedge clock or negedge reset_n)
    begin       
        if (! reset_n)
            cnt <= 22'b100;
        else
            cnt <= cnt + 22'b1;
    end

    assign sck = ~ cnt [3];
    assign cs  =   cnt [8];

    wire sample_bit = ( cs == 1'b0 && cnt [3:0] == 4'b1111 );
    wire value_done = ( cnt [21:0] == 22'b0 );

    always @ (posedge clock or negedge reset_n)
    begin       
        if (! reset_n)
        begin       
            shift <= 16'h0000;
            value <= 16'h0000;
        end
        else if (sample_bit)
        begin       
            shift <= (shift << 1) | sdo;
        end
        else if (value_done)
        begin       
            value <= shift;
        end
    end

endmodule




Схема подключения изображена на рисунке:



Датчик подключен к порту Pmod, выходи которого описаны в «cmoda7.xdc» прописаны как JA[7..0].

## Pmod Header JA
set_property -dict {PACKAGE_PIN G17 IOSTANDARD LVCMOS33} [get_ports {JA[0]}]
...
set_property -dict {PACKAGE_PIN K18 IOSTANDARD LVCMOS33} [get_ports {JA[7]}]

RTL схему можно исследовать во вкладке RTL Analisys → Schematic, где модуль интерфейса SPI «mfp_pmod_als_spi_receiver» находится в «mfp_system»:



Что бы загрузить в FPGA созданный проект нужно сгенерировать bitstream файл «cmoda7.bit», для этого во вкладке Program and Debug → Generate Bitstream и подождать завершения операции:



После создания bitstream файла нужно его загрузить в плату, для этого во вкладке Program and Debug → откроем Open Hardware Manager → Open Target → Auto Connect → Program Device и загрузим систему:



Загрузка программы для работы с датчиком


Чтобы процессор обменивался с датчиком данными нужно написать программу и загрузить в блок RAM1 который так же подключён к шине AHB Lite.

Процессор MIPSfpga программируют с использованием инструментов разработки Codescape компании Imagination. Установите Codescape SDK и OpenOCD. Codescape поддерживает программирование как на языке C, так и на языке ассемблера.

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

#define MFP_LIGHT_SENSOR_ADDR   0xBF800014

и

#define MFP_LIGHT_SENSOR        (* (volatile unsigned *) MFP_LIGHT_SENSOR_ADDR  )

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

#include "mfp_memory_mapped_registers.h"

int main ()
{
    int n = 0;

    for (;;)
    {
        MFP_7_SEGMENT_HEX = MFP_LIGHT_SENSOR;
    }

    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

Результат портирования системы можно посмотреть на видео:



Для понимания работы процессора, и возможности написания новых модулей на языке описания аппаратуры (Verilog, VHDL), а так же схемотехники, архитектуры компьютера, микроархитектуры (организация процессорного конвейера) и программирования на ассемблере, и много чего другого что поможет вам взять старт в изучении всего что связано с электроникой рекомендуется прочитать книгу про которую написано в статье: Бесплатный учебник электроники, архитектуры компьютера и низкоуровневого программирования на русском языке.

В следующей части будет описано как подключать различную периферию в систему MIPSfpga на примере клавиатуры Digilent Pmod KYPD, встроенной АЦП, и LCD дисплея от Nokia.

Портирование MIPSfpga на другие платы и интеграция периферии в систему. Часть 2
Поделиться публикацией
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 1
  • 0
    Хотелось CMOD A7-35 заказать, ан нет, в адресе доставки РФ не значится.
    Arty Board Artix-7 тоже неплохая.

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

    Самое читаемое