原创博客,转载请注明出处:【重新发布,代码开源】FPGA设计千兆以太网MAC(1)——通过MDIO接口配置与检测PHY芯片 - 没落骑士 - 博客园

https://www.cnblogs.com/moluoqishi/p/9118283.html

一、前言  

  本文设计思想采用明德扬至简设计法。以太网这一高效实用的数据传输方式应用于各个领域,如网络交换设备,高速网络相机等。虽然各FPGA厂商都提供MAC IP核,但大多收费,有时无法破解。不同厂家之间无法移植,而且为了通用性考虑牺牲了效率,因此自己动手写一个以太网MAC是个不错的选择。

  本博文讨论通过MDIO接口管理PHY芯片来验证其正确工作,为在此基础上设计MAC逻辑开个头。PHY芯片采用RTL8211EGVB,选用GMII接口与MAC连接。下面我们来开始第一步,在此之前明确设计目的:检测PHY芯片是否完成自动协商 链路速率是否达到1000M。所以要从datasheet中了解到芯片引脚 寄存器地址 接口时序。

二、设计分析

  管理帧格式如下:

  读写操作时序:

  MDC为MAC驱动时钟信号,MDIO是串行数据总线,需要连接上拉电阻保证idle状态下高电平。其中前导码包含32个比特“1”,PHY地址根据芯片引脚连接而定,此处为01.turn around域是为了防止读操作时数据冲突,在读操作过程中MAC和PHY均在第1比特处进入高阻态,PHY在第2比特处驱动MDIO接口为低电平以占据总线控制权。注意两点:第一如果时钟信号在读写操作后停止,时钟必须保证至少7个时钟周期持续翻转且MDIO高电平从而保证之前的操作完成。故在设计中可以等待一段时间后再拉低时钟使能信号。第二两个操作之间至少一个idle比特。

  正确驱动接口时序需要关注AC characterisics.

  很明显MAC驱动总线时,在MDC下降沿更新数据。而PHY驱动总线时,MDC上升沿后更新数据。根据datasheet中的timing参数设定MDC时钟周期是800ns,MAC接收PHY数据时下降沿采样。

  接下来关注要访问的内部寄存器地址,首先读取PHY寄存器数据以检测其工作状态,若发现异常再考虑写入数据。这里读取基本模式状态寄存器0X01的bit5,若为1说明自动协商完成。第二个寄存器是PHY特定状态寄存器0X11中的[15:14]和13,分别是当前速率和全/半双工通信模式。若检测到自动协商完成,且工作在1000M全双工模式下,说明工作正确。

三、硬件架构与状态机设计

  所有准备工作完成,现在开始设计。按照“自顶向下”设计原则,规划好整体结构和模块间接口,再设计内部状态机一步步实现逻辑功能。

  Mdio_ctrl模块负责完成PHY芯片的配置与检测逻辑,Mdio接口模块完成读写操作时序。此处仅通过读操作简单检测PHY状态,暂不进行配置,故两模块工作状态跳转如图所示:

  剩下的工作就是把两个状态机实现出来,非常简单。有需要的朋友可以参考一下,关于芯片的具体参数详见:Realtek RTL8211E(G)-VB(VL)-CG Datasheet 1.8.上代码!

四、代码编写

