KV260 Kria Starter Kit Series: 3 makalesindeki Vivado projesinde bazı değişiklikler yapıldı. Interrupt kontroller yerine BRAM Controller ve BRAM generator kullanıldı. Bu uygulamanın amacı bir Block RAM donanımı kurup onun adresini istediğimiz gibi ayarlamak ve bu adrese Linux User Space'de Vitis uygulamamızı çalıştırarak bir data yazmak ve onu geri okumak.
Vivado projesi oluşturma, Platform oluşturma vs donanım tasarım işleri önceki makale ile aynı prosedürde yapıldı. Sadece biraz tasarım farklılığı var. Bunun dışında Petalinux ile proje oluşturma, rootfs, sysroot, dtsi vs oluşturma adımları da aynı yalnız pl.dtsi dosyası açılıp bir kontrol edilir ve PL tasarımı device tree dosyasına yansımış mı diye kontrol edilir:
/*
* CAUTION: This file is automatically generated by Xilinx.
* Version: XSCT 2022.1
* Today is: Sun Aug 18 10:08:02 2024
*/
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = <&fpga_full>;
overlay0: __overlay__ {
#address-cells = <2>;
#size-cells = <2>;
firmware-name = "kv260_vitis_platform_20221.bit.bin";
resets = <&zynqmp_reset 116>;
};
};
fragment@1 {
target = <&amba>;
overlay1: __overlay__ {
afi0: afi0 {
compatible = "xlnx,afi-fpga";
config-afi = < 0 0>, <1 0>, <2 0>, <3 0>, <4 0>, <5 0>, <6 0>, <7 0>, <8 0>, <9 0>, <10 0>, <11 0>, <12 0>, <13 0>, <14 0xa00>, <15 0x000>;
};
clocking0: clocking0 {
#clock-cells = <0>;
assigned-clock-rates = <99999001>;
assigned-clocks = <&zynqmp_clk 71>;
clock-output-names = "fabric_clk";
clocks = <&zynqmp_clk 71>;
compatible = "xlnx,fclk";
};
clocking1: clocking1 {
#clock-cells = <0>;
assigned-clock-rates = <99999001>;
assigned-clocks = <&zynqmp_clk 72>;
clock-output-names = "fabric_clk";
clocks = <&zynqmp_clk 72>;
compatible = "xlnx,fclk";
};
};
};
fragment@2 {
target = <&amba>;
overlay2: __overlay__ {
#address-cells = <2>;
#size-cells = <2>;
axi_bram_ctrl_0: axi_bram_ctrl@80000000 {
/* This is a place holder node for a custom IP, user may need to update the entries */
clock-names = "s_axi_aclk";
clocks = <&misc_clk_0>;
compatible = "xlnx,axi-bram-ctrl-4.1";
reg = <0x0 0x80000000 0x0 0x2000>;
xlnx,bram-addr-width = <0xb>;
xlnx,bram-inst-mode = "EXTERNAL";
xlnx,ecc = <0x0>;
xlnx,ecc-onoff-reset-value = <0x0>;
xlnx,ecc-type = <0x0>;
xlnx,fault-inject = <0x0>;
xlnx,memory-depth = <0x800>;
xlnx,rd-cmd-optimization = <0x0>;
xlnx,read-latency = <0x1>;
xlnx,s-axi-ctrl-addr-width = <0x20>;
xlnx,s-axi-ctrl-data-width = <0x20>;
xlnx,s-axi-id-width = <0x10>;
xlnx,s-axi-supports-narrow-burst = <0x1>;
xlnx,single-port-bram = <0x0>;
};
misc_clk_0: misc_clk_0 {
#clock-cells = <0>;
clock-frequency = <99999000>;
compatible = "fixed-clock";
};
zyxclmm_drm {
compatible = "xlnx,zocl";
};
};
};
};
Daha sonra Vitis ile platform oluşturulmaya başlandı ve önceki makaledeki adımların aynısı izlendi. Application'a geçmeden önceki KV260 board'unun boot edilip /lib/firmware/xilinx dizini altına gönderilmesi gereken .dtbo shell.json ve en önemlisi .bit.bin dosyalarının kopyalanması süreci gerçekleştirildi. Bunu yaparken dtbo ve json zaten elimizde mevcut ancak Vivado'dan elde edilen bitstream'in .bit.bin uzantısına dönüştürülmesi gerekti. Bu adım Xilinx tutorial'ı referans alınarak gerçekleştirildi.
boot.bif isimli bir dosya oluşturuldu ve içerisine aşağıdaki kod yazıldı.
the_ROM_image:
{ /home/adem/kria_vitis_platform/hardware/kv260_vitis_platform_20221/kv260_vitis_platform_20221.bit
}
Daha sonra Xilinx bootgen uygulaması çalıştırılarak .bit.bin uzantılı dosya oluşturuldu:
bootgen -image boot.bif -arch zynqmp -process_bitstream bin
Oluşan dosya /home/adem/kria_vitis_platform/hardware/kv260_vitis_platform_20221/ dizini altındadır. Bu dosya yukarıda da belirtildiği gibi board içerisinde /lib/firmware/Xilinx dizininde oluşturduğum uygulama klasörü 'bram_app' dizinine pl.dtbo ve shell.json ile birlikte kopyalandı. Daha sonra aşağıdaki komutlar çağrılarak donanım board'a yuklendi.
sudo xmutil listapps
sudo xmutil unloadapp
sudo xmutil loadapp bram_app
Burada artık eksik kalan tek şey Application idi. Xilinx'in KV260 örnek uygulaması olarak tasarladığı ve bir önceki makalede başarılı şekilde çalıştırılan Vector Addition uygulamasında kernel C++ ile yazılmıştı ve xclbin olarak üretilen bir dosya uygulama çıktısı olarak kullanılıyordu. Bunu yapabilmek için hangi kaynaklara ihtiyaç olduğunu araştırdığımda fazlaca karmaşık bir döküman ve komut setiyle karşılaştım. Ben de Vitis'den Linux uygulamasını Hello World taslağı ile açıp yazdığım uygulamayı .elf olarak çıktsını alıp board üzerinde çalıştırmak istedim. Uygulama şu şekilde yazıldı:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#define BRAM_BASE_ADDR 0x80000000 // Replace with the actual base address from the device tree
#define BRAM_SIZE 0x10000 // Adjust based on the BRAM size
int main()
{
printf("Hello World\n");
int fd;
unsigned long *bram;
// Open the memory device
fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0) {
perror("open");
return -1;
}
// Map the BRAM address space into user space
bram = (unsigned long *)mmap(NULL, BRAM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, BRAM_BASE_ADDR);
if (bram == MAP_FAILED) {
perror("mmap");
close(fd);
return -1;
}
// Write 32-bit data to BRAM
bram[0] = 0x12345678; // Writing to the first 32-bit word
printf("Written 0x12345678 to BRAM at offset 0x0\n");
// Optionally, read back and verify the data
unsigned long read_data = bram[0];
if (read_data == 0x12345678) {
printf("Read back 0x%lx successfully!\n", read_data);
} else {
printf("Read back verification failed! Got 0x%lx instead.\n", read_data);
}
// Unmap the BRAM memory and close the file descriptor
munmap(bram, BRAM_SIZE);
close(fd);
return 0;
}
Bu uygulama 0x80000000 adresine AXI4 interface ile bağlı olan BRAM bellek elemanına Petalinux User Space'den erişilmesini ve data yazılıp okunmasını sağlıyor. Bunu Vitis ile derleyip .elf dosyası oluşturuldu ve scp kullanılarak board içerisindeki /home/petalinux dizinine gönderildi. chmod kullanılarak executable yetkisi verildi ve board üzerinde çalıştırıldı.
sudo ./kv260_bram_app.elf
Output:
Hello World
Written 0x12345678 to BRAM at offset 0x0
Read back 0x12345678 successfully!
Ayrıca devmem 0x80000000 32 komutu kullanılarak da Linux'un kendi memory erişim özelliği ile adres kontrolü yapıldı ve gerçekten 0x12345678 değeri okundu.
Yorumlar
Yorum Gönder