Оригинал взят у
vkorehovisback в Как сделать PCI карту своими руками, Часть 6 (Больше верилога!)
https://github.com/vkorehov/cncfpga2
Дело в том что использовать закрытые коры это не православно.
Поэтому первым делом я решил от нее избавится.
Я решил оставить тестбенч, временные констрейнты в UCF, некоторые настройки компиляции.
Все остальное (что было закрыто) я решил переписать на чистом верилоге!
Начать нужно с секции ввода вывода она скопирована из коры, и не является закрытой (cnc_top.v).
Она содержит, входные буферы и входные триггеры:
это все примитивы библиотеки unisim. Найти таблицы истинности можно в гугле.
Для всех остальных сигналов все делается похожим способом. Иногда вместо IOBUF используется IBUF или ОBUF если соответствующий сигнал только выход или только вход, и т.д.
Клок обрабатывается по особенному:
Далее везде используется CLK
Особенно нужно остановиться на реализации ресета, в коре использовалась стандартная реализация:
Для Xilinx есть более экономичный и элегантный способ:
http://www.eetimes.com/document.asp?doc_id=1278998
Далее уже нужно сделать скелетон нашей реализации PCI (pci.v)
Заметьте что некоторые выходы помещены в регистры! это важно!.
когда я смотрел на реализацию в старой коре, они используют триггеры как для входов так для выходов, так и для сигналов включения выходов (OE_...)
Далее временная спецификация (сохранена от старой коры):
Первым делом они назначают выводам различные временные группы.
названия будут: PCI_PADS_C, PCI_PADS_G, PCI_PADS_B и т.д.
Далее специальные названия для триггеров входных данных+комманд и выходных данных (команды я не использую)
обратите в нимание, что при работе с шинами, в верилоге у нас выход с регистром называется AD_O, в процессе компиляции он разбивается на провода и соответственно в файле с константами его нужно указывать как AD_O_xxx
Если что-то удалилось из дизайна в процессе оптимиазации, то также эти регистры нельзя указать в файле с константами и это будет вызывать ошибки.
Следует отметить что поскольку некоторые сигналы PCI коммутируются одновременно (TRDY и STOP например), то компилятор будет считать их эквивалентными и пытаться убрать второй регистр, это крайне нежелательно, по причинам которые обьясню чуть позже, но пока лишь скажу что для того чтобы этого не происходило нужно убрать галку с
Equivalent Register Removal в настройках синтеза.
Далее определяем одни группы через логические операции над другими:
Далее когда названия групп определены, переходим к спецификации констрейнтов оптимизации (скопировано из коры как есть):
Если такой файл попробовать имплементировать, то имплементация выдаст ошибку таймингов.
Для того чтобы тайминги соответствовали спецификации PCI, нужно запихнуть входные триггеры, выходные триггеры а также триггеры Output Enable (OE_) в блоки ввода-вывода (IOB)
Для этого используются вот такие директивы:
После этого все должно скомпилироваться успешно, а в конце маппинга, выдается вот такая вот таблица:
Обратите внимание на INFF, OUTFF и ENFF.
Я использовал ровно такие настройки как и старая кора.
Далее собственно имплементация интерфейса.
я разделил это на три блока:
сигналы PCI и управление шиной:
Ввод данных:
Вывод данных:
как видно я использую два бара, один бар ввода вывода, а другой памяти.
размеры задаются параметрами:
следует иметь ввиду, что размер окна памяти (BAR2_) не может быть меньше 16 адресов (потому как первые 4 бита конфигурационного регистра зарезервивованы) а размер окна I/O не может быть меньше 4 адресов , потому как адресация I/O 8-битная, и только два младших бита зарезервивованы.
Я жестко требую выравнивания памяти при обращении. Byte Enable не использую все должно быть выравнено на 32 бита.
Далее важно проверить соответствие спецификации, конечной Post-Route симуляции!
Например я пытался слишком быстро устанавливать TRDY в результате чего адреса читались как нули (не успевали устаовиться) я это исправил задержав TRDY на один клок, это кстати быстрее оригинальной коры на один клок, у них задерживалось на два клока относительно DEVSEL.
Генерируем файл верилога для конечной симуляции:

