Jetson/L4T/peripheral/

From eLinux.org
< Jetson‎ | L4T‎ | peripheral
Jump to: navigation, search

CAN

Loopback test for 2 CAN interfaces

(verified on AGX Xavier devkit with Jetpack 5.1.2)

  • Connections

1.png

  • Setup

2.jpeg

  • Steps to verify

1. Configure pinmux for CAN bus through Jetson IO 1-1. Run Jetson-IO

$ sudo /opt/nvidia/jetson-io/jetson-io.py

1-2. Enable can0 and can1 Configure Jetson 40pin Header -> Configure header pins manually -> Enable can0/can1 as following -> Back -> Save pin changes -> Save and reboot to reconfigure pins -> Enter

[*] can0           (29,31)
[*] can1           (33,37)

1-3. Check pinmux register

$ sudo busybox devmem 0x0c303018 //can0_din - 0xC454
$ sudo busybox devmem 0x0c303010 //can0_dout - 0xC400
$ sudo busybox devmem 0x0c303008 //can1_din - 0xC454
$ sudo busybox devmem 0x0c303000 //can1_dout - 0xC400

2. Enable CAN

2-1. Load kernel modules

$ sudo modprobe can

=> can: controller area network core

$ sudo modprobe can_raw

=> can: raw protocol

$ sudo modprobe mttcan

=> net canX: mttcan device registered (regs=00000000b6e0395a, irq=51)

2-2. Setup interface

$ sudo ip link set can0 up type can bitrate 100000 berr-reporting on restart-ms 1000
$ sudo ip link set can1 up type can bitrate 100000 berr-reporting on restart-ms 1000

2-3. Check can interface

$ ifconfig
can0: flags=193<UP,RUNNING,NOARP>  mtu 72
       unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 10  (UNSPEC)
       RX packets 0  bytes 0 (0.0 B)
       RX errors 0  dropped 0  overruns 0  frame 0
       TX packets 0  bytes 0 (0.0 B)
       TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
       device interrupt 51  
can1: flags=193<UP,RUNNING,NOARP>  mtu 72
       unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 10  (UNSPEC)
       RX packets 0  bytes 0 (0.0 B)
       RX errors 0  dropped 0  overruns 0  frame 0
       TX packets 0  bytes 0 (0.0 B)
       TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
       device interrupt 52

3. Test

3-1. Run candump for all interface in background

$ candump -x any &

3-2. Run cansend from can0-TX, and you will see it receive the same data from can1-RX

$ cansend can0 123#abcdabcd
 can1  RX - -  123   [4]  AB CD AB CD
 can0  TX - -  123   [4]  AB CD AB CD

MCP2515 Verification

(verified on Orin NX+p3768 devkit with Jetpack 5.1.2)

  • Connections

3.png

  • Setup

4.jpeg

  • Mapping
SPI:
spi1: spi@3210000
spi3: spi@3230000
Interrupt:
PIN29 -> GPIO01 -> PQ.05
PIN33 -> GPIO13 -> PH.00
  • Steps to verify

Step1. Add configuration in device tree for MCP2515

diff --git a/cvb/tegra234-p3768-0000-a0.dtsi b/cvb/tegra234-p3768-0000-a0.dtsi
--- a/cvb/tegra234-p3768-0000-a0.dtsi
+++ b/cvb/tegra234-p3768-0000-a0.dtsi
@@ -162,12 +162,24 @@
 			};
 		};
 	};
