[原创] 安路SF1系列FPGA(三)USB2.0高速通信-1

瓜弟   2023-3-28 21:47 楼主

        FPGA使用USB与PC进行通信的方式通常有以下几种方案:1、通过FT232、CH340等转接芯片使用SPI、UART进行通信;2、CY7C68013等第三方MCU进行转发;3、直接使用FPGA的IO口进行低速USB通信;4、使用FPGA+USBPHY进行高速通信。本文使用外接USBPHY进行USB2.0高速通信,方案框图如下
USB.jpg

1、概述

        USBD协议栈使用ASICS.ws公司的开源USB2_Dev核,该公司开源产品GitHub地址为www-asics-ws · GitHub,框图如下图所示。从框图可知,该IP核与FPGA与其他功能模块之间使用Wishbone总线进行通信,与USBPHY芯片之间使用UTMI接口进行通信,同时移植过程中还需要实现SSRAM功能。通过该IP,可实现USB2.0的高速通信(480Mbps)。由于安路的SF1系列FPGA的RISCV硬核与外部交互的总线为AHB,所以再USBD核与AHB总线之间需要使用AHB转Wishbone总线模块。该USBD核与USBPHY之间的接口使用的是UTMI接口,该接口信号众多,且当前UTMI接口的USBPHY芯片也较少,故使用UTMI2ULPI接口,将其转换为ULPI接口以减少IO口的占用,该模块使用开源项目core_ulpi_wrapper

USB2.png

2、AHB2Wishbone总线转换

         wb.png         如上图所示为Wishbone的主从连接关系,各引脚功能如下:

CLK_O 输出信号,系统时钟,作为MASTER和SLAVE的时钟输入
RST_O 输出复位信号,作为MASTER和SLAVE的复位输入,使得WISHBONE接口内部的状态机全部恢复到起始态
CLK_I 输入信号,MASTER和SLAVE的时钟输入端,所有WISHBONE输出信号都在CLK-I的上升沿有效
DAT_O 数据输出信号,最大位宽为64位
RST_I 输入信号,使得WISHBONE接口内部的状态机全部恢复到起始态
TGD_I 输入信号,数据标签类型
TGD_O 输出信号,数据标签类型
ACK_I 输入信号,确认信号,当该信号有效时,表明一个总线周期结束
ADR_O 输出信号,地址输出
CYC_O 周期输出信号,当该信号有效,表明进程中的总线是有效的,即它确定了总线周期的持续时间。CYC_O从数据传输的第一个比特开始有效,到数据传输结束为止
ERR_l 输入信号,当该信号有效,表明总线周期非正常结束,表示有错误发生
LOCK_O 输出信号,当该信号有效,表明当前总线周期锁定,不能被其他进程中断
RTY_I 输入信号,当该信号有效,表明MASTER还没有准备好接收或发送数据,重新请求总线
SEL_O 输出信号,用于选择数据信号线的输出
STB_O 输出信号,表明一个有效数据传送周期
WE_O 读使能信号,决定信号的读和写功能

        上述信号可裁剪,本文使用了ADR_O、WE_O、STB_O、SEL_O、ACK_I、CLK_O、RST_O等信号,AHB转Wishbone代码如下

module AHB2WB
(
    input wire CLK,
    input wire RSTn,

    output reg[31:0] WB_ADDR,
    input wire[31:0] WB_DATA_IN,
    output reg[31:0] WB_DATA_OUT,
    output reg WB_WE,
    output reg[3:0] WB_SEL,
    output reg WB_STB,
    input wire WB_ACK,
    output reg WB_CYC,

    input wire HSEL,
    input wire[1:0] HTRANS,
    input wire[31:0] HADDR,
    input wire HWRITE,
    input wire[2:0] HSIEZ,
    input wire[2:0] HBURST,
    input wire[3:0] HPROT,
    input wire[31:0] HWDATA,
    output reg[31:0] HRDATA,
    output reg HREADY,
    output reg[1:0] HRESP
);

parameter HTRANS_IDLE       = 2'b00;        //Slave忽略掉此时的传输
parameter HTRANS_BUSY       = 2'b01;        //表示master正在处理数据,slave需要忽略掉此时的传输
parameter HTRANS_NONSEQ     = 2'b10;        //表明当前是单笔的数据,或者是Burst的第一笔数据
parameter HTRANS_SEQ        = 2'b11;        //是Burst传输的剩余数据

