http://vkorehovisback.livejournal.com/ ([identity profile] vkorehovisback.livejournal.com) wrote in [community profile] engineering_ru2014-06-07 11:40 am

Как сделать PCI карту своими руками, Часть 6 (Больше верилога!)

Оригинал взят у [livejournal.com profile] vkorehovisback в Как сделать PCI карту своими руками, Часть 6 (Больше верилога!)
https://github.com/vkorehov/cncfpga2

Дело в том что использовать закрытые коры это не православно.
Поэтому первым делом я решил от нее избавится.
Я решил оставить тестбенч, временные констрейнты в 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.

Генерируем файл верилога для конечной симуляции:
prt
это создаст файлы в папке 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, что мне не нужно):

wave

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

[identity profile] caxapococ.livejournal.com 2014-06-07 08:36 pm (UTC)(link)
Вы достигли цели - я почувствовал себя ничтожеством.

[identity profile] rutinin.livejournal.com 2014-06-07 09:22 pm (UTC)(link)
Здесь хабр? и все сплошь айтишники.

[identity profile] poun.livejournal.com 2014-06-07 09:50 pm (UTC)(link)
Пойду китайский учить, это проще...

[identity profile] alikr.livejournal.com 2014-06-08 06:20 am (UTC)(link)
Слушайте, а может хватит троллить сообщество этой ерундой? Кому ваши исходники здесь нужны?

[identity profile] svkior.livejournal.com 2014-06-08 07:27 am (UTC)(link)
Спасибо. Мне понравилось.
Жалко, что ru_electronics помер.

[identity profile] svkior.livejournal.com 2014-06-08 09:48 am (UTC)(link)
Ну все таки engineering-ru - это общеинженерная конференция.
Больше не для инженеров, а для интересующихся.
Здесь, как я понял, "мяса" обычно не выкладывают, только вид на упаковку.

[identity profile] viktor-r21.livejournal.com 2014-06-08 01:20 pm (UTC)(link)
Это как раз для инженеров. Только инженеры разные бывают. Я например из области строительства и не особо понял смысл этой статьи. Понял что человек решил что-то запилить своими руками, а каков результат нет.
Вы, я думаю, тоже будете не в теме если писать все предпосылки к строительству готового сооружения.
Кстати, раз уж на то пошло, объясните пожалуйста что делает автор?

[identity profile] svkior.livejournal.com 2014-06-08 06:52 pm (UTC)(link)
> Это как раз для инженеров.

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

> Только инженеры разные бывают
Согласен, что пост очень тривиален, с точки зрения тех кто "в теме", и сильно нетривиален, для тех кто "не в теме".

> Понял что человек решил что-то запилить своими руками, а каков результат нет.
Автор поста (уже 6-го из серии) описывает процесс разработки платы управления станком с ЧПУ.
По уровню подачи материала - 2-3 курс студентов специальностей смежных с системотехникой.

> Вы, я думаю, тоже будете не в теме если писать все предпосылки к строительству готового сооружения.
Ну я, допустим, буду наверное исключением (10 лет по стройкам), но в целом согласен.

> Кстати, раз уж на то пошло, объясните пожалуйста что делает автор?
Коротенько точно не получиться. Но я постараюсь быстро накидать от "сотворения вселенной". Задавайте вопросы - буду уточнять.

У автора в предыдущих постах была разведена плата, которая вставляется в обычный персональный компьютер со слотом PCI.
На боковой панели платы выведен разъем, к которому можно подключить станок с ЧПУ.
На этой же плате расположена микросхема ПЛИС - программируемая логическая интегральная схема.

Эта микросхема - своеобразная "монтажная плата" для создания интегральных схем.
То есть можно в графическом редакторе нарисовать принципиальную электрическую схему (Всякие элементы "И", "ИЛИ", "НЕ", Регистры, Суматоры, Умножители и прочую фигню), присвоить входам схемы определенные ножки ПЛИС, затем "прошить" ПЛИС (при помощи специального програматора).
После этого ПЛИС будет работать прямо так, как нарисовали на схеме.
Раньше для "прошивания" схемы нужно было брать в руки паяльник, кучу проводов и кучу микросхем и сидеть это все и паять.

Далее. Рисовать схемы инженерам достаточно быстро надоело, потому что микросхемы стали сложнее и схемы на одну микросхемку стали занимать целые атласы (как архитектурка на типовой дом культуры на 1000 мест), и естественно, это народу надоело. Придумали язык описания аппаратуры.
На котором описывается схема в виде "почти как Паскаль или Си", только это не программа, а именно описание схемы. (Автор поста использует язык Verilog HDL).
И придумали так же "Синтезаторы" - программы перевода с языка описания аппаратуры в принципиальную электрическую схему. А далее по накатанной.

На этом же языке описания начали писать тесты - специальные програмки, которые моделируют входные сигналы на микросхему и проверяют, что выходные сигналы соответствуют тому, что хотел программист.
Такие програмки исполняются в специальных программах Моделлерах (в данном посте автор использует ModelSim).

Это что касается "от сотворения мира".

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

Если после столь путанного и туманного объяснения у вас возникли какие то вопросы и не пропал интерес, то спрашивайте что угодно, постараюсь ответить в меру своих сил.

[identity profile] viktor-r21.livejournal.com 2014-06-08 08:37 pm (UTC)(link)
Спасибо за объяснение. Написали вы доступно. Теперь я кажется понял к чему в журнале у автора замысловатый рисунок столешницы).

