When we have learned the relevant knowledge of Ethernet and the application of the Ethernet module IP core in the FPGA, we can encapsulate the Ethernet IP into the IP module required by the RISCV cpu. The repackaged module will retrofit the previous Ethernet module. It is mainly to remove the UDP layer and the IP layer. The functions of these layers will be implemented at the software level later (the protocol stack at the software level not only implements UDP, IP, but also other protocols that need to be supported), so the repackaged module only needs to keep the mac layer. Okay, at the same time design the bus interface with RISCV cup.
The modified Ethernet module block diagram:
figure 1
The internal block diagram of eth_core.v is shown in Figure 2:
figure 2
The interface on the cpu side:
Receiver: The interface on the Ethernet CPU side is designed as a dual-port (IP_RX_DRAM), the depth of the dual-port is 4096 bytes, and it is divided into ping-pong buffers. Since the interface is 32bit, the depth of each buffer is 512.
Transmitter: designed as dual-port (MAC_TX_DRAM), the depth of dual-port is 4096 bytes, divided into a ping-pong buffer. The interface is 32bit, and the depth of each buffer is 512.
Receive and send dual-port control byte definition: bit[31] on the CPU side is the control word, bit[27:16] is the number of bytes (bytes) of the received Ethernet data (length).
mac_rx_web: cpu write signal, used when clearing the current buffer after getting a complete data packet.
mac_rx_addrb: cpu address bus, used to indicate read and write dual-port addresses
mac_rx_doutb[31:0]: The read data channel in the dual-port, connected to the read data bus of the CPU .
mac_tx_wea: cpu write signal, used to write software data into dual ports.
mac_tx_addra: cpu address bus, indicating the address of the dual-port.
mac_tx_dina[31:0] Write to dual port cpu data write channel
mac_tx_douta[31:0] Read dual port to cpu read data channel.
mdio control interface:
phy_addr[4:0] : Set the physical address of Ethernet phy mdio
smi_data[24:0]: A set of mdio command interfaces, including start of frame + read/write OpCode + mdio register addr + write_data[15:0]. (If it is a read command, the content of write_data[15:0] will not be used by fpga) {st_op, phy_reg, phy_reg_val_w} <= smi_data;
smi_en: command valid signal, one clock width.
smi_dout[15:0] : After the read command is sent, when smi_done == 1, the contents of the register returned from the phy chip are obtained.
smi_done: Indicates the end of the current command operation.
smi_err: An error occurred with the current command.
CPU register interface:
image 3
In cpu design, we will:
The Ethernet register group allocates physical addresses: 0xd000_0000 — 0xd000_7ff space.
Allocate the Ethernet sending buffer as: 0xd000_8000 — 0xd000_8fff
Allocate the Ethernet receive buffer as: 0xd000_c000 — 0xd000_cfff
0xd000_0000 ETH version register R:
Indicates the version number of the current Ethernet IP core. Read-only register.
0xD000_0004 ETH control register R/W:
Control register, used to start the Ethernet IP core, whether to add padding in hardware, whether to calculate CRC, etc.
0xD000_0008 ETH status register R:
Ethernet status register, indicating whether the received packet has errors, whether the Ethernet phy is interrupted, the current Ethernet phy status, etc.
0xD000_0014 Eth mac register h R/W:
0xD000_0018 Eth mac register l R/W:
Ethernet MAC Address Register
0xD000_001c Eth ip register R/W:
Ethernet IP address register, indicating the IP address of the current device.
0xD000_0020 Eth mdio register R/W:
SMI (mdio) Control Register
0xD000_0024 Eth mask register R/W:
0xD000_0028 Eth IRQ enable R/W:
0xD000_002c Eth IRQ STAT R:
Ethernet Interrupt Register, Interrupt Mask Register, Interrupt Status Register. Indicates Ethernet mac layer receiving errors; Ethernet phy related interrupts; smi errors, etc.
For the specific information of the register, please refer to RISC-V Address Map version 3.09
CPU interface verilog code:
`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, // Ethernet transmit clock output E_TXEN, // transmit enable output [3:0] E_TXD, // send data 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 <= 0; else begin if( rib_shk && i_rib_srd && reg_bank_cs && reg_w_addr == 12'h02c) ETH_IRQ_REG <= 0; else if(ETH_IRQ_ENA & ETH_IRQ_MASK & eth_irq_stat) ETH_IRQ_REG <= 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
For more information on the RISCV Ethernet framework part, please refer to the video explanation.