FPGA: Xilinx Рекомендации советы.

 

  1. ISE iMPACK программирование FPGA (конфигурирование) из командной строки (SRAM и SPI FLASH).
  2. ISE iMPACK программирование CPLD (конфигурирование) из командной строки.
  3. ISE iMPACK стирание конфигурационной SPI flash памяти FPGA из командной строки.
  4. ISE iMPACK чтение уникального номера DNA (unique ID) микросхемы Spartan-6 из командной строки.
  5. Xilinx: конвертация MCS или BIT в BIN файл.

 

 




 

 ISE iMPACK программирование FPGA (конфигурирование) из командной строки (SRAM и SPI FLASH).

 

 

    Скрипты позволяют автоматизировать процесс (программирования). 

    В ряде случаев удобнее запустить скрипт из командной строки, чем запускать графическую утилиту

iMPACK, и проходить весь процесс нажимая на кнопки в меню. 

    Например в режиме отладки проекта, когда одни и те же действия приходится повторять из раза в раз.

Когда после очередной компиляции проекта нужно залить в кристалл FPGA свежую прошивку.

    Серийное программирование проще осуществлять через командный файл т.к. не нужно вспоминать

(помнить) все параметры программирования, а просто необходимо запустить скрипт в котором все

необходимые параметры уже заданы.

    Программирование можно поручить специалисту далекому от ПЛИС, например радиомонтажнику.

 

    При использовании скрипта инструкция по программированию будет максимально простой:

  1. Подключите программатор.
  2. Подайте питание на устройство.
  3. Запустите файл prog.cmd.
  4. Дождитесь окончания программирования, готово !

 

 

    В данной статье приводится пример программирования FPGA Xilinx Spartan-6 XC6SLX75 и

SPI Flash W25Q64FV, при помощи программатора XILINX Platform Cable USB, под управлением ОС

Windows 7 64, среда Xilinx ISE 14.7.

 

    Для программирования создаются два файла.

Первый файл, это скрипт запуска утилиты XILINX iMPACK с указанием файла с командами, второй файл

это последовательность команд для iMPACK.

 

 

Программирование в режиме SRAM.

 

    Для программирования необходимо наличие файла прошивки (BIT файла).

В примере данный файл обозначен как example_top.bit

 

Файл запуска программирования: prog_ram.cmd

impact -batch impact_batch_ram.cmd

Файл последовательность команд для iMPACK:  impact_batch_ram.cmd

setMode -bs
setCable -port usb21 -baud 6000000
addDevice -p 1 -file ..\example_top.bit
program -p 1
quit

Где:

..\example_top.bit - BIT файл прошивки.

 

 

Программирование SPI FLASH.

 

    Для программирования необходимо наличие файла прошивки (BIT файла).

В примере данный файл обозначен как example_top.bit

    Сначала запускается утилита promgen, которая производит конвертацию BIT файла в MCS.

Затем MCS файл будет записан в SPI flash. 

    В качестве SPI Flash в проекте установлена микросхема W25Q64FV.

 

Файл запуска программирования: prog_spi.cmd

promgen -spi -w -p mcs -o .\example_top.mcs -s 65536 -u 0 ..\example_top.bit
impact -batch impact_batch_spi.cmd

Где:

promgen - утилита конвертации BIT файла в MCS.

65536 - PROM size in kilobytes.

 

Файл последовательность команд для iMPACK:  impact_batch_spi.cmd

setMode -bs
setCable -port usb21 -baud 6000000
addDevice -p 1 -file ..\example_top.bit
attachflash -position 1 -spi "W25Q64FV"
assignfiletoattachedflash -position 1 -file .\example_top.mcs
program -p 1 -dataWidth 1 -spionly -e -loadfpga
quit

Где:

"W25Q64FV" - Указывается микросхема SPI.

..\example_top.bit - BIT файл прошивки.

.\example_top.mcs - MCS файл прошивки.

 

 

 

ВНИМАНИЕ: Обнаружена проблема.

После конвертации BIT файла в MCS при помощи promgen, (на некоторых прошивках) 

mcs прошивается с ошибкой и не работает.

В чем дело пока непонятно FPGA и плата одна и та же, при этом проект вырос в размерах.

 

РЕШЕНИЕ:

Был создан новый impact_batch_spi.cmd скрипт который перед прошивкой производит

конвертацию (c помощью impact) и затем прошивает.