+
+	can_clock: can_clock {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <25000000>;
+		clock-accuracy = <100>;
+	};
+
 	spi@3210000{ /* SPI1 in 40 pin conn */
 		status = "okay";
 		spi@0 { /* chip select 0 */
-			compatible = "tegra-spidev";
+			compatible = "microchip,mcp2515";
 			reg = <0x0>;
-			spi-max-frequency = <50000000>;
+			spi-max-frequency = <2000000>;
+			interrupt-parent = <&tegra_main_gpio>;
+			interrupts = <TEGRA234_MAIN_GPIO(Q, 5) IRQ_TYPE_LEVEL_LOW>;
+			clocks = <&can_clock>;
+			nvidia,enable-hw-based-cs;
 			controller-data {
 				nvidia,enable-hw-based-cs;
 				nvidia,rx-clk-tap-delay = <0x10>;
@@ -189,9 +201,13 @@
 	spi@3230000{ /* SPI3 in 40 pin conn */
 		status = "okay";
 		spi@0 { /* chip select 0 */
-			compatible = "tegra-spidev";
+			compatible = "microchip,mcp2515";
 			reg = <0x0>;
-			spi-max-frequency = <50000000>;
+			spi-max-frequency = <2000000>;
+			interrupt-parent = <&tegra_main_gpio>;
+			interrupts = <TEGRA234_MAIN_GPIO(H, 0) IRQ_TYPE_LEVEL_LOW>;
+			clocks = <&can_clock>;
+			nvidia,enable-hw-based-cs;
 			controller-data {

Step2. Configure pinmux for CAN bus through Jetson-IO

2-1 Run Jetson-IO

$ sudo /opt/nvidia/jetson-io/jetson-io.py

2-2 Enable spi1 and spi3

Configure Jetson 40pin Header -> Configure header pins manually -> Enable spi1/spi3 as following -> Back -> Save pin changes -> Save and reboot to reconfigure pins -> Enter

[*] spi1           (19,21,23,24,26)
[*] spi3           (13,16,18,22,37)

Step3. Enable can0/can1 and test

$ sudo ip link set can0 up type can bitrate 500000
$ sudo ip link set can1 up type can bitrate 500000
$ candump -x any &
$ cangen can0

SPI

Loopback test

(verified on Jetson Nano devkit with Jetpack 4.6.4)

Step 1. Connect MOSI and MISO of SPI1

MOSI(PIN19 of 40-pin GPIO header) MISO(PIN21 of 40-pin GPIO header)

Step 2. Remove GPIO usage of SPI pins

2-1. Check flash log to know which dtb you are using (ex. tegra210-p3448-0000-p3449-0000-b00.dtb)

2-2. Remove GPIO usage in that device tree

2-2-1. Method 1 - Remove from source

diff --git a/kernel-dts/porg-platforms/tegra210-porg-gpio-p3448-0000-b00.dtsi b/kernel-dts/porg-platforms/tegra210-porg-gpio-p3448-0000-b00.dtsi
index 1ea952f..49d4196 100644
--- a/kernel-dts/porg-platforms/tegra210-porg-gpio-p3448-0000-b00.dtsi
+++ b/kernel-dts/porg-platforms/tegra210-porg-gpio-p3448-0000-b00.dtsi
@@ -27,11 +27,6 @@
                gpio_default: default {
                        gpio-input = <
                                TEGRA_GPIO(BB, 0)
-                               TEGRA_GPIO(B, 4)
-                               TEGRA_GPIO(B, 5)
-                               TEGRA_GPIO(B, 6)
-                               TEGRA_GPIO(B, 7)
-                               TEGRA_GPIO(DD, 0)
                                TEGRA_GPIO(E, 6)
                                TEGRA_GPIO(S, 5)
                                TEGRA_GPIO(A, 5)
@@ -49,11 +44,6 @@
                                TEGRA_GPIO(J, 7)
                                TEGRA_GPIO(G, 2)
                                TEGRA_GPIO(G, 3)
-                               TEGRA_GPIO(C, 0)
-                               TEGRA_GPIO(C, 1)
-                               TEGRA_GPIO(C, 2)
-                               TEGRA_GPIO(C, 3)
-                               TEGRA_GPIO(C, 4)
                                TEGRA_GPIO(H, 2)
                                TEGRA_GPIO(H, 5)
                                TEGRA_GPIO(H, 6)

2-2-2. Method 2 - Remove from decompiled dtb

2-2-2-1 Find you dtb in Linux_for_Tegra/kernel/dtb/tegra210-p3448-0000-p3449-0000-b00.dtb

2-2-2-2 Dissemble the dtb to dts

$ dtc -I dtb -O dts -o temp.dts tegra210-p3448-0000-p3449-0000-b00.dtb

2-2-2-3 Modify the following line

-        gpio-input = <0xd8 0xc 0xd 0xe 0xf 0xe8 0x26 0x95 0x5 0xbc 0xbd 0xbe 0xc1 0xc2 0xa8 0xc8 0xca 0x4d 0x4e 0x4c 0x4f 0x32 0x33 0x10 0x11 0x12 0x13 0x14 0x3a 0x3d 0x3e 0x41 0xe4>;
+        gpio-input = <0xd8 0x26 0x95 0x5 0xbc 0xbd 0xbe 0xc1 0xc2 0xa8 0xc8 0xca 0x4d 0x4e 0x4c 0x4f 0x32 0x33 0x3a 0x3d 0x3e 0x41 0xe4>;

2-2-2-4 Assemble the dts back to dtb dtc -I dts -O dtb -o tegra210-p3448-0000-p3449-0000-b00.dtb temp.dts

2-2-2-5 Flash the board

Step 3. Running Jetson-IO to enable SPI1

3-1. Run Jetson IO

$ sudo /opt/nvidia/jetson-io/jetson-io.py

3-2. Configure SPI1 Configure Jetson 40pin Header => Configure header pins manually => Select "spi1 (19,21,23,24,26)" => Back -> Save pin changes => Save and reboot to reconfigure pins

3-3. Check pinmux for SPI if the same as following

$  sudo cat /sys/kernel/debug/tegra_pinctrl_reg | grep -i spi
Bank: 1 Reg: 0x70003050 Val: 0x0000e044 -> spi1_mosi_pc0
Bank: 1 Reg: 0x70003054 Val: 0x0000e044 -> spi1_miso_pc1
Bank: 1 Reg: 0x70003058 Val: 0x0000e044 -> spi1_sck_pc2
Bank: 1 Reg: 0x7000305c Val: 0x0000e048 -> spi1_cs0_pc3
Bank: 1 Reg: 0x70003060 Val: 0x0000e048 -> spi1_cs1_pc4
Bank: 1 Reg: 0x70003064 Val: 0x00006044 -> spi2_mosi_pb4
Bank: 1 Reg: 0x70003068 Val: 0x00006044 -> spi2_miso_pb5
Bank: 1 Reg: 0x7000306c Val: 0x00006044 -> spi2_sck_pb6
Bank: 1 Reg: 0x70003070 Val: 0x00006048 -> spi2_cs0_pb7
Bank: 1 Reg: 0x70003074 Val: 0x00006048 -> spi2_cs1_pdd0

Step 4. Probe SPI driver

$ sudo modprobe spidev

Step 5. Download/build this test file

5-1 Download

$ wget https://raw.githubusercontent.com/torvalds/linux/v4.9/tools/spi/spidev_test.c

5-2 Build on the board

$ gcc -o spidev_test spidev_test.c

Step 6. Run the command and check if the result is expected

$ sudo ./spidev_test -D /dev/spidev0.0 -v -p "HelloWorld123456789abcdef"
Or 
$ sudo ./spidev_test -D /dev/spidev0.0 -s 10000000 -v
CPOL=0, CPHA=0 -> SPI Mode 0
CPOL=0, CPHA=1 -> SPI Mode 1
CPOL=1, CPHA=0 -> SPI Mode 2
CPOL=1, CPHA=1 -> SPI Mode 3

To send SPI packey with SPI Mode 3: (add -O and -H)

$ sudo ./spidev_test -D /dev/spidev0.0 -O -H -v -p "HelloWorld123456789abcdef"

I2C

Commands

  • Module
$ sudo i2cdump -y 0 0x50
  • Carrier board
$ sudo i2cdump -y 0 0x56
  • Check I2C bus 0
$ i2cdetect -y 0

Mapping

  • Jetson Nano devkit

Bus #: device node => address (aliases: compatible) => SoC PIN (GPIO): usage

Bus 0: /dev/i2c-0 => 7000c000.i2c (&i2c1: tegra210-i2c) => I2C0_SCL/I2C0_SDA (PJ.01/PJ.00): Custom 3.3V I2C devices
Bus 1: /dev/i2c-1 => 7000c400.i2c (&i2c2: tegra210-i2c) => I2C1_SCL/I2C1_SDA (PJ.02/PJ.03): Custom 3.3V I2C devices
Bus 2: /dev/i2c-2 => 7000c500.i2c (&i2c3: tegra210-i2c) => I2C2_SCL/I2C2_SDA (PF.00/PF.01): Custom 1.8V I2C devices
Bus 3: /dev/i2c-3 => 7000c700.i2c (&i2c4: tegra210-i2c) => NA: hdmc_dcc
Bus 4: /dev/i2c-4 => 7000d000.i2c (&i2c5: tegra210-i2c) => NA: PMIC(MAX77620 includes RTC)
Bus 5: /dev/i2c-5 => 7000d100.i2c (&i2c6: tegra210-i2c) => NA: NA
Bus 6: /dev/i2c-6 => 546c0000.i2c (tegra-vii2c) => CAMI2C_SCL/CAMI2C_SDA (PS.02/PS.03), INA3221
Bus 7: /dev/i2c-7 => 546c0000.i2c (tegra-vii2c)
Bus 8: /dev/i2c-8 => 546c0000.i2c (tegra-vii2c)

UART

Loopback test

  • Method 1
$ sudo su
# stty -F /dev/ttyTHS0 115200 raw -echo
# cat /dev/ttyTHS0 &
# echo "test" > /dev/ttyTHS0
  • Method 2
$ sudo apt install pip
$ sudo pip install pyserial
$ python reader_writer.py
  • Method 3

1. copy uarttest to device (on host PC)

2. create test binary (on the board)

$ sudo mkdir /data
$ dd if=/dev/urandom of=/data/tx_testfile.bin bs=1 count=10485760

3. run test

# ./uarttest -test 0x800000 -tty /dev/ttyTHS0 -tx -rx -baud 3000000 -hwflow enable

Mapping

  • Jetson AGX Orin devkit

Module-UART# => SoC-UART#(used pins): <uart*>@<address>(serial#) - <node>

UART1 => UART1(PR02, PR03, PR04, PR05): uarta@3100000 (serial0) - /dev/ttyTHS0
UART2 => UART5(PY05, PY06, PY07, PZ00): uarte@3140000 (serial4) - /dev/ttyTHS4
UART3 => UART3(PCC05, PCC06): uartc@c280000 (serial2) - /dev/ttyTCU0
UART4 => UART4(PH03, PH04, PH05, PH06): uartd@3130000 (serial3) - /dev/ttyTHS3
UART5 => UART2(PX04, PX05, PX06, PX07): uartb@3110000 (serial1) - /dev/ttyTHS1
  • Jetson Orin Nano devkit

Module UART# => SOC UART#(used pins): <uart*>@\<address> - \<node> => Usage

UART0 => UART2(PX04, PX05, PX06, PX07): uartb@3110000 (serial1) - Unused => To M.2 Key E
UART1 => UART1(PR02, PR03, PR04, PR05): uarta@3100000 (serial0) - /dev/ttyTHS0 => General UART
UART2 => UART3(PCC05, PCC06): uartc@c280000 (serial2) - /dev/ttyTCU0 => Debug

Flash

Image-Based OTA without layout change

  • Device: Orin Nano devkit - NVMe with rootfs A/B enabled
  • Version: R35.3.1 -> R35.4.1

[Host]

1. Setup environment variable

$ export BASE_BSP=${HOME}/workspace/nvidia_sdk/JetPack_5.1.1_Linux_JETSON_ORIN_NANO_TARGETS/Linux_for_Tegra
$ export TARGET_BSP=${HOME}/workspace/nvidia_sdk/JetPack_5.1.2_Linux_JETSON_ORIN_NANO_TARGETS/Linux_for_Tegra

2. Flash the board with rootfs A/B enabled

$ cd ${BASE_BSP}
$ sudo ROOTFS_AB=1 ROOTFS_RETRY_COUNT_MAX=2 ADDITIONAL_DTB_OVERLAY_OPT="BootOrderNvme.dtbo" ./tools/kernel_flash/l4t_initrd_flash.sh --external-device nvme0n1p1 -S 100GiB -c tools/kernel_flash/flash_l4t_t234_nvme_rootfs_ab.xml -p "-c bootloader/t186ref/cfg/flash_t234_qspi.xml"  --showlogs --network usb0 jetson-orin-nano-devkit internal

3. Extract OTA tool

$ cd ${TARGET_BSP}/../
$ wget https://developer.nvidia.com/downloads/embedded/l4t/r35_release_v4.1/release/ota_tools_r35.4.1_aarch64.tbz2
$ sudo tar xpf ota_tools_r35.4.1_aarch64.tbz2

4. extract golden image from device

[Host]
$ sudo ./tools/kernel_flash/l4t_initrd_flash.sh --initrd jetson-orin-nano-devkit mmcblk0p1

[Target]
(plug USB storage)
$ mount /dev/sda /mnt
$ dd if=/dev/nvme0n1p1 of=/mnt/golden_a.img.raw
$ dd if=/dev/nvme0n1p2 of=/mnt/golden_b.img.raw
$ sync
$ umount /mnt

[Host]
move golden_a.img.raw and golden_a.img.raw to ${TARGET_BSP}/bootloader/system.img.raw and system.img_b.raw

5. Generate OTA package

$ sudo ROOTFS_AB=1 ./tools/ota_tools/version_upgrade/l4t_generate_ota_package.sh -s --external-device nvme0n1 -S 100GiB jetson-orin-nano-devkit R35-4

6. Put OTA package into target

$ scp ${TARGET_BSP}/bootloader/jetson-orin-nano-devkit/ota_payload_package.tar.gz nvidia@<ip address>:~/

[Target]

1. Setup OTA tool

$ cd ${HOME}
$ mkdir ota-tool
$ export WORKDIR=${HOME}/ota-tool
$ wget https://developer.nvidia.com/downloads/embedded/l4t/r35_release_v4.1/release/ota_tools_r35.4.1_aarch64.tbz2
$ sudo tar xpf ota_tools_r35.4.1_aarch64.tbz2 -C ${WORKDIR}/.

2. Place OTA package

$ sudo mkdir /ota
$ sudo mv ${HOME}/ota_payload_package.tar.gz /ota/

3. Unpack the OTA payload package and prepare to start OTA

$ cd ${WORKDIR}/Linux_for_Tegra/tools/ota_tools/version_upgrade
$ sudo ./nv_ota_start.sh /ota/ota_payload_package.tar.gz

4. Reboot to start OTA update

$ sudo reboot

Image-Based OTA with layout change

  • Device: Xavier NX devkit - eMMC
  • Version: R32.7.4 -> R35.4.1

[Host]

1. Setup environment variable

$ export BASE_BSP=${HOME}/workspace/nvidia_sdk/JetPack_4.6.4_Linux_JETSON_XAVIER_NX_TARGETS/Linux_for_Tegra
$ export TARGET_BSP=${HOME}/workspace/nvidia_sdk/JetPack_5.1.2_Linux_JETSON_XAVIER_NX_TARGETS/Linux_for_Tegra

2. Extract OTA tool

$ cd ${TARGET_BSP}/../
$ wget https://developer.nvidia.com/downloads/embedded/l4t/r35_release_v4.1/release/ota_tools_r35.4.1_aarch64.tbz2
$ sudo tar xpf ota_tools_r35.4.1_aarch64.tbz2

IOTA-1.png

3. Generate recovery image and recovery dtb (this step is used for layout change only, ex. R32->R35)

$ cd ${TARGET_BSP}
$ sudo ./tools/ota_tools/version_upgrade/build_base_recovery_image.sh jetson-xavier-nx-devkit-emmc R32-7 ${BASE_BSP} ${BASE_BSP}/rootfs ${TARGET_BSP}

IOTA-2.png

4. Generate OTA package

$ sudo -E ./tools/ota_tools/version_upgrade/l4t_generate_ota_package.sh jetson-xavier-nx-devkit-emmc R32-7

IOTA-3.1.png

..

IOTA-3.2.png

(or Update NVMe)

$ sudo -E ./tools/ota_tools/version_upgrade/l4t_generate_ota_package.sh --external-device nvme0n1 -S 118GiB jetson-xavier-nx-devkit-emmc R32-7

5. Put OTA package into target

$ scp ${TARGET_BSP}/bootloader/jetson-xavier-nx-devkit-emmc/ota_payload_package.tar.gz nvidia@<ip address>:~/

IOTA-4.png

[Target]

1. Setup OTA tool

$ cd ${HOME}
$ mkdir ota-tool
$ export WORKDIR=${HOME}/ota-tool
$ wget https://developer.nvidia.com/downloads/embedded/l4t/r35_release_v4.1/release/ota_tools_r35.4.1_aarch64.tbz2
$ sudo tar xpf ota_tools_r35.4.1_aarch64.tbz2 -C ${WORKDIR}/.

2. Place OTA package

$ sudo mkdir /ota
$ sudo mv ${HOME}/ota_payload_package.tar.gz /ota/

IOTA-5.png

3. Unpack the OTA payload package and prepare to start OTA

$ cd ${WORKDIR}/Linux_for_Tegra/tools/ota_tools/version_upgrade
$ sudo ./nv_ota_start.sh /ota/ota_payload_package.tar.gz

IOTA-6.1.png

..

IOTA-6.2.png

4. Reboot to start OTA update

$ sudo reboot

IOTA-7.1.png

..

IOTA-7.2.png

Capsule Update

  • Device: Orin NX 16G + p3768(Orin Nano devkit) + NVMe
  • Version: R35.3.1 -> R35.4.1

Note. <Linux_for_Tegra>/nv_tegra/bsp_version stores the BSP version

[Host]

$ sudo ./l4t_generate_soc_bup.sh -e t23x_3767_bl_spec t23x
$ ./generate_capsule/l4t_generate_soc_capsule.sh -i bootloader/payloads_t23x/bl_only_payload -o ./TEGRA_BL.Cap t234
$ scp TEGRA_BL.Cap <Username>@<IP address>:~/

[Target]

$ ll /dev/disk/by-partlabel/esp
$ sudo mount /dev/nvme0n1p11 /mnt/
$ sudo mkdir -p /mnt/EFI/UpdateCapsule
$ cd /mnt/EFI/UpdateCapsule/
$ sudo cp ${HOME}/TEGRA_BL.Cap .

With QSPI flash:

$ sudo su
# printf "\x07\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00" > /tmp/var_tmp.bin
# cd /sys/firmware/efi/efivars/
# dd if=/tmp/var_tmp.bin of=OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c bs=12
# reboot

You will see update status in UEFI after reboot

Run the following command to check if update is successful

$ sudo nvbootctrl dump-slots-info
0 - No Capsule update
1 - Capsule update successfully
2 - Capsule install successfully but boot new firmware failed
3 - Capsule install failed

PXE boot

(verified on Ubuntu 18.04 + AGX Orin devkit with Jetpack 5.1.2)

[Host]

1. DHCP server

1-1. Install

$ sudo apt install isc-dhcp-server

1-2. Configuration modify /etc/default/isc-dhcp-server, XXX is the network interface connected to target

INTERFACESv4="XXX"
INTERFACESv6=""
DHCPDARGS="XXX"

1-3. Add pxe details in dhcp conf file (/etc/dhcp/dhcpd.conf), if XXX interface shows 192.168.0.1

subnet 192.168.0.0 netmask 255.255.255.0 {
        range 192.168.0.11 192.168.0.20;
        option routers 192.168.0.1;

        # Required for PXE Boot
        class "pxeclients" {
                match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
                        filename "efi/grubnetaa64.efi.signed";
                        # TFTP Server IP address
                        next-server 192.168.0.1;
                        option root-path "/tftp/";
        }
}

host mytegra {
  hardware ethernet 48:B0:2D:68:B2:9C;
  fixed-address 192.168.0.10;
}

1-4. Restart and check DHCP server

$ sudo ifconfig enx00e04c360130 192.168.0.1
$ sudo systemctl restart isc-dhcp-server
$ sudo systemctl status isc-dhcp-server

2. Setup TFTP Server

2-1. Install

$ sudo apt install -y tftpd-hpa

2-2. Configuration modify /etc/default/tftpd-hpa, OOO is the username of your host PC

TFTP_USERNAME="OOO"
TFTP_DIRECTORY="/tftp"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure"

2-3. Restart and check TFTP server

$ sudo apt purge xinetd
$ sudo systemctl restart tftpd-hpa
$ sudo systemctl status tftpd-hpa.service

3. Restart host PC

4. Setup NFS

$ export MYROOTFS=${HOME}/workspace/nvidia_sdk/JetPack_5.1.2_Linux_JETSON_AGX_ORIN_TARGETS/Linux_for_Tegra/rootfs
$ echo "$MYROOTFS *(rw,sync,insecure,no_subtree_check,no_root_squash)" | sudo tee --append /etc/exports > /dev/null

5. OS setup

5-1. Create directory

$ sudo mkdir /tftp
$ sudo mkdir /tftp/grub
$ sudo mkdir /tftp/efi

5-2. Copy files (kernel image, initrd, efi)

$ sudo cp $MYROOTFS/boot/Image /tftp/
$ sudo cp $MYROOTFS/boot/initrd.img /tftp/
(https://ubuntu.pkgs.org/20.04/ubuntu-main-arm64/grub-efi-arm64-signed_1.142+2.04-1ubuntu26_arm64.deb.html)
$ sudo cp grubnetaa64.efi.signed /tftp/efi/

5-3. Create grub.cfg

$ sudo touch /tftp/grub/grub.cfg

modify grub.cfg as following:

set timeout_style=menu
set timeout=10

menuentry "Jetson" {
      linux /Image root=/dev/nfs rw netdevwait ip=:::::eth0:on nfsroot=192.168.0.1:/home/chunhuaif/workspace/nvidia_sdk/JetPack_5.1.2_Linux_JETSON_AGX_ORIN_TARGETS/Linux_for_Tegra/rootfs fbcon=map:0 net.ifnames=0  console=ttyTCU0,115200 firmware_class.path=/etc/firmware fbcon=map:0 net.ifnames=0
      initrd /initrd
}

[Target]

1. Reboot

2. Press ESC to enter UEFI menu

3. Select PXE as boot device