parameter HBURST_SINGLE     = 3'b000;       //单笔数据传输
parameter HBURST_INCR       = 3'b000;       //不定长递增方式批量传输
parameter HBURST_WRAP4      = 3'b000;       //4个数据回绕方式批量传输
parameter HBURST_INCR4      = 3'b000;       //4个数据递增方式批量传输
parameter HBURST_WRAP8      = 3'b000;       //8个数据回绕方式批量传输
parameter HBURST_INCR8      = 3'b000;       //8个数据递增方式批量传输
parameter HBURST_WRAP16     = 3'b000;       //16个数据回绕方式批量传输
parameter HBURST_INCR16     = 3'b000;       //16个数据递增方式批量传输

parameter HREADY_REDY       = 1'b1;
parameter HREADY_BUSY       = 1'b0;

parameter HRESP_OKEY        = 2'b00;        //传输完成
parameter HRESP_ERROR       = 2'b01;        //传输错误
parameter HRESP_RETRY       = 2'b10;        //传输未完成,请求主设备重新开始一个传输,
                                            //arbiter会继续使用通常的优先级
parameter HRESP_SPLIT       = 2'b11;        //传输未完成,请求主设备分离一次传输,
                                            //arbiter会调整优先级方案以便其他请求总线的主设备可以访问总线

parameter HSIZE_8b          = 3'b000;
parameter HSIZE_16b         = 3'b001;
parameter HSIZE_32b         = 3'b010;
parameter HSIZE_64b         = 3'b011;
parameter HSIZE_128b        = 3'b100;
parameter HSIZE_256b        = 3'b101;
parameter HSIZE_512b        = 3'b110;
parameter HSIZE_1024b       = 3'b111;

parameter HWRITE_WR         = 1'b1;
parameter HWRITE_RD         = 1'b0;

parameter FSM_IDLE          = 4'h0;
//parameter FSM_ADDR          = 4'h1;
parameter FSM_WR            = 4'h2;
parameter FSM_RD            = 4'h4;
reg[3:0] FSM_Current;
reg[3:0] FSM_Next;

always@(posedge CLK)
begin
    if(RSTn == 0)
        begin
            FSM_Current <= FSM_IDLE;
        end
    else
        begin
            FSM_Current <= FSM_Next;
        end
end

always @(*)
begin
    if(RSTn == 0)
        begin
            FSM_Next <= FSM_IDLE;
        end
    else
        begin
            case(FSM_Current)
                FSM_IDLE:
                    begin
                        if(
                            (HSEL == 1)
                            &&(HTRANS == HTRANS_NONSEQ)
                        )
                            begin
                                if(HWRITE == HWRITE_WR)
                                    begin
                                        FSM_Next <= FSM_WR;
                                    end
                                else
                                    begin
                                        FSM_Next <= FSM_RD;
                                    end
                            end
                        else
                            begin
                                FSM_Next <= FSM_IDLE;
                            end
                    end
                FSM_WR:
                    begin
                        if(WB_ACK == 1)
                            begin
                                FSM_Next <= FSM_IDLE;
                            end
                        else
                            begin
                                FSM_Next <= FSM_WR;
                            end
                    end
                FSM_RD:
                    begin
                        if(WB_ACK == 1)
                            begin
                                FSM_Next <= FSM_IDLE;
                            end
                        else
                            begin
                                FSM_Next <= FSM_WR;
                            end
                    end
                default:
                    begin
                        FSM_Next <= FSM_IDLE;
                    end
            endcase
        end
end