MDIO控制模块:

 `timescale 1ns / 1ps

 module mdio_ctrl(
input clk,//100M
input rst_n, input en,
output reg chk_result =,
output reg chk_vld =, input rdy,
output reg rd_en =,
output reg [-:] phy_addr =,
output reg [-:] reg_addr =,
input [-:] rd_data,
input rd_vld
); parameter MS_CYC = 100_000; localparam IDLE = ;
localparam WAIT = ;
localparam RD_PHY = ;
localparam CHECK = ; localparam WAIT_MS = ; localparam BMSR = 'h01,
PHYSR = 'h11; reg [-:] state_c = ,state_n = ;
wire idle2wait,wait2rd_phy,rd_phy2check,check2idle,check2wait;
wire link_up;
reg [-:] rd_memory [:];
reg [ (-):] ms_cnt = ;
wire add_ms_cnt ;
wire end_ms_cnt ;
reg [ (-):] wait_cnt = ;
wire add_wait_cnt ;
wire end_wait_cnt ;
reg [ (-):] rd_cnt = ;
wire add_rd_cnt ;
wire end_rd_cnt ;
reg [ (-):] rdata_cnt = ;
wire add_rdata_cnt ;
wire end_rdata_cnt ;
wire [*-:] registers;
reg rd_finish = ; initial begin
rd_memory[] = ;
rd_memory[] = ;
end always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
state_c <= IDLE ;
end
else begin
state_c <= state_n;
end
end always @(*) begin
case(state_c)
IDLE :begin
if(idle2wait)
state_n = WAIT ;
else
state_n = state_c ;
end
WAIT :begin
if(wait2rd_phy)
state_n = RD_PHY ;
else
state_n = state_c ;
end
RD_PHY :begin
if(rd_phy2check)
state_n = CHECK ;
else
state_n = state_c ;
end
CHECK :begin
if(check2idle)
state_n = IDLE ;
else if(check2wait)
state_n = WAIT ;
else
state_n = state_c ;
end
default : state_n = IDLE ;
endcase
end assign idle2wait = state_c==IDLE && (en);
assign wait2rd_phy = state_c==WAIT && (end_wait_cnt);
assign rd_phy2check = state_c==RD_PHY && (end_rdata_cnt);
assign check2idle = state_c==CHECK && (link_up);
assign check2wait = state_c==CHECK && (!link_up); assign link_up = rd_memory[][] == 'b1 && rd_memory[1][15:13] == 3'b10_1;//auto_nego && gigabit && full_duplex //计数器
always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
ms_cnt <= ;
end
else if(add_ms_cnt) begin
if(end_ms_cnt)
ms_cnt <= ;
else
ms_cnt <= ms_cnt+ ;
end
end
assign add_ms_cnt = (state_c == WAIT);
assign end_ms_cnt = add_ms_cnt && ms_cnt == (MS_CYC)- ;//100MHZ时钟100_000 always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
wait_cnt <= ;
end
else if(add_wait_cnt) begin
if(end_wait_cnt)
wait_cnt <= ;
else
wait_cnt <= wait_cnt+ ;
end
end
assign add_wait_cnt = (end_ms_cnt);
assign end_wait_cnt = add_wait_cnt && wait_cnt == (WAIT_MS)- ; always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
rd_cnt <= ;
end
else if(add_rd_cnt) begin
if(end_rd_cnt)
rd_cnt <= ;
else
rd_cnt <= rd_cnt+ ;
end
end
assign add_rd_cnt = (state_c == RD_PHY && rdy && !rd_finish);
assign end_rd_cnt = add_rd_cnt && rd_cnt == ()- ; always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
rd_finish <= ;
end
else if(end_rd_cnt)begin
rd_finish <= 'b1;
end
else if(state_c == CHECK)
rd_finish <= ;
end always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
rdata_cnt <= ;
end
else if(add_rdata_cnt) begin
if(end_rdata_cnt)
rdata_cnt <= ;
else
rdata_cnt <= rdata_cnt+ ;
end
end
assign add_rdata_cnt = (rd_vld);
assign end_rdata_cnt = add_rdata_cnt && rdata_cnt == ()- ; //接口信号逻辑
always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
rd_en <= ;
phy_addr <= ;
reg_addr <= ;
end
else if(add_rd_cnt)begin
rd_en <= 'b1;
phy_addr <= 'b00001;
reg_addr <= registers[-*rd_cnt- -:];
end
else begin
rd_en <= ;
phy_addr <= ;
reg_addr <= ;
end
end assign registers = {BMSR,PHYSR};//5'h01,5'h11 always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
rd_memory[] <= ;
rd_memory[] <= ;
end
else if(add_rdata_cnt)begin
rd_memory[rdata_cnt] <= rd_data;
end
end //用户侧输出检测结果
always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
chk_vld <= ;
end
else if(state_c == CHECK)begin
chk_vld <= 'b1;
end
else
chk_vld <= ;
end always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
chk_result <= ;
end
else if(check2idle)begin
chk_result <= 'b1;
end
else if(check2wait)
chk_result <= ;
end endmodule

mdio_ctrl

MDIO时序接口模块:

 `timescale 1ns / 1ps

 module mdio_interface#(parameter MDC_CYC = )//ns
