8.Cコンパイラ、アセンブラ
とりあえず、開発スナップショット版は、GCC(CYGWIN)を使います。
LCC4.2もコンパイルしてみました。LCCはアセンブラソースを吐きます。これをGCCのアセンブラ?(as)でアセンブルしてもOKです。
例の剰余演算でも乗算コードを吐かないため、こちらの方がソースとしては理解しやすいかもしれません。
8.1スタートアップコード
PLASAM PROJECTの資産を流用します。GCCが吐くフォーマットは、ELFというバイナリフォーマットです。これを、YACCのメモリに載せるためにPLASAM作者から、convert.cが提供されています。(YACCでは、改造版convertc_tak.cを使用、以下a.exe、改造の許可はPLASAM作者であるSteve Rhoadsさんから頂いています。)中身は、筆者も理解していませんが、このプログラムは、スタートアップのオブジェクトコードを書き換えています。
以下は、PLASAM作者から提供されているboot.asmの冒頭部分です。
アセンブラにコメントが書いている通りですが、$gpの設定、$4,$5の設定を行っているようです。
$4,$5は、RAM領域を指し、これで、ゼロクリアした後、C言語のメインに行きます。
##################################################################
# TITLE: Boot Up Code
# AUTHOR: Steve Rhoads (rhoadss@yahoo.com)
# DATE CREATED: 1/12/02
# FILENAME: boot.asm
# PROJECT: Plasma CPU core
# COPYRIGHT: Software placed into the public domain by the author.
# Software 'as is' without warranty. Author liable for nothing.
# DESCRIPTION:
# Initializes the stack pointer and jumps to main2().
##################################################################
.text
.align 2
.globl entry
.ent entry
entry:
.set noreorder
#These eight instructions must be the first instructions.
#convert.exe will correctly initialize $gp
lui $gp,0
ori $gp,$gp,0
#convert.exe will set $4=.sbss_start $5=.bss_end
lui $4,0
ori $4,$4,0
lui $5,0
ori $5,$5,0
lui $sp,0
ori $sp,$sp,0xfff0 #initialize stack pointer
$BSS_CLEAR:
sw $0,0($4)
slt $3,$4,$5
bnez $3,$BSS_CLEAR
addiu $4,$4,4
jal main2
nop
|
逆アセンブルリストを見てみましょう。
0: 3c1c0000 lui $gp,0x0 4: 379c0000 ori $gp,$gp,0x0 8: 34040000 li $a0,0x0 c: 34050000 li $a1,0x0 10: 341d0fec li $sp,0xfec 14: ac800000 sw $zero,0($a0) 18: 0085182a slt $v1,$a0,$a1 1c: 1460fffd bnez $v1,0x14 20: 24840004 addiu $a0,$a0,4 24: 0c000232 jal 0x8c8 |
この段階では、未だ、$gpも,$a0,$a1も0になっています。この後、a.exe(convertc_tak.c)が適切な値に設定してくれています。
これを、シミュレータで確かてみましょう。POWER ON後、0番地から実行を始めます。下の図の場合、3c1c0000が0番地からフェッチした命令になります。(PCは、このとき既に次の番地++4になっている) $gpの上位16ビットをゼロクリアしています。次に下位16ビットに8c6c番地を設定しています。結果、$gpを32ビット00008c6c番地に設定しています。どこから、この番地がでできたかは不明ですが、実際のアクセスでは、RAMが4KBなので、ちゃんとIMMEDIATE OFFSETが加算され、4KBのアクセスになります。同じように$a0,$a1もc80、d18に設定されているのが分かります。$spも0f18で、4KB以下になっています。(そうなるように小さいプログラムしか組めませんが。)
![]() |
GCCが吐くMAPファイルを見ると$gp、$a0,$a1に対応したアドレスが見れます。
Allocating common symbols
Common symbol size file
result_buffer 0x8 test_ram4k_demo.o
char_ptr 0x4 test_ram4k_demo.o
buffer 0x78 test_ram4k_demo.o
buf 0x2 test_ram4k_demo.o
read_ptr 0x4 test_ram4k_demo.o
sym 0x1 test_ram4k_demo.o
Memory Configuration
Name Origin Length Attributes
*default* 0x00000000 0xffffffff
Linker script and memory map
Address of section .text set to 0x0
LOAD boot.o
LOAD test_ram4k_demo.o
.text 0x00000000 0xa98
0x00000000 _ftext=.
*(.text)
.text 0x00000000 0x80 boot.o
0x00000054 putchar
0x00000000 entry
0x00000060 puts
0x0000004c isr_enable
.text 0x00000080 0xa18 test_ram4k_demo.o
0x00000868 strcpy
0x000000d8 print
0x000000c8 read_uart
0x000008c8 main2
0x000002d0 getsym
0x00000a70 parse_error
0x000006a0 strrev
0x00000798 calculator
0x00000704 itoa
0x00000114 print_long
0x00000898 calculator_test
0x000003ec expression
0x00000a20 set_interrupt_address
0x00000368 evaluate_number
0x00000a44 init_parser
0x000004dc term
0x00000080 print_uart
0x00000120 interrupt
0x000005f0 factor
0x000000b8 putc_uart
0x000002b8 print_longlong
*(.stub)
*(.gnu.warning)
*(.gnu.linkonce.t*)
*(.mips16.fn.*)
*(.mips16.call.*)
.init
*(.init)
.fini
*(.fini)
0x00000a98 _ecode=.
.reginfo 0x00000a98 0x18
*(.reginfo)
.reginfo 0x00000a98 0x18 boot.o
.reginfo 0x00000ab0 0x18 test_ram4k_demo.o
.ctors
*(.ctors)
.dtors
*(.dtors)
.eh_frame
*(.eh_frame)
.gcc_except_table
*(.gcc_except_table)
.sdeinit
*(.sdeinit)
.sdefini
*(.sdefini)
.rodata 0x00000ab0 0x1cc
*(.rodata)
.rodata 0x00000ab0 0x1cc test_ram4k_demo.o
*(.rdata)
*(.gnu.linkonce.r*)
.rodata1
*(.rodata1)
0x00000c7c _etext=.
0x00000c7c PROVIDE (etext, .)
.data 0x00000c7c 0x0
0x00000c7c _fdata=.
*(.data)
*(.gnu.linkonce.d*)
.data1
*(.data1)
0x00008c6c _gp=(.+0x7ff0)
.lit8
*(.lit8)
.lit4
*(.lit4)
.sdata 0x00000c7c 0x4
*(.sdata)
.sdata 0x00000c7c 0x4 test_ram4k_demo.o
0x00000c7c int_flag
*(.gnu.linkonce.s*)
0x00000c80 _edata=.
0x00000c80 PROVIDE (edata, .)
0x00000c80 .=ALIGN(0x8)
0x00000c80 __bss_start=.
0x00000c80 _fbss=__bss_start
.sbss 0x00000c80 0x15
*(.sbss)
*(.scommon)
.scommon 0x00000c80 0x15 test_ram4k_demo.o
0x00000c80 result_buffer
0x00000c88 char_ptr
0x00000c8c buf
0x00000c90 read_ptr
0x00000c94 sym
.bss 0x00000ca0 0x78
*(.dynbss)
*(.bss)
*(COMMON)
COMMON 0x00000ca0 0x78 test_ram4k_demo.o
0x00000ca0 buffer
0x00000d18 _end=.
0x00000d18 PROVIDE (end, .)
.stab
*(.stab)
.stabstr
*(.stabstr)
.comment
*(.comment)
.debug
*(.debug)
.line
*(.line)
.debug_srcinfo
*(.debug_srcinfo)
.debug_sfnames
*(.debug_sfnames)
.debug_aranges
*(.debug_aranges)
.debug_pubnames
*(.debug_pubnames)
.debug_info
*(.debug_info)
.debug_abbrev
*(.debug_abbrev)
.debug_line
*(.debug_line)
.debug_frame
*(.debug_frame)
.debug_str
*(.debug_str)
.debug_loc
*(.debug_loc)
.debug_macinfo
*(.debug_macinfo)
.debug_weaknames
*(.debug_weaknames)
.debug_funcnames
*(.debug_funcnames)
.debug_typenames
*(.debug_typenames)
.debug_varnames
*(.debug_varnames)
.mdebug
*(.mdebug)
.rel.text
*(.rel.text)
*(.rel.gnu.linkonce.t*)
.rela.text
*(.rela.text)
*(.rela.gnu.linkonce.t*)
.rel.data
*(.rel.data)
*(.rel.gnu.linkonce.d*)
.rela.data
*(.rela.data)
*(.rela.gnu.linkonce.d*)
.rel.sdata
*(.rel.sdata)
*(.rel.gnu.linkonce.s*)
.rela.sdata
*(.rela.sdata)
*(.rela.gnu.linkonce.s*)
.rel.rodata
*(.rel.rodata)
*(.rel.gnu.linkonce.r*)
.rela.rodata
*(.rela.rodata)
*(.rela.gnu.linkonce.r*)
.gptab.sdata
*(.gptab.data)
*(.gptab.sdata)
.gptab.sbss
*(.gptab.bss)
*(.gptab.sbss)
OUTPUT(test.exe elf32-bigmips)
|
なお、スタックは、使える限りの上限に設定した方が、スタック溢れの確率を減らせるので、この例(4KB)では、以下のように記述しています。$ff0から、UARTや、RTLシミュレーションのデバッグポートを配置したので、$fecとしています。最初の設定では、a.exeにより上書きされてしまいますが、main2に行く前のディレイドブランチにより、$SPは、$fecに設定されます。
##################################################################
# TITLE: Boot Up Code
# AUTHOR: Steve Rhoads (rhoadss@yahoo.com)
# DATE CREATED: 1/12/02
# FILENAME: boot.asm
# PROJECT: Plasma CPU core
# COPYRIGHT: Software placed into the public domain by the author.
# Software 'as is' without warranty. Author liable for nothing.
# DESCRIPTION:
# Initializes the stack pointer and jumps to main2().
##################################################################
.text
.align 2
.globl entry
.ent entry
entry:
.set noreorder
#These nine instructions must be the first instructions
#convert.exe will correctly initialize $gp
lui $gp,0
ori $gp,$gp,0
#convert.exe will set $4=.sbss_start $5=.bss_end
ori $4,$0,0
ori $5,$0,0
ori $sp,$0,0x00fec #initialize stack pointer
$BSS_CLEAR:
sw $0,0($4)
slt $3,$4,$5
bnez $3,$BSS_CLEAR
addiu $4,$4,4
jal main2
ori $sp,$0,0x00fec #initialize stack pointer
|
下は、d18番地に0を書いた後、8c8番地(main2)に飛ぶ前に$spをfecに設定している部分のシミュレーション波形です。

以上から、コンパイルから、RAM容量に応じたRTLシミュレーションにいたる手順は以下の通りです。
これで、RAM初期化ファイルができました。codexxは、QurtasのWizardで2ポートRAMを生成する際に指定しました。Wizardで生成したRAMがRTLシミュレーション時に自動的にcodexxを読みます。(このとき、内部的には、HEXファイルをVerilog形式に直しています。)
Xilinxで注意することがあります。RTLシミュレーション時は、生成ツールの出力を使うのです問題ないのですが、「ゲートシミュレーションおよび、実際のFPGAでは、CoregenでRegenerateを行う必要がある」ということです。
また、このWizardで生成したRAMを論理合成すれば、初期化付RAMが合成されます。