当我们学习过以太网的相关知识,学习了以太网模块IP核在FPGA 中的应用, 就可以将以太网IP 封装成RISCV cpu 所需要的IP 模块。 重新封装的模块将对之前的以太网模块进行改造。 主要是去掉UDP层, IP 层, 这些层的功能之后会在软件层面实现(软件层面的协议栈不光实现UDP,IP, 还有其他的协议需要支持),所以重新封装的模块只要保留mac 层就可以了, 同时设计好和RISCV cup 的总线接口。
改造后的以太网模块框图:

图1
其中eth_core.v 内部框图为图2所示:

图2
cpu 端的接口:
接收端:设计以太网cpu 端的接口为 双端口, (IP_RX_DRAM) ,双端口的深度为4096 bytes ,分为乒乓buffer。由于接口为32bit, 所以每个buffer的深度为512。
发送端:设计为双端口(MAC_TX_DRAM),双端口的深度为4096 bytes ,分为乒乓buffer。 接口为32bit, 每个buffer的深度为512。
接收,发送的双端口 控制字节定义:cpu 端的 bit[31] 为控制字, bit[27:16] 为接收到的以太网数据的字节(bytes)数量(length)。
mac_rx_web: cpu 写信号, 当得到完整的数据包后, 清除当前buffer时,使用。
mac_rx_addrb: cpu 地址总线, 用于指示读写双端口地址
mac_rx_doutb[31:0]: 双端口中的读数据通道, 连接到cpu 的 读数据总线上。
mac_tx_wea: cpu 写信号, 用于将软件的数据写入到双端口中。
mac_tx_addra: cpu 地址总线,指示双端口的地址。
mac_tx_dina[31:0] 写入双端口的 cpu 数据写 通道
mac_tx_douta[31:0] 读双端口到 cpu 读数据通道。
mdio 控制接口:
phy_addr[4:0] : 设置以太网phy mdio 的物理地址
smi_data[24:0]: 一组mdio 命令接口, 包括 start of frame + 读/写 OpCode + mdio register addr + write_data[15:0]。 (如果是读命令, write_data[15:0] 的内容不会被fpga 使用) {st_op, phy_reg, phy_reg_val_w} <= smi_data;
smi_en: 命令有效信号, 一个时钟宽度。
smi_dout[15:0] : 读命令发送后, 当smi_done == 1 时,得到的从phy 芯片返回的 寄存器 内容。
smi_done: 表示当前的命令操作结束。
smi_err: 当前的命令发生错误。
CPU寄存器接口:
图3
在cpu 设计中,我们将:
以太网的寄存器组分配物理地址:0xd000_0000 — 0xd000_7ff 空间。
将以太网发送buffer 分配空间为:0xd000_8000 — 0xd000_8fff
将以太网接收buffer 分配空间为:0xd000_c000 — 0xd000_cfff
0xd000_0000 ETH版本寄存器 R :
指示当前以太网IP 核的版本号。 只读寄存器。
0xD000_0004 ETH控制寄存器 R/W:
控制寄存器,用于启动以太网IP核,是否硬件添加padding, 是否计算CRC等。
0xD000_0008 ETH状态寄存器 R:
以太网状态寄存器,指示接收到的包是否有错误,以太网phy 是否有中断,当前以太网phy状态等。
0xD000_0014 Eth mac 寄存器 h R/W:
0xD000_0018 Eth mac 寄存器 l R/W:
以太网MAC地址寄存器
0xD000_001c Eth ip寄存器 R/W:
以太网IP 地址寄存器,表示当前设备的IP地址。
0xD000_0020 Eth mdio寄存器 R/W:
SMI (mdio)控制寄存器
0xD000_0024 Eth mask寄存器 R/W:
0xD000_0028 Eth IRQ enable R/W:
0xD000_002c Eth IRQ STAT R:
以太网中断寄存器, 中断屏蔽寄存器,中断状态寄存器。 指示以太网mac 层的接收错误; 以太网phy 相关中断;smi 错误等。
寄存器的具体信息,请参照RISC-V Address Map 3.09版
CPU接口verilog 代码:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: Fraser Innavotion Inc // Engineer: William Gou // // Create Date: 09/25/2018 09:28:18 AM // Design Name: // Module Name: RIB_fii_ETH // Project Name: RISC-V SOC RTL // Target Devices: FII-PE7030, FII-PRX100T, FII-BM7100, FII-PRA040, FII-PRA006 // Tool Versions: vivado 18.1/18.2 // Description: // // Dependencies: // this is a simple version of risc-v integer version,aim to show up basic concept of RTL // Revision:1.0 // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module RIB_fii_ETH #( parameter [ 31: 0 ] DEV_BASEADDR = 32'hD000_0000, parameter PERI_DEEP = 4, //memory data width parameter PERI_W = 32, //memory data width parameter PERI_MSK_W = 4, //memory data mask width parameter PERI_ADDR_W = 32 //memory address width ) ( input clk, input [31:0] i_rib_saddr, input [31:0] i_rib_sdin, output [31:0] o_rib_sdout, input i_rib_svalid, output [1:0] o_rib_sready, input [3:0] i_rib_swe, input i_rib_srd, input i_rib_sop, input E_RXC, input E_RXDV, input [3:0] E_RXD, output E_TXC, //以太网发送时钟 output E_TXEN, //发送使能 output [3:0] E_TXD, //发送数据 output e_mdc, input e_mdio_i, output e_mdio_o, output e_mdio_t, input ETH_IRQN, (* mark_debug = "true" *) output eth_irq, input rst_n ); wire [ PERI_W - 1 : 0 ] i_PERI_din = i_rib_sdin; reg [ PERI_W - 1 : 0 ] o_PERI_dout; assign o_rib_sdout = o_PERI_dout; wire [ PERI_ADDR_W - 1 : 0 ] i_addr = i_rib_saddr; wire [ PERI_MSK_W - 1: 0 ] i_wem = i_rib_swe; wire i_cs = (i_rib_saddr[31:16] == DEV_BASEADDR[31:16]) ? 1'b1 : 1'b0; wire reg_bank_cs = i_cs & (i_rib_saddr[15:14] == 2'b00) ; // register bank wire tx_bank_cs = i_cs & (i_rib_saddr[15:14] == 2'b10) ; // tx buffer bank wire rx_bank_cs = i_cs & (i_rib_saddr[15:14] == 2'b11) ; // rx buffer bank reg eth_valid = 0; always @( posedge clk ) if(!rst_n) eth_valid <= 0; else if(i_cs & i_rib_sop) eth_valid <= i_rib_svalid; else eth_valid <= 0; assign o_rib_sready = {1'b0, eth_valid}; wire rib_shk = i_rib_svalid & o_rib_sready[0]; wire i_we = (|i_wem) ? 1'b1 : 1'b0; //=============================================================================== wire [ PERI_MSK_W - 1: 0 ] wen = ( { PERI_MSK_W{i_cs} } & i_wem ); wire ren = i_cs & ( ~i_we ); //=============================================================================== wire [ 11: 0 ] reg_w_addr = i_addr[ 11: 0 ]; wire [ 7: 0 ] ETH_VER = 8'h01; reg [ 31: 0 ] ETH_CTRL = 0; //wire [ 31: 0 ] ETH_STAT; wire [2:0] eth_speed; wire [7:0] mac_error; reg [ 15: 0 ] ETH_MAC_H = 16'h66_72; reg [ 31: 0 ] ETH_MAC_L = 32'h61_73_65_72; reg [ 31: 0 ] ETH_IP = 32'hc0_a8_00_3c; reg [ 31: 0 ] ETH_MDIO = 32'h8000_0000; always @( posedge clk ) if(!rst_n) ETH_CTRL <= 0; else begin if( reg_bank_cs && reg_w_addr == 12'h004) // eth control reg begin if( wen[0] ) ETH_CTRL[07:00] <= i_PERI_din[07:00]; if( wen[1] ) ETH_CTRL[15:08] <= i_PERI_din[15:08]; if( wen[2] ) ETH_CTRL[23:16] <= i_PERI_din[23:16]; if( wen[3] ) ETH_CTRL[31:24] <= i_PERI_din[31:24]; end end always @( posedge clk ) if(!rst_n) ETH_MAC_H <= 16'h66_72; else begin if( reg_bank_cs && reg_w_addr == 12'h014) // mac high 16bit reg begin if( wen[0] ) ETH_MAC_H[07:00] <= i_PERI_din[07:00]; if( wen[1] ) ETH_MAC_H[15:08] <= i_PERI_din[15:08]; end end always @( posedge clk ) if(!rst_n) ETH_MAC_L <= 32'h61_73_65_72; else begin if( reg_bank_cs && reg_w_addr == 12'h018) // mac low 32bit reg begin if( wen[0] ) ETH_MAC_L[07:00] <= i_PERI_din[07:00]; if( wen[1] ) ETH_MAC_L[15:08] <= i_PERI_din[15:08]; if( wen[2] ) ETH_MAC_L[23:16] <= i_PERI_din[23:16]; if( wen[3] ) ETH_MAC_L[31:24] <= i_PERI_din[31:24]; end end always @( posedge clk ) if(!rst_n) ETH_IP <= 32'hc0_a8_00_3c; else begin if( reg_bank_cs && reg_w_addr == 12'h01c) // eth ip 32bit reg begin if( wen[0] ) ETH_IP[07:00] <= i_PERI_din[07:00]; if( wen[1] ) ETH_IP[15:08] <= i_PERI_din[15:08]; if( wen[2] ) ETH_IP[23:16] <= i_PERI_din[23:16]; if( wen[3] ) ETH_IP[31:24] <= i_PERI_din[31:24]; end end always @( posedge clk ) if(!rst_n) ETH_MDIO <= 32'h8000_0000; else begin ETH_MDIO[28] <= 0; if( rib_shk && reg_bank_cs && reg_w_addr == 12'h020) // eth mdio reg begin if( wen[0] ) ETH_MDIO[07:00] <= i_PERI_din[07:00]; if( wen[1] ) ETH_MDIO[15:08] <= i_PERI_din[15:08]; if( wen[2] ) ETH_MDIO[23:16] <= i_PERI_din[23:16]; if( wen[3] ) ETH_MDIO[31:24] <= i_PERI_din[31:24]; end end reg [ 31: 0 ] ETH_IRQ_MASK = 0; always @( posedge clk ) if(!rst_n) ETH_IRQ_MASK <= 0; else begin if( reg_bank_cs && reg_w_addr == 12'h024) // eth mask reg begin if( wen[0] ) ETH_IRQ_MASK[07:00] <= i_PERI_din[07:00]; if( wen[1] ) ETH_IRQ_MASK[15:08] <= i_PERI_din[15:08]; if( wen[2] ) ETH_IRQ_MASK[23:16] <= i_PERI_din[23:16]; if( wen[3] ) ETH_IRQ_MASK[31:24] <= i_PERI_din[31:24]; end end reg [ 31: 0 ] ETH_IRQ_ENA = 0; always @( posedge clk ) if(!rst_n) ETH_IRQ_ENA <= 0; else begin if( reg_bank_cs && reg_w_addr == 12'h028) // eth irq reg begin if( wen[0] ) ETH_IRQ_ENA[07:00] <= i_PERI_din[07:00]; if( wen[1] ) ETH_IRQ_ENA[15:08] <= i_PERI_din[15:08]; if( wen[2] ) ETH_IRQ_ENA[23:16] <= i_PERI_din[23:16]; if( wen[3] ) ETH_IRQ_ENA[31:24] <= i_PERI_din[31:24]; end end // eth control reg wire [ 31: 0 ] eth_irq_stat = {0, smi_err, 6'b00_0000, ~ETH_IRQN, mac_error}; (* mark_debug = "true" *)reg [ 31: 0 ] ETH_IRQ_REG = 0; genvar i; generate for (i = 0; i < 32; i = i + 1) begin : eth_irq_rr always @( posedge clk ) if(!rst_n) ETH_IRQ_REG[i] <= 0; else begin if( rib_shk && i_rib_srd && reg_bank_cs && reg_w_addr == 12'h02c) ETH_IRQ_REG[i] <= 0; else if(ETH_IRQ_ENA[i] & ETH_IRQ_MASK[i] & eth_irq_stat[i]) ETH_IRQ_REG[i] <= 1; end end endgenerate assign eth_irq = |(ETH_IRQ_REG); //=============================================================================== localparam CPU_BUF_SIZE = 9; localparam CPU_IDX_BIT = 1; localparam MAIN_CLK = 50; localparam PHY_SPEED = "100M"; // "AUTO", "1G", "100M", "10M" //=============================================================================== wire smi_done; wire [15:0] smi_dout; wire smi_err; wire smi_en = ETH_MDIO[28]; wire [3:0] mac_rx_web = {4{rx_bank_cs}} & wen; wire [CPU_BUF_SIZE + CPU_IDX_BIT - 1:0] mac_rx_addrb = i_addr[31:2]; wire [31:0] mac_rx_doutb; wire [3:0] mac_tx_wea = {4{tx_bank_cs}} & wen; wire [CPU_BUF_SIZE + CPU_IDX_BIT - 1:0] mac_tx_addra = i_addr[31:2]; wire [31:0] mac_tx_dina = i_rib_sdin; wire [31:0] mac_tx_douta; ETH_CORE # ( .CPU_BUF_SIZE (CPU_BUF_SIZE), .CPU_IDX_BIT (CPU_IDX_BIT), .MAIN_CLK (MAIN_CLK), .PHY_SPEED (PHY_SPEED) // "AUTO", "1G", "100M", "10M" ) ETH_CORE_inst ( .MAC_TX_CRC_EN (ETH_CTRL[1]), .MAC_TX_PADDING (ETH_CTRL[2]), .mac_error (mac_error[7:0]), .E_RXC (E_RXC), .E_RXDV (E_RXDV), .E_RXD (E_RXD), .E_TXC (E_TXC), .E_TXEN (E_TXEN), .E_TXD (E_TXD), .sys_clk (clk), .eth_en (ETH_CTRL[0]), .e_mdc (e_mdc), .e_mdio_i (e_mdio_i), .e_mdio_o (e_mdio_o), .e_mdio_t (e_mdio_t), .phy_addr (ETH_MDIO[25:21]), .smi_data ({2'b01, ETH_MDIO[27:26], ETH_MDIO[20:0]}), .smi_en (smi_en), .smi_dout (smi_dout), .smi_done (smi_done), .smi_err (smi_err), .eth_speed (eth_speed), .mac_rx_web (mac_rx_web), .mac_rx_addrb (mac_rx_addrb), .mac_rx_doutb (mac_rx_doutb), .mac_tx_wea (mac_tx_wea), .mac_tx_addra (mac_tx_addra), .mac_tx_dina (mac_tx_dina), .mac_tx_douta (mac_tx_douta), .reset (!rst_n | !ETH_CTRL[0]) ); //=============================================================================== always @ (*) begin casex ({rx_bank_cs, tx_bank_cs, reg_bank_cs, reg_w_addr[11:0]}) 15'h1_000: o_PERI_dout = {24'h00_0000, ETH_VER}; 15'h1_004: o_PERI_dout = ETH_CTRL; 15'h1_008: o_PERI_dout = {0, eth_speed, smi_err, 6'b00_0000, ~ETH_IRQN, mac_error}; 15'h1_014: o_PERI_dout = {16'h0000, ETH_MAC_H}; 15'h1_018: o_PERI_dout = ETH_MAC_L; 15'h1_01c: o_PERI_dout = ETH_IP; 15'h1_020: o_PERI_dout = {smi_done, smi_err, 1'b0, ETH_MDIO[28:16],smi_dout[15:0]}; 15'h1_024: o_PERI_dout = ETH_IRQ_MASK; 15'h1_028: o_PERI_dout = ETH_IRQ_ENA; 15'h1_02c: o_PERI_dout = ETH_IRQ_REG; 15'h2_xxx: o_PERI_dout = mac_tx_douta >> {i_addr[1:0], 3'b000}; // tx buffer data 15'h4_xxx: o_PERI_dout = mac_rx_doutb >> {i_addr[1:0], 3'b000}; // rx buffer data default: o_PERI_dout = 0; endcase end //=============================================================================== endmodule |
关于RISCV 以太网框架部分的详细信息,请参照视频讲解。