setMode -pff
addConfigDevice  -name "example_top" -path "."
setMode -pff
setSubmode -pffspi
setAttribute -configdevice -attr multibootBpiType -value ""
addDesign -version 0 -name "0"
setMode -pff
addDeviceChain -index 0
setAttribute -configdevice -attr compressed -value "FALSE"
setAttribute -configdevice -attr autoSize -value "FALSE"
setAttribute -configdevice -attr fileFormat -value "mcs"
setAttribute -configdevice -attr fillValue -value "FF"
setAttribute -configdevice -attr swapBit -value "FALSE"
setAttribute -configdevice -attr dir -value "UP"
setAttribute -configdevice -attr multiboot -value "FALSE"
setAttribute -configdevice -attr spiSelected -value "TRUE"
addPromDevice -p 1 -size 8192 -name 8M
setMode -pff
addDeviceChain -index 0
setSubmode -pffspi
setMode -pff
setAttribute -design -attr name -value "0000"
addDevice -p 1 -file "../example_top.bit"
setMode -pff
setSubmode -pffspi
generate
setCurrentDesign -version 0

setMode -bs
setCable -port usb21 -baud 6000000
addDevice -p 1 -file ..\example_top.bit
attachflash -position 1 -spi "W25Q64FV"
assignfiletoattachedflash -position 1 -file .\example_top.mcs
program -p 1 -dataWidth 1 -spionly -e -loadfpga
quit

 

Там же лежит скрипт для конвертации из BIT в MCS при помощи impact (prog_gen_mcs.cmd).

Скачать готовый файлы можно тут GitHub.


 

ISE iMPACK программирование CPLD (конфигурирование) из командной строки.

 

    В данной статье приводится пример программирования CPLD Xilinx X2C32A, при помощи

программатора XILINX Platform Cable USB, под управлением ОС Windows 7 64, среда Xilinx ISE 14.7.

 

    Для программирования создаются два файла.

Первый файл, это скрипт запуска утилиты XILINX iMPACK с указанием файла с командами, второй файл

это последовательность команд для iMPACK.

 

Файл запуска программирования: prog_cpld.cmd

impact -batch impact_batch_cpld.cmd

 

Файл последовательность команд для iMPACK:  impact_batch_cpld.cmd

setMode -bs
setCable -port usb21 -baud 6000000
addDevice -p 1 -file ..\top_example.jed
program -e -v -r -p 1
quit

Производится стирание кристалла (ключ -e),

запись, проверка того что записали (ключ -v),

установка бита запрет чтения прошивки (ключ -r).

 

Скачать готовый файлы можно тут GitHub.


 

ISE iMPACK стирание конфигурационной SPI flash памяти FPGA из командной строки.

 

 

Если нужно стереть содержимое SPI flash подключенной к FPGA то вот скрипты.

Файл запуска программирования: prog_erase.cmd

impact -batch impact_batch_erase.cmd

 

Файл последовательность команд для iMPACK:  impact_batch_erase.cmd

setMode -bs
setCable -port usb21 -baud 6000000
Identify -inferir
attachflash -position 1 -spi "W25Q64FV"
erase -p 1 -o -spionly
quit

 

 

Скачать готовый файлы можно тут GitHub.


 

ISE iMPACK чтение уникального номера DNA (unique ID) микросхемы Spartan-6 из командной строки.

 

В каждой микросхеме семейства Spartan-6 есть уникальный номер DNA(unique ID).

Данный номер можно считать внутри проекта и снаружи при помощи iMPACK.

 

Файл запуска чтения DNA: prog_read_dna.cmd

impact -batch impact_batch_read_dna.cmd

 

Файл последовательность команд для iMPACK:  impact_batch_read_dna.cmd

setMode -bs
setCable -port usb21 -baud 6000000
Identify
identifyMPM
Readdna -p 1
quit

 

 В результате (log работы impack):

iMPACT Version: 

iMPACT log file Started

......

Identifying chain contents...'0': : Manufacturer's ID = Xilinx xc6slx75, Version : 2
----------------------------------------------------------------------
----------------------------------------------------------------------

......


Validating chain...
Boundary-scan chain validated successfully.
'1': DNA = '100110101101111001010100000010101011110100010010011011110'

 

'1': DNA = '100110101101111001010100000010101011110100010010011011110'

и есть уникальный 57-битный номер.

 

Проект на языке verilog для чтения DNA и вывод в UART порт.

