Menu Close

以太网模块(IP核) RISCV接口封装

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

改造后的以太网模块框图:

ethernet verilog code structure

图1

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

ethernet verilog module flow chart

 

图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寄存器接口:

ethernet verilog code register manual

图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 代码:

`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 以太网框架部分的详细信息,请参照视频讲解。

Posted in FPGA, FPGA 教材教案, Verilog, 教材与教案, 文章

发表回复

相关链接