LSI Jiu-Jitsu

電子工作とブラジリアン柔術

Cortex-M0 RTLで遊ぶ (2) - プログラム実行

前回のシミュレーション環境を少し修正してM0プログラムを動かしてみます。

プログラムのコンパイルはarm-gccを使用しています。
aptからインストールしました。

$ sudo apt install gcc-arm-none-eabi

$ arm-none-eabi-gcc -v
gcc version 8.3.1 20190703 (release) [gcc-8-branch revision 273027] (15:8-2019-q3-1+b1)

若干、リビジョンが古いようですので気になる方はこちらから最新をダウンロードするのが良いと思います。


https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads

my_test.vは以下のように修正しています。

`timescale 1ns/1ns

module my_test();

  reg  clk;
  reg  reset_n;

  wire [31:0]  HADDR;
  wire [2:0]   HSIZE;
  wire [1:0]   HTRANS;
  wire [31:0]  HWDATA;
  wire         HWRITE;
  wire [31:0]  HRDATA;
  wire         HREADY;
  wire         HRESP;

  parameter ADDR_FINISH = 32'h0000fffc;

  CORTEXM0INTEGRATION CORTEXM0INTEGRATION(

    ~省略~

  );

  cmsdk_ahb_ram_beh #( 
     .AW      ( 16          )
    ,.filename( "test.vmem" )
    ,.WS_N    ( 0           )
    ,.WS_S    ( 0           )
  )cmsdk_ahb_ram_beh(

    ~省略~

  );

  always #100 clk = ~clk;

  initial
    begin
      clk          = 1'b0;
      reset_n      = 1'b0;

      #250 reset_n  = 1'b0;
      #250 reset_n  = 1'b1;

      repeat(10) @(posedge clk);

      repeat(20000) @(posedge clk);
      $display("*** Simulation timeout... ***");
      $finish;
    end

    initial
      begin
        repeat(10) @(posedge clk);
        forever begin
           @(posedge clk);
           if(HADDR==ADDR_FINISH && HTRANS==2'b10 && HWRITE==1'b1 && HREADY==1'b1)begin
             repeat(3) @(posedge clk);
             $display("*** Simulation finish... ***");
             $finish;
           end
         end
       end

    initial
      begin
        $dumpvars();
      end

endmodule


修正点としてはメモリモデルのインスタンス時にパラメータを渡しています。

  cmsdk_ahb_ram_beh #( 
     .AW      ( 16          )  ← アドレスバス幅(内部メモリのwordサイズにも使用されている)
    ,.filename( "test.vmem" )  ← シミュレーション開始時に内部メモリに展開するファイル名
    ,.WS_N    ( 0           )  ← アクセス時のウェイト数?(良くわからないのでデフォルトのまま)
    ,.WS_S    ( 0           )
  )cmsdk_ahb_ram_beh(


その他、0xfffc番地にライトアクセスが発生したら3サイクル後にシミュレーションを終了するようにしています。
(ライトアクセスが発生しない時は20000サイクル後にタイムアウトのメッセージを表示して終了)

  parameter ADDR_FINISH = 32'h0000fffc;

    initial
      begin
        repeat(10) @(posedge clk);
        forever begin
           @(posedge clk);
           if(HADDR==ADDR_FINISH && HTRANS==2'b10 && HWRITE==1'b1 && HREADY==1'b1)begin
             repeat(3) @(posedge clk);
             $display("*** Simulation finish... ***");
             $finish;
           end
         end
       end


プログラム環境は、4つのファイルで構成しています。

  • startup.s:ブートプログラム
  .equ STACK_TOP, 0xff00

  .global _start

  .section .isr_vector, "a", %progbits
  .align 4

_vect_table:
  .word  STACK_TOP
  .word  (_start + 1)

  .align 4
_start:
  bl main
_loop:
  nop
  nop
  nop
  b _loop

  .align 4

0x00番地に配置するスタックアドレスは0xff00に設定しています。

  • test.c:メインプログラム
#define ADDR_FINISH 0xfffc

#include <sys/types.h>

int main(void){

  *(volatile uint32_t*)(ADDR_FINISH) = 0xff;

  return 1;
}

実行直後に0xfffc番地へ0xffのライトを行いシミュレーションが終了する予定です。

ENTRY(_start)

MEMORY{
  ROM (xr) : ORIGIN = 0x00000000, LENGTH = 32K
  RAM (xw) : ORIGIN = 0x00008000, LENGTH = 32K
}

SECTIONS{
  .isr_vector : {
    . = ALIGN(4);
    KEEP(*(.isr_vector))
    . = ALIGN(4);
  } > ROM

  .text : {
    . = ALIGN(4);
    *(.text)
    *(.text*)
    . = ALIGN(4);
  } > ROM

  .rodata : {} > ROM
  .data   : {} > RAM
  .bss    : {} > RAM
}

64Kバイトのメモリモデルの前半32KバイトをROM領域に、後半32KバイトをRAM領域に設定しています。

TARGET = test

all:
	arm-none-eabi-as  -mcpu=cortex-m0 -mthumb startup.s -o startup.o
	arm-none-eabi-gcc -mcpu=cortex-m0 -mthumb -Os -c $(TARGET).c -o $(TARGET).o 
	arm-none-eabi-ld -T $(TARGET).ld startup.o $(TARGET).o -o $(TARGET).elf
	arm-none-eabi-objcopy -O verilog $(TARGET).elf $(TARGET).vmem --verilog-data-width=1
	dos2unix $(TARGET).vmem
	arm-none-eabi-objdump $(TARGET).elf --disassemble-all  > $(TARGET).lst

clean:
	$(RM) *.elf *.bin *.o *.lst *.vmem

コンパイル→リンク後のelfファイルをobjcopyでVerilogHDLの$readmemh向けのフォーマットに変換します。
また、objdumpで逆アセンブルも行います。

makeでエラーが出なければコンパイル成功です。

アセンブルしたtest.lstを確認してみます。

Disassembly of section .isr_vector:

00000000 <_vect_table>:
          ↓ スタックアドレスに0xff00を設定
   0:   0000ff00        andeq   pc, r0, r0, lsl #30
          ↓ 0x10番地の _start へジャンプ(LSBの1はthumb命令の意味)
   4:   00000011        andeq   r0, r0, r1, lsl r0
        ...

00000010 <_start>:
          ↓ test.c のmain関数は0x20番地
  10:   f000 f806       bl      20 <main>

00000014 <_loop>:
  14:   46c0            nop                     ; (mov r8, r8)
  16:   46c0            nop                     ; (mov r8, r8)
  18:   46c0            nop                     ; (mov r8, r8)
  1a:   e7fb            b.n     14 <_loop>
  1c:   00000000        andeq   r0, r0, r0

Disassembly of section .text:

00000020 <main>:
                         ↓ 0xffをr2へセット
  20:   22ff            movs    r2, #255        ; 0xff
                         ↓ 0x2c番地にセットされている値(0xfffc)をr3へセット
  22:   4b02            ldr     r3, [pc, #8]    ; (2c <main+0xc>)
  24:   2001            movs    r0, #1
                         ↓ r3のアドレス(0xfffc)へr2の値(0xff)をライト
  26:   601a            str     r2, [r3, #0]
  28:   4770            bx      lr
  2a:   46c0            nop                     ; (mov r8, r8)
  2c:   0000fffc        strdeq  pc, [r0], -ip

問題無さそうです。

メモリモデルがロードする test.vmem も問題無さそうです。

@00000000
00 FF 00 00 11 00 00 00 00 00 00 00 00 00 00 00
00 F0 06 F8 C0 46 C0 46 C0 46 FB E7 00 00 00 00
@00000020
FF 22 02 4B 01 20 1A 60 70 47 C0 46 FC FF 00 00

test.vmem をシミュレーション実行と同じディレクトリに置いてシミュレーションを実行します。


終了メッセージが表示されたので成功です。

*** Simulation finish... ***


波形で確認しても0xfffcへライトした3サイクル後にシミュレーションが終了しています。

なお、プログラムを再コンパイルしてtest.vmemを更新た際にVerilogHDLの修正が無い時は ./simv のみの実行で再実行できます。