always@(posedge CLK)
begin
    if(RSTn == 0)
        begin
            HRDATA <= 32'hZ;
            HREADY <= HREADY_REDY;
            HRESP <= HRESP_OKEY;
            WB_ADDR <= 32'hZ;
            WB_DATA_OUT <= HWDATA;
            WB_WE <= 1'b0;
            WB_STB <= 1'b0;
            WB_CYC <= 1'b0;
            WB_SEL <= 4'h0;
        end
    else
        begin
            case(FSM_Current)
                FSM_IDLE:
                    begin
                        if(
                            (HSEL == 1)
                            &&(HTRANS == HTRANS_NONSEQ)
                        )
                            begin
                                HRDATA <= HRDATA;
                                HREADY <= HREADY_BUSY;
                                HRESP <= HRESP_OKEY;
                                WB_ADDR <= HADDR;
                                WB_DATA_OUT <= HWDATA;
                                WB_WE <= 1'b0;
                                WB_STB <= 1'b0;
                                WB_CYC <= 1'b0;
                                WB_SEL <= 4'h0;
                            end
                        else
                            begin
                                HRDATA <= HRDATA;
                                HREADY <= HREADY_REDY;
                                HRESP <= HRESP_OKEY;
                                WB_ADDR <= HADDR;
                                WB_DATA_OUT <= HWDATA;
                                WB_WE <= 1'b0;
                                WB_STB <= 1'b0;
                                WB_CYC <= 1'b0;
                                WB_SEL <= 4'h0;
                            end
                    end
                FSM_WR:
                    begin
                        HRDATA <= HRDATA;
                        HREADY <= HREADY_BUSY;
                        HRESP <= HRESP_OKEY;
                        WB_ADDR <= WB_ADDR;
                        WB_DATA_OUT <= HWDATA;
                        WB_WE <= 1'b1;
                        WB_STB <= 1'b1;
                        WB_CYC <= 1'b1;
                        case(HSIEZ)
                            HSIZE_8b :
                                begin
                                    WB_SEL <= 4'h1;
                                end
                            HSIZE_16b:
                                begin
                                    WB_SEL <= 4'h3;
                                end
                            HSIZE_32b:
                                begin
                                    WB_SEL <= 4'hF;
                                end
                            default :
                                begin
                                    WB_SEL <= 4'h0;
                                end
                        endcase
                    end
                FSM_RD:
                    begin
                        HRDATA <= WB_DATA_IN;
                        HREADY <= HREADY_BUSY;
                        HRESP <= HRESP_OKEY;
                        WB_ADDR <= WB_ADDR;
                        WB_DATA_OUT <= HWDATA;
                        WB_WE <= 1'b0;
                        WB_STB <= 1'b1;
                        WB_CYC <= 1'b1;
                        case(HSIEZ)
                            HSIZE_8b :
                                begin
                                    WB_SEL <= 4'h1;
                                end
                            HSIZE_16b:
                                begin
                                    WB_SEL <= 4'h3;
                                end
                            HSIZE_32b:
                                begin
                                    WB_SEL <= 4'hF;
                                end
                            default :
                                begin
                                    WB_SEL <= 4'h0;
                                end
                        endcase
                    end
                default:
                    begin
                        HRDATA <= 32'hZ;
                        HREADY <= HREADY_REDY;
                        HRESP <= HRESP_OKEY;
                        WB_ADDR <= 32'hZ;
                        WB_DATA_OUT <= HWDATA;
                        WB_WE <= 1'b0;
                        WB_STB <= 1'b0;
                        WB_CYC <= 1'b0;
                        WB_SEL <= 4'h0;
                    end
            endcase
        end
end

endmodule

        Riscv端的软件代码如下

#include <stdio.h>
#include "nuclei_sdk_hal.h"
#include "anl_printf.h"



int main(void)
{
	unsigned long test;
	while(1)
	{
		*((unsigned long *)0x40000000) = 0x5A5A5A;
		test = *((unsigned long *)0x40000004);
	}
}


        使用片上逻辑分析仪对Wishbone的关键信号进行分析,截图如下,可见其符合Wishbone时序。

wave.png

本帖最后由 瓜弟 于 2023-3-28 21:46 编辑

回复评论 (10)

点赞  2023-3-28 21:47

项目还在测试中,测完再发

点赞  2023-3-28 21:49

牛逼!

默认摸鱼,再摸鱼。2022、9、28
点赞  2023-3-28 23:24

很不错的一个项目,值得深入研究了解。


点赞  2023-3-29 06:33

学习了!                                                                                                                                       

点赞  2023-3-29 07:50

厉害厉害,话说FPGA可以自制成一个USBPHY吗,感觉应该也可以。

点赞  2023-3-29 09:11
引用: wangerxian 发表于 2023-3-29 09:11 厉害厉害,话说FPGA可以自制成一个USBPHY吗,感觉应该也可以。

USB3好像可以,走LVDS,USB2应该不行

点赞  2023-3-29 17:22
电子设计新人,来学习学习,看看大神们的思路都是怎么样的。
点赞  2023-8-14 22:36

没有后续了吗?楼主大神

点赞  2023-11-13 09:57
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复