带你快速入门AXI4总线--AXI4-Lite篇(3)----XILINX AXI4-Lite接口IP源码仿真分析(Master接口)
写在前面
在带你快速入门AXI4总线--AXI4-Lite篇(2)----XILINX AXI4-Lite接口IP源码仿真分析(Slave接口)中我们已经对Slave接口的代码做了分析,并观察了其仿真波形,在本文我们将生成AXI4-Lite_Master接口的IP来对其解析。
1、调用IP
具体步骤不讲,请参看Slave接口的文章,只需要将IP的接口类型改为Master即可,其他一致。
2、Slave接口的源码分析
打开上节生成的源码(注意:我删除了源码的注释,不然太长了。再优化了一下格式,主要是对齐。顺便再吐槽一下CSDN不能折叠代码):
代码较长,我将其分成NO.1-13共13个部分来进行讲解。只讲大体思路,其他内容请看代码注释。
NO.1:
`timescale 1 ns / 1 ps
//NO.1--------------------------------输入输出端口-------------------------------------------module myip_axi_lite_master_v1_0_M00_AXI #(parameter C_M_START_DATA_VALUE = 32'hAA000000, //初始写入数据的值parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h40000000, //写入地址的基地址parameter integer C_M_AXI_ADDR_WIDTH = 32, //地址位宽parameter integer C_M_AXI_DATA_WIDTH = 32, //数据位宽parameter integer C_M_TRANSACTIONS_NUM = 4 //传输的最大事务个数)(
//工具人型信号 input wire INIT_AXI_TXN, //传输开始信号output reg ERROR, //错误output wire TXN_DONE, //传输完成
//全局信号 input wire M_AXI_ACLK,input wire M_AXI_ARESETN,
//写地址通道信号 output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_AWADDR,output wire [2 : 0] M_AXI_AWPROT,output wire M_AXI_AWVALID,input wire M_AXI_AWREADY,
//写数据通道信号 output wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_WDATA,output wire [C_M_AXI_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB,output wire M_AXI_WVALID,input wire M_AXI_WREADY,
//写响应通道信号 input wire [1 : 0] M_AXI_BRESP,input wire M_AXI_BVALID,output wire M_AXI_BREADY,
//读地址通道信号 output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_ARADDR,output wire [2 : 0] M_AXI_ARPROT,output wire M_AXI_ARVALID,input wire M_AXI_ARREADY,
//读数据通道信号 input wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA,input wire [1 : 0] M_AXI_RRESP,input wire M_AXI_RVALID,output wire M_AXI_RREADY);
这部分主要是模块端口及参数例化。
参数例化:初始写入数据的值;写入地址的基地址;数据位宽32位;地址位宽4位;传输的最大事务个数
模块端口:
AXI4-Lite协议的端口。不记得可以看这里:带你快速入门AXI4总线--AXI4-Lite篇(1)----AXI4-Lite总线
其他工具人型信号
NO.2:
//NO.2--------------------------------以2为底取对数的function------------------------------------------- //以2为底取对数function integer clogb2 (input integer bit_depth);beginfor(clogb2=0; bit_depth>0; clogb2=clogb2+1)bit_depth = bit_depth >> 1;endendfunctionlocalparam integer TRANS_NUM_BITS = clogb2(C_M_TRANSACTIONS_NUM-1); //计算最大传输事务计数的位宽
这部分主要是写了一个求2为底的对数的FUNCTION,这个function一般用来计算变量的位宽;计算了传输事务最大个数的位宽。
NO.3:
//NO.3--------------------------------reg、wire、参数定义-------------------------------------------//状态机状态定义parameter [1:0] IDLE = 2'b00, //初始状态INIT_WRITE = 2'b01, //写事务状态INIT_READ = 2'b10, //读事务状态INIT_COMPARE = 2'b11; //比较读、写结果状态//axi总线信号reg [1:0] mst_exec_state;reg axi_awvalid;reg axi_wvalid;reg axi_arvalid;reg axi_rready;reg axi_bready;reg [C_M_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;reg [C_M_AXI_DATA_WIDTH-1 : 0] axi_wdata;reg [C_M_AXI_ADDR_WIDTH-1 : 0] axi_araddr;wire write_resp_error; //写响应错误wire read_resp_error; //读响应错误reg start_single_write; //单次写事务触发信号reg start_single_read; //单次读事务触发信号reg write_issued; //拉高表示此时正在发起进行写事务reg read_issued; //拉高表示此时正在发起进行读事务reg writes_done; //所有写事务完成reg reads_done; //所有读事务完成reg error_reg; //错误寄存器,拉高表示此模块读写有误reg [TRANS_NUM_BITS : 0] write_index; //写事务计数reg [TRANS_NUM_BITS : 0] read_index; //读事务计数reg [C_M_AXI_DATA_WIDTH-1 : 0] expected_rdata; //理论上应该读取的数据(即被写入的数据)reg compare_done; //比较读写结果完成reg read_mismatch; //读、写结果不匹配reg last_write; //最后一次写事务reg last_read; //最后一次读事务reg init_txn_ff; //传输开始信号打1拍reg init_txn_ff2; //传输开始信号打2拍reg init_txn_edge; //没用到的信号wire init_txn_pulse; //传输开始信号的上升沿捕获信号
这部分定义了一系列的变量,具体作用请看注释。
NO.4:
//NO.4--------------------------------部分赋值模块-------------------------------------------assign M_AXI_AWADDR = C_M_TARGET_SLAVE_BASE_ADDR + axi_awaddr; //写地址 = 基地址 + 偏移地址assign M_AXI_WDATA = axi_wdata;assign M_AXI_AWPROT = 3'b000; //保护类型assign M_AXI_AWVALID = axi_awvalid;assign M_AXI_WVALID = axi_wvalid;assign M_AXI_WSTRB = 4'b1111; //写入数据全部选中assign M_AXI_BREADY = axi_bready;assign M_AXI_ARADDR = C_M_TARGET_SLAVE_BASE_ADDR + axi_araddr; //读地址 = 基地址 + 偏移地址assign M_AXI_ARVALID = axi_arvalid;assign M_AXI_ARPROT = 3'b001; //保护类型assign M_AXI_RREADY = axi_rready;assign TXN_DONE = compare_done; //比较完成信号赋值给传输完成信号
这部分对部分变量进行了赋值,避免了输出信号的直接操作。
NO.5:
//NO.5--------------------------------对传输开始信号打拍并求其上升沿(异步信号)-------------------------------------------assign init_txn_pulse = (!init_txn_ff2) && init_txn_ff; //求上升沿always @(posedge M_AXI_ACLK) begin // Initiates AXI transaction delay if (M_AXI_ARESETN == 0 ) begin init_txn_ff <= 1'b0; init_txn_ff2 <= 1'b0; end else begin init_txn_ff <= INIT_AXI_TXN;init_txn_ff2 <= init_txn_ff; end end
这部分对INIT_AXI_TXN这个信号进行了上升沿捕捉,这是控制主机模块开始写事务的使能信号,由外部传入,属于异步信号。
NO.6:
//NO.6--------------------------------状态机实现流程控制------------------------------------------- always @ ( posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 1'b0) begin mst_exec_state <= IDLE; start_single_write <= 1'b0; write_issued <= 1'b0; start_single_read <= 1'b0; read_issued <= 1'b0; compare_done <= 1'b0; ERROR <= 1'b0;end else begin case (mst_exec_state) IDLE: //初始状态 if ( init_txn_pulse == 1'b1 ) //传输开始 begin mst_exec_state <= INIT_WRITE; ERROR <= 1'b0; compare_done <= 1'b0; end else begin mst_exec_state <= IDLE; end INIT_WRITE: //写传输事务状态 if (writes_done) // 全部4次写事务完成 begin mst_exec_state <= INIT_READ; //跳转到读事务状态 end else begin mst_exec_state <= INIT_WRITE; //空闲状态下生成start_single_write、write_issued if (~axi_awvalid && ~axi_wvalid && ~M_AXI_BVALID && ~last_write && ~start_single_write && ~write_issued)begin start_single_write <= 1'b1; write_issued <= 1'b1; //拉高表示此时正在发起进行写事务 end else if (axi_bready) begin write_issued <= 1'b0; //写入响应后表示写事务可以结束(有1个周期的时延) end else begin start_single_write <= 1'b0; //其他情况不进行写事务 end end INIT_READ: //读传输事务状态 if (reads_done) // 全部4次读事务完成 begin mst_exec_state <= INIT_COMPARE; //跳转到比较读写结果状态 end else begin mst_exec_state <= INIT_READ; //start_single_read、read_issued if (~axi_arvalid && ~M_AXI_RVALID && ~last_read && ~start_single_read && ~read_issued)begin start_single_read <= 1'b1; read_issued <= 1'b1; //拉高表示此时正在发起进行读事务 end else if (axi_rready) //读取响应后表示读事务可以结束(有1个周期的时延) begin read_issued <= 1'b0; end else begin start_single_read <= 1'b0; //其他情况不进行读事务 end end INIT_COMPARE: //比较读、写数据结果 beginmst_exec_state <= IDLE; ERROR <= error_reg; compare_done <= 1'b1; end default : begin mst_exec_state <= IDLE; end endcase end end
这个部分使用一个状态机实现了整个代码的流程控制,状态流程大致如下:
整个流程想实现的功能是:首先进入初始状态,等外部发送可以开始进行传输信号后(init_txn_pulse),进入写事务状态。在写事务状态如果完成了所有的写事务(4个),则跳转到读事务状态。若没有完成则继续保持在该状态。在读事务状态如果完成了所有的读事务(4个),则跳转到比较状态。若没有完成则继续保持在该状态。在比较状态,对读、写数据进行比较,观察是否由错误。
NO.7:
//NO.7--------------------------------写地址通道-------------------------------------------
//写地址有效 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_awvalid <= 1'b0; end else begin if (start_single_write) //单次写入有效 begin axi_awvalid <= 1'b1; end else if (M_AXI_AWREADY && axi_awvalid) //写地址通道握手完成 begin axi_awvalid <= 1'b0; end end end
//写次数计数 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin write_index <= 0; end else if (start_single_write) begin write_index <= write_index + 1; end end
//写地址 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_awaddr <= 0; end else if (M_AXI_AWREADY && axi_awvalid) begin axi_awaddr <= axi_awaddr + 32'h00000004; end end
这个部分主要对三个信号赋值:
写地址有效信号axi_awvalid:当单次写事务使能信号有效时,拉高axi_awvalid。当完成握手后,拉低axi_awvalid。
写次数计数信号write_index:每当单次写事务使能信号有效时(表示进行了一次写事务),其值+1,用来统计写事务的次数。
写地址信号axi_awaddr:每当握手完成后,其值加4。即两次写事务的写入地址相差4
NO.8:
//NO.8--------------------------------写数据通道-------------------------------------------
//写数据有效always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_wvalid <= 1'b0; end else if (start_single_write) begin axi_wvalid <= 1'b1; end else if (M_AXI_WREADY && axi_wvalid) begin axi_wvalid <= 1'b0; end end
//写数据 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 ) begin axi_wdata <= C_M_START_DATA_VALUE; end else if (M_AXI_WREADY && axi_wvalid) begin axi_wdata <= C_M_START_DATA_VALUE + write_index; end end
这个部分主要对2个信号赋值:
写数据有效信号axi_wvalid:当单次写事务使能信号有效时,拉高axi_wvalid。当完成握手后,拉低axi_wvalid。
写数据信号axi_wdata:每当握手完成后,其值在首次写入值的基础上 + 当前写事务的次数。
NO.9:
//NO.9--------------------------------写响应通道-------------------------------------------
//准备接收写响应always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_bready <= 1'b0; end else if (M_AXI_BVALID && ~axi_bready) begin axi_bready <= 1'b1; end else if (axi_bready) begin axi_bready <= 1'b0; end else axi_bready <= axi_bready; end assign write_resp_error = (axi_bready & M_AXI_BVALID & M_AXI_BRESP[1]); //判断响应是否有效
这个部分主要对2个信号赋值:
写响应准备信号axi_bready:当从机发送的响应有效信号有效时,拉高axi_bready。当完成握手后,拉低axi_bready。
写响应错误信号write_resp_error:根据响应值对响应结果进行正确性判断
NO.10:
//读次数计数 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin read_index <= 0; end else if (start_single_read) begin read_index <= read_index + 1; end end
//读地址有效 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_arvalid <= 1'b0; end else if (start_single_read) begin axi_arvalid <= 1'b1; end else if (M_AXI_ARREADY && axi_arvalid) begin axi_arvalid <= 1'b0; end end
//读地址 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_araddr <= 0; end else if (M_AXI_ARREADY && axi_arvalid) begin axi_araddr <= axi_araddr + 32'h00000004; end end
这个部分主要对三个信号赋值:
读地址有效信号axi_arvalid:当单次读事务使能信号有效时,拉高axi_arvalid。当完成握手后,拉低axi_arvalid。
读次数计数信号read_index:每当单次读事务使能信号有效时(表示进行了一次读事务),其值+1,用来统计读事务的次数。
读地址信号axi_araddr:每当握手完成后,其值加4。即两次读事务的读地址相差4(这一点与写地址一致)
NO.11:
//NO.11--------------------------------读数据通道(含读响应)-------------------------------------------
//准备读数据always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_rready <= 1'b0; end else if (M_AXI_RVALID && ~axi_rready) begin axi_rready <= 1'b1; end else if (axi_rready) begin axi_rready <= 1'b0; end end assign read_resp_error = (axi_rready & M_AXI_RVALID & M_AXI_RRESP[1]); //判断响应是否有效
//生成理论上应该读到的值,后续与实际读出的值做对比 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin expected_rdata <= C_M_START_DATA_VALUE; end else if (M_AXI_RVALID && axi_rready) begin expected_rdata <= C_M_START_DATA_VALUE + read_index;end end
这个部分主要对3个信号赋值:
准备读数据信号axi_rready:当从机告知可以读数据时,拉高axi_rready。当完成握手后,拉低axi_rready。
读响应错误信号read_resp_error:根据响应值对响应结果进行正确性判断
理论上应该被读出的信号expected_rdata:根据之前写入的信号判断当前理应被读出的信号是多少
NO.12:
//NO.12--------------------------------读、写事务状态判断-------------------------------------------
//最后一次写事务 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) last_write <= 1'b0; else if ((write_index == C_M_TRANSACTIONS_NUM) && M_AXI_AWREADY) //达到最大写次数,且第四次写地址已完成 last_write <= 1'b1; else last_write <= last_write; end
//全部写事务完成 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) writes_done <= 1'b0; else if (last_write && M_AXI_BVALID && axi_bready) writes_done <= 1'b1; else writes_done <= writes_done; end
//最后一次读事务 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) last_read <= 1'b0; else if ((read_index == C_M_TRANSACTIONS_NUM) && (M_AXI_ARREADY) ) last_read <= 1'b1; else last_read <= last_read; end
//全部读事务完成 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) reads_done <= 1'b0; else if (last_read && M_AXI_RVALID && axi_rready) reads_done <= 1'b1; else reads_done <= reads_done; end
这个部分主要对4个信号赋值:
最后一次写事务信号last_write:当开始了最后一次写事务后,拉高此信号。
最后一次读事务信号last_read:当开始了最后一次读事务后,拉高此信号。
全部写事务完成信号writes_done:当完成了所有写事务后,拉高此信号。
全部读事务完成信号reads_done:当完成了所有读事务后,拉高此信号。
NO.13:
//NO.13--------------------------------错误判断-------------------------------------------
//判断读出的数据是否与写入的数据匹配 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) read_mismatch <= 1'b0; else if ((M_AXI_RVALID && axi_rready) && (M_AXI_RDATA != expected_rdata)) //读、写数据不匹配 read_mismatch <= 1'b1; else read_mismatch <= read_mismatch; end
//错误判断的三种类型:1、读写不匹配;2、写响应不正确;3、读响应不正确 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) error_reg <= 1'b0; else if (read_mismatch || write_resp_error || read_resp_error) error_reg <= 1'b1; else error_reg <= error_reg; end endmodule
这个部分主要对2个信号赋值:
判断读、写是否匹配的信号read_mismatch:若读出的数据与写入的数据不一致则拉高此信号。
错误error_reg:若存在以下三种错误则拉高此信号:
1、读写不匹配;
2、写响应不正确;
3、读响应不正确
3、仿真波形
代码分析完了,接下来使用Vivado自带的仿真器来进行仿真,观看仿真结果,加深对设计方法的理解:
3.1、AXI4-Lite总线的仿真波形
我们先把自动生成的仿真信号删除,添加如下的波形信号:
仿真结果如下:
可以看到仿真结果是用这个彩条+字符的形式表示的,非常清晰。这就是添加了AXI VIP IP的效果。
在AXI4-Lite总线上共发生了8个事务:先是连续的4个写事务,接着4个读事务。下面的五个通道分别示意了此时通道内执行的握手操作,将鼠标放在其中任意一处上,会出现如下信息(顺序1、地址40000000等):
左键点击,会显示具体的事务流程如下:
从上图的箭头我们可以直到一次写事务的流程:写地址----写数据----写响应。再看看读事务的流程:
可以看到读事务的流程:读地址----读数据。
3.2、主机IP的master接口仿真波形
看完了AXI4-Lite总线的仿真波形,我们再看下上面具体解析代码(可以理解为底层驱动)的仿真波形。按如下方法添加:
将信号按通道或用途做好分类(我还删除了一些不要紧的信号),状态机部分的仿真结果如下:
不讲解了,直接看图吧。
AXI4-Lite总线部分的仿真结果如下:
在上图中,主机先发起了4次写事务,分别往地址h0000_0000、h0000_0004、h0000_0000和h0000_000c中写入了数据aa00_0000、 aa00_0001、aa00_0002和aa00_0003。
接着又发起了4次读事务,分别从之前写入的地址中读取到了数据aa00_0000、 aa00_0001、aa00_0002和aa00_0003(这里图没截好),与写入的一致,证明验证成功。
4、其他
- 可以看到其实AXI4-Lite总线的使用还是相对比较简单的,只要设计好各个通道的握手时序,以及读写的时序关系就好了。
- 创作不易,希望各位大佬多多三连支持!一家之言,如有错误还请指正!
版本信息
文件:V1.0
编号:64
Vivado:Vivado 2019.2
Modelsim:无
Quartus II:无
带你快速入门AXI4总线--AXI4-Lite篇(3)----XILINX AXI4-Lite接口IP源码仿真分析(Master接口)相关推荐
- 带你快速入门AXI4总线--AXI4-Full篇(3)----XILINX AXI4-Full接口IP源码仿真分析(Master接口)
写在前面 接slave接口篇,本文继续打包一个AXI4-Full-Master接口的IP,学习下源码,再仿真看看波形. 带你快速入门AXI4总线--AXI4-Full篇(2)----XILINX AX ...
- 带你快速入门AXI4总线--AXI4-Stream篇(1)----AXI4-Stream总线
写在前面 随着对XILINX器件使用的深入,发现越来越多的IP都选配了AXI4的接口.这使得只要学会了AXI4总线的使用,基本上就能对XILINX IP的使用做到简单的上手.所以学会AXI4总线,对X ...
- 带你快速入门AXI4总线--AXI4-Full篇(1)----AXI4-Full总线
写在前面 AXI4系列链接:带你快速入门AXI4总线--汇总篇(直达链接) 1.什么是AXI4-Full? AXI 表示 Advanced eXtensible Interface(高级可扩展接口), ...
- 【效率】超详细!手把手带你快速入门 GitHub!
作者:Peter 编辑:JackTian 来源:公众号「杰哥的IT之旅」 快速入门GitHub GitHub在程序开发领域家喻户晓,现在几乎整个互联网的开发者都将版本管理工具GitHub作为版 ...
- 一文带你快速入门【哈希表】
最近开始学习哈希表,为此特写一遍文章介绍一下哈希表,带大家快速入门哈希表
- 手把手带你快速入门超越GAN的Normalizing Flow
手把手带你快速入门超越GAN的Normalizing Flow 作者:Aryansh Omray,微软数据科学工程师,Medium技术博主 机器学习领域的一个基本问题就是如何学习复杂数据的表征是机器学 ...
- 四篇文章带你快速入门Jetpck(中)之ViewModel,DataBinding
文章目录 四篇文章带你快速入门Jetpck(中)之ViewModel,DataBinding Jetpack 官方推荐架构 ViewModel 添加依赖 创建ViewModel 初始化ViewMode ...
- 带你快速了解PROFIBUS总线接口技术
带你快速了解PROFIBUS总线接口技术 一.概述 1.PROFIBUS是一种用于工厂自动化车间级监控和现场设备层数据通信与控制的现场总线技术.可实现现场设备层到车间级监控的分散式数字控制和现场通信网 ...
- Markdown 快速入门上(基础语法篇)
Markdown 快速入门上(基础语法篇) 1. 简介 Markdown 是一门轻量级的标记性语言,可以用来修饰纯文本使得文档具有一定的优美格式. 像Microsoft Word 文本编辑器, 你需要 ...
- 新手如何快速入门Python(菜鸟必看篇)
学习任何一门语言都是从入门(1年左右),通过不间断练习达到熟练水准(3到5年),少数人最终能精通语言,成为执牛耳者,他们是金字塔的最顶层.虽然万事开头难,但好的开始是成功的一半,今天这篇文章就来谈谈如 ...
最新文章
- oracle nvl用法
- Zookeeper源码编译为Eclipse工程(转)
- 利用ACS实现AAA认证
- 十、Sumif 函数
- 【转】【Unity+Lua】实测如何性能优化(Lua和C#交互篇)
- ORACLE 外部表的简单使用
- python自定义函数数据分析_Python实现自定义函数的5种常见形式分析
- saltstack常用模块
- WIndows thinpc 精简版的WIN7
- 【java实现点卡生成】
- RLE压缩及优化--图片压缩
- 【Django 2021年最新版教程21】数据库查询 model 多条数据 queryset转dict字典 返回渲染到前端
- Elasticsearch——》es近义词(同义词)配置
- 【STM32F429的DSP教程】第18章 DSP控制函数-更好用的SIN,COS计算
- Postman中文版下载[9.12.0]
- 数据中台在企业数字化转型中的践行(下篇)
- 你永远穷不过一个广东人
- KDD CUP 2022 风能预测赛题冠军方案分享
- 路由与交换(一):路由概念及基本配置
- 3dmax常用操作快捷键复合操作法及解决快捷键冲突2021