OpenMSP430 on Xilinx XC6LX16
1赞以前曾经在Altera FPGA上使用OpenCore上面的OpenMSP430,成功运行了一些测试程序。最近闲来无事,正好手头有一个Spartan 6的xc6lx16的FPGA,也把OpenMSP430搞上去跑一下吧。
配置如下,时钟50MHz,单板晶振提供,指令存储和内存均使用FPGA片内的资源。使用自S6自带的RAM8BWER实现,指令存储16x2048bits,数据存储16x512bits。使用的编译器为mspgcc版本为4.6.3。
测试程序Makefile如下:
#*
#***********************************************************************************************
# Function : Makefile
# Parameter : all clean debug flash
# Author : Lyu Yang
# Date : 2014
# Description : Makefile for OpenMSP430
#***********************************************************************************************
#*
PROJECT = osmp
CROSS_COMPILE = msp430-
INCLUDE_DIRS = -I ./
LIBRARY_DIRS = -L ./
CC = $(CROSS_COMPILE)gcc
CFLAGS = $(INCLUDE_DIRS) -O2 -Wall -g -mcpu=430 -mivcnt=16 -mmpy=16
CXX = $(CROSS_COMPILE)g++
CXXFLAGS = $(INCLUDE_DIRS)
AS = $(CROSS_COMPILE)gcc
ASFLAGS = $(INCLUDE_DIRS) -c -x assembler-with-cpp
LD = $(CROSS_COMPILE)gcc
LDFLAGS = $(LIBRARY_DIRS) -mcpu=430 -T./omsp430.lds -Wl,-Map,$(PROJECT).map
OBJCP = $(CROSS_COMPILE)objcopy
OBJCPFLAGS = -O binary --gap-fill 0xff
AR = $(CROSS_COMPILE)ar
ARFLAGS = cr
DUMP = $(CROSS_COMPILE)objdump
DUMPFLAG = -DS
# User should list all object files
OBJS = ./main.o
.PHONY: all clean debug flash
all: $(PROJECT).elf $(PROJECT).hex $(PROJECT).bin $(PROJECT).mif
$(CROSS_COMPILE)size $(PROJECT).elf
$(PROJECT).elf: $(OBJS)
$(LD) $(LDFLAGS) -o $@ $^
$(PROJECT).asm: $(PROJECT).elf
$(DUMP) $(DUMPFLAG) $(PROJECT).elf > $(PROJECT).asm
$(PROJECT).hex: $(PROJECT).elf
$(OBJCP) -O ihex --gap-fill 0xff $< $@
$(PROJECT).bin: $(PROJECT).elf
$(OBJCP) $(OBJCPFLAGS) $< $@
$(PROJECT).mif: $(PROJECT).bin
./mifwrite $^ $@
clean:
@-rm -f $(PROJECT).elf $(PROJECT).hex $(PROJECT).asm *.log *.map *.mif *.bin
@-rm -f *.o
debug:
$(CROSS_COMPILE)gdb $(PROJECT).elf -x gdbinit.txt
gdbserver:
$(CROSS_COMPILE)gdbproxy --debug msp430 --spy-bi-wire TIUSB
flash:
$(CROSS_COMPILE)flash -n $(MCU) -w "$(PROJECT).hex" -v -g -z [VCC]
download-uart: all
openmsp430-loader.tcl -device /dev/ttyUSB0 -baudrate 115200 $(PROJECT).elf
download-jtag:
$(CROSS_COMPILE)jtag -e $(PROJECT).elf
download-bsl:
$(CROSS_COMPILE)bsl -e $(PROJECT).elf
连接脚本如下:
/* Default linker script, for normal executables */
OUTPUT_FORMAT("elf32-msp430","elf32-msp430","elf32-msp430")
OUTPUT_ARCH(msp430)
MEMORY
{
text (rx) : ORIGIN = 0xf000, LENGTH = 0x0fe0
data (rwx) : ORIGIN = 0x0200, LENGTH = 1024
vectors (rw) : ORIGIN = 0xffe0, LENGTH = 32
bootloader(rx) : ORIGIN = 0x0c00, LENGTH = 1K
infomem(rx) : ORIGIN = 0x1000, LENGTH = 256
infomemnobits(rx) : ORIGIN = 0x1000, LENGTH = 256
}
__WDTCTL = 0x0120;
SECTIONS
{
PROVIDE (__stack = 0x600) ;
/* Read-only sections, merged into text segment. */
.hash : { *(.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.init : { *(.rel.init) }
.rela.init : { *(.rela.init) }
.rel.text :
{
*(.rel.text)
*(.rel.text.*)
*(.rel.gnu.linkonce.t*)
}
.rela.text :
{
*(.rela.text)
*(.rela.text.*)
*(.rela.gnu.linkonce.t*)
}
.rel.fini : { *(.rel.fini) }
.rela.fini : { *(.rela.fini) }
.rel.rodata :
{
*(.rel.rodata)
*(.rel.rodata.*)
*(.rel.gnu.linkonce.r*)
}
.rela.rodata :
{
*(.rela.rodata)
*(.rela.rodata.*)
*(.rela.gnu.linkonce.r*)
}
.rel.data :
{
*(.rel.data)
*(.rel.data.*)
*(.rel.gnu.linkonce.d*)
}
.rela.data :
{
*(.rela.data)
*(.rela.data.*)
*(.rela.gnu.linkonce.d*)
}
.rel.ctors : { *(.rel.ctors) }
.rela.ctors : { *(.rela.ctors) }
.rel.dtors : { *(.rel.dtors) }
.rela.dtors : { *(.rela.dtors) }
.rel.got : { *(.rel.got) }
.rela.got : { *(.rela.got) }
.rel.bss : { *(.rel.bss) }
.rela.bss : { *(.rela.bss) }
.rel.plt : { *(.rel.plt) }
.rela.plt : { *(.rela.plt) }
/* Internal text space. */
.text :
{
. = ALIGN(2);
*(.init)
KEEP(*(.init))
*(.init0) /* Start here after reset. */
KEEP(*(.init0))
*(.init1) /* User definable. */
KEEP(*(.init1))
*(.init2) /* Initialize stack. */
KEEP(*(.init2))
*(.init3) /* Initialize hardware, user definable. */
KEEP(*(.init3))
*(.init4) /* Copy data to .data, clear bss. */
KEEP(*(.init4))
*(.init5) /* User definable. */
KEEP(*(.init5))
*(.init6) /* C++ constructors. */
KEEP(*(.init6))
*(.init7) /* User definable. */
KEEP(*(.init7))
*(.init8) /* User definable. */
KEEP(*(.init8))
*(.init9) /* Call main(). */
KEEP(*(.init9))
__ctors_start = . ;
*(.ctors)
KEEP(*(.ctors))
__ctors_end = . ;
__dtors_start = . ;
*(.dtors)
KEEP(*(.dtors))
__dtors_end = . ;
. = ALIGN(2);
*(.text)
. = ALIGN(2);
*(.text.*)
. = ALIGN(2);
*(.fini9) /* Jumps here after main(). User definable. */
KEEP(*(.fini9))
*(.fini8) /* User definable. */
KEEP(*(.fini8))
*(.fini7) /* User definable. */
KEEP(*(.fini7))
*(.fini6) /* C++ destructors. */
KEEP(*(.fini6))
*(.fini5) /* User definable. */
KEEP(*(.fini5))
*(.fini4) /* User definable. */
KEEP(*(.fini4))
*(.fini3) /* User definable. */
KEEP(*(.fini3))
*(.fini2) /* User definable. */
KEEP(*(.fini2))
*(.fini1) /* User definable. */
KEEP(*(.fini1))
*(.fini0) /* Infinite loop after program termination. */
KEEP(*(.fini0))
*(.fini)
KEEP(*(.fini))
_etext = .;
} > text
.data :
{
PROVIDE (__data_start = .) ;
. = ALIGN(2);
*(.data)
*(SORT_BY_ALIGNMENT(.data.*))
. = ALIGN(2);
*(.gnu.linkonce.d*)
. = ALIGN(2);
_edata = . ;
} > data AT > text
PROVIDE (__data_load_start = LOADADDR(.data) );
PROVIDE (__data_size = SIZEOF(.data) );
/* Bootloader. */
.bootloader :
{
PROVIDE (__boot_start = .) ;
*(.bootloader)
. = ALIGN(2);
*(.bootloader.*)
} > bootloader
/* Information memory. */
.infomem :
{
*(.infomem)
. = ALIGN(2);
*(.infomem.*)
} > infomem
/* Information memory (not loaded into MPU). */
.infomemnobits :
{
*(.infomemnobits)
. = ALIGN(2);
*(.infomemnobits.*)
} > infomemnobits
.bss :
{
PROVIDE (__bss_start = .) ;
*(.bss)
*(SORT_BY_ALIGNMENT(.bss.*))
*(COMMON)
PROVIDE (__bss_end = .) ;
_end = . ;
} > data
PROVIDE (__bss_size = SIZEOF(.bss) );
.noinit :
{
PROVIDE (__noinit_start = .) ;
*(.noinit)
*(.noinit.*)
*(COMMON)
PROVIDE (__noinit_end = .) ;
_end = . ;
} > data
.vectors :
{
PROVIDE (__vectors_start = .) ;
*(.vectors*)
KEEP(*(.vectors*))
_vectors_end = . ;
} > vectors
/* Stabs for profiling information*/
.profiler 0 : { *(.profiler) }
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* DWARF 3 */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
PROVIDE (__data_start_rom = _etext) ;
PROVIDE (__data_end_rom = _etext + SIZEOF (.data)) ;
PROVIDE (__noinit_start_rom = _etext + SIZEOF (.data)) ;
PROVIDE (__noinit_end_rom = _etext + SIZEOF (.data) + SIZEOF (.noinit)) ;
PROVIDE (__subdevice_has_heap = 0) ;
}
笔者写了一个四位的数码管外设,直接挂在OpenMSP430的数据总线上,用C语言写了一个LED灯指示计数并且控制数码管显示的程序作为测试。通过C语言把编译出来的二进制文件转换为用于Xilinx ROM初始化的16位*.COE文件,并在ISE 的Core Generator中设置初始化数据,空闲位置补充为0xFFFF。经过synthesis,translate,map,P&R之后,生成.bit二进制文件,下载之......
结果,灯也不闪,数码管也不亮.......伤心之。
仔细检查个模块,UCF,程序均没问题,那么问题究竟在哪里呢?
结果发现,在最后生成了一个警告,被我忽略了,即:
WARNING:PhysDesignRules:2410 - This design is using one or more 9K Block RAMs
(RAMB8BWER). 9K Block RAM initialization data, both user defined and
default, may be incorrect and should not be used. For more information,
please reference Xilinx Answer Record 39999.
按照提示,问题出在这个9Kb的block RAM。问题偏偏只出现在Spartan 6的FPGA上,因为上述的msp430的程序使用RAMB8BWER实现,而初始化的数据无法正常使用,通过查看Xilinx Answer Record ,得到解决办法是,在Map的时候,对于map器传入参数-convert_bram8即可,强制按8bit进行初始化,至于为什么这么做,我也不清楚,也许涉及到S6 的实现方式吧。设置如下:
