|
|
 |
FPGA design from scratch. Part 23
Simulating program execution in the MicroBlaze processor
The best way to verify an embedded system incorporating a microprocessor is to simulate the program execution. The testbench will be very simple, we only have to provide the system clock, load a program into the instruction memory, generate a reset and off we go. We will use this verification strategy when we build our simulation testbench.
Verification strategy
When we build a system of proven IP blocks we are not going to verify the operation of these IP blocks. Instead we will verify the connectivity, are all our peripherals connected and can we read and write to registers in the IPs. For this task a program running in the MicroBlaze processor is a perfect solution. We can observe all busses and we can use the Simvision waveform viewer to watch signals and timing relations. In theory this is very simple but in front of your computer it is a lot of work to set everything up. Let's go through to whole process.
Verification flow
- Write a small c-program or use machine code from start.
- Compile the program using gcc and generate machine code in ASCII format.
- Load the machine code to the MicroBlaze instruction memory.
- Start the simulation and generate system clock and a reset.
- The MicroBlaze processor will execute the program and write the result to the data memory.
- Read the result and compare it to expected data.
Generating a new MicroBlaze instruction and data memory modelThe VHDL model generated by platgen (see Part 17) is not suitable for our simulation setup. We must be able to load and dump data to/from the memory array in the simulation model. For that reason we need a behavioral model. We will use Coregen to generate one (see Part 4 for more information on using Coregen). The generated memory model is a dual-port memory 1024x32 with enable and reset inputs and byte write enabled. We also have to modify the wrapper file to support our new memory model.
Writing a simple program
Our first program we will code in 32 bit instruction words like this:
$ADDRESSFMT H $DATAFMT B # 1111111111222222222233 # 01234567890123456789012345678901 # # MSRSET 0/10010100100000000000000000000000; # SW store word 1/11011000100000100001101010101010; 2/10010100100000000000000000000000; 3/11011000100000100001101010101010; 4/10010100100000000000000000000000; 5/11011000100000100001101010101010;
The program is saved in a file called microblaze_instruction_mem.def.
Loading the program
The following NCSIM tcl command will be used to load the program: memory -load memory_instance_name -file memory_content
The instance name in our example has the following format: ETC_SYSTEM_TEST.ETC_system_test:lmb_bram:lmb_bram_rtl:U0:$PROCESS_008:memory
The '.' is the verilog scope delimiter and the ':' is the VHDL delimiter. The $ sign is escaped using '' . Running an NCSIM simulation
Here is the NCSIM control file generated from Mongoose:
/* NCSIM simulation control file generated from Mongoose 15.5 */ /* Generation date : 2007-04-30 */ /* Generation time : 14:15:58 */
-cdslib /home/svenand/root/projects/ETC/verification/simSetup/ncsim/cds_system.lib -hdlvar /home/svenand/root/projects/ETC/verification/simSetup/ncsim/hdl.var -logfile /home/svenand/root/projects/ETC/verification/log/verilog.log -messages -input /home/svenand/root/projects/ETC/verification/mongoose/input/ncsim_tcl.def ETC_SYSTEM_TEST
Simulation result
The program is executing. The MicroBlaze processor is reading instructions from the instruction memory. In the Simvision plot you can also see the content of the instruction memory displayed.

