项目简述

本次项目在计算机将图像数据信息通过千兆网发送给FPGA后,由于接收到的数据可能混乱和无效,需要对数据CRC校验和无效包过滤。

项目原理及框图

对iddr_ctrl模块的输入数据和使能信号,分成两部分处理:第一部分数据通过包有效检验,CRC32校验(单独建一个模块例化使用),包长度统计(通过rx_en和一个计数器来实现)后,把这三部分处理后的标志信号放在一个status_fifo中缓存,用一个16位位宽的status_value寄存器缓存;第二部分,将数据和使能信号缓存在一个数据FIFO中data_fifo模块中,待第一部分的status_fifo中缓存好了数据的状态信息如包有效标志,包长度,CRC32校验正确标志。通过判断status_value寄存器中的包有效标志,CRC32校验正确标志拉高,则数据校验和过滤完成,就将数据frx_data和使能信号frx_en输出。

大概的设计思路,先设计CRC32校验模块,然后再设计上面的rx_filter_buffer用来对数据进行过滤和有效包检验。

CRC32校验原理是本次设计的核心,对于CRC-32的理解:第一步,刚开始先看看CRC的原理,第二步看书上对CRC-8的两种推导,一是实际手算推导,二是移位寄存器方式实现CRC(硬件的实现方式)。第三步,则是在理解CRC-8的基础上,CRC-32与它类似,主要是理解移位寄存器方式和对应的CRC校验码。

1.CRC的原理是通过网上博客讲解,我的理解如下:

根本思想就是先在要发送的帧后面附加一个数(这个就是用来校验的校验码,但要注意,这里的数也是二进制序列的,下同),生成一个新帧发送给接收端。当然,这个附加的数不是随意的,它要使所生成的新帧能与发送端和接收端共同选定的某个特定数整除(注意,这里不是直接采用二进制除法,而是采用一种称之为“模2除法”)。到达接收端后,再把接收到的新帧除以(同样采用“模2除法”)这个选定的除数。因为在发送端发送数据帧之前就已通过附加一个数,做了“去余”处理(也就已经能整除了),所以结果应该是没有余数。如果有余数,则表明该帧在传输过程中出现了差错。

CRC校验原理就是以下几个步骤:

(1)先选择(可以随机选择,也可按标准选择,具体在后面介绍)一个用于在接收端进行校验时,对接收的帧进行除法运算的除数(是二进制比较特串,通常是以多项方式表示,所以CRC又称多项式编码方法,这个多项式也称之为“生成多项式”)。

(2)看所选定的除数二进制位数(假设为k位),然后在要发送的数据帧(假设为m位)后面加上k-1位“0”,然后以这个加了k-1个“0“的新帧(一共是m+k-1位)以“模2除法”方式除以上面这个除数,所得到的余数(也是二进制的比特串)就是该帧的CRC校验码,也称之为FCS(帧校验序列)。但要注意的是,余数的位数一定要是比除数位数只能少一位,哪怕前面位是0,甚至是全为0(附带好整除时)也都不能省略

(3)再把这个校验码附加在原数据帧(就是m位的帧,注意不是在后面形成的m+k-1位的帧)后面,构建一个新帧发送到接收端,最后在接收端再把这个新帧以“模2除法”方式除以前面选择的除数,如果没有余数,则表明该帧在传输过程中没出错,否则出现了差错。

通过以上介绍,大家一定可以理解CRC校验的原理,并且不再认为很复杂吧。

从上面可以看出,CRC校验中有两个关键点:一是要预先确定一个发送端和接收端都用来作为除数的二进制比特串(或多项式);二是把原始帧与上面选定的除进行二进制除法运算,计算出FCS。前者可以随机选择,也可按国际上通行的标准选择,但最高位和最低位必须均为“1”,如在IBM的SDLC(同步数据链路控制)规程中使用的CRC-16(也就是这个除数一共是17位)生成多项式g(x)= x16 + x15 + x2 +1(对应二进制比特串为:11000000000000101);而在ISO HDLC(高级数据链路控制)规程、ITU的SDLC、X.25、V.34、V.41、V.42等中使用CCITT-16生成多项式g(x)=x16 + x15 + x5 +1(对应二进制比特串为:11000000000100001)。

2.CRC-8的实现

对于FPGA来言,主要是通过移位寄存器实现CRC校验码。

对于CRC-32的原理,我们直接给出它的校验码的化简结果,原理和CRC-8类似

