Vengineerの戯言

人生は短いけど、長いです。人生を楽しみましょう!

Apple iPhone 12には、NFCコントローラが2個載っている

@Vengineerの戯言 : Twitter
SystemVerilogの世界へようこそすべては、SystemC v0.9公開から始まった 

はじめに

iPhone 11 の device tree を眺めていて気が付いたのが、stockholm というキーワード。Google君に聞いたら、見つかりました。

ここ

The Secure Element (or SE) is the NFC/ApplePay chip. This chip is codenamed Stockholm.

ほう。NFC/ApplyPay 用のコントローラなのね。ということで、iPhone 11 Pro Max の Teardown にて確認。

NXP SN200 NFC&SE Module

というのがありました。Google君に聞いたら、NXP SN200というのはありませんでした。しかしながら、NXP SN100Uというの OEM としてあるので、これの仲間なんでしょうね。この NXP SN100U は、Xiaomi Mi 10 で使われているようですね。

それから ここ によると、iPhone 11 Pro Max にはNXP 100VB27 が載っているようです。Appleが使わなくなる(なった)ので、NXPはSN100Uを EOM販売することにしたんでしょうかね。

Update: We found a new die in the NXP SN200 that is different from the previous SN100 used in last year’s iPhone Xs/Xs Max/XR.

iPhone 12 は?

iPhone 11 ではStockholmだけでしたが、iPhone 12 の device tree では Stockholm だけでなく、hammerfestというのも追加されています。rf-antenna-nameとfunction-enableが違うので違う機能なんでしょうね。

hammerfest は、どんな機能なのかな?ということで、Google君に聞いたら出てきました。どうやら、「MacSafe section as “Accessory Identification NFC.”」ということです。

atadistance.net

アップルのサイトにもありました。

MagSafe

  • 最大15Wのワイヤレス充電10
  • マグネットアレイ
  • アラインメントマグネット
  • アクセサリ識別NFC
  • 磁力計

iPhone 11では Stockholmはuart4 に接続していましたが、iPhone 12 では aop-spmi0 でstockholm-spmi => stockholm, hammerfest-spmi => hammerfest のように2つ接続されているようですね。

終わりに

iPhone 11 だけでなく、iPhone 12 の device tree を見つけられたので、こんなことも知ることができました。
Apple M1機の device tree ないですかね?

Apple M1機の PCI Express は、3ポート?

@Vengineerの戯言 : Twitter
SystemVerilogの世界へようこそすべては、SystemC v0.9公開から始まった 

はじめに

Apple M1搭載の Macbook AirMacbook ProMac mini では、M1に接続している PCIe デバイスが違います。

上記のことから、Apple M1 には 3つの PCIe Port があるようです。

dts から PCI Express の Port を確認する

dts ファイルの この部分 が PCIe Controller になります。

レジスタ空間ですが、以下のように 6つ あります。

  • PCI Configuration Space
  • IP Core
  • AXI Bridge
  • Port (0)
  • Port (1)
  • Port (2)
            reg = <0x6 0x90000000 0x0 0x1000000                                                                 /* config */
                   0x6 0x80000000 0x0 0x100000  0x6 0x8c000000 0x0 0x100000                                     /* core and AXI bridge */
                   0x6 0x81000000 0x0 0x20000   0x6 0x82000000 0x0 0x20000   0x6 0x83000000 0x0 0x20000>;       /* ports */

Mac mini の場合、Port に以下のように接続されているのだと思います。

  • Wifi 6 / Bletooth 5
  • USB 3.0 Hub
  • GbE

また、perst-gpios と clkreq-gpios なるものがあります。

            pinctrl-0 = <&pcie_clkreq_pins>;

            perst-gpios = <&gpio 152 0   &gpio 153 0   &gpio 33 0>;
            clkreq-gpios = <&gpio 150 0   &gpio 151 0   &gpio 32 0>;

の pcie_clkreq_pins は、gpio のところの下記のようになっています。pcie_clkreq_pins ということは、PCI Express の CLKREQ# 信号のことだと思います。この信号が変化すると、GPIO から割り込みが入るようになるんだと思います。
ここでも 3 ピンありますので、3つの PCIe Port は正しいと思います。clkreq-gpios でしていしている GPIO のピン番号は、下記のように pcie_clkreq_pins と同じピン番号になっています。

            pcie_clkreq_pins: pcie_clkreq_pins {
                pins = "gpio150", "gpio151", "gpio32";
                function = "periph";
            };

