时间:2025-02-25 来源:FPGA_UCY 关于我们 0
软硬件开发层次
设计总是从硬件到软件,硬件在底层,软件在顶层。
用FPGA配置成SOC之后,他还只是个空壳子,SOC的工作依赖一条条指令,生成指令是软件干的事,你需要用C或汇编写程序,写好的程序通过编译生成汇编代码,汇编代码通过arm指令集的对应转换,生成机器码。将机器码存在SOC的存储器,这时候SOC就能根据指令执行了。
SOC开发流程
1)我们用描述完一个soc之后,要用编译,综合和实现,用硬件实现soc功能;
2)keil将c或汇编转换为机器码,仿真过程中将机器码作为描述的ram的初始化内容,就可以进行仿真了;
3)仿真工具可以选用自带仿真,也可使用。仿真后看到预期波形后,就可以将机器码下载到soc中,soc就可以执行你所编写的程序,开发板可以看到和波形预期的结果。
认识总线
认识总线的过程很麻烦,很痛苦,但要设计soc必须要和总线打交道,硬着头皮看吧,假设大家已经了解总线知识了。
Map
我们挂在总线上的外设,在处理器核看来,只是 map上一块连续区域,例如下图所示,其地址编码为 -,那么处理器核通过 AHB 总线发出的任何一次总线操作,只要地址总线上的值在 - 之间,都认为是处理器核在向 提出总线操作。
总线扩展
当SOC有多个外设,单处理器核只有一个总线接口的情况下,就需要有总线扩展模块,使处理器核能访问多个外设。下图是单主机总线扩展模块,主要由两部分组成: 与 Slave MUX。
的作用是对地址总线进行译码,生成对应的外设的选择信号,同样以 外设为例,由于其地址编码为 -,那么只要地址总线HADDR 的高 16bit 为 时,地址总线的值必定处于 的地址编码区域中,则判定为处理器核对 提起的一次总线操作,因此 对应的选择信号HSEL 将会被置位有效;
需要注意的是, 对于每个外设, 利用地址总线生成选择信号 HSEL 所需要的宽度是不同的。例如 的地址编码为 -,其有效长度为-,那么 对于的选择信号()需要对 HADDR 的高 16bit 进行译码;而 的地址编码为 0f,其有效长度为 0x0-0xf,因此 对应的选择信号需要对 HADDR 的高28bit 进行译码。
Slave MUX 的作用则是选中slave返回的数据,即通过每个外设的选择信号,对所有外设返回的读取数据()、响应信号(HRESP)以及反馈信号()进行选择,保证返回给 端口的数据来自于当前总线操作的目标外设。 例如当前总线操作是读取 的数据,那么所有外设的选择信号中只有 对应的选择信号为‘1’,其他所有选择信号为‘0’,那么 Slave MUX 则根据这些选择信号选中 返回的数据,保证处理器核能够正确读取。
每增加一个外设,只需要将外设Slave接口与上图 接口相连接,再在 模块中添加对应的译码模块生成选择信号即可。
搭建SOC实例
本实验的目的是认识总线、 RAM、汇编与调试,走完soc设计流程;工程在我的主页免费下载;
实验要求:
在裸核+总线扩展模块的基础上,添加 Block RAM 与 BRAM总线接口模块,并将其接入总线扩展模块预留的 接口下,然后完成 模块中对 RAM 的译码,最终经过 编译、综合、实现,下载至 FPGA 中。完成硬件设计后,本节实验还需要学习 keil 的使用,以及编写简单的汇编代码,并通过调试器将代码下载至 SoC 中,利用 keil 进行调试运行。下图是本实验搭建的soc
1.硬件部分
1.找到.v” 文件,修改 模块代码。
A. 在端口参数部分,令 使能有效。(将 = 0 改为 1)
B. 生成选择信号,根据 map 来修改, 的总线编码为 -,因为对于一次总线操作,只要地址总线的高 16 位为 0,则 认为这是一次对指令存储器的操作。(将 = 1’b0 改为 = (HADDR[31:16] == 16’h0000) ? : 1’b0)
2.在顶层文件中将 总线接口与总线扩展模块连接
在.v 中,已经完成了处理器核、总线扩展模块、 总线接口模块以及 Block RAM 模块的例化(调用这些模块,将这些模块添加至设计里面) , 但未在总线扩展模块接口部分连接 总线接口。方法如下
将这段代码
/* Connect to Interconnect Port 0 */
.HCLK (clk),
.HRESETn (cpuresetn),
.HSEL (/*Port 0*/),
.HADDR (/*Port 0*/),
.HPROT (/*Port 0*/),
.HSIZE (/*Port 0*/),
.HTRANS (/*Port 0*/),
.HWDATA (/*Port 0*/),
.HWRITE (/*Port 0*/),
.HRDATA (/*Port 0*/),
.HREADY (/*Port 0*/),
.HREADYOUT (/*Port 0*/),
.HRESP (/*Port 0*/),
.BRAM_ADDR (RAMCODE_ADDR),
.BRAM_RDATA (RAMCODE_RDATA),
.BRAM_WDATA (RAMCODE_WDATA),
.BRAM_WRITE (RAMCODE_WRITE)
改为如下代码
/* Connect to Interconnect Port 0 */
.HCLK (clk),
.HRESETn (cpuresetn),
.HSEL (HSEL_P0),
.HADDR (HADDR_P0),
.HPROT (HPROT_P0),
.HSIZE (HSIZE_P0),
.HTRANS (HTRANS_P0),
.HWDATA (HWDATA_P0),
.HWRITE (HWRITE_P0),
.HRDATA (HRDATA_P0),
.HREADY (HREADY_P0),
.HREADYOUT (HREADYOUT_P0),
.HRESP (HRESP_P0),
.BRAM_ADDR (RAMCODE_ADDR),
.BRAM_RDATA (RAMCODE_RDATA),
.BRAM_WDATA (RAMCODE_WDATA),
.BRAM_WRITE (RAMCODE_WRITE)
硬件部分已改完
2.汇编、启动与 Keil 工具
软件部分只需修改启动代码“.s“
1)打开 Keil, 点击左上角 菜单,选择 new ,在“keil” 文件夹下新建名为 code 的工程,在第一个弹框初并选择
2.在左侧 导航栏中,展开 ,右键点击 Group 1,在工程中添加之前编写的汇编文件
3.在左边导航栏处右键点击 1,选择 ,对 栏进行配置。此步骤为将片上起始地址为 ,大小为 的 作为ROM, Keil 将会通过调试器把程序下载到这一段存储器中。
4.在 栏处修改输出文件夹,点击 for ,将输出文件地址从 改为上一级文件夹地址,及工程所在文件夹地址
5.勾选 Run #1,并在后面添加" -cvf .\code.axf --vhx --32x1 -o code.hex"
勾选 Run #2,并在后面添加:“ -cvf .\code.axf -o code.txt”
6.在 处勾选 Use from 以及 Don’
7. 在 Debug 处取消勾选 Load at , 选择 CMSIS- 并在工程目录下新建名为 code.ini 的启动脚本文件(在keil文件夹)
8.进入 ,选择 Flash 栏目,由于我们没有 Flash,所以选择 Do not Erase,并且取消勾选 以及
9.在 出取消勾选
10.在“keil/.s” 文件中,实现 ARM 汇编编写计数,使得 R0 从 0 计数到 4 后重新开始计数,循环往复。在启动代码中加入下面这段程序
;Inset a loop algorithm there;
MOVS R1, #4
Clear MOVS R0, #0
Adder ADDS R0, R0, #1
CMP R0, R1
BEQ Clear
BNE Adder
11.编译
12.去,把生成的hex文件路径添加到RAM中,然后编译,生成比特流文件,然后到keil里调试。由于接下来这些步骤本人主页都可找得到。不加以赘述。
13.最终的实验现象:在红框 1 处为 CPU 内部寄存器的值,能够实时显示每一步寄存器值的变化,而在红框2 处为当前执行的汇编代码以及代码地址,在红框 3 处显示的是当前执行的源代码,这里的源代码既可以是汇编代码也可以是 C 语言代码。根据之前汇编语言编写的循环计数,在红框 1 处的 R1 的值保持为 4,而 R0 则应该在 0-4 之间循环计数。至此我们完成了最小 SoC 的设计,完成 AHB 总线扩展编写了 BRAM 以及 RAM 总线接口,编写了汇编代码并成功仿真与调试, SoC 的整个设计流程大致如此。