Оригинал взят у
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-08 11:54 pm (UTC)с самого IOBUF не пробовал(имеется ввиду например с AD_IN15 или AD[15] ?), но в триггере сразу за ним (который помещается в INFF) в этот момент находятся иксы (по крайней мере на симуляции)
попробую с IOBUF взять, тогда CurrentOutput не нужен.