ARMマイコンで作るシンセサイザー
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
535
On Slideshare
359
From Embeds
176
Number of Embeds
3

Actions

Shares
Downloads
1
Comments
0
Likes
1

Embeds 176

https://twitter.com 170
http://makezine.jp 4
https://tweetdeck.twitter.com 2

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. ARMマイコンで作る シンセサイザー Naomasa Matsubayashi
  • 2. Twitter Github @fadis_ ARMhttps://マgithub.イコcom/ンFadis/ で作る シslideshare http://ンwww.セサslideshare.イザnet/ー fadis Naomasa Matsubayashi
  • 3. slideshare http://www.slideshare.net/fadis
  • 4. 世の中には色んな ARM がある
  • 5. でかいARM ARM Cortex-A ARM BeagleBoard-X15 ARM Cortex-A15 1.5GHz x2 http://www.elinux.org/Beagleboard:BeagleBoard-X15
  • 6. ARM Cortex-M LPC812 ARM Cortex-M0+ 30MHz http://www.nxp-lpc.com/lpc_micon/cortex-m0+/lpc800/ 小さいARM
  • 7. マイクロコントローラ向けARM Cortex-Mシリーズ Cortex-M7 Cortex-M4 Cortex-M3 Cortex-M1 Cortex-M0+ CortexM0 http://arm.com/ja/products/processors/cortex-m/index.php
  • 8. ARM命令には対応していない Cortex-M7 Thumb2(+FPU)のみ Cortex-M4 Thumb2(+FPU)のみ Cortex-M3 Thumb2のみ Cortex-M1 Thumb2の一部だけ Cortex-M0+ Thumb2の一部だけ CortexM0 Thumb2の一部だけ Thumb2とは: ARMプロセッサのもう1つの命令セット。 命令が可変長でARM命令セットよりマシン語が小さくなる。
  • 9. パイプラインが短い Cortex-M7 6段 投機的実行 Cortex-M4 3段 投機的実行 Cortex-M3 3段 Cortex-M1 3段 Cortex-M0+ 2段 CortexM0 3段 投機的実行とは: 処理が必要かどうか確定する前から 実行を始めておく事。必要なかったら実行結果を捨てる。
  • 10. MMUが無い 仮想アドレスの変換は出来ない Cortex-M7/M4/M3/M0+には メモリ保護ユニットだけは備わっている 基本的にOSを動かすことは想定されていない ただしLinuxを動かしてしまった人は居る http://www.linux-arm.org/LinuxKernel/LinuxM3 MMUとは: メモリ管理ユニット。仮想アドレスと 物理アドレスの変換、及びメモリ保護を行うハードウェア。
  • 11. ARMにしては遅い クロックDMIPS/MHz 200-400MHz 100-300MHz 75-120MHz 30-50MHz 30-50MHz Cortex-M7 Cortex-M4 Cortex-M3 Cortex-M0+ CortexM0 2.14 1.52 1.50 1.08 0.99 DMIPS/MHzとは: Dhrystoneベンチマークを1秒間に実行 できた回数を周波数で割ったもの。大きい程クロック辺りの 性能が良い。速いARMだと15以上、x86_64だと30以上。
  • 12. 安い NXP LPC812の場合 マルツで1個 110円(税抜) http://www.marutsu.co.jp/pc/i/226931/ マルツとは: 秋葉原の本店をはじめ全国12カ所にある電子部 品屋さん。入門キットから特殊用途の石まで幅広い品揃え。
  • 13. 低消費電力 NXP LPC812の場合 最大クロック(30MHz)時 10.89mW 割り込み待機時 5.28μW クロックを低めに設定すれば コイン型リチウム電池でも動く
  • 14. ARM Cortex-Mで動く バイナリを作ろう
  • 15. mbed https://mbed.org/ ブラウザ上で動く統合開発環境
  • 16. mbed 色んなCortex-Mマイコン向けの バイナリを吐ける
  • 17. mbed 色んなデバイスのドライバが 最初から用意されている
  • 18. mbed
  • 19. USBストレージをmマウbンeトd イーサネットドライバ HTTPサーバ DHCPでアドレスを貰ってくる HTTPサーバを80番ポートにbind 驚異のレイヤーの高さ
  • 20. ダメだ もっとレイヤーを下げるんだ
  • 21. arm-none-eabi-g++ ローカルのgccでビルドしよう
  • 22. CMSIS Cortex-M向けの ハードウェア抽象化レイヤー ARM社がインターフェースを定めて マイコンのベンダが実装する 違うベンダのマイコンに移行しても 基本的な機能の使い方は一緒で習得が楽
  • 23. mbedはCMSISの上で動く アプリケーション mbedコミュニティ ペリフェラルライブラリ mbedライブラリ CMSIS Cortex-Mマイコン HTTPサーバ とかはここ イーサネット ドライバ とかはここ デバイスの 初期化 ここまでは使いたいとかはここ
  • 24. CMSIS mbedのソースコードは githubで公開されている https://github.com/mbedmicro/mbed/ 各マイコン向けのCMSISの実装も入っている
  • 25. ldscript mbedのヤツは ARM純正コンパイラ用に書かれている ldscriptとは: リンカに渡す設定ファイル。バイナリイメージ のどこに何が置かれるかを記述する。
  • 26. MEMORY {! flash (rx) : ORIGIN = 0x00000000, LENGTH = 16K! ram (rwx) : ORIGIN = 0x10000000, LENGTH = 4K! }! ENTRY(Reset_Handler)! SECTIONS {! .text : {! KEEP(*(.isr_vector))! *(.text*)! KEEP(*(.init))! KEEP(*(.fini))! *crtbegin.o(.ctors)! *crtbegin?.o(.ctors)! *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)! *(SORT(.ctors.*))! *(.ctors)! *crtbegin.o(.dtors)! *crtbegin?.o(.dtors)! *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)! *(SORT(.dtors.*))! *(.dtors)! *(.rodata*)! KEEP(*(.eh_frame*))! } > flash! .ARM.extab : {! *(.ARM.extab* .gnu.linkonce.armextab.*)! } > flash! __exidx_start = .;! .ARM.exidx : {! *(.ARM.exidx* .gnu.linkonce.armexidx.*)! } > flash! __exidx_end = .;! __etext__ = .;! .data : AT(__etext__) {! __data_values__ = LOADADDR(.data);! __data_begin__ = .;! *(vtable)! *(.data*)! . = ALIGN(4);! ldscript RAMとフラッシュメモリの 開始アドレスとサイズは 普通データシートに書いてある ! 今回使うLPC812の情報は 以下のURLから辿れる http://www.nxp-lpc.com/lpc_micon/cortex-m0+/lpc800/
  • 27. MEMORY {! flash (rx) : ORIGIN = 0x00000000, LENGTH = 16K! ram (rwx) : ORIGIN = 0x10000000, LENGTH = 4K! }! ENTRY(Reset_Handler)! SECTIONS {! .text : {! KEEP(*(.isr_vector))! *(.text*)! KEEP(*(.init))! KEEP(*(.fini))! *crtbegin.o(.ctors)! *crtbegin?.o(.ctors)! *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)! *(SORT(.ctors.*))! *(.ctors)! *crtbegin.o(.dtors)! *crtbegin?.o(.dtors)! *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)! *(SORT(.dtors.*))! *(.dtors)! *(.rodata*)! KEEP(*(.eh_frame*))! } > flash! .ARM.extab : {! *(.ARM.extab* .gnu.linkonce.armextab.*)! } > flash! __exidx_start = .;! .ARM.exidx : {! *(.ARM.exidx* .gnu.linkonce.armexidx.*)! } > flash! __exidx_end = .;! __etext__ = .;! .data : AT(__etext__) {! __data_values__ = LOADADDR(.data);! __data_begin__ = .;! *(vtable)! *(.data*)! . = ALIGN(4);! PROVIDE_HIDDEN (__preinit_array_start = .);! ldscript フラッシュメモリには ! 割り込みベクタ マシン語 書き変わらないデータ ! を置く
  • 28. __exidx_start = .;! .ARM.exidx : {! *(.ARM.exidx* .gnu.linkonce.armexidx.*)! } > flash! __exidx_end = .;! __etext__ = .;! .data : AT(__etext__) {! __data_values__ = LOADADDR(.data);! __data_begin__ = .;! *(vtable)! *(.data*)! . = ALIGN(4);! PROVIDE_HIDDEN (__preinit_array_start = .);! KEEP(*(.preinit_array))! PROVIDE_HIDDEN (__preinit_array_end = .);! . = ALIGN(4);! PROVIDE_HIDDEN (__init_array_start = .);! KEEP(*(SORT(.init_array.*)))! KEEP(*(.init_array))! PROVIDE_HIDDEN (__init_array_end = .);! . = ALIGN(4);! PROVIDE_HIDDEN (__fini_array_start = .);! KEEP(*(SORT(.fini_array.*)))! KEEP(*(.fini_array))! PROVIDE_HIDDEN (__fini_array_end = .);! . = ALIGN(4);! __data_end__ = .;! } > ram! .bss : {! __bss_begin__ = .;! *(.bss*)! *(COMMON)! __bss_end__ = .;! } > ram! __stack_begin__ = .;! .stack_dummy : {! *(.stack)! } > ram! __stack_end__ = .;! } ldscript RAMには ! vtable 書き変わるデータ スタック ! を置く
  • 29. スタートアップルーチン プログラムが起動してから main関数に入るまでの処理を書いたもの 起動 グローバル変数の初期値を設定 グローバル変数のコンストラクタを実行 ハードウェアの初期化 main()にジャンプ
  • 30. スタートアップルーチン mbedのヤツは例によって ARM純正コンパイラ用に書かれている
  • 31. スタートアップルーチン Reset_Handler:! ldr r1, =__etext__! ldr r2, =__data_begin__! ldr r3, =__data_end__! ldr r0, =init_data! blx r0! ldr r0, =init_bss! blx r0! ldr r0, =run_preinit! blx r0! ldr r0, =run_init! blx r0! ldr r0, =SystemInit! blx r0! ldr r0, =main! blx r0! .pool ! .size Reset_Handler, . - Reset_Handler
  • 32. Rスesetタ_Hanーdler:ト! アップルーチン ldr r1, =__etext__! ldr r2, =__data_begin__! ldr r3, =__data_end__! ldr r0, =init_data! blx r0! ldr r0, =init_bss! blx r0! ldr r0, =run_preinit! blx r0! ldr r0, =run_init! blx r0! ldr r0, =SystemInit! blx r0! ldr r0, =main! blx r0! .pool ! アセ ン .siブze Rリeseでt_Ha格ndle闘r, .し - Rたeseくt_Haなndleいr ので Cで実装した初期化コードに飛ぶ
  • 33. Rスesetタ_Hanーdler:ト! アップルーチン ldr r1, =__etext__! ldr r2, =__data_begin__! ldr r3, =__data_end__! ldr r0, =init_data! blx ldr デバr0! r0, イ=init_スbssの! 初期化は blx r0! ldr r0, =run_preinit! blx CMSISr0! におまかせ ldr r0, =run_init! blx r0! ldr r0, =SystemInit! blx r0! ldr r0, =main! blx r0! .pool ! .size Reset_Handler, . - Reset_Handler
  • 34. void run_preinit(void) {! int *cur = &__preinit_array_start;! for( ; cur < &__preinit_array_end; cur++ ) {! void (*f)(void) = (void *)*cur;! (*f)();! }! }! void run_init(void) {! int *cur = &__init_array_start;! for( ; cur < &__init_array_end; cur++ ) {! void (*f)(void) = (void *)*cur;! (*f)();! }! }! void init_data(void) {! unsigned char *src = &__data_values__;! unsigned char *dest = &__data_begin__;! unsigned int len = &__data_end__ - &__data_begin__;! while( len-- ) *dest++ = *src++;! }! void init_bss(void) {! unsigned char *dest = &__bss_begin__;! unsigned int len = &__bss_end__ - &__bss_begin__;! while( len-- ) *dest++ = 0;! }
  • 35. void run_preinit(void) {! int *cur = &__preinit_array_start;! for( ; cur < &__preinit_array_end; cur++ ) {! void (*f)(void) = (void *)*cur;! (*f)();! }! }! void run_init(void) {! int *cur = &__init_array_start;! for( ; cur < &__init_array_end; cur++ ) {! RAMに置いておくデータを フラッシュメモリからコピー void (*f)(void) = (void *)*cur;! (*f)();! }! }! void init_data(void) {! unsigned char *src = &__data_values__;! unsigned char *dest = &__data_begin__;! unsigned int len = &__data_end__ - &__data_begin__;! while( len-- ) *dest++ = *src++;! }! void init_bss(void) {! unsigned char *dest = &__bss_begin__;! unsigned int len = &__bss_end__ - &__bss_begin__;! while( len-- ) *dest++ = 0;! }
  • 36. void run_preinit(void) {! int *cur = &__preinit_array_start;! for( ; cur < &__preinit_array_end; cur++ ) {! void (*f)(void) = (void *)*cur;! (*f)();! }! }! void run_init(void) {! int *cur = &__init_array_start;! for( ; cur < &__init_array_end; cur++ ) {! void (*f)(void) = (void *)*cur;! (*f)();! }! }! void init_data(void) {! unsigned char *src = &__data_values__;! unsigned char *dest = &__data_begin__;! unsigned int len = &__data_end__ - &__data_begin__;! while( len-- ) *dest++ = *src++;! }! void init_bss(void) {! unsigned char *dest = &__bss_begin__;! unsigned int len = &__bss_end__ - &__bss_begin__;! while( len-- ) *dest++ = 0;! } グローバルオブジェクトの コンストラクタとかを実行
  • 37. LEDを点滅させてみよう
  • 38. #include "LPC8xx.h"! extern "C"! __attribute__((interrupt("IRQ")))! void SysTick_Handler(void) {! LPC_GPIO_PORT->NOT0 = ( 1 << 16 );! }! int main() {! LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 6);! LPC_GPIO_PORT->DIR0 |= ( 1 << 16 );! SysTick_Config( 6000000 );! while( 1 );! }
  • 39. SysTickタイマーの割り込みが発生したら #include "LPC8xx.h"! extern "C"! __attribute__((interrupt("IRQ")))! void SysTick_Handler(void) {! LPC_GPIO_PORT->NOT0 = ( 1 << 16 );! }! int LPC_16main() {! SYSCON->番目のSYSAHBCLKCTRL GPIOの値|= を(反1 << 転 6);! LPC_GPIO_PORT->DIR0 |= ( 1 << 16 );! SysTick_Config( 6000000 );! while( 1 );! } __attribute__((interrupt(“IRQ”)))とは: これを指定して おくとgccが割り込みエントリルーチンを付けてくれる
  • 40. #include "LPC8xx.h"! extern "C"! __attribute__((void GPIOSysTick_へのクinterrupt("ロックを有IRQ")))効にし! Handler(void) {! て LPC_}! 16番GPIO_目をPORT->出力NOT0 モー= ド( 1 に<< 切16 り替);! え int main() {! LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 6);! LPC_GPIO_PORT->DIR0 |= ( 1 << 16 );! SysTick_Config( 6000000 );! while( 1 );! } SysTickが6,000,000カウントする度に SysTick割り込みを発生させるようにする
  • 41. LPC812は 起動時は12MHzで動いている SysTickが 6,000,000カウントするには 0.5秒かかる 0.5秒毎にGPIOの出力が 反転するはず
  • 42. http://youtu.be/9GypdG2DmIc
  • 43. 第2回カーネル/VM関西で紹介したネタ シンセサイザー入門 100MHzのCortex-M3マイコンを使って FM音源とかを作る話
  • 44. 30MHzのCortex-M0+でも シンセサイザーが作れるのでは
  • 45. 音を出せるようにしよう
  • 46. マイコンにDACが無いので SPIにDACをぶら下げる 3.3V VDD VSS ARMマイコン LPC812 VREF VSS SPI DAC MCP4821 アンプIC HT82V739 PIO0_8 PIO0_9 PIO0_1 VDD AVSS CS SCLK SDI VOUTA 100kΩ 1μF 1μF 47μF VDD VSS VREF AUDIN OUTN OUTP
  • 47. void init_dac() {! NVIC_DisableIRQ( (IRQn_Type)( SPI0_IRQn ) );! NVIC_SetPriority(SPI0_IRQn, 0); ! do {! NVIC_ClearPendingIRQ(SPI0_IRQn); ! } while(NVIC_GetPendingIRQ(SPI0_IRQn) != 0);! LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 11);! LPC_SPI0->DLY = 4;! LPC_SPI0->DIV = 2;! LPC_SPI0->TXCTRL = (16-1)<<24 | 1<<22 | ( 1 << 20 );! LPC_SPI0->CFG = (1<<5)|(1<<4)|(1<<2);! switch_matrix::bind_SPI0_SSEL_IO( 8 ); ! switch_matrix::bind_SPI0_SCK_IO( 9 );! switch_matrix::bind_SPI0_MOSI_IO( 1 );! LPC_SPI0->INTENSET = 0x0;! NVIC_EnableIRQ( (IRQn_Type)( SPI0_IRQn ) );! LPC_SPI0->TXDAT = 2048 | ( 1 << 13 )|( 1 << 12 );! }! void set_dac( uint16_t value ) {! value &= 4095;! LPC_SPI0->TXDAT = value | ( 1 << 13 )|( 1 << 12 );! }
  • 48. MCP4821(今回使ったDAC)は 16bit送る毎に出力が更新される void init_dac() {! NVIC_DisableIRQ( (IRQn_Type)( SPI0_IRQn ) );! NVIC_SetPriority(SPI0_IRQn, 0); ! do {! NVIC_ClearPendingIRQ(SPI0_IRQn); ! } while(NVIC_GetPendingIRQ(SPI0_LPC_SYSCON->SYSAHBCLKCTRL LPC812|= のIRQn) は != 0);! (1 SPI<< 11);! LPC_SPI0->一DLY 度= 4;! LPC_SPI0->DIV = 2;LPC_SPI0->TXCTRL に! = (16-16bit1)<<24 ま| 1<<で22 送| れ( 1 る << 20 );! LPC_SPI0->CFG = (1<<5)|(1<<4)|(1<<2);! switch_matrix::bind_SPI0_SSEL_IO( 8 ); ! switch_送matrix::信完bind_了時SPI0_の割SCK_りIO( 込み9 );が! 要らない switch_matrix::送bind_信バSPI0_ッフMOSI_ァもIO( 要1 );! LPC_SPI0->INTENSET = 0x0;! らない NVIC_EnableIRQ( (IRQn_やType)( ったSPI0_ね! IRQn ) );! LPC_SPI0->TXDAT = 2048 | ( 1 << 13 )|( 1 << 12 );! }! void set_dac( uint16_t value ) {! value &= 4095;! LPC_SPI0->TXDAT = value | ( 1 << 13 )|( 1 << 12 );! }
  • 49. void init_system_clock() {! LPC_SYSCON->SYSAHBCLKDIV = 2;! LPC_SYSCON->SYSPLLCTRL = 0x24;! }! int main() {! LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 6);! LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 18);! LPC_GPIO_PORT->DIR0 |= ( 1 << 8 );! LPC_GPIO_PORT->DIR0 |= ( 1 << 9 );! LPC_GPIO_PORT->DIR0 |= ( 1 << 1 );! init_system_clock();! init_dac();! SysTick_Config( 1875*2 );! while( 1 );! } CPUのクロックを30MHzにして SysTickを8kHzに設定
  • 50. uint16_t step = 0;! ! extern "C"! __attribute__((interrupt("IRQ")))! void SysTick_Handler(void) {! set_dac( step << 5 );! ++step;! } SysTick割り込みが発生したら カウンタの値をDACに送る これでノコギリ波が出るはず
  • 51. http://youtu.be/fajL7klkpNI
  • 52. 音色を計算しよう
  • 53. 4オペレータ3和音FM音源 ダイナミックレンジ圧縮 オーディオバッファ
  • 54. 浮動小数演算器が無い 16.16の固定小数点数を使う 整数部16bit符号付き整数小数部16bit符号無し整数 整数から固定小数点数への変換は左に16bitシフト 固定小数点数から整数への変換は右に16bitシフト
  • 55. ブロック単位で計算 PCMのサンプルは8kHzはないと まともな音に聞こえない 音量の上げ下げやキーの状態は もっと低い頻度でしか変化しない 1ブロック ブロックの中では音量やキーの変化は 無視できるものとする
  • 56. ブロック単位で計算 周波数の低い波や線形な値の変化は 間引いて計算出来る 補間補間補間補間 必要なサンプル数 32 計算したサンプル数 4 ごまかしたサンプル数 24
  • 57. http://youtu.be/OaUmnOxzUI0
  • 58. 計算を速くしよう
  • 59. 32bitの整数同士の乗算の結果は 最大で64bitの整数になる FFFFFFFF! FFFFFFFF 32bit 32bit * FFFFFFFE00000001 64bit
  • 60. 乗算命令mulは 32bitと32bitを掛けて 結果の下32bitを得る http://infocenter.arm.com/help/index.jsp?topic=/ com.arm.doc.dui0204ij/Cihihggj.html
  • 61. 16.16の固定小数点数同士の 乗算の結果は 32.32の固定小数点数になる FFFF.FFFF! FFFF.FFFF * FFFFFFFE.00000001 結果を16.16で得るには 16bit目から47bit目までが欲しい
  • 62. FFFF.FFFF! FFFF.FFFF * FFFFFFFE.00000001 結果を16.16で得るには 16bit目から47bit目までが欲しい 乗算結果の32bit目以上の値が必要
  • 63. 32bitと32bitを掛けて 結果を64bitで得る命令 mull
  • 64. mullが使われることを期待して 乗算をこんな風に実装していた inline constexpr self_type operator*(! const self_type &y! ) const {! return self_type(! (! static_cast< double_type >( base ) *! static_cast< double_type >( y.base )! ) >> Shift::value, raw()! );! }
  • 65. 現実 …! 00001374 <__aeabi_lmul>:! 1374: 469c mov ip, r3! 1376: 0403 lsls r3, r0, #16! 1378: b5f0 push {r4, r5, r6, r7, lr}! 137a: 0c1b lsrs r3, r3, #16! 137c: 0417 lsls r7, r2, #16! 137e: 0c3f lsrs r7, r7, #16! 1380: 0c15 lsrs r5, r2, #16! 1382: 1c1e adds r6, r3, #0! 1384: 1c04 adds r4, r0, #0! 1386: 0c00 lsrs r0, r0, #16! 1388: 437e muls r6, r7! 138a: 436b muls r3, r5! 138c: 4347 muls r7, r0! 138e: 4345 muls r5, r0! 1390: 18fb adds r3, r7, r3! 1392: 0c30 lsrs r0, r6, #16! 1394: 1818 adds r0, r3, r0! 1396: 4287 cmp r7, r0! 1398: d902 bls.n 13a0 <__aeabi_lmul+0x2c>! 139a: 2380 movs r3, #128 ; 0x80! 139c: 025b lsls r3, r3, #9!
  • 66. 現実 1392: 0c30 lsrs r0, r6, #16! 1394: 1818 adds r0, r3, r0! 1396: 4287 cmp r7, r0! 1398: d902 bls.n 13a0 <__aeabi_lmul+0x2c>! 139a: 2380 movs r3, #128 ; 0x80! 139c: 025b lsls r3, r3, #9! 139e: 18ed adds r5, r5, r3! 13a0: 0c03 lsrs r3, r0, #16! 13a2: 18ed adds r5, r5, r3! 13a4: 4663 mov r3, ip! 13a6: 435c muls r4, r3! 13a8: 434a muls r2, r1! 13aa: 0436 lsls r6, r6, #16! 13ac: 0c36 lsrs r6, r6, #16! 13ae: 18a1 adds r1, r4, r2! 13b0: 0400 lsls r0, r0, #16! 13b2: 1980 adds r0, r0, r6! 13b4: 1949 adds r1, r1, r5! 13b6: bdf0 pop {r4, r5, r6, r7, pc}! … mullは使われていない
  • 67. ARM命令には対応していない Cortex-M7 Thumb2(+FPU)のみ Cortex-M4 Thumb2(+FPU)のみ Cortex-M3 Thumb2のみ Cortex-M1 Thumb2の一部だけ Cortex-M0+ Thumb2の一部だけ CortexM0 Thumb2の一部だけ Thumb2とは: ARMプロセッサのもう1つの命令セット。 命令が可変長でARM命令セットよりマシン語が小さくなる。
  • 68. ARM命令には対応していない 一部とは Cortex-M7 Thumb2(+FPU)のみ Cortex-M4 Thumb2(+FPU)のみ Cortex-M3 Thumb2のみ Cortex-M1 Thumb2の一部だけ Cortex-M0+ Thumb2の一部だけ CortexM0 Thumb2の一部だけ Thumb2とは: ARMプロセッサのもう1つの命令セット。 命令が可変長でARM命令セットよりマシン語が小さくなる。
  • 69. 移動 M7/M4/M3 mov movw movt add adc adr sub sbc rsb mul mla mls smull umull smlal umlal sdiv udiv ssat usat cmp cmn and eor orr orn bic mvn tst teq lsl lsr asr ror rrx clz ldr ldrh ldrb ldrsh ldrsb ldrt ldrht ldrbt ldrsht ldrsbt ldrd ldm str strh strb strsh strsb strt strht strbt strsht strsbt strd stm push pop ldrex ldrexh ldrexb strex strexh strexb clrex b bl bx blx cbz cbzn tbb tbh svc it cpsid cpsie mrs msr bkpt sxth sxtb uxth uxtb ubfx sbfx bfc bfi rev rev16 revsh rbit sev wef wfi nop isb dmb dsb 加算 減算 乗算 除算 飽和演算 比較 論理演算 シフト ロード ストア スタック操作 セマフォ 分岐 状態変更 型の拡張 ビットフィールド ビット列反転 ヒント バリアARMv7m
  • 70. mov movw movt add adc adr sub sbc rsb mul mla mls smull umull smlal umlal sdiv udiv ssat usat cmp cmn and eor orr orn bic mvn tst teq lsl lsr asr ror rrx clz ldr ldrh ldrb ldrsh ldrsb ldrt ldrht ldrbt ldrsht ldrsbt ldrd ldm str strh strb strsh strsb strt strht strbt strsht strsbt strd stm push pop ldrex ldrexh ldrexb strex strexh strexb clrex b bl bx blx cbz cbzn tbb tbh svc it cpsid cpsie mrs msr bkpt sxth sxtb uxth uxtb ubfx sbfx bfc bfi rev rev16 revsh rbit sev wef wfi yeild nop isb dmb dsb 移動 加算 減算 乗算 除算 飽和演算 比較 論理演算 シフト ロード ストア スタック操作 セマフォ 分岐 状態変更 型の拡張 ビットフィールド ビット列反転 ヒント バリア M1/M0+/M0 ARMv6m
  • 71. mov movw movt M1/M0+/M0 add adc adr sub sbc rsb mul mla mls smull umull smlal umlal sdiv udiv ssat usat cmp cmn and eor orr orn bic mvn tst teq lsl lsr asr ror rrx clz ldr ldrh ldrb ldrsh ldrsb ldrt ldrht ldrbt ldrsht ldrsbt ldrd ldm str strh strb strsh strsb strt strht strbt strsht strsbt strd stm push pop ldrex ldrexh ldrexb strex strexh strexb clrex b bl bx blx cbz cbzn tbb tbh svc it cpsid cpsie mrs msr bkpt sxth sxtb uxth uxtb ubfx sbfx bfc bfi rev rev16 revsh rbit sev wef wfi yeild nop isb dmb dsb 移動 加算 減算 乗算 除算 mul 飽和演mla 算 mls smull umull smlal umlal 比較 論理演算 シフト ロード ストア mullは犠牲になったのだ スタック操作 セマフォ 分岐 状態変更 型の拡張 ビットフィールド ビット列反転 ヒント バリアARMv6m
  • 72. mull無しでどうやって 結果が64bitの乗算をするか a b a*d b*d abcdはそれぞれ16bit * c d a*c b*c ((a*c)<<32)+((a*d+b*c)<<16)+b*d 筆算の要領で16bitづつ 計算していく
  • 73. a b a*d b*d * c d a*c b*c ((a*c)<<32)+((a*d+b*c)<<16)+b*d 1サンプル毎にこんな計算を 何度もしていたら間に合わない
  • 74. 64bitの乗算を 回避せよ
  • 75. -1<結果<1が明らかな場合 0000.FFFF! 0000.FFFF * 00000000.FFFE0001 32bit目から47bit目は明らかなので 16bit目から31bit目までが欲しい この計算はmul命令でできる
  • 76. -1<結果<1が明らかな場合 実はFM音源の計算中に現れる 乗算の多くにこれが成り立つ 音量の時間変化 FM変調ADSR (最大1) 両辺共に1の場合のみ 注意すればmulでOK
  • 77. -1<結果<1が明らかな場合 実はFM音源の計算中に現れる 乗算の殆どにこれが成り立つ i1 i2 i3 i4 l1 l2 l3 l4 ミキサー i1*l1+i2*l2+i3*l3+i4*l4 入力はFMオペレータの出力なので最大1 重みは全部あわせて最大1 両辺共に1の場合のみ注意すればmulでOK
  • 78. 16.16固定小数点数と整数の乗算 FFFF.FFFF! FFFF * FFFEFFFF.0001 結果を16.16で得るには 0bit目から31bit目までが欲しい この計算はmul命令でできる
  • 79. 時刻行列 ブロックの中で何番目のサンプルかは 整数で表される 0 1 2 … …31 t = 0*fs + ss t= 31*fs + ss サンプルのインデックスと時刻の関係は線形
  • 80. 時刻行列 2x2の行列で表現出来る tはサンプルの時刻 iはサンプルのインデックス fsはPCMの周波数 ssはこのブロックの再生が始まる時刻 サンプルのインデックスと時刻の関係は線形
  • 81. トーン時間 実際の時刻に対して 音の周波数倍で進行するクロック トーン時間の小数部分だけを取り出すと 意図した周波数と位相のノコギリ波になる
  • 82. トーン行列 時刻とトーンクロックの関係は線形なので 2x2の行列で表現できる tはサンプルの時刻、ttはサンプルのトーン時刻 ftはノートの周波数、stはノートの位相 この乗算は小数同士かつ 結果の絶対値が1以上になる
  • 83. トーン行列 時刻とトーンクロックの関係は線形なので 2x2の行列で表現できる この部分が事前に求まっていれば インデックスは整数なので トーンクロックは高速な乗算で求まる
  • 84. ブロックの開始時刻がブロック毎に変わるので ブロック毎に 時刻行列とトーン行列の積を求める 行列の積の計算には遅い乗算が入るが ブロックにつき1回の遅い乗算で サンプル単位の遅い乗算を回避出来る
  • 85. 結果
  • 86. http://youtu.be/DDLSEirr8_A
  • 87. まとめ 小さなARMには 大きな魅力が秘められている
  • 88. Thank you for listening.