//*****************************************************************************
// Проект чтение DNA Spartan-6
//
// Вывод осуществляется циклически один раз в секунду, 
// в UART порт (115200 8N1), в бинарном виде.
//*****************************************************************************
//*****************************************************************************
`timescale 1ns/1ps

module example_top
(
    input  wire i_sys_clk,
    output wire d_fpg3,
    output wire o_led
);
localparam I_SYS_CLK = 32'd50_000_000; // входная тактовая частота

wire sys_clk_ibufg;

//-----------------------------------------------------------------------
IBUFG  u_ibufg_sys_clk
(
    .I  (i_sys_clk),
    .O  (sys_clk_ibufg)
);

wire dna_dout;
reg dna_read = 0;
reg dna_shift = 0;


DNA_PORT #(
    .SIM_DNA_VALUE(57'h000000000000000)  // Specifies the Pre-programmed factory ID value
)
DNA_PORT_inst (
    .DOUT   (dna_dout),        // 1-bit output: DNA output data
    .CLK    (sys_clk_ibufg),   // 1-bit input: Clock input
    .DIN    (1'b0),            // 1-bit input: User data input pin
    .READ   (dna_read),        // 1-bit input: Active high load DNA, active low read input
    .SHIFT  (dna_shift)        // 1-bit input: Active high shift enable input
);

reg [1:0] tick_sh = 0;
reg       tick = 0;

reg [25:0] led_count = 0;
reg        led = 0;
//------------------------------------------------------------------------------
// Led blink
//------------------------------------------------------------------------------
always @(posedge sys_clk_ibufg)
begin
    led_count <= led_count + 1'b1;
    if (led_count == I_SYS_CLK / 2) begin
        led_count <= 0;
        led       <= ~led;
    end

    tick_sh[0] <= ~led;
    tick_sh[1] <= tick_sh[0];
     
    if (tick_sh[1:0] == 2'b01) tick <= 1;
    else tick <= 0;
end

assign o_led  = led;

wire u_tx_busy;
reg [7:0] u_tx_data = 0;
reg       u_tx_we = 0;

uart_tx U_TX
(
    .i_clk      (sys_clk_ibufg),    // Clk input
    .i_en_h     (1'b1),       // module enable active-HIGH
    .i_div      (I_SYS_CLK / (4 * 115200)),        // div speed uart-rx. (baud = clk / div)
    .i_tx_data  (u_tx_data),  // data out
    .i_we_h     (u_tx_we),    // we strob Active HIGH, tx_data write to modul
    .i_parity_en_h(1'b0),      // bit parity disable(0) / enable (1)
    .i_parity_type_el_oh(1'b0),// bit parity type E (Even parity) вЂ" proverka na chetnost O (Odd parity) вЂ" proverka na NE chetnost;
//   .o_int_h    (),           // strob interupt active-HIGH, rx->data out
    .o_tx       (d_fpg3),     // rs-232 output
    .o_busy_h   (u_tx_busy)   // (STATUS) busy=1 uart tx byte
);

reg [7:0]  d_st = 0;
reg [6:0]  d_count = 0;
reg [63:0] dna_code = 0;

// MY debug port ----------------------------------------
always @(posedge sys_clk_ibufg)
begin
    case(d_st)
    0:
    begin
        dna_read <= 1;
        d_st <= 1;
    end

    1:
    begin
        dna_read <= 0;
        d_count <= 0;
        d_st <= 2;
    end
    
    2:
    begin
        d_count <= d_count + 1'b1;
        dna_code[0] <= dna_dout;
        dna_code[63:1] <= dna_code[62:0];
        if (d_count == 'd57 - 1) begin
            dna_shift <= 0;
            d_st <= 3;
        end else dna_shift <= 1;
    end
    
    3:
    begin
        d_count <= 0;
        if (u_tx_busy == 0) d_st <= 4;
    end
     
    4:
    begin
        case(d_count)
        0: u_tx_data <= dna_code[63:56];
        1: u_tx_data <= dna_code[55:48];
        2: u_tx_data <= dna_code[47:40];
        3: u_tx_data <= dna_code[39:32];
        4: u_tx_data <= dna_code[31:24];
        5: u_tx_data <= dna_code[23:16];
        6: u_tx_data <= dna_code[15:8];
        7: u_tx_data <= dna_code[7:0];
        default:u_tx_data <= 0;
        endcase
        u_tx_we <= 1;
        d_st <= 5;
    end
     
    5:
    begin
        u_tx_we <= 0;
        d_st <= 6;
    end
     
    6:
    begin
        d_st <= 7;
    end
    
    7:
    begin
        if(u_tx_busy == 0) begin    
            if (d_count < 'd7) begin
                d_count <= d_count + 1;
                d_st <= 4;
            end else begin
                d_st <= 8;
            end
        end
    end 
     
    8:
    begin
        if (tick) d_st <= 3; 
    end
     
    default: d_st <= 0;
    endcase
end

endmodule   

 

// Module UART TX DATA(RS-232)-------------------------------------------------
// INPUT:
// i_clk  - global system clk
// i_en_h - =1 enable TX module
// i_div  - set predelitel () DIV[16] = F(clk) / (4 * SPEED(bit/s))
// i_we_h - strob 
// i_tx_data - byte data to tx
// i_parity_en_h - bit parity disable(0) / enable (1)
// i_parity_type_el_oh - bit parity type E (Even parity) — proverka na chetnost O (Odd parity) — proverka na NE chetnost;
//
// format slova: 8N1, 8E1, 8O1. (start-data-stop), (start-data-parity-stop)
//
// OUTPUT:
// o_int_h - interrupt(ACTIVE HIGH), byte recive
// o_tx   - TX data RS232
//
// v2.0 2015
// stop bit zakanchivaem peredachu ranhe na 1 c_pulse
//=============================================================================
// DIV[16] = F(clk) / (4 * SPEED(bit/s))
// div115200 = 25000000(25MHz) / (4 * 115200) = 54.25 => 54(10) => 36(16)
//
//
//data  kolichestvo 1  bit chetnosti
//                       even odd
//0000000   0               0  1
//1010001   3               1  0
//1101001   4               0  1
//1111111   7               1  0
//
//=============================================================================

//`define DEBUG

module uart_tx (
    input wire i_clk,              // Clk input
    input wire i_en_h,             // module enable active-HIGH
    input wire [15:0] i_div,       // div speed uart-rx. (baud = clk / div)
    input wire [7:0] i_tx_data,    // data out
    input wire i_we_h,             // we strob Active HIGH, tx_data write to modul

    input wire i_parity_en_h,      // bit parity disable(0) / enable (1)
    input wire i_parity_type_el_oh,// bit parity type E (Even parity) — proverka na chetnost O (Odd parity) — proverka na NE chetnost;
     
    output reg o_int_h = 0,        // strob interupt active-HIGH, rx->data out
    output reg o_tx = 1,           // rs-232 output
    output reg o_busy_h = 0        // (STATUS) busy=1 uart tx byte
     
// debug -------------------------------
`ifdef DEBUG
    ,
    output wire pulse_o,
    output wire [7:0] d,
    output wire [5:0] c_pulse_t
`endif
);

parameter TX_WAIT_DATA = 1'b0, TX_START = 1'b1;//, TX_B0 = 2, TX_B1 = 3, TX_B2 = 4, TX_B3 = 5, TX_B4 = 6, TX_B5 = 7, TX_B6 = 8, TX_B7 = 9, TX_STOP = 10; 
reg state = TX_WAIT_DATA; // sostoyanie avtomata = nachalnoe sostoyanie avtomata

reg enable_counter = 0;          // signal razreheniya raboti countera DIV(predelitela)
reg [7:0] tx_d = 0;              // promuhutochiny buffer dla prinatie danie 8 bit ot system
reg [15:0] div_q = 0;            // counter dla predilitela
reg [15:0] div = 0;              // counter dla predilitela
reg pulse = 0;                   // 1 tic, raboti DIV(predelitela)
reg [5:0] c_pulse = 0;           // counter pulse, avtomat, otscheti po kotorim chitaem intervali TX


always @( posedge i_clk)
begin
    case (state)
    TX_WAIT_DATA:                          // ohidaem zapisi danih strob we_h 0->1
    begin
        o_int_h <= 0;                        // Clear flag INT_H, itogo strob 1 takt !!!
        if (i_en_h && i_div != 0 && i_we_h) begin
            div <= i_div;
            state <= TX_START;
            enable_counter <= 1;
            o_tx <= 0;                       // send start
            tx_d <= i_tx_data;
            o_busy_h <= 1;                   // busy set, start tx byte
        end
    end
     
    TX_START:
    begin
    if (pulse) begin
        case (c_pulse)
        3,//: o_tx <= tx_d[ 0 ];// tx 0 bit 
        7,//: o_tx <= tx_d[ 1 ];// tx 1 bit
        11,//: o_tx <= tx_d[ 2 ];// tx 2 bit
        15,//: o_tx <= tx_d[ 3 ];// tx 3 bit
        19,//: o_tx <= tx_d[ 4 ];// tx 4 bit
        23,//: o_tx <= tx_d[ 5 ];// tx 5 bit
        27,//: o_tx <= tx_d[ 6 ];// tx 6 bit
        31:// o_tx <= tx_d[ 7 ];// tx 7 bit
        begin
            o_tx <= tx_d[ c_pulse[4:2] ];
        end
          
        35: // parity | tx stop bit
        begin
            if (i_parity_en_h) begin
                if (i_parity_type_el_oh == 0)
                    o_tx <= ^{tx_d, 1'b1}; // tx parity event
                else
                    o_tx <= ^{tx_d, 1'b0}; // tx parity odd 
                end else o_tx <= 1;  // tx stop bit
        end
          
        38:
        begin
            if (i_parity_en_h == 0) begin
                enable_counter <= 0;
                state <= TX_WAIT_DATA;
                o_int_h <= 1;
                o_busy_h <= 0;
            end 
        end
          
        39:
        begin
            if (i_parity_en_h) begin
                o_tx <= 1;  // tx stop bit
            end else begin
                enable_counter <= 0;
                state <= TX_WAIT_DATA;
                o_int_h <= 1;
                o_busy_h <= 0;
            end 
        end
          
        41:
        begin
            enable_counter <= 0;
            state <= TX_WAIT_DATA;
            o_int_h <= 1;
            o_busy_h <= 0;
        end
          
        endcase
    end // if
    end
 
    endcase
end

//-----------------------------------------------------------------------------
// counter 1 tic * 4
//-----------------------------------------------------------------------------
always @(posedge i_clk)
begin
    if (enable_counter) begin
        if (pulse)
            c_pulse <= c_pulse + 1'b1;
    end else 
        c_pulse <= 0;
end

//-----------------------------------------------------------------------------
// counter bit interval / 4
//-----------------------------------------------------------------------------
always @( posedge i_clk )
begin
    if (enable_counter == 0) begin
        div_q <= 0;
    end else begin
        if (div_q == div && div != 0) begin
            div_q <= 0;
            pulse <= 1;
        end else begin
            pulse <= 0;
            div_q <= div_q + 1'b1;
        end
    end
end

`ifdef DEBUG
assign pulse_o = pulse;
assign d = tx_d;
assign c_pulse_t = c_pulse;
`endif

endmodule

 

Скачать готовый файлы можно тут GitHub.

 


Xilinx: конвертация MCS или BIT в BIN файл.

 

MCS файл это обычный Intel HEX файл.

Для конвертации в BIN можно воспользоваться утилитой hex2bin.

 

BIT файл это по сути загрузочный (то что нужно записать в SPI FLASH) BIN файл с заголовком.

Размер заголовка 89 байт. Для превращения BIT в BIN файл необходимо пропустить первый 89 байт, 

а  все что идет после скопировать в BIN файл.

 

Для конвертации можно применить утилиту DD:

dd if=top.bit of=top.bin skip=89 bs=1

где 

top.bit  - входной файл.

top.bin - выходной файл.

Готовый скрипт тут.

 

Зачем это нужно ?

 

Пример 1.

Периодически приходится работать с семейством Spartan-6 (хотя на дворе 2023 год),

и приходится программировать загрузочную SPI флешку.

Программируя через iMPACK приходится применять те флешки которые которые есть в списке.

Учитывая что ISE уже давно не поддерживается, то и набор флешек не обновляется да и набор не велик...

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

Решение это ставить на плату флешки других производителей и прошивать флешки на плате своими силами.

Для этого в схему FPGA-SPIFLASH вносятся следующие изменения.

В разрыв FPGA-SPIFLASH ставиться мультиплексор (можно аналоговый) и разъем для программирования.

На разъем выводяться сигналы SPI.

К разъему SPI подключается FTx232H и при помощи ПО программатора прошивается микросхема памяти.

 

Пример 2.

Данный метод применим не только к FPGA, но и для других систем,

например TI Sitara AM335x для загрузки образа Linux (частями или по отдельности) и т.п.