前言
本文是为数电课编写的实验指导,介绍了如何为 Wujian100 添加一个简单的外设。本文主要参考了视频,添加了一个三色灯控制器。
- 项目代码:wujian100_basys3
- 指导视频:在Basys3上开发Wujian100 RISC-V SoC
- wujian100 相关文章:哈猪猪的博客
实验目的
- 熟悉基本的软硬件开发流程。
- 学习 Block Design,给 wujian100 SoC 添加外设。
实验器材
Basys3, Vivado 2022.2
实验原理
wujian100 SoC 概述
Wujian100 是阿里平头哥自主研发的 RISC-V SOC。其系统结构层次如下图所示。(其中有删除线的代表在移植过程中被裁剪的模块,虚线框的代表我们将会在实验一二中添加的模块)
Wujian100 SoC 基于玄铁 E902,是一块极低功耗、极低成本的嵌入式 CPU 核,非常适用于边缘智能场景。E902 采用 RISC-V RV32E[M]C 指令集架构,并支持部分平头哥自研拓展指令。
Wujian100 SoC 采用哈佛架构,拥有一块 64KB 的指令 SRAM 以及三块大小均为 64KB 的数据 SRAM。如果软件程序不是很大,在实现时我们可以通过删去部分 DRAM 来节省 FPGA 资源。
Wujian100 SoC 的系统总线支持 AMBA3.0 AHB-Lite 协议。AHB 总线协议主要用于高性能、高时钟频率的系统结构中。E902 实现了 AHB-Lite 协议中的部分内容。系统总线连接了 CPU,DMA 以及 SRAM 等核心部件,还连接了很多没有内部逻辑的 Dummy 模块。SoC 预留了这些 Dummy 模块的接口以及地址,方便用户自己设计支持 AHB 协议的模块替换 Dummy 以实现功能拓展。由各个 Dummy 模块名可知,用户可以设计主设备、从设备以及指令、数据存储器,连接到系统总线上。
系统总线通过同步桥连接到低速 AHB 总线上,后者再转接到低速 APB 总线并连接各个 APB 外设上:脉宽调制器(PWM)、定时器(TIM)、串行接口模块(USI)等。其中 USI 兼容支持 USART,SPI 以及 I2C 三种串行总线协议。在 SoC 文档 doc\wujian100_open Userguide v1.0.docx
中我们可以了解到 USI 的复用方式和对应信号名。此外,在低速总线上也预留了很多 Dummy 模块以供拓展。
wujian100 soc 的源码可以通过以下方法获取:
git clone https://github.com/T-head-Semi/wujian100_open
为了方便同学们实验,我们已经帮大家完成了 wujian100 到 Basys3 的移植,并且裁剪了部分模块以适应 Basys3 的硬件资源。同学们可以通过北大网盘链接下载得到工程文件,工程结构详见 readme。
我们主要完成了以下工作,需要同学们注意:
- 删除了 DMA 模块。删除了第三个 DATA SRAM,其地址空间为:0x2002_0000~0x2002_FFFF。因此,修改了 CDK 项目链接脚本
sdk\board\wujian100_open_evb\gcc_csky.ld
的地址空间。 - 修改了约束文件:映射了 GPIO,USART,JTAG,RST_N 等信号。请仔细阅读约束文件。其中系统的低有效复位键映射到了拨码开关 SW15 上,SW15 断开时系统复位。
- 添加了 pll 分频器,将板载 100MHz 时钟分频为 20MHz,以适用于 wujian100 SoC。
- 编写了 tcl 脚本,用于忽略综合和比特流生成时的警告。
外设添加
Wujian100 SoC 为我们预留了很多 dummy 模块,观察它们的源码可以看到它们只有基础的 AHB-Lite 总线端口定义,没有内部逻辑。我们可以设计自己的模块,再通过替换这些 dummy 来接入 SoC。
在本实验中,我们将借助 Vivado 的 Block Design 工具设计一个三色灯控制器。
三色灯及其电路图如下所示:
当 R、G、B 引脚为高电平(3.3V-5V)时,三色灯发出对应颜色的灯光。
我们要设计的三色灯控制器结构如下图所示:
三色灯控制器主要分为三个模块:AHB-Lite to AXI Bridge,AXI Interconnect 以及 MYIO。
- MYIO:我们将在 Vivido 中创建的三色灯驱动 IP,采用 AXI-Lite 协议,受总线控制输出 32 位的信号 myio。由于三色灯只需要 3bits 信号控制,因此实现时我们只使用输出信号的低三位。
- AHB-Lite to AXI Bridge:wujian100 SoC 的系统总线采用 AHB-Lite 协议,而我们创建的 IP MYIO 是 AXI-Lite 总线,因此我们需要转接桥来实现总线协议的转换。AHB-Lite to AXI Bridge 能将 AHB-Lite 转换为 AXI。
- AXI Interconnect:能够将 AXI 协议进一步转换为 AXI-Lite,最终连接到 MYIO 上。
调试原理
Wujian100 SoC 支持平头哥自定义的两线调试接口协议,通过两线接口与调试器 CKLink 通信。调试器通过 USB 与 PC 机相连,并与 Debug Server 通信。最后,借助平头哥的集成开发环境 CDK,我们可以实现对 wujian100 的调试。
调试时,首先我们将 CKLink 的 TCK
,TMS
,GND
连接至 Basys3 映射好的两线调试接口以及 GND
上,连接 CKLink 至 PC。在 CDK 中,默认设置下 Debug Server 即可识别到 CPU E902。在编译好程序后,我们就可以利用调试模式全速运行程序或者进行调试。
最后,Basys3 的管脚映射图可以参阅附录图。
实验步骤
移植运行wujian100
进入 Vivado 工程目录 wujian100_basys3_base\
,解压打开工程。
综合、实现、生成比特流。
为提高运行速度,可以在 tcl console 中输入以下命令以多线程运行。$max_threads
是你电脑支持的最高线程数。
set_param general.maxThreads $max_threads
下载比特流到basys3上。
在 IMPLEMENTATION -> Open Implemented Design -> Report Utilization 中可以看到详细的资源使用情况:
测试 GPIO
连接 CKLink 的 TCK
,TMS
,GND
至 basys3。basys3 的对应 pin 请参阅约束文件。
在本例中,PAD_JTAG_TCLK
连接 J1
,即 Pmod JA1;PAD_JTAG_TMS
连接 L2
,即 Pmod JA2。连线结果如下图:
gpio cdk 工程位于 sdk\projects\Labs\Lab1\gpio
,打开项目文件 CDK\wujian100_open-gpio.cdkproj
,编译。
打开 Project Settings -> Debug -> Connector Configurations -> Use ICE Settings,保持默认参数如下:
点击右侧 Connected Debug Target -> Update,出现如下信息表示 CKLink 成功识别到了 wujian100 的 CPU E902M:
Running Debug Server, auto to check the target at first...
T-HEAD: CKLink_Lite_V2, App_ver 2.36, Bit_ver null, Clock 2526.316KHz,
2-wire, With DDC, Cache Flush On, SN CKLink_Lite_V2-0307F39794.
+-- Debug Arch is CKHAD. --+
+-- CPU 0 --+
T-HEAD Xuan Tie CPU Info:
WORD[0]: 0x0804000c
WORD[1]: 0x10000000
WORD[2]: 0x24204038
MISA : 0x40001014
Target Chip Info:
CPU Type is E902M, Endian=Little, ISA Patch: 0x0, Revision: 0x0.
HWBKPT number is 5, HWWP number is 2.
MISA: (RV32MCE, Imp M-mode)
GDB connection command for CPUs(CPU0):
target remote 172.29.64.1:1025
target remote 192.168.1.5:1025
否则,请排查以下问题:
- bitstream 下载是否正确。
- CKLink 连线是否正确。
- 是否正确安装了 CKLink 的驱动,连接上后在设备管理器中能否找到 CKLink。
- 作为复位键的拨码开关 SW15 是否处于闭合状态。
- 是否打开了多个 CDK 工程的 Debug Server。
编译成功,且 CKLink 连接成功后,打开调式模式,点击运行,即可运行程序。
可以观察到 led 灯组正在展示一个二进制累加器:
测试 USART
仔细阅读 SoC 文档了解 USI 的复用方式和对应信号名,并在约束文件查看已经映射好的对应 basys3 pin。
在本例中,PAD_USI0_SCLK
为 RXD
,映射到 A14
,即 Pmod JB1;PAD_USI0_SD0
为 TXD
,映射到 A16
,即 Pmod JB2。
USART 的 CDK 项目位于 sdk\projects\Labs\Lab1\usart
,打开项目文件并编译。
提前在 pc 上安装好 USB 转 TTL 模块(如 CH340)的驱动程序,连接模块的 GND
,TXD
,RXD
到 basys3 对应 pin。打开 pc 的串口调试程序,初始化波特率 115200,8N1。CDK 利用调试模式全速运行程序,即可进行串口测试。
为 wujian100 添加外设
我们将在 Vivado 中通过创建 block design 来为 SoC 添加外设——三色灯控制器,替代 main_dummy_top0
。
首先我们点击左侧:IP INTEGRATOR->Create Block Design,命名为 ahb_axi。
添加 AHB-Lite 转 AXI 桥
我们需要将 AXI-Lite 的 MYIO 与 AHB-Lite 系统总线桥接起来。
先添加 AHB-Lite 转 AXI 桥。右键 Add IP,搜索并添加 AHB-Lite to AXI Bridge。选中新加入的 IP,点击上方工具栏的 Make External 或直接按下 Ctrl+T
,增加外部接口。
创建 IP
点击 Vivado 顶部 tools->create and package new ip->Create AXI4 Peripheral,命名为 myio。设置保持如下默认即可:
在 IP Catalog 中找到新建的 IP,右键点击 Edit in ip package。修改 module hdl 代码:myio_v1_0_S00_AXI.v
。观察代码可知,CPU 能通过总线读写 slv_reg0/1/2/3。因此:
我们在用户接口定义处添加:
// Users to add ports here
output [31:0] myio,
// User ports ends
在最后的用户逻辑中添加:
// Add user logic here
assign myio=slv_reg0;
// User logic ends
这样,我们将第一个 slave 寄存器的输出连接到 myio 上,再取 myio 的任意3个口(如低3位)输出到三色灯上。这样就能通过软件让 cpu 控制三色灯了。
再修改顶层代码:myio_v1_0.v
添加接口:
output [31:0] myio,
再在模块 myio_v1_0_S00_AXI
的实例化中添加接口:
myio_v1_0_S00_AXI_inst (
.myio (myio)
...
);
最后,回到 Vivado Package IP 中,一步步完成左侧的 Packaging Steps,最终 Re-Package IP 完成我们 IP 的创建和打包。
互联
打包后就可以在 Block Design 中添加刚刚创建的 IP 了。
由于我们创建的 IP myio 使用的是轻量级 AXI 总线,因此还需要加入 AXI Interconnect。加入后右键 Customize IP,改为输入输出各1个接口。
接着,Run connection automation。为简化工程,删掉自动生成的复位模块,直接将三个模块的复位端口相连,并连接到 s_ahb_hresetn_0
上,后者最后将和 soc 中 AHB 总线相连。
为 myio Make External,引出 myio[31:0]
。
在 Address Editor 中修改 myio 的地址。我们要将它分配到 main_dummy_top0
的位置上,因此地址范围为:0x4001_0000~0x4001_FFFF
。
最后,在 Diagram 上方工具栏中点击 Validate Design,完成设计。最终的结构应和原理中的相同。
系统整合
完成 Block Design 后,我们需要将 ahb_axi 整合进 SoC 中。
在 Source->Design Sources 中找到我们设计的 ahb_axi。右键 Generate Output Product,接着再 Create HDL Wrapper。打开源码 ahb_axi_wrapper.v
可以看到,ports 的最后三个就是我们的 myio,时钟和复位,其余的都是 AHB-Lite 的。
在 Sources 中新建一个 myio_top.v
,作为顶层模块用来替代 main_dummy_top0
。后者是模块 ahb_dummy
的实例化。
因此首先,我们定义 myio_top
的输入输出接口和 ahb_dummy
一致。
接着,我们再实例化我们的模块 ahb_axi_wrapper
。将它的接口与 myio_top
的接口匹配相连。注意以下几个问题:
- 将同一信号的 port 相连。wrapper 中的端口名基本上只是多了开头的
AHB_INTERFACE_0_
,除了以下几个 port。 hready
。ahb_axi_wrapper
里有hready_in
,hready_out
。但是ahb_dummy
只有hready
。处理方法是将hready_out
和hready
直接相连,hready_in
则:
wire hready_in;
assign hready_in=hready;
原因请自行查阅 wujian100 和 Xilinx 手册。
ahb_axi_wrapper
多了一个hburst
,直接加到myio_top
作为 input。事实上 dummy0 的顶层模块是有这根线的。
input hburst;
wire [2:0] hburst;
hresp
。hresp
是 2bits 信号,而ahb_axi_wrapper
中的AHB_INTERFACE_0_hresp
只需要hresp
最低位,处理方法:
wire [1:0] hresp;
assign hresp[1]=1'b0;
ahb_axi_wrapper u_ahb_axi_wrapper (
...
.AHB_INTERFACE_0_hresp ( hresp[0] ),
...
);
myio
,引为myio_top
的 output port。
完成 myio_top.v
后,我们到 ahb_matrix_top.v
中替代 x_main_dummy_top0
。注意 hburst
和 myio
。
myio_top x_main_dummy_top0 ( //ahb_dummy_top
...
.hburst (hmain0_dummy0_s7_hburst),
.myio (myio)
};
将模块的输出 myio
一直引到系统顶层:wujian100_open_fpga_top.v
。注意要在每层同时添加 port、wire 和实例化的连线。
最后,在约束文件中添加:
set_property -dict {PACKAGE_PIN M18 IOSTANDARD LVCMOS33 } [get_ports { myio[0] }]; #R
set_property -dict {PACKAGE_PIN N17 IOSTANDARD LVCMOS33 } [get_ports { myio[1] }]; #G
set_property -dict {PACKAGE_PIN P18 IOSTANDARD LVCMOS33 } [get_ports { myio[2] }]; #B
映射到了 Basys3 JC 的三个口。
综合实现生成比特流,下载到 Basys3 上。
测试外设
连接三色灯到 Basys3 JC 对应 pin。
在本例中,R
对应 M18
,即 Pmod JC2
,G
对应 N17
,即 Pmod JC3
,B
对应 P18
,即 Pmod JC4
。
测试三色灯的 CDK 项目位于 sdk\projects\Labs\Lab1\myio
,编译,调试模式运行。可以观察到三色灯的颜色变化: