XGMII2AXIS


一、问题描述

项目中遇到万兆以太网的使用,想将xgmii转成axis使用,目前思路就两种,一种是计数器,一种是状态机,于是用三段式状态机编写代码,并编写tb用于逻辑验证。

二、XGMII2AXIS仿真结果

需要考虑的也就是tkeep信号,只要对齐数据位就行了,AXIS2XGMII也是一样,这里不在展示。
在这里插入图片描述


备注

仅用于记录日常学习,侵权必究

`timescale 1ns / 1ps module XGMII_tx( input wire clk_322m, // 322.26MHz核心时钟 input wire reset_n, // 同步复位,低有效 // MAC层接口 (AXI-Stream风格) input wire [63:0] mac_tx_data, // MAC发送数据 input wire [7:0] mac_tx_keep, // 字节有效指示 input wire mac_tx_valid, // 数据有效 input wire mac_tx_last, // 帧结束 input wire mac_tx_user, // 用户错误指示 output wire mac_tx_ready, // 发送就绪 // XGMII接口 output reg [63:0] xgmii_txd, // XGMII发送数据 output reg [7:0] xgmii_txc, // XGMII发送控制(1=控制字符, 0=数据) // 状态指示 output wire tx_busy, // 发送忙状态 output wire [31:0] tx_frame_count // 发送帧计数 ); // ============================================================================ // 参数定义 // ============================================================================ // XGMII控制字符定义 localparam [7:0] CHAR_IDLE = 8'h07; // 空闲字符 localparam [7:0] CHAR_START = 8'hFB; // 起始字符 localparam [7:0] CHAR_TERM = 8'hFD; // 终止字符 localparam [7:0] CHAR_ERROR = 8'hFE; // 错误字符 // 前导码模式 (7个0xD5 + 1个SFD 0xD5) localparam [63:0] PREAMBLE_PATTERN = {8'hD5, 8'hD5, 8'hD5, 8'hD5, 8'hD5, 8'hD5, 8'hD5, 8'hD5}; // 状态机状态定义 parameter [2:0] ST_IDLE = 3'b000, // 空闲状态 ST_PREAMBLE = 3'b001, // 发送前导码 ST_SFD = 3'b010, // 发送起始定界符 ST_DATA = 3'b011, // 发送数据 ST_TERMINATE = 3'b100, // 发送结束符 ST_ERROR = 3'b101 ; // 错误状态 // ============================================================================ // 内部信号定义 // ============================================================================ reg [2:0] current_state; reg [2:0] next_state; reg [2:0] preamble_cnt; // 前导码计数器 reg [31:0] frame_counter; // 帧计数器 reg last_cycle; // 最后一拍数据标志 reg [7:0] term_keep; // 结束时的keep信号 // ============================================================================ // 状态机主控逻辑 // ============================================================================ // 状态寄存器 always @(posedge clk_322m or negedge reset_n) begin if (!reset_n) begin current_state <= ST_IDLE; end else begin current_state <= next_state; end end // 下一状态逻辑 always @(*) begin case (current_state) ST_IDLE: begin if (mac_tx_valid && mac_tx_ready) begin next_state = ST_PREAMBLE; end else begin next_state = ST_IDLE; end end ST_PREAMBLE: begin if (preamble_cnt == 3'd6) begin next_state = ST_SFD; end else begin next_state = ST_PREAMBLE; end end ST_SFD: begin next_state = ST_DATA; end ST_DATA: begin if (mac_tx_last && mac_tx_valid && mac_tx_ready) begin if (mac_tx_user) begin next_state = ST_ERROR; end else begin next_state = ST_TERMINATE; end end else begin next_state = ST_DATA; end end ST_TERMINATE: begin next_state = ST_IDLE; end ST_ERROR: begin next_state = ST_IDLE; end default: begin next_state = ST_IDLE; end endcase end // ============================================================================ // 前导码计数器 // ============================================================================ always @(posedge clk_322m or negedge reset_n) begin if (!reset_n) begin preamble_cnt <= 3'd0; end else begin case (current_state) ST_PREAMBLE: begin preamble_cnt <= preamble_cnt + 3'd1; end default: begin preamble_cnt <= 3'd0; end endcase end end // ============================================================================ // 帧计数器 // ============================================================================ always @(posedge clk_322m or negedge reset_n) begin if (!reset_n) begin frame_counter <= 32'd0; end else if (current_state == ST_TERMINATE) begin frame_counter <= frame_counter + 32'd1; end end // ============================================================================ // XGMII数据生成 // ============================================================================ always @(posedge clk_322m or negedge reset_n) begin if (!reset_n) begin xgmii_txd <= {8{CHAR_IDLE}}; xgmii_txc <= 8'hFF; end else begin case (current_state) ST_IDLE: begin xgmii_txd <= {8{CHAR_IDLE}}; xgmii_txc <= 8'hFF; end ST_PREAMBLE: begin // 发送前导码 (7个0xD5) xgmii_txd <= PREAMBLE_PATTERN; xgmii_txc <= 8'h00; // 全部为数据字符 end ST_SFD: begin // 发送起始定界符 (SFD + 剩余前导码) xgmii_txd <= {CHAR_START, 7{8'hD5}}; xgmii_txc <= 8'h01; // 仅字节0为控制字符 end ST_DATA: begin if (mac_tx_valid && mac_tx_ready) begin xgmii_txd <= mac_tx_data; xgmii_txc <= 8'h00; // 全部为数据字符 // 记录最后一拍的keep信号 if (mac_tx_last) begin last_cycle <= 1'b1; term_keep <= mac_tx_keep; end end end ST_TERMINATE: begin // 根据最后一拍的keep信号生成结束符位置 case (term_keep) 8'b0000_0001: begin xgmii_txd <= {7{CHAR_IDLE}, CHAR_TERM}; xgmii_txc <= 8'h80; end 8'b0000_0011: begin xgmii_txd <= {6{CHAR_IDLE}, CHAR_TERM, 8'h00}; xgmii_txc <= 8'h40; end 8'b0000_0111: begin xgmii_txd <= {5{CHAR_IDLE}, CHAR_TERM, 16'h0000}; xgmii_txc <= 8'h20; end 8'b0000_1111: begin xgmii_txd <= {4{CHAR_IDLE}, CHAR_TERM, 24'h000000}; xgmii_txc <= 8'h10; end 8'b0001_1111: begin xgmii_txd <= {3{CHAR_IDLE}, CHAR_TERM, 32'h00000000}; xgmii_txc <= 8'h08; end 8'b0011_1111: begin xgmii_txd <= {2{CHAR_IDLE}, CHAR_TERM, 40'h0000000000}; xgmii_txc <= 8'h04; end 8'b0111_1111: begin xgmii_txd <= {CHAR_IDLE, CHAR_TERM, 48'h000000000000}; xgmii_txc <= 8'h02; end 8'b1111_1111: begin xgmii_txd <= {CHAR_TERM, 56'h00000000000000}; xgmii_txc <= 8'h01; end default: begin xgmii_txd <= {8{CHAR_IDLE}}; xgmii_txc <= 8'hFF; end endcase last_cycle <= 1'b0; end ST_ERROR: begin // 发送错误指示 xgmii_txd <= {CHAR_ERROR, 7{CHAR_IDLE}}; xgmii_txc <= 8'h01; end default: begin xgmii_txd <= {8{CHAR_IDLE}}; xgmii_txc <= 8'hFF; end endcase end end // ============================================================================ // 输出信号分配 // ============================================================================ // MAC发送就绪信号:在数据状态且非最后一拍时有效 assign mac_tx_ready = (current_state == ST_DATA) && !last_cycle; // 状态指示 assign tx_busy = (current_state != ST_IDLE); assign tx_frame_count = frame_counter; endmodule;该代码关于 xgmii_txd部分报错,请解决问题
10-10
`timescale 1ns / 1ps module tb_fpga_core_udp; // clocks & resets reg clk = 0; reg rst = 1; reg qsfp_tx_clk_1 = 0; reg qsfp_rx_clk_1 = 0; reg qsfp_tx_rst_1 = 1; reg qsfp_rx_rst_1 = 1; // XGMII inputs to DUT (default idle) reg [63:0] qsfp_rxd_1 = 64'h0707070707070707; reg [7:0] qsfp_rxc_1 = 8'hFF; // XGMII outputs from DUT wire [63:0] qsfp_txd_1; wire [7:0] qsfp_txc_1; // other lanes idle reg qsfp_tx_clk_2 = 0, qsfp_rx_clk_2 = 0, qsfp_tx_rst_2 = 1, qsfp_rx_rst_2 = 1; reg [63:0] qsfp_rxd_2 = 64'h0707070707070707; reg [7:0] qsfp_rxc_2 = 8'hFF; wire [63:0] qsfp_txd_2; wire [7:0] qsfp_txc_2; reg qsfp_tx_clk_3 = 0, qsfp_rx_clk_3 = 0, qsfp_tx_rst_3 = 1, qsfp_rx_rst_3 = 1; reg [63:0] qsfp_rxd_3 = 64'h0707070707070707; reg [7:0] qsfp_rxc_3 = 8'hFF; wire [63:0] qsfp_txd_3; wire [7:0] qsfp_txc_3; reg qsfp_tx_clk_4 = 0, qsfp_rx_clk_4 = 0, qsfp_tx_rst_4 = 1, qsfp_rx_rst_4 = 1; reg [63:0] qsfp_rxd_4 = 64'h0707070707070707; reg [7:0] qsfp_rxc_4 = 8'hFF; wire [63:0] qsfp_txd_4; wire [7:0] qsfp_txc_4; reg [7:0] pkt [0:63]; // 156.25 MHz clocks always #3.2 clk = ~clk; always #3.2 qsfp_tx_clk_1 = ~qsfp_tx_clk_1; always #3.2 qsfp_rx_clk_1 = ~qsfp_rx_clk_1; // DUT fpga_core UUT ( .clk(clk), .rst(rst), .qsfp_tx_clk_1(qsfp_tx_clk_1), .qsfp_tx_rst_1(qsfp_tx_rst_1), .qsfp_txd_1(qsfp_txd_1), .qsfp_txc_1(qsfp_txc_1), .qsfp_rx_clk_1(qsfp_rx_clk_1), .qsfp_rx_rst_1(qsfp_rx_rst_1), .qsfp_rxd_1(qsfp_rxd_1), .qsfp_rxc_1(qsfp_rxc_1), .qsfp_tx_clk_2(qsfp_tx_clk_2), .qsfp_tx_rst_2(qsfp_tx_rst_2), .qsfp_txd_2(qsfp_txd_2), .qsfp_txc_2(qsfp_txc_2), .qsfp_rx_clk_2(qsfp_rx_clk_2), .qsfp_rx_rst_2(qsfp_rx_rst_2), .qsfp_rxd_2(qsfp_rxd_2), .qsfp_rxc_2(qsfp_rxc_2), .qsfp_tx_clk_3(qsfp_tx_clk_3), .qsfp_tx_rst_3(qsfp_tx_rst_3), .qsfp_txd_3(qsfp_txd_3), .qsfp_txc_3(qsfp_txc_3), .qsfp_rx_clk_3(qsfp_rx_clk_3), .qsfp_rx_rst_3(qsfp_rx_rst_3), .qsfp_rxd_3(qsfp_rxd_3), .qsfp_rxc_3(qsfp_rxc_3), .qsfp_tx_clk_4(qsfp_tx_clk_4), .qsfp_tx_rst_4(qsfp_tx_rst_4), .qsfp_txd_4(qsfp_txd_4), .qsfp_txc_4(qsfp_txc_4), .qsfp_rx_clk_4(qsfp_rx_clk_4), .qsfp_rx_rst_4(qsfp_rx_rst_4), .qsfp_rxd_4(qsfp_rxd_4), .qsfp_rxc_4(qsfp_rxc_4) ); // ----------------------------- // // ----------------------------- integer f_xgmii, f_axis; initial begin f_xgmii = $fopen("xgmii_rx_log.txt","w"); if (!f_xgmii) begin $display("ERROR: cannot open xgmii_rx_log.txt"); $stop; end f_axis = $fopen("axis_rx_log.txt","w"); if (!f_axis) begin $display("ERROR: cannot open axis_rx_log.txt"); $stop; end end always @(posedge qsfp_rx_clk_1) begin $fwrite(f_xgmii, "%0t ns | rxc=%02x rxd=%016h\n", $time, qsfp_rxc_1, qsfp_rxd_1); end // // always @(posedge clk) begin $fwrite(f_axis, "%0t ns | tvalid=%b tready=%b tlast=%b tuser=%b tkeep=%02x tdata=%016h\n", $time, UUT.rx_axis_tvalid, UUT.rx_axis_tready, UUT.rx_axis_tlast, UUT.rx_axis_tuser, UUT.rx_axis_tkeep, UUT.rx_axis_tdata); end // ----------------------------- // IPv4 header checksum( // ----------------------------- task automatic ipv4_header_checksum_calc; inout [7:0] pkt [0:63]; integer j; integer sum; begin sum = 0; pkt[24] = 8'h00; pkt[25] = 8'h00; // 先清零 for (j = 14; j < 34; j = j+2) begin sum = sum + {pkt[j], pkt[j+1]}; end // 折返進位 while (sum >> 16) sum = (sum & 16'hFFFF) + (sum >> 16); sum = ~sum & 16'hFFFF; pkt[24] = sum[15:8]; pkt[25] = sum[7:0]; end endtask // ----------------------------- // UDP over IPv4 // ----------------------------- task automatic send_udp_frame; integer i; integer payload_len; integer ip_tot_len; integer udp_len; integer length; // 整個 Ethernet payload 長度(IP header + UDP header + payload) integer rem; // 最後一拍剩餘 bytes reg [63:0] w; reg [7:0] c; begin // for (i = 0; i < 64; i = i+1) pkt[i] = 8'h00; // Ethernet header {pkt[0],pkt[1],pkt[2],pkt[3],pkt[4],pkt[5]} = 48'h02_00_00_00_00_00; // Dst MAC {pkt[6],pkt[7],pkt[8],pkt[9],pkt[10],pkt[11]} = 48'h5A_51_52_53_54_55; // Src MAC pkt[12] = 8'h08; pkt[13] = 8'h00; // Ethertype IPv4 // Payload payload_len = 4; pkt[42] = "H"; pkt[43] = "I"; pkt[44] = "!"; pkt[45] = 8'h00; // IPv4 header ip_tot_len = 20 + 8 + payload_len; // IP header (20) + UDP header (8) + payload pkt[14] = 8'h45; pkt[15] = 8'h00; // Version/IHL, DSCP/ECN pkt[16] = ip_tot_len[15:8]; pkt[17] = ip_tot_len[7:0]; // Total length pkt[18] = 8'h00; pkt[19] = 8'h01; // ID pkt[20] = 8'h00; pkt[21] = 8'h00; // Flags/Fragment pkt[22] = 8'h40; pkt[23] = 8'h11; // TTL=64, Protocol=UDP(17) pkt[24] = 8'h00; pkt[25] = 8'h00; // Header checksum (先清零,待計算) pkt[26] = 8'hC0; pkt[27] = 8'hA8; pkt[28] = 8'h01; pkt[29] = 8'h01; // Src IP 192.168.1.1 pkt[30] = 8'hC0; pkt[31] = 8'hA8; pkt[32] = 8'h01; pkt[33] = 8'h80; // Dst IP 192.168.1.128 // UDP header udp_len = 8 + payload_len; pkt[34] = 8'h04; pkt[35] = 8'hD2; // Src port 1234 pkt[36] = 8'h04; pkt[37] = 8'hD2; // Dst port 1234 pkt[38] = udp_len[15:8]; pkt[39] = udp_len[7:0]; // UDP length pkt[40] = 8'h00; pkt[41] = 8'h00; // UDP checksum=0(允許) // ipv4_header_checksum_calc(pkt); // Ethernet payload length = 14 + ip_tot_len - 14; // 就是 ip_tot_len(Ethernet payload=IP封包) length = 20 + 8 + payload_len; // 等同 ip_tot_len,保險起見再指定 // ==================== //+ 6x55 + D5 qsfp_rxd_1 <= {8'hD5, 8'h55, 8'h55, 8'h55, 8'h55, 8'h55, 8'h55, 8'hFB}; qsfp_rxc_1 <= 8'b00000001; // lane0 control @(posedge qsfp_rx_clk_1); // i = 0; while (length - i >= 8) begin qsfp_rxd_1 <= {pkt[i+7], pkt[i+6], pkt[i+5], pkt[i+4], pkt[i+3], pkt[i+2], pkt[i+1], pkt[i]}; qsfp_rxc_1 <= 8'h00; i = i + 8; @(posedge qsfp_rx_clk_1); end //rem bytes + Terminate + Idle rem = length - i; w = 64'h0707070707070707; // c = 8'hFF; // case (rem) 0: begin w[7:0] = 8'hFD; // lane0 terminate end 1: begin w[7:0] = pkt[i]; w[15:8] = 8'hFD; c = 8'b11111110; end 2: begin w[7:0] = pkt[i]; w[15:8] = pkt[i+1]; w[23:16] = 8'hFD; c = 8'b11111100; end 3: begin w[7:0] = pkt[i]; w[15:8] = pkt[i+1]; w[23:16] = pkt[i+2]; w[31:24] = 8'hFD; c = 8'b11111000; end 4: begin w[7:0] = pkt[i]; w[15:8] = pkt[i+1]; w[23:16] = pkt[i+2]; w[31:24] = pkt[i+3]; w[39:32] = 8'hFD; c = 8'b11110000; end 5: begin w[7:0] = pkt[i]; w[15:8] = pkt[i+1]; w[23:16] = pkt[i+2]; w[31:24] = pkt[i+3]; w[39:32] = pkt[i+4]; w[47:40] = 8'hFD; c = 8'b11100000; end 6: begin w[7:0] = pkt[i]; w[15:8] = pkt[i+1]; w[23:16] = pkt[i+2]; w[31:24] = pkt[i+3]; w[39:32] = pkt[i+4]; w[47:40] = pkt[i+5]; w[55:48] = 8'hFD; c = 8'b11000000; end 7: begin w[7:0] = pkt[i]; w[15:8] = pkt[i+1]; w[23:16] = pkt[i+2]; w[31:24] = pkt[i+3]; w[39:32] = pkt[i+4]; w[47:40] = pkt[i+5]; w[55:48] = pkt[i+6]; w[63:56] = 8'hFD; c = 8'b10000000; end endcase qsfp_rxd_1 <= w; qsfp_rxc_1 <= c; @(posedge qsfp_rx_clk_1); // Idle qsfp_rxd_1 <= 64'h0707070707070707; qsfp_rxc_1 <= 8'hFF; @(posedge qsfp_rx_clk_1); qsfp_rxd_1 <= 64'h0707070707070707; qsfp_rxc_1 <= 8'hFF; @(posedge qsfp_rx_clk_1); end endtask // ----------------------------- // Test sequence // ----------------------------- initial begin // 上電:保持 reset rst = 1; qsfp_rx_rst_1 = 1; qsfp_tx_rst_1 = 1; // repeat (10) @(posedge clk); rst = 0; // //XGMII resets repeat (4) @(posedge qsfp_rx_clk_1); qsfp_rx_rst_1 = 0; repeat (4) @(posedge qsfp_tx_clk_1); qsfp_tx_rst_1 = 0; // repeat (200) @(posedge qsfp_rx_clk_1); // // wait (UUT.rx_axis_tready === 1'b1); // send_udp_frame(); // repeat (2000) @(posedge clk); // $fclose(f_xgmii); $fclose(f_axis); $stop; end endmodule 這個模組有哪裡需要改進的嗎
09-06
### 关于XGMII接口的详细介绍 #### XGMII接口概述 XGMII(10 Gigabit Media Independent Interface)是一种用于10G以太网设备的标准并行接口,其主要目的是连接MAC层和物理层(PHY)。它提供了32位的数据宽度以及156.25 MHz的工作频率[^2]。这种配置使得XGMII能够满足高吞吐量需求的应用场景。 #### 数据传输特性 - **数据位宽**: 32位 - **时钟频率**: 156.25 MHz - **引脚数量**: 总共74根信号线加上额外控制线路[^2] 这些参数共同决定了XGMII可以稳定地处理高达10 Gbps的数据速率。由于采用的是并行架构,因此相比串行接口而言更容易受到噪声影响,但在短距离通信中表现良好。 #### 应用领域 XGMII广泛应用于各种高性能网络硬件之中,比如交换机、路由器以及其他类型的10G以太网终端设备。特别值得一提的是,在某些特定情况下,像Finisar FTLX8571D3这样的光模块也会利用该接口来完成从电域到光学领域的转换过程以便进行远距离光纤通讯[^2]。 #### 设计中的注意事项及其解决办法 当涉及到实际部署时可能会遇到一些技术难题: ##### 同步问题 因为存在多个独立运行却相互关联的功能单元之间需要保持精确的时间关系,所以如何确保所有部分都能在同一时刻操作成为一个关键环节。对此可以通过引入专门用来协调不同组件间动作周期的机制加以改善;例如增加锁相环路(PLL)电路来帮助维持稳定的相对定时关系。 以下是简化版伪代码展示了一个可能实现方式: ```c++ // 假设有一个全局变量表示当前状态 volatile uint32_t currentState; void synchronizeModules() { while (true) { // 不断循环直到达到预期条件为止 if ((currentState & MODULE_A_READY_MASK) && (currentState & MODULE_B_READY_MASK)) break; usleep(MICROSECONDS_PER_LOOP); // 微秒级延迟等待下一轮检测机会 updateState(); // 定期刷新最新状况信息 } } ``` #### 参数设置实例 对于基于XGMII构建的整个系统来说,合理定义各项初始值至关重要。下面给出了一组典型的例子供参考: ```verilog module top_level ( input wire clk_156mhz, inout wire [31:0] data_bus, output reg ready_signal = 0, // 默认未准备好 parameter DATA_WIDTH=32, CLOCK_FREQ_MHZ=156.25, PIN_COUNT=74+ /*其他辅助端子数目*/ ); always @(posedge clk_156mhz) begin // 实际业务逻辑省略... end initial begin $display("Initializing with parameters:"); $display("\tData Width=%d", DATA_WIDTH); $display("\tClock Frequency=%.2f MHz", CLOCK_FREQ_MHz); $display("\tTotal Pin Count=%d", PIN_COUNT); end endmodule ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徕卡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值