FPGA实现千兆以太网发送

article/2025/3/10 21:14:48

科研要求,使用手上的DE2-115开发板实现千兆以太网的数据发送
千兆以太网使用的时钟频率为125MHz,一般的GMII接口由于收发数据所使用的数据线为8根即一个时钟周期的上升沿可以发送8bit数据,而DE2-115开发板所使用的接口为RGMII,收发数据所使用的数据线为4根,所以需要在一个时钟周期的上升沿和下降沿都进行数据的传输。如下图TX_DATA和RX_DATA。
DE2-115 千兆以太网接口
接下来就是具体的verilog代码的编写了,在这里参考了黑金开发板百兆网口的代码。以太网一帧的数据并不只包括数据,还有以太网协议用来检验是否为一帧数据的开始、结束、传输协议、校验码等参数,所以在发送一帧数据前需要发送这些参数,CRC校验码在一帧数据的最后被发送,这些具体格式可以去网上参考。

module ethernet#(//开发板MAC地址 00-11-22-33-44-65parameter BOARD_MAC = 48'h00_11_22_33_44_65,       //开发板IP地址 192.168.1.12    parameter BOARD_IP  = {8'd192,8'd168,8'd1,8'd12},   //目的MAC地址 2C_56_DC_19_01_F9  parameter  DES_MAC  = 48'h2C_56_DC_19_01_F9, //目的IP地址 192.168.1.13     parameter  DES_IP   = {8'd192,8'd168,8'd1,8'd13} )

首先设置源IP地址,源MAC地址,目的IP地址以及目的MAC地址。

//上升沿发送改为上升沿和下降沿都发送
always @(posedge eth_tx_clk_250m or negedge rst_n)beginif(!rst_n)cnt 			<= 1'b1;else if(!cnt_1)beginif(cnt)begincnt			<= 1'b0;eth_tx_data <= eth_tx_data_s[3:0];//eth_rx_data_s[3:0] <= eth_rx_data;endelse if(!cnt) begincnt			<= 1'b1;eth_tx_data <= eth_tx_data_s[7:4];//eth_rx_data_s[7:4] <= eth_rx_data;endend
end

在这里使用了250MHz的时钟作为RGMII接口在125MHZ时钟的上升沿和下降沿都进行数据的发送。这里本来可以使用quartus提供的IP核Addioout将单沿数据转换为双沿数据,但在实际使用过程采用SignalTap进行观测的时候发现抓取不到该IP核输出的数据,而使用ModelSim进行仿真的时候是有输出的。最后在网上查到有帖子说是SignalTap抓取不到双沿数据,但是本着科学探索的精神,使用WireShark在PC上抓取传输上来的数据包,结果发现问题比较复杂,遂放弃……

