硬件环境需要我们自己手动将系统需要的外设添加到软核上,那么首先搭一个最简单的SOC,想想我们需要点什么?
软核or1200不用说了,上了CPU必须是要跑程序的,RAM是必须的,要下载程序代码,下载器是必须的,要偷窥下程序运行信息,上个串口可以吧,CPU和外设之前要使用一种互联结构,总结一下就是:CPU、RAM、UART,BUS......
那骚年们,去opencores找齐下面的源码包吧:
BUS:wb_conbus_latest.tar.gz或wb_conmax_latest.tar.gz
Download:adv_debug_sys_latest.tar.gz
UART:uart16550_latest.tar.gz
SDRAM:sdr_ctrl_latest.tar.gz
CPU:or1200-latest.rar.gz
CPU的源码的话,有时间有条件的话用SVN工具把or1200整个工程直接checkout出来吧,以前checkout过一次花了3个小时,想省事的话就单单下or1200的源码包就可以了,到此准备完毕。
把源码包都解压出来了,好,我们首先看看书《CPU源代码分析与芯片设计及Linux移植》的第三章关于wishbone总线的相关资料,第一次接触不用太深究先,先弄明白总线的接口和设备的wishbone接口怎么对应起来接线就可以。
然后在看看上面我们解压出来的源码包的顶层文件,都有对应于wishbone总线的接口,比如说兼容16550的UART顶层文件uart_top.v的Wishbone信号:
wb_rst_i, wb_adr_i, wb_dat_i, wb_dat_o,wb_we_i, wb_stb_i, wb_cyc_i, wb_ack_o, wb_sel_i,具体含义参考《CPU源代码分析与芯片设计及Linux移植》
再如or1200_top.v的Wishbone信号:
可见or1200有两组wishbone信号,一组用于CPU读写指令,一组用于读写数据
第一步:我们就将CPU和外设的wishbone信号全都和wb_conbus_latest.tar.gz总线全部连接起来,无非就是例化每个顶层文件,然后把对应的信号端口连到BUS上,具体例化连接代码看附件吧,代码注释有详细说明。
第二步:就是绑定FPGA板子的复位和时钟信号,比如我用的板子是Altera的片子,就在Quartus中例化两个PLL,一个用于CPU和wishbone连接的外设的时钟,另一个专门用于SDRAM控制器的时钟。复位只选择复位SDRAM控制器的复位,然后让SDRAM由复位状态到初始化状态完毕输出完毕信号,然后用这个信号作为所有外设和CPU的复位参考信号,具体例化连接代码看附件吧,代码注释有详细说明。
第三步:小心检查各个外设所绑定的芯片与电路板连接的管脚,我在连接的过程经常出错,最后自己敲一个绑管脚的tcl脚本吧。
第四步:用自己FPGA芯片的软件综合一下大概看看时序妥不妥,目前我们不去做时序约束,玩玩先,以后有时间才去弄,对吧?
在用软件synthesize之前,还要修改一下or1200-latest.rar.gz源码下的or1200_defines.v文件。
结果就是把CPU内的Cache和MMU全部关闭,实现除法指令,把CPU的复位地址设置为0x100,CPU内的可编程中断设置为20个
最后说说附件的程序代码怎么用吧,上面讲到的简单的SOC只需要几部分源码。
先看目录结构吧
adv_debug_sys:or1200的调试接口源码
or1200:CPU的源码
sdr_ctrl:SDRAM控制器的源码
wb_switch_b3:互联总线的源码
Instances:CPU和外设的例化文件,需要到的有oc_advance_dubug.txt、oc_or1200.txt、oc_wb_switch_b3.txt、oc_sdram.txt、oc_uart.txt至于altera_pll.txt例化的PLL,在重新综合到自己的工程前要根据自己的开发板使用的软件环境重新例化。
or1200_basic_soc_defines.v:SOC自定义参数
or1200_basic_soc_top.v:SOC顶层文件
需要修改有or1200_basic_soc_defines.v、or1200_basic_soc_top.v、oc_wb_switch_b3.txt,并且altera_pll.txt的锁相环要根据自己的软件重新配置
现在,假设我要重新建立一个工程,例如我使用的环境是QuartusII9.1
第一步:建立一个名为or1200_basic_soc文件夹,然后将附件解压的rtl复制入or1200_basic_soc,在QuartusII中新建一个or1200_basic_soc工程
Next,将所有rtl文件夹内的源码全部添加入工程中
继续Next,选择自己板子上所使用的器件
最后Finish
第二步:根据自己软件使用环境例化PLL或DLL
首先例化altera_pll,用于CPU、BUS、UART、DEBUG逻辑的时钟,
输入时钟为50M
输出时钟根据具体情况选择,10M、15M、20M、30M、40M······这里我根据板子的速度选择为40M。
同样的,我们把c0,c1配置成40M输出即可,配置完成就Finish吧。
接着例化altera_ddr_pll,同样配置输入50M,照搬吧,输出第一个clk c0选择50M,其实不是我想选,板子速度上不去了,太恶心了。
然后选择clk c1同样选择50M,在clock phase shift设置7.5ns的相位延时。
至于为什么呢?因为这里clk c0是输出到FPGA内SDRAM控制器的时钟,clk c1是输出到板子上SDRAM芯片的时钟,懂的朋友们就不需要吐槽我了,还没懂的朋友看看《终极内存技术指南》这篇文字或者参考下特权同学的《深入浅出玩转FPGA》,好,配置完成直接Finish。
至于延时的计算,sdr_ctrl内附的文档附录有说明
比如我的50M时钟周期为20ns,所以相移时间(20-2-2.5)/2大约7.5ns
第三步:修改附件源码rtl目录中instances文件夹的altera_pll.txt
例化altera_pll,inclk0是板子的时钟输入,c0是输出到外设逻辑的时钟端口,c1是输出到or1200的时钟端口,locked是PLL时钟有效时的锁定信号
例化altera_ddr_pll,同样inclk0是板子的时钟输入,因为我的板子上又两个50M的有源时钟,所以可以分开给PLL供两个时钟信号,如果板子上只有一个的话,altera_pll和altera_ddr_pll就共用一个时钟输入信号。
c0是输出到SDRAM控制器的时钟,c1输出到板子上SDRAM芯片的时钟管脚,说明一下,global_reset_request连接到SDRAM控制器的sdr_init_done信号,表示的是SDRAM控制器是否初始化完成,因为SDRAM控制器上电时需要对芯片进行一系列复杂的初始化,具体参照《终极内存技术指南》
最后的wb_rst_pad_i信号是前面提过的全局复位信号,具体分析是这样,由于PLL没锁定之前一直为低,并且SDRAM控制器没对片外芯片初始化好之前global_reset_request一直为低,所以wb_rst_pad_i在altera_pll、altera_ddr_pll、SDRAM控制器任意一个没初始化好之前保持为高,输出到CPU和外设的复位引脚,由于CPU、外设、总线的默认是高电平复位的,所以在altera_pll、altera_ddr_pll、SDRAM控制器没准备好之前都一直处在复位状态。
还有一点可能需要修改的就是oc_sdram.txt这个文件,在instances目录下
.cfg_sdr_width:SDRAM芯片的位宽,我板子的是32bit
.cfg_colbits:列地址位数,我板子的是8根地址线
具体地址线和位宽参考板子的芯片资料和板子原理图,我板子的是K4S641632K的1M x 16Bit x4Banks SDRAM
这些参数都是根据SDRAM芯片设置的,具体参考sdr_ctrl_latest.tar.gz源码包下doc文件夹内的关于SDRAM控制器的说明文档《sdram_controller_specs.pdf》
这些参数都是很保守的参数配置,一般不会出现什么问题
其他不作任何修改。
忘记一个步骤了,还要写一个tcl脚本去把顶层文件的端口绑定到对应板子上的时钟、复位、SDRAM、UART上,当然,你也可以选择手动去绑······
在QuartusII中Synthesis&Fitter&Assembler吧,如果板子资源不足的话,尝试修改or1200_defines.v去关闭不必要的选项,不过查找起来比较麻烦,基本上8W门的芯片是够用的。稍微看看时序,唉,40M多一点点,没做约束基本够用了,玩着先吧。
到现在,SOC上有CPU、RAM、Debug、UART、BUS。现在可以在这个SOC上面跑我们第一个程序啦,接下去我们开始写第一个简单的程序。