это создаст файлы в папке nergen.
Создадим новый .do файл для Modelsim: timesim.do
Создадим новый .f файл для Modelsim: cnc_tb_timesim.f
Запустим симуляцию:
проверим чтобы нигде не было красного (1'bx) что означает неинициализированные данные.
а синее (1'bz) какраз было в так называемых turnaround циклах PCI. естественно нас интересуют:
AD, CBE, TRDY, DEVSEL, STOP, PAR.
да, важно понять что PAR устанавливатся на следующем клоке, т.е. сначала мы пишем данные, а на следующем клоке устанавливаем их parity. и так это сдвинуто на протяжении всей транзакции (если использовать Burst, что мне не нужно):

Далее записываем новую прошивку в устройство, выключаем питание, загружаемся, драйвер показывает новые ресурсы.
проверяем соответствие верилогу:

Дело в том что использовать закрытые коры это не православно.
Поэтому первым делом я решил от нее избавится.
Я решил оставить тестбенч, временные констрейнты в UCF, некоторые настройки компиляции.
Все остальное (что было закрыто) я решил переписать на чистом верилоге!
Начать нужно с секции ввода вывода она скопирована из коры, и не является закрытой (cnc_top.v).
Она содержит, входные буферы и входные триггеры:
module cnc_top(
inout [31:0] AD,
...
...
IOBUF_PCI33_5 XPCI_ADB3 (.O(AD_IN3 ),.IO(AD[3 ]),.I(AD_O3 ),.T(OE_AD_N));
IOBUF_PCI33_5 XPCI_ADB2 (.O(AD_IN2 ),.IO(AD[2 ]),.I(AD_O2 ),.T(OE_AD_N));
IOBUF_PCI33_5 XPCI_ADB1 (.O(AD_IN1 ),.IO(AD[1 ]),.I(AD_O1 ),.T(OE_AD_N));
IOBUF_PCI33_5 XPCI_ADB0 (.O(AD_IN0 ),.IO(AD[0 ]),.I(AD_O0 ),.T(OE_AD_N));
...
FDPE XPCI_ADIQ3 (.Q(AD_I3 ),.D(AD_IN3 ),.C(CLK),.CE(1'b1),.PRE(RST));
FDPE XPCI_ADIQ2 (.Q(AD_I2 ),.D(AD_IN2 ),.C(CLK),.CE(1'b1),.PRE(RST));
FDPE XPCI_ADIQ1 (.Q(AD_I1 ),.D(AD_IN1 ),.C(CLK),.CE(1'b1),.PRE(RST));
FDPE XPCI_ADIQ0 (.Q(AD_I0 ),.D(AD_IN0 ),.C(CLK),.CE(1'b1),.PRE(RST));
...
// instantiate our PCI interface implementation
PCI PCI(
.AD_I({... AD_I3, AD_I2, AD_I1, AD_I0}),
.AD_O({... AD_O3, AD_O2, AD_O1, AD_O0}),
.OE_AD_N(OE_AD_N),
...
это все примитивы библиотеки unisim. Найти таблицы истинности можно в гугле.
Для всех остальных сигналов все делается похожим способом. Иногда вместо IOBUF используется IBUF или ОBUF если соответствующий сигнал только выход или только вход, и т.д.
Клок обрабатывается по особенному:
module cnc_top(
...
input PCLK,
...
);
...
// clock
IBUFG_PCI33_5 XPCI_CKI (.O(NUB),.I(PCLK));
BUFG XPCI_CKA (.O(CLK),.I(NUB));
...
Далее везде используется CLK
Особенно нужно остановиться на реализации ресета, в коре использовалась стандартная реализация:
always @(posedge RST)
begin
something_to_init = 111;
end
Для Xilinx есть более экономичный и элегантный способ:
http://www.eetimes.com/document.asp?doc_id=1278998
module cnc_top(
...
input RST_N,
...
);
IBUF_PCI33_5 XPCI_RST (.O(RST_I),.I(RST_N));
assign RST = ~RST_I;
// make sure that RST is bound to Global Reset Request,
// which will put all state to initial block defined one.
STARTUP_SPARTAN2 SPARTAN2(.GSR(RST));
Далее уже нужно сделать скелетон нашей реализации PCI (pci.v)
module PCI(
input [31:0] AD_I,
output reg [31:0] AD_O,
output reg OE_AD_N,
input [3:0] CBE_I_N,
output [3:0] CBE_O_N,
output OE_CBE_N,
input PAR_I,
output reg PAR_O,
output reg OE_PAR_N,
input FRAME_I_N,
output FRAME_O_N,
output OE_FRAME_N,
input TRDY_I_N,
output reg TRDY_O_N,
output reg OE_TRDY_N,
input IRDY_I_N,
output IRDY_O_N,
output OE_IRDY_N,
input STOP_I_N,
output reg STOP_O_N,
output reg OE_STOP_N,
input DEVSEL_I_N,
output reg DEVSEL_O_N,
output reg OE_DEVSEL_N,
input IDSEL_I,
input PERR_I_N,
output PERR_O_N,
output OE_PERR_N,
input SERR_I_N,
output OE_SERR_N,
output OE_REQ_N,
input GNT_I_N,
input CLK,
output OE_INTA_N,
output reg PING_DONE,
input RST
);
...
Заметьте что некоторые выходы помещены в регистры! это важно!.
когда я смотрел на реализацию в старой коре, они используют триггеры как для входов так для выходов, так и для сигналов включения выходов (OE_...)
Далее временная спецификация (сохранена от старой коры):
Первым делом они назначают выводам различные временные группы.
NET "SERR_N" TNM = PADS:PCI_PADS_C ; NET "PERR_N" TNM = PADS:PCI_PADS_C ; NET "REQ_N" TNM = PADS:PCI_PADS_G ; NET "GNT_N" TNM = PADS:PCI_PADS_G ; NET "FRAME_N" TNM = PADS:PCI_PADS_C ; NET "IRDY_N" TNM = PADS:PCI_PADS_C ; NET "TRDY_N" TNM = PADS:PCI_PADS_C ; NET "DEVSEL_N" TNM = PADS:PCI_PADS_C ; NET "STOP_N" TNM = PADS:PCI_PADS_C ; NET "CBE_N<3>" TNM = PADS:PCI_PADS_B ; NET "CBE_N<2>" TNM = PADS:PCI_PADS_B ; NET "CBE_N<1>" TNM = PADS:PCI_PADS_B ; NET "CBE_N<0>" TNM = PADS:PCI_PADS_B ; NET "PAR" TNM = PADS:PCI_PADS_P ; NET "IDSEL" TNM = PADS:PCI_PADS_C ; NET "INTA_N" TNM = PADS:PCI_PADS_X ; NET "RST_N" TNM = PADS:PCI_PADS_X ; # NET "AD<31>" TNM = PADS:PCI_PADS_D ; NET "AD<30>" TNM = PADS:PCI_PADS_D ; ... NET "AD<0>" TNM = PADS:PCI_PADS_D ;
названия будут: PCI_PADS_C, PCI_PADS_G, PCI_PADS_B и т.д.
Далее специальные названия для триггеров входных данных+комманд и выходных данных (команды я не использую)
... INST XPCI_CBIQ3 TNM = FFS:PCI_FFS_ICE ; INST XPCI_CBIQ2 TNM = FFS:PCI_FFS_ICE ; INST XPCI_CBIQ1 TNM = FFS:PCI_FFS_ICE ; INST XPCI_CBIQ0 TNM = FFS:PCI_FFS_ICE ; # INST XPCI_ADIQ31 TNM = FFS:PCI_FFS_ICE ; INST XPCI_ADIQ30 TNM = FFS:PCI_FFS_ICE ; INST XPCI_ADIQ29 TNM = FFS:PCI_FFS_ICE ; ... INST PCI/AD_O_0 TNM = FFS:PCI_FFS_OCE ; INST PCI/AD_O_1 TNM = FFS:PCI_FFS_OCE ; INST PCI/AD_O_2 TNM = FFS:PCI_FFS_OCE ; INST PCI/AD_O_3 TNM = FFS:PCI_FFS_OCE ; ... INST PCI/PING_DONE TNM = FFS:USER_FFS;
обратите в нимание, что при работе с шинами, в верилоге у нас выход с регистром называется AD_O, в процессе компиляции он разбивается на провода и соответственно в файле с константами его нужно указывать как AD_O_xxx
Если что-то удалилось из дизайна в процессе оптимиазации, то также эти регистры нельзя указать в файле с константами и это будет вызывать ошибки.
Следует отметить что поскольку некоторые сигналы PCI коммутируются одновременно (TRDY и STOP например), то компилятор будет считать их эквивалентными и пытаться убрать второй регистр, это крайне нежелательно, по причинам которые обьясню чуть позже, но пока лишь скажу что для того чтобы этого не происходило нужно убрать галку с
Equivalent Register Removal в настройках синтеза.
Далее определяем одни группы через логические операции над другими:
TIMEGRP "ALL_FFS" = FFS : EXCEPT : "USER_FFS" ; TIMEGRP "FAST_FFS" = "PCI_FFS_ICE" : "PCI_FFS_OCE" ; TIMEGRP "SLOW_FFS" = PCI_FFS : EXCEPT : "FAST_FFS" ;
Далее когда названия групп определены, переходим к спецификации констрейнтов оптимизации (скопировано из коры как есть):
################################################################################ # Time Specs ################################################################################ # # Important Note: The timespecs used in this section cover all possible # paths. Depending on the design options, some of the timespecs may # not contain any paths. Such timespecs are ignored by PAR and TRCE. # # Note: Timespecs are derived from the PCI Bus Specification, the # minimum clock delay of 0.000 ns, the maximum clock delay of 3.000 ns, # and a 90% tracking ratio between clock and data paths. # # Then, for paths on the primary global clock network: # # 1) Clk To Out = 11.000ns - 3.000ns = 8.000ns # 2) Setup = 7.000ns + 0.90 * 0.000ns = 7.000ns # 3) Grant Setup = 10.000ns + 0.90 * 0.000ns = 10.000ns # 4) AD/CBE Toff = 28.000ns - 3.000ns = 25.000ns # 5) AD/CBE Ton = 30.000ns + 11.000ns - 3.000ns = 38.000ns # 6) Period = = 30.000ns # # The following timespecs are for setup specifications. When using a # single clock, these timespecs are merged as pads-to-all. # TIMESPEC TS_ADF_SETUP = FROM : "PCI_PADS_D" : TO : ALL_FFS : 7.000 ; TIMESPEC TS_PAF_SETUP = FROM : "PCI_PADS_P" : TO : ALL_FFS : 7.000 ; TIMESPEC TS_BYF_SETUP = FROM : "PCI_PADS_B" : TO : ALL_FFS : 7.000 ; TIMESPEC TS_CNF_SETUP = FROM : "PCI_PADS_C" : TO : ALL_FFS : 7.000 ; TIMESPEC TS_GNF_SETUP = FROM : "PCI_PADS_G" : TO : ALL_FFS : 10.000 ; # # All critical input and output is registered to ensure clock to out # specifications are met by silicon. When using a single clock, these # timespecs are merged as all-to-pads. # TIMESPEC TS_CNF_CKOUT = FROM : ALL_FFS : TO : "PCI_PADS_C" : 8.000 ; TIMESPEC TS_GNF_CKOUT = FROM : ALL_FFS : TO : "PCI_PADS_G" : 8.000 ; # # Similar to above, the critical input and output paths are registered # to ensure clock to out specifications are made by silicon. Since this # interface uses address stepping, the clock to valid and clock to data # have different specifications. # TIMESPEC TS_ADF_CKOUT = FROM : "FAST_FFS" : TO : "PCI_PADS_D" : 8.000 ; TIMESPEC TS_ADS_TSOUT = FROM : "SLOW_FFS" : TO : "PCI_PADS_D" : 25.000 ; # TIMESPEC TS_BYF_CKOUT = FROM : "FAST_FFS" : TO : "PCI_PADS_B" : 8.000 ; TIMESPEC TS_BYS_TSOUT = FROM : "SLOW_FFS" : TO : "PCI_PADS_B" : 25.000 ; # TIMESPEC TS_PAF_CKOUT = FROM : "FAST_FFS" : TO : "PCI_PADS_P" : 8.000 ; TIMESPEC TS_PAS_TSOUT = FROM : "SLOW_FFS" : TO : "PCI_PADS_P" : 25.000 ; # # The design may be covered by a default period constraint. This is # generally sufficient when using a single clock. The period should # be set at the minimum PCI Bus clock period. # NET "PCLK" PERIOD = 30.000;
Если такой файл попробовать имплементировать, то имплементация выдаст ошибку таймингов.
Для того чтобы тайминги соответствовали спецификации PCI, нужно запихнуть входные триггеры, выходные триггеры а также триггеры Output Enable (OE_) в блоки ввода-вывода (IOB)
Для этого используются вот такие директивы:
INST "XPCI_CBIQ3" IOB = TRUE ; INST "XPCI_CBIQ2" IOB = TRUE ; INST "XPCI_CBIQ1" IOB = TRUE ; INST "XPCI_CBIQ0" IOB = TRUE ; # INST "XPCI_ADIQ31" IOB = TRUE ; INST "XPCI_ADIQ30" IOB = TRUE ; INST "XPCI_ADIQ29" IOB = TRUE ; INST "XPCI_ADIQ28" IOB = TRUE ; ... INST XPCI_IDSELIQ IOB = TRUE; INST XPCI_SERRIQ IOB = TRUE; INST XPCI_PERRIQ IOB = TRUE; INST XPCI_FRAMEIQ IOB = TRUE; INST XPCI_IRDYIQ IOB = TRUE; INST XPCI_DEVSELIQ IOB = TRUE; INST PCI/DEVSEL_O_N IOB = TRUE; INST PCI/OE_DEVSEL_N IOB = TRUE; INST XPCI_STOPIQ IOB = TRUE; INST XPCI_TRDYIQ IOB = TRUE; INST PCI/TRDY_O_N IOB = TRUE; INST PCI/OE_TRDY_N IOB = TRUE; INST PCI/STOP_O_N IOB = TRUE; INST PCI/OE_STOP_N IOB = TRUE; INST PCI/PAR_O IOB = TRUE; INST PCI/OE_PAR_N IOB = TRUE; INST PCI/AD_O_0 IOB = TRUE; INST PCI/AD_O_1 IOB = TRUE; INST PCI/AD_O_2 IOB = TRUE; INST PCI/AD_O_3 IOB = TRUE; ...
После этого все должно скомпилироваться успешно, а в конце маппинга, выдается вот такая вот таблица:
... | AD<28> | IOB | BIDIR | PCI33_5 | | | INFF | | IFD | | | | | | | | OUTFF | | | | AD<29> | IOB | BIDIR | PCI33_5 | | | INFF | | IFD | | | | | | | | OUTFF | | | | AD<30> | IOB | BIDIR | PCI33_5 | | | INFF | | IFD | | | | | | | | OUTFF | | | | AD<31> | IOB | BIDIR | PCI33_5 | | | INFF | | IFD | | | | | | | | OUTFF | | | | CBE_N<0> | IOB | BIDIR | PCI33_5 | | | INFF | | IFD | | CBE_N<1> | IOB | BIDIR | PCI33_5 | | | INFF | | IFD | | CBE_N<2> | IOB | BIDIR | PCI33_5 | | | INFF | | IFD | | CBE_N<3> | IOB | BIDIR | PCI33_5 | | | INFF | | IFD | | DEVSEL_N | IOB | BIDIR | PCI33_5 | | | OUTFF | | | | | | | | | | ENFF | | | | FRAME_N | IOB | BIDIR | PCI33_5 | | | INFF | | IFD | | IDSEL | IOB | INPUT | PCI33_5 | | | INFF | | IFD | | IRDY_N | IOB | BIDIR | PCI33_5 | | | INFF | | IFD | | PAR | IOB | BIDIR | PCI33_5 | | | OUTFF | | | | | | | | | | ENFF | | | | PING_DONE | IOB | OUTPUT | LVTTL | 12 | SLOW | OUTFF | | | | RST_N | IOB | INPUT | PCI33_5 | | | | | | | STOP_N | IOB | BIDIR | PCI33_5 | | | OUTFF | | | | | | | | | | ENFF | | | | TRDY_N | IOB | BIDIR | PCI33_5 | | | OUTFF | | | | | | | | | | ENFF | | | +------------------------------------------------------------------------------------------------------------------------+
Обратите внимание на INFF, OUTFF и ENFF.
Я использовал ровно такие настройки как и старая кора.
Далее собственно имплементация интерфейса.
я разделил это на три блока:
сигналы PCI и управление шиной:
reg IsWrite;
wire BAR1Matches = (CBE_I_N[3:1] == 3'b001) & (AD_I[31:BAR1_WINDOW_BITS]==Bar1Addr[31:BAR1_WINDOW_BITS]) & (AD_I[1:0] == 2'b00);
wire BAR2Matches = (CBE_I_N[3:1] == 3'b011) & (AD_I[31:BAR2_WINDOW_BITS]==Bar2Addr[31:BAR2_WINDOW_BITS]);
wire CFGMatches = (CBE_I_N[3:1] == 3'b101) & (AD_I[1:0] == 2'b00) & (AD_I[10:8] == 3'b000);
reg [1:0] AccessType; // local
parameter ACCESS_IO = 2'b00;
parameter ACCESS_MEM = 2'b01;
parameter ACCESS_CONFIG = 2'b10;
always @(posedge CLK)
begin
case(Transaction)
TX_STOP: begin
Transaction <= TX_IDLE;
end
TX_DEVSEL: begin
Transaction <= TX_TRDY;
end
TX_TRDY: begin
if (FRAME_I_N)
Transaction <= TX_STOP;
end
default: // TX_IDLE
begin
if (~FRAME_I_N & ((IDSEL_I & CFGMatches) | (PCICommandIOSpaceBit0 & BAR1Matches) | (PCICommandMEMSpaceBit1 & BAR2Matches)))
Transaction <= TX_DEVSEL;
end
endcase
end
always @(posedge CLK)
begin
if (Transaction == TX_IDLE) begin
IsWrite <= CBE_I_N[0];
AccessType <= {CBE_I_N[3], CBE_I_N[2]};
CurrentAddr <= (IDSEL_I ? AD_I[7:2] :
(CBE_I_N[2] ? {32'b0, AD_I[BAR2_WINDOW_BITS-1:2]} :
{32'b0, AD_I[BAR1_WINDOW_BITS-1:2]}));
end
DEVSEL_O_N <= ~(Transaction == TX_DEVSEL | Transaction == TX_TRDY);
OE_DEVSEL_N <= ~(Transaction == TX_DEVSEL | Transaction == TX_TRDY | Transaction == TX_STOP);
TRDY_O_N <= ~(Transaction == TX_TRDY);
OE_TRDY_N <= ~(Transaction == TX_TRDY | Transaction == TX_STOP);
STOP_O_N <= ~(Transaction == TX_TRDY);
OE_STOP_N <= ~(Transaction == TX_TRDY | Transaction == TX_STOP);
OE_AD_N <= ~((Transaction == TX_DEVSEL | Transaction == TX_TRDY) & ~IsWrite);
OE_PAR_N <= ~((Transaction == TX_DEVSEL | Transaction == TX_TRDY | Transaction == TX_STOP) & ~IsWrite);
// Output parity with 1 clock shift
PAR_O <= ^{AD_I, CBE_I_N};
end
Ввод данных:
always @(posedge CLK)
begin
if (Transaction == TX_TRDY & AccessType == ACCESS_CONFIG & IsWrite)
case (CurrentAddr)
1: begin
if (~CBE_I_N[0]) begin
PCICommandIOSpaceBit0 <= AD_I[0];
PCICommandMEMSpaceBit1 <= AD_I[1];
end
if (~CBE_I_N[1])
PCICommandIntrDisableBit10 <= AD_I[10];
end
4: begin
Bar1Addr[31:BAR1_WINDOW_BITS] <= AD_I[31:BAR1_WINDOW_BITS];
end
5: begin
Bar2Addr[31:BAR2_WINDOW_BITS] <= AD_I[31:BAR2_WINDOW_BITS];
end
15: begin
if (~CBE_I_N[0])
PCIInterruptLine <= AD_I[7:0];
end
endcase
end
always @(posedge CLK)
begin
if (Transaction == TX_TRDY & IsWrite & AccessType == ACCESS_MEM)
mem[CurrentAddr] <= AD_I;
end
always @(posedge CLK)
begin
if (Transaction == TX_TRDY & IsWrite & AccessType == ACCESS_IO)
io[CurrentAddr] <= AD_I;
end
Вывод данных:
always @(posedge CLK)
begin
case (AccessType)
ACCESS_CONFIG: begin
case(CurrentAddr)
0: AD_O <= {CFG_DEVICE, CFG_VENDOR};
1: AD_O <= {PCIStatus, PCICommand};
2: AD_O <= {CFG_CC, CFG_REVISION};
4: AD_O <= {Bar1Addr[31:BAR1_WINDOW_BITS], {(BAR1_WINDOW_BITS-1){1'b0}}, /* IO space */1'b1};
5: AD_O <= {Bar2Addr[31:BAR2_WINDOW_BITS], {(BAR2_WINDOW_BITS-4){1'b0}}, /* MEM space, Prefetch=true,location=32bit */4'b1000};
15: AD_O <= {PCIMaxLat, PCIMinGnt, PCIInterruptPin, PCIInterruptLine};
16: AD_O <= {32'b0, Bar1Addr};
17: AD_O <= {32'b0, Bar2Addr};
default: AD_O <= 32'b0;
endcase
end
ACCESS_MEM : begin
AD_O <= mem[CurrentAddr];
end
ACCESS_IO : begin
AD_O <= io[CurrentAddr];
end
endcase
end
как видно я использую два бара, один бар ввода вывода, а другой памяти.
размеры задаются параметрами:
parameter BAR1_WINDOW_BITS = 4; parameter BAR2_WINDOW_BITS = 4;
следует иметь ввиду, что размер окна памяти (BAR2_) не может быть меньше 16 адресов (потому как первые 4 бита конфигурационного регистра зарезервивованы) а размер окна I/O не может быть меньше 4 адресов , потому как адресация I/O 8-битная, и только два младших бита зарезервивованы.
Я жестко требую выравнивания памяти при обращении. Byte Enable не использую все должно быть выравнено на 32 бита.
Далее важно проверить соответствие спецификации, конечной Post-Route симуляции!
Например я пытался слишком быстро устанавливать TRDY в результате чего адреса читались как нули (не успевали устаовиться) я это исправил задержав TRDY на один клок, это кстати быстрее оригинальной коры на один клок, у них задерживалось на два клока относительно DEVSEL.
Генерируем файл верилога для конечной симуляции:

это создаст файлы в папке nergen.
Создадим новый .do файл для Modelsim: timesim.do
vlib work vlog -f cnc_tb_timesim.f vsim -novopt cnc_tb glbl view signals structure wave add wave -logic /CLK add wave -logic /RST_N add wave -literal -hex /AD add wave -literal -hex /CBE add wave -logic /PAR add wave -logic /FRAME_N add wave -logic /IRDY_N add wave -logic /TRDY_N add wave -logic /STOP_N add wave -logic /DEVSEL_N add wave -logic /REQ_N add wave -logic /GNT_N add wave -logic /SERR_N add wave -logic /PERR_N add wave -logic /IDSEL add wave -logic /INTR_A add wave -label "OPERATION" -radix ascii /cnc_tb/STM/operation run -all
Создадим новый .f файл для Modelsim: cnc_tb_timesim.f
+licq_all+ +access+r ./netgen/par/cnc_top_timesim.v ./busrecord.v ./dumb_arbiter.v ./dumb_targ32.v ./stimulus.v ./cnc_tb.v +libext+.vmd+.v -y $XILINX/verilog/src/unisims -y $XILINX/verilog/src/simprims
Запустим симуляцию:
проверим чтобы нигде не было красного (1'bx) что означает неинициализированные данные.
а синее (1'bz) какраз было в так называемых turnaround циклах PCI. естественно нас интересуют:
AD, CBE, TRDY, DEVSEL, STOP, PAR.
да, важно понять что PAR устанавливатся на следующем клоке, т.е. сначала мы пишем данные, а на следующем клоке устанавливаем их parity. и так это сдвинуто на протяжении всей транзакции (если использовать Burst, что мне не нужно):

Далее записываем новую прошивку в устройство, выключаем питание, загружаемся, драйвер показывает новые ресурсы.
проверяем соответствие верилогу:

no subject
Date: 2014-06-10 03:04 pm (UTC)no subject
Date: 2014-06-10 03:10 pm (UTC)без входных триггеров никак нельзя.
no subject
Date: 2014-06-10 07:34 pm (UTC)no subject
Date: 2014-06-10 09:26 pm (UTC)no subject
Date: 2014-06-10 09:27 pm (UTC)no subject
Date: 2014-06-10 10:33 pm (UTC)no subject
Date: 2014-06-11 06:08 am (UTC)no subject
Date: 2014-06-11 07:26 am (UTC)"Th from FRAME to *"
там нету такого, там есть OFFSET констрейнт.
no subject
Date: 2014-06-11 07:32 am (UTC)no subject
Date: 2014-06-11 10:16 am (UTC)вы мне предлагаете выести frame из группы входных триггеров? а то что он зависит от ad это ничего?
что я с ним буду делать одним?
ну допустим FRAME = 0, и что дальше? данных то нету для перехода стейт машины.
вы предлагаете вообще полностью констрейнты перелопатить? работать с шиной без входных триггеров??
там 7ns окно за которое нужно успеть сделать все до следующих триггееров, иначе данные уже не будут валидными и получаем нсетабольную работу.
Далее, что будет происходить в блоках always @ posedge CLK.
если HOLD TIME = 0 ? ISE автиматически вставит задержки чтобы это компенсировать?
как это все задать в ISE не имею понятия, и судя по гуглу мало кто имеет.
no subject
Date: 2014-06-11 10:19 am (UTC)Напоминаю: я предлагал брать FRAME напрямую, минуя входной триггер.
no subject
Date: 2014-06-11 01:00 pm (UTC)no subject
Date: 2014-06-11 01:29 pm (UTC)no subject
Date: 2014-06-11 02:14 pm (UTC)там не один FRAME участвует. там еше и AD_I должны сравниваться со значениями в памяти и только тогда DEVSEL
FRAME может быть и не наш а другого устройства.
кроме того вы не обеспечите никогда hold time = 0нс, никаким констрейнтом, если у вас не будет сдвинутый клок с ипользованием DLL. Так что не знаю, что вы пишите в вашем SDC файле.
в коде выше используется BUFG, для использования DLL нужно BUFGDLL
no subject
Date: 2014-06-11 02:54 pm (UTC)Там ещё и CBE должны совпасть, я в курсе, как работает PCI.
> кроме того вы не обеспечите никогда hold time = 0нс
Сдвигом по клоковому дереву, например. Без DLL.
Кроме того, на частоте 33МГц на современных ПЛИС значение задержки всё равно будет близким к нулю, зато вы б перестали терять такт. Тогда да, только wire на стейт-машине даст два такта. Тем же условием, которое переключается в состояние DEVSEL_TX, выставлять DEVSEL.
> Так что не знаю, что вы пишите в вашем SDC файле.
Меня на моём PCI три такта не парят, и входы и выходы у меня в регистрах.
no subject
Date: 2014-06-11 03:13 pm (UTC)тем что использую always(*) для подсчета CurrentAddr, IsWrite,
Если избавиться еще и от входных триггеров тогда DEVSEL можно сделать вообще FAST а не MED.
"Сдвигом по клоковому дереву," что это такое, как это на английском и поддерживается ли это в ISE 10?
значит ли это что мне нужно самому везде выставлять задержки? или оно само может это сделать?
no subject
Date: 2014-06-11 03:35 pm (UTC)____
На английском это clock skew compensation, clock delay buffer, из этой серии.
____
Про ISE я не знаю.
У меня просто написано что-то типа:
# QSF: -name TH_REQUIREMENT 0 ns -from DEVSEL -to *
set_min_delay 0.0 -from [get_ports {DEVSEL}] -to [get_registers *]
Обычно клоковые фронты САПР сам выравнивает, иначе б ничего не работало. У вас, кстати, PCI clk входной или внутри генерируется и на выход идёт? Если входной, то при задании нулевой задержки САПР будет стараться вытянуть, а временной анализатор потом покажет, справился ли он.
no subject
Date: 2014-06-11 03:43 pm (UTC)но такт потерен из-за входных триггеров.
FAST DEVSEL это когда он выставляется на следующем такте после фазы адреса, а у меня с пропуском такта.
># QSF: -name TH_REQUIREMENT 0 ns -from DEVSEL -to *
>set_min_delay 0.0 -from [get_ports {DEVSEL}] -to [get_registers *]
а причем тут DEVSEL ? Мы же про FRAME говорили.
>PCI clk входной
входной конечно, как он может внутри генерироваться?
no subject
Date: 2014-06-11 06:58 pm (UTC)Да без разницы, одинаковые констрейны
> как он может внутри генерироваться?
Легко, PPL-кой.
no subject
Date: 2014-06-12 06:27 am (UTC)кстати после добавления BUFGDLL у меня все тайминги улучшились на 3-4нс
no subject
Date: 2014-06-12 08:15 am (UTC)Опорная частота конечно всё равно нужна, зато один генератор на плате.
no subject
Date: 2014-06-12 08:47 am (UTC)no subject
Date: 2014-06-12 09:31 am (UTC)Но вообще вопрос был про " но я бы это не назвал генерацией."
(no subject)
From:no subject
Date: 2014-06-11 04:39 pm (UTC)в UCF констрайнты задал пока только для входов:
NET "PCLK" TNM_NET = "PCLK";
TIMESPEC "TS_PCLK" = PERIOD "PCLK" 30 ns HIGH 50%;
TIMEGRP PCI_PADS_D OFFSET = IN 7 ns VALID 7 ns BEFORE "PCLK";
TIMEGRP PCI_PADS_P OFFSET = IN 7 ns VALID 7 ns BEFORE "PCLK";
TIMEGRP PCI_PADS_B OFFSET = IN 7 ns VALID 7 ns BEFORE "PCLK";
TIMEGRP PCI_PADS_C OFFSET = IN 7 ns VALID 7 ns BEFORE "PCLK";
TIMEGRP PCI_PADS_G OFFSET = IN 10 ns VALID 10 ns BEFORE "PCLK";
получается что я могу частично использовать данные из flipflop а частично напрямую?
и САПР сам будет управлять когерентностью?
зачем же ждать временного анализатора, прямо при имплементации и показывает.
вот так если вообще убрать все входные триггеры:
WARNING:Par:62 - Your design did not meet timing. The following are some suggestions to assist you to meet timing in your design. Review the timing report using Timing Analyzer (In ISE select "Post-Place & Route Static Timing Report"). Go to the failing constraint(s) and select the "Timing Improvement Wizard" link for suggestions to correct each problem. Try the Design Goal and Strategies for Timing Performance (In ISE select Project -> Design Goals & Strategies) to ensure the best options are set in the tools for timing closure. Use the Xilinx "SmartXplorer" script to try special combinations of options known to produce very good results. INFO:Timing:2761 - N/A entries in the Constraints list may indicate that the constraint does not cover any paths or that it has no requested value. Number of Timing Constraints that were not applied: 1 Asterisk (*) preceding a constraint indicates it was not met. This may be due to a setup or hold violation.no subject
Date: 2014-06-11 07:02 pm (UTC)Предполагаю в случае несложного дизайна ничего страшного не произойдёт, времянка вытянется.
>зачем же ждать временного анализатора, прямо при имплементации и показывает.
Я не понимаю, как тогда реализована машина состояний. Точнее, догадываюсь, что состояние складывается из предыдущего + воздействие, переключающее на текущее - тогда да, можно выставить DEVSEL через такт.