(
input clk,//100M时钟
input rst_n, input rd_en,
input [-:] phy_addr,
input [-:] reg_addr,
output reg [-:] rd_data =,
output reg rd_vld =,
output reg rdy =, output reg mdo =,
output reg mdo_en =,
input mdi,
output reg mdc =
); localparam N = MDC_CYC/; localparam IDLE = ;
localparam WRI_COM = ;
localparam RD_DATA = ; localparam PRE = 'hffff_ffff,
START = 'b01,
OP = 'b10,
TA = 'b11; reg [-:] state_c =,state_n =;
wire idle2wri_com,wri_com2rd_data,rd_data2idle;
reg [ (-):] div_cnt = ;
wire add_div_cnt ;
wire end_div_cnt ;
reg [ (-):] bit_cnt = ;
wire add_bit_cnt ;
wire end_bit_cnt ;
reg [-:] M =;
wire [-:] command;
reg rd_flag = ;
reg [-:] phy_addr_tmp = ;
reg [-:] reg_addr_tmp = ; //寄存地址
always @(posedge clk or negedge rst_n)begin
if(rst_n=='b0)begin
phy_addr_tmp <= ;
reg_addr_tmp <= ;
end
else if(rd_en)begin
phy_addr_tmp <= phy_addr;
reg_addr_tmp <= reg_addr;
end
end always@(*)begin
if(state_c == IDLE && !rd_en && !rd_flag)
rdy <= ;
else
rdy <= ;
end always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
state_c <= IDLE ;
end
else begin
state_c <= state_n;
end
end always @(*) begin
case(state_c)
IDLE :begin
if(idle2wri_com)
state_n = WRI_COM ;
else
state_n = state_c ;
end
WRI_COM :begin
if(wri_com2rd_data)
state_n = RD_DATA ;
else
state_n = state_c ;
end
RD_DATA :begin
if(rd_data2idle)
state_n = IDLE ;
else
state_n = state_c ;
end
default : state_n = IDLE ;
endcase
end assign idle2wri_com = state_c==IDLE && end_div_cnt && (rd_flag || rd_en);
assign wri_com2rd_data = state_c==WRI_COM && end_bit_cnt;
assign rd_data2idle = state_c==RD_DATA && end_bit_cnt; always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
rd_flag <= () ;
end
else if(state_c == IDLE && rd_en)begin
rd_flag <= ('b1) ;
end
else if(state_c == WRI_COM)
rd_flag <= ;
end //分频计数器
always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
div_cnt <= ;
end
else if(add_div_cnt) begin
if(end_div_cnt)
div_cnt <= ;
else
div_cnt <= div_cnt+ ;
end
end
assign add_div_cnt = ();
assign end_div_cnt = add_div_cnt && div_cnt == (N)- ; //比特计数器
always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
bit_cnt <= ;
end
else if(add_bit_cnt) begin
if(end_bit_cnt)
bit_cnt <= ;
else
bit_cnt <= bit_cnt+ ;
end
end
assign add_bit_cnt = (end_div_cnt && state_c != IDLE);
assign end_bit_cnt = add_bit_cnt && bit_cnt == (M)- ; always@(*)begin
case(state_c)
WRI_COM:M = ;
RD_DATA:M = ;
default:M = ;
endcase
end //mdc时钟
always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
mdc <= ('b1) ;
end
else if(add_div_cnt && div_cnt == (N>>) - )begin
mdc <= ('b1) ;
end
else if(end_div_cnt)
mdc <= ;
end //mdio输出
always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
mdo <= ('b1) ;
end
else if(add_bit_cnt && state_c == WRI_COM)begin
mdo <= command[--bit_cnt] ;
end
else if(state_c != WRI_COM)
mdo <= 'b1;
end assign command = {PRE,START,OP,phy_addr_tmp,reg_addr_tmp,TA}; always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
mdo_en <= () ;
end
else if(state_c == WRI_COM && add_bit_cnt)
case(bit_cnt)
: mdo_en <= 'b1;
:mdo_en <= ;
default:;
endcase
end //mdio输入
always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
rd_data <= () ;
end
else if(add_bit_cnt && state_c == RD_DATA)begin
rd_data[--bit_cnt] <= (mdi) ;
end
end always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
rd_vld <= () ;
end
else if(rd_data2idle)begin
rd_vld <= ('b1) ;
end
else
rd_vld <= ;
end endmodule

mdio_interface

顶层封装:

 `timescale 1ns / 1ps

 module phy_manage(
input clk,
input rst_n, input mdio_en,
output link_up,
output chk_done, output mdc,
inout mdio
); wire rdy;
wire rd_en;
wire [-:] phy_addr;
wire [-:] reg_addr;
(*DONT_TOUCH = "TRUE"*)wire [-:] rd_data;
wire rd_vld;
wire mdo_en,mdo,mdi; mdio_ctrl mdio_ctrl(
.clk (clk) ,//100M
.rst_n (rst_n) , .en (mdio_en) ,
.chk_result(link_up) ,
.chk_vld (chk_done) , .rdy (rdy) ,
.rd_en (rd_en) ,
.phy_addr (phy_addr) ,
.reg_addr (reg_addr) ,
.rd_data (rd_data) ,
.rd_vld (rd_vld)
); mdio_interface#(.MDC_CYC())//ns
mdio_interface
(
.clk (clk) ,//100M时钟
.rst_n (rst_n) , .rd_en (rd_en) ,
.phy_addr (phy_addr) ,
.reg_addr (reg_addr) ,
.rd_data (rd_data) ,
.rd_vld (rd_vld) ,
.rdy (rdy) , .mdo (mdo) ,
.mdo_en (mdo_en) ,
.mdi (mdi) ,
.mdc (mdc)
); //三态门
assign mdio = mdo_en ? mdo : 'bz;
assign mdi = mdio; endmodule

phy_manage

五、功能仿真

之后编写testbench进行行为仿真:

 `timescale  ns/ ps

 `define BIT_CNT uut.mdio_interface.bit_cnt

 module phy_manage_tb();

 //时钟和复位
reg clk ;
reg rst_n; //uut的输入信号
reg mdio_en; //uut的输出信号
wire link_up;
wire chk_done;
wire mdc;
wire mdio;
wire [-:] back_data1,back_data2; //时钟周期,单位为ns,可在此修改时钟周期。
parameter CYCLE = ; //复位时间,此时表示复位3个时钟周期的时间。
parameter RST_TIME = ; defparam uut.mdio_ctrl.MS_CYC = ; //待测试的模块例化
phy_manage uut(
.clk (clk) ,
.rst_n (rst_n) , .mdio_en (mdio_en) ,
.link_up (link_up) ,
.chk_done (chk_done) , .mdc (mdc) ,
.mdio (mdio)
); //生成本地时钟50M
initial begin
clk = ;
forever
#(CYCLE/)
clk=~clk;
end //产生复位信号
initial begin
rst_n = ;
#;
rst_n = ;
#(CYCLE*RST_TIME);
rst_n = ;
end //输入信号din0赋值方式
initial begin
#;
//赋初值
mdio_en = ;
#(*CYCLE);
mdio_en = ;
#(*CYCLE);
mdio_en = ;
//开始赋值
#100_000;
$stop;
end //模拟PHY响应 //data
assign back_data1 = {'b0000_0000_0010_0000};
assign back_data2 = {'b1010_0000_0000_0000}; integer i = ,j = ;
initial begin
forever begin
wait(uut.mdio_interface.state_c == && `BIT_CNT == );
@(posedge mdc);
force mdio = ;
@(posedge mdc);
j = j+;
if(j == )
force mdio = back_data1[--i+];
else
force mdio = back_data2[--i+]; wait(uut.mdio_interface.state_c == );
@(posedge mdc);
release mdio;
end
end initial begin
forever begin
@(posedge mdc);
if(uut.mdio_interface.state_c == )begin
#;
i = i+;
end
else
i = ;
end
end endmodule

phy_manage_tb

  testbench中利用force强迫更新mdio双向端口方式模拟PHY芯片响应。仿真波形上半部分为MDIO控制模块信号,下半部分则是MDIO时序接口模块信号。可见当读取寄存器数值满足PHY工作需求时,link_up信号拉高,证明此时MAC可以传输数据给PHY。

六、板级调试

  完整的设计,板级调试是必不可少的。真正地将接口调通,PHY芯片正确响应才能说明达到设计目的。顶层封装测试工程,内部例化:差分时钟缓冲原语、PLL、PHY管理顶层封装以及VIO ILA调试IP。我们来看下原理图顶层:

测试工程顶层:

 `timescale 1ns / 1ps

 module mdio_test(
input sys_clk_p,
input sys_clk_n,
input rst_n, output mdc,
inout mdio, output phy_reset//PHY芯片复位信号 低有效
); wire sys_clk_ibufg;
wire clk;
wire en;
wire chk_done;
wire link_up; assign phy_reset = 'b1;//始终不复位 IBUFGDS #
(
.DIFF_TERM ("FALSE"),
.IBUF_LOW_PWR ("FALSE")
)
u_ibufg_sys_clk
(
.I (sys_clk_p), //差分时钟的正端输入,需要和顶层模块的端口直接连接
.IB (sys_clk_n), // 差分时钟的负端输入,需要和顶层模块的端口直接连接
.O (sys_clk_ibufg) //时钟缓冲输出
); clk_wiz_0 u_clk
(
// Clock out ports
.clk_out1(clk), // output clk_out1 100Mhz
// Clock in ports
.clk_in1(sys_clk_ibufg)); // input clk_in1 vio_0 u_vio (
.clk(clk), // input wire clk
.probe_out0(en) // output wire [0 : 0] probe_out0
); phy_manage phy_manage(
.clk (clk) ,
.rst_n (rst_n) , .mdio_en (en) ,
.link_up (link_up) ,
.chk_done (chk_done) , .mdc (mdc) ,
.mdio (mdio)
); endmodule

mdio_test

时钟引脚约束文件:

 create_clock -period 5.000 [get_ports sys_clk_p]
set_property PACKAGE_PIN R4 [get_ports sys_clk_p]
set_property IOSTANDARD DIFF_SSTL15 [get_ports sys_clk_p] set_property PACKAGE_PIN T6 [get_ports rst_n]
set_property IOSTANDARD LVCMOS15 [get_ports rst_n] set_property PACKAGE_PIN W10 [get_ports mdc]
set_property IOSTANDARD LVCMOS33 [get_ports mdc] set_property PACKAGE_PIN V10 [get_ports mdio]
set_property IOSTANDARD LVCMOS33 [get_ports mdio] set_property PACKAGE_PIN L15 [get_ports phy_reset]
set_property IOSTANDARD LVCMOS33 [get_ports phy_reset]

clk_pin 

  有一点相信调试过以太网的人大多都跳过一个坑:没有驱动PHY的复位输入信号。本人也在此处栽过跟头,这里直接连续赋值拉高PHY芯片复位信号。关于板级调试还有个小技巧,根据高亚军老师的书籍得知,将set up debug生成的ILA探针相关约束命令单独放入一个约束文件便于调试IP的管理和修改,debug约束文件就不贴出来了。

  查看debug波形,MDIO时序接口模块在释放MDIO串行总线时,由于存在上拉电阻为高电平,下一个MDC时钟上升沿时刻,PHY拉低MDIO信号响应并得到总线控制权,开始输出数据。

  得到读取的两个寄存器数据,根据数值分析满足:PHY自动协商完成,且工作在全双工1000Mbps速率下。

  最终RJ45接口绿色指示灯常亮,表明自动协商完成,网络连接正确。到此简易的PHY芯片检测管理模块设计完成。

【重新发布,代码开源】FPGA设计千兆以太网MAC(1)——通过MDIO接口配置与检测PHY芯片的更多相关文章

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

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

  2. FPGA设计千兆以太网MAC(3)——数据缓存及位宽转换模块设计与验证

    本文设计思想采用明德扬至简设计法.上一篇博文中定制了自定义MAC IP的结构,在用户侧需要位宽转换及数据缓存.本文以TX方向为例,设计并验证发送缓存模块.这里定义该模块可缓存4个最大长度数据包,用户根 ...

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

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

  4. 【小梅哥FPGA进阶学习之旅】基于Altera FPGA 的DDR2+千兆以太网电路设计

    DDR2电路设计 在高速大数据的应用中,高速大容量缓存是必不可少的硬件.当前在FPGA系统中使用较为广泛的高速大容量存储器有经典速度较低的单数据速率的SDRAM存储器,以及速度较高的双速率DDR.DD ...

  5. 千兆以太网TCP协议的FPGA实现

    转自https://blog.csdn.net/zhipao6108/article/details/82386355 千兆以太网TCP协议的FPGA实现 Lzx 2017/4/20 写在前面,这应该 ...

  6. AC6102 开发板千兆以太网UDP传输实验2

    AC6102 开发板千兆以太网UDP传输实验 在芯航线AC6102开发板上,设计了一路GMII接口的千兆以太网电路,通过该以太网电路,用户可以将FPGA采集或运算得到的数据传递给其他设备如PC或服务器 ...

  7. AC6102 开发板千兆以太网UDP传输实验

    AC6102 开发板千兆以太网UDP传输实验 在芯航线AC6102开发板上,设计了一路GMII接口的千兆以太网电路,通过该以太网电路,用户可以将FPGA采集或运算得到的数据传递给其他设备如PC或服务器 ...

  8. 迅为IMX6开发板支持全网通4G模块丨GPS模块丨WIFI蓝牙丨千兆以太网

    迅为i.MX6开发板丨迅为i.MX6Q开发板丨四核imx6开发板丨Cortec-A9开发板丨资料介绍: 特点: 处理器:Freescale Cortex-A9四核i.MX6Q主频1GHz 核心板配置: ...

  9. 237-基于Xilinx Kintex-7 XC7K325T 的FMC/千兆以太网/SATA/四路光纤数据转发卡

    基于Xilinx Kintex-7 XC7K325T 的FMC/千兆以太网/SATA/四路光纤数据转发卡 一. 板卡概述  本板卡基于Xilinx公司的FPGAXC7K325T-2FFG900 芯片, ...

随机推荐

  1. linux 关机命令shutdown

    linux系统,正确的关机很重要,因为linux是多任.多用户系统,在后台可能同时有很多人在主机上面工作.不正确的挂机可能会导致数据中断. 1.关机前的操作(可以不进行) 可以使用who命令查看系统有 ...

  2. BBS论坛(三十三)

    33.celery实现邮件异步发送 (1)task.py pip install celery redis from celery import Celery from flask import Fl ...

  3. IView组件化之部署及按钮学习

    IView是什么? iView 是一套基于 Vue.js 的开源 UI 组件库,主要服务于 PC 界面的中后台产品. Npm安装IView npm install iview 在main.js中配置I ...

  4. Python链接Mssql之Python库pymssql

    连接数据库 pymssql连接数据库的方式和使用sqlite的方式基本相同: 使用connect创建连接对象 connect.cursor创建游标对象,SQL语句的执行基本都在游标上进行 cursor ...

  5. asp.net core 系列 17 通用主机 IHostBuilder

    一.概述 ASP.NET Core 通用主机 (HostBuilder),该主机对于托管不处理 HTTP 请求的应用非常有用.通用主机的目标是将 HTTP 管道从 Web 主机 API 中分离出来,从 ...

  6. VueJs 源码分析 ---(一) 整体对 vuejs 框架的理解

    vue-2.x SourceCode vue 2.x 源码解析 关于vue,以及为何要来写这份源码解析的原因 笔者从最开始接触到 vue 应该还是在 15年 10月份左右,当时听说 前端圈中发生很多的 ...

  7. dotnet core 开发无缝兼容Http和Websocket协议的接口服务

    在应用接口开发中往往要针对不同协义开发相应的代理服务,但对于Websocket和http这两种协议来说就有些不同,从实现上来看Websocket可以说是Http的升级子协议, 两者在协议处理上基本一致 ...

  8. 知其所以然~tcp和udp的区别

    TCP UDP TCP与UDP基本区别 基于连接与无连接 TCP要求系统资源较多,UDP较少: UDP程序结构较简单 流模式(TCP)与数据报模式(UDP); TCP保证数据正确性,UDP可能丢包 T ...

  9. springboot+springcloud集成jar

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  10. Jenkins定时构建时间设置

    每隔5分钟构建一次 H/ * * * * 每两小时构建一次 H H/ * * * 每天中午12点定时构建一次 H * * * 每天下午18点定时构建一次 H * * * 在每个小时的前半个小时内的每1 ...