/***********************************************///Project Name : UDP_Send
//Email		   :
//Create Time	: 2021/01/09 13:36
//Editor       : Liu
//Version		: Rev1.0.0/***********************************************/module udp_send#(//开发板MAC地址 00-11-22-33-44-65parameter BOARD_MAC = 48'h00_11_22_33_44_65,      //开发板IP地址 192.168.1.12     parameter BOARD_IP  = {8'd192,8'd168,8'd1,8'd12},   //目的MAC地址 2C_56_DC_19_01_F9parameter DES_MAC   = 48'h2C_56_DC_19_01_F9, //目的IP地址 192.168.1.13     parameter DES_IP    = {8'd192,8'd168,8'd1,8'd13} )
(input                clk,			    //时钟信号input						rst_n,        	 //复位信号,低电平有效	 input						tx_start_en,    //以太网开始发送数据信号input			 [31:0]	tx_data,	    	 //以太网待发送数据input        [15:0]  tx_byte_num,    //以太网发送的有效字节数input        [31:0]  crc_data   ,    //CRC校验数据input        [3:0]   crc_next   ,    //CRC下次校验完成数据output  reg          tx_done    ,    //以太网发送完成信号output  reg          tx_req     ,    //读数据请求信号output  reg          eth_tx_en  ,    //MII输出数据有效信号output  reg  [7:0]   eth_tx_data_s,  //MIIH输出数据output  reg          crc_en     ,    //CRC开始校验使能output  reg          crc_clr        //CRC数据复位信号 
);//状态机
localparam  st_idle      = 7'b0000001;   //初始状态,等待开始发送信号
localparam  st_check_sum = 7'b0000010;   //IP首部校验和
localparam  st_preamble  = 7'b0000100;   //发送前导码+帧起始界定符
localparam  st_eth_head  = 7'b0001000;   //发送以太网帧头
localparam  st_ip_head   = 7'b0010000;   //发送IP首部+UDP首部
localparam  st_tx_data   = 7'b0100000;   //发送数据
localparam  st_crc       = 7'b1000000;   //发送CRC校验值localparam  ETH_TYPE     = 16'h0800;    	//以太网协议类型 IP协议
//以太网数据最小46个字节,IP首部20个字节+UDP首部8个字节
//所以数据至少46-20-8=18个字节
localparam  MIN_DATA_NUM = 16'd18;//reg define
reg    [6:0]       cur_state        ;
reg    [6:0]       next_state       ;reg    [7:0]       preamble[7:0]    ;     //前导码
reg    [7:0]       eth_head[13:0]   ;     //以太网首部
reg    [31:0]      ip_head[6:0]     ;     //IP首部 + UDP首部reg                start_en_d0      ;
reg                start_en_d1      ;
reg    [15:0]      tx_data_num      ;     //发送的有效数据字节个数
reg    [15:0]      total_num        ;     //总字节数
reg    [15:0]      udp_num          ;     //UDP字节数
reg                skip_en          ;     //控制状态跳转使能信号
reg    [4:0]       cnt              ;
reg    [31:0]      check_buffer     ;     //首部校验和
reg    [2:0]       tx_bit_sel       ;
reg    [15:0]      data_cnt         ;     //发送数据个数计数器
reg                tx_done_t        ;
reg    [4:0]       real_add_cnt     ;     //以太网数据实际多发的字节数//wire define                       
wire               pos_start_en     ;     //开始发送数据上升沿
wire   [15:0]      real_tx_data_num ;     //实际发送的字节数(以太网最少字节要求)assign  pos_start_en = (~start_en_d1) & start_en_d0;
assign  real_tx_data_num = (tx_data_num >= MIN_DATA_NUM) ? tx_data_num : MIN_DATA_NUM; //采tx_start_en的上升沿
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginstart_en_d0 <= 1'b0;start_en_d1 <= 1'b0;end    else beginstart_en_d0 <= tx_start_en;start_en_d1 <= start_en_d0;end
end //寄存数据有效字节
always @(posedge clk or negedge rst_n) beginif(!rst_n) begintx_data_num <= 16'd0;total_num <= 16'd0;udp_num <= 16'd0;endelse beginif(pos_start_en && cur_state == st_idle) begin//数据长度tx_data_num <= 16'd4;	 //tx_byte_num;        //IP长度:有效数据+IP首部长度            total_num <= 16'd4 + 16'd28;  //tx_byte_num//UDP长度:有效数据+UDP首部长度            udp_num <= 16'd4 + 16'd8;     //tx_byte_num          end    end
end   always @(posedge clk or negedge rst_n) beginif(!rst_n)cur_state <= st_idle;  elsecur_state <= next_state;
endalways @(*) beginnext_state = st_idle;case(cur_state)st_idle     : begin                               //等待发送数据if(skip_en)                next_state = st_check_sum;elsenext_state = st_idle;end  st_check_sum: begin                               //IP首部校验if(skip_en)next_state = st_preamble;elsenext_state = st_check_sum;    end                             st_preamble : begin                               //发送前导码+帧起始界定符if(skip_en)next_state = st_eth_head;elsenext_state = st_preamble;      endst_eth_head : begin                               //发送以太网首部if(skip_en)next_state = st_ip_head;elsenext_state = st_eth_head;      end              st_ip_head : begin                                //发送IP首部+UDP首部               if(skip_en)next_state = st_tx_data;elsenext_state = st_ip_head;      endst_tx_data : begin                                //发送数据                  if(skip_en)next_state = st_crc;elsenext_state = st_tx_data;      endst_crc: begin                                     //发送CRC校验值if(skip_en)next_state = st_idle;elsenext_state = st_crc;      enddefault : next_state = st_idle;   endcase
end    //发送数据
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginskip_en <= 1'b0; cnt <= 5'd0;check_buffer <= 32'd0;ip_head[1][31:16] <= 16'd0;tx_bit_sel <= 3'b0;crc_en <= 1'b0;eth_tx_en <= 1'b0;eth_tx_data_s <= 8'd0;tx_req <= 1'b0;tx_done_t <= 1'b0; data_cnt <= 16'd0;real_add_cnt <= 5'd0;//初始化数组    //前导码 7个8'h55 + 1个8'hd5preamble[0] <= 8'h55;                 preamble[1] <= 8'h55;preamble[2] <= 8'h55;preamble[3] <= 8'h55;preamble[4] <= 8'h55;preamble[5] <= 8'h55;preamble[6] <= 8'h55;preamble[7] <= 8'hd5;//目的MAC地址eth_head[0] <= DES_MAC[47:40];eth_head[1] <= DES_MAC[39:32];eth_head[2] <= DES_MAC[31:24];eth_head[3] <= DES_MAC[23:16];eth_head[4] <= DES_MAC[15:8];eth_head[5] <= DES_MAC[7:0];//源MAC地址eth_head[6] <= BOARD_MAC[47:40];eth_head[7] <= BOARD_MAC[39:32];eth_head[8] <= BOARD_MAC[31:24];eth_head[9] <= BOARD_MAC[23:16];eth_head[10] <= BOARD_MAC[15:8];eth_head[11] <= BOARD_MAC[7:0];//以太网类型eth_head[12] <= ETH_TYPE[15:8];eth_head[13] <= ETH_TYPE[7:0];        endelse beginskip_en <= 1'b0;tx_req <= 1'b0;crc_en <= 1'b0;eth_tx_en <= 1'b0;tx_done_t <= 1'b0;case(cur_state)st_idle     : beginif(pos_start_en)skip_en <= 1'b1; if(skip_en) begin//版本号:4 首部长度:5(单位:32bit,20byte/4=5)//IPV4:4 IPV6:6  ip_head[0] <= {8'h45, 8'h00, total_num};  //16位标识,每次发送累加1      ip_head[1][31:16] <= ip_head[1][31:16] + 1'b1; //bit[15:13]: 010表示不分片ip_head[1][15:0] <= 16'h4000;    //协议:17(udp)                  ip_head[2] <= {8'h40,8'd17,16'h0};//8'd17 = 8'h11   //源IP地址               ip_head[3] <= BOARD_IP;    //目的IP地址                        ip_head[4] <= DES_IP;       //16位源端口号:1234  16位目的端口号:1234                      ip_head[5] <= {16'd1234,16'd1234};  //16位udp长度,16位udp校验和              ip_head[6] <= {udp_num,16'h0000};                   end                                                   endst_check_sum: begin                           //IP首部校验cnt <= cnt + 5'd1;if(cnt == 5'd0) begin                   check_buffer <=   ip_head[0][31:16] + ip_head[0][15:0]+ ip_head[1][31:16] + ip_head[1][15:0]+ ip_head[2][31:16] + ip_head[2][15:0]+ ip_head[3][31:16] + ip_head[3][15:0]+ ip_head[4][31:16] + ip_head[4][15:0];endelse if(cnt == 5'd1)                      //可能出现进位,累加一次check_buffer <= check_buffer[31:16] + check_buffer[15:0];else if(cnt == 5'd2) begin                //可能再次出现进位,累加一次check_buffer <= check_buffer[31:16] + check_buffer[15:0];skip_en <= 1'b1;end                             else if(cnt == 5'd3) begin                //按位取反 cnt <= 5'd0;            ip_head[2][15:0] <= ~check_buffer[15:0];end    end st_preamble : begin                           //发送前导码+帧起始界定符eth_tx_en <= 1'b1;eth_tx_data_s <= preamble[cnt][7:0];    if(cnt == 5'd6)                       skip_en <= 1'b1; if(skip_en)cnt <= 5'd0;elsecnt <= cnt + 5'd1;endst_eth_head : begin                           //发送以太网首部eth_tx_en <= 1'b1;crc_en <= 1'b1;eth_tx_data_s <= eth_head[cnt][7:0];if(cnt == 5'd12)skip_en <= 1'b1;if(skip_en)cnt <= 5'd0;                                 elsecnt <= cnt + 5'd1;end st_ip_head  : begin                           //发送IP首部 + UDP首部crc_en <= 1'b1;eth_tx_en <= 1'b1; tx_bit_sel <= tx_bit_sel + 3'd1; if(tx_bit_sel == 3'd1)eth_tx_data_s <= ip_head[cnt][31:24];else if(tx_bit_sel == 3'd2)eth_tx_data_s <= ip_head[cnt][23:16];else if(tx_bit_sel == 3'd3)begineth_tx_data_s <= ip_head[cnt][15:8];if(cnt == 5'd6)skip_en <= 1'b1;end		   else if(tx_bit_sel == 3'd4) begineth_tx_data_s <= ip_head[cnt][7:0];tx_bit_sel <= 3'd1; if(cnt == 5'd6) begintx_bit_sel <= 3'd0;//提前读请求数据,等待数据有效时发送tx_req <= 1'b1;cnt <= 5'd0;end elsecnt <= cnt + 5'd1; 	  end                            endst_tx_data  : begin                           //发送数据crc_en <= 1'b1;eth_tx_en <= 1'b1;tx_bit_sel <= tx_bit_sel + 3'd1;  if(tx_bit_sel[0] == 1'b0) beginif(data_cnt < tx_data_num - 16'd1)data_cnt <= data_cnt + 16'd1;                        else if(data_cnt == tx_data_num - 16'd1)begin//如果发送的有效数据少于18个字节,在后面填补充位//补充的值为最后一次发送的有效数据if(data_cnt + real_add_cnt < real_tx_data_num - 16'd1)real_add_cnt <= real_add_cnt + 5'd1;  elseskip_en <= 1'b1;end        end        if(tx_bit_sel == 3'd0)eth_tx_data_s <= tx_data[31:24];else if(tx_bit_sel == 3'd1)eth_tx_data_s <= tx_data[23:16];else if(tx_bit_sel == 3'd2)eth_tx_data_s <= tx_data[15:8];else if(tx_bit_sel == 3'd3) begineth_tx_data_s <= tx_data[7:0];tx_bit_sel <= 3'd0; if(data_cnt != tx_data_num - 16'd1)tx_req <= 1'b1;  end                                                                                                                                                   if(skip_en) begindata_cnt <= 16'd0;real_add_cnt <= 5'd0;tx_bit_sel <= 3'd0;end                                                          end  st_crc      : begin                          //发送CRC校验值eth_tx_en <= 1'b1;tx_bit_sel <= tx_bit_sel + 3'd1;if(tx_bit_sel == 3'd0)//注意是crc_nexteth_tx_data_s <= {~crc_data[24],~crc_data[25],~crc_data[26],~crc_data[27], ~crc_next[0], ~crc_next[1], ~crc_next[2], ~crc_next[3]};else if(tx_bit_sel == 3'd1)eth_tx_data_s <= {~crc_data[16],~crc_data[17],~crc_data[18],~crc_data[19],~crc_data[20],~crc_data[21],~crc_data[22], ~crc_data[23]};else if(tx_bit_sel == 3'd2)eth_tx_data_s <= {~crc_data[8],~crc_data[9],~crc_data[10],~crc_data[11], ~crc_data[12],~crc_data[13],~crc_data[14], ~crc_data[15]};else if(tx_bit_sel == 3'd3)begineth_tx_data_s <= {~crc_data[0],~crc_data[1],~crc_data[2],~crc_data[3], ~crc_data[4],~crc_data[5],~crc_data[6], ~crc_data[7]}; skip_en <= 1'b1;tx_done_t <= 1'b1;tx_bit_sel <= 3'd0;end                                                                                                                                                                         end                          default :;  endcase                                             end
end            //发送完成信号及crc值复位信号
always @(posedge clk or negedge rst_n) beginif(!rst_n) begintx_done <= 1'b0;crc_clr <= 1'b0;endelse begintx_done <= tx_done_t;crc_clr <= tx_done_t;end
end
endmodule