perst-gpio は、PCI Express のリセット信号(#PERST) です。こちらも 3つあります。PERST# に関しては、デバイスドライバここ で次のように制御しています。PERST# を 0 に、ポートを初期化 (pcie_apple_m1_init_port(pcie, idx)、Reference Clockのドライブ (pcie_apple_m1_setup_refclk(pcie, idx)、ポートの電源制御 (pcie_apple_m1_port_pwron(pcie, idx)、PERST# を 1 にして、リセットを解除する

	gpiod_direction_output(pcie->perstn[idx], 0);

	pcie_apple_m1_init_port(pcie, idx);

	snprintf(tunable_name, sizeof(tunable_name) - 1, "tunable-port%d-config", idx);
	res = pcie_apple_m1_tunable(pcie, tunable_name);
	if(res < 0)
		return res;

	rmwl(0, PORT_APPCLK_EN, pcie->base_port[idx] + PORT_APPCLK);

	res = pcie_apple_m1_setup_refclk(pcie, idx);
	if(res < 0)
		return res;

	pcie_apple_m1_port_pwron(pcie, idx);

	rmwl(0, PORT_PERST_OFF, pcie->base_port[idx] + PORT_PERST);
	gpiod_direction_output(pcie->perstn[idx], 1);

PCI Controller の デバイスドライバは、ここ(pcie_apple_m1_setup_port) にあります。デバイスドライバの中でも NUM_PORTマクロは、3になっています。

下記の部分は、電源制御部で Port (0) は 何らか<0 1>を設定していますが、Port (1) と Port (2) は何も設定されていません。

            devpwr-on-0 = <0 1>;
            devpwr-on-1 = <>;
            devpwr-on-2 = <>;

設定された値は、デバイスドライバここ(pcie_apple_m1_port_pwron) で次のような処理をやっています。<0 1> なので、GPIO を出力にして、1 をドライブする感じですね。

static void pcie_apple_m1_port_pwron(struct pcie_apple_m1 *pcie, unsigned idx)
{
	int i;
	u32 *seq = pcie->devpwron[idx].seq;

	for(i=0; i<pcie->devpwron[idx].num; i++) {
		usleep_range(2500, 5000);
		if(seq[1] < 2)
			gpiod_direction_output(pcie->devpwrio->desc[seq[0]], seq[1]);
		else if(seq[1] == 2)
			gpiod_direction_input(pcie->devpwrio->desc[seq[0]]);
		usleep_range(2500, 5000);
usleep_range(250000, 500000);
		seq += 2;
	}
}

下記の rerfclk-always-on-2 と max-speed-2 =<1> では、Port (2) に対する refrence clock (100MHz) を常に供給し、PCIe Gen1 (2.5GT/s) に設定しています。
これにより、Port (2)は GbE が繋がっているんだと思います。

            refclk-always-on-2;
            max-speed-2 = <1>; /* 2.5 GT/s */

I/O MMU は、各ポートにそれぞれ付いています。

            iommu-map = <0x0000 &pcie_dart0 0x8000 0x0100>, /* fake, and should never be used as RC bridges don't DMA */
                        <0x0100 &pcie_dart0 0x0000 0x0100>,
                        <0x0200 &pcie_dart1 0x0000 0x0100>,
                        <0x0300 &pcie_dart2 0x0000 0x0100>;

Apple Siliconの説明のビデオにも出てきた各デバイス毎に I/O MMU が接続されていることが分かります (Apple M1内の各機能だけでなく、PCI Express もそのようになっています)

f:id:Vengineer:20210214124551p:plain

PCI Express Phy

一般的には PCI Express の各 Port 毎に Phy が付いているのですが、Apple M1 の 3つの PCI Express の Port に関しては、1つのPhy を 3つの Port を共有しているようです。
デバイスドライバここ の部分(pcie_apple_m1_setup_phy_global)が初期設定部分です。

2つのクロック(CLK0REQ, CLK1REQ)を設定して、PHYを REST => RUN にしているだけっぽいですが。

	rmwl(0, CORE_PHY_CTL_CLK0REQ, pcie->base_core[0] + CORE_PHY_CTL);
	res = readl_poll_timeout(pcie->base_core[0] + CORE_PHY_CTL, stat, (stat & CORE_PHY_CTL_CLK0ACK), 100, 50000);
	if(res < 0) {
		dev_err(&pcie->pdev->dev, "%s: global phy clk%d wait timed out.\n", __func__, 0);
		return res;
	}
	udelay(1);
	rmwl(0, CORE_PHY_CTL_CLK1REQ, pcie->base_core[0] + CORE_PHY_CTL);
	res = readl_poll_timeout(pcie->base_core[0] + CORE_PHY_CTL, stat, (stat & CORE_PHY_CTL_CLK1ACK), 100, 50000);
	if(res < 0) {
		dev_err(&pcie->pdev->dev, "%s: global phy clk%d wait timed out.\n", __func__, 1);
		return res;
	}
	rmwl(CORE_PHY_CTL_RESET, 0, pcie->base_core[0] + CORE_PHY_CTL);
	udelay(1);
	rmwl(0, CORE_RC_PHYIF_CTL_RUN, pcie->base_core[0] + CORE_RC_PHYIF_CTL);
	udelay(1);

この後に、以下のような、pcie_apple_m1_tunable という関数で、"tunable-xxxx" ものを抽出しているのですが、dts ファイルではそんなの設定していません。

	rmwl(0, 1, pcie->base_config + (idx << 15) + 0x8bc);
	snprintf(tunable_name, sizeof(tunable_name) - 1, "tunable-port%d", idx);
	res = pcie_apple_m1_tunable(pcie, tunable_name);
	if(res < 0)
		return res;
	rmwl(0x3000000, 0, pcie->base_config + (idx << 15) + 0x890);
	snprintf(tunable_name, sizeof(tunable_name) - 1, "tunable-port%d-gen3-shadow", idx);
	res = pcie_apple_m1_tunable(pcie, tunable_name);
	if(res < 0)
		return res;
	rmwl(0x3000000, 0x1000000, pcie->base_config + (idx << 15) + 0x890);
	snprintf(tunable_name, sizeof(tunable_name) - 1, "tunable-port%d-gen4-shadow", idx);
	res = pcie_apple_m1_tunable(pcie, tunable_name);

調べてみたら、Documentation/devicetree/bindings/arm/apple,pcie-m1.txt の中に以下のような説明がありました。「

Filled by bootloader:

- tunable-axi2af : settings applied to AXI bridge registers
- tunable-common : settings applied to root complex registers
- tunable-phy-ip-auspma : settings applied to PHY PMA registers
- tunable-phy-ip-pll : settings applied to PHY PLL registers
- tunable-phy : settings applied to PHY core registers
- tunable-fuse : settings applied to PHY registers based on on-chip fuses
- tunable-port%d-config : settings applied to port registers for port %d
- tunable-port%d : PCIe Gen1/2 settings applied to port config space
- tunable-port%d-gen3-shadow : PCIe Gen3 settings applied to port config space
- tunable-port%d-gen4-shadow : PCIe Gen4 settings applied to port config space

Apple M1で Linux をロードするまでのステップはどうなっているのかを調べてみた」を書くために preloader-m1ソースコードを調べているときに、なんか出てきたのを思い出しました。ここ です。

    prepare_tunable(apple_dt, "/arm-io/apcie",             "apcie-axi2af-tunables",        linux_dt, "/soc/pcie", "tunable-axi2af",            TUNABLE_PCIE, 4);
    prepare_tunable(apple_dt, "/arm-io/apcie",             "apcie-common-tunables",        linux_dt, "/soc/pcie", "tunable-common",            TUNABLE_PCIE, 1);
    prepare_tunable(apple_dt, "/arm-io/apcie",             "apcie-phy-ip-auspma-tunables", linux_dt, "/soc/pcie", "tunable-phy-ip-auspma",     TUNABLE_PCIE, 3);
    prepare_tunable(apple_dt, "/arm-io/apcie",             "apcie-phy-ip-pll-tunables",    linux_dt, "/soc/pcie", "tunable-phy-ip-pll",        TUNABLE_PCIE, 3);
    prepare_tunable(apple_dt, "/arm-io/apcie",             "apcie-phy-tunables",           linux_dt, "/soc/pcie", "tunable-phy",               TUNABLE_PCIE, 2);
    prepare_tunable(apple_dt, "/arm-io/apcie/pci-bridge0", "apcie-config-tunables",        linux_dt, "/soc/pcie", "tunable-port0-config",      TUNABLE_PCIE_PARENT, 6);
    prepare_tunable(apple_dt, "/arm-io/apcie/pci-bridge0", "pcie-rc-gen3-shadow-tunables", linux_dt, "/soc/pcie", "tunable-port0-gen3-shadow", TUNABLE_PCIE_PARENT, 0);
    prepare_tunable(apple_dt, "/arm-io/apcie/pci-bridge0", "pcie-rc-gen4-shadow-tunables", linux_dt, "/soc/pcie", "tunable-port0-gen4-shadow", TUNABLE_PCIE_PARENT, 0);
    prepare_tunable(apple_dt, "/arm-io/apcie/pci-bridge0", "pcie-rc-tunables",             linux_dt, "/soc/pcie", "tunable-port0",             TUNABLE_PCIE_PARENT, 0);
    if(!strcmp(model, "J274")) {
        /* Mac Mini has xHCI and Ethernet */
        prepare_tunable(apple_dt, "/arm-io/apcie/pci-bridge1", "apcie-config-tunables",        linux_dt, "/soc/pcie", "tunable-port1-config",      TUNABLE_PCIE_PARENT, 10);
        prepare_tunable(apple_dt, "/arm-io/apcie/pci-bridge1", "pcie-rc-gen3-shadow-tunables", linux_dt, "/soc/pcie", "tunable-port1-gen3-shadow", TUNABLE_PCIE_PARENT, 0 | 0x8000);
        prepare_tunable(apple_dt, "/arm-io/apcie/pci-bridge1", "pcie-rc-gen4-shadow-tunables", linux_dt, "/soc/pcie", "tunable-port1-gen4-shadow", TUNABLE_PCIE_PARENT, 0 | 0x8000);
        prepare_tunable(apple_dt, "/arm-io/apcie/pci-bridge1", "pcie-rc-tunables",             linux_dt, "/soc/pcie", "tunable-port1",             TUNABLE_PCIE_PARENT, 0 | 0x8000);
        prepare_tunable(apple_dt, "/arm-io/apcie/pci-bridge2", "apcie-config-tunables",        linux_dt, "/soc/pcie", "tunable-port2-config",      TUNABLE_PCIE_PARENT, 14);
        prepare_tunable(apple_dt, "/arm-io/apcie/pci-bridge2", "pcie-rc-gen3-shadow-tunables", linux_dt, "/soc/pcie", "tunable-port2-gen3-shadow", TUNABLE_PCIE_PARENT, 0 | 0x10000);
        prepare_tunable(apple_dt, "/arm-io/apcie/pci-bridge2", "pcie-rc-gen4-shadow-tunables", linux_dt, "/soc/pcie", "tunable-port2-gen4-shadow", TUNABLE_PCIE_PARENT, 0 | 0x10000);
        prepare_tunable(apple_dt, "/arm-io/apcie/pci-bridge2", "pcie-rc-tunables",             linux_dt, "/soc/pcie", "tunable-port2",             TUNABLE_PCIE_PARENT, 0 | 0x10000);

"apcie-config-tunables", "pcie-rc-gen3-shadow-tunables", , "pcie-rc-gen4-shadow-tunables", , "pcie-rc-tunables" は、PCI Configuration Space のアドレスを設定するためのもので、
レジスタ空間の1番目の PCI Configuration Space の 0x0, 0x8000, 0x10000 を各Port に設定しています。

また、

/* Mac Mini has xHCI and Ethernet */

とありますね。となると、Mac Mini の Port (1) が xHCI (USB 3.0 Hub) で、Port (2) が Ethernet に決まりですね。

終わりに

Apple M1 には、PCI Express のポートが3つ付いていることが分かりました。また、このブログで2月1日に書きましたNVMe も PCI Express なので、Apple M1には4つの PCI Express が出ていることが分かりました。

実は、このブログを書いた後、Apple M1について、何を書いたかな?と振り返ってみたら、「Apple M1 Mac mini の PCIe 関連を調べてみた」でPCI Express について書いていたことが発覚。まー、すべてが同じということではないので、ちょっと内容を追加したので、そのままアップします。

参考ブログ:
vengineer.hatenablog.com

vengineer.hatenablog.com

Apple M1機の USB 3.0/Thunderbolt 4

@Vengineerの戯言 : Twitter
SystemVerilogの世界へようこそすべては、SystemC v0.9公開から始まった 

はじめに

Apple M1機には、USB Type-C/Thunderbolt 4 のコネクタが2つ付いています。

IFIXITの M1 MacBook Teardowns: Something Old, Something New には、

  • Intel JHL8040R Thunderbolt 4 Retimer (x2) (basically a Thunderbolt 4 extender/repeater)

というのがあります。

以下、簡単ですが、この2つのチップについて調べました。

Universal USB Type-C and Power Delivery (PD) 3.0 controller

dtsファイル の中を見ると、I2C に 2つの TI の TPS6598 が繋がっているのが分かります。

        i2c0: i2c0@20a110000 {
            compatible = "apple,i2c-v0";
            reg = <0x2 0x35010000 0x0 0x4000>;
            interrupts = <0 627 4>;
            clocks = <&i2c0_clk>;
            pinctrl-0 = <&i2c0_pins>;
            pinctrl-names = "default";
            #address-cells = <0x1>;
            #size-cells = <0x0>;

            hpm0: hpm0@38 {
                compatible = "ti,tps6598x";
                reg = <0x38>;
                interrupt-parent = <&gpio>;
                interrupts = <106 8>;

                no-long-writes;

                typec0: connector {
                    compatible = "usb-c-connector";
                    label = "USB-C A";
                    power-role = "source";
                    data-role = "host";
                };
            };

            hpm1: hpm1@3f {
                compatible = "ti,tps6598x";
                reg = <0x3f>;
                interrupt-parent = <&gpio>;
                interrupts = <106 8>;

                no-long-writes;

                typec1: connector {
                    compatible = "usb-c-connector";
                    label = "USB-C B";
                    power-role = "source";
                    data-role = "host";
                };
            };
        };

TI のサイトで調べたら、TPS65983B : Universal USB Type-C™ and Power Delivery (PD) 3.0 controller でした。ブロック図を見ると、I2Cのインターフェースが付いているようです。

Thunderbolt 4 Retimer

下図は、USB4で採用した3つの新機能、高速化には「あの部品」が不可欠にからの引用です。

f:id:Vengineer:20210213133511p:plain

M1 SoC から この Thunderbolt 4 Retimer を経由して、コネクタに繋がっているんですね。

もっと具体的には、ここ にブロック図がありました。引用します。

f:id:Vengineer:20210213134126p:plain

USB IP

USB IP は、下記のように、Synopsys の DWC3 ですね。モードとしては、Host として使っています。Apple M1 にはこれが2つ載っています。

        usb_drd0: usb_drd0@382280000 {
            compatible = "apple,dwc3-m1";
            clocks = <&atc0_usb>;
            clock-names = "usbclk";
            reg = <0x3 0x80000000 0x0 0x1000000   0x3 0x82000000 0x0 0x1000000>;
            reg-names = "atcphy", "usbcore";
            #address-cells = <2>;
            #size-cells = <2>;
            ranges;

            usbdrd_dwc3_0: dwc3@382280000 {
                compatible = "snps,dwc3";
                reg = <0x3 0x82280000 0x0 0x100000>;
                interrupts = <0 777 4>;
                iommus = <&usb_dart0 1>;
                dr_mode = "host";
            };
        };

デバイスドライバ (apple,dwc3-m1) は、ここ にあります。

Apple M1機では、USB 2.0 は繋がっているのだろうか? ここ を見た感じでは、USB 2.0 もつながっているようですね。

Synopsys の DesignWare USB 3.0 Digital Controller IP の説明を眺めたら、

  • USB 3.0 PIPE and USB 2.0 UTMI/UTMI+ interfaces for PHYs

とあるので、USB 3.0 と USB 2.0 の両方繋がるんですね。

終わりに

USB 3.0 Host Controller と言えば、やっぱり、Synopsys なんですね。接続テストとか考えると、デファクトスタンダートなものを使うのが一番いいですからね。

Apple の Device Treeって、何だ?

@Vengineerの戯言 : Twitter
SystemVerilogの世界へようこそすべては、SystemC v0.9公開から始まった 

はじめに

今日は、github のとあるところで見つけた、Apple A13 (iPhone 11 Pro) の Device Tree を探っていきます。

Device Treeって、Linux では、arch/arm64/boot/dts の下にあるようなファイルです。Linux では、ARMより前に、PowerPCの方が先で 昔流行った kurobox の dts ファイルの最初のバージョンは、10 Feb 2007 になっています。あたしが Linux に興味を持ったのは、1997年頃なのでそれよりも10年経ってからですね。

Device Tree は、Linuxでは PowerPCの方が先ですが、もっと古い情報は、ここ にありました。この 3ページ目にあるものを以下に引用します。

  • Sun Microsystems - Open Boot / OpenFirmware (1988)
    • Used in SPARC systems
    • Uses DT to describe hardware
  • IEEE-1275 formalized specification (1994) Documents DT
  • Apple adopts Open Firmware on Power Mac 7200 (1995) using DT
  • Common Hardware Reference Platform (CHRP) specifies DT (1995)
  • ePAPR specifies DT (2008)

これを見ると、PowerPCの前がSunですね。Open Boot / OpenFirmware ですね。あたしもこの頃、Sun 関連をやっていましたが、そういうものがあるねという程度だったんですよね。

その後、Linuxでお世話になるとは思いもよらなかったです。

Apple A13 (iPhone 11 Pro) の Device Tree

Apple の製品でも、Device Tree が使われているのを知ったので、Google 君にいろいろと聞いてみたら、見つけました

この Device Tree からわかったことは、

  • CPU 関連
    • eCore の方が CPUの番号とClusterの番号が若い
    • eCore の L2は4MB、pCore の L2は8MB (公開情報)
    • BootするCPUは、CPU-0 なので、eCore
  • Memory 関連
    • メモリは、0x8_0000_0000 から
    • Hynix製
    • pram (Parameter RAM/NVRAM) や Video RAM も メモリにある
    • Coprocessor の命令コードも メモリにある
  • 各種アクセラレータ
    • GPU
    • Apple Neural Engine : ane
    • Video Encoder
    • Video Decoder
    • JPEG (x2)
    • ISP
    • Scalar (scaler and colourspace converter for the display)
  • Coprocessor 搭載機能
  • I/O
    • NVMe
    • PCIe : WLAN/Bluetooth (Broadcom)
    • USB
    • MIPS : display (Synopsys)
    • MCA : Audio
    • Termal Sensor
    • I2S : Audio
    • I2C :
    • SPI : Mult Touch / Audio Codec
    • UART (x3)
    • GPIO
  • 各種アクセラレート、Coprocessor、I/O の中で、メモリとDMAをするものは、I/O MMU
    • Page Size は、0x4000 なので、16KB

終わりに

Device Tree からある程度ハードウェアの構成が分かります。この情報と、テカナリエレポートの分解解析レポートをにらめっこしながらいろいろと妄想すると、めっちゃ面白いですよ。

Apple M1の Device Tree も見たいのですが、Boot Message をパブリックにするのって、規約違反なんでしょうかね。
What happens when an M1 Mac starts up? に、Boot Message の一部が載っていましたが、全部知りたいです。

Apolloは、チョコじゃないよ、AIアクセラレータのアーキテクチャを探索してくれるよ。。。

@Vengineerの戯言 : Twitter
SystemVerilogの世界へようこそすべては、SystemC v0.9公開から始まった 

はじめに、

Google AIのブログにアップされたこのブログ「Machine Learning for Computer Architecture」
Google AI Blog: Machine Learning for Computer Architecture

人間が決めていたAIアクセラレータのアーキテクチャをMLにて決めるというもの。

1月18日の
vengineer.hatenablog.com
の「Eyeriss v2: A Flexible Accelerator for Emerging Deep Neural Networks on Mobile Devices 」でも同じような感じで、PE、MACNoC、PE Architecture、PE SIMD Support、Global Buffer Size、Areaでの検討をやっているので、比較すると面白いかもしれませんね。

今日のブログでは、AIアクセラレータのアーキテクチャをMLで探索するというものを見ていきたいと思います。

Apollo: Transferable Architecture Exploration

ブログの大本の論文は、「Apollo: Transferable Architecture Exploration」Apolloの対象はエッジ用アクセラレータ。
arxiv.org

探索の対象としたモデル

下表(論文から引用しています)のようなモデルに対して、コンピュータアーキテクチャを探索しているようです。MobileNetV2、MobileNetEdge以外の M3からM7については具体的な名前は付いていないものの Layers、Params、# of MACs を見る限り、MobileNetV2やMobileNetEdgeに比べて大幅な増加がみられないのでエッジ側のコンピュータ・アーキテクチャを決めたいのかなと思いました。
f:id:Vengineer:20210206091843p:plain

評価関数

テンプレートとなるAIアクセラレータのアーキテクチャは下記の図(ブログから引用しています)です。この図は論文にはありません。チップ外にはDRAMが付いているのでパラメータがそれなりに大きくなっても使える感じのアクセラレータを想定しているんでしょうね。DRAMからController内のメモリに、Activation用メモリ、命令用メモリ、パラメータ用メモリがあり、これらのメモリは各PEが共通して使用する。各PEには複数のコア(メモリ)とコアで共有して使うPEメモリがあります。
f:id:Vengineer:20210206092104p:plain

論文には下表のようなパラメータ(上記の図の各構成要素をパラメータにしている感じ)を変えて、AIアクセラレータのアーキテクチャを探索したようです。最後の I/O Bandwidth は外部にDRAMを接続しているので必要なパラメータだと思います。全体としては、5 x 10^8 ぐらい。。。5000万パターン。。。莫大だ。。。
f:id:Vengineer:20210206092233p:plain

探索時の評価関数は、下記のように、上記のAIアーキテクチャのパラメータだけでなく、workloads (w) を入力として、面積 と Latency を設定した数値以下にする感じですかね。
f:id:Vengineer:20210206111916p:plain

最適化戦略

Apolloでは、最適化の方法としては、次の5つを使っています。詳細は省きます。

  • Evolutionary
  • Model-Based Optimization (MBO)
  • Population-Based black-box optimization (P3BO)
  • Random
  • Vizier

評価

  • Single model architecture search

下図(論文からの引用)では、各モデルに対して、各最適化戦略を適応した時に、1/Latency/Area vs step です。Random (赤)が下の方にあるので、上の方にある方がいいのかな?
青(Evolutionary)、緑(Population-Based black-box optimization (P3BO))、紫(Vizier)の順でいい感じ?
f:id:Vengineer:20210206120611p:plain

  • Multi-model architecture search

Area Budgetを 6.8, 5,8, 4.8 に変えたの時の図(論文からの引用)。
こちらでは、緑(Population-Based black-box optimization (P3BO))、青(Evolutionary)の順。紫(Vizier)は全然ダメですね。。Area Budget が 5.8, 4.8 だと、1.0 以下ですね。1.0 は、
The baseline runtime numbers are obtained from a productionized edge accelerator.
とありますね。となると、Area Budge は、6.8 ㎜^2 でないと、Google Edge TPUよりはよくならないと。。。
f:id:Vengineer:20210206120514p:plain

  • Transfer learning between optimizations with different constraints

Area Budget 6.8 mm^2 の学習結果を、Area Budget 4.8 mm^2 に転移学習してみたら、下図(論文からの引用)のように、全体的によくなった。特に、ダメだった Vizer が他の戦略(EvolutionaryとP3BO)以上によくなっている。
あれ、もしかしたら、紫(Vizier) がいいのを確認するためのものだったの?
f:id:Vengineer:20210206121454p:plain

どうしてそうなったかは、

This suggest that Vizier uses the selected trials from the source task more efficiently than Evolutionary and P3BO for optimizing the target task.

ということのようですね。

そして、

We leave extending Evolutionary and P3BO by more advanced transfer learning approaches as future work.

ともありますね。

終わりに、

論文のPage.2に、

In this work, we use an in-house and highly parameterized edge accelerator.

と記載があります。
現在市販されている Google Edge TPUは、32コア(リング構成になっていると妄想)ですが、次のEdgeTPUはDRAM付の2D構成のPEになるんでしょうかね。Google Edge TPUのコアとこの論文のPE(PE内部のコア)には互換性があるんでしょうかね。Pixel 4 に搭載されている Pixel Neural Core だと DRAM(LPDDR4)が付いているので、Pixel 6に搭載する EdgeTPUなんでしょうかね?。

1月2日にこのブログにアップした「ハードウェアを意識したNASからハードウェアの構成も一緒に決めるNASに!」では、ハードウェアの構成も一緒に決めるNAS、NAHASを紹介しました。
vengineer.hatenablog.com

今日紹介した Apollo も同じような感じですね。ApolloGoogleからですが、NAHASはどこからなんでしょうね。。Google からだと思っていましたが、Apollo が出てきたので、そうじゃないのかな?

AIアクセラレータのマクロ・アーキテクチャーは人間が決めるものだと思いますが、マイクロ・アーキテクチャについては今回紹介したブログ・論文にあるようにMLにて決めた方が良くなるんでしょうね。
人間はより上位のマクロ・アーキテクチャを決めていくしかないんでしょうね。。。辛いと思うか、楽しいと思うかは人それぞれですが、、、実際の半導体に実装できる環境じゃないと、マクロ・アーキテクチャを決めても「絵に描いた餅」なんですけどね。。

最後に、昨日の MobileDets の論文でもそうだったが、

Our results indicate that transfer learning is effective in improving the target architecture exploration, especially when the optimization constraints have tighter bounds

とあるので、転移学習は重要なんだね。。

P.S
Conclusion に、

The evolution of accelerator architectures mandates broadening the scope of optimizations to the entire computing stack, including scheduling and mapping, that potentially yields higher benefits at the cost of handling more complex optimization problems. We argue that such co-evolution between the cascaded layers of the computing stack is inevitable in designing efficient accelerators honed for a diverse category of applications. This is an exciting path forward for future research directions.

とあったので、まだまだ研究は続く。

MobileDets は、ハードウェアプラットフォーム間でのモデルアーキテクチャの転移可能性を見つけたよー。

@Vengineerの戯言 : Twitter
SystemVerilogの世界へようこそすべては、SystemC v0.9公開から始まった 

はじめに、

Google AIのブログにアップされたこのブログ「Machine Learning for Computer Architecture」
Google AI Blog: Machine Learning for Computer Architecture

人間が決めていたAIアクセラレータのアーキテクチャをMLにて決めるというもの。

ブログの大本の論文は、「Apollo: Transferable Architecture Exploration」
arxiv.org

Apolloの著者の一人である Berkin Akin さんの論文を調べてみたら、「MobileDets: Searching for Object Detection Architectures for Mobile Accelerators」というものを見つけました。v1は 2020年4月30日なのでもう1年近く前になっていますね。こちらの論文がそれほど騒がれなかったのはGoogle単体ではなく、University of Wisconsin-Madison との共著だったからだと思います。ちなみに、Berkin Akin さんは、CMUPh.Dです。
arxiv.org

この MobleDets の論文を眺めたら、非常に興味深いことが分かったので、ちょっとまとめてみました。

TensorFlowのモデルは、ここにあります。

MobileDets

下図は論文からの引用です。MobileNetV2をベースのモデルの探索をやっています。MobileNetV2のベースが25.3 mAP @ 12msに対して、IBN (Inverted Bottleneck Layer) だけを使った探索結果が 26.9 mAP @ 12ms になっています。Latencyが同じ(12ms)で、精度が1.6ポイント上がっています。これに対して、Fused conv (expansion) と Tucker conv(compression) という Layer を使った探索結果では 28.5mAP @ 12 ms で、1.6+1.6=3.2ポイント精度が上がったようです。
f:id:Vengineer:20210206115226p:plain

Fused conv (expansion) と Tucker conv(compression) については、下図(論文から引用)のような入力と出力は Inverted bottleneck layer と同じですが、内部の処理が違うって感じです。
f:id:Vengineer:20210206101624p:plain

MobileDetsのターゲットハードウェアは、Pixel 1 CPU、Pixel 4 EdgeTPU、Pixel 4 DSP (明示していないが、Snapdragon 855のHexagon 690だと思う)です。なんで、CPUが Pixel 4ではなく、Pixel 1のを使っているのかは謎です。

下図(論文から引用)がPixel 1 CPUでの探索結果です。CPUだと、MobileNetV3の方がMobileNetV2よりいい結果なので図にも載っているのだと思います。EdgeTPUやDSPではMobileNetV3の中で使われている h-swish と squeeze-and-exciteをサポートしていないので載せていないようです。Parate frontは、MobileNetV3ぐらいかそれ以上になっていますね。
f:id:Vengineer:20210206102108p:plain

下図(論文から引用)がPixel 4 EdgeTPUでの探索結果です。MobileNetV2に対してmAPが大幅に向上しています。
f:id:Vengineer:20210206102538p:plain

最後(論文から引用)は、Pixel 4 DSPでの探索結果です。こちらはLatencyが 11ms以上になっていますが、mAPは大幅に向上しています。
f:id:Vengineer:20210206102724p:plain

下表(論文から引用)を見ると、MAdds/Paramsが増えるけど、Latencyが小さくなり、mAP値が向上している感じですね。ターゲットハードウェアを EdgeTPUやDSPにした時のモデルをCPUで実行すると遅くなりますが、mAP値は上がっています。この表では、DSPをターゲットハードウェアにしてやったものが一番いいmAP値を出しています。時に、IBN+Fused を DSPで探索したモデルは、EdgeTPU、DSP共にMobileNetV2のLatencyとそれほど変わらないにも関わらず、mAP値は6.7ポイントも向上しているのが興味深いです。
f:id:Vengineer:20210206102919p:plain

グラフ(論文から引用)にすることこんな感じ。
f:id:Vengineer:20210206104259p:plain

モデルの構成は、下図(論文から引用)のようになったようです。CPUに関しては各Groupの後半をTruckerに、EdgeTPUに関しては前半をFusedに変更している感じです。DSPではばらばらですね。
f:id:Vengineer:20210206103655p:plain

終わりに、

MobileDets は mAP が向上しただけでなく、ハードウェアプラット間でのアーキテクチャの転移ができるということを見つけたのが大きな成果だと思います。

MobileDetsは、Pixel 4のEdgeTPUだけでなく、EdgeTPUが載っていないけど、Snapdragon 855が載っているスマホで NNAPIにてHexagon DSPが使えるなら積極的に使えそうです。

MobileNetV2に対して、ハードウェアのアーキテクチャが違う、CPU、EdgeTPU、DSP、でそれぞれモデルが変わるのは非常に興味深いです。なぜ、モデルが変わるかがわかれば、逆にアーキテクチャの改善ができると思います。

参考ブログ:
vengineer.hatenablog.com

Apple M1 で SSD(NVMe)から Linux がブートできるようになったので、NVMe関連を眺めてみた

@Vengineerの戯言 : Twitter
SystemVerilogの世界へようこそすべては、SystemC v0.9公開から始まった 

はじめに

Apple M1 Mac miniLinuxUSBメモリ経由でブートしていましたが、下記のツイートのように、先週、SSD(NVMe)からブートできるようになったようです。

Corelliumのブログが更新されていました。Apple M1 Mac mini だけでなく、Macbook Air/Macbook Pro でも USBメモリおよびSSD(NVMe)でブートできるようになったようです。
corellium.com

今日のブログでは、SSD(NVMe)関連について見ていきます。

NVMe とは

NVMe の仕様書は、ここ で公開されている。最新版は 1.4b です。

NVMe とは、ウィキペディア によると、

PCI Express (PCIe) を通じて、不揮発性ストレージメディアを接続するための論理デバイスインターフェースの規格であり、AHCIに代わる次世代の接続プロトコル規格である。

とあります。Apple M1 と SDD の接続は、PCIe で行うようです。
物理的な形状としては、

の3種類がありますが、一般的には M.2 コネクタのものが多いですね。

ウィキペディアには、iOSでは、
>>iPhone 6Sと6S Plusの発売でAppleスマートフォンにNVMe over PCIeを採用した初のモバイル展開を発表した。Appleはこれらの発売に続いて、同じくNVMe over PCIeを使用するiPad ProとiPhone SE を発売した<< ともあります。Apple M1は、iPhoneiPad用のAxxシリーズと基本的には互換性があるので、NVMe over PCIe を使っているのだと思います。

Apple M1 Mac mini/Macbook Air/Macbook Pro では、M.2 コネクタではなく、銀色のパッケージが2つ、基板上に載っています。
下の写真は、Apple M1 MacBook teardowns reveal surprises からの引用で、Apple M1 Macbook Pro の基板です。M1の左側に銀色のパッケージが2つ載っています。

https://zdnet1.cbsistatic.com/hub/i/r/2020/11/20/4c59a1c0-d558-462f-a3a3-9a3d2008e6b3/resize/1200xauto/0c45e4c9aa534195daac2fc7cfb49b6a/2020-11-20-08-11-01-2.jpg

下の写真は、FIXITのiPhone 12 and 12 Pro Teardownからの引用で、オレンジの四角で囲まれている部分がSSD(NVMe)です。この基板は Apple A14 Bionic が載っていて、SSD(NVMe)は裏側に載っています。

https://d3nevzfk7ii3be.cloudfront.net/igi/VC2EIMTYPUdYIacu.medium

Apple M1 の NVMe関連

NVMe関連のデバイスドライバは次の3つです。

それぞれ、見ていきましょう。

NVMe over PCIe デバイスドライバ (apple,nvme-m1)

dts ファイルの中での NVMe 関連は以下のところです。
ans の compatible のところが、apple,nvme-m1 になっています。
apple,nvme-m1 の device_type は、"pci" ですので、NVMe over PCIe ということになるのだと思います。

        ans: ans@27bcc0000 {
            compatible = "apple,nvme-m1";
            reg = <0x2 0x7bcc0000 0x0 0x40000  /* NVMe + Apple regs */
                   0x2 0x7bc50000 0x0 0x4000>; /* SART regs */
            interrupts = <0 590 4>;
            clocks = <&pcie_st_clk>;
            mboxes = <&ans_mbox 32>;

            #address-cells = <3>;
            #size-cells = <2>;
            #interrupt-cells = <1>;
            device_type = "pci";
            msi-controller;
            msi-parent = <&ans>;
            ranges = <0x02000000   0x0 0x7bc00000   0x2 0x7bc00000 0x0 0x00100000>;
            bus-range = <0x00 0x01>;
        };
    };

apple,nvme-m1 の デバイスドライバは、drivers/pci/controller の下の pcie-apple-m1-nvme.c になります。
このファイルの最初に以下のようなコメントがあります。Apple M1 には、ANS という coprocessor が居るようです。だから、apple,nvme-m1 のインスタンスの名前が ans になっています。

/*
 * Quick explanation of this driver:
 *
 * M1 contains a coprocessor, called ANS, that fronts the actual PCIe
 * bus to the NVMe device.  This coprocessor does not, unfortunately,
 * expose a PCIe like interface.  But it does have a MMIO range that is
 * a mostly normal NVMe BAR, with a few quirks (handled in NVMe code).
 *
 * So, to reduce code duplication in NVMe code (to add a non-PCI backend)
 * we add a synthetic PCI bus for this device.
 */

ans のレジスタ空間は、下記のように2個です。1番目のレジスタ空間は NVMe と Apple 専用のレジスタ、2番目のレジスタ空間は SART レジスタです。割り込みは1つ、クロックは pcie_st_clk、メールボックスは ans_mbox が 32 個付いています。

            reg = <0x2 0x7bcc0000 0x0 0x40000  /* NVMe + Apple regs */
                   0x2 0x7bc50000 0x0 0x4000>; /* SART regs */
            interrupts = <0 590 4>;
            clocks = <&pcie_st_clk>;
            mboxes = <&ans_mbox 32>;

クロックの pcie_st_clk は、下記のように、pcie_clk => ans_clk => pcie_st_clk になっていますので、大本は PCIe のクロックです。

        ans_clk: ans_clk@23b7003f0 {
            compatible = "apple,pmgr-clk-gate";
            #clock-cells = <0>;
            reg = <0x2 0x3b7003f0 0x0 0x8>;
            clocks = <&pcie_clk>;
            clock-output-names = "ans_clk";
        };

        pcie_st_clk: pcie_st_clk@23b700418 {
            compatible = "apple,pmgr-clk-gate";
            #clock-cells = <0>;
            reg = <0x2 0x3b700418 0x0 0x8>;
            clocks = <&ans_clk>;
            clock-output-names = "pcie_st_clk";
        };

メールボックスは、apple,iop-mailbox-m1 です。先ほどの ANS のところにあった coprocessor を意味するのか、iop というキーワードが付いています。
レジスタ空間が1つ、割り込みは2本、クロックは apple,nvme-m1 と同じ pcie_st_clk です。endpoints というものが 32 になっています。

        ans_mbox: ans_mbox@277400000 {
            compatible = "apple,iop-mailbox-m1";
            reg = <0x2 0x77400000 0x0 0x20000>;
            interrupts = <0 583 4   0 586 4>;
            clocks = <&pcie_st_clk>;

            #mbox-cells = <1>;
            endpoints = <32>;
            wait-init;
        };

apple,nvme-m1 に戻ります。初期化の apple_m1_ans_probe の下記の部分では、PCIe の ホストブリッジを生成しています。

	bridge = devm_pci_alloc_host_bridge(&pdev->dev, sizeof(*ans));
	if(!bridge)
		return -ENOMEM;
	ans = pci_host_bridge_priv(bridge);
	ans->bridge = bridge;
	ans->dev = &pdev->dev;
	platform_set_drvdata(pdev, ans);

下記の部分では、レジスタ空間の割り当てを行っています。レジスタ空間の 0 番では ans->nvme に、1番ではans->sart に ベースアドレスを設定してます。

	for(i=0; i<2; i++) {
		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
		if(!res) {
			dev_err(&pdev->dev, "missing MMIO range %d.\n", i);
			return -EINVAL;
		}
		base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
		if(IS_ERR(base)) {
			err = PTR_ERR(base);
			dev_err(&pdev->dev, "failed to map MMIO %d: %d.\n", i, err);
			return err;
		}
		if(i == 0) {
			ans->nvme = base;
			bus_base = res->start;
		} else
			ans->sart = base;
	}

下記の部分は、メールボックスの設定部分です。rx_callback には、受信した時のコールバック(apple_m1_ans_mbox_msg)を設定しています。

	ans->mbox.dev = ans->dev;
	ans->mbox.rx_callback = apple_m1_ans_mbox_msg;
	ans->mbox.tx_block = true;
	ans->mbox.tx_tout = 500;
	ans->chan = mbox_request_channel(&ans->mbox, 0);
	if(IS_ERR(ans->chan)) {
		err = PTR_ERR(ans->chan);
		if(err != -EPROBE_DEFER)
			dev_err(ans->dev, "failed to attach to mailbox: %d.\n", err);
		return err;
	}

apple_m1_ans_mbox_msgを覗いてみましたが、何もしていませんでした。

static void apple_m1_ans_mbox_msg(struct mbox_client *cl, void *msg)
{
}

下記の部分で、ans の初期化を apple_m1_ans_prepare で行っています。

	err = apple_m1_ans_prepare(ans);
	if(err)
		return err;

apple_m1_ans_prepare では、ANSのメールボックスの起動を mbox_send_messege で行っています。その後は NVMeとApple レジスタの初期化です。

static int apple_m1_ans_prepare(struct apple_m1_ans *ans)
{
	int ret;
	u32 val;
	u64 msg[2] = { -1ull, -1ull };

	ret = mbox_send_message(ans->chan, msg);
	if(ret < 0) {
		dev_err(ans->dev, "ANS mailbox startup failed: %d.\n", ret);
		return ret;
	}

	ret = readl_poll_timeout(ans->nvme + APPLE_BOOT_STATUS, val, (val == APPLE_BOOT_STATUS_OK), 100, 500000);
	if(ret < 0) {
		dev_err(ans->dev, "ANS NVMe startup timed out (0x%x).\n", val);
		return ret;
	}

	ans->basecmd = readl(ans->nvme + APPLE_BASE_CMD_ID) & APPLE_BASE_CMD_ID_MASK;

	writel(APPLE_MAX_PEND_CMDS_VAL, ans->nvme + APPLE_MAX_PEND_CMDS);

	writel(readl(ans->nvme + 0x24004) | 0x1000, ans->nvme + 0x24004);
	writel(APPLE_LINEAR_SQ_CTRL_EN, ans->nvme + APPLE_LINEAR_SQ_CTRL);
	writel(readl(ans->nvme + 0x24008) & ~0x800, ans->nvme + 0x24008);
	/* set command permissions */
	writel(0x102, ans->nvme + 0x24118);
	writel(0x102, ans->nvme + 0x24108);
	writel(0x102, ans->nvme + 0x24420);
	writel(0x102, ans->nvme + 0x24414);
	writel(0x10002, ans->nvme + 0x2441c);
	writel(0x10002, ans->nvme + 0x24418);
	writel(0x10002, ans->nvme + 0x24144);
	writel(0x10002, ans->nvme + 0x24524);
	writel(0x102, ans->nvme + 0x24508);
	writel(0x10002, ans->nvme + 0x24504);

	dev_info(ans->dev, "ANS NVMe startup done, base command %d.\n", ans->basecmd);

	return 0;
}

apple_m1_ans_probeの最後に次のようなコードがあります。bridge->probe_only に true を設定しているので、PCIe デバイスをプローブのみ対応しているようです。このプローブの時に使う PCIe Configuration Spaceへのアクセスは通常のアクセスではなく、bridge->ops に登録したものを使います。

	bridge->probe_only = true;
	bridge->sysdata = ans;
	bridge->ops = &apple_m1_ans_pci_ops;

	err = pci_host_probe(bridge);
	if(err < 0)
		return err;

bridge->ops に登録するのは、以下のapple_m1_ans_pci_ops です。read 時には apple_m1_ans_config_read、write 時には apple_m1_ans_config_write が呼ばれます。

static struct pci_ops apple_m1_ans_pci_ops = {
	.read = apple_m1_ans_config_read,
	.write = apple_m1_ans_config_write,
};

apple_m1_ans_config_read と apple_m1_ans_config_write は次のように定義されています。物理的なレジスタにアクセスするのではなく、デバイスドライバの ans->config というメモリにアクセスしています。

int apple_m1_ans_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val)
{
	unsigned vpci = bus->number;
	struct apple_m1_ans *ans = bus->sysdata;
	unsigned long flags;
	u64 data;


	if(devfn != 0 || vpci >= NUM_VPCI) {
		*val = 0xFFFFFFFF;
		return PCIBIOS_DEVICE_NOT_FOUND;
	}
	*val = 0;
	if((where & (size - 1)) || size > 4)
		return PCIBIOS_BAD_REGISTER_NUMBER;
	if(where >= CFGREG_SIZE || size >= CFGREG_SIZE - size)
		return PCIBIOS_SUCCESSFUL;

	spin_lock_irqsave(&ans->lock, flags);

	data = ans->config[vpci][where >> 3];
	data >>= (where & 7) * 8;
	*val = data & (BIT_MASK(size * 8) - 1);

	spin_unlock_irqrestore(&ans->lock, flags);

	return 0;
}