[identity profile] 22sobaki.livejournal.com 2014-06-08 07:48 pm (UTC)(link)
Обращаюсь к вам и к топикстартеру.
Не хотите ли завести специальное сообщество под электронику, программирование и т.п.? Очень уж специфические вещи, и подавляющему большинству читателей здесь не то что малопонятны, а вообще мимо. С другой стороны, спецам, я так понимаю, они интересны и не публиковать их жалко. Здесь новое сообщество можно попиарить. М?

[identity profile] svkior.livejournal.com 2014-06-08 07:54 pm (UTC)(link)
Я - за.
Готов следить за сообществом.
Готов писать в сообщество.
Готов писать в engineering-ru выжимки из постов сообщества.
Не хочу, чтобы это было бы очередное сообщество из 3-х человек.
Что для этого нужно делать?

[identity profile] 22sobaki.livejournal.com 2014-06-08 08:13 pm (UTC)(link)
Ну как. Учреждайте сообщество. Насколько оно будет многочисленное и живое, зависит от вас и многого другого, сами понимаете. Со своей стороны могу:
- помочь советами, если надо
- попиарить здесь, как уже сказал
- и направлять к вам авторов постов по такой специальной электронной тематике, которые приходят в инжиниринг

[identity profile] svkior.livejournal.com 2014-06-08 08:18 pm (UTC)(link)
А в это сообщество писать посты про большие и интересные системы, типа - уже весь станок с ЧПУ, как он работает. Система звукоусиления кинотеатра, Система театрального освещения и т.п.

Я правильно понял?

[identity profile] 22sobaki.livejournal.com 2014-06-08 08:25 pm (UTC)(link)
Совершенно верно.

[identity profile] serokoy.livejournal.com 2014-06-08 03:38 pm (UTC)(link)
1. Почему вы присваиваете значение регистрам через =, а не через <= ?
2. У вас написание си-стайл, то есть будто вы программу пишете, а не железо описываете. Зря. Чем проще описание, тем лучше. Вы к тому же кидаете всё под один always, читабельности это не прибавляет.

[identity profile] serokoy.livejournal.com 2014-06-08 07:00 pm (UTC)(link)
1. Нет. Ну, при синтезе вы не заметите разницу, но при моделировании даже без задержек - ещё как. Тем более у вас D-trigger, других там нет в ПЛИС, так что во избежание непонимания при вылавливании косяков используйте D-триггер, описанный с неблокирубщим присвоением. Ну и просмотрите warning'и от Вивадо - он не мог не смолчать на это.

2. По-хорошему каждый триггер пишется своим always. Ну или если у вас пачка триггеров, работающих на одинаковую задачу, то их можно сунуть в один always-begin-end.
Далее, пример Си-стайла:
IsMemory = 1'b1;
Ну как-то в цифровой схеме присваивать константу не по сбросу... Всё равно что кидать вход на питание или землю.

Переписывается в такое вот примерно:
always @(posedge CLK)
IsMemory <= IsMemory ? ~(PCICommandIOSpaceBit0 & BAR1Matches) : PCICommandMEMSpaceBit0 & BAR2Matches;

Просто опять же, синтезаторы у САПР ныне мощные. У Quartus получше, у Vivado похуже. Но и у них бывает (и ещё как бывает) сносит крышу, и кроме удобства читаемости вы можете нарваться на то, что он сделает работающую, но неоптимальную логику. При невысоких для нынешних ПЛИС PCI-ных частотах это пока что неважно. Но потом вы можете что-то не вытянуть по времянке.

[identity profile] serokoy.livejournal.com 2014-06-08 07:15 pm (UTC)(link)
1. Ну вы ж под Xilinx пишете? Это САПР, который разводит под него.
2. Потому что у вас по какому-то условию регистру присваивается 1. Так? А по второму триггер сбрасывается в ноль. Потому и стоит оператор "?": если IsMemory = "0", то по "PCICommandMEMSpaceBit0 & BAR2Matches" триггер становится равным "1". А скидывается уже по "PCICommandIOSpaceBit0 & BAR1Matches".
LUT может меньше и не генерировать, но вы сами ж через некоторое время запутаетесь в своём коде. Честное слово. :)
Контроллеры вообще хорошо писать на стейт-машине, по которой потом переключатся триггеры.

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-08 19:34 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-08 19:39 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-08 20:00 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-09 06:18 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-09 07:39 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-09 09:27 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-09 10:44 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-10 06:19 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-10 06:35 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-10 08:21 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-10 13:11 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-10 15:05 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-10 19:29 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-10 21:26 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-11 06:11 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-10 15:04 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-10 19:34 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-10 21:27 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-11 06:08 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-11 07:32 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-11 10:19 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-11 13:29 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-11 14:54 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-11 15:35 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-11 18:58 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-12 08:15 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-12 09:31 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-11 19:02 (UTC) - Expand

[identity profile] serokoy.livejournal.com 2014-06-08 07:16 pm (UTC)(link)
По поводу разницы в присвоениях и возможных косяках при этом вот статья: http://svo.2.staticpublic.s3-website-us-east-1.amazonaws.com/verilog/assignments/

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-08 19:54 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-08 20:42 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-08 21:08 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-08 21:38 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-08 21:15 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-08 21:16 (UTC) - Expand

(no subject)

[identity profile] serokoy.livejournal.com - 2014-06-08 21:24 (UTC) - Expand

[identity profile] ikaktys.livejournal.com 2014-06-11 02:31 pm (UTC)(link)
а каким софтом будет все это богатство управляться ?