上边就是整个数据发送的代码,主要包括一个有限状态机。这块需要自己看代码理解,配合SignalTap观察具体状态,可以配合WireShark抓包,根据抓取到的数据分析问题。
千兆以太网发送代码
verilog代码
百度云链接
链接:https://pan.baidu.com/s/13ErHI6FA_ibCeTBW2vQAiw
提取码:oq2s
百度云链接为最新代码


http://chatgpt.dhexx.cn/article/eC7q3jw3.shtml

相关文章

fpga 运行linux,如何使用PCIe总线将FPGA板连接到运行Linux的X86主机

步骤1:零件清单 0。运行Ubuntu 1的构建/主机计算机。 Xilinx VC707或KC705板 2。 FPGA板电源线 3。 USB转Micro-USB电缆 4。 8线PCIE带状电缆(可选) 步骤2:将板卡连接到主机 如果外形尺寸如果允许,将板卡直接插入主机板主板上的开放PCIE插槽中。在这些说明中,我们使用的是1…

FPGA 定点数

​ 定点数就是小数位宽不发生改变的数&#xff0c;小数点位置固定。 定点数位宽构成&#xff1a;{符号位&#xff0c;整数位&#xff0c;小数位} 当定点数为有符号数&#xff0c;数据的最高位为符号位&#xff0c;整数位和小数位需要根据精度去定义位宽。 假如一个定点数位…

黑金核心板32bitDDR3 MIG核UI接口仿真模型搭建

图2-4-1为黑金开发板上两片DDR3原理图。 1. 搭建仿真模型时需要例化两个ddr3_model,不然仿真时init_calib_complete信号会一直低。 2.还要考虑信号的延时&#xff0c;需要例化WireDelay模块&#xff0c;如Xilinx官方中给的例程中所示&#xff0c;不然app_rd_data数据一直是红…

基于FPGA的UDP 通信(五)

引言 前文链接&#xff1a; 基于FPGA的UDP 通信&#xff08;一&#xff09; 基于FPGA的UDP 通信&#xff08;二&#xff09; 基于FPGA的UDP 通信&#xff08;三&#xff09; 基于FPGA的UDP 通信&#xff08;四&#xff09; 本文基于FPGA设计千兆以太网通信模块UDP数据发送…

Verilog:【8】基于FPGA实现SD NAND FLASH的SPI协议读写

碎碎念&#xff1a; 终于熬过了期末周&#xff0c;可以开始快乐的开发之旅了。 这一期作为一千粉后的首篇博客&#xff0c;由于之后项目会涉及到相关的部分&#xff0c;因此介绍的是使用FPGA实现SD NAND FLASH的读写操作&#xff0c;以雷龙发展提供的CS创世SD NAND FLASH样品为…

