update chapter 7/8

pull/18/head
liguo 2 years ago
parent 353b3406bb
commit 5274da8002

@ -46,14 +46,13 @@
- [5.5 lab3_challenge1 进程等待和数据段复制(难度:★★☆☆☆](chapter5_process.md#lab3_challenge1_wait) - [5.5 lab3_challenge1 进程等待和数据段复制(难度:★★☆☆☆](chapter5_process.md#lab3_challenge1_wait)
- [5.6 lab3_challenge2 实现信号量(难度:★★★☆☆](chapter5_process.md#lab3_challenge2_semaphore) - [5.6 lab3_challenge2 实现信号量(难度:★★★☆☆](chapter5_process.md#lab3_challenge2_semaphore)
[第六章. PKE实验4文件系统](chapter6_filesystem.md) ----- 课程资源: [PPT](./resources/第六章.实验4文件系统.pptx) [视频讲解](https://www.bilibili.com/video/BV1Us4y1h7tT) [第六章. PKE实验4文件系统](chapter6_filesystem.md)
- [5.1 实验4的基础知识](chapter6_filesystem.md#fundamental)
- [6.1 实验4的基础知识](chapter6_filesystem.md#fundamental) - [5.2 lab4_1 文件操作](chapter6_filesystem.md#lab4_1_file)
- [6.2 lab4_1 文件操作](chapter6_filesystem.md#lab4_1_file) - [5.3 lab4_2 目录文件](chapter6_filesystem.md#lab4_2_dir)
- [6.3 lab4_2 目录文件](chapter6_filesystem.md#lab4_2_dir) - [5.4 lab4_3 硬链接](chapter6_filesystem.md#lab4_3_hardlink)
- [6.4 lab4_3 硬链接](chapter6_filesystem.md#lab4_3_hardlink) - [5.5 lab4_challenge1 多级目录(难度:★☆☆☆☆](chapter6_filesystem.md#lab4_challenge1_treedir)
- [6.5 lab4_challenge1 相对路径(难度:★★★☆☆](chapter6_filesystem.md#lab4_challenge1_pwd) - [5.6 lab3_challenge2 虚拟文件系统(难度:★★★★☆](chapter6_filesystem.md#lab4_challenge2_vfs)
- [6.6 lab4_challenge2 重载执行(难度:★★★★☆](chapter6_filesystem.md#lab4_challenge2_exec)
## 第二部分:系统能力培养实验 ## 第二部分:系统能力培养实验
@ -64,7 +63,7 @@
- [7.3 fpga实验2以中断方式实现uart通信](chapter7_riscv_on_pynq.md#hardware_lab2) - [7.3 fpga实验2以中断方式实现uart通信](chapter7_riscv_on_pynq.md#hardware_lab2)
- [7.4 fpga实验3配置连接到PS端的USB设备](chapter7_riscv_on_pynq.md#hardware_lab3) - [7.4 fpga实验3配置连接到PS端的USB设备](chapter7_riscv_on_pynq.md#hardware_lab3)
[第八章. PKE实验5设备和文件](chapter8_device.md) ----- 课程资源: [PPT](./resources/第八章.实验4设备管理.pptx) [视频讲解](https://www.bilibili.com/video/BV1LB4y157Rb) [第八章. PKE实验5设备管理](chapter8_device.md) ----- 课程资源: [PPT](./resources/第八章.实验4设备管理.pptx) [视频讲解](https://www.bilibili.com/video/BV1LB4y157Rb)
- [8.1 实验5的基础知识](chapter8_device.md#fundamental) - [8.1 实验5的基础知识](chapter8_device.md#fundamental)
- [8.2 lab5_1 轮询方式](chapter8_device.md#polling) - [8.2 lab5_1 轮询方式](chapter8_device.md#polling)

@ -3,71 +3,81 @@
### 目录 ### 目录
- [7.1 系统能力培养部分实验环境安装](#environments) - [7.1 系统能力培养部分实验环境安装](#environments)
- [7.1.1 Vivado开发环境](#subsec_vivadoenvironments) - [7.1.1 Vivado开发环境](#subsec_vivadoenvironments)
- [7.1.2 fpga-pynq工程源码获取](#subsec_src) - [7.1.2 fpga-pynq工程源码获取](#subsec_src)
- [7.1.3 工程编译环境配置](#subsec_projectenv) - [7.1.3 工程编译环境配置](#subsec_projectenv)
- [7.1.4 蓝牙小车硬件组装](#subsec_hardware) - [7.1.4 蓝牙小车硬件组装](#subsec_hardware)
- [7.1.5 操作系统的替换修改](#subsec_system) - [7.1.5 操作系统的替换修改](#subsec_system)
- [7.2 fpga实验1在Rocket Chip上添加uart接口](#hardware_lab1) - [7.2 fpga实验1在Rocket Chip上添加uart接口](#hardware_lab1)
- [实验目的](#lab1_target) - [实验目的](#lab1_target)
- [实验内容](#lab1_content) - [实验内容](#lab1_content)
- [实验结果](#lab1_result) - [实验结果](#lab1_result)
- [7.3 fpga实验2以中断方式实现uart通信](#hardware_lab2) - [7.3 fpga实验2以中断方式实现uart通信](#hardware_lab2)
- [实验目的](#lab2_target) - [实验目的](#lab2_target)
- [实验内容](#lab2_content) - [实验内容](#lab2_content)
- [实验结果](#lab2_result) - [实验结果](#lab2_result)
- [7.4 fpga实验3配置连接到PS端的USB设备](#hardware_lab3) - [7.4 fpga实验3配置连接到PS端的USB设备](#hardware_lab3)
- [实验目的](#lab3_target) - [实验目的](#lab3_target)
- [实验内容](#lab3_content) - [实验内容](#lab3_content)
- [实验结果](#lab3_result) - [实验结果](#lab3_result)
在本章中我们将结合fpga-pynq开发板在PYNQ上部署RISCV处理器。并带领读者对rocket chip进行一些修改逐步为其添加uart外设支持和中断支持并搭载PKE内核运行小车控制程序实现一个可以通过蓝牙控制的智能小车。读者在完成6.1节和6.2节中的实验环境搭建后便可进行6.3节、6.4节和6.5节中的相关实验。 在本章中我们将结合fpga-pynq开发板在PYNQ上部署RISCV处理器。并带领读者对rocket chip进行一些修改逐步为其添加uart外设支持和中断支持并搭载PKE内核运行小车控制程序实现一个可以通过蓝牙控制的智能小车。读者在完成7.1节中的实验环境搭建后便可进行7.2节、7.3节和7.4节中的相关实验。
**需要特别注意的是本章中的三个fpga实验并不是独立设计的而是与第七章中的实验4_1、4_2和4_3依次对应。每一个fpga实验为对应的软件实现提供硬件支持。因此读者应该按照如下顺序来完成第六章和第七章的实验在完成“6.3 fpga实验1”后前往第七章完成对应的PKE软件实验——“7.2 lab4_1_POLL”在完成"6.4 fpga实验2"后前往第七章完成对应的PKE软件实验——“7.3 lab4_2_PLIC”在完成"6.5 fpga实验3"后前往第七章完成对应的PKE软件实验——“7.4 lab4_3_hostdevice”。** **需要特别注意的是本章中的三个fpga实验并不是独立设计的而是与第八章中的实验5_1、5_2和5_3依次对应。每一个fpga实验为对应的软件实现提供硬件支持。因此读者应该按照如下顺序来完成第七章和第八章的实验在完成“7.2 fpga实验1”后前往第八章完成对应的PKE软件实验——“8.2 lab4_1_POLL”在完成"7.3 fpga实验2"后前往第八章完成对应的PKE软件实验——“8.3 lab4_2_PLIC”在完成"7.4 fpga实验3"后前往第八章完成对应的PKE软件实验——“8.4 lab4_3_hostdevice”。**
<a name="environments"></a> <a name="environments"></a>
## 6.1 系统能力培养部分实验环境安装 ## 7.1 系统能力培养部分实验环境安装
<a name="subsec_vivadoenvironments"></a> <a name="subsec_vivadoenvironments"></a>
### 6.1.1 Vivado开发环境 ### 7.1.1 Vivado开发环境
Vivado设计套件是FPGA厂商赛灵思公司2012年发布的集成设计环境。本实验需要使用Vivado对FPGA电路进行修改。**Vivado设计套件在Windows平台与Linux平台都可安装使用且安装与使用过程基本相同读者可根据自身情况选择**。下面详细介绍这两种搭建方案: Vivado设计套件是FPGA厂商赛灵思公司2012年发布的集成设计环境。本实验需要使用Vivado对FPGA电路进行修改。**Vivado设计套件在Windows平台与Linux平台都可安装使用且安装与使用过程基本相同请根据所用平台跳转到相应章节进行Vivado环境安装**
**方案一Windows平台** * [Windows平台](#subsec_vivadoenvironments_windows)
* [Linux平台Ubuntu桌面环境](#subsec_vivadoenvironments_linux)
Vivado开发环境提供Windows平台支持该方案的安装过程简单**推荐读者优先选择此方案进行Vivado开发环境搭建**。具体步骤如下: <a name="subsec_vivadoenvironments_windows"></a>
#### 场景一Windows平台
1. 下载安装程序。建议安装2016.2版本的Vivado进入[下载页面](https://china.xilinx.com/support/download/index.html/content/xilinx/zh/downloadNav/vivado-design-tools/archive.html)下拉找到2016.2版本,下载**Windows Web Installer**在弹出的Xilinx账号登录页面注册账号并登录后下载会自动开始。 1. 下载安装程序。建议安装2016.2版本的Vivado进入[下载页面](https://china.xilinx.com/support/download/index.html/content/xilinx/zh/downloadNav/vivado-design-tools/archive.html)下拉找到2016.2版本,下载**Windows Web Installer**在弹出的Xilinx账号登录页面注册账号并登录后下载会自动开始。
<img src="pictures/vivado_17.png" alt="vivado_17" style="zoom:80%;" /> <img title="" src="pictures/vivado_17.png" alt="vivado_17" style="zoom:80%;" data-align="center" width="483">
2. 启动安装程序。**注意安装过程需要保证网络畅通。以及30G以上的磁盘剩余空间**。 2. 启动安装程序。**注意安装过程需要保证网络畅通。以及30G以上的磁盘剩余空间**。
双击所下载的安装包直接运行即可。如下图窗口弹出时点击Continue。 双击所下载的安装包直接运行即可。如下图窗口弹出时点击Continue。
<img src="pictures/vivado_1.jpg" alt="vivado_1" style="zoom:80%;" /> <img title="" src="pictures/vivado_1.jpg" alt="vivado_1" style="zoom:80%;" data-align="center" width="428">
3. 进入欢迎页面点击next。 3. 进入欢迎页面点击next。
<img src="pictures/vivado_2.jpg" alt="vivado_2" style="zoom:80%;" /> <img title="" src="pictures/vivado_2.jpg" alt="vivado_2" style="zoom:80%;" data-align="center">
4. 输入Xilinx账号密码并选中Download and Install Now后点击next。下载Vivado安装包时注册的账号可以在此处使用若无账号则可以点击上方“please create one”进行注册。 4. 输入Xilinx账号密码并选中Download and Install Now后点击next。下载Vivado安装包时注册的账号可以在此处使用若无账号则可以点击上方“please create one”进行注册。
<img src="pictures/vivado_3.jpg" alt="vivado_3" style="zoom:80%;" /> <img title="" src="pictures/vivado_3.jpg" alt="vivado_3" style="zoom:80%;" data-align="center">
5. 勾选同意用户协议后点击next。 5. 勾选同意用户协议后点击next。
<img src="pictures/vivado_4.jpg" alt="vivado_4" style="zoom:80%;" /> <img title="" src="pictures/vivado_4.jpg" alt="vivado_4" style="zoom:80%;" data-align="center">
6. 勾选Vivado HL System Edition后点击next。 6. 勾选Vivado HL System Edition后点击next。
<img src="pictures/vivado_5.jpg" alt="vivado_5" style="zoom:80%;" /> <img title="" src="pictures/vivado_5.jpg" alt="vivado_5" style="zoom:80%;" data-align="center">
7. 根据需要勾选安装项点击next。 7. 根据需要勾选安装项点击next。
<img src="pictures/vivado_6.jpg" alt="vivado_6" style="zoom:80%;" /> <img title="" src="pictures/vivado_6.jpg" alt="vivado_6" style="zoom:80%;" data-align="center">
8. 选择安装位置。点击左上角的安装位置选择器右侧三点弹出目录选择窗口读者应在此处选择恰当的安装位置Vivado程序将会被安装在所选目录下。 8. 选择安装位置。点击左上角的安装位置选择器右侧三点弹出目录选择窗口读者应在此处选择恰当的安装位置Vivado程序将会被安装在所选目录下。
@ -79,90 +89,147 @@ Vivado开发环境提供Windows平台支持该方案的安装过程简单*
当这两个条件不满足时,页面中会有红字提醒(如下图所示),读者应留意查看: 当这两个条件不满足时,页面中会有红字提醒(如下图所示),读者应留意查看:
<img src="pictures/vivado_7.jpg" alt="vivado_7" style="zoom:80%;" /> <img title="" src="pictures/vivado_7.jpg" alt="vivado_7" style="zoom:80%;" data-align="center">
根据提示更换适当的安装目录(下图是选择安装目录适当时的显示): 根据提示更换适当的安装目录(下图是选择安装目录适当时的显示):
<img src="pictures/vivado_8.jpg" alt="vivado_8" style="zoom:80%;" /> <img title="" src="pictures/vivado_8.jpg" alt="vivado_8" style="zoom:80%;" data-align="center">
确认无误后点击next继续下一步安装。 确认无误后点击next继续下一步安装。
9. 检查安装信息无误后点击install。 9. 检查安装信息无误后点击install。
<img src="pictures/vivado_9.jpg" alt="vivado_9" style="zoom:80%;" /> <img title="" src="pictures/vivado_9.jpg" alt="vivado_9" style="zoom:80%;" data-align="center">
10. 等待下载安装完成(**耗时较长,务必保证下载时网络畅通**)。 10. 等待下载安装完成(**耗时较长,务必保证下载时网络畅通**)。
<img src="pictures/vivado_10.jpg" alt="vivado_10" style="zoom:80%;" /> <img title="" src="pictures/vivado_10.jpg" alt="vivado_10" style="zoom:80%;" data-align="center">
安装过程中弹出相关组件的安装窗口时,按照弹窗提示点击安装(根据所选组件不同,确认安装弹窗中的软件名称可能有变,一律点击安装即可)。 安装过程中弹出相关组件的安装窗口时,按照弹窗提示点击安装(根据所选组件不同,确认安装弹窗中的软件名称可能有变,一律点击安装即可)。
<img src="pictures/vivado_12.jpg" alt="vivado_12" style="zoom:80%;" /> <img title="" src="pictures/vivado_12.jpg" alt="vivado_12" style="zoom:80%;" data-align="center" width="326">
11. 添加Vivado证书。读者应自行申请Vivado证书后导入。 11. 添加Vivado证书。读者应自行申请Vivado证书后导入。
<img src="pictures/vivado_13.jpg" alt="vivado_13" style="zoom:80%;" /> <img title="" src="pictures/vivado_13.jpg" alt="vivado_13" style="zoom:80%;" data-align="center">
12. 出现如下的MATLAB选择窗口时直接点击右上角叉号关闭即可本实验中不会用到相关功能。 12. 出现如下的MATLAB选择窗口时直接点击右上角叉号关闭即可本实验中不会用到相关功能。
<img src="pictures/vivado_15.jpg" alt="vivado_15" style="zoom:80%;" /> <img title="" src="pictures/vivado_15.jpg" alt="vivado_15" style="zoom:80%;" data-align="center" width="522">
13. 安装完成并导入证书后,会弹出如下窗口,表明安装成功。点击确认后安装过程结束。 13. 安装完成并导入证书后,会弹出如下窗口,表明安装成功。点击确认后安装过程结束。
<img src="pictures/vivado_16.jpg" alt="vivado_15" style="zoom:80%;" /> <img title="" src="pictures/vivado_16.jpg" alt="vivado_15" style="zoom:80%;" data-align="center">
<a name="subsec_vivadoenvironments_linux"></a>
**方案二Linux平台Ubuntu桌面环境** #### 场景二Linux平台Ubuntu桌面环境
**注意本方案只在具有图形界面支持的Linux桌面环境中适用Ubuntu桌面环境或虚拟机等。若读者在前文中选择在WSL中安装相关实验环境则强烈建议直接参考前文的方案一在Windows系统中安装Windows版本的Vivado不要按照本方案进行安装。** > **注意本方案只在具有图形界面支持的Linux桌面环境中适用Ubuntu桌面环境或虚拟机等。若读者在前文中选择在WSL中安装相关实验环境则强烈建议直接参考前文的[方案一](#subsec_vivadoenvironments_windows)在Windows系统中安装Windows版本的Vivado不要按照本方案进行安装。**
1. 下载安装程序。建议安装2016.2版本的Vivado进入[下载页面](https://china.xilinx.com/support/download/index.html/content/xilinx/zh/downloadNav/vivado-design-tools/archive.html)下拉找到2016.2版本,下载**Linux Web Installer**在弹出的Xilinx账号登录页面注册账号并登录后下载会自动开始。 1. 下载安装程序。建议安装2016.2版本的Vivado进入[下载页面](https://china.xilinx.com/support/download/index.html/content/xilinx/zh/downloadNav/vivado-design-tools/archive.html)下拉找到2016.2版本,下载**Linux Web Installer**在弹出的Xilinx账号登录页面注册账号并登录后下载会自动开始。
<img src="pictures/vivado_18.png" alt="vivado_5" style="zoom:80%;" /> <img title="" src="pictures/vivado_18.png" alt="vivado_5" style="zoom:80%;" data-align="center" width="510">
2. 启动安装程序。**注意安装过程需要保证网络畅通。以及30G以上的磁盘剩余空间**。 2. 启动安装程序。**注意安装过程需要保证网络畅通。以及30G以上的磁盘剩余空间**。
首先打开命令行,运行如下命令切换到安装包所在目录: 首先打开命令行,运行如下命令切换到安装包所在目录:
`$ cd [your.vivado.download.path]` ```bash
$ cd [your.vivado.download.path]
```
以上命令中,[your.vivado.download.path]指的是Vivado安装包的所在目录。接下来要为安装包添加可执行权 限,继续在命令行中执行如下命令: 以上命令中,[your.vivado.download.path]指的是Vivado安装包的所在目录。接下来要为安装包添加可执行权 限,继续在命令行中执行如下命令:
`$ chmod +x Xilinx_Vivado_SDK_2016.2_0605_1_Lin64.bin` ```bash
$ chmod +x Xilinx_Vivado_SDK_2016.2_0605_1_Lin64.bin
```
上述命令执行完毕后,安装程序便具有了可执行权限,可以直接启动运行。执行如下命令: 上述命令执行完毕后,安装程序便具有了可执行权限,可以直接启动运行。执行如下命令:
`$ ./Xilinx_Vivado_SDK_2016.2_0605_1_Lin64.bin` ```bash
$ ./Xilinx_Vivado_SDK_2016.2_0605_1_Lin64.bin
```
启动安装程序。 启动安装程序。
**3\~9步与上文方案一中Windows平台安装的3\~9步相同。** 3. 进入欢迎页面点击next。
<img title="" src="pictures/vivado_2.jpg" alt="vivado_2" style="zoom:80%;" data-align="center">
4. 输入Xilinx账号密码并选中Download and Install Now后点击next。下载Vivado安装包时注册的账号可以在此处使用若无账号则可以点击上方“please create one”进行注册。
<img title="" src="pictures/vivado_3.jpg" alt="vivado_3" style="zoom:80%;" data-align="center">
5. 勾选同意用户协议后点击next。
<img title="" src="pictures/vivado_4.jpg" alt="vivado_4" style="zoom:80%;" data-align="center">
6. 勾选Vivado HL System Edition后点击next。
<img title="" src="pictures/vivado_5.jpg" alt="vivado_5" style="zoom:80%;" data-align="center">
7. 根据需要勾选安装项点击next。
<img title="" src="pictures/vivado_6.jpg" alt="vivado_6" style="zoom:80%;" data-align="center">
8. 选择安装位置。点击左上角的安装位置选择器右侧三点弹出目录选择窗口读者应在此处选择恰当的安装位置Vivado程序将会被安装在所选目录下。
**这里选择安装目录时,需要注意以下两点**
- 当前用户需要有安装目录的写入权限。
- 所选安装位置剩余空间必须足够读者可以查看左下角的Disk Space Required栏此处会显示安装所需空间以及磁盘剩余空间
当这两个条件不满足时,页面中会有红字提醒(如下图所示),读者应留意查看:
<img src="pictures/vivado_7.jpg" title="" alt="vivado_7" data-align="center">
根据提示更换适当的安装目录(下图是选择安装目录适当时的显示):
<img src="pictures/vivado_8.jpg" title="" alt="vivado_8" data-align="center">
确认无误后点击next继续下一步安装。
9. 检查安装信息无误后点击install。
<img src="pictures/vivado_9.jpg" title="" alt="vivado_9" data-align="center">
10. 等待下载安装完成(**耗时较长,务必保证下载时网络畅通**。注意若安装时长时间卡在“generating installed device list”如下图 10. 等待下载安装完成(**耗时较长,务必保证下载时网络畅通**。注意若安装时长时间卡在“generating installed device list”如下图
<img src="pictures/vivado_19.png" alt="vivado_5" style="zoom:80%;" /> <img title="" src="pictures/vivado_19.png" alt="vivado_5" style="zoom:80%;" data-align="center" width="368">
可以先退出安装程序,**退出时仔细阅读弹窗内容(不要删除已经下载的文件缓存,下次运行安装程序时会自动跳过下载,节约读者安装时间)**。之后打开命令行,执行: 可以先退出安装程序,**退出时仔细阅读弹窗内容(不要删除已经下载的文件缓存,下次运行安装程序时会自动跳过下载,节约读者安装时间)**。之后打开命令行,执行:
`$ sudo apt install libncurses5` ```bash
$ sudo apt install libncurses5
```
上述命令会安装缺失的ncurses库。ncurses库安装完成后需再次执行Vivado安装程序重做1~9步。 上述命令会安装缺失的ncurses库。ncurses库安装完成后需再次执行Vivado安装程序重做1~9步。
**接下来的11\~13步与上文方案一中Windows平台安装的11\~13步相同。** 11. 添加Vivado证书。读者应自行申请Vivado证书后导入。
<img title="" src="pictures/vivado_13.jpg" alt="vivado_13" style="zoom:80%;" data-align="center">
12. 出现如下的MATLAB选择窗口时直接点击右上角叉号关闭即可本实验中不会用到相关功能。
<img title="" src="pictures/vivado_15.jpg" alt="vivado_15" style="zoom:80%;" data-align="center" width="547">
13. 安装完成并导入证书后,会弹出如下窗口,表明安装成功。点击确认后安装过程结束。
<img title="" src="pictures/vivado_16.jpg" alt="vivado_15" style="zoom:80%;" data-align="center">
<a name="subsec_src"></a> <a name="subsec_src"></a>
### 6.1.2 fpga-pynq工程源码获取 ### 7.1.2 fpga-pynq工程源码获取
为了方便大家快速获取源码,已将全部源码(包括子模块)打包上传到百度网盘,可以直接下载。 为了方便大家快速获取源码,已将全部源码(包括子模块)打包上传到百度网盘,可以直接下载。
``` > 链接:[fpga-pynq](https://pan.baidu.com/s/1mTCcKG0EiFdxq4C5HTey3w)
链接https://pan.baidu.com/s/1mTCcKG0EiFdxq4C5HTey3w > 提取码1234
提取码1234
```
下载完成后,在命令行中执行如下命令,将源码进行解压。 下载完成后,在命令行中执行如下命令,将源码进行解压。
``` ```bash
$ cd 你的源码下载目录 $ cd 你的源码下载目录
$ cat fpga-pynq.0* > fpga-pynq.tar.gz #组装文件 $ cat fpga-pynq.0* > fpga-pynq.tar.gz #组装文件
$ md5sum fpga-pynq.tar.gz > md5 #计算MD5校验码 $ md5sum fpga-pynq.tar.gz > md5 #计算MD5校验码
@ -172,42 +239,47 @@ $ tar -zxvf fpga-pynq.tar.gz #解压文件
或者从github获取源码 或者从github获取源码
``` ```bash
$ git clone https://github.com/huozf123/fpga-pynq.git $ git clone https://github.com/huozf123/fpga-pynq.git
$ cd fpga-pynq $ cd fpga-pynq
$ git submodule update --init --recursive $ git submodule update --init --recursive
``` ```
**Windows用户注意选择在WSL中完成实验的读者在完成上述步骤后还需要额外注释掉Makefrag文件的第108行在终端中继续输入以下命令** **WindowsWSL用户注意选择在WSL中完成实验的读者在完成上述步骤后还需要额外注释掉Makefrag文件的第108行WSL终端中继续输入以下命令:**
``` ```bash
$ cd fpga-pynq $ cd fpga-pynq
$ sed -i "108s/^/#/" common/Makefrag $ sed -i "108s/^/#/" common/Makefrag
``` ```
<a name="subsec_projectenv"></a> <a name="subsec_projectenv"></a>
### 6.1.3 工程编译环境配置 ### 7.1.3 工程编译环境配置
**注意该步骤中Linuxubuntu桌面环境与WindowsWSL环境下的操作存在一些差异读者应按照自己所用平台选择正确的步骤执行。** **注意该步骤中Linuxubuntu桌面环境与WindowsWSL环境下的操作存在一些差异读者应按照自己所用平台选择正确的步骤执行**
* [WindowsWSL环境](#subsec_projectenv_windows)
* [Linuxubuntu桌面环境](#subsec_projectenv_linux)
<a name="subsec_projectenv_linux"></a>
**Linuxubuntu桌面环境** #### Linuxubuntu桌面环境
1. 安装java jdk首先检查电脑中是否已安装java在中端输入以下命令 1. 安装java jdk首先检查电脑中是否已安装java在中端输入以下命令
``` ```bash
java -version $ java -version
``` ```
2. 若能显示java版本号则表明已安装java可以跳过java的安装。否则继续在终端执行如下命令安装java 1.8 2. 若能显示java版本号则表明已安装java可以跳过java的安装。否则继续在终端执行如下命令安装java 1.8
``` ```bash
sudo apt install openjdk-8-jre-headless $ sudo apt install openjdk-8-jre-headless
``` ```
3. 添加Vivado相关的环境变量在命令行中执行替换掉“你的vivado安装目录” 3. 添加Vivado相关的环境变量在命令行中执行替换掉“你的vivado安装目录”
``` ```bash
$ source 你的vivado安装目录/Vivado/2016.2/settings64.sh $ source 你的vivado安装目录/Vivado/2016.2/settings64.sh
$ source 你的vivado安装目录/SDK/2016.2/settings64.sh $ source 你的vivado安装目录/SDK/2016.2/settings64.sh
``` ```
@ -216,113 +288,129 @@ $ source 你的vivado安装目录/SDK/2016.2/settings64.sh
4. 因为Vivado、SDK存在bug所以需要继续在命令行中执行以下命令替换“你的vivado安装目录” 4. 因为Vivado、SDK存在bug所以需要继续在命令行中执行以下命令替换“你的vivado安装目录”
``` ```bash
$ sudo apt-get install libgoogle-perftools-dev $ sudo apt-get install libgoogle-perftools-dev
$ export SWT_GTK3=0 $ export SWT_GTK3=0
$ sudo sed -i "11,15s/^/#/" 你的vivado安装目录/Vivado/2016.2/.settings64-Vivado.sh #注释该文件第11-15行 $ sudo sed -i "11,15s/^/#/" 你的vivado安装目录/Vivado/2016.2/.settings64-Vivado.sh #注释该文件第11-15行
``` ```
5. 最后初始化子模块(若从网盘中下载源码,则此步无需执行),执行: 5. 最后初始化子模块(若从网盘中下载源码,则此步无需执行),执行:
```
```bash
$ cd $REPO/pynq-z2/ $ cd $REPO/pynq-z2/
$ make init-submodules $ make init-submodules
``` ```
**WindowsWSL环境** <a name="subsec_projectenv_windows"></a>
#### WindowsWSL环境
按照上文中第1、2步说明在WSL中安装java 1.8第3、4步在Windows中无需执行。最后在WSL中按第5步初始化子模块若从网盘中下载源码同样无需执行 1. 安装java jdk首先检查电脑中是否已安装java在中端输入以下命令
```bash
$ java -version
```
2. 若能显示java版本号则表明已安装java可以跳过java的安装。否则继续在终端执行如下命令安装java 1.8
```bash
$ sudo apt install openjdk-8-jre-headless
```
3. 最后初始化子模块(若从网盘中下载源码,则此步无需执行),执行:
```bash
$ cd $REPO/pynq-z2/
$ make init-submodules
```
<a name="subsec_hardware"></a> <a name="subsec_hardware"></a>
### 6.1.4 蓝牙小车硬件组装 ### 7.1.4 蓝牙小车硬件组装
**组装蓝牙小车需要的材料清单** #### 组装蓝牙小车需要的材料清单
pynq-z1开发板、sd卡、蓝牙模块、小车相关硬件电机、电机驱动板、底盘、配套总线、ttl转总线模块、以太网网线、7.4v电池、电池配套电源线、移动电源、usb供电线。 > pynq-z1开发板、sd卡、蓝牙模块、小车相关硬件电机、电机驱动板、底盘、配套总线、ttl转总线模块、以太网网线、7.4v电池、电池配套电源线、移动电源、usb供电线。
**蓝牙小车组装步骤** #### 蓝牙小车组装步骤
1组装小车 1. 组装小车
首先完成小车底盘组装(**若采用已预先组装并连线的小车进行实验,则在该步骤中只需将剩下的四个车轮装上后,即可进行第二步**)。先将小车两个前轮上的电机驱动板用一根总线连接(每个电机驱动板上有两个总线接口,功能完全相同,两个电机各自任选一个总线接口用导线连接即可),另外再准备一根总线,一端插入两个电机中任一个的余下总线接口,另一端从小车底盘上方引出备用。再将两个后轮也按相同方式连接,引出另一根总线。小车底盘全部组装完成后,上方应该有两根引出的总线待连接(一根连接两个前轮,另一根连接两个后轮)。如下图所示: 首先完成小车底盘组装(**若采用已预先组装并连线的小车进行实验,则在该步骤中只需将剩下的四个车轮装上后,即可进行第二步**)。先将小车两个前轮上的电机驱动板用一根总线连接(每个电机驱动板上有两个总线接口,功能完全相同,两个电机各自任选一个总线接口用导线连接即可),另外再准备一根总线,一端插入两个电机中任一个的余下总线接口,另一端从小车底盘上方引出备用。再将两个后轮也按相同方式连接,引出另一根总线。小车底盘全部组装完成后,上方应该有两根引出的总线待连接(一根连接两个前轮,另一根连接两个后轮)。如下图所示:
<img src="pictures/hardware_2.jpg" alt="hardware_2" style="zoom:10%;" /> <img title="" src="pictures/hardware_2.jpg" alt="hardware_2" style="zoom:10%;" data-align="center" width="298">
2将蓝牙模块连接到**PMODA**接口 2. 将蓝牙模块连接到**PMODA**接口
将蓝牙模块的VCC、GND、TXD和RXD管脚连接到pynq-z1的**PMODA**接口,分别对应**PMODA**上面一排从左往右第1、2、3和4个管脚。如下图所示 将蓝牙模块的VCC、GND、TXD和RXD管脚连接到pynq-z1的**PMODA**接口,分别对应**PMODA**上面一排从左往右第1、2、3和4个管脚。如下图所示
<img src="pictures/hardware_1.jpg" alt="hardware_1" style="zoom:10%;" /> <img title="" src="pictures/hardware_1.jpg" alt="hardware_1" style="zoom:10%;" data-align="center" width="362">
3ttl转总线模块连接 3. ttl转总线模块连接
将从小车底盘上引出的两根总线插入到ttl转总线模块上的两个总线接口两个接口不必进行区分。然后用跳线帽将该模块上的**ttl-zx**针脚进行短接以便让模块工作在ttl转总线模式。之后取出与电池配套的电源线接到ttl转总线模块的电源输入上**注意查看电路板背面的正负极标志,红色导线接正极,黑色导线接负极**)。再将电源线上的电池插头与电池连接。 将从小车底盘上引出的两根总线插入到ttl转总线模块上的两个总线接口两个接口不必进行区分。然后用跳线帽将该模块上的**ttl-zx**针脚进行短接以便让模块工作在ttl转总线模式。之后取出与电池配套的电源线接到ttl转总线模块的电源输入上**注意查看电路板背面的正负极标志,红色导线接正极,黑色导线接负极**)。再将电源线上的电池插头与电池连接。
取三根公对母杜邦线下面为了叙述方便将这三根杜邦线分别命名为线1、线2和线3读者可通过杜邦线颜色进行区分将线1、线2和线3的母头分别连接到ttl转总线模块上的**RXD、TXD与GND注意ttl转总线模块上有两排黄色的ttl接口一排为公头一排为母头这里说的这三个接口是指下面一排的公头**。ttl转总线模块连接好后的部分连接如下图所示 取三根公对母杜邦线下面为了叙述方便将这三根杜邦线分别命名为线1、线2和线3读者可通过杜邦线颜色进行区分将线1、线2和线3的母头分别连接到ttl转总线模块上的**RXD、TXD与GND注意ttl转总线模块上有两排黄色的ttl接口一排为公头一排为母头这里说的这三个接口是指下面一排的公头**。ttl转总线模块连接好后的部分连接如下图所示
<img src="pictures/hardware_6.jpg" alt="hardware_6" style="zoom:20%;" /> <img title="" src="pictures/hardware_6.jpg" alt="hardware_6" style="zoom:20%;" data-align="center" width="409">
再将线1、线2和线3的公头连接到pynq-z1的**PMODB**接口,分别对应**PMODB**上面一排**从右往左第3、4、5个管脚**。 再将线1、线2和线3的公头连接到pynq-z1的**PMODB**接口,分别对应**PMODB**上面一排**从右往左第3、4、5个管脚**。
<img src="pictures/hardware_7.jpg" alt="hardware_7" style="zoom:10%;" /> <img title="" src="pictures/hardware_7.jpg" alt="hardware_7" style="zoom:10%;" width="213" data-align="center">
注意在连线过程中三根杜邦线没有发生交叉始终是按照线123或线321的顺序插入到接口中的。因此在连接完成后读者可以通过检查线3的两端是否连接的都是GND一端为ttl转总线上的GND另一端为pynq-z1上的GND来判断接线是否正确。连接完成后如下图 注意在连线过程中三根杜邦线没有发生交叉始终是按照线123或线321的顺序插入到接口中的。因此在连接完成后读者可以通过检查线3的两端是否连接的都是GND一端为ttl转总线上的GND另一端为pynq-z1上的GND来判断接线是否正确。连接完成后如下图
<img src="pictures/hardware_3.jpg" alt="hardware_3" style="zoom:10%;" /> <img title="" src="pictures/hardware_3.jpg" alt="hardware_3" style="zoom:10%;" width="434" data-align="center">
4为pynq-z1供电 4. 为pynq-z1供电
首先应确保pynq-z1开发板处于从USB供电的状态这需要读者使用跳线帽将电路板左下角印有*USB*标志的两个针脚短接。然后将USB供电线一端接入开发板左侧的micro USB接口另一端接入移动电源5V 1A。如下图所示 首先应确保pynq-z1开发板处于从USB供电的状态这需要读者使用跳线帽将电路板左下角印有*USB*标志的两个针脚短接。然后将USB供电线一端接入开发板左侧的micro USB接口另一端接入移动电源5V 1A。如下图所示
<img src="pictures/hardware_4.jpg" alt="hardware_4" style="zoom:80%;" /> <img title="" src="pictures/hardware_4.jpg" alt="hardware_4" style="zoom:80%;" width="406" data-align="center">
5将pynq-z1开发板、ttl转总线模块、电机电池以及开发板供电电源固定在小车底盘上 5. 将pynq-z1开发板、ttl转总线模块、电机电池以及开发板供电电源固定在小车底盘上
<a name="subsec_system"></a> <a name="subsec_system"></a>
### 6.1.5 操作系统的替换修改 ### 7.1.5 操作系统的替换修改
注意,下文中涉及到的指令较多,且需要在不同的地方进行输入。总的来说,**需要输入命令的地方大致有三处本地终端、ssh会话、sftp会话**。读者在操作过程中,务必要留意文中类似于: **“在终端输入”指本地计算机的终端、“在ssh会话中输入”、在“sftp会话中输入”这样的提示**。另外需要在这三处输入的命令可能无法避免的交替出现因此建议读者各打开一个窗口分别用来创建ssh连接与sftp连接创建方式在下文需要用到的ssh与sftp的地方有说明读者可以在遇到后再进行创建并在实验完成前不要关闭这样可以避免反复的进行ssh、sftp连接。若由于重启开发板或者其他原因导致连接断开则按照相关创建步骤的说明重新创建连接即可。 注意,下文中涉及到的指令较多,且需要在不同的地方进行输入。总的来说,**需要输入命令的地方大致有三处本地终端、ssh会话、sftp会话**。读者在操作过程中,务必要留意文中类似于: **“在终端输入”指本地计算机的终端、“在ssh会话中输入”、在“sftp会话中输入”这样的提示**。另外需要在这三处输入的命令可能无法避免的交替出现因此建议读者各打开一个窗口分别用来创建ssh连接与sftp连接创建方式在下文需要用到的ssh与sftp的地方有说明读者可以在遇到后再进行创建并在实验完成前不要关闭这样可以避免反复的进行ssh、sftp连接。若由于重启开发板或者其他原因导致连接断开则按照相关创建步骤的说明重新创建连接即可。
1下载[PYNQ v2.6启动镜像Boot Image](https://xilinx-ax-dl.entitlenow.com/dl/ul/2020/10/22/R210391224/pynq_z1_v2.6.0.zip?hash=LpAIQVeSunXk7KU0smi8Ag&expires=1649065517&filename=pynq_z1_v2.6.0.zip),然后解压得到.img镜像文件。 1. 下载[PYNQ v2.6启动镜像Boot Image](https://xilinx-ax-dl.entitlenow.com/dl/ul/2020/10/22/R210391224/pynq_z1_v2.6.0.zip?hash=LpAIQVeSunXk7KU0smi8Ag&expires=1649065517&filename=pynq_z1_v2.6.0.zip),然后解压得到.img镜像文件。
2把启动镜像写到sd卡中**此步Linuxubuntu桌面环境与WindowsWSL环境下操作不同**
**Linuxubuntu桌面环境** 2. 把启动镜像写到sd卡中**此步Linuxubuntu桌面环境与WindowsWSL环境下操作不同**
* 将sd卡插入读卡器连上电脑。 #### Linuxubuntu桌面环境
* 打开终端输入lsblk命令找到刚刚插入的sd卡设备可通过输出的设备所对应的容量判断记下该设备的名称通常为**sd*** - 将sd卡插入读卡器连上电脑
<img src="pictures/fpga_4.png" alt="fpga_4" style="zoom:80%;" /> - 打开终端输入lsblk命令找到刚刚插入的sd卡设备可通过输出的设备所对应的容量判断记下该设备的名称通常为**sd***)。
* 继续在终端中输入如下命令进行写入,将*镜像文件路径*替换为**第1步**中解压得到的.img镜像文件所在路径将*设备名称*替换为上一步中记录下的设备名称(**该命令具有一定风险会清空指定设备上原有的所有文件不可逆输入该命令时务必仔细检查正确的名称一般为sdb或sdc等后面不带数字且容量与插入的sd卡设备相同**
- 继续在终端中输入如下命令进行写入,将*镜像文件路径*替换为**第1步**中解压得到的.img镜像文件所在路径将*设备名称*替换为上一步中记录下的设备名称(**该命令具有一定风险会清空指定设备上原有的所有文件不可逆输入该命令时务必仔细检查正确的名称一般为sdb或sdc等后面不带数字且容量与插入的sd卡设备相同**
``` ```
sudo dd bs=4M if=镜像文件路径 of=/dev/设备名称 sudo dd bs=4M if=镜像文件路径 of=/dev/设备名称
``` ```
该命令执行耗时较长,且在执行完成前不会有任何输出,请读者耐心等待。 该命令执行耗时较长,且在执行完成前不会有任何输出,请读者耐心等待。
* 拔出sd卡。
**WindowsWSL环境** - 拔出sd卡。
* 在[这里](https://sourceforge.net/projects/win32diskimager/)下载Win32 Disk Imager软件并安装。 #### WindowsWSL环境
* 将sd卡插入读卡器连上电脑 - 在[这里](https://sourceforge.net/projects/win32diskimager/)下载Win32 Disk Imager软件并安装
* 打开Win32 Disk Imager在**映像文件**处选择解压得到的img文件然后在**设备**选择处选择插入的sd卡最后点击**写入**按钮 - 将sd卡插入读卡器连上电脑
<img src="pictures/windows_1.jpg" alt="windows_1" style="zoom:80%;" /> - 打开Win32 Disk Imager在**映像文件**处选择解压得到的img文件然后在**设备**选择处选择插入的sd卡最后点击**写入**按钮。
* 拔出sd卡。 <img title="" src="pictures/windows_1.jpg" alt="windows_1" style="zoom:80%;" data-align="center" width="361">
- 拔出sd卡。
3将sd卡插入到pynq-z1开发板中检查pynq-z1左上角的跳线帽是否短接了靠上面的两个针脚这样做是为了确保pynq-z1处于从sd卡启动的状态。如下图所示 3. 将sd卡插入到pynq-z1开发板中检查pynq-z1左上角的跳线帽是否短接了靠上面的两个针脚这样做是为了确保pynq-z1处于从sd卡启动的状态。如下图所示
<img src="pictures/hardware_5.jpg" alt="hardware_5" style="zoom:80%;" /> <img title="" src="pictures/hardware_5.jpg" alt="hardware_5" style="zoom:80%;" data-align="center" width="451">
打开电源左下方的开关等待系统启动。系统启动耗时较长启动完成后开发板下方的四个蓝色led灯会在数次闪烁后保持长亮此时表明系统已经启动完成。用网线连接电脑和开发板开发板上的网口在左侧 打开电源左下方的开关等待系统启动。系统启动耗时较长启动完成后开发板下方的四个蓝色led灯会在数次闪烁后保持长亮此时表明系统已经启动完成。用网线连接电脑和开发板开发板上的网口在左侧
@ -334,44 +422,50 @@ pynq-z1开发板、sd卡、蓝牙模块、小车相关硬件电机、电机
* 电源开关旁的跳线帽是否松动 * 电源开关旁的跳线帽是否松动
4修改网卡IP地址**此步Linuxubuntu桌面环境与WindowsWSL环境下操作不同** 4. 修改网卡IP地址**此步Linuxubuntu桌面环境与WindowsWSL环境下操作不同**
**Linuxubuntu桌面环境** #### Linuxubuntu桌面环境
打开电脑终端,执行如下命令,需要将*有线网卡名称*替换为电脑上实际的有线网卡名称,可以通过执行*ifconfig*命令查看通常为eth0、enp7s0等。该命令将电脑有线网卡ip地址设置为192.168.2.13。**注意若在所使用电脑的有线网卡中配置了自动连接的PPPOE协议等手动指定的ip地址可能会在一段时间后就被自动覆盖导致实验失败读者应根据自己电脑的具体情况暂时关闭相关设置。** 打开电脑终端,执行如下命令,需要将*有线网卡名称*替换为电脑上实际的有线网卡名称,可以通过执行*ifconfig*命令查看通常为eth0、enp7s0等。该命令将电脑有线网卡ip地址设置为192.168.2.13。**注意若在所使用电脑的有线网卡中配置了自动连接的PPPOE协议等手动指定的ip地址可能会在一段时间后就被自动覆盖导致实验失败读者应根据自己电脑的具体情况暂时关闭相关设置。**
``` ```bash
sudo ifconfig 有线网卡名称 192.168.2.13 sudo ifconfig 有线网卡名称 192.168.2.13
``` ```
**WindowsWSL环境** #### WindowsWSL环境
点击左下角Windows图标输入“控制面板”这时会看到控制面板点击打开控制面板。然后依次点击网络和Internet、网络和共享中心、更改适配器设置在窗口左侧找到以太网右键单击点击“属性”打开以太网属性设置窗口。 点击左下角Windows图标输入“控制面板”这时会看到控制面板点击打开控制面板。然后依次点击网络和Internet、网络和共享中心、更改适配器设置在窗口左侧找到以太网右键单击点击“属性”打开以太网属性设置窗口。
<img src="pictures/windows_2.jpg" alt="windows_2" style="zoom:80%;" /> <img title="" src="pictures/windows_2.jpg" alt="windows_2" style="zoom:80%;" data-align="center" width="243">
找到*Internet协议版本4TCP/IPv4*双击打开。选择“使用下面的IP地址”并将IP地址改为192.168.2.13子网掩码改为255.255.255.0,最后点击确定保存。 找到*Internet协议版本4TCP/IPv4*双击打开。选择“使用下面的IP地址”并将IP地址改为192.168.2.13子网掩码改为255.255.255.0,最后点击确定保存。
<img src="pictures/windows_3.jpg" alt="windows_3" style="zoom:80%;" /> <img title="" src="pictures/windows_3.jpg" alt="windows_3" style="zoom:80%;" data-align="center" width="237">
5下载[libfesvr.so](https://gitee.com/hustos/myriscv-fesvr/blob/modify/build/libfesvr.so)frontend server运行需要的动态链接库、[riscv-fesvr](https://gitee.com/hustos/myriscv-fesvr/blob/modify/build/riscv-fesvr)frontend server 5. 下载[libfesvr.so](https://gitee.com/hustos/myriscv-fesvr/blob/modify/build/libfesvr.so)frontend server运行需要的动态链接库、[riscv-fesvr](https://gitee.com/hustos/myriscv-fesvr/blob/modify/build/riscv-fesvr)frontend server
6创建一个烧写bitstream的shell脚本 6. 创建一个烧写bitstream的shell脚本
由于烧写bitstream的操作在之后的实验中会多次进行读者每次在对硬件进行修改后都需要重新烧写bitstream到pynq-z1开发板中才能生效因此可以将烧写bitstream的命令保存在一个脚本中便于后续使用。用文本编辑器创建一个文本文件命名为program.sh 输入如下内容后保存: 由于烧写bitstream的操作在之后的实验中会多次进行读者每次在对硬件进行修改后都需要重新烧写bitstream到pynq-z1开发板中才能生效因此可以将烧写bitstream的命令保存在一个脚本中便于后续使用。用文本编辑器创建一个文本文件命名为program.sh 输入如下内容后保存:
注意若读者在Windows平台进行实验**千万不要用系统自带的记事本创建此文件!**因为记事本会默认将文件保存成dos格式会导致将脚本传输到Linux系统中执行时报错。Windows用户可以选择**1. 在WSL中创建该文件2. 不要在Windows中创建该文件暂时跳过这一步当后文利用ssh协议连接到开发板后直接在ssh会话中启动vim创建该文件3. 若执意在Windows图形界面中创建该文件请下载[sublime文本编辑器](http://www.sublimetext.com/)进行创建**。 > 注意若读者在Windows平台进行实验**千万不要用系统自带的记事本创建此文件**因为记事本会默认将文件保存成dos格式会导致将脚本传输到Linux系统中执行时报错。Windows用户可以选择
>
> 1. 在WSL中创建该文件
>
> 2. 不要在Windows中创建该文件暂时跳过这一步当后文利用ssh协议连接到开发板后直接在ssh会话中启动vim创建该文件
>
> 3. 若执意在Windows图形界面中创建该文件请下载[sublime文本编辑器](http://www.sublimetext.com/)进行创建。
``` ```bash
#! /bin/sh #! /bin/sh
cp rocketchip_wrapper.bit.bin /lib/firmware/ cp rocketchip_wrapper.bit.bin /lib/firmware/
echo 0 > /sys/class/fpga_manager/fpga0/flags echo 0 > /sys/class/fpga_manager/fpga0/flags
echo rocketchip_wrapper.bit.bin > /sys/class/fpga_manager/fpga0/firmware echo rocketchip_wrapper.bit.bin > /sys/class/fpga_manager/fpga0/firmware
``` ```
7将这三个文件通过SFTP传输到开发板的/home/xilinx目录下需要先将[libfesvr.so所在路径]、[riscv-fesvr所在路径]与[program.sh所在路径]分别替换成前两步中libfesvr.so、riscv-fesvr与program.sh文件所在的路径。**此步Linuxubuntu桌面环境与WindowsWSL环境下操作不同** 7. 将这三个文件通过SFTP传输到开发板的/home/xilinx目录下需要先将[libfesvr.so所在路径]、[riscv-fesvr所在路径]与[program.sh所在路径]分别替换成前两步中libfesvr.so、riscv-fesvr与program.sh文件所在的路径。**此步Linuxubuntu桌面环境与WindowsWSL环境下操作不同**
**Linuxubuntu桌面环境** #### Linuxubuntu桌面环境
创建sftp连接 创建sftp连接
@ -388,72 +482,76 @@ echo rocketchip_wrapper.bit.bin > /sys/class/fpga_manager/fpga0/firmware
put [program.sh所在路径] /home/xilinx put [program.sh所在路径] /home/xilinx
``` ```
**WindowsWSL环境** #### WindowsWSL环境
在Windows下进行fpga实验的读者既可以选择在WSL中执行上述Linux环境下的sftp连接命令从而直接在WSL中完成文件的传输操作也可以选择更加方便的带图形界面的sftp客户端程序来完成文件的传输操作。下面介绍如何使用MobaXterm软件来完成文件传输。
在Windows下进行fpga实验的读者既可以选择在WSL中执行上述Linux环境下的sftp连接命令从而直接在WSL中完成文件的传输操作也可以选择更加方便的带图形界面的sftp客户端程序来完成文件的传输操作。下面介绍如何使用MobaXterm软件来完成文件传输。**注意后文中的其他步骤仍然会用到sftp连接为避免文章篇幅过长Windows环境下图形界面的文件传输操作只会在此处介绍一次读者在后续步骤中碰到需要使用sftp传输文件的操作时可以参考此处的操作方式** > 后文中的其他步骤仍然会用到sftp连接为避免文章篇幅过长Windows环境下图形界面的文件传输操作只会在此处介绍一次读者在后续步骤中碰到需要使用sftp传输文件的操作时可以参考此处的操作方式
以传输文件libfesvr.so为例下面是具体步骤 以传输文件libfesvr.so为例下面是具体步骤
打开MobaXterm点击左上角的“Session”。 打开MobaXterm点击左上角的“Session”。
<img src="pictures/windows_4.jpg" alt="windows_4" style="zoom:80%;" /> <img title="" src="pictures/windows_4.jpg" alt="windows_4" style="zoom:80%;" data-align="center" width="410">
在弹出的窗口中选择“SFTP“然后在Remote host后填入192.168.2.99在Username后填入xilinx最后点击OK。 在弹出的窗口中选择“SFTP“然后在Remote host后填入192.168.2.99在Username后填入xilinx最后点击OK。
<img src="pictures/windows_5.jpg" alt="windows_5" style="zoom:80%;" /> <img title="" src="pictures/windows_5.jpg" alt="windows_5" style="zoom:80%;" data-align="center" width="464">
输入密码xilinx后点击OK。 输入密码xilinx后点击OK。
<img src="pictures/windows_6.jpg" alt="windows_6" style="zoom:80%;" /> <img title="" src="pictures/windows_6.jpg" alt="windows_6" style="zoom:80%;" data-align="center" width="268">
在图中“1”处定位到libfesvr.so所在文件夹然后在图中“2”处找到文件libfesvr.so将该文件用鼠标拖动到图中“3”处等待文件传输完成即可。 在图中“1”处定位到libfesvr.so所在文件夹然后在图中“2”处找到文件libfesvr.so将该文件用鼠标拖动到图中“3”处等待文件传输完成即可。
<img src="pictures/windows_7.jpg" alt="windows_7" style="zoom:80%;" /> <img title="" src="pictures/windows_7.jpg" alt="windows_7" style="zoom:80%;" data-align="center" width="480">
**以同样的方式再将riscv-fesvr文件与program.sh文件传输到pynq-z1开发板中。** **以同样的方式再将riscv-fesvr文件与program.sh文件传输到pynq-z1开发板中。**
8用ssh远程登录192.168.2.99开发板的ip地址用户名密码都为xilinx。**此步Linuxubuntu桌面环境与WindowsWSL环境下操作不同** 8. 用ssh远程登录192.168.2.99开发板的ip地址用户名密码都为xilinx。**此步Linuxubuntu桌面环境与WindowsWSL环境下操作不同**
**Linuxubuntu桌面环境** #### Linuxubuntu桌面环境
在终端中输入如下命令来建立ssh连接 在终端中输入如下命令来建立ssh连接
``` ```bash
ssh xilinx@192.168.2.99 ssh xilinx@192.168.2.99
第一次运行时会提示确认连接信息输入yes后回车 第一次运行时会提示确认连接信息输入yes后回车
密码输入xilinx 密码输入xilinx
``` ```
**WindowsWSL环境** #### WindowsWSL环境
Windows用户既可以选择在WSL中用与上述Linux下相同的方式在终端中建立ssh连接也可以选择用带图形界面的ssh客户端来建立ssh连接。下面介绍如何在Windows环境中使用MobaXterm软件建立ssh连接的步骤。
Windows用户既可以选择在WSL中用与上述Linux下相同的方式在终端中建立ssh连接也可以选择用带图形界面的ssh客户端来建立ssh连接。下面介绍如何在Windows环境中使用MobaXterm软件建立ssh连接的步骤。**注意后文中的其他步骤仍然会用到ssh连接为避免文章篇幅过长Windows环境下图形界面的ssh连接建立过程只会在此处介绍一次读者在后续步骤中碰到需要使用ssh与pynq-z1建立连接的操作时可以参考此处的操作方式** > 后文中的其他步骤仍然会用到ssh连接为避免文章篇幅过长Windows环境下图形界面的ssh连接建立过程只会在此处介绍一次读者在后续步骤中碰到需要使用ssh与pynq-z1建立连接的操作时可以参考此处的操作方式
打开MobaXterm点击左上角的“Session”。 打开MobaXterm点击左上角的“Session”。
<img src="pictures/windows_4.jpg" alt="windows_4" style="zoom:80%;" /> <img title="" src="pictures/windows_4.jpg" alt="windows_4" style="zoom:80%;" data-align="center" width="464">
在弹出的窗口中选择“SSH“然后在Remote host后填入192.168.2.99勾选Specify username并在其后填入xilinx最后点击OK。 在弹出的窗口中选择“SSH“然后在Remote host后填入192.168.2.99勾选Specify username并在其后填入xilinx最后点击OK。
<img src="pictures/windows_8.jpg" alt="windows_8" style="zoom:80%;" /> <img title="" src="pictures/windows_8.jpg" alt="windows_8" style="zoom:80%;" data-align="center" width="450">
输入密码xilinx后回车。 输入密码xilinx后回车。
9在**ssh会话**中,依次执行以下命令: 9. 在**ssh会话**中,依次执行以下命令:
``` ```bash
$ sudo mv libfesvr.so /usr/local/lib/ #密码输入xilinx $ sudo mv libfesvr.so /usr/local/lib/ #密码输入xilinx
$ sudo mkdir -p /lib/firmware $ sudo mkdir -p /lib/firmware
$ chmod +x program.sh $ chmod +x program.sh
$ chmod +x riscv-fesvr $ chmod +x riscv-fesvr
``` ```
10修改完成断开ssh、sftp连接。 10. 修改完成断开ssh、sftp连接。
若是在图形窗口中创建的连接则直接关闭窗口即可若是在Linux终端或WSL中创建的连接则输入“exit”后回车。 若是在图形窗口中创建的连接则直接关闭窗口即可若是在Linux终端或WSL中创建的连接则输入“exit”后回车。
<a name="hardware_lab1"></a> <a name="hardware_lab1"></a>
## 6.2 fpga实验1在Rocket Chip上添加uart接口 ## 7.2 fpga实验1在Rocket Chip上添加uart接口
<a name="lab1_target"></a> <a name="lab1_target"></a>
@ -469,10 +567,10 @@ echo rocketchip_wrapper.bit.bin > /sys/class/fpga_manager/fpga0/firmware
Rocket Chip相关的verilog文件主要是由Chisel自动生成的这些verilog文件难以直接进行修改编辑。为了改变Rocket Chip的行为正确的修改方式是对common/src/main/scala下的文件进行修改并运行rocket chip生成器生成相关的verilog文件再将生成的verilog文件复制到src/verilog工程目录下。其中运行rocket chip生成器以及复制文件的相关操作都已经为读者写入了make file中并不需要手动执行读者只需按照以下步骤修改相关配置文件并按照说明执行编译命令即可。 Rocket Chip相关的verilog文件主要是由Chisel自动生成的这些verilog文件难以直接进行修改编辑。为了改变Rocket Chip的行为正确的修改方式是对common/src/main/scala下的文件进行修改并运行rocket chip生成器生成相关的verilog文件再将生成的verilog文件复制到src/verilog工程目录下。其中运行rocket chip生成器以及复制文件的相关操作都已经为读者写入了make file中并不需要手动执行读者只需按照以下步骤修改相关配置文件并按照说明执行编译命令即可。
**下面是具体的操作步骤**
为了能够在PKE中像访问普通内存一样对新增的uart外设进行访问我们需要对Rocket Chip进行修改为其添加MMIO的支持。然后需要在顶层verilog文件中增加MMIO接口和uart接口其中的MMIO接口用来将Rocket Chip中的MMIO接口与block design中添加的MMIO_s_axi接口进行连接uart接口则用来将在block design中添加的uart接口暴露出去。在完成顶层verilog文件中接口的添加后还需修改xdc约束文件将uart接口与fpga开发板上的物理接口进行绑定便于后续外设的连接。 为了能够在PKE中像访问普通内存一样对新增的uart外设进行访问我们需要对Rocket Chip进行修改为其添加MMIO的支持。然后需要在顶层verilog文件中增加MMIO接口和uart接口其中的MMIO接口用来将Rocket Chip中的MMIO接口与block design中添加的MMIO_s_axi接口进行连接uart接口则用来将在block design中添加的uart接口暴露出去。在完成顶层verilog文件中接口的添加后还需修改xdc约束文件将uart接口与fpga开发板上的物理接口进行绑定便于后续外设的连接。
**步骤一:修改工程文件**
1. 修改Rocket Chip以增加其对MMIOMemory-mapped I/O的支持 1. 修改Rocket Chip以增加其对MMIOMemory-mapped I/O的支持
修改文件`$REPO/common/src/main/scala/Top.scala`。修改后的文件为[Top.scala](https://gitee.com/hustos/fpga-pynq/blob/uart-pynq/common/src/main/scala/Top.scala)修改过的地方均有modified标识。 修改文件`$REPO/common/src/main/scala/Top.scala`。修改后的文件为[Top.scala](https://gitee.com/hustos/fpga-pynq/blob/uart-pynq/common/src/main/scala/Top.scala)修改过的地方均有modified标识。
@ -493,57 +591,55 @@ Rocket Chip相关的verilog文件主要是由Chisel自动生成的这些veril
set_property -dict { PACKAGE_PIN T10 IOSTANDARD LVCMOS33 } [get_ports { uart2_in }]; #IO_L1N_T0_34 Sch=jb_n[2] set_property -dict { PACKAGE_PIN T10 IOSTANDARD LVCMOS33 } [get_ports { uart2_in }]; #IO_L1N_T0_34 Sch=jb_n[2]
``` ```
修改后的文件为[base.xdc](https://gitee.com/hustos/fpga-pynq/blob/uart-pynq/pynq-z2/src/constrs/base.xdc) 修改后的文件为[base.xdc](https://gitee.com/hustos/fpga-pynq/blob/uart-pynq/pynq-z2/src/constrs/base.xdc)。
**在完成上述三处文件修改后编译生成vivado工程**。
4. 生成vivado工程 4. 生成vivado工程
**注意该步骤中Linuxubuntu桌面环境与WindowsWSL环境下的操作存在一些差异读者应按照自己所用平台选择正确的步骤执行。** **注意该步骤中Linuxubuntu桌面环境与WindowsWSL环境下的操作存在一些差异读者应按照自己所用平台选择正确的步骤执行。**
**Linuxubuntu桌面环境** #### Linuxubuntu桌面环境
在命令行中执行如下命令生成并打开vivado工程 在命令行中执行如下命令生成并打开vivado工程
``` ```bash
$ cd $REPO/pynq-z2/ $ cd $REPO/pynq-z2/
$ make project $ make project
$ make vivado $ make vivado
``` ```
**WindowsWSL环境** #### WindowsWSL环境
在WSL命令行中执行如下命令 在WSL命令行中执行如下命令
``` ```bash
$ cd $REPO/pynq-z2/ $ cd $REPO/pynq-z2/
$ make project $ make project
``` ```
在Windows中**第一次**生成工程时还需要进行以下操作后续在修改Rocket Chip后重新生成时只需要执行上述两条命令即可 在Windows中**第一次**生成工程时还需要进行以下操作后续在修改Rocket Chip后重新生成时只需要执行上述两条命令即可
按快捷键win+r打开运行窗口输入cmd后按回车键打开命令提示符窗口。在命令提示符窗口中执行如下命令设置vivado环境变量替换掉“你的vivado安装目录”): 按快捷键win+r打开运行窗口输入cmd后按回车键打开命令提示符窗口。在命令提示符窗口中执行如下命令设置vivado环境变量替换掉“vivado安装路径”):
``` ```
> 你的vivado安装路径\Vivado\2016.2\settings64.bat > [vivado安装路径]\Vivado\2016.2\settings64.bat
``` ```
然后切换盘符到源码所在盘比如若源码保存在d盘则在命令提示符窗口中输入`d:`,切换完成后继续在命令提示符窗口中输入以下命令(替换掉“你的源码所在目录”): 然后切换盘符到源码所在盘比如若源码保存在d盘则在命令提示符窗口中输入`d:`,切换完成后继续在命令提示符窗口中输入以下命令(替换掉“源码路径”):
``` ```
> cd 你的源码所在目录\fpga-zynq\pynq-z2\ > cd [源码路径]\fpga-zynq\pynq-z2\
> vivado -mode tcl -source src/tcl/pynq_rocketchip_ZynqFPGAConfig.tcl > vivado -mode tcl -source src/tcl/pynq_rocketchip_ZynqFPGAConfig.tcl
``` ```
双击桌面图标打开Windows中安装好的vivado点击Open Project找到`你的源码所在目录\fpga-zynq\pynq-z2\pynq_rocketchip_ZynqFPGAConfig\pynq_rocketchip_ZynqFPGAConfig.xpr`双击打开。 双击桌面图标打开Windows中安装好的vivado点击Open Project找到`[源码路径]\fpga-zynq\pynq-z2\pynq_rocketchip_ZynqFPGAConfig\pynq_rocketchip_ZynqFPGAConfig.xpr`双击打开。
**然后我们需要在block design中添加uart相关的接口和IP核并完成地址分配。** **步骤二:在block design中添加uart相关的接口和IP核并完成地址分配。**
5. 修改block design 1. 修改block design
1打开vivado工程后在*Sourses*面板点击*rocketchip_wrapper*左边的加号,然后再双击*system_i*打开block design面板。在最左边找到*S_AXI*端口,点击选中,然后再按*ctrl+c*、*ctrl+v*,这样就复制出一个同样的端口*S_AXI1*,点击选中这个*S_AXI1*,找到左边*External Interface Properties*面板,把*Name*改为*MMIO_S_AXI**Clock Port*选择*ext_clk_in*;选中最左侧的*ext_clk_in*端口,找到左边*External Port Properties*面板,将*FrequencyMhz*修改为*50* 1打开vivado工程后在*Sourses*面板点击*rocketchip_wrapper*左边的加号,然后再双击*system_i*打开block design面板。在最左边找到*S_AXI*端口,点击选中,然后再按*ctrl+c*、*ctrl+v*,这样就复制出一个同样的端口*S_AXI1*,点击选中这个*S_AXI1*,找到左边*External Interface Properties*面板,把*Name*改为*MMIO_S_AXI**Clock Port*选择*ext_clk_in*;选中最左侧的*ext_clk_in*端口,找到左边*External Port Properties*面板将FrequencyMhz修改为50。
<img src="pictures/fpga_1.png" alt="fpga_1" style="zoom:80%;" /> <img title="" src="pictures/fpga_1.png" alt="fpga_1" style="zoom:80%;" data-align="center">
2在右侧电路图面板空白处点击右键再点击*Add IP...*然后输入axi interconnect然后双击出现的搜索结果把axi interconnect放入电路图中然后双击新出现的*axi_interconnect_2*,将*Number of Master Interface*改为2然后点击*OK*。 2在右侧电路图面板空白处点击右键再点击*Add IP...*然后输入axi interconnect然后双击出现的搜索结果把axi interconnect放入电路图中然后双击新出现的*axi_interconnect_2*,将*Number of Master Interface*改为2然后点击*OK*。
@ -561,37 +657,41 @@ Rocket Chip相关的verilog文件主要是由Chisel自动生成的这些veril
7按照如图方式接好电路图(注意uart_lite_0右侧的UART端口右边有个加号需要点一下加号展开再进行连线)。 7按照如图方式接好电路图(注意uart_lite_0右侧的UART端口右边有个加号需要点一下加号展开再进行连线)。
<img src="pictures/fpga_2.png" alt="fpga_2" style="zoom:80%;" /> <img title="" src="pictures/fpga_2.png" alt="fpga_2" style="zoom:80%;" data-align="center">
8点击上面的*Address Editor*,然后在未映射的单元上右键单击, 并在弹出的菜单中点击*assign address*。 8点击上面的*Address Editor*,然后在未映射的单元上右键单击, 并在弹出的菜单中点击*assign address*。
<img src="pictures/fpga_11.jpg" alt="fpga_11" style="zoom:80%;" /> <img title="" src="pictures/fpga_11.jpg" alt="fpga_11" style="zoom:80%;" data-align="center">
按照如图所示配置地址建议先修改Range属性再修改Offset Address属性否则Offset Address可能修改不成功该操作将蓝牙模块的地址映射到了0x6000_0000将电机驱动的地址映射到了0x6000_1000。 按照如图所示配置地址建议先修改Range属性再修改Offset Address属性否则Offset Address可能修改不成功该操作将蓝牙模块的地址映射到了0x6000_0000将电机驱动的地址映射到了0x6000_1000。
<img src="pictures/fpga_3.png" alt="fpga_3" style="zoom:80%;" /> <img title="" src="pictures/fpga_3.png" alt="fpga_3" style="zoom:80%;" data-align="center">
2. 保存所有修改后的文件(也可以选择直接点击下一步中的*Generate Bitstream*vivado会对未保存内容弹窗提醒用户保存读者应留意相关弹窗内容
6. 保存所有修改后的文件(也可以选择直接点击下一步中的*Generate Bitstream*vivado会对未保存内容弹窗提醒用户保存读者应留意相关弹窗内容 **步骤三bitstream文件的生成以及打包。**
**上述修改全部完成后便可以进行bitstream文件的生成以及打包的工作。打包得到的rocketchip_wrapper.bit.bin文件最终是需要烧录到pynq-z1开发板中来实现相关硬件功能的。** > 打包得到的rocketchip_wrapper.bit.bin文件最终是需要烧录到pynq-z1开发板中来实现相关硬件功能的。
7. 生成bitstream文件 1. 生成bitstream文件
最后,点击左下角的*Generate Bitstream*生成比特流文件。(该步骤耗时较长) 最后,点击左下角的*Generate Bitstream*生成比特流文件。(该步骤耗时较长)
当弹出如下对话框时点击yes 当弹出如下对话框时点击yes
<img src="pictures/fpga_9.png" alt="fpga_9" style="zoom:80%;" /> <img title="" src="pictures/fpga_9.png" alt="fpga_9" style="zoom:80%;" data-align="center" width="415">
当弹出如下对话框时表明bitstream生成完成**点击cancel** 当弹出如下对话框时表明bitstream生成完成**点击cancel**
<img src="pictures/fpga_10.png" alt="fpga_10" style="zoom:80%;" /> <img title="" src="pictures/fpga_10.png" alt="fpga_10" style="zoom:80%;" data-align="center" width="275">
<a name="build_bit"></a>
8. 打包bitstream文件为bin文件 2. 打包bitstream文件为bin文件
**注意该步骤中Linuxubuntu桌面环境与WindowsWSL环境下的操作存在一些差异读者应按照自己所用平台选择正确的步骤执行。** **注意该步骤中Linuxubuntu桌面环境与WindowsWSL环境下的操作存在一些差异读者应按照自己所用平台选择正确的步骤执行。**
**Linuxubuntu桌面环境** #### Linuxubuntu桌面环境
1创建Full_Bitstream.bif文件 1创建Full_Bitstream.bif文件
@ -610,12 +710,12 @@ Rocket Chip相关的verilog文件主要是由Chisel自动生成的这些veril
在终端执行以下命令生成bin文件 在终端执行以下命令生成bin文件
``` ```bash
$ cd $REPO/pynq-z2/pynq_rocketchip_ZynqFPGAConfig/pynq_rocketchip_ZynqFPGAConfig.runs/impl_1 $ cd $REPO/pynq-z2/pynq_rocketchip_ZynqFPGAConfig/pynq_rocketchip_ZynqFPGAConfig.runs/impl_1
$ bootgen -image Full_Bitstream.bif -arch zynq -process_bitstream bin -w on $ bootgen -image Full_Bitstream.bif -arch zynq -process_bitstream bin -w on
``` ```
**WindowsWSL环境** #### WindowsWSL环境
1创建Full_Bitstream.bif文件 1创建Full_Bitstream.bif文件
@ -636,15 +736,17 @@ Rocket Chip相关的verilog文件主要是由Chisel自动生成的这些veril
> bootgen -image Full_Bitstream.bif -arch zynq -process_bitstream bin -w on > bootgen -image Full_Bitstream.bif -arch zynq -process_bitstream bin -w on
``` ```
9. 硬件组装 3. 硬件组装
按照前文7.1.4节的说明完成蓝牙小车的组装。
按照前文6.1.4节的说明完成蓝牙小车的组装。 4. 操作系统的替换修改
10. 操作系统的替换修改 按照前文7.1.5节的说明完成操作系统的替换修改。
按照前文6.1.5节的说明完成操作系统的替换修改。 <a name="write_bitstream"></a>
11. 烧写bitstream 5. 烧写bitstream
将`$REPO/pynq-z2/pynq_rocketchip_ZynqFPGAConfig/pynq_rocketchip_ZynqFPGAConfig.runs/impl_1`下的rocketchip_wrapper.bit.bin文件传输到pynq-z1开发板中后执行烧录脚本即可具体方法如下 将`$REPO/pynq-z2/pynq_rocketchip_ZynqFPGAConfig/pynq_rocketchip_ZynqFPGAConfig.runs/impl_1`下的rocketchip_wrapper.bit.bin文件传输到pynq-z1开发板中后执行烧录脚本即可具体方法如下
@ -652,13 +754,13 @@ Rocket Chip相关的verilog文件主要是由Chisel自动生成的这些veril
若使用sftp协议在命令行中传输则在**sftp会话**中输入如下命令: 若使用sftp协议在命令行中传输则在**sftp会话**中输入如下命令:
``` ```bash
put [rocketchip_wrapper.bit.bin所在路径] /home/xilinx put [rocketchip_wrapper.bit.bin所在路径] /home/xilinx
``` ```
Windows图形界面下的传输方法不再赘述。 Windows图形界面下的传输方法不再赘述。
2 在**ssh会话**中,执行以下命令: 2 在**ssh会话**中,执行以下命令:
``` ```
$ sudo ./program.sh #密码输入xilinx $ sudo ./program.sh #密码输入xilinx
@ -668,11 +770,11 @@ Rocket Chip相关的verilog文件主要是由Chisel自动生成的这些veril
#### 实验结果 #### 实验结果
在完成本实验后,一个基础的蓝牙小车所需要的硬件环境就搭建完成了。接下来,读者应该前往第章完成**lab4_1_POLL**实验,在完成**lab4_1_POLL**并将riscv_pke与小车控制程序发送到pynq-z1上执行后小车便可以通过蓝牙app进行控制。 在完成本实验后,一个基础的蓝牙小车所需要的硬件环境就搭建完成了。接下来,读者应该前往第章完成**lab4_1_POLL**实验,在完成**lab4_1_POLL**并将riscv_pke与小车控制程序发送到pynq-z1上执行后小车便可以通过蓝牙app进行控制。
<a name="hardware_lab2"></a> <a name="hardware_lab2"></a>
## 6.3 fpga实验2以中断方式实现uart通信 ## 7.3 fpga实验2以中断方式实现uart通信
**该实验以fpga实验1的修改为基础读者可以先将fpga实验1中最后生成的`rocketchip_wrapper.bit.bin`文件复制到其他目录保存,然后直接在同一个项目文件夹下继续本实验**。 **该实验以fpga实验1的修改为基础读者可以先将fpga实验1中最后生成的`rocketchip_wrapper.bit.bin`文件复制到其他目录保存,然后直接在同一个项目文件夹下继续本实验**。
@ -690,9 +792,7 @@ Rocket Chip相关的verilog文件主要是由Chisel自动生成的这些veril
要想在Rocket Chip上实现对uart外设的中断通信支持一方面需要通过修改Rocket Chip的结构来为Rocket Chip添加中断引脚另一方面需要修改block design将uart产生的中断信号线连接到Rocket Chip的中断引脚。 要想在Rocket Chip上实现对uart外设的中断通信支持一方面需要通过修改Rocket Chip的结构来为Rocket Chip添加中断引脚另一方面需要修改block design将uart产生的中断信号线连接到Rocket Chip的中断引脚。
**下面是具体的操作步骤:** **步骤一修改Rocket Chip结构为其添加外部中断接口**
**首先我们需要修改Rocket Chip结构为其添加外部中断接口。**
1. 修改Rocket Chip结构暴露外部中断接口 1. 修改Rocket Chip结构暴露外部中断接口
@ -724,22 +824,23 @@ Rocket Chip相关的verilog文件主要是由Chisel自动生成的这些veril
这是修改好的[rocketchip_wrapper.v](https://gitee.com/hustos/fpga-pynq/blob/uart-interrupt-pynq/common/rocketchip_wrapper.v)文件。 这是修改好的[rocketchip_wrapper.v](https://gitee.com/hustos/fpga-pynq/blob/uart-interrupt-pynq/common/rocketchip_wrapper.v)文件。
**上述文件修改完成后需要删除之前编译工程时产生的临时文件并重新编译vivado工程。只有执行该步操作才能将上述修改引入到vivado工程中去。**
3. 重新生成vivado工程 3. 重新生成vivado工程
在Linux终端或WSL中执行如下命令重新生成vivado工程。命令执行完成后会自动在vivado中打开项目。 **上述文件修改完成后需要删除之前编译工程时产生的临时文件并重新编译vivado工程。只有执行该步操作才能将上述修改引入到vivado工程中去。**
在Linux终端或WSL中执行如下命令重新生成vivado工程。在ubuntu桌面环境中命令执行完成后会自动在vivado中打开项目windows环境下则需要找到工程文件双击打开。
``` ```
$ cd $REPO/pynq-z2 $ cd $REPO/pynq-z2
$ rm -f src/verilog/Top.ZynqFPGAConfig.v $ rm -f src/verilog/Top.ZynqFPGAConfig.v
$ rm -f ../common/build/* $ rm -f ../common/build/*
$ make project $ make project
$ make vivado
``` ```
**然后要将block design中的两个axi uartlite IP核的中断信号引出** **步骤二:将block design中的两个axi uartlite IP核的中断信号引出**
4. 修改block design 1. 修改block design
1打开vivado工程后在*Sourses*面板点击*rocketchip_wrapper*左边的加号,然后再双击*system_i*打开block design面板。在右侧电路图面板空白处点击右键再点击Add IP...然后输入concat然后双击出现的搜索结果把concat放入电路图中然后双击新出现的xlconcat_0模块将Number of Ports设置为2In0 Width设置为1In1 Width设置为1然后点击OK。该concat IP核的作用是将axi_uartlite_0与axi_uartlite_1两个模块产生的一位中断信号进行拼接合并成一路位宽为2的中断信号该信号会被连接到上文中为Rocket Chip添加的位宽为2的中断输入端口。 1打开vivado工程后在*Sourses*面板点击*rocketchip_wrapper*左边的加号,然后再双击*system_i*打开block design面板。在右侧电路图面板空白处点击右键再点击Add IP...然后输入concat然后双击出现的搜索结果把concat放入电路图中然后双击新出现的xlconcat_0模块将Number of Ports设置为2In0 Width设置为1In1 Width设置为1然后点击OK。该concat IP核的作用是将axi_uartlite_0与axi_uartlite_1两个模块产生的一位中断信号进行拼接合并成一路位宽为2的中断信号该信号会被连接到上文中为Rocket Chip添加的位宽为2的中断输入端口。
@ -747,41 +848,41 @@ Rocket Chip相关的verilog文件主要是由Chisel自动生成的这些veril
3按如图方式连线 3按如图方式连线
<img src="pictures/fpga_5.png" alt="fpga_5" style="zoom:80%;" /> <img title="" src="pictures/fpga_5.png" alt="fpga_5" style="zoom:80%;" data-align="center">
5. 保存所有修改后的文件(也可以选择直接点击下一步中的*Generate Bitstream*vivado会对未保存内容弹窗提醒用户保存读者应留意相关弹窗内容 2. 保存所有修改后的文件(也可以选择直接点击下一步中的*Generate Bitstream*vivado会对未保存内容弹窗提醒用户保存读者应留意相关弹窗内容
**完成所有的修改后,需要重新进行bitstream文件的生成以及打包的工作然后将新生成的rocketchip_wrapper.bit.bin烧录到pynq-z1开发板中。** **步骤三:重新进行bitstream文件的生成以及打包的工作**
6. 生成bitstream文件 1. 生成bitstream文件
最后,点击左下角的*Generate Bitstream*生成比特流文件。(该步骤耗时较长) 最后,点击左下角的*Generate Bitstream*生成比特流文件。(该步骤耗时较长)
7. 打包bitstream文件为bin文件 2. 打包bitstream文件为bin文件
见上文**fpga实验1 步骤8**。若读者选择在与fpga实验1相同的项目文件夹下继续完成本次实验则Full_Bitstream.bif文件应该已经存在那么可以跳过**fpga实验1 步骤8**中**创建Full_Bitstream.bif文件**的步骤。 见上文[fpga实验1 步骤三-2](#build_bit)。若读者选择在与fpga实验1相同的项目文件夹下继续完成本次实验则Full_Bitstream.bif文件应该已经存在那么可以跳过**fpga实验1 步骤三-2**中创建Full_Bitstream.bif文件的步骤。
8. 硬件连接 3. 硬件连接
本次实验的硬件连接与**fpga实验1**完全相同,读者同样需要按照6.1.4节的说明完成蓝牙小车的硬件组装。 本次实验的硬件连接与**fpga实验1**完全相同,读者同样需要按照7.1.4节的说明完成蓝牙小车的硬件组装。
9. 操作系统的替换修改 4. 操作系统的替换修改
若在进行本次实验时pynq-z1开发板中插入的sd卡与**fpga实验1**中相同,且没有删除过**fpga实验1**中创建的相关文件,则该步骤可以直接跳过。否则,读者应参考6.1.5节中的说明重新为sd卡写入操作系统并完成相关文件的替换和修改。 若在进行本次实验时pynq-z1开发板中插入的sd卡与**fpga实验1**中相同,且没有删除过**fpga实验1**中创建的相关文件,则该步骤可以直接跳过。否则,读者应参考7.1.5节中的说明重新为sd卡写入操作系统并完成相关文件的替换和修改。
10. 烧写bitstream 5. 烧写bitstream
见上文**fpga实验1 步骤11** 见上文[fpga实验1 步骤三-5](#write_bitstream)
<a name="lab2_result"></a> <a name="lab2_result"></a>
#### 实验结果 #### 实验结果
在完成本实验后当蓝牙模块在收到蓝牙串口app发送的指令时会产生一个外部中断该中断能够在后续的PKE实验中被操作系统所捕获并做出相应的反应如唤醒一个处于阻塞状态的进程。读者在完成本次fpga实验后应该前往第章完成PKE实验**lab4_2_PLIC**,该实验会通过中断的方式来通过外设向蓝牙小车发送控制指令。 在完成本实验后当蓝牙模块在收到蓝牙串口app发送的指令时会产生一个外部中断该中断能够在后续的PKE实验中被操作系统所捕获并做出相应的反应如唤醒一个处于阻塞状态的进程。读者在完成本次fpga实验后应该前往第章完成PKE实验**lab4_2_PLIC**,该实验会通过中断的方式来通过外设向蓝牙小车发送控制指令。
<a name="hardware_lab3"></a> <a name="hardware_lab3"></a>
## 6.4 fpga实验3配置连接到PS端的USB设备 ## 7.4 fpga实验3配置连接到PS端的USB设备
**该实验以fpga实验2的修改为基础读者可以先将fpga实验2中最后生成的`rocketchip_wrapper.bit.bin`文件复制到其他目录保存,然后直接在同一个项目文件夹下开始本实验**。 **该实验以fpga实验2的修改为基础读者可以先将fpga实验2中最后生成的`rocketchip_wrapper.bit.bin`文件复制到其他目录保存,然后直接在同一个项目文件夹下开始本实验**。
@ -789,9 +890,9 @@ Rocket Chip相关的verilog文件主要是由Chisel自动生成的这些veril
#### 实验目的 #### 实验目的
读者在完成**fpga实验1**与**fpga实验2**以及第七章中对应的PKE实验**lab4_1 POLL**和**lab4_2_PLIC**后便能得到一个可以通过蓝牙串口助手进行控制的蓝牙小车。该蓝牙小车通过uart接口获取来自手机或其他蓝牙设备的控制指令并根据收到的不同指令选择做出前进、后退、左转和右转动之一。在这个控制过程中接收控制信号的蓝牙模块和发出控制指令的电机驱动模块都是连接在PL端的设备所谓PL端即为pynq开发板上搭载的一块FPGA可编程芯片在fpga实验中生成的比特流文件都需要烧录到PL端使其能够支持riscv-pke的运行在第7章会有关于PL端与PS端更详细的介绍。连接在PL端的设备通常功能比较简单可以由PKE直接进行访问控制。而其他一些功能更加复杂的外设例如USB摄像头则被连接到PS端。运行在PL端的PKE在访问PS端的USB设备时需要通过HTIF协议来调用ARM端的系统调用函数利用功能更加完整的PS端操作系统对其进行访问控制。 读者在完成**fpga实验1**与**fpga实验2**以及第七章中对应的PKE实验**lab4_1 POLL**和**lab4_2_PLIC**后便能得到一个可以通过蓝牙串口助手进行控制的蓝牙小车。该蓝牙小车通过uart接口获取来自手机或其他蓝牙设备的控制指令并根据收到的不同指令选择做出前进、后退、左转和右转动之一。在这个控制过程中接收控制信号的蓝牙模块和发出控制指令的电机驱动模块都是连接在PL端的设备所谓PL端即为pynq开发板上搭载的一块FPGA可编程芯片在fpga实验中生成的比特流文件都需要烧录到PL端使其能够支持riscv-pke的运行在第章会有关于PL端与PS端更详细的介绍。连接在PL端的设备通常功能比较简单可以由PKE直接进行访问控制。而其他一些功能更加复杂的外设例如USB摄像头则被连接到PS端。运行在PL端的PKE在访问PS端的USB设备时需要通过HTIF协议来调用ARM端的系统调用函数利用功能更加完整的PS端操作系统对其进行访问控制。
本实验的目的是对连接到PS端的USB设备进行一些配置便于USB摄像头的连接。在对应的第章**lab4_3_hostdevice**中我们要为蓝牙小车连接一个USB摄像头让小车根据摄像头拍摄的画面识别前方出现的障碍物并做出自动避障动作。 本实验的目的是对连接到PS端的USB设备进行一些配置便于USB摄像头的连接。在对应的第章**lab4_3_hostdevice**中我们要为蓝牙小车连接一个USB摄像头让小车根据摄像头拍摄的画面识别前方出现的障碍物并做出自动避障动作。
<a name="lab3_content"></a> <a name="lab3_content"></a>
@ -809,15 +910,15 @@ zynq的PS端外设都被封装成了一个ZYNQ7 Processing System IP核因此
打开vivado工程后在*Sourses*面板点击*rocketchip_wrapper*左边的加号,然后再双击*system_i*打开block design面板。双击processing_system7_0进入PS端ARM核的配置界面如下图。 打开vivado工程后在*Sourses*面板点击*rocketchip_wrapper*左边的加号,然后再双击*system_i*打开block design面板。双击processing_system7_0进入PS端ARM核的配置界面如下图。
<img src="pictures/fpga_6.png" alt="fpga_6" style="zoom:80%;" /> <img title="" src="pictures/fpga_6.png" alt="fpga_6" style="zoom:80%;" data-align="center">
<img src="pictures/fpga_7.png" alt="fpga_7" style="zoom:80%;" /> <img title="" src="pictures/fpga_7.png" alt="fpga_7" style="zoom:80%;" data-align="center">
点击该窗口左侧的*MIO Configuration*之后我们可以在右侧的界面中将需要使用的PS端外设与复用IO相连并配置MIO的电平标准。 点击该窗口左侧的*MIO Configuration*之后我们可以在右侧的界面中将需要使用的PS端外设与复用IO相连并配置MIO的电平标准。
按如下图示点击首先确保USB 0前的复选框处于勾选状态然后将USB的12个管脚的speed属性由slow改为fast修改完成后点击OK保存并关闭窗口。 按如下图示点击首先确保USB 0前的复选框处于勾选状态然后将USB的12个管脚的speed属性由slow改为fast修改完成后点击OK保存并关闭窗口。
<img src="pictures/fpga_8.png" alt="fpga_8" style="zoom:80%;" /> <img title="" src="pictures/fpga_8.png" alt="fpga_8" style="zoom:80%;" data-align="center">
2. 生成bitstream文件 2. 生成bitstream文件
@ -825,24 +926,22 @@ zynq的PS端外设都被封装成了一个ZYNQ7 Processing System IP核因此
3. 打包bitstream文件为bin文件 3. 打包bitstream文件为bin文件
见上文**fpga实验1 步骤8** 见上文[fpga实验1 步骤三-2](#build_bit)
4. 硬件连接 4. 硬件连接
在完成6.1.4节中硬件连接的基础之上还需要将USB摄像头连接到pynq-z1开发板左上角的USB接口并将USB摄像头固定在小车前方指前进方向固定时建议将摄像头略微向上倾斜。另外需要注意的是为了与第章中**lab4_3_hostdevice**的代码相匹配这里连接的摄像头必须支持YUYV格式的画面输出。 在完成7.1.4节中硬件连接的基础之上还需要将USB摄像头连接到pynq-z1开发板左上角的USB接口并将USB摄像头固定在小车前方指前进方向固定时建议将摄像头略微向上倾斜。另外需要注意的是为了与第章中**lab4_3_hostdevice**的代码相匹配这里连接的摄像头必须支持YUYV格式的画面输出。
5. 操作系统的替换修改 5. 操作系统的替换修改
若在进行本次实验时pynq-z1开发板中插入的sd卡与**fpga实验1**中相同,且没有删除过**fpga实验1**中创建的相关文件则该步骤可以直接跳过。否则读者应参考6.1.5节中的说明重新为sd卡写入操作系统并完成相关文件的替换和修改。 若在进行本次实验时pynq-z1开发板中插入的sd卡与**fpga实验1**中相同,且没有删除过**fpga实验1**中创建的相关文件则该步骤可以直接跳过。否则读者应参考7.1.5节中的说明重新为sd卡写入操作系统并完成相关文件的替换和修改。
6. 烧写bitstream 6. 烧写bitstream
见上文**fpga实验1 步骤11**。 见上文[fpga实验1 步骤三-5](#write_bitstream)。
<a name="lab3_result"></a> <a name="lab3_result"></a>
#### 实验结果 #### 实验结果
在完成本次实验后USB摄像头便可以正确的连接到开发板的PS端。读者接下来应该前往第章继续完成**lab4_3_hostdevice**,在完成**lab4_3_hostdevice**中PKE相关代码的修改后连接到PS端的USB外设便可以被运行在PL端的PKE调用从而在此基础上进一步为蓝牙小车增添自动避障的功能。 在完成本次实验后USB摄像头便可以正确的连接到开发板的PS端。读者接下来应该前往第章继续完成**lab4_3_hostdevice**,在完成**lab4_3_hostdevice**中PKE相关代码的修改后连接到PS端的USB外设便可以被运行在PL端的PKE调用从而在此基础上进一步为蓝牙小车增添自动避障的功能。

@ -1,8 +1,8 @@
# 第八章实验5设备管理基于[RISCV-on-PYNQ](https://gitee.com/hustos/fpga-pynq) # 第八章实验5设备管理基于[RISCV-on-PYNQ](https://gitee.com/hustos/fpga-pynq)
### 目录 ### 目录
- [8.1 实验4的基础知识](#fundamental)
- [8.1 实验5的基础知识](#fundamental)
- [8.1.1 pynq开发板介绍](#subsec_pynq) - [8.1.1 pynq开发板介绍](#subsec_pynq)
- [8.1.2 内存映射I/O(MMIO)](#subsec_MMIO) - [8.1.2 内存映射I/O(MMIO)](#subsec_MMIO)
- [8.1.3 riscv-fesvr原理](#subsec_fesvr) - [8.1.3 riscv-fesvr原理](#subsec_fesvr)
@ -10,116 +10,128 @@
- [8.1.5 中断驱动I/O控制方式](#subsec_plic) - [8.1.5 中断驱动I/O控制方式](#subsec_plic)
- [8.1.6 设备文件](#subsec_file) - [8.1.6 设备文件](#subsec_file)
- [8.2 lab5_1 POLL](#polling) - [8.2 lab5_1 POLL](#polling)
- [给定应用](#lab4_1_app) - [给定应用](#lab5_1_app)
- [实验内容](#lab4_1_content) - [实验内容](#lab5_1_content)
- [实验指导](#lab4_1_guide) - [实验指导](#lab5_1_guide)
- [8.3 lab5_2_PLIC](#PLIC) - [答案解析](#lab5_1_ans)
- [给定应用](#lab4_2_app) - [8.3 lab5_2 PLIC](#PLIC)
- [实验内容](#lab4_2_content) - [给定应用](#lab5_2_app)
- [实验指导](#lab4_2_guide) - [实验内容](#lab5_2_content)
- [8.4 lab5_3_hostdevice](#hostdevice) - [实验指导](#lab5_2_guide)
- [给定应用](#lab4_3_app) - [答案解析](#lab5_2_ans)
- [实验内容](#lab4_3_content) - [8.4 lab5_3 hostdevice](#hostdevice)
- [实验指导](#lab4_3_guide) - [给定应用](#lab5_3_app)
- [实验内容](#lab5_3_content)
- [实验指导](#lab5_3_guide)
- [答案解析](#lab5_3_ans)
<a name="fundamental"></a> <a name="fundamental"></a>
## 7.1 实验5的基础知识 ## 8.1 实验5的基础知识
完成前面所有实验后PKE内核的整体功能已经得到完善。在实验四的设备实验中我们将结合fpga-pynq板在rocket chip上增加uart模块和蓝牙模块并搭载PKE内核实现蓝牙通信控制智能小车设计设备管理的相关实验。 完成前面所有实验后PKE内核的整体功能已经得到完善。在实验五的设备实验中我们将结合fpga-pynq板在Rocket chip上增加uart模块和蓝牙模块并搭载PKE内核实现蓝牙通信控制智能小车设计设备管理的相关实验。
<a name="subsec_pynq"></a> <a name="subsec_pynq"></a>
### 7.1.1 pynq开发板介绍 ### 8.1.1 pynq开发板介绍
本实验中我们所使用的pynq-z2开发板上搭载两块芯片一块为Arm架构32位芯片称为PS端我们能在上面运行Ubuntu另一块为FPGA可编程芯片称为PL端通过烧录Rocket chip电路使它能够运行Riscv架构的操作系统即riscv-pke。 本实验中我们所使用的pynq-z1开发板上搭载两块芯片一块为ARM架构32位芯片称为PS端我们能在上面运行Ubuntu操作系统另一块为FPGA可编程芯片称为PL端通过烧录Rocket chip电路使它能够运行Riscv架构的操作系统即riscv-pke。
![](pictures/fig6_4.png) <img title="" src="pictures/fig6_4.png" alt="" data-align="center">
如上图在开发板上运行的时候PKE在PL端运行一方面它可以通过Rocket Chip电路的连线访问PL端的设备device如蓝牙、小车电机等另一方面在PS端运行的riscv-fesvr程序可以和PKE通过HTIF协议通信使得PKE可以读写PS端Linux操作系统下的设备文件host device比如摄像头、声卡等。也就是说PKE除了可以访问本身的设备还可以利用PS端操作系统的功能访问更复杂的设备这就是代理内核的特点。 如上图在开发板上运行的时候PKE在PL端运行一方面它可以通过Rocket Chip电路的连线访问PL端的设备device如蓝牙、小车电机控制器另一方面在PS端运行的riscv-fesvr程序可以和PKE通过HTIF协议通信使得PKE可以读写PS端Linux操作系统下的设备文件host device比如摄像头、声卡等。也就是说PKE除了可以访问本身的设备还可以利用PS端操作系统的功能访问更复杂的设备这就是代理内核的特点。
实验四和前三个实验的关系如下: 实验五和前四个实验的区别如下:
![](pictures/fig6_5.png) <img title="" src="pictures/fig6_5.png" alt="" data-align="center" width="600">
可见除了部分硬件相关的操作外PKE在Spike和开发板上的运行是完全等价的代理内核一方面让我们可以用最简单的方法访问两端的设备另一方面在不同地方运行基本不用改太多代码非常优越。 可见除了部分硬件相关的操作外PKE在Spike和开发板上的运行是完全等价的代理内核一方面让我们可以用最简单的方法访问PL端与PS端两端的设备,另一方面在不同地方运行基本不用改太多代码,非常优越。
<a name="subsec_MMIO"></a> <a name="subsec_MMIO"></a>
### 7.1.2 内存映射I/O(MMIO) ### 8.1.2 内存映射I/O(MMIO)
内存映射(Memory-Mapping I/O)是一种用于设备驱动程序和设备通信的方式它区别于基于I/O端口控制的Port I/O方式。RICSV指令系统的CPU通常只实现一个物理地址空间这种情况下外设I/O端口的物理地址就被映射到CPU中单一的物理地址空间成为内存的一部分CPU可以像访问一个内存单元那样访问外设I/O端口而不需要设立专门的外设I/O指令。 内存映射(Memory-Mapping I/O)是一种用于设备驱动程序和设备通信的方式它区别于基于I/O端口控制的Port I/O方式。RICSV指令系统的CPU通常只实现一个物理地址空间这种情况下外设I/O端口的物理地址就被映射到CPU中单一的物理地址空间成为内存的一部分CPU可以像访问一个内存单元那样访问外设I/O端口而不需要设立专门的外设I/O指令。
在MMIO中内存和I/O设备共享同一个地址空间。MMIO是应用得最为广泛的一种IO方法它使用相同的地址总线来处理内存和I/O设备I/O设备的内存和寄存器被映射到与之相关联的地址。当CPU访问某个内存地址时它可能是物理内存也可以是某个I/O设备的内存。此时用于访问内存的CPU指令就可以用来访问I/O设备。每个I/O设备监视CPU的地址总线一旦CPU访问分配给它的地址它就做出响应将数据总线连接到需要访问的设备硬件寄存器。为了容纳I/O设备CPU必须预留给I/O一个地址映射区域。 在MMIO中内存和I/O设备共享同一个地址空间。MMIO是应用得最为广泛的一种IO方法它使用相同的地址总线来处理内存和I/O设备I/O设备的内存和寄存器被映射到与之相关联的地址。当CPU访问某个内存地址时它可能是物理内存也可以是某个I/O设备的内存。此时用于访问内存的CPU指令就可以用来访问I/O设备。每个I/O设备监视CPU的地址总线一旦CPU访问分配给它的地址它就做出响应将数据总线连接到需要访问的设备硬件寄存器。为了容纳I/O设备CPU必须预留给I/O一个地址映射区域。
在本章节中修改后的RocketChip将蓝牙控制寄存器和小车电机端接到固定的内存地址,因此可以通过对这些地址进行读写控制蓝牙和小车电机。 在本上一章节中修改后的Rocket Chip将蓝牙控制寄存器和小车电机控制器映射到了固定的内存地址,因此可以通过对这些地址进行读写控制蓝牙和小车电机。
<a name="subsec_fesvr"></a> <a name="subsec_fesvr"></a>
### 7.1.3 riscv-fesvr原理 ### 8.1.3 riscv-fesvr原理
riscv-fesvr是PKE在PYNQ开发板上运行的重要工具它是运行在ARM端系统上的程序用于控制PKE的启动。除了启动功能riscv-fesvr程序主要分为两个模块系统调用模块负责接受PKE对ARM端的系统调用请求在ARM端执行这些函数并返回相应的操作结果内存模块负责ARM端读写RISCV端的内存和PKE交换数据。
PKE调用宿主机/开发板ARM端的系统调用函数使用的是HTIF协议。协议要求内核保留两个64位变量tohost和fromhost作为PKE与riscv-fesvr交换数据的地方。当PKE调用宿主机/开发板ARM端系统调用函数时需要将系统调用函数的编号和参数按照固定格式写入tohost变量通过该方式向宿主机/开发板ARM端系统发起系统调用。
tohost与fromhost变量的格式如下
riscv-fesvr是PKE在PYNQ开发板上使用的重要工具它是ARM端系统上运行的程序控制PKE的启动。除了启动功能riscv-fesvr程序主要分为两个模块系统调用模块块负责接受PKE对ARM端的系统调用请求在ARM端执行这些函数内存模块负责读写RISCV端的内存和PKE交换数据。 <img src="pictures/to_from_host.png" title="" alt="" data-align="center" width="500">
PKE调用宿主机/开发板ARM端的系统调用函数使用的是HTIF协议。协议要求内核保留两个地址作为和riscv-fesvr共享数据的地方。PKE要使用系统调用函数就需要将系统调用函数的编号和参数通过这个地址发送给riscv-fesvr方法是定义一个数组magic_mem该数组存储了调用号和参数再将数组的起始地址按一定的格式填入这个地址中如下图。 当通过HTIF进行系统调用时dev与cmd固定为0data部分是一个描述系统调用的数组指针
![](pictures/fig6_7.png) <img src="pictures/fig6_7.png" title="" alt="" data-align="center">
打个比方我通过HTIF协议调用ARM端的write函数那么我先定义一个数组magic_memmagic_mem[0]为write的系统调用号magic_mem[1]为文件描述符magic_mem[2]为缓冲区地址magic_mem[3]为写入长度。然后把一个数写入共享地址这个数高8位和中间8位都是0低48位为magic_mem的地址。riscv-fesvr会首先读出magic_mem里的数据然后根据magic_mem[0]决定要调用write函数这时需要注意magic_mem[2]里的缓冲区地址是RISCV端内存的地址所以先把RISCV端内存里的这段数据读到外面内存再以外面内存地址为参数调用write函数。 例如当通过HTIF协议调用ARM端的write函数时需要先定义一个数组magic_memmagic_mem[0]为write的系统调用号magic_mem[1]为文件描述符magic_mem[2]为缓冲区地址magic_mem[3]为写入长度。然后将magic_mem数组的地址写入tohost变量的data部分。随后riscv-fesvr会首先读出magic_mem里的数据然后根据magic_mem[0]决定要调用write函数。另外需要注意magic_mem[2]里的缓冲区地址是RISCV端内存的地址所以需要先把RISCV端内存里的这段数据读到ARM端内存再以ARM端内存地址为参数调用write函数。
riscv-fesvr的内存模块用来读写RISCV端的内存从而可以读取系统调用参数以及读写PKE的缓冲区。Pynq开发板把RISCV端的内存抽象成设备文件/dev/mem所以内存模块可以通过在固定偏移量读写该文件从而实现读写内存。 PKE在写入tohost变量发起ARM端系统调用后需要接着对fromhost变量进行轮询。当PKE检测到fromhost变量被riscv-fesvr更新为非0值且其dev与cmd部分都为0时从magic_mem数组中读出系统调用返回值。
riscv-fesvr的内存模块用来读写RISCV端的内存从而可以读取系统调用参数以及读写PKE的缓冲区。pynq开发板把RISCV端的内存抽象成设备文件/dev/mem所以内存模块可以通过在固定偏移量读写该文件从而实现读写RISCV端内存。
另外控制摄像头需要用到的ioctl、mmap、munmap三个系统调用函数是原版的riscv-fesvr不支持的所以我们对riscv-fesvr的系统调用模块进行了修改 另外控制摄像头需要用到的ioctl、mmap、munmap三个系统调用函数是原版的riscv-fesvr不支持的所以我们对riscv-fesvr的系统调用模块进行了修改
* ioctl函数比较简单可以直接用PKE传过来的参数调用系统调用函数 * ioctl系统调用的实现比较简单可以直接用PKE传过来的参数调用ioctl系统调用。
* mmap函数比较麻烦因为ARM端通过mmap映射的是ARM端的内存RISCV端无法访问。所以再添加readmmap函数PKE可以通过HTIF调用此函数读取ARM端被映射的内存。将ARM端用mmap映射的所有内存用数组存储映射地址返回给PKE数组索引PKE向readmmap传入索引riscv-fesvr根据索引找到地址读取ARM端的内存数据返回给PKE。 * mmap系统调用的实现比较麻烦因为ARM端通过mmap映射的是ARM端的内存RISCV端无法访问。所以另外添加了readmmap系统调用PKE可以通过HTIF调用此系统调用函数读取ARM端被映射的内存。riscv-fesvr将mmap映射的内存用数组存储映射地址返回给PKE数组索引读取时,PKE向readmmap传入数组索引riscv-fesvr根据索引找到ARM端被映射地址读取ARM端的内存数据返回给PKE。
<a name="subsec_polling"></a> <a name="subsec_polling"></a>
### 7.1.4 轮询I/O控制方式 ### 8.1.4 轮询I/O控制方式
在实验四中,我们设备管理的主要任务是控制设备与内存的数据传递,具体为从蓝牙设备读取到用户输入的指令字符(或传递数据给蓝牙在手机端进行打印),解析为小车前、后、左、右、停止等动作来传输数据给电机实现对小车的控制。在前两个实验中,我们分别需要对轮询控制方式和中断控制方式进行实现。 在实验五中,我们设备管理的主要任务是控制设备与内存间的数据传递。具体来讲,需要将从蓝牙设备读取到的用户输入指令字符,解析为小车前进、后退、左转、右转和停止动作,并传输对应的小车控制指令给电机控制器实现对小车的控制。在前两个实验中,我们分别需要对蓝牙设备的轮询和中断访问方式进行实现。
首先程序直接控制方式又称循环测试方式每次从外部设备读取一个字的数据到存储器对于读入的每个字CPU需要对外设状态进行循环检查直到确定该数据已经传入I/O数据寄存器中 对于轮询方式又称程序直接控制方式CPU需要不断对外设状态进行检查。当外设准备好CPU所需数据后CPU会从外部设备读取一个字的数据到存储器随后再次进行状态检查准备从外设读取下一个字的数据直到数据读取完毕或者程序终止
轮询I/O控制方式流程如图 轮询I/O控制方式流程如图
<img src="pictures/fig6_1_polling.png" alt="fig6_1" style="zoom:100%;" /> <img title="" src="pictures/fig6_1_polling.png" alt="fig6_1" style="zoom:100%;" data-align="center" width="301">
<a name="subsec_plic"></a> <a name="subsec_plic"></a>
### 7.1.5 中断驱动I/O控制方式 ### 8.1.5 中断驱动I/O控制方式
在前一种轮询的控制方式中由于CPU的高速性和I/O设备的低速性导致CPU浪费绝大多数时间处于等待I/O设备完成数据传输的循环测试中会造成大量资源浪费。中断驱动的方式是允许请求I/O的进程在设备工作时进入休眠状态使CPU能够运行别的进程。直到设备工作完成时再由设备发出中断中断处理程序唤醒之前休眠的进程使其能够接受设备返回的数据继续执行。采用中断驱动的控制方式在I/O操作过程中CPU可以执行其他的进程CPU与设备之间达到了部分并行的工作状态从而提升了资源利用率。 在前一种轮询的控制方式中由于CPU的高速性和I/O设备的低速性导致CPU浪费绝大多数时间等待I/O设备完成数据传输的循环测试中会造成大量资源浪费。中断驱动的方式是允许请求I/O的进程在设备工作时进入休眠状态使CPU能够运行别的进程。直到设备工作完成时再由设备发出中断中断处理程序唤醒之前休眠的进程使其能够接受设备返回的数据继续执行。采用中断驱动的控制方式在I/O操作过程中CPU可以执行其他的进程CPU与设备之间达到了部分并行的工作状态从而提升了资源利用率。
Riscv包含三类中断:软中断、时钟中断和外部中断。软中断和时钟中断我们在实验一已经接触过,而设备触发的中断属于外部中断。在实验一中,我们在机器态捕获了时钟中断,然后将其转发成内核态的软中断交由中断处理程序处理本章则直接通过设置MIDELEG寄存器利用RISCV的中断委托机制直接将外部中断交由内核态的中断处理程序处理不用经过机器态的捕获。另外RISCV架构还指定了PLICPriority Level Interrupt Controller模块管理各级中断该设备使用MMIO控制PKE在处理中断之后通过读写指定的内存地址来获取触发中断的设备的编号以及通知PLIC本次中断是否处理成功。 RISCV包含三类中断:软中断、时钟中断和外部中断。软中断和时钟中断我们在实验一已经接触过,而设备触发的中断属于外部中断。在实验一中,我们在机器态捕获了时钟中断,然后将其转发成内核态的软中断交由中断处理程序处理。本章则直接通过设置MIDELEG寄存器利用RISCV的中断委托机制直接将外部中断代理到内核态的中断处理程序处理不用经过机器态的捕获。另外RISCV架构还指定了PLICPriority Level Interrupt Controller模块管理各级中断该设备使用MMIO控制PKE在处理中断之后通过读写指定的内存地址来获取触发中断的设备的编号以及通知PLIC本次中断是否处理成功。
中断驱动I/O方式流程如图 中断驱动I/O方式流程如图
<img src="pictures/fig6_2_plic.png" alt="fig6_2" style="zoom:100%;" /> <img title="" src="pictures/fig6_2_plic.png" alt="fig6_2" style="zoom:100%;" data-align="center" width="319">
<a name="subsec_file"></a> <a name="subsec_file"></a>
### 7.1.6 设备文件 ### 8.1.6 设备文件
用户程序访问外部设备通常有两种方式:通过特定系统调用访问和通过设备文件访问。前者即操作系统提供专门的函数控制设备,后者是操作系统把设备指定成一个文件,通过通用的文件的读写函数控制设备。设备文件常用的函数除了open、read、write、close还有以下几种 用户程序访问外部设备通常有两种方式:通过特定系统调用访问和通过设备文件访问。前者即操作系统提供专门的函数控制设备,后者是操作系统把设备抽象成一个设备文件,通过通用的文件访问函数控制设备。设备文件常用的函数除了open、read、write、close还有以下几种
* ioctl`int ioctl(int fd, unsigned long request, void *data);`用来设置设备参数。fd是文件描述符request是一个常数表示参数类型不同的类型对应不同的常数data通常是一个指向要设置的参数的值的指针参数的值可以是整数也可以是结构体等返回值为该函数是否执行成功。如摄像头设备我们就可以通过该函数设置摄像头的拍摄分辨率、颜色格式等音频设备我们可以设置采样率、数据格式等。 * ioctl`int ioctl(int fd, unsigned long request, void *data);`用来设置设备参数。fd是文件描述符request是一个常数表示参数类型不同的类型对应不同的常数data通常是一个指向要设置的参数的值的指针参数的值可以是整数也可以是结构体等返回值为该函数是否执行成功。如摄像头设备我们就可以通过该函数设置摄像头的拍摄分辨率、颜色格式等音频设备可以设置采样率、数据格式等。
* mmap`void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);`该函数原本的作用是将虚拟地址和文件进行映射使得读写文件可以像读写内存一样方便同时也能节省物理内存但对于有些不支持read/write读写的设备就必须使用mmap函数将虚拟地址和设备文件映射才能读写设备的数据。addr参数表示映射的起始虚拟地址通常填NULL表示由操作系统自己指定lengthprot、flags分别表示映射地址空间的长度、权限和其他参数fd为文件描述符offset为文件偏移量返回值为映射的起始虚拟地址。 * mmap`void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);`该函数原本的作用是将虚拟地址和文件进行映射使得读写文件可以像读写内存一样方便同时也能节省物理内存但对于有些不支持read/write读写的设备就必须使用mmap函数将虚拟地址和设备文件映射才能读写设备的数据。addr参数表示映射的起始虚拟地址通常传递NULL表示由操作系统指定lengthprot、flags分别表示映射地址空间的长度、访问权限和其他参数fd为文件描述符offset为文件偏移量返回值为映射的起始虚拟地址。
在本章节中,将使用PKE通过HTIF协议和PS端的riscv-fesvr进行通信以读写PS端的摄像头设备文件进而控制摄像头设备。 在本章的第三个实验中,我们将使用PKE通过HTIF协议和PS端的riscv-fesvr进行通信以读写PS端的摄像头设备文件进而控制摄像头设备。另外由于我们已经在实验四中为PKE实现了虚拟文件系统因此在本章第三个实验中我们会先将PS端的设备文件挂载到虚拟文件系统中然后通过虚拟文件系统对摄像头外设进行访问控制。相应的文件系统也需要添加上述几个设备文件访问函数的实现。
<a name="polling"></a> <a name="polling"></a>
## 7.2 lab4_1 poll ## 8.2 lab5_1 poll
<a name="lab4_1_app"></a> <a name="lab5_1_app"></a>
#### **给定应用** #### **给定应用**
- user/app_poll.c
- user/app_polling.c
```c ```c
1 /* 1 /*
2 * Below is the given application for lab4_1. 2 * Below is the given application for lab5_1.
3 * The goal of this app is to control the car via Bluetooth. 3 * The goal of this app is to control the car via Bluetooth.
4 */ 4 */
5 5
@ -142,50 +154,54 @@ Riscv包含三类中断软中断、时钟中断和外部中断。软中断和
应用通过轮询的方式从蓝牙端获取指令,实现对小车的控制功能。 应用通过轮询的方式从蓝牙端获取指令,实现对小车的控制功能。
* 先提交lab3_3的答案然后切换到lab4_1继承lab3_3中所做的修改并make * 先提交lab4_3的答案然后切换到lab5_1继承lab4_3中所做的修改并make
```bash ```bash
//切换到lab4_1 //切换到lab5_1
$ git checkout lab4_1_poll $ git checkout lab5_1_poll
//继承lab3_3以及之前的答案 //继承lab4_3以及之前的答案
$ git merge lab3_3_rrsched -m "continue to work on lab4_1" $ git merge lab4_3_hardlink -m "continue to work on lab5_1"
//重新构造 //重新构造
$ make clean; make $ make clean; make
``` ```
由于本实验给出的基础代码修改了硬件相关的部分代码所以无法在Spike上运行需在PYNQ开发板上进行验证。读者在进行本实验之前应该已经按照第章中的说明完成了fpga实验1并将rocketchip_wrapper.bit.bin文件烧录到了开发板中。 由于本实验给出的基础代码修改了硬件相关的部分代码所以无法在Spike上运行需在PYNQ开发板上进行验证。读者在进行本实验之前应该已经按照第章中的说明完成了fpga实验1并将rocketchip_wrapper.bit.bin文件烧录到了开发板中。
make完成后需要将obj目录下编译生成的可执行文件传输到开发板中然后在开发板上运行程序。具体的做法如下 make完成后需要将obj目录下编译生成的可执行文件传输到开发板中然后在开发板上运行程序。具体的做法如下
1. 首先将make命令编译生成的两个可执行文件obj/app_polling文件与obj/riscv-pke文件通过sftp协议传输到开发板的/home/xilinx目录下。至于如何向开发板中传送文件在第章中已有详细的介绍,读者可以参考之前的描述。 1. 首先将make命令编译生成的两个可执行文件obj/app_polling文件与obj/riscv-pke文件通过sftp协议传输到开发板的/home/xilinx目录下。至于如何向开发板中传送文件在第章中已有详细的介绍,读者可以参考之前的描述。
2. 通过ssh协议连接到开发板连接方法在第章中同样有详细说明并在ssh客户端中输入如下命令执行app_polling 2. 通过ssh协议连接到开发板连接方法在第章中同样有详细说明并在ssh客户端中输入如下命令执行app_polling
```bash ```bash
$ sudo ./program.sh # 若重启过开发板,则在执行程序前要先执行此脚本进行烧录 $ sudo ./program.sh # 若重启过开发板,则在执行程序前要先执行此脚本进行烧录
$ sudo ./riscv-fesvr riscv-pke app_poll $ sudo ./riscv-fesvr riscv-pke app_polling
In m_start, hartid:0 In m_start, hartid:0
(Emulated) memory size: 256 MB (Emulated) memory size: 256 MB
Enter supervisor mode... Enter supervisor mode...
PKE kernel start 0x0000000080000000, PKE kernel end: 0x0000000080009000, PKE kernel size: 0x0000000000009000 . PKE kernel start 0x0000000080000000, PKE kernel end: 0x0000000080011000, PKE kernel size: 0x0000000000011000 .
free physical memory address: [0x0000000080009000, 0x00000000800fffff] free physical memory address: [0x0000000080011000, 0x00000000800fffff]
kernel memory manager is initializing ... kernel memory manager is initializing ...
KERN_BASE 0x0000000080000000 KERN_BASE 0x0000000080000000
physical address of _etext is: 0x0000000080005000 physical address of _etext is: 0x0000000080009000
kernel page table is on kernel page table is on
RAMDISK0: base address of RAMDISK0 is: 0x0000000080072000
RFS: format RAMDISK0 done!
Switch to user mode... Switch to user mode...
in alloc_proc. user frame 0x00000000800f9000, user stack 0x000000007ffff000, user kstack 0x00000000800f8000 in alloc_proc. user frame 0x0000000080066000, user stack 0x000000007ffff000, user kstack 0x0000000080065000
FS: created a file management struct for a process.
in alloc_proc. build proc_file_management successfully.
User application is loading. User application is loading.
Application: app_polling Application: app_polling
CODE_SEGMENT added at mapped info offset:3 CODE_SEGMENT added at mapped info offset:4
Application program entry point (virtual address): 0x0000000000010078 DATA_SEGMENT added at mapped info offset:5
Application program entry point (virtual address): 0x00000000000100b0
going to insert process 0 to ready queue. going to insert process 0 to ready queue.
going to schedule process 0 to run. going to schedule process 0 to run.
Ticks 0
please input the instruction through bluetooth! please input the instruction through bluetooth!
You have to implement sys_user_uart_getchar to get data from UART using uartgetchar in lab4_1 and modify it in lab4_2. You have to implement sys_user_uart_getchar to get data from UART using uartgetchar in lab5_1 and modify it in lab5_2.
System is shutting down with exit code -1. System is shutting down with exit code -1.
``` ```
@ -200,22 +216,27 @@ make完成后需要将obj目录下编译生成的可执行文件传输到开
```bash ```bash
$ sudo ./program.sh # 若重启过开发板,则在执行程序前要先执行此脚本进行烧录 $ sudo ./program.sh # 若重启过开发板,则在执行程序前要先执行此脚本进行烧录
$ sudo ./riscv-fesvr riscv-pke app_poll $ sudo ./riscv-fesvr riscv-pke app_polling
In m_start, hartid:0 In m_start, hartid:0
(Emulated) memory size: 256 MB (Emulated) memory size: 256 MB
Enter supervisor mode... Enter supervisor mode...
PKE kernel start 0x0000000080000000, PKE kernel end: 0x0000000080009000, PKE kernel size: 0x0000000000009000 . PKE kernel start 0x0000000080000000, PKE kernel end: 0x0000000080011000, PKE kernel size: 0x0000000000011000 .
free physical memory address: [0x0000000080009000, 0x00000000800fffff] free physical memory address: [0x0000000080011000, 0x00000000800fffff]
kernel memory manager is initializing ... kernel memory manager is initializing ...
KERN_BASE 0x0000000080000000 KERN_BASE 0x0000000080000000
physical address of _etext is: 0x0000000080005000 physical address of _etext is: 0x0000000080009000
kernel page table is on kernel page table is on
RAMDISK0: base address of RAMDISK0 is: 0x0000000080072000
RFS: format RAMDISK0 done!
Switch to user mode... Switch to user mode...
in alloc_proc. user frame 0x00000000800f9000, user stack 0x000000007ffff000, user kstack 0x00000000800f8000 in alloc_proc. user frame 0x0000000080066000, user stack 0x000000007ffff000, user kstack 0x0000000080065000
FS: created a file management struct for a process.
in alloc_proc. build proc_file_management successfully.
User application is loading. User application is loading.
Application: app_polling Application: app_polling
CODE_SEGMENT added at mapped info offset:3 CODE_SEGMENT added at mapped info offset:4
Application program entry point (virtual address): 0x0000000000010078 DATA_SEGMENT added at mapped info offset:5
Application program entry point (virtual address): 0x00000000000100b0
going to insert process 0 to ready queue. going to insert process 0 to ready queue.
going to schedule process 0 to run. going to schedule process 0 to run.
please input the instruction through bluetooth! please input the instruction through bluetooth!
@ -226,6 +247,7 @@ please input the instruction through bluetooth!
1. 在手机端下载任意一种蓝牙串口通信APP提供一个在安卓12上测试无误的软件下载[蓝牙串口](https://www.coolapk.com/apk/com.orion.bluetoothserialtool)下面的步骤都以此APP为例进行说明其他蓝牙串口通信APP的使用方法与之类似 1. 在手机端下载任意一种蓝牙串口通信APP提供一个在安卓12上测试无误的软件下载[蓝牙串口](https://www.coolapk.com/apk/com.orion.bluetoothserialtool)下面的步骤都以此APP为例进行说明其他蓝牙串口通信APP的使用方法与之类似
2. 打开手机蓝牙开关。 2. 打开手机蓝牙开关。
3. 点击蓝牙串口通信APP下方的蓝牙设备按钮找到蓝牙模块其名称通常是“BT04-A”点击进行配对。若提示输入PIN码则输入“1234”。 3. 点击蓝牙串口通信APP下方的蓝牙设备按钮找到蓝牙模块其名称通常是“BT04-A”点击进行配对。若提示输入PIN码则输入“1234”。
4. 点击蓝牙串口通信APP下方的收发数据按钮进入收发数据界面。在下方的对话框中即可输入控制指令具体命令如下 4. 点击蓝牙串口通信APP下方的收发数据按钮进入收发数据界面。在下方的对话框中即可输入控制指令具体命令如下
@ -233,7 +255,7 @@ please input the instruction through bluetooth!
**注意,若软硬件都正确无误,在发送下方指令后小车会立即开始运动。请务必确保小车的运动在实验人员的控制之内!若小车上有连接用于调试的网线等设备,建议暂时使小车的四个车轮处于悬空状态。当功能全部测试无误后,可以在保持程序运行的情况下直接拔掉网线,并将小车放置在地面上移动**。 **注意,若软硬件都正确无误,在发送下方指令后小车会立即开始运动。请务必确保小车的运动在实验人员的控制之内!若小车上有连接用于调试的网线等设备,建议暂时使小车的四个车轮处于悬空状态。当功能全部测试无误后,可以在保持程序运行的情况下直接拔掉网线,并将小车放置在地面上移动**。
| 命令 | 效果 | | 命令 | 效果 |
| :--: | :---------------------------------------------: | |:---:|:----------------------------:|
| 1 | 小车前进 | | 1 | 小车前进 |
| 2 | 小车后退 | | 2 | 小车后退 |
| 3 | 小车左转 | | 3 | 小车左转 |
@ -241,27 +263,27 @@ please input the instruction through bluetooth!
| 0 | 小车停止 | | 0 | 小车停止 |
| q | 程序停止在ssh会话中输入ctrl+c也可以停止程序 | | q | 程序停止在ssh会话中输入ctrl+c也可以停止程序 |
<a name="lab4_1_guide"></a> <a name="lab5_1_guide"></a>
#### **实验指导** #### **实验指导**
基于实验lab1_1你已经了解和掌握操作系统中系统调用机制的实现原理。对于本实验的应用我们发现user/app_poll.c文件中有三个函数调用uart_getcharuart_putchar和uart2_putchar。UART是一种控制设备的端口协议但在本实验中它可以通过MMIO进行控制。对代码进行跟踪我们发现这三个函数都在user/user_lib.c中进行了实现对应于lab1_1的流程我们可以在kernel/syscall.h中查看新增的系统调用以及编号 基于实验lab1_1你已经了解和掌握操作系统中系统调用机制的实现原理。对于本实验的应用我们发现user/app_poll.c文件中有三个函数调用uart_getcharuart_putchar和uart2_putchar。UART是一种控制设备的端口协议但在本实验中它可以通过MMIO进行控制。对代码进行跟踪我们发现这三个函数都在user/user_lib.c中进行了实现对应于lab1_1的流程我们可以在kernel/syscall.h中查看新增的系统调用以及编号
```c ```c
16 #define SYS_user_uart_putchar (SYS_user_base + 6) 34 #define SYS_user_uart_putchar (SYS_user_base + 30)
17 #define SYS_user_uart_getchar (SYS_user_base + 7) 35 #define SYS_user_uart_getchar (SYS_user_base + 31)
18 #define SYS_user_uart2_putchar (SYS_user_base + 8) 36 #define SYS_user_uart2_putchar (SYS_user_base + 32)
``` ```
继续追踪我们发现在kernel/syscall.c的do_syscall函数中新增了对应系统调用编号的实现函数对于新增系统调用分别有如下函数进行处理 继续追踪我们发现在kernel/syscall.c的do_syscall函数中新增了对应系统调用编号的实现函数对于新增系统调用分别有如下函数进行处理
```c ```c
133 case SYS_user_uart_putchar: 297 case SYS_user_uart_putchar:
134 sys_user_uart_putchar(a1);return 1; 298 sys_user_uart_putchar(a1);return 1;
135 case SYS_user_uart_getchar: 299 case SYS_user_uart_getchar:
136 return sys_user_uart_getchar(); 300 return sys_user_uart_getchar();
137 case SYS_user_uart2_putchar: 301 case SYS_user_uart2_putchar:
138 sys_user_uart2_putchar(a1);return 1; 302 sys_user_uart2_putchar(a1);return 1;
``` ```
读者的任务即为在kernel/syscall.c中追踪并完善sys_user_uart_getchar。对于uart相关的函数我们给出uart端口的地址映射如图 读者的任务即为在kernel/syscall.c中追踪并完善sys_user_uart_getchar。对于uart相关的函数我们给出uart端口的地址映射如图
@ -270,37 +292,59 @@ please input the instruction through bluetooth!
图中的axi_uartlite_0对应于蓝牙设备的uart端口aix_uartlite_1则对应于小车电机驱动板的uart端口。其中蓝牙设备的uart端口的偏移地址为0x60000000对应写地址为0x60000004读地址为0x60000000同时对0x60000008的状态位进行轮询检测到信号时进行读写操作。 图中的axi_uartlite_0对应于蓝牙设备的uart端口aix_uartlite_1则对应于小车电机驱动板的uart端口。其中蓝牙设备的uart端口的偏移地址为0x60000000对应写地址为0x60000004读地址为0x60000000同时对0x60000008的状态位进行轮询检测到信号时进行读写操作。
在kernel/syscall.c中找到函数实现空缺并根据注释完成sys_user_uart_getchar系统调用由于lab4_2需要对lab4_1完成的代码进行修改所以这里一并给出了lab4_2的提示 在kernel/syscall.c中找到函数实现空缺并根据注释完成sys_user_uart_getchar系统调用由于lab5_2需要对lab5_1完成的代码进行修改所以这里一并给出了lab5_2的提示
```c ```c
95 ssize_t sys_user_uart_getchar() { 227 ssize_t sys_user_uart_getchar() {
96 // TODO (lab4_1 and lab4_2): implment the syscall of sys_user_uart_getchar and modify it in lab4_2. 228 // TODO (lab5_1 and lab5_2): implment the syscall of sys_user_uart_getchar and modify it in lab5_2.
97 // hint (lab4_1): The functionality of sys_user_uart_getchar is to get data from UART address. 229 // hint (lab5_1): the functionality of sys_user_uart_getchar is to get data from UART address.
98 // Therefore we should check the data from the address of bluetooth status repeatedly, until the data is ready. 230 // therefore, we should let a pointer point, insert it in
99 // Then read the data from the address of bluetooth reading and return. 231 // the rear of ready queue, and finally, schedule a READY process to run.
100 // hint (lab4_2): the functionality of sys_user_uart_getchar is let process sleep and wait for value. therefore, 232 // hint (lab5_2): the functionality of sys_user_uart_getchar is let process sleep
101 // we should call do_sleep to let process 0 sleep. 233 // and register a callback function to handle system call return value.
102 // then we should get uartvalue and return. 234 // therefore, we should call do_sleep to let process 0 sleep.
103 panic( "You have to implement sys_user_uart_getchar to get data from UART using uartgetchar in lab4_1 and modify it in lab4_2.\n" ); 235 // Note that the do_sleep function will never return, and the function passed to do_sleep
104 236 // will be called in do_wake.
105 } 237 panic( "You have to implement sys_user_uart_getchar to get data from UART using uartgetchar in lab5_1 and modify it in lab5_2.\n" );
238 }
``` ```
**注意编写自己的代码时千万不要修改或删去lab4_2的提示即100行到102行),防止后面实验的合并错误!** **注意编写自己的代码时千万不要修改或删去lab5_2的提示即232行到236行),防止后面实验的合并错误!**
**实验完毕后,记得提交修改(命令行中-m后的字符串可自行确定以便在后续实验中继承lab4_1中所做的工作** **实验完毕后,记得提交修改(命令行中-m后的字符串可自行确定以便在后续实验中继承lab5_1中所做的工作**
``` ```
$ git commit -a -m "my work on lab4_1 is done." $ git commit -a -m "my work on lab5_1 is done."
``` ```
<a name="lab4_1_ans"></a>
#### 答案解析
将kernel/syscall.c文件中的
```c
panic( "You have to implement sys_user_uart_getchar to get data from UART using uartgetchar in lab5_1 and modify it in lab5_2.\n" );
```
替换成:
```c
volatile uint32 *status = (void*)(uintptr_t)0x60000008;
volatile uint32 *rx = (void*)(uintptr_t)0x60000000;
while (!(*status & 0x00000001));
return *rx;
```
替换原因:
地址0x60000008被映射到axi uartlite IP核的状态寄存器而该状态寄存器的最低位被用来描述接收数据FIFO的状态该位为1时表明接收数据FIFO有数据到达。因此在从蓝牙设备读取数据的过程中需要通过轮询的方式不断检查状态寄存器最低位的值直到其值为1表明有数据到达时停止检测并读取接收数据FIFO中的值进行返回接收数据FIFO寄存器的地址被映射为0x60000000
<a name="PLIC"></a> <a name="PLIC"></a>
## 7.3 lab4_2_PLIC ## 7.3 lab5_2 PLIC
<a name="lab4_2_app"></a> <a name="lab5_2_app"></a>
#### **给定应用** #### **给定应用**
@ -308,7 +352,7 @@ $ git commit -a -m "my work on lab4_1 is done."
```c ```c
1 /* 1 /*
2 * Below is the given application for lab4_2. 2 * Below is the given application for lab5_2.
3 * The goal of this app is to control the car via Bluetooth. 3 * The goal of this app is to control the car via Bluetooth.
4 */ 4 */
5 5
@ -352,19 +396,18 @@ $ git commit -a -m "my work on lab4_1 is done."
43 43
44 return 0; 44 return 0;
45 } 45 }
``` ```
应用通过中断的方式从蓝牙端获取指令实现对小车的控制功能。在等待蓝牙的进程休眠的时候会执行delay进程可以看到waiting for you提示信息。 应用通过中断的方式从蓝牙端获取指令实现对小车的控制功能。在等待蓝牙的进程休眠的时候会执行delay进程可以看到waiting for you提示信息。
* 先提交lab4_1的答案然后切换到lab4_2继承lab4_1中所做的修改并make * 先提交lab5_1的答案然后切换到lab5_2继承lab5_1中所做的修改并make
```bash ```bash
//切换到lab4_2 //切换到lab5_2
$ git checkout lab4_2_PLIC $ git checkout lab5_2_PLIC
//继承lab4_1以及之前的答案 //继承lab5_1以及之前的答案
$ git merge lab4_1_poll -m "continue to work on lab4_2" $ git merge lab5_1_poll -m "continue to work on lab5_2"
//重新构造 //重新构造
$ make clean; make $ make clean; make
@ -377,70 +420,156 @@ $ sudo ./program.sh # 若重启过开发板,则在执行程序前要先执行
$ sudo ./riscv-fesvr riscv-pke app_PLIC $ sudo ./riscv-fesvr riscv-pke app_PLIC
``` ```
直接编译执行结果和完成后的lab4_1一致一直阻塞在这里等待蓝牙数据。需要修改lab4_1所写的代码并添加中断处理使得等待蓝牙的进程能够自动休眠执行delay进程直到发生外部中断后才继续执行。 直接编译执行结果和完成后的lab5_1一致一直阻塞在这里等待蓝牙数据。需要修改lab5_1所写的代码并添加中断处理使得等待蓝牙的进程能够自动休眠执行delay进程直到发生外部中断后才继续执行。
<a name="lab4_2_content"></a> <a name="lab5_2_content"></a>
#### **实验内容** #### **实验内容**
如输出提示所表示的那样需要修改lab4_1所写的代码并添加中断处理。完成后按lab4_1的方法执行程序在等待蓝牙的时候会不断输出waiting for you提示信息在手机上输入控制指令后小车应能根据指令反应。 如输出提示所表示的那样需要修改lab5_1所写的代码并添加中断处理。完成后按lab5_1的方法执行程序在等待蓝牙的时候会不断输出waiting for you提示信息在手机上输入控制指令后小车应能根据指令反应。
<a name="lab4_2_guide"></a> <a name="lab5_2_guide"></a>
#### **实验指导** #### **实验指导**
在kernel/syscall.c中找到lab4_1写的代码并根据注释进行修改 在kernel/syscall.c中找到lab5_1写的代码并根据注释进行修改
```c ```c
95 ssize_t sys_user_uart_getchar() { 227 ssize_t sys_user_uart_getchar() {
96 // TODO (lab4_1 and lab4_2): implment the syscall of sys_user_uart_getchar and modify it in lab4_2. 228 // TODO (lab5_1 and lab5_2): implment the syscall of sys_user_uart_getchar and modify it in lab5_2.
97 // hint (lab4_1): The functionality of sys_user_uart_getchar is to get data from UART address. 229 // hint (lab5_1): the functionality of sys_user_uart_getchar is to get data from UART address.
98 // Therefore we should check the data from the address of bluetooth status repeatedly, until the data is ready. 230 // therefore, we should let a pointer point, insert it in
99 // Then read the data from the address of bluetooth reading and return. 231 // the rear of ready queue, and finally, schedule a READY process to run.
100 // hint (lab4_2): the functionality of sys_user_uart_getchar is let process sleep and wait for value. therefore, 232 // hint (lab5_2): the functionality of sys_user_uart_getchar is let process sleep
101 // we should call do_sleep to let process 0 sleep. 233 // and register a callback function to handle system call return value.
102 // then we should get uartvalue and return. 234 // therefore, we should call do_sleep to let process 0 sleep.
103 panic( "You have to implement sys_user_uart_getchar to get data from UART using uartgetchar in lab4_1 and modify it in lab4_2.\n" ); // 该行已被你之前写的代码替换 235 // Note that the do_sleep function will never return, and the function passed to do_sleep
104 236 // will be called in do_wake.
105 } 237 panic( "You have to implement sys_user_uart_getchar to get data from UART using uartgetchar in lab5_1 and modify it in lab5_2.\n" );
238 }
``` ```
当蓝牙有数据发送时pke会收到外部中断你需要完成接收到外部中断后的处理。 当应用程序调用uartgetchar获取蓝牙输入指令时会首先让当前进程阻塞并等待蓝牙数据到达。你需要在上述sys_user_uart_getchar函数中实现对当前进程的阻塞。为了完成该工作本实验中添加了一个`do_sleep`函数(`kernel/process.c`
```c
312 void do_sleep(void wake_cb(void*), void* wake_cb_arg){
313 current->status = BLOCKED;
314 set_wake_callback(current->pid, wake_cb, wake_cb_arg);
315 schedule();
316 }
```
`do_sleep`除了将当前进程阻塞外,还将一个调用者提供的回调函数`wake_cb`以及它对应的参数`wake_cb_arg`注册到了当前进程中。
相应的,本实验中还提供了一个`do_wake``kernel/process.c`)函数,用来唤醒指定进程:
```c
320 void do_wake(uint64 pid){
321 procs[pid].status = READY;
322 current->status = READY;
323 insert_to_ready_queue(&procs[pid]);
324 insert_to_ready_queue( current );
325
326 if (procs[pid].wake_callback)
327 procs[pid].wake_callback(procs[pid].wake_callback_arg);
328
329 schedule();
330 }
```
`do_wake`除了会唤醒指定进程还会使用wake_callback_arg参数调用在`do_sleep`中注册的`wake_callback`函数。
当蓝牙有数据发送时PKE会收到外部中断你需要完成接收到外部中断后的处理。
在kernel/strap.c中找到函数空缺并根据注释完成中断处理函数 在kernel/strap.c中找到函数空缺并根据注释完成中断处理函数
```c ```c
103 case CAUSE_MEXTERNEL_S_TRAP: 110 case CAUSE_MEXTERNEL_S_TRAP:
104 { 111 {
105 //reset the PLIC so that we can get the next external interrupt. 112 //reset the PLIC so that we can get the next external interrupt.
106 volatile int irq = *(uint32 *)0xc201004L; 113 volatile int irq = *(uint32 *)0xc201004L;
107 *(uint32 *)0xc201004L = irq; 114 *(uint32 *)0xc201004L = irq;
108 volatile int *ctrl_reg = (void *)(uintptr_t)0x6000000c; 115 volatile int *ctrl_reg = (void *)(uintptr_t)0x6000000c;
109 *ctrl_reg = *ctrl_reg | (1 << 4); 116 *ctrl_reg = *ctrl_reg | (1 << 4);
110 // TODO (lab4_2): implment the case of CAUSE_MEXTERNEL_S_TRAP. 117 // TODO (lab5_2): implment the case of CAUSE_MEXTERNEL_S_TRAP.
111 // hint: the case of CAUSE_MEXTERNEL_S_TRAP is to get data from UART address and wake the process. therefore, 118 // hint: the case of CAUSE_MEXTERNEL_S_TRAP is to get data from UART address and wake
112 // and you need to store the data in struct process.value. 119 // the process. therefore, you need to construct an update_uartvalue_ctx structure
113 panic( "You have to implement CAUSE_MEXTERNEL_S_TRAP to get data from UART and wake the process 0 in lab4_2.\n" ); 120 // then store the interrupt processing process pid and the uart value in it
114 121 // and use this structure to update the wake callback context of the process
115 break; 122 // finally call do_wake to wake up the process.
116 } 123 panic( "You have to implement CAUSE_MEXTERNEL_S_TRAP to get data from UART and wake the process 0 in lab5_2.\n" );
124
125 break;
126 }
```
注意由于在do_sleep函数中调用了schedule函数而schedule最终会导致CPU切换到用户态继续执行用户代码因此**do_sleep函数不会返回在它之后的代码也不会被运行即使进程被唤醒**。所以你无法通过在调用do_sleep函数的位置后编写代码来尝试处理uartgetchar系统调用的返回值。正确的做法是通过在do_sleep中注册一个回调函数该函数在基础代码中已经提供位于`kernel/syscall.c`的`update_uartvalue`函数并在获取到蓝牙设备输入后将蓝牙输入注册为该回调函数的调用参数。该回调函数会在进程被唤醒时执行可以在这里处理uartgetchar的返回值然后CPU控制权会回到用户态程序。
**实验完毕后,记得提交修改(命令行中-m后的字符串可自行确定以便在后续实验中继承lab5_2中所做的工作**
```
$ git commit -a -m "my work on lab5_2 is done."
```
<a name="lab5_2_ans"></a>
#### 答案解析
将kernel/strap.c文件中的
```c
panic( "You have to implement CAUSE_MEXTERNEL_S_TRAP to get data from UART and wake the process 0 in lab5_2.\n" );
``` ```
**实验完毕后,记得提交修改(命令行中-m后的字符串可自行确定以便在后续实验中继承lab4_2中所做的工作** 替换为:
```c
volatile uint32 *rx = (void*)(uintptr_t)0x60000000;
uint32 data = *rx;
uint64 handle_intr_pid = 0; // the pid of the process that will handle this interrupt
struct update_uartvalue_ctx *ctx = (struct update_uartvalue_ctx *)alloc_page();
ctx->uartvalue = (char)data;
ctx->pid = handle_intr_pid;
set_wake_callback(handle_intr_pid, NULL, ctx); // add wake callback context
do_wake(handle_intr_pid);
``` ```
$ git commit -a -m "my work on lab4_2 is done."
替换原因:
此处的代码会在产生外部中断时被调用而外部中断的产生则表明蓝牙设备接收到了数据。此时需要从接收数据FIFO中映射地址0x60000000将收到的数据读出并构造一个update_uartvalue_ctx上下文结构体该结构体在kernel/process.h文件中被定义将从蓝牙设备接收到的数据暂存至该上下文中同时存入的还有小车控制进程的id号0最后调用do_wake函数唤醒小车控制进程。在do_wake函数中会调用do_sleep中注册的进程唤醒回调函数update_uartvalue该函数根据上下文信息处理uart_getchar系统调用的返回值
```c
229 void update_uartvalue(void *ctx) {
230 struct update_uartvalue_ctx *uart_ctx = (struct update_uartvalue_ctx *)ctx;
231 char value = uart_ctx->uartvalue;
232 process *proc = &procs[uart_ctx->pid];
233
234 // set system call return value
235 proc->trapframe->regs.a0 = (uint64)value;
236 }
``` ```
再将kernel/syscall.c文件中的sys_user_uart_getchar函数内容替换为
```c
do_sleep(update_uartvalue, NULL);
return 0;
```
替换原因:
当小车控制进程间接调用该函数获取蓝牙设备输入时第一行代码会导致其被阻塞并让出CPU在来自蓝牙设备的中断到达之前小车控制进程会一直处于阻塞状态。当蓝牙设备接收到数据并产生中断时do_wake会调用由do_sleep注册的回调函数update_uartvalue处理系统调用返回值。最后小车控制进程会被上文中添加的do_wake函数所唤醒。注意位于do_sleep后面的语句永远不会被执行包括return 0因此我们需要通过注册回调函数的方式来正确处理uart_getchar系统调用的返回值。
<a name="hostdevice"></a> <a name="hostdevice"></a>
## 7.4 lab4_3_hostdevice ## 7.4 lab5_3 hostdevice
<a name="lab4_3_app"></a> <a name="lab5_3_app"></a>
#### **给定应用** #### **给定应用**
- user/app_host_device.c - user/app_host_device.c
```c ```c
@ -531,16 +660,16 @@ $ git commit -a -m "my work on lab4_2 is done."
85 } 85 }
``` ```
该用户程序包含两个进程,其中主进程和实验4_2类似负责接收蓝牙发送过来的数据根据数据控制小车行动前进、后退、左转、右转、停止子进程则负责拍摄和分析首先初始化摄像头设备然后是个死循环判断摄像头拍摄的图像数据如果当前小车处于前进状态则拍摄然后检查数据如果判断前面有障碍物则控制车轮停转刹车否则如果主进程退出了则自己进行释放文件、内存、关闭设备等操作再退出。在用户程序操控摄像头的过程中使用了ioctl、mmap、munmap等系统调用你需完善其中的open和ioctl两个系统调用,对其进行完善从而实现小车的障碍识别和停止功能。 该用户程序包含两个进程,其中主进程和实验5_2类似负责接收蓝牙发送过来的数据根据数据控制小车行动前进、后退、左转、右转、停止子进程则负责拍摄和分析首先初始化摄像头设备然后是个死循环判断摄像头拍摄的图像数据如果当前小车处于前进状态则拍摄然后检查数据如果判断前面有障碍物则控制车轮停转刹车否则如果主进程退出了则自己进行释放文件、内存、关闭设备等操作再退出。在用户程序操控摄像头的过程中使用了ioctl、mmap、munmap等系统调用你需完善其中的ioctl系统调用对其进行完善从而实现小车的障碍识别和停止功能。
* 先提交lab4_2的答案然后切换到lab4_3继承lab4_2中所做的修改并make * 先提交lab5_2的答案然后切换到lab5_3继承lab5_2中所做的修改并make
```bash ```bash
//切换到lab4_3 //切换到lab5_3
$ git checkout lab4_3_hostdevice $ git checkout lab5_3_hostdevice
//继承lab4_2以及之前的答案 //继承lab5_2以及之前的答案
$ git merge lab4_2_PLIC -m "continue to work on lab4_3" $ git merge lab5_2_PLIC -m "continue to work on lab5_3"
//重新构造 //重新构造
$ make clean; make $ make clean; make
@ -557,39 +686,52 @@ $ sudo ./riscv-fesvr riscv-pke app_host_device
#### 实验内容 #### 实验内容
如应用提示所表示的那样,读者需要找到并完成对open和ioctl的调用使得用户能够设置设备参数从而控制摄像头实现拍照等功能获取图片后检查数据从而判断前方是否出现障碍物。 如应用提示所表示的那样读者需要找到并完成对ioctl的调用使得用户能够设置设备参数从而控制摄像头实现拍照等功能获取图片后检查数据从而判断前方是否出现障碍物。
跟踪相关系统调用在kernel/file.c里可以看到需要补充的函数 在之前的文件系统实验中我们已经为PKE实现了虚拟文件系统并为host端的文件设计了一个hostfs从而为PKE提供通过虚拟文件系统直接访问host端文件的能力。同样的在本次实验中摄像头在ARM端系统中也是作为设备文件进行管理的它对应`/dev`目录下的`/dev/hostfs`文件。通过直接操作该设备文件的方式便可以通过文件系统接口来控制摄像头设备。
为了在PKE中操作摄像头设备本实验修改了PKE文件系统的默认挂载方式首先仍然将ARM端的hostfs_root目录作为虚拟文件系统根目录进行挂载其次将ARM端的`/dev`目录挂载到PKE的`/dev`目录这样便可以像访问本地设备一样访问ARM端设备文件另外本实验并不涉及到RFS。与上述变更相关的代码可以在`kernel/proc_file.c`中的`fs_init`函数内找到:
```c ```c
25 int do_open(char *pathname, int flags) { 21 void fs_init(void) {
26 // TODO (lab4_3): call host open through spike_file_open and then bind fd to spike_file 22 // initialize the vfs
27 // hint: spike_file_dup function can bind spike_file_t to an int fd. 23 vfs_init();
28 panic( "You need to finish open function in lab4_3.\n" ); 24
29 } 25 // register hostfs-file and mount it as the root
30 int do_write(int fd, char *buf, uint64 count) { 26 if( register_hostfs() < 0 ) panic( "fs_init: cannot register hostfs.\n" );
31 spike_file_t *f = spike_file_get(fd); 27 struct device *hostdev = init_host_device("HOSTDEV", "./hostfs_root");
32 return spike_file_write(f, buf, count); 28 vfs_mount("HOSTDEV", MOUNT_AS_ROOT);
33 } 29
34 int do_close(int fd) { 30 // register hostfs-dev and mount it as /dev
35 spike_file_t *f = spike_file_get(fd); 31 struct device *hostdev_dev = init_host_device("dev", "/dev");
36 return spike_file_close(f); 32 vfs_mount("dev", MOUNT_DEFAULT);
37 } 33
38 34 // register and mount rfs
39 int do_ioctl(int fd, uint64 request, char *data) { 35 if( register_rfs() < 0 ) panic( "fs_init: cannot register rfs.\n" );
40 // TODO (lab4_3): call host ioctl through frontend_sycall 36 struct device *ramdisk0 = init_rfs_device("RAMDISK0");
41 // hint: fronted_syscall ioctl argument: 37 rfs_format_dev(ramdisk0);
42 // 1.call number 38 vfs_mount("RAMDISK0", MOUNT_DEFAULT);
43 // 2.fd 39 }
44 // 3.the order to device ```
45 // 4.data address
46 panic( "You need to call host's ioctl by frontend_syscall in lab4_3.\n" ); 另外为了让hostfs支持对给定的不同路径`hostfs_root`、`/dev`实例化出多个hostfs设备并挂载到不同的虚拟文件系统目录`/`、`/dev`基础代码对hostfs也进行了一些改造。这部分相关代码可以在`kernel/hostfs.c`中找到,读者可以自行阅读,这里不再赘述。
47 }
为了通过ioctl操作设备文件基础代码中还为VFS添加了文件ioctl操作的相关支持。读者在完成文件系统相关实验后应该对文件系统相关函数的实验方式有了一定的了解与对文件的read/write等操作的实现方式一样ioctl同样是作为vinode的一个操作函数实现的。对ioctl实现的跟踪方式也与之前在文件系统中对文件操作函数的跟踪方式类似通过对相关调用的跟踪在`kernel/hostfs.c`里可以看到需要补充的函数:
```c
215 int hostfs_ioctl(struct vinode *f_inode, uint64 request, char *data) {
216 spike_file_t *pf = (spike_file_t *)f_inode->i_fs_info;
217 if (pf < 0) {
218 sprint("hostfs_write: invalid file handle!\n");
219 return -1;
220 }
221 panic( "You need to call host's ioctl by frontend_syscall in lab5_3.\n" );
222 }
``` ```
实验预期结果小车在前进过程中能够正常识别障碍物后并自动停车。测试时别忘记把摄像头接到USB接口否则系统会找不到设备文件。 实验预期结果小车在前进过程中能够正常识别障碍物后并自动停车。测试时别忘记把摄像头接到USB接口否则系统会找不到设备文件。
<a name="lab4_3_guide"></a> <a name="lab5_3_guide"></a>
#### 实验指导 #### 实验指导
@ -610,10 +752,31 @@ USB摄像头最基础的控制方法是使用读写设备文件的方式。拍
应用第21行可以看到我们从摄像头获取的数据是YUYV格式读者可进行查阅它用灰度、蓝色色度、红色色度三个属性表示颜色每个像素点都有灰度属性。由于我们分析障碍物只需要灰度图所以取每个像素点的灰度属性即可。因此对于获取过来的数据删去奇数索引的数据就可以得到灰度图。 应用第21行可以看到我们从摄像头获取的数据是YUYV格式读者可进行查阅它用灰度、蓝色色度、红色色度三个属性表示颜色每个像素点都有灰度属性。由于我们分析障碍物只需要灰度图所以取每个像素点的灰度属性即可。因此对于获取过来的数据删去奇数索引的数据就可以得到灰度图。
注意:对于灰度阈值的设定可根据环境亮度进行一定的调整,可以先根据摄像头返回的图像进行分析,计算出对应障碍物的灰度值;灰度阈值越精确,小车对于障碍物的识别将越灵敏,并能在合理的距离内识别到障碍物并停车。 注意:对于灰度阈值的设定可根据环境亮度进行一定的调整,可以先根据摄像头返回的图像进行分析,计算出对应障碍物的灰度值;灰度阈值越精确,小车对于障碍物的识别将越灵敏,并能在合理的距离内识别到障碍物并停车。
**虽然如此,该算法仍不是非常精确。所以,这里给出的障碍物判断算法仅供参考,我们鼓励大家编写更高级的算法,实现更强大的功能。**
**虽然如此,但该算法仍不是非常精确。所以,这里给出的障碍物判断算法仅供参考,我们鼓励大家编写更高级的算法,实现更强大的功能。**
**实验完毕后,记得提交修改(命令行中-m后的字符串可自行确定以便在后续实验中继承lab4_3中所做的工作** **实验完毕后,记得提交修改(命令行中-m后的字符串可自行确定以便在后续实验中继承lab4_3中所做的工作**
``` ```
$ git commit -a -m "my work on lab4_3 is done." $ git commit -a -m "my work on lab5_3 is done."
```
<a name="lab5_3_ans"></a>
#### 答案解析
将`kernel/hostfs.c`文件中的:
```c
panic( "You need to call host's ioctl by frontend_syscall in lab5_3.\n" );
```
替换成:
```c
return frontend_syscall(HTIFSYS_ioctl, pf->kfd, request, (uint64)data, 0, 0, 0, 0);
``` ```
替换原因:
frontend_syscall函数在spike_interface/spike_utils.c中定义该函数会将其所有参数通过HTIF协议传给riscv-fesvr在PS端进行系统调用。注意fd参数是PKE内部的文件描述符不是该文件在PS端操作系统里对应的文件描述符所以需要先将fd通过spike_file_get转成spike_file_t对象然后获取该对象的kfd属性这个属性存储的值才是该文件在PS端操作系统里对应的文件描述符。

Loading…
Cancel
Save