11.GCCによるデバッグRTLシミュレーション
一通り、アセンブラでテストしましたが、テストできなかった命令があることやインプリメントしていない命令もあるので(GCCがとりあえずそれを吐かなければよしとしています。)簡単なCプログラムを走らせてデバッグを続けます。とりあえず、GCCのデバッグ環境を構築します。
GCCはSH2で使用したCコンパイラ(ベストテクノロジー)を使用します。ただし、ROM/RAMのアドレス空間、配置がオリジナルではあり得ない仕様なのでそのままでは使用できません。ROM/RAMのアドレス配置をGCCに教えるにはリンカスクリプトで指定します。16KBのRAM空間を宣言し、0番地にReset Vector,3efc番地にStackTopを配置します。最後の100番地分は、8ビットで絶対アドレスでアクセス可能な領域になります。
リンカスクリプト h8300_tak.x
OUTPUT_ARCH(h8300h) ENTRY("_start") MEMORY { vectors : org =0x000000, len =0x100 rom : org =0x000100, len =14k-0x100 ram : org =0x003800, len =2k-100 eight : org =0xffff00, len =0x100 } SECTIONS { .vectors : { /* Use something like this to place a specific function's address into the vector table. */ /* 0x00 */ LONG(ABSOLUTE(_start)) FILL(0xff) } > vectors .text : { CREATE_OBJECT_SYMBOLS *(.text) etext = .; } > rom .init : { *(.init) } > rom .fini : { *(.fini) } > rom .got : { *(.got) *(.got.plt) } > rom .rodata : { *(.rodata) *(.rodata.*) _erodata = .; } > rom .eh_frame_hdr : { *(.eh_frame_hdr) }> rom .eh_frame : { *(.eh_frame) }> rom .jcr : { *(.jcr) } > rom .tors : { __CTOR_LIST__ = .; LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2) ___ctors = . ; *(.ctors) ___ctors_end = . ; LONG(0) __CTOR_END__ = .; __DTOR_LIST__ = .; LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2) ___dtors = . ; *(.dtors) ___dtors_end = . ; LONG(0) __DTOR_END__ = .; _mdata = .; } > rom .data : AT (_mdata) { _data = .; *(.data) _edata = .; } > ram .gcc_exc : { *(.gcc_exc) } > ram .bss : { _bss = . ; *(.bss) *(COMMON) _ebss = . ; _end = . ; } > ram .stack 0x003f0c : { _stack = . ; *(.stack) } > ram .eight : { *(.eight) } > eight .stab . (NOLOAD) : { [ .stab ] } .stabstr . (NOLOAD) : { [ .stabstr ] } /DISCARD/ : { *(.comment) } } |
以下は、筆者環境下での、
バッチファイルです。
"D:\Program Files\BestTech\GCC Developer Lite\GCC\H8\bin\h8300-coff-gcc" -I D:\PROGRA~1\BestTech\GCCDEV~1\TARGET\3052F -L D:\PROGRA~1\BestTech\GCCDEV~1\TARGET\3052F -mh -nostartfiles -O2 -Wl,--script=h8300_tak.x -Wl,-Map,H8.map h8crt0_tak.s simple_test1.c -ladd3052 -lm -o h8.coff "D:\Program Files\BestTech\GCC Developer Lite\GCC\H8\bin\h8300-coff-objcopy" -O srec h8.coff h8.mot "D:\Program Files\BestTech\GCC Developer Lite\GCC\H8\bin\h8300-coff-objdump" -d h8.coff > h8.txt "F:\altera\h8_project\h8writer\ASM\genromh8_1" h8.mot -16 copy code0.hex f:\altera\h8_project\*.* copy code1.hex f:\altera\h8_project\*.* copy code2.hex f:\altera\h8_project\*.* copy code3.hex f:\altera\h8_project\*.* copy code4.hex f:\altera\h8_project\*.* copy code5.hex f:\altera\h8_project\*.* copy code6.hex f:\altera\h8_project\*.* copy code7.hex f:\altera\h8_project\*.* |
例によってテストベンチにはデバッグ用ポートを追加します。
アドレス70hをデバッグの出力ポートとします。
`timescale 1ns/1ps module h8_test; reg clock=0; reg sync_reset=1; wire [31:0] MOUT; reg int_req2=0; wire int_req2_ack; wire [13:0] monitor_daddr; wire [31:0] monitor_in_data; wire monitor_ram_write; initial begin #10000; // int_req2=1; // wait(int_req2_ack); // int_req2=0; end H8 cpu(.clock(clock),.sync_reset(sync_reset),.MOUT(MOUT),.int_req2(int_req2), .int_req2_ack(int_req2_ack),.monitor_daddr(monitor_daddr), .monitor_in_data(monitor_in_data), .monitor_ram_write(monitor_ram_write)); always #10 clock=~clock; initial begin #205 ; sync_reset=0; #500000000; $finish; end //TAK `define Print_Port_Address 14'h0070 `define Print_CAHR_Port_Address 14'h0072 `define Print_INT_Port_Address 14'h0074 `define Print_LONG_Port_Address 14'h0076 task Cprint;// String OUT until the byte 00 or xx detected with least Byte first and justified. integer i; begin :Block i=0; while (1) begin if (char_buffer[i*8 +:8] ===8'h00 || char_buffer[i*8 +:8]===8'hxx) begin disable Block; end $write("%c",char_buffer[i*8 +:8]); i=i+1; end end endtask reg [0:639] char_buffer; integer counter=0; always @ (posedge clock ) begin if ((monitor_ram_write === 1'b1)) begin if (monitor_daddr==`Print_Port_Address) begin if (monitor_in_data[7:0]===8'h00) begin char_buffer[counter*8 +:8]=monitor_in_data[7:0]; if (char_buffer[0 +:8*7]==="$finish") begin $stop; end else if (char_buffer[0 +:8*5]==="$time") begin $display("Current Time on Simulation=%d",$time); end else Cprint;//$write("%s",char_buffer); for (counter=0; counter< 80; counter=counter+1) begin char_buffer[counter*8 +:8]=8'h00; end counter=0; end else begin char_buffer[counter*8 +:8]=monitor_in_data[7:0]; counter=counter+1; end end else if (monitor_daddr==`Print_CAHR_Port_Address) begin $write("%h ",monitor_in_data[7:0]); end else if (monitor_daddr==`Print_INT_Port_Address) begin $write("%h ",monitor_in_data[15:0]);//Little Endian end else if (monitor_daddr==`Print_LONG_Port_Address) begin $write("%h ",monitor_in_data[31:0]);//Big Endian end end //if end //always endmodule |
これに合わせて論理合成TOP層も書き換えます。
//May.31.2004 //Jun.5.2004 Add debug port `include "define.h" module H8(clock,sync_reset,MOUT,int_req2,int_req2_ack, monitor_daddr,monitor_in_data, monitor_ram_write); input clock; input sync_reset; output [31:0] MOUT; input int_req2; output int_req2_ack; output [13:0] monitor_daddr; output [31:0] monitor_in_data; output monitor_ram_write; reg [7:0] int_vector=8'h20;//Interim May.31.2004 reg [12:0] //ram_module wire ram_write; wire [1:0] ram_access_mode; wire [1:0] paddress_sel; wire data_address_latch; wire ram_data_in_sel; //ea_unit wire [3:0] ea_left_sel; wire [2:0] ea_right_sel; wire [3:0] ea_reg_address;//Jun.3.2004 wire [2:0] next_pc_offset; //alu wire [1:0] alu_left_sel; wire [3:0] alu_right_sel; wire [4:0] ope; wire imm_sel; wire v_clear,h_latch,n_latch,z_latch,v_latch,c_latch, ccr_all_write; wire [1:0] ccr_input_sel; wire [1:0] bus_state; wire [1:0] alu_access_mode; wire [1:0] left_address_sel, right_address_sel; wire [2:0] write_address_sel; wire [1:0] reg_file_access_mode; wire reg_write; wire insel; wire [63:0] irword; wire [31:0] MOUT; wire [31:0] ALU_OUT; wire [23:0] EA,pc_reg; wire [31:0] RO_left,RO_right,left_address_out; wire [31:0] z_bus; wire [7:0] CCR; wire BCC_jmp; wire [7:0] IR1=irword[23+32:16+32]; wire [7:0] IR3=irword[23+32-16:16+32-16]; wire i_bit; wire [7:0] state; wire stop_state; wire [31:0] reg_out_for_mul_div; assign ea_reg_address=ea_left_sel==`PC_SEL ? IR1[3:0] : //Jun.3.2004 ea_left_sel==`ABS8_SEL ? IR3[3:0] : //Jun.3.2004 ea_left_sel==`IR1H_SEL_EA? IR1[7:4] :IR3[7:4];//May.31.2004 assign reg_out_for_mul_div=alu_access_mode == `LONG_ACCESS ? left_address_out : alu_access_mode == `WORD_ACCESS ? ( ea_reg_address[3] ==1'b1 ? {16'h0000,left_address_out[31:16]} : {16'h0000,left_address_out[15:0]} ) : ea_reg_address[3] ==1'b0 ? {24'h00_0000,left_address_out[15:8]} : {24'h00_0000,left_address_out[7:0]} ; //Note: register field mapping R0H =>0000, R0L=>1000 // always @(negedge clock) temp <=mul_div_dest_reg_out; decoder d1(clock,sync_reset, ram_write,ram_access_mode,data_address_latch, ram_data_in_sel,irword, ea_left_sel, ea_right_sel,next_pc_offset, alu_left_sel,alu_right_sel,ope,alu_access_mode,imm_sel,v_clear,h_latch,n_latch,z_latch,v_latch,c_latch, ccr_all_write,bus_state,ccr_input_sel,paddress_sel, left_address_sel, right_address_sel,write_address_sel,reg_file_access_mode, reg_write, insel,int_req2,int_req2_ack,i_bit,stop_state,state); /* decoder decoder1(clock,sync_reset, ram_write,ram_access_mode,data_address_latch, ram_data_in_sel,irword, ea_left_sel, ea_right_sel,next_pc_offset, alu_left_sel,alu_right_sel,ope,alu_access_mode,imm_sel,v_clear,h_latch,n_latch,z_latch,v_latch,c_latch, ccr_all_write,bus_state,ccr_input_sel,paddress_sel, left_address_sel, right_address_sel,write_address_sel,reg_file_access_mode, reg_write, insel,int_req2,int_req2_ack,i_bit,state,stop_state,state); */ h8_ram_module ram_module (.clock(clock),.sync_reset(sync_reset),.ram_write(ram_write), .paddress_sel(paddress_sel),.EA(EA),.data_address_latch(data_address_latch), .bcc_ea_sel(BCC_jmp),.IR(irword),.MOUT(MOUT),.access_mode(ram_access_mode), .ALU_OUT(z_bus),.ram_data_in_sel(ram_data_in_sel),.pc_reg(pc_reg),.next_pc_offset(next_pc_offset),.ea_left_sel(ea_left_sel),.CCR(CCR),.bus_state(bus_state)); ALU alu(.clock(clock),.sync_reset(sync_reset),.alu_left_sel(alu_left_sel), .alu_right_sel(alu_right_sel),.ope(ope),.imm_sel(imm_sel),.v_clear(v_clear), .h_latch(h_latch),.n_latch(n_latch),.z_latch(z_latch),.v_latch(v_latch),.c_latch(c_latch), .ccr_all_write(ccr_all_write),.access_mode(alu_access_mode), .RO_left(RO_left),.RO_right(RO_right),.irword(irword),.MOUT(MOUT), .bus_state(bus_state),.ccr_input_sel(ccr_input_sel),.z_bus(z_bus),.CCR(CCR),.BCC_jmp(BCC_jmp),.i_bit(i_bit),.stop_state(stop_state),.state(state), .mul_div_dest_reg(reg_out_for_mul_div),.mul_div_address3(ea_reg_address[3]));//Jun.3.2004 ea_unit EA_Unit(.clock(clock),.sync_reset(sync_reset),.EA(EA),.irword(irword),.ea_left_sel(ea_left_sel), .ea_right_sel(ea_right_sel), .RO_left(left_address_out),.alu_out(z_bus),.MOUT(MOUT),.pc_reg(pc_reg),.next_pc_offset(next_pc_offset),.int_vector(int_vector)); reg_file RegFile(.clock(clock),.sync_reset(sync_reset),.irhword(irword[63:32]), .left_address_sel(left_address_sel),.right_address_sel(right_address_sel), .write_address_sel(write_address_sel), .access_mode(reg_file_access_mode), .reg_write(reg_write),.alu_out(z_bus),.ea(EA), .reg_left_out(RO_left), .reg_right_out(RO_right),.insel(insel),.left_address_out(left_address_out),.ea_reg_address(ea_reg_address[2:0])); endmodule |
スタートアップルーチンはやはりカスタマイズが必要です。スタートアップルーチンの目的は、C言語のメインに行く前にスタックポインタの設定、割り込みのEnable、RAMの初期化です。main終了後にSleep命令が入っていましたが、Sleep命令はサポートしていないため書き換えました。
スタートアップルーチンのアセンブラソース h8crt0_tak.s
.h8300h .section .text .global _start .global _exit .extern _main .extern _data .extern _mdata .extern _edata .extern _bss .extern _ebss .extern _stack _start: mov.l #_stack,sp ;; copy DATA from ROM mov.l #_mdata,er0 mov.l #_data,er1 mov.l #_edata,er2 data_init_loop: cmp er2,er1 bge fin_data_init mov.b @er0+,r3l mov.b r3l,@er1 adds #1,er1 bra data_init_loop fin_data_init: ;; clear BSS mov.l #_bss,er0 mov.l #_ebss,er1 mov.b #0,r2l bss_init_loop: cmp er1,er0 bge fin_bss_init mov.b r2l,@er0 adds #1,er0 bra bss_init_loop fin_bss_init: jsr @_main _exit: ;TAK sleep bra _exit |
さて、以上の準備を経てようやくC言語でテストソースを書くことができます。最初にやることは、デバッグポートを動作させることです。
デバッグのポイントは、いきなり大きなプログラムを走らせてデバッグをするのではなく、極小のプログラムからブートストラップしてことです。
最初のCプログラムはsimple_test1.cです。 プリントポートを叩いてメッセージを表示させてみましょう。
#define print_port *(volatile unsigned char*)0x70 //unsigned char print_port;// String OUT port for verilog #define print_char_port *(volatile unsigned char*)0x74 // unsigned char* print_char_port;// char OUT port for verilog #define print_int_port *(volatile unsigned char*)0x76 // int(16bit) OUT port for verilog #define print_long_port *(volatile unsigned char*)0x78 // unsigned long* print_long_port;// long(32bit) OUT port for verilog //Verilog Specific Routines void print(unsigned char* ptr)//Verilog Test Bench Use { while (*ptr) { print_port=*(ptr++); } print_port=0x00;//Write Done } void print_char(unsigned char val)//Little Endian write out 16bit number { print_char_port=(unsigned char)val ; } void print_int(int val)//Little Endian write out 16bit number { print_int_port=val ; } void print_long(unsigned long val)//Little Endian write out 32bit number { print_long_port=val; } void main() { print("Welcome to H8300H CPU on Veritak Simulation World !!\n"); print(" Jun.5.2004 www.sugawara-systemc.com \n\n"); print("$finish"); } |
たったこれだけのプログラムですが、設計したCPUがちゃんとやってくれると嬉しいものです。以下のように実行されました。
さて、設計したコアの性能はどうなっているのでしょう。命令ステート数は、メモリリードを含まない命令の殆どを2ステートで実行しますのでざっくり30%程度CPIは向上している筈です。
例によってベンチマークを駆動してみましょう。
実行時間は、1.28msとなりました。SH2に次ぐスピードです。