【FPGA算法加速】运行PYNQ,对应FPGA芯片版本:赛灵思黑金AX7020

黑金AX7020开放板实物图&#xff1a; 这里的右下角需要连接本地的路由器&#xff0c;图中并未连上&#xff0c;PC端与FPGA在同一路由的网下&#xff0c;到时候PC端可以在线访问FPGA的资源。 一、连接FPGA硬件设备 1、 SD卡插回开发板&#xff0c;确认开发板启动模式为 SD卡模…

黑金Xilinx FPGA学习笔记(一)verilogHDL扫盲文-(1)

verilog简介 HDL 顾名思义Hardware Description Languag verilog HDL 语言的语法和格式都比较随便&#xff0c;它没有 VDL HDL 语言那么严谨&#xff0c;因此受到了广泛的应用。 0.3RTL级和组合逻辑级 笔者的眼中 Verilog HDL 语言建立的硬件模块可以 分为有时钟源和无时钟…

基于FPGA的示波器设计

目录 一、设计要求 二、系统架构设计 一、设计要求 本次基于FPGA的示波器设计主要技术要求包含以下内容&#xff1a; 系统能够实现模数转换功能&#xff0c;即包含ADC驱动模块&#xff1b;系统能够实现ADC采集数据的缓存功能&#xff1b;系统包含辅助测试模块&#xff0c;DA…

FPGA实现 贪吃蛇

一. 硬件 黑金AX4010AN430显示屏&#xff08;480*272&#xff09;蓝牙模块安卓手机 采用app通过蓝牙模块来操作蛇的移动情况 二. 数据的存储 reg[18:0] Snake[0:7]; //蛇 18:10 x 9:1 y 0 :flag 是否是蛇身 reg[17:0] Food; // 食物三. 蛇的移动的方向 通过上下左右来…

黑金全部开发板资料(FPGA+ZYNQ)分享

黑金开发板 能找到的所有黑金开发板的资料&#xff0c;足够学习使用。 ALTERA CYCLONE 10 黑金开发板ZYNQ7020_2019 黑金zynq7015_2017 黑金zynq7010_2017 …

