【集创赛】基于arm处理器的SOC设计【2】

2021-04-15 11:33:40 浏览数 (1)

第三部分 最简SOC系统搭建

2.1 最简系统搭建

仅需要 M3 CPU 内核,总线解码模块,RAM模块,ROM模块,简单的外设模块,在配合软件驱动,一个最简的M3 SOC 系统即可运行。这里演示如何在资源有限的硬件平台上,仅利用其中的CPU部分,搭建最简单的系统。在m3designstartlogicalcortexm3integration_ds_obsverilog文件夹中有两个VerilogHDL文件:cortexm3ds_logic.v和CORTEXM3INTEGRATIONDS.v。其中前者是经过扰乱的Cortex-M3CPU代码,后者是对外的接口文件。

通过使用vivado的块图设计(Blcok design)工具,预先设计好各个子模块和接口连接关系后,可以以图形化的方式来快捷方便地搭建所需的定制化SOC系统。

2.2 块图设计

我们团队利用CMSOK中提供的资源,以及自己编写所需的模块,设计了如下的子IP模块,用于在块图设计(Blcok design)中使用,各个子模块的功能和接口在下面详细介绍,用于搭建的最简的SOC系统如下所示,利用块图设计,不用一分钟便可以搭建完成,十分方便快捷。

图形化界面搭建完成后,使用vivado自动化工具,即可自动完成模块例化,端口连接的工作。

由于没有采用Xilinx官方IP,故生成的代码是纯Verilog代码,可以任意移植到任何一个FPGA平台上。

利用vivado的块图设计,大大减少了在SOC总线连接时使用的时间,由于采用标准化总线连接,也大大降低了由于失误而造成的总线连接错误。

在上面的最简系统中,外设模块是一个LED灯和按键(KEY或SW)的驱动模块。位宽均设置为12位,CPU通过AHB_lite总线,来读取按键的值,向LED写入数据。

该模块挂载在了地址为 0x4100_0000 ---- 0x410F_FFFF的地址空间。LED和KEY(SW)的偏移地址均为0x00。输入的按键值通过两级寄存器进行缓存,消除毛刺。

2.3 软件测试

在软件模块,先定义AHB_mux 的各个子端口的地址空间。定义LED,KEY(SW)的基地址,定义LED,KEY的读写指针。

稍后,便可以在主程序中来控制按键和LED了。测试代码如下,通过不断读取按键(SW)的值,并写入到LED中,便可以实现LED灯随按键的状态而改变。

稍后,建立FPGA工程,导入vivado生成的工程代码,分配引脚,约束时序,通过综合,布局布线,生成比特文件后下载到FPGA开发板上。

然后,建立软件工程,在keil软件上编写头文件,定义地址分布,驱动程序后,编译,利用keil软件,通过板载的DAP,将生成的软件代码下载到板卡上,软件自动运行,即可看到LED灯随着按键的改变而改变,最简SOC系统即测试成功。

4 仿真结果

通过在modelsim中仿真,也验证了我们设计的正确性。下面仿真图为LED灯随按键KEY值变化的仿真时序图。

第四部分 子模块设计

1,接口设计

Vivado工具里的接口是一个非常方便地概念,使用vivado的接口工具,可以将一个复杂的总线里的多条信号线简化为一个总线接口,在块图设计时,只需要将对应总线连接,vivado工具就会自动生成各个子信号之间的连接。十分方便快捷,且避免了手动连线造成的错误。

在该项目中,定制了如下的总线模块:

  • AHB_lite:ahb_lite总线接口
  • APB:apb总线接口
  • CMSIS_DAP:cmsis_
  • dap调试接口 UART :串口总线
  • USER_CAMERA:摄像头数据总线
  • fpga_sram:sram内存访问端口。

2,IP设计

在vivado工具中,除了官方的IP外,也可以将用户自定义的代码打包为IP,在块图设计和源码级设计中,均可以同官方IP一样方便使用。在我们得到项目中,也将各个子模块打包为了IP文件,实现了代码在IP级别的重用。Vivado的IP工具和接口工具,大大地提高了FPGA设计的便利性。

在该项目中,除了将一些CMSDK中的相关模块打包外,还自定义了许多其他的模块。具体的模块如下所示:

Cmsdk开头的是ARM Cortex DesignStart提供的模块,user开头的是自定义的模块。下面介绍下常用的几个子模块。