int apple_m1_ans_config_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val)
{
	unsigned vpci = bus->number;
	struct apple_m1_ans *ans = bus->sysdata;
	unsigned long flags;
	u64 data, mask;

	if(devfn != 0 || vpci >= NUM_VPCI)
		return PCIBIOS_DEVICE_NOT_FOUND;
	if((where & (size - 1)) || size > 4)
		return PCIBIOS_BAD_REGISTER_NUMBER;
	if(where >= CFGREG_SIZE || size >= CFGREG_SIZE - size)
		return PCIBIOS_SUCCESSFUL;

	spin_lock_irqsave(&ans->lock, flags);

	data = val;
	mask = BIT_MASK(size * 8) - 1;
	data <<= (where & 7) * 8;
	mask <<= (where & 7) * 8;
	mask &= apple_m1_ans_config[vpci][(where >> 2) | 1];
	ans->config[vpci][where >> 3] = (ans->config[vpci][where >> 3] & ~mask) | (data & mask);

	spin_unlock_irqrestore(&ans->lock, flags);

	return 0;
}

ans->config は、下記のように apple_m1_ans_config の値が初期値になっています。そして、レジスタ空間の 0番の bridge memory base & limitに、レジスタ空間の 1 番の BAR[0] に bus_base を設定ます。

	for(i=0; i<NUM_VPCI; i++)
		for(j=0; j<CFGREG_SIZE/8; j++)
			ans->config[i][j] = apple_m1_ans_config[i][j * 2];
	/* build bridge memory base & limit */
	ans->config[0][4] |= ((bus_base >> 16) & 0xfff0) | (bus_base & 0xfff00000);
	/* build device BAR */
	ans->config[1][2] |= bus_base;

メールボックスデバイスドライバ(apple,iop-mailbox-m1)

メールボックスデバイスドライバ apple,iop-mailbox-m1 にあります。
マクロ定義の部分で、AP to IOP, IOP to AP というのがあります。IOP が Processor で、APがユーザーアプリケーション側で、今回は、NVMe になる。

/* A2I -> AP to IOP, I2A -> IOP to AP */
#define CPU_CTRL		0x0044
#define   CPU_CTRL_RUN		BIT(4)
#define A2I_STAT		0x8110
#define   A2I_STAT_EMPTY	BIT(17)
#define   A2I_STAT_FULL		BIT(16)
#define I2A_STAT		0x8114
#define   I2A_STAT_EMPTY	BIT(17)
#define   I2A_STAT_FULL		BIT(16)
#define   I2A_STAT_ENABLE	BIT(0)
#define A2I_MSG0		0x8800
#define A2I_MSG1		0x8808
#define I2A_MSG0		0x8830
#define I2A_MSG1		0x8838

このメールボックスに関しては、奥が深そうなので、別途、ブログに書こうと思います。

NVMe デバイスドライバ

NVMe デバイスドライバはもともとあった nvme.h pcie.c を修正しています。なんで修正しているのかな?と思ってました。コースコードを眺めてみたら、どうやら pcie.c の中でいろいろなベンダーのNVMeをサポートするような感じになっているので、修正して使うのは普通のようです。

nvme.h の変更(追加)部分は、下記の部分だけです。

	/*
	 * Apple's SoC ANS2 controller frontend uses a different
	 * scheme for submission queues, where instead of rings they
	 * are arrays and instead of ringing a doorbell one writes
	 * an index into that array into a FIFO-type register.
	 */
	NVME_QUIRK_LINEAR_SQ			= (1 << 16),

pcie.c の変更部分は、こちら です。
nvme.h で追加した部分は、下記の pci_device_id nvme_id_table の .driver_data に設定する値(ビット)の定義のようです。追加した NVME_QUIRK_LINEAR_SQ に対応するように変更しているっぽいです。

	{ PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0xff81),
		.driver_data = NVME_QUIRK_SINGLE_VECTOR |
				NVME_QUIRK_SHARED_TAGS |
				NVME_QUIRK_LINEAR_SQ },

おわりに