对于以上原理的分析,设计的CRC-32的Verilog代码如下:主要是对校验码生成原理的理解,而校验结果直接通过查找表实现即可。

module crc32_d8_rec_02(
input wire restb,
input wire sclk,
input wire dsin, //输入数据有效标志
input wire[7:0] din,
output wire crc32_cal_end,
output wire crc_err

);

reg dsin_r,crc32_end_r,crc_err_r;
wire [7:0]d;
reg [31:0]crc32_value;
wire[31:0]c;

assign d = din;
always @(posedge sclk ) begin
begin
// reset
dsin_r<=dsin;
end

end
assign c =crc32_value ;

assign crc32_cal_end = crc32_end_r;
assign crc_err = crc_err_r;

always@(posedge sclk)begin
if(dsin==1'b0)begin
crc32_value <=32'hffffffff;

end
else begin
crc32_value[0]<=c[24]^c[30]^d[1]^d[7];
crc32_value[1]<=c[25]^c[31]^d[0]^d[6]^c[24]^c[30]^d[1]^d[7];

crc32_value[2]<=c[26]^d[5]^c[25]^c[31]^d[0]^d[6]^c[24]^c[30]^d[1]^d[7];
crc32_value[3]<=c[27]^d[4]^c[26]^d[5]^c[25]^c[31]^d[0]^d[6];
crc32_value[4]<=c[28]^d[3]^c[27]^d[4]^c[26]^d[5]^c[24]^c[30]^d[1]^d[7];
crc32_value[5]<=c[29]^d[2]^c[28]^d[3]^c[27]^d[4]^c[25]^c[31]^d[0]^d[6]^c[24]^c[30]^d[1]^d[7];
crc32_value[6]<=c[30]^d[1]^c[29]^d[2]^c[28]^d[3]^c[26]^d[5]^c[25]^c[31]^d[0]^d[6];
crc32_value[7]<=c[31]^d[0]^c[29]^d[2]^c[27]^d[4]^c[26]^d[5]^c[24]^d[7];
crc32_value[8]<=c[0]^c[28]^d[3]^c[27]^d[4]^c[25]^d[6]^c[24]^d[7];
crc32_value[9]<=c[1]^c[29]^d[2]^c[28]^d[3]^c[26]^d[5]^c[25]^d[6];
crc32_value[10]<=c[2]^c[29]^d[2]^c[27]^d[4]^c[26]^d[5]^c[24]^d[7];
crc32_value[11]<=c[3]^c[28]^d[3]^c[27]^d[4]^c[25]^d[6]^c[24]^d[7];
crc32_value[12]<=c[4]^c[29]^d[2]^c[28]^d[3]^c[26]^d[5]^c[25]^d[6]^c[24]^c[30]^d[1]^d[7];
crc32_value[13]<=c[5]^c[30]^d[1]^c[29]^d[2]^c[27]^d[4]^c[26]^d[5]^c[25]^c[31]^d[0]^d[6];
crc32_value[14]<=c[6]^c[31]^d[0]^c[30]^d[1]^c[28]^d[3]^c[27]^d[4]^c[26]^d[5];
crc32_value[15]<=c[7]^c[31]^d[0]^c[29]^d[2]^c[28]^d[3]^c[27]^d[4];
crc32_value[16]<=c[8]^c[29]^d[2]^c[28]^d[3]^c[24]^d[7];
crc32_value[17]<=c[9]^c[30]^d[1]^c[29]^d[2]^c[25]^d[6];
crc32_value[18]<=c[10]^c[31]^d[0]^c[30]^d[1]^c[26]^d[5];
crc32_value[19]<=c[11]^c[31]^d[0]^c[27]^d[4];
crc32_value[20]<=c[12]^c[28]^d[3];
crc32_value[21]<=c[13]^c[29]^d[2];
crc32_value[22]<=c[14]^c[24]^d[7];
crc32_value[23]<=c[15]^c[25]^d[6]^c[24]^c[30]^d[1]^d[7];
crc32_value[24]<=c[16]^c[26]^d[5]^c[25]^c[31]^d[0]^d[6];
crc32_value[25]<=c[17]^c[27]^d[4]^c[26]^d[5];
crc32_value[26]<=c[18]^c[28]^d[3]^c[27]^d[4]^c[24]^c[30]^d[1]^d[7];
crc32_value[27]<=c[19]^c[29]^d[2]^c[28]^d[3]^c[25]^c[31]^d[0]^d[6];
crc32_value[28]<=c[20]^c[30]^d[1]^c[29]^d[2]^c[26]^d[5];
crc32_value[29]<=c[21]^c[31]^d[0]^c[30]^d[1]^c[27]^d[4];
crc32_value[30]<=c[22]^c[31]^d[0]^c[28]^d[3];
crc32_value[31]<=c[23]^c[29]^d[2];

end

end

//数据有效标志下降沿标志数据结束
always@(dsin or dsin_r)begin
if(!dsin && dsin_r)
crc32_end_r<=1'b1;
else begin
crc32_end_r<=1'b0;
end
end

//数据校验出错标志
always @(posedge sclk or negedge restb) begin
if (restb==1'b0) begin
// reset
crc_err_r<=1'b0;
end
else if (crc32_end_r==1'b1 && crc32_value!=32'hc704dd7b) begin
crc_err_r<=1'b1;
end
else
crc_err_r<=1'b0;
end

endmodule

第二个模块,数据的包有效检验和过滤模块的设计

1、检测有效包,目前我们定义上位机软件设置源端口为 1 234 ,目的端口 1 23
U DP 协议字段为 8 ’h11 这样可以证明此包为来自于我们需要的应用 。 我们
在代码中需要检测这三个字段 检测到了后需要把此包的 vld_pkg 有效 这
样将来读取此包数据时会下发到下一级 。针对上图就是红色圈着的五个字节,只需要和标准值匹配则表明包有效。

关键代码如下

always @(posedge sclk) begin
if(rst) begin
pkg_value <= 40'd0;
end
else if(PHY_rx_cnt==31 || (PHY_rx_cnt>=42 && PHY_rx_cnt<=45)) begin
pkg_value <= {pkg_value[31:0],PHY_rxd};
end
end

always @(posedge sclk) begin
if(rst) begin
pkg_vld <= 1'b0;
end
else if(PHY_rx_neg && pkg_value==40'h11_04d2_007b) begin
pkg_vld <= 1'b1;
end
else begin
pkg_vld <= 1'b0;
end
end

2、C RC 是为了证明一个数据包是否出错 此 C RC 为 32 位校验码 只能用于验证
数据是否出错不能纠正错误 。 CRC 校验的区域为去掉 一帧 前 8 字节的所有数
据(去掉 7 个 0x 55 和 1 个 0xd 5

关键代码如下:rx_en_new为CRC校验模块的使能信号,是在去掉帧头后驱动这个模块

reg rx_en_new;
always @(posedge sclk or negedge rx_en) begin
if (rx_en == 1'b0) begin
rx_en_new <= 1'b0;
end
else if (PHY_rx_cnt == 'd7) begin
rx_en_new <= 1'b1;
end
end

rx_filter_buffer模块的时序图如上所示,根据时序图和原理框图可以很快设计出本模块代码如下所示:

module rx_filter_buffer(
input wire sclk,
input wire rst,
input wire [7:0]PHY_rxd,
input wire rx_en,
output reg frx_en,
output reg [7:0]frx_data

);

reg data_rd_en_r1;
wire [6 : 0] rd_data_count;
wire status_fifo_empty;
wire status_rd_en;
wire [15:0]status_dout;
wire [7:0]data_dout;
reg [15:0] status_value;

wire crc32_ok;
wire crc_err;
wire crc32_end;

assign crc32_ok =~crc_err ;
reg pkg_vld;

wire pkg_end;
reg [15:0] pkg_status;
assign pkg_end= pkg_vld;
assign status_rd_en = ~status_fifo_empty;
parameter PKG_LENTH = 1077;

reg [13:0] data_rd_en_cnt;
wire data_rd_en;
reg data_rd_en_r;
assign data_rd_en = data_rd_en_r1;

reg [39:0] pkg_value;
reg [10:0] pkg_len;
reg [12:0] PHY_rx_cnt;
reg rx_en_new;
always @(posedge sclk or negedge rx_en) begin
if (rx_en == 1'b0) begin
rx_en_new <= 1'b0;
end
else if (PHY_rx_cnt == 'd7) begin
rx_en_new <= 1'b1;
end
end
//status_fifo
status_fifo status_wr16rd16_8192 (
.wr_clk(sclk), // input wire wr_clk
.rd_clk(sclk), // input wire rd_clk
.din(pkg_status), // input wire [15 : 0] din
.wr_en(pkg_end), // input wire wr_en
.rd_en(status_rd_en), // input wire rd_en
.dout(status_dout), // output wire [15 : 0] dout
.full(full), // output wire full
.empty(status_fifo_empty), // output wire empty
.rd_data_count(rd_data_count) // output wire [6 : 0] rd_data_count
);

crc32_d8_rec_02 inst_crc32_d8_rec_02
(
.restb (1'b1),
.sclk (sclk),
.dsin (rx_en_new),
.din (PHY_rxd),
.crc32_cal_end (crc32_end),
.crc_err (crc_err)
);

//data_rd_cnt;dataFIFO读出的数据个数
always @(posedge sclk )
if(rst==1'b1) begin
// reset
data_rd_en_cnt<='d0;
end

else if(data_rd_en)begin
data_rd_en_cnt<=data_rd_en_cnt+'d1;
end
else begin//datafifo刚好读完
data_rd_en_cnt<='d0;
end

always @(posedge sclk )
data_rd_en_r1<=data_rd_en_r;

//data_rd_en
always @(posedge sclk ) begin
if(rst==1'b1 ) begin
// reset
data_rd_en_r<='d0;
end
else if(status_rd_en)begin
data_rd_en_r<='b1;
end
else if((data_rd_en_cnt==PKG_LENTH-1) && data_rd_en)begin
data_rd_en_r<='d0;
end
end

always @(posedge sclk ) begin
if (rst==1'b1) begin
// reset
PHY_rx_cnt<=13'd0;
end
else if (rx_en==1'b1) begin
PHY_rx_cnt<=PHY_rx_cnt+1'b1;
end

end

// always @(posedge sclk ) begin
// if (rst==1'b1) begin
// // reset
// PHY_rx_cnt<=11'd0;
// end
// else if (rx_en==1'b1) begin
// if(PHY_rx_cnt==PKG_LENTH )
// PHY_rx_cnt<=11'd0;
// else
// PHY_rx_cnt<=PHY_rx_cnt+1'b1;
// end
// else begin
// PHY_rx_cnt<=11'd0;
// end
// end

reg phy_rx_ctrl_r;
always @(posedge sclk ) begin
begin
// reset
phy_rx_ctrl_r<=rx_en;
end
end

reg PHY_rx_neg;
always @(posedge sclk ) begin
if(!rx_en &&phy_rx_ctrl_r ) begin
// reset
PHY_rx_neg<=1'b1;
end
else begin
PHY_rx_neg<=1'b0;
end
end
//pkg_len
always @(posedge sclk ) begin
if(rst==1'b1 ) begin
// reset
pkg_len<=11'd0;
end
else if(PHY_rx_neg) begin
pkg_len<=PKG_LENTH;
end
end

//包有效字段检验
//5byte-40bit数据和标准值一样
always @(posedge sclk) begin
if(rst) begin
pkg_value <= 40'd0;
end
else if(PHY_rx_cnt==31 || (PHY_rx_cnt>=42 && PHY_rx_cnt<=45)) begin
pkg_value <= {pkg_value[31:0],PHY_rxd};
end
end

always @(posedge sclk) begin
if(rst) begin
pkg_vld <= 1'b0;
end
else if(PHY_rx_neg && pkg_value==40'h11_04d2_007b) begin
pkg_vld <= 1'b1;
end
else begin
pkg_vld <= 1'b0;
end
end

always @(posedge sclk ) begin
if(rst==1'b1 ) begin
// reset
pkg_status<=16'd0;
end
else if(pkg_vld) begin
pkg_status<={crc32_ok,pkg_vld,pkg_len};
end
else begin
pkg_status<=16'd0;
end
end

//status_value
always @(posedge sclk ) begin
if(rst==1'b1 ) begin
// reset
status_value<=16'd0;
end
else if(status_rd_en) begin
status_value<=status_dout;
end

end

//data_fifo
data_fifo asfifo_wr8X8192_rd8X8192 (
.wr_clk(sclk), // input wire wr_clk
.rd_clk(sclk), // input wire rd_clk
.din(PHY_rxd), // input wire [7 : 0] din
.wr_en(rx_en), // input wire wr_en
.rd_en(data_rd_en), // input wire rd_en
.dout(data_dout), // output wire [7 : 0] dout
.full(full), // output wire full
.empty(empty) // output wire empty
);

always @(posedge sclk) begin
if(rst==1'b1) begin
frx_data <= 'd0;
frx_en <=1'b0;
end
else if(data_rd_en) begin
frx_data <= data_dout;
frx_en <=data_rd_en;
end
// else begin
// frx_data <= 'd0;
// frx_en <=1'b0;
// end
end

endmodule

由于没有A7的开发板,故本次设计了仿真文件验证:

仿真程序如下:

`timescale 1ns / 1ps
`define CLOCK 8

module tb_gigbit;

reg rst_n ;
reg gb_tx_data_en ;
reg gb_tx_clk ;
reg [ 7:0] gb_tx_data ;
wire [7:0] frx_data;
wire frx_en;

reg [7:0] mem[1073:0];
initial begin
$readmemb("./data.txt",mem);
end
// //D:\FPGA_class\3ZQ\10qianz
// initial begin
// $readmemb("D:/FPGA_class/3ZQ/10qianz/data.txt",mem);
// end

initial begin
rst_n = 1'b0;
gb_tx_clk = 1'b0;
gb_tx_data = 8'd0;
gb_tx_data_en = 1'b0;
#1000;
rst_n = 1'b1;
gen_data();
#100;
gb_tx_data_en = 1'b0;

end

always #(`CLOCK/2) gb_tx_clk = ~gb_tx_clk;
// task gen_data;
// integer i;
// integer j;
// begin
// @(posedge gb_tx_clk);
// gb_tx_data_en = 1'b1;
// for(j=0;j<1073;j=j+1)begin
// for (i=0;i<1073;i=i+1)begin
// gb_tx_data=mem[i];
// @(posedge gb_tx_clk);
// end
// end
// gb_tx_data = 8'h0;
// gb_tx_data_en = 1'b0;
// #(1000*`CLOCK);
// @(posedge gb_tx_clk);
// end
// endtask

task gen_data;
integer i;
begin
@(posedge gb_tx_clk);
gb_tx_data_en = 1'b1;
for (i=0;i<1073;i=i+1)begin
gb_tx_data=mem[i];
@(posedge gb_tx_clk);
end
@(posedge gb_tx_clk);
gb_tx_data = 8'h0;
gb_tx_data_en = 1'b0;
#(100*`CLOCK);
@(posedge gb_tx_clk);
end
endtask

rx_filter_buffer inst_rx_filter_buffer (
.sclk (gb_tx_clk),
.rst (~rst_n),
.PHY_rxd (gb_tx_data),
.rx_en (gb_tx_data_en),
.frx_en (frx_en),
.frx_data (frx_data)
);

endmodule

仿真波形如下:

这次千兆网的校验和数据包有效的测验,我领悟到,针对一个新的设计,关键是两点:第一是拆分。第二是针对同一个知识点,用多种方式去理解。

参考资料:

V3学院

CSDN博客

https://www.cnblogs.com/liushui-sky/p/9962123.html

咸鱼FPGA

https://www.cnblogs.com/xianyufpga/p/12147156.html

千兆网数据CRC检验和过滤的更多相关文章

  1. linux fedora 14(内核2.6.35.6) PF_RING+libpcap 极速捕获千兆网数据包,不丢包

    前面讲到了libpcap 捕获数据包,尤其在千兆网的条件下,大量的丢包,网上搜索好久,大概都是PF_PACKET +MMAP,NAPI,PF_RING之类的方法,我对PF_RING+libpcap进行 ...

  2. FPGA千兆网UDP协议实现

    接着上一篇百兆网接口的设计与使用,我们接着来进行FPGA百兆网UDP(User Datagram Protocol)协议的设计. 1)UDP简介 在此,参考博主夜雨翛然的博文“https://www. ...

  3. Dalsa Sherlock 直连千兆网相机(通用驱动)

    支持 Sherlock 7.1.7.2,用于千兆网相机与 Sherlock 的连接. 可适用于很多厂商的相机,如:巴斯勒(Basler),JAI,堡盟相机(Baumer),灰点相机(Point Gre ...

  4. 010 FPGA千兆网UDP通信【转载】

    一.以太网帧格式 图8‑12以太网帧格式 表8‑5以太网帧格式说明 类别 字节数 说明 前导码(Preamble) 8 连续 7 个 8'h55 加 1 个 8'hd5,表示一个帧的开始,用于双方设备 ...

  5. 华为S5700S-52P-LI-AC千兆网管交换机web登录界面配置

    研究一下午,包装附的说明书根本就是错误的,通过技术售后和官方的文档结合,总算可以登录交换机的web管理界面. 首先需要使用通讯控制线缆(包装中附)连接电脑和交换机,一头接交换机的Console口,一头 ...

  6. 迅为双核imx6DL核心板_ARM定制专家_Cortex SATA 千兆网 4G GPS

    核心板参数 尺寸:51mm*61mm CPU:Freescale Cortex-A9 双核精简版 i.MX6DL,主频 1.2 GHz 内存:1GB DDR3 存储:8GB EMMC 存储 EEPRO ...

  7. 011 FPGA千兆网TCP通信【转载】

    一.LWIP 首先通过上面的简单分析,我们应该很清楚一件事:TCP协议很复杂,光握手过程就需要"三次握手.四次挥手"的复杂过程,不是特别适合FPGA的纯逻辑实现,因为用FPGA实现 ...

  8. 【转】简谈基于FPGA的千兆以太网

    原文地址: http://blog.chinaaet.com/luhui/p/5100052903 大家好,又到了学习时间了,学习使人快乐.今天我们来简单的聊一聊以太网,以太网在FPGA学习中属于比较 ...

  9. FPGA设计千兆以太网MAC(2)——以太网协议及设计规划

    上篇该系列博文中通过MDIO接口实现了PHY芯片的状态检测,验证其已处于1000M 全双工工作模式.在设计MAC逻辑之前,要先清楚MAC与PHY之间的接口以及以太网协议细节,这样才能保证网络的兼容性. ...

随机推荐

  1. Windows RestartManeger重启管理器

    介绍   重启管理器API可以消除或是减少在完成安装或是更新的过程中系统需要重启的次数.软件安装或是更新过程之所以需要重启系统的原因在于一些需要更新的文件正在被运行中的程序或服务使用.而重启管理器可以 ...

  2. Go语言程序调试

    1. Go语言二进制程序分析    在分析一些使用GOlang语言进行编译的恶意程序时,由于程序在被打包成二进制程序时会打包诸多引用的库,并且作者对二进制程序进行了去符号化,导致在动态或是静态分析时函 ...

  3. Django视图与模板(6)

    前面记到数据库与模型(models)有联系,现在记录一下视图与模板,他们两个也有联系. 个人理解:视图就好像一个cpu,比较核心,就是用来处理问题的,又叫业务逻辑处理,他把处理完的结果插入到模板里面, ...

  4. 017 Linux 之啥是 ssh ?

    1 什么是 ssh?有什么用? (1)ssh 是一种协议 SSH(Secure Shell) 是较可靠,专为远程登录会话和其他网络服务提供安全性的协议,利用 SSH 协议可以有效防止远程管理过程中的信 ...

  5. 使用工具john破解系统密码

    下载解压得到一个存在着hash值的passwd的文件,还有一个压缩包解压得到的是一个密码本,应该就是需要使用爆破的密码本了 放在kali里面,根据题目的要求,将root的hash复制下来然后输入到一个 ...

  6. [Java]Thinking in Java 练习2.2

    题目 创建一个"Hello, World"程序,用javac进行编译,再用java运行它. 程序 1 public class Ex2_2 { 2 public static vo ...

  7. 商业智能BI必备的特性

    商业智能BI的本质对企业来说,商业智能BI不能直接产生决策,而是利用BI工具处理后的数据来支持决策.核心是通过构建数据仓库平台,有效整合数据.组织数据,为分析决策提供支持并实现其价值. 传统的DW/O ...

  8. 一键生成的BI智能数据看板谁不爱?

    随着互联网思维的深化,如财务.市场.运营.销售等越来越多的岗位,都开始重视并自发性的开始了解并学习数据分析,来引导帮助决策. 人力资源制定效能仪表盘,去实时掌握人员状况和人均效能,通过对招聘漏斗的分析 ...

  9. 网络之IP地址、子网掩码、网关关联

    IP地址?子网掩码? 网关?我们经常混淆这些知识,同时面试的时候又容易被问.下面我们就一个一个的来介绍他们的区别和用途. 网络无处不在,深深影响着我们的生活.而下面几点知识是我们在网络学习中经常遇到的 ...

  10. Delegate 委托细说

    目录 委托的申明 委托的赋值 委托实例方法的使用C#Invoke\BeginInvoke\Endinoke  系统自带的委托Action.Action<T>.Func<T>.P ...