This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[FAQ] TDA4AL-Q1: Large byte transfer

Part Number: TDA4AL-Q1

Tool/software:

Example application on SDK 10.0 to show large byte transfer between CDD IPC(MCU1_0) and Linux (A72) using cdd_ipc_rc_linux ,rpsmg_char_simple applications on J721S2 ?

  • 1.1. Prerequisites:

    1. The Linux sdk for J721S2 - link
    2. RTOS sdk for J721S2 - link
    3. TI's RPMessage Char library & example - link - git clone git://git.ti.com/rpmsg/ti-rpmsg-char.git
    4. A SD card (16GB or larger)

    we will need to prepare the SD card that will contain the Linux image. Once you have the Linux SDK downloaded, you can use the included script to prepare the SD card. The detailed steps are given here. On success, you should have the SD card prepared, which will have the root/ and the boot/ directory

    we have to copy the downloaded rpmsg_char library & example to the sd card. To copy use cp ti-rpmsg-char /media/$USER/root/ command

    1.2. Building rpmsg_char_simple

    Let us start with rpmsg_char_simple and look and what modifications we made to the existing example application to do large byte transfer:

    1. First of all we added a new flag "-L". This flag will trigger the operation of large byte transfer instead of the regular ping messages. If this flag is encountered we set a boolean flag largeflag to true.

          bool largeflag = false;

          while (1) {
              c = getopt(argc, argv, "Lr:n:p:d:l:");
              if (c == -1)
                  break;

              switch (c) {
              case 'L':
                  printf("receiving address\n");
                  largeflag = true;
                  break;

    2. If the largeflag boolean is true then we invoke our own function rpmsg_char_get_addr(), instead of the regular ping message function.

          if (largeflag)
          {
              printf("now will receive\n");
              status = rpmsg_char_get_addr(rproc_id, dev_name, local_endpt, remote_endpt);
          }
          else
          {
              status = rpmsg_char_ping(rproc_id, dev_name, local_endpt, remote_endpt, num_msgs);
          }

    3. The rpmsg_char_get_addr() function is similar to the rpmsg_char_ping() with few differences. First thing it does is send a "Large" notification to the remote core.

      ret = send_msg(rcdev->fd, (char *)packet_buf, packet_len);

      After that, it recieves the address and size of the data sent by the remote core in a fat pointer style buffer. The first element contains the address and the second element the size.

              uint32_t addrBuffer[2];
              ret = recv_msg(rcdev->fd, 8, (char*) &addrBuffer[0], &packet_len);                                                                                  

              if (ret < 0) {
                      printf("recv_msg failed for iteration %d, ret = %d\n", ret);
                      goto out;
              }

       
              printf("Received %d bytes\n", packet_len);
              /* NOTE: Recieve an address*/
              printf("Recieved Address: %#x\n", *(uint32_t *)addrBuffer);
              /* NOTE: The next 4 bytes should contian the size */
              printf("Recieved Size: %#x\n", *(uint32_t *)(addrBuffer + 1));

    4. On receiving the address we have to translate the physical address to a virtual address. The phys_read_write() function does this. This function implements the devmem2 logic for translating physical address to virtual address using mmap() and /dev/mem.

          if (0 != phys_read_write(data_addr, &byte, data_size))
          {
              printf("Unable to read data.\n");
              goto out;
          }

      The /dev/mem is a character device that can be used to access the main memory of the system. In the function we use the mmap() call to map /dev/mem's file descriptor to the virtual address space of the process. We map a page (4096 bytes) at a time.

      So, we loop through each page mapping and unmapping every time, and in each iteration we change the contents of the memory to reflect on the remote side that we were able to access the memory. 

          if((memfd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) {
              printf("Failed to open /dev/mem\n");
              ret = -1;
              goto err;
          }

          printf("\n");

          int numIter = nbytes/MAP_SIZE, i;
          // int numIter = 2, i;
          for (i = numIter; i > 0; i--, phy_addr += MAP_SIZE)
          {
              void *map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, phy_addr & ~MAP_MASK);
              // printf("Memory mapped at address %p.\n", map_base); 
              if(map_base == NULL)
              {
                  printf("mmap failed.\n");
                  ret = -1;
                  goto close;
              }

              void *virt_addr =  map_base + (phy_addr & MAP_MASK);
              *byte = *((uint8_t*) virt_addr);
              // printf("Value at address %#X (%p): %#X\n", phy_addr, virt_addr, *byte); 

              /* NOTE: Writing */
              if (memset(virt_addr, 0x24, MAP_SIZE) == NULL) {
                  printf("Unable to write to shared memory.\n");
                  ret = -1;    
              }
              *byte = *((uint8_t*) virt_addr);
              // printf("Value after writing: %#x\n", *byte); 

              if(munmap(map_base, MAP_SIZE) == -1)
              {
                  printf("Unmap Failed.\n");
              }

          }


      After that we do the same for the remaining bytes if the size was not a multiple of the PAGESIZE. Note that, we can potentially map the whole size at once, instead of mapping and unmapping a page at once, but I haven't tried it yet.

    5. After returning from the phys_read_write() to rpmsg_char_get_addr(), we send another message to the remote core saying "Verify". This lets the remote core know that we have altered the data sent by the remote core and asks it to check that

              memset(packet_buf, 0, sizeof(packet_buf));
              sprintf(packet_buf, "Verify");
              packet_len = strlen(packet_buf);
              printf("Sending message %s\n", packet_buf);
              ret = send_msg(rcdev->fd, (char *)packet_buf, packet_len);



      rpmsg-patch

      Build & install the rpmsg library and example using the following command. The binary should be generated in the examples folder.

      autoreconf -i

      ./configure --host=aarch64-none-linux-gnu --prefix=<target-dir>

      cd /ti-rpmsg-char/

      make

      make install

      make -C examples

      make -C examples install

    1.3. Building AUTOSAR application :

     In the AUTOSAR side we have to modify the MPU (memory protection unit) file and in the Linux side we have to change the device tree file.

    The AUTOSAR side is simple. We just add a new region at the end in the file ti-processor-sdk-rtos-j721s2-evm-10_01_00_04/mcusw/mcal_drv/mcal/examples/CddIpcRProcLinux/soc/j721s2/mcu1_0/CddIpcR5Mpu.c. We use the same address 0xAB000000, and reserve 32 MB of memory. Following snippet shows the new region:

    {
        /* NOTE: Region 9 configuration: Large byte transfer region */
        .regionId = 9U,
        .enable = 1U,
        .baseAddr = 0xAB000000,
        .size = CSL_ARM_R5_MPU_REGION_SIZE_32MB,
        .subRegionEnable = CSL_ARM_R5_MPU_SUB_REGION_ENABLE_ALL,
        .exeNeverControl = 1U,
        .accessPermission = CSL_ARM_R5_ACC_PERM_PRIV_USR_RD_WR,
        .shareable = 1U,
        .cacheable = (uint32_t)FALSE,
        .cachePolicy = CSL_ARM_R5_CACHE_POLICY_NON_CACHEABLE,
        .memAttr = 0U,
    }

    Now let us look at what  changes have we made in cdd_ipc_app_rc_linux applications. This file is at ti-processor-sdk-rtos-j721s2-evm-10_01_00_04/mcusw/mcal_drv/mcal/examples/CddIpcRProcLinux/CddIpcAppRProcLinux.c. Inside the cdd_ipc_Linux() function, we make the following changes:

    1. In the endpoint 14 block of the while(1) loop, we have put a condition to check the received message, if it is a "Large" notification or not. If it is, then we create the fat pointer buffer, put the size and data in it, memset the actual data in that address and send the buffer on the Linux side.

       if (0 == (strncmp("Large", (char*) &Cdd_IpcAppMpu10Buffer[0U], 5)))
      {
          System_printf("Sending Address Now!!\n");

          Cdd_IpcAppMcu20FatBuffer[0] = 0xAB000000;
          Cdd_IpcAppMcu20FatBuffer[1] = 0x100000; /* NOTE: Data of 1 MB */

          uint8_t data_byte = 0x42;
          /* NOTE: Setting 1MB of data*/
          memset((void *)Cdd_IpcAppMcu20FatBuffer[0], data_byte, Cdd_IpcAppMcu20FatBuffer[1]);
          System_printf("Data in %#x set to %#x\n", *((uint32_t *)Cdd_IpcAppMcu20FatBuffer),
                     *((uint8_t *)Cdd_IpcAppMcu20FatBuffer[0]));
          uint32_t Length = 8U; /* NOTE: Change this according to the size*/

          System_printf("Sending Address: %#x & Data Size: %#x \n",
               *((uint32_t *)Cdd_IpcAppMcu20FatBuffer), *((uint32_t *)(Cdd_IpcAppMcu20FatBuffer + 1)));
          Cdd_IpcSendMsg(CddIpcConf_IpcComChanId_Cdd_IpcMpu10_EP14, &Cdd_IpcAppMcu20FatBuffer[0U], Length);
      }

    2. We add another condition for the "Verify" notification. In that we just print the first and the last byte of the data to check that it has indeed been changed. One could add more checks and actually compare it with the sent data for a more strong verification.

      else if (0 == (strncmp("Verify", (char*) &Cdd_IpcAppMpu10Buffer[0U], 5)))
      {
          System_printf("Verifying data\n");

          /* NOTE: Reading a single byte of the actual data */
          uint32_t data_addr = 0xab000000;
          uint32_t data_size = 0x100000;
          System_printf("first byte: %#x last byte: %#x\n", *((uint8_t *)data_addr),
          *((uint8_t *)data_addr + data_size - 1));
      }

    RTOS SDK patch

    After doing the above changes mentioned in cdd_ipc_app_rc_linux 

    1. Go to ti-processor-sdk-rtos-j721s2-evm-10_01_00_04/mcusw/build/ and run the following command:
              make cdd_ipc_app_rc_linux CORE=mcu1_0 BOARD=j721s2_evm SOC=j721s2 CDD_IPC_LINUX_BUILD=yes -sj

    2. Copy the generated the binary in ti-processor-sdk-rtos-j721s2-evm-10_01_00_04/mcusw/binary/cdd_ipc_app_rc_linux/bin/j721s2_evm/cdd_ipc_app_rc_linux_mcu1_0_release.xer5f, and put it into
      ti-processor-sdk-linux-adas-j721s2-evm-10_01_00_04/board-support/prebuilt-images/ti-dm/j721s2/ipc_echo_testb_mcu1_0_release_strip.xer5f.
              cp -r ti-processor-sdk-rtos-j721s2-evm-10_01_00_04/mcusw/binary/cdd_ipc_app_rc_linux/bin/j721s2_evm/cdd_ipc_app_rc_linux_mcu1_0_release.xer5f ti-processor-sdk-linux-adas-j721s2-evm-10_01_00_04/board-support/prebuilt-images/ti-dm/j721s2/ipc_echo_testb_mcu1_0_release_strip.xer5f

    3. Go to the root directory of the Linux SDK and run this command:
              make u-boot -j

    4. Copy the tispl.bin file from Linux sdk ti-processor-sdk-linux-adas-j721s2-evm-10_01_00_04/board-support/ti-u-boot-2025.01+git/build/a72/tispl.bin to boot/ directory of the the sd card. You can copy the already existing tispl.bin as a backup if you want.
              cp  ti-processor-sdk-linux-adas-j721s2-evm-10_01_00_04/board-support/ti-u-boot-2025.01+git/build/a72/tispl.bin /media/$USER/boot

    1.4. Building Linux SDK :

    In the Linux side, the device tree file we have to change is in ti-processor-sdk-linux-adas-j721s2-evm-10_01_00_04/board-support/ti-linux-kernel-6.12.17+git-ti/arch/arm64/boot/dts/ti/k3-j721e-som-p0.dtsi. We have added the following new region in the reserved memory node:

    main_r5fss0_core0_large_byte_region: r5f-largebyte-memories@ab000000 {
        reg = <0x00 0xab000000 0x00 0x2000000>; /* NOTE: 32 Mb */
        no-map;
    };


    linux dts patch 

    To apply the changes we made to the device tree file, we will need to rebuild them again. To do so we can just build the Linux kernel using the make linux -j command in the root directory of Linux SDK.

    This will generate all the required .dtb and .dtbo files necessary. We need to replace the already existing dts files with the newly built one. To do so, insert your SD card in you PC and run the following command:

    cp ti-processor-sdk-linux-adas-j721s2-evm-10_01_00_04/board-support/ti-linux-kernel-6.12.17+git-ti/arch/arm64/boot/dts/ti/k3-j721s2*.dtb /media/$USER/rootfs/boot/dtb/ti/

    cp ti-processor-sdk-linux-adas-j721s2-evm-10_01_00_04/board-support/ti-linux-kernel-6.12.17+git-ti/arch/arm64/boot/dts/ti/k3-j721s2*.dtbo /media/$USER/rootfs/boot/dtb/ti/

    1.5. Final Output: