FPGA: Xilinx Рекомендации советы.
- ISE iMPACK программирование FPGA (конфигурирование) из командной строки (SRAM и SPI FLASH).
- ISE iMPACK программирование CPLD (конфигурирование) из командной строки.
- ISE iMPACK стирание конфигурационной SPI flash памяти FPGA из командной строки.
- ISE iMPACK чтение уникального номера DNA (unique ID) микросхемы Spartan-6 из командной строки.
- Xilinx: конвертация MCS или BIT в BIN файл.
ISE iMPACK программирование FPGA (конфигурирование) из командной строки (SRAM и SPI FLASH).
Скрипты позволяют автоматизировать процесс (программирования).
В ряде случаев удобнее запустить скрипт из командной строки, чем запускать графическую утилиту
iMPACK, и проходить весь процесс нажимая на кнопки в меню.
Например в режиме отладки проекта, когда одни и те же действия приходится повторять из раза в раз.
Когда после очередной компиляции проекта нужно залить в кристалл FPGA свежую прошивку.
Серийное программирование проще осуществлять через командный файл т.к. не нужно вспоминать
(помнить) все параметры программирования, а просто необходимо запустить скрипт в котором все
необходимые параметры уже заданы.
Программирование можно поручить специалисту далекому от ПЛИС, например радиомонтажнику.
При использовании скрипта инструкция по программированию будет максимально простой:
- Подключите программатор.
- Подайте питание на устройство.
- Запустите файл prog.cmd.
- Дождитесь окончания программирования, готово !
В данной статье приводится пример программирования 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 (частями или по отдельности) и т.п.