Write a simple c-program
Let's write a simple c-program adding two integers and storing the result.
int main() { int number1; int number2; int number3; number1 = 824; number2 = 200; number3 = number1 + number2;
return 0; }
Compile and build the program inside SDK
Here is the log file from the build process:
**** Full rebuild of configuration Debug for project ETC_system_program ****
make clean all rm -rf main.o main.d ETC_system_program.elf mb-gcc -mno-xl-soft-mul -mxl-pattern-compare -mcpu=v6.00.b -I../../microblaze_0_sw_platform/microblaze_0/include -c -xl-mode-executable -g -O0 -omain.o ../main.c Building target: ETC_system_program.elf mb-gcc -o ETC_system_program.elf main.o -mno-xl-soft-mul -mxl-pattern-compare -mcpu=v6.00.b -L../../microblaze_0_sw_platform/microblaze_0/lib -xl-mode-executable Finished building: ETC_system_program.elf
************** Validating ELF File **************
Validating ELF Section Addresses with Hardware Address Map... elfcheck -mhs /home/svenand/root/projects/ETC/xps/ETC_system.mhs -p xc4vfx12ff668-10 -xmpdir /home/svenand/root/projects/ETC/xps -pe microblaze_0 ETC_system_program.elf elfcheck Xilinx EDK 9.1.01 Build EDK_J_SP1.3 Copyright (c) 1995-2007 Xilinx, Inc. All rights reserved.
Command Line: elfcheck -mhs /home/svenand/root/projects/ETC/xps/ETC_system.mhs -p xc4vfx12ff668-10 -xmpdir /home/svenand/root/projects/ETC/xps -pe microblaze_0 ETC_system_program.elf
ELF file : ETC_system_program.elf
Populating list of memories for processor microblaze_0...
Analyzing file ETC_system_program.elf...
Elfcheck on ETC_system_program.elf completed successfully!
************** Determining Size of ELF File **************
mb-size ETC_system_program.elf text data bss dec hex filename 748 48 1040 1836 72c ETC_system_program.elf
Build complete for project ETC_system_program
Generate assembly code and hex machine code
We use the following command to generate hexadecimal machine code: mb-objdump -d main.o
Here is the result:
==> mb-objdump -d main.o
main.o: file format elf32-microblaze
Disassembly of section .text:
00000000 <main>: 0: 3021ffec addik r1, r1, -20 4: fa610010 swi r19, r1, 16 8: 12610000 addk r19, r1, r0 c: 30600338 addik r3, r0, 824 10: f8730004 swi r3, r19, 4 14: 306000c8 addik r3, r0, 200 18: f8730008 swi r3, r19, 8 1c: e8930004 lwi r4, r19, 4 20: e8730008 lwi r3, r19, 8 24: 10641800 addk r3, r4, r3 28: f873000c swi r3, r19, 12 2c: 10600000 addk r3, r0, r0 30: 10330000 addk r1, r19, r0 34: ea610010 lwi r19, r1, 16 38: 30210014 addik r1, r1, 20 3c: b60f0008 rtsd r15, 8 40: 80000000 or r0, r0, r0
Make a NCSIM memory load file
We take the hex code and make a NCSIM memory load file. This script will fix everything in one run: mb-objdump -d main.o | cut -f2 | sed -n '7,$ p' | cleanup > microblaze_instruction_mem.def The script cleanup removes trailing spaces using the following command : sed 's/[ \t]*$//'
3021ffec fa610010 12610000 30600338 f8730004 306000c8 f8730008 e8930004 e8730008 10641800 f873000c 10600000 10330000 ea610010 30210014 b60f0008 80000000
If we don't specify the addresses in the memory image file we have to add an start address and end address to the memory load command. The following NCSIM tcl command will be used to load the program: memory -load memory_instance_name -file memory_content -0 -1000 Running a simulation
Here is the result from the simulation. The result (=1024) is stored in memory[3] location.

Reading and writing registers in peripherals
We will start by using simple peek and poke commands to access the registers in the peripherals. First let's take a look at the address map which can be found in the ETC_system.log file.
MicroBlaze address map
(0000000000-0x00001fff) dlmb_cntlr dlmb (0000000000-0x00001fff) ilmb_cntlr ilmb (0x40000000-0x4000ffff) Push_Buttons_Position mb_opb (0x40020000-0x4002ffff) LEDs_Positions mb_opb (0x40040000-0x4004ffff) LEDs_4Bit mb_opb (0x40600000-0x4060ffff) RS232_Uart mb_opb (0x41400000-0x4140ffff) debug_module mb_opb (0x42a08000-0x42a08fff) ETC_0 mb_opb (0x42a09000-0x42a09fff) ETC_0 mb_opb (0x44000000-0x47ffffff) DDR_SDRAM_64Mx32 mb_opb (0x71a00000-0x71a0000f) ETC_0 mb_opb
C-program to access registers in ETC
Here is a simple program that will do the job.
int main()
#define poke(addr,val) (*(unsigned char*) (addr) = (val)) #define pokew(addr,val) (*(unsigned*) (addr) = (val)) #define peek(addr) (*(unsigned char*) (addr)) #define peekw(addr) (*(unsigned*) (addr))
{
unsigned char byte_of_data; unsigned word_of_data; pokew(0x71a00000,0xffffffff); pokew(0x71a00000,0xaaaaaaaa); pokew(0x71a00000,0x55555555); return 0; }
Here is the assembly code.
0: 3021fff0 addik r1, r1, -16 4: fa61000c swi r19, r1, 12 8: 12610000 addk r19, r1, r0 c: 3060ffff addik r3, r0, -1 10: b00071a0 imm 29088 14: f8600000 swi r3, r0, 0 18: b000aaaa imm -21846 1c: 3060aaaa addik r3, r0, -21846 20: b00071a0 imm 29088 24: f8600000 swi r3, r0, 0 28: b0005555 imm 21845 2c: 30605555 addik r3, r0, 21845 30: b00071a0 imm 29088 34: f8600000 swi r3, r0, 0 38: 10600000 addk r3, r0, r0 3c: 10330000 addk r1, r19, r0 40: ea61000c lwi r19, r1, 12 44: 30210010 addik r1, r1, 16 48: b60f0008 rtsd r15, 8 4c: 80000000 or r0, r0, r0
From this Simvision plot we can see the address and data appear on the OPB bus.

Top Next Previous
|