在前面了解PCI配置空间、TLP以及Type0配置读的基础上,本文介绍Type0配置写子函数。该子函数产生一个Type0配置写TLP,先来看具体代码: /************************************************************ Task : TSK_TX_TYPE0_CONFIGURATION_WRITE Inputs : Tag, PCI/PCI-Express Reg Address, First BypeEn Outputs : Transaction Tx Interface Signaling Description : Generates a Type 0 Configuration Write TLP *************************************************************/ task TSK_TX_TYPE0_CONFIGURATION_WRITE; input tag_; input reg_addr_; input reg_data_; input first_dw_be_; begin if (trn_lnk_up_n) begin $display(" : Trn interface is MIA", $realtime); $finish(1); end TSK_TX_SYNCHRONIZE(0, 0, 0); trn_td <= #(Tcq) { 1'b0, 2'b10, 5'b00100, 1'b0, 3'b000, 4'b0000, 1'b0, 1'b0, 2'b00, 2'b00, 10'b0000000001, // 32 COMPLETER_ID_CFG, tag_, 4'b0000, first_dw_be_, // 64 COMPLETER_ID_CFG, 4'b0000, reg_addr_ , 2'b00, // 32 reg_data_ , reg_data_ , reg_data_ , reg_data_ // 64 }; trn_tsof_n <= #(Tcq) 0; trn_teof_n <= #(Tcq) 0; trn_trem_n <= #(Tcq) 2'b00; trn_tsrc_rdy_n <= #(Tcq) 0 ; TSK_TX_SYNCHRONIZE(1, 1, 1); trn_tsof_n <= #(Tcq) 1; trn_teof_n <= #(Tcq) 1; trn_trem_n <= #(Tcq) 2'b00; trn_tsrc_rdy_n <= #(Tcq) 1; end endtask // TSK_TX_TYPE0_CONFIGURATION_WRITE 有了对Type0配置读的了解,那么理解上面的代码就容易很多了,TLP前面32bit和配置读一样,唯一的区别在于TLP最后32bit带了1DW的写数据。写数据通过函数第三个参数调用的时候引入。 需要注意的是,上述代码中两次调用了函数 TSK_TX_SYNCHRONIZE,第一次调用( TSK_TX_SYNCHRONIZE(0, 0, 0); ),只是为了同步trn_clk和trn_tdst_rdyn_n,之后TLP信息被赋值给trn_td。第二次调用( TSK_TX_SYNCHRONIZE(1, 1, 1); )是为了同步信号,也是为了将TLP信息添加到本地buffer,并最终发送到输出log。也就是说,我们在仿真的时候看到很多下图所示的信息都是第二次调用函数 TSK_TX_SYNCHRONIZE的时候处理,第二次调用除了在仿真的时候的打印输出下图所示的信息外,还将TLP信息Log到输出文件(tx.dat和rx.dat) 这里有个疑问是在TSK_BAR_SCAN函数中,对每个BAR先使用Type配置写,写入的数据是”P_ADDRESS_MASK = 32'hffff_ffff;“,源代码给出的注释是对BAR空间写PCI_MASK来找到range。接着使用Type0配置读刚刚被写入MASK值的BAR空间,读回的数据保存在BAR_INIT_P_BAR_RANGE 里,也就是所谓“ 找到range ”。我的问题是,为什么对BAR空间写入MASK后,再对其进行type0读就可以得到range?