FPAG—UART串口实现与解析-黑金fpga资料解析

UART实现-黑金fpga开发板案例解析 uart 异步串口通讯 无需时钟线&#xff0c;俩根线一跟复制发数据一根负责收数据。 具体的时序如下图1&#xff1a; 图1&#xff1a;UART时序图 可以看到 当数据线由高位变为地位时&#xff0c;即遇到一个下降沿时刻&#xff0c;表示开始这一…

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十四:SD卡模块

驱动SD卡是件容易让人抓狂的事情,驱动SD卡好比SDRAM执行页读写,SD卡虽然不及SDRAM的麻烦要求(时序参数),但是驱动过程却有猥琐操作。除此此外,描述语言只要稍微比较一下C语言,描述语言一定会泪流满面,因为嵌套循环,嵌套判断,或者嵌套函数等都是它的痛。. 史莱姆模块是…

黑金AX7Z100 FPGA开发板移植LWIP库(二)PL端

前言 上一篇博文中实验了PS端移植LwIP库的演示程序。本篇接下来基于Vivado17.4整理比较详细的PL端移植过程。 一、Vivado 工程建立 1、新建一个空工程&#xff0c;名称为net_lwip_pl。 2、FPGA芯片选择xc7z100ffg900-2。 二、配置PS&PL系统硬件 1、工程建好以后&…

【FPGA从0开始系列】黑金EP4CE10F17C8开发板按键实验(二)

项目目录 1.实验目的2.准备阶段3.实验原理4.编写Verilog HDL程序5.配置引脚6.查看和分析RTL7.下载程序8.总结 1.实验目的 查阅AX4010黑金系列用户手册&#xff0c;编写按键程序&#xff0c;实验简单的按键控制LED的功能&#xff0c;同时学习 Quartus RTL Viewer 的使用 2.准备…

汉诺塔(C语言实现)

目录 汉诺塔的游戏规则&#xff1a; 当A只有一个环的时候&#xff1a; 当A只有两个环的时候&#xff1a; 当A只有三个环的时候&#xff1a; 思路&#xff1a; 当n1时&#xff1a; 当n2时&#xff1a; 当n3时&#xff1a; 当n4时&#xff1a; 见代码 运行截图 汉诺塔的游戏…

【C语言】汉诺塔问题

汉诺塔是一个非常经典的问题&#xff0c;其背后是一个传说故事&#xff1a; 在世界中心贝拿勒斯&#xff08;在印度北部&#xff09;的圣庙里&#xff0c;一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候&#xff0c;在其中一根针上从下到上地穿好了由大到小的6…

汉诺塔C语言步骤解析

汉诺塔问题在C语言中一般采用递归法来写&#xff0c;假设有A、B、C三根棒&#xff0c;A棒放着若干个圆盘&#xff0c;将其移动到C棒上&#xff0c;中途可在B棒中暂时放置圆盘。 分析&#xff1a; (1) 如果只有一个圆盘&#xff0c;则把该圆盘从A棒移动到C棒 (2) 如果圆盘数量…

汉诺塔递归的c语言实现(递归)

对于递归来讲, 汉诺塔实际是经典到不能再经典的例子了, 每个数据结构的教材对会提到. 但是到最后只给出一段类似下面的一段代码: #include<stdio.h>void move(int n,char a,char b,char c) {if(n1)printf("\t%c->%c\n",a,c); //当n只有1个的时候直接从…

C语言编程实现汉诺塔问题

C语言编程实现汉诺塔问题 1.首先解释一下&#xff0c;汉诺塔问题&#xff1a;古代梵塔内有A、B、C3个座&#xff0c;开始时A座上面有64个盘子&#xff0c;盘子大小不等&#xff0c;大的在下&#xff0c;小的在上。一个老和尚想把64个盘子从A移到C&#xff0c;规定移动过程中3个…

汉诺塔C语言实现(纯代码)

a、b、c三座塔&#xff0c;将n个从小到大&#xff08;自上而下&#xff09;的圆盘从a移动到c&#xff0c;移动期间小圆盘必须在大圆盘上面&#xff0c;问移动步骤。 #include<stdio.h>int main() {void hanoi(int n,char one,char two,char three);int m;printf("…