(原创)构建基于aemb的sopc系统(三)--处理器仿真和软件相关笔记
下面构建基于AEMB的最小系统,先做仿真,后在DE2平台上实现。
在仿真之前,需先生成aemb的可执行代码,并转换成存储器初始化文件dump.vmem,这需要交叉编译工具链和可以将.srec文件转换成verilog vmem格式的srecord软件。Verilog VMEM格式16进制格式,它可以用$readmemh调用载入用于仿真。
在http://www.aeste.my/files网页上下载AEMB-ELF的编译工具链,mb-elf-2009-08-16.part1.rar和mb-elf-2009-08-16.part2.rar。解压出一个名称为mb的文件夹,把它拷贝到linux工作环境下。改变/mb/bin目录下所有可执行文件的属性
# cd mb/bin/
# chmod a+x ./*
Srecord可以在http://srecord.sourceforge.net/download.html网页上下载。
# wget http://srecord.sourceforge.net/srecord-1.55.tar.gz
解压编译安装。测试srec_cat命令是否可用。
下载aemb_latest.tar.gz,在linux工作环境下解压。
$ tar zxf aemb_latest.tar.gz
切换到/sw目录,并执行gccrom脚本
$ cd aemb/trunk/sw/
$ ./gccrom cc/testbench.cc
显示错误
mb-g++: error trying to exec 'cc1plus': execvp: No such file or directory
在xilinx的《Embedded System Tools Reference Guide》中讲到:The PowerPC and MicroBlaze processor GCC tools are constructed using open source GCC 4.1.1 sources.
在CentOS4.8中gcc是3.4.6的,本以为是gcc版本的原因,但事实上不是。
真正的原因是:mb-elf-2009-08-16.part1.rar和mb-elf-2009-08-16.part2.rar两个文件是在windows下解压的,然后拷贝到linux中。其中的文件权限发生了改变。解决这个问题,可以改变mb中相关文件的权限。本文中在linux下直接解压mb-elf-2009-08-16.part1.rar和mb-elf-2009-08-16.part2.rar。便没有发生这样的错误。
执行$ ./gccrom cc/testbench.cc,有句错误
cc/aemb/hook.hh:133: error: ‘PTIMISATION_REQUIRED’ does not name a type
打开cc/aemb/下的hook.hh文件
#ifndef __OPTIMIZE__
// The main programme needs to be compiled with optimisations turned
// on (at least -O1). If not, the MUTEX value will be written to the
// same RAM location, giving both threads the same value.
OPTIMISATION_REQUIRED OPTIMISATION_REQUIRED
#endif
在没有定义_OPTIMIZE_宏的情况下,如果没有在编译的时候没有打开编译优化选项(至少是-O1)就会报错。
执行
$ ./gccrom –Os cc/testbench.cc
显示
xgcc=0
dump=0
copy=0
srec=0
sha1=5287deea1841502ff27755bbe7a2701e
Gccrom将在同一目录下产生rom.dump文件,并在仿真目录下产生dump.vmem文件。The rom.dump file contains an assembly dump of the compiled code. This file will come in handy for debugging any problems with the core. The dump.vmem file contains a VMEM format memory file that can be read in by any Verilog simulator.
创建modelsim的仿真脚本文件。
编写vlog.args文件
+libext+.v
-vlog01compat
+acc
-y ./pll
-y ./ram
-y ./aemb
-y ./gpio
-y ./wb_conmax
-y ./mc
-y ./wb_ssram_if
-v altera_mf.v
-v 220model.v
-work ./work
//
// Test bench files
//
//aemb_sopc_tb.v
edk63.v
//
// RTL files
//
+incdir+../rtl
… …
… …
… …
编写sim.do文件
vlib ./work
vlog -f ./vlog.args
vsim -novopt work.aemb_sopc_tb -pli
add wave -radix hex /*
add wave -radix hex /aemb_sopc_tb/aemb_sopc_inst/aemb_sys_inst/wire_iwb_*
add wave -radix hex /aemb_sopc_tb/aemb_sopc_inst/aemb_sys_inst/wire_dwb_*
#add wave -radix hex /aemb_sopc_tb/aemb_sopc_inst/aemb_sys_inst/wire_ram0_*
#add wave -radix hex /aemb_sopc_tb/aemb_sopc_inst/aemb_sys_inst/wire_gpio_*
#add wave -radix hex /aemb_sopc_tb/aemb_sopc_inst/aemb_sys_inst/wire_uart_*
#add wave -radix hex /aemb_sopc_tb/aemb_sopc_inst/aemb_sys_inst/wire_ssram_*
#add wave -radix hex /aemb_sopc_tb/aemb_sopc_inst/aemb_sys_inst/wire_vga_*
#add wave -radix hex /aemb_sopc_tb/aemb_sopc_inst/aemb_sys_inst/vga_inst/wb_*
#add wave -radix hex /aemb_sopc_tb/aemb_sopc_inst/aemb_sys_inst/vga_inst/u1/*
#add wave -radix hex /aemb_sopc_tb/aemb_sopc_inst/aemb_sys_inst/vga_inst/wb_export_*
#add wave -radix hex /aemb_sopc_tb/aemb_sopc_inst/oVGA_*
#add wave -radix hex /aemb_sopc_tb/aemb_sopc_inst/aemb_sys_inst/
#add wave -radix hex /aemb_sopc_tb/aemb_sopc_inst/aemb_sys_inst/u_ram0/*
run 400000ns
编写random.v文件
定义一个randseed的值不然仿真有错误
仿真波形出来了,不懂什么意思。
修改edk63.v文件,
localparam AEMB_DWB = 32;
localparam AEMB_IWB = 32;
仿真结果
# AEMB2 32-bit Microprocessor Core Tests
#
# 1. Integer Arithmetic
#
# -PASS-
#
# 2. Integer Factorisation
#
# -PASS-
#
# 3. Floating Point Arithmetic
#
# -PASS-
#
# 4. Memory Allocation
#
# -PASS-
#
# 5. Hardware Interrupts
#
# -PASS-
#
# 6. Accellerator Link
#
# -PASS-
#
# 7. Hardware Exceptions
#
# -PASS-
#
#
# *** EXIT 0021f534 ***
# ** Note: $finish : ./verilog/edk63.v(212)
# Time: 2225460 ps Iteration: 0 Instance: /edk63
读一下edk63.v文件吧。
对aeMB读写数据的操作进行仿真
不明白的一部分代码
// SPECIAL PORTS
if (dwb_wre_o & dwb_stb_o & dwb_ack_i) begin
case ({dwb_adr_o,2'o0})
32'hFFFFFFD0: $displayh(dwb_dat_o);
32'hFFFFFFC0: $write("%c",dwb_dat_o[31:24]);
32'hFFFFFFE0: sys_int_i <= #1 !sys_int_i;
endcase // case ({dwb_adr_o,2'o0})
这是什么意思呢。
读一下testbench.cc文件,发现这些信息都是由iprintf函数打印出来的,找到它,那段代码就应该可以理解了。
在sw/目录下所有的文件中,只有simboard.hh这个文件中的outbyte函数引用了这个地址。
/*
I/O FUNCTIONS
*/
void outbyte(char c)
{
volatile char *COUT = (char *) 0xFFFFFFC0;
*COUT = c;
}
但它跟iprintf有什么关系呢。
在《MicroBlaze software Reference Guide》有关于outbyte的一个解释。
网上说iprintf是newlib C库中的函数
iprintf is a restricted version of printf: it has the same arguments and behavior, save that it cannot perform any floating-point formatting: the f, g, G, e, and F type specifiers are not recognized.
后记:怎样把UART配置成系统的standard output?
在or1200中,编译一个可在or1200上执行的C程序,除了C相关代码外,还需要链接描述文件(.ld文件,用于指定程序的存放位置和运行位置),汇编.S文件(初始化运行环境,跳转到C语言的main函数),和Makefile文件(编译脚本文件)。
那在aemb的参考代码中这些工程是怎样实现的呢?
gccrom,bash 脚本文件。
#!/bin/sh
# $Id: gccrom,v 1.18 2008-05-01 08:35:04 sybreon Exp $
# Compile using C pre-processor
ELFFILE="rom"
XILFLAGS="-mtune=v5.00 -mxl-soft-div -msoft-float -mxl-barrel-shift -mno-xl-soft-mul"
CXXFLAGS="-O0"
LNKFLAGS="-Wl,-defsym -Wl,_STACK_SIZE=0x2000 -Wl,-defsym -Wl,_HEAP_SIZE=0x2000"
LIBFLAGS=""
INCFLAGS="-Icc/"
mb-g++ $XILFLAGS $CXXFLAGS $LNKFLAGS $LIBFLAGS $INCFLAGS -specs=aemb.specs $@ -o $ELFFILE && \
echo "xgcc=$?" && \
# Create a text listing of the compiled code
mb-objdump -DSCz $ELFFILE > $ELFFILE.dump && \
echo "dump=$?" && \
# Convert the ELF file to an SREC file
mb-objcopy -O srec $ELFFILE $ELFFILE.srec && \
echo "copy=$?" && \
# Generate a Verilog VMEM file from the SREC file
srec_cat $ELFFILE.srec -o ../sim/dump.vmem -vmem 32 && \
echo "srec=$?" && \
# echo the checksum
MD5=$(sha1sum $ELFFILE | cut -c1-32) && \
echo "sha1=$MD5" && \
# Cleanup code
rm $ELFFILE.srec && rm $ELFFILE
$@ 全部传递给脚本的参数,相当于 $1 $2…$n。所以说./gccrom –Os cc/testbench.cc 哪行命令,把-Os通过$@传递给mb-g++,和通过修改CXXFLAGS的值效果是一样的。-Os enables all -O2 optimizations that do not typically increase code size.
脚本的执行步骤
Ø Compile using C pre-processor
Ø Create a text listing of the compiled code
Ø Convert the ELF file to an SREC file
Ø Generate a Verilog VMEM file from the SREC file
Ø echo the checksum
Ø Cleanup code
那么mb-g++在执行过程中究竟用到哪些库呢,以及mb-g++都完成了哪些操作呢?
《Embedded System Tools Reference Guide》--GNU Compiler Tools一章
Modelsim中打印出来的信息
现在学习一下aemb的软件构架。
Linker Options
Commonly Used Compiler Options
关于gccrom中的LNKFLAGS变量
LNKFLAGS="-Wl,-defsym -Wl,_STACK_SIZE=0x2000 -Wl,-defsym -Wl,_HEAP_SIZE=0x2000"
-defsym _STACK_SIZE=value
这两个是在一起用的:
The total memory allocated for the stack can be modified using this linker option. The variable _STACK_SIZE is the total space allocated for the stack. The _STACK_SIZE variable is given the default value of 100 words, or 400 bytes. If your program is expected to need more than 400 bytes for stack and heap combined, it is recommended that you increase the value of _STACK_SIZE using this option. The value is in bytes.
-defsym _HEAP_SIZE=value
The total memory allocated for the heap can be controlled by the value given to the variable _HEAP_SIZE. The default value of _HEAP_SIZE is zero. Dynamic memory allocation routines use the heap. If your program uses the heap in this fashion, then you must provide a reasonable value for _HEAP_SIZE
-I Directory Name
This option searches for header files in the /<dir_name> directory before searching the header files in the standard path.
Memory Layout
The MicroBlaze and PowerPC processors use 32-bit logical addresses and can address any
memory in the system in the range 0x0 to 0xFFFFFFFF. This address range can be
categorized into the following types:
Ø Reserved memory
Ø I/O memory
User and Program Memory
怎样实现memory map,也得靠“Linker Scripts”。
In special cases, you might want to partition the various sections of your ELF file across different memories. This is done using the linker command language (refer to the “Linker Scripts,” page 125 for details). The following are some situations in which you might want to change the memory map of your executable:
− When partitioning large code segments across multiple smaller memories
− Remapping frequently executed sections to fast memories
− Mapping read-only segments to non-volatile flash memories
Object-File Sections
Linker Scripts
You do not need a linker script if you do not want to change the default contiguous assignment of program contents to memory. There is a default linker script provided with the linker that places section contents contiguously.
在aemb网站上给出的aemb的编译工具链中,默认的编译脚本文件存放在
mb/microblaze-xilinx-elf/lib/ldscripts 目录中。
-n –N –r –Ur分别代表什么意思?后面有介绍。
读了一下elf32microblaze.x文件,有些语法读不懂,而且只看到SECTIONS段,没有看到MEMORY段。
To use a linker script, provide it on the GCC command line. Use the command line option -T <script> for the compiler, as described below:
compiler -T <linker_script> <Other Options and Input Files>
If the linker is executed on its own, include the linker script as follows:
linker -T <linker_script> <Other Options and Input Files>
MicroBlaze Compiler Usage and Options
When compiling with the MicroBlaze compiler, the pre-processor provides the definition __MICROBLAZE__ automatically. You can use this definition in any conditional code.
在aemb/trunk/sw/cc/aemb的core.hh文件中有这样一段代码
#ifdef __MICROBLAZE__
#include "aemb/msr.hh"
#include "aemb/stack.hh"
#include "aemb/heap.hh"
#include "aemb/thread.hh"
#include "aemb/hook.hh"
#include "aemb/stdio.hh"
#include "aemb/semaphore.hh"
#endif
怪不得在整个sw目录中也没找到有声明 __MICROBLAZE__的地方。
mb-gcc的参数
XILFLAGS="-mtune=v5.00 -mxl-soft-div -msoft-float -mxl-barrel-shift -mno-xl-soft-mul"
重新理解一下XILFLAGS中选项的含义。
-mxl-soft-div
This option tells the compiler that there is no hardware divide unit on the target
MicroBlaze hardware.默认选项
-msoft-float
This option tells the compiler to use software emulation for floating point arithmetic.默认选项
-mxl-barrel-shift
The MicroBlaze processor can be configured to be built with a barrel shifter. In order to use
the barrel shift feature of the processor, use the option -mxl-barrel-shift.
默认情况下并没有这个选项,编译器用加和乘操作对操作数进行移位。使能这个选项可以显著加速程序运行,特别是当用浮点库的时候。
-mno-xl-soft-mul
This option tells the compiler that there is no hardware multiplier unit on MicroBlaze, so
every 32-bit multiply operation is replaced by a call to the software emulation
routine__mulsi3. This option is the default.
-mtune 没找到
还有一个-spec选项不知做何用。
MicroBlaze Linker Script Sections
在默认情形下,连接器可以为section分配任意位置的存储区域。你也可以用MEMORY命令定义存储区域,并通过输出section描述的> REGION属性显示地将该输出section限定于某块存储区域,当存储区域大小不能满足要求时,连接器会报告该错误。
用到的命令:
OUTPUT_FORMAT(BFDNAME) : 设置输出文件使用的BFD格式,同ld选项-o format BFDNAME, 不过ld选项优先级更高.
SEARCH_DIR(PATH) :定义搜索路径,同ld的-L选项, 不过由-L指定的路径要比它定义的优先被搜索。
ENTRY(SYMBOL) : 将符号SYMBOL的值设置成入口地址。入口地址(entry point): 进程执行的第一条用户空间的指令在进程地址空间的地址)
ld有多种方法设置进程入口地址, 按一下顺序: (编号越前, 优先级越高)
1. ld命令行的-e选项
2. 连接脚本的ENTRY(SYMBOL)命令
3. 如果定义了start符号, 使用start符号值
4. 如果存在.text section, 使用.text section的第一字节的位置值
5. 使用值0
SECTIONS :命令告诉ld如何把输入文件的sections映射到输出文件的各个section: 如何将输入section合为输出section; 如何把输出section放入程序地址空间(VMA)和进程地址空间(LMA).
KEEP :在连接命令行内使用了选项--gc-sections后,连接器可能将某些它认为没用的section过滤掉,此时就有必要强制连接器保留一些特定的 section,可用KEEP()关键字达此目的。如KEEP(*(.text))或KEEP(SORT(*)(.text))
PROVIDE :关键字该关键字用于定义这类符号:在目标文件内被引用,但没有在任何目标文件内被定义的符号。
ALIGN(EXP) :返回定位符'.'的修调值,对齐后的值。
. 是一个特殊的符号,它是定位器,一个位置指针,指向程序地址空间内的某位置(或某section内的偏移,如果它在SECTIONS命令内的某section描述内),该符号只能在SECTIONS命令内使用。
后记:查一下《Using ld –The GNU Linker》
Tips for Writing or Customizing Linker Scripts
The following points must be kept in mind when writing or customizing your own linker script:
• Ensure that the different vector sections are assigned to the appropriate memories as defined by the MicroBlaze hardware.
• Allocate space in the .bss section for stack and heap. Set the _stack variable to the location after _STACK_SIZE locations of this area, and the _heap_start variable to the next location after the _STACK_SIZE location. Because the stack and heap need not be initialized for hardware as well as simulation, define the _bss_end variable after the .bss and COMMON definitions. Note, however, that the .bss section boundary does not include either stack or heap.
• Ensure that the variables _SDATA_START__ , _SDATA_END__, SDATA2_START, _SDATA2_END__, _SBSS2_START__ , _SBSS2_END__, _bss_start, _bss_end, _sbss_start, and _sbss_end are defined to the beginning and end of the sections sdata, sdata2, sbss2, bss, and sbss respectively.
• ANSI C requires that all uninitialized memory be initialized to startup (not required for stack and heap). The standard CRT that is provided assumes a single .bss section that is initialized to zero. If there are multiple .bss sections, this CRT will not work. You should write your own CRT that initializes all the .bss sections.
Startup Files
大概是reset.S的作用吧,这是带C库的启动过程。
For MicroBlaze, there are two distinct stages of C runtime initialization. The first stage is primarily responsible for setting up vectors, after which it invokes the second stage initialization. It also provides exit stubs based on the different application modes.
First Stage Initialization Files
Ø crt0.o 用于程序单独运行时,不使用bootloader和debugging stub,本文中用到。
作用:This CRT populates the reset, interrupt, exception, and hardware exception vectors and invokes the second stage startup routine _crtinit. On returning from _crtinit, it ends the program by infinitely looping in the _exit label.
Ø crt1.o 用于程序使用debugging stub时
Ø crt2.o 用于程序使用bootloader时
Ø crt3.o 用于当程序中只有reset向量时,其它向量都不予处理,压缩了代码空间。
Second Stage Initialization Files
在C语言中规定,所有的全局和静态变量必须初始化成0。这是上述CRT文件的一个基本的功能。之后,_crtinit程序被调用了,它用于初始化程序中的.bss段,而且它也用于调用main程序。在调用main函数之前,它也调用了一些其它的初始化函数。在下列文件中都提供了_crtinit程序。
Ø crtinit.o
This is the default second stage C startup file. This startup file performs the following
steps:
1. Clears the .bss section to zero.
2. Invokes _program_init.
3. Invokes “constructor” functions (_init).
4. Sets up the arguments for main and invokes main.
5. Invokes “destructor” functions (_fini).
6. Invokes _program_clean and returns.
Ø pgcrtinit.o
This second stage startup file is used during profiling.
Ø sim-crtinit.o
This second-stage startup file is used when the -mno-clearbss switch is used in the compiler.
Ø sim-pgcrtinit.o
This second stage startup file is used during profiling in conjunction with the -mno-clearbss switch.
Modifying Startup Files
The initialization files are distributed in both pre-compiled and source form with EDK.
Pre-compiled 可在/mb/microblaze-xilinx-elf/lib目录下找到。
Source没有找到!
To fulfill a custom startup file requirement, you can take the files from the source area and include them as a part of your application sources. Alternatively, you can assemble the files into .o files and place them in a common area. To refer to the newly created object files instead of the standard files, use the -B directory -name command-line option while invoking mb-gcc.
阻止默认的setup files发生作用,在编译时用-nostartfiles选项。
Note: The miscellaneous compiler standard CRT files, such as crti.o, and crtbegin.o, are not provided with source code. They are available in the installation to be used as is. You might need to bring them in on your final link command.
混合编程时C++用到的一些初始化代码是没有源码的,只能用现成的,在链接是设定。
昨天基本上没写什么笔记。忙了两件其它的事。
那么crt0.s, crt1.s, crt2.s, crt3.s这几种启动选项,怎样选择用哪种方式启动呢?在EDK的安装目录下找到这几个文件,看看它们的说明。
crt0.s
# Default C run-time initialization for MicroBlaze standalone
# executables (compiled with -xl-mode-executable or no switches)
当编译时用到-xl-mode-executable选项时,会用crt0.s启动,默认时也按这种方式启动。
crt1.s
# Default C run-time initialization for MicroBlaze standalone
# executables compiled with -xl-mode-xmdstub for
# debugging with XMDSTUB
当编译时用到-xl-mode-xmdstub选项时,会用crt1.s启动。
crt2.s
# Default C run-time initialization for MicroBlaze standalone
# executables that are boot-strapped (-xl-mode-bootstrap)
当编译时用到-xl-mode-bootstrap选项时,会用crt2.s启动。
crt3.s
# C run-time for MicroBlaze applications that do not want vectors (compiled
# with -xl-mode-novectors)
当编译时用到-xl-mode-novectors选项时,会用crt3.s启动。
在第二阶段的初始化操作时,各个crt程序,又分别调用不同的crtinit程序。
Command Line Arguments
MicroBlaze programs cannot take command-line arguments. The command-line arguments argc and argv are initialized to 0 by the C runtime routines.
//现在仔细读一下crt0.s中的代码吧。