3,user_CortexM3_cpu

该IP将arm m3内核进行封装,将cmsis_dap和中断接口引出,对AHB_ICODE 和 AHB_DCODE 总线进行仲裁后作为一路 AHB_CODE引出,用于连接ROM模块,将AHB_SYSTEM总线直接引出,用于连接外设模块。生成的IP块图如下:

4,user_apb_mux

该模块用于AHB 总线的解码和多路转换。该模块的输入端连接至CPU的ahb_system接口上,输出端口是经过地址译码的10个ahb 端口,用于连接ram和其他外设模块。各个模块的地址分配在此模块中完成。该模块也负责ahb总线的多路转换,用于将从各个子模块中读回的数据根据地址进行分配,传输至master端。

各个子模块的地址分配如下:

5,cmsdk_fpga_sram,cmsdk_ahb_to_sram

这两个模块要配合使用,cmsdk_ahb_to_sram用于将ahb协议转化为sram的传输协议,而cmsdk_fpga_sram中例化了FPGA片上的存储资源,用于作为SOC系统的RAM和ROM使用。cmsdk_fpga_sram的空间大小可以通过vivado的GUI界面来配置,数据位宽为32bit。

6,cmsdk_ahb_to_apb

该模块用于实现 ahb 协议到 apb 协议的转换,用于实现CPU控制低速apb外设的目的。

7,user_apb_mux

该模块用于实现apb地址的解码和多路转换。该模块的输入端口连接到cmsdk_ahb_to_apb模块,输出端口为16个apb子端口,可以连接16个ahb外部设备。要注意的是,apb_mux模块只对地址的低16位进行了译码,连接到子端口的外部设备的地址等于 ahb_to_apb 模块挂载在ahb_mux上的基地址 apb_mux 模块的偏移地址。地址分配如下:

8,其他模块

其他模块如数码管驱动模块,按键读取模块,apb_uart模块,apb_timer模块,VGA显示模块,矩阵键盘译码模块,ahb_gpio模块等基础模块便不一一介绍,如需了解模块详情,阅读源代码即可。

第五部分 软件设计

1,设计思路

搭建好硬件系统后,需要为各个外设模块编写软件驱动程序。具体的原理即为,控制为外设模块的寄存器被分配唯一的一个地址上,通过CPU向该地址写数据,即可对该外设进行配置,通过CPU向该地址写数据,即可得到该外设的状态。

在C语言中,利用指针即可对指定地址进行读写操作。将指针指向一个地址,向该指针写数据即可实现向分配到该地址的寄存器写入数据,读取该指针即可读取指向该地址的寄存器的值。更加详细的内容即为C语言的基础知识,在此不做过多讲解,可从任意一本C语言的教材数中找到。

2,地址分配

如下所示,先定义各个总线子模块的基地址,然后定义各个外设模块的基地址。如图中所示,先定义10个AHB从端口的基地址,然后定义APB总线的基地址,由于在设计中APB总线挂载在AHB9从端口上,故APB0模块的基地址即为AHB_MUX9的基地址。

接下来定义各个APB从端口的偏移地址,挂载在每个APB从端口的外设的绝对地址等于APB模块的基地址 APB从端口的偏移地址。

LED灯和按键(KEY或SW)的驱动模块挂载在AHB总线的端口1上,向该模块的偏移地址为0 的寄存器写数据,即向LED灯端口写入数据,读取偏移地址为0的寄存器数据,即得到按键(SW或KEY)的输入数据。

3,驱动编写

外设驱动的设计思路便是。利用地址映射的思想,通过指针来实现对寄存器的访问,通过对特定寄存器的读写,实现特定功能。

更加详细的说明这个报告里显得繁琐,也不一一进行说明了。这些属于嵌入式的基本知识,在任何关于嵌入式设计的资料中都会涉及到。

对于自定义的较为简单的外设模块,直接采用指针访问的形式。

如下图的LED灯和按键驱动模块,设计较为简单,只有一个有效的寄存器,则可通过指针的方式直接访问该寄存器。语句 ((unsigned int) AHB_LED_BASE) 即定义了一个指向 AHB_LED_BASE地址的指针,再利用 #define 语句将该指针重定义到AHB_LED 标识符,则即可利用 标识符 AHB_LED 来访问该段地址空间。

0 人点赞