822 lines
36 KiB
HTML
822 lines
36 KiB
HTML
<!doctype html>
|
||
<html lang="tr">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>VCU118 Rocket SoC Flash Boot Akışı</title>
|
||
<style>
|
||
:root {
|
||
color-scheme: light;
|
||
--ink: #172026;
|
||
--muted: #5b6670;
|
||
--line: #d8dee4;
|
||
--soft: #f5f7f9;
|
||
--code: #111827;
|
||
--accent: #0f766e;
|
||
}
|
||
body {
|
||
margin: 0;
|
||
color: var(--ink);
|
||
font: 15px/1.55 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||
background: #fff;
|
||
}
|
||
main {
|
||
max-width: 980px;
|
||
margin: 0 auto;
|
||
padding: 40px 28px 64px;
|
||
}
|
||
h1, h2, h3 {
|
||
line-height: 1.2;
|
||
margin: 0 0 12px;
|
||
}
|
||
h1 {
|
||
font-size: 32px;
|
||
margin-bottom: 8px;
|
||
}
|
||
h2 {
|
||
font-size: 22px;
|
||
margin-top: 34px;
|
||
padding-top: 18px;
|
||
border-top: 1px solid var(--line);
|
||
}
|
||
h3 {
|
||
font-size: 17px;
|
||
margin-top: 22px;
|
||
}
|
||
p {
|
||
margin: 8px 0 14px;
|
||
}
|
||
.meta {
|
||
color: var(--muted);
|
||
margin-bottom: 24px;
|
||
}
|
||
.summary {
|
||
border-left: 4px solid var(--accent);
|
||
background: var(--soft);
|
||
padding: 14px 16px;
|
||
margin: 22px 0;
|
||
}
|
||
code {
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||
font-size: 0.92em;
|
||
color: var(--code);
|
||
background: #eef2f6;
|
||
padding: 1px 5px;
|
||
border-radius: 4px;
|
||
}
|
||
pre {
|
||
overflow-x: auto;
|
||
background: #101820;
|
||
color: #eef6ff;
|
||
padding: 14px 16px;
|
||
border-radius: 6px;
|
||
line-height: 1.45;
|
||
}
|
||
pre code {
|
||
background: transparent;
|
||
color: inherit;
|
||
padding: 0;
|
||
border-radius: 0;
|
||
}
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin: 14px 0 20px;
|
||
font-size: 14px;
|
||
}
|
||
th, td {
|
||
border: 1px solid var(--line);
|
||
padding: 8px 10px;
|
||
vertical-align: top;
|
||
text-align: left;
|
||
}
|
||
th {
|
||
background: var(--soft);
|
||
font-weight: 650;
|
||
}
|
||
ul, ol {
|
||
padding-left: 22px;
|
||
}
|
||
li {
|
||
margin: 6px 0;
|
||
}
|
||
.warn {
|
||
background: #fff7ed;
|
||
border: 1px solid #fed7aa;
|
||
padding: 12px 14px;
|
||
border-radius: 6px;
|
||
}
|
||
.ok {
|
||
background: #ecfdf5;
|
||
border: 1px solid #a7f3d0;
|
||
padding: 12px 14px;
|
||
border-radius: 6px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<main>
|
||
<h1>VCU118 Rocket SoC Flash Boot Akışı</h1>
|
||
<p class="meta">Workspace: <code>/home/ankaoc/Desktop/rocket/rocket-chip</code> - Kart: Xilinx VCU118, <code>xcvu9p-flga2104-2L-e</code> - Tarih: 1 Temmuz 2026</p>
|
||
|
||
<div class="summary">
|
||
<p><strong>Son durum:</strong> Rocket SoC VCU118 üzerinde çalışıyor. Bootloader BootROM'dan başlıyor, kart üzerindeki QSPI flash'ı SPI üzerinden okuyor, <code>APP_FLASH_OFFSET</code> adresindeki uygulama imajını DTIM'e kopyalıyor, CRC kontrolünden sonra uygulamaya atlıyor. Hello UART uygulaması kart üzerinde doğrulandı.</p>
|
||
</div>
|
||
|
||
<h2>Ne Yapıldı?</h2>
|
||
<ul>
|
||
<li>Chipyard kullanmadan, repo içindeki Rocket Chip ve <code>mill</code> akışıyla VCU118 için SoC üretildi.</li>
|
||
<li>VCU118 top wrapper non-project Vivado akışına bağlandı.</li>
|
||
<li>Rocket SoC'ye UART ve SPI flash controller eklendi.</li>
|
||
<li>SPI flash sinyalleri Virtex UltraScale+ <code>STARTUPE3</code> primitive'i üzerinden karttaki config QSPI flash'a bağlandı.</li>
|
||
<li>BootROM içeriği hello programı yerine flash bootloader olacak şekilde değiştirildi.</li>
|
||
<li>Bootloader, flash'tan header okuyup payload'u <code>0x80000000</code> DTIM adresine yüklüyor.</li>
|
||
<li>ELF uygulaması için flash imaj formatı ve CRC32 kontrolü eklendi.</li>
|
||
<li>Bitstream ve ELF/app flash programlama iki ayrı akışa ayrıldı.</li>
|
||
<li>Eski tek MCS ile komple flash programlama opsiyonu korundu.</li>
|
||
</ul>
|
||
|
||
<h2>Klasör Yapısı</h2>
|
||
<p>Donanım tarafı tek <code>hardware/</code> ağacında toplanır. <code>soc/</code> artık yazılım ve flash imaj üretimi içindir; target dizinleri ise akış giriş noktalarını ve scriptleri tutar.</p>
|
||
<pre><code>hardware/
|
||
upstream/rocket-chip/
|
||
src/
|
||
bootrom/
|
||
scala
|
||
resources
|
||
soc/
|
||
Makefile
|
||
scala/fpga/VCU118Peripherals.scala
|
||
generated/
|
||
vcu118-soc/
|
||
vcu118-soc-verilog/
|
||
asic-rocket-soc/
|
||
asic-rocket-soc-verilog/
|
||
asic-rocket-soc-sim-verilog/
|
||
fpga/vcu118/
|
||
rtl/
|
||
Makefile
|
||
axi4_ram_64.sv
|
||
axi4_slave_tieoff_64.sv
|
||
rocket_bscan_dmi_tunnel.sv
|
||
constraints/
|
||
vcu118_rocket.xdc
|
||
sim/
|
||
fpga/vcu118/Makefile
|
||
asic/rocket/
|
||
wave.do
|
||
tb/
|
||
external/gps/rtl/
|
||
filelist.f
|
||
|
||
soc/
|
||
Makefile
|
||
software/
|
||
Makefile
|
||
bsp/
|
||
bootloader/
|
||
common/
|
||
hello_uart/
|
||
tools/make_flash_image.py
|
||
out/
|
||
software/
|
||
flash/
|
||
|
||
targets/fpga/vcu118/
|
||
Makefile
|
||
docs/
|
||
vcu118_rocket_flash_boot_guide.html
|
||
flow/vivado/
|
||
Makefile
|
||
scripts/
|
||
build.tcl
|
||
program_flash.tcl
|
||
program_fpga.tcl
|
||
write_flash.tcl
|
||
write_flash_regions.tcl
|
||
tools/
|
||
gen_vcu118_wrapper.py
|
||
out/
|
||
bitstream/
|
||
build/
|
||
flash/
|
||
logs/
|
||
reports/</code></pre>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Dizin</th>
|
||
<th>İçerik</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>hardware/soc</code></td>
|
||
<td>Ortak Scala/Chisel SoC generator kaynakları ve FIRRTL/RTL üretim Makefile'ı.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>hardware/fpga/vcu118/rtl</code></td>
|
||
<td>FPGA shell yardımcı RTL kaynakları.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>hardware/fpga/vcu118/constraints</code></td>
|
||
<td>VCU118 XDC constraint dosyaları.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>soc/software</code></td>
|
||
<td>Bare-metal BSP, bootloader, linker scriptleri ve uygulama kaynakları.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>soc/software/tools</code></td>
|
||
<td>ELF dosyasını bootloader flash imajına çeviren ortak yardımcı araçlar.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>targets/fpga/vcu118/tools</code></td>
|
||
<td>VCU118 top wrapper generator.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>targets/fpga/vcu118/flow/vivado/scripts</code></td>
|
||
<td>Vivado non-project build, flash image ve programlama Tcl scriptleri.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>targets/fpga/vcu118/out</code></td>
|
||
<td>Bitstream, generated wrapper, MCS/BIN, Vivado logları ve raporlar.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3>Makefile Ağacı</h3>
|
||
<p>Her katmanın kendi Makefile'ı var. <code>soc/Makefile</code> ortak SoC/software hedeflerini, <code>targets/fpga/vcu118/Makefile</code> FPGA hedeflerini, <code>targets/asic/rocket/Makefile</code> ASIC sim/shell hedeflerini yönetir.</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Makefile</th>
|
||
<th>Tipik hedefler</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>soc/Makefile</code></td>
|
||
<td><code>software</code>, <code>chisel</code>, <code>fpga-rtl</code>, <code>asic-rtl</code>, <code>clean</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>hardware/soc/Makefile</code></td>
|
||
<td><code>firrtl</code>, <code>rtl</code>, <code>fpga-rtl</code>, <code>asic-rtl</code>, <code>clean</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>soc/software/Makefile</code></td>
|
||
<td><code>all</code>, <code>app</code>, <code>bootloader</code>, <code>hello</code>, <code>flash-image</code>, <code>clean</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>hardware/fpga/vcu118/rtl/Makefile</code></td>
|
||
<td><code>shell</code>, <code>generated</code>, <code>clean</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>targets/fpga/vcu118/flow/vivado/Makefile</code></td>
|
||
<td><code>bitstream</code>, <code>program</code>, <code>flash-app</code>, <code>flash-bitstream</code>, <code>flash-combined</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>hardware/sim/fpga/vcu118/Makefile</code></td>
|
||
<td><code>smoke</code>, <code>clean</code>. Full Verilator harness henüz eklenmedi.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h2>Kavramlar: Scala, Chisel, Rocket, Mill, FIRRTL ve firtool</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Kavram</th>
|
||
<th>Bu projedeki anlamı</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Scala</td>
|
||
<td>JVM üzerinde çalışan programlama dili. Rocket Chip ve Chisel tabanlı SoC generator kodu Scala ile yazılıyor. Burada Scala kodu doğrudan RTL değildir; RTL üreten generator kodudur.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Chisel</td>
|
||
<td>Scala içine gömülü hardware construction language. Register, wire, module, bus bağlantısı gibi donanım yapıları Scala kodu ile tarif edilir. Chisel çıktısı önce FIRRTL ara temsiline gider.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Rocket Chip</td>
|
||
<td>Chisel ile yazılmış parametrik RISC-V SoC generator. Rocket core, TileLink bus yapısı, BootROM, debug, interrupt ve memory/peripheral bağlantı altyapısını sağlar.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Mill</td>
|
||
<td>Scala/JVM build tool. Bu repoda Rocket Chip Scala kodunu derleyip çalıştırılabilir assembly jar üretmek için kullanılıyor.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>FIRRTL</td>
|
||
<td>Chisel'den çıkan ara devre temsili. FPGA/ASIC backend'e gitmeden önce donanım bu IR ile ifade edilir.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>firtool</td>
|
||
<td>CIRCT tabanlı FIRRTL compiler. Bu akışta <code>VCU118RocketSystem.fir</code> dosyasını synthesizable SystemVerilog dosyalarına çevirir.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p>Kısaca zincir şöyledir:</p>
|
||
<pre><code>Scala/Chisel Rocket config
|
||
-> mill ile assembly jar
|
||
-> Rocket generator çalışır
|
||
-> VCU118RocketSystem.fir
|
||
-> firtool
|
||
-> split SystemVerilog RTL
|
||
-> VCU118 wrapper + shell RTL + XDC
|
||
-> Vivado synth/place/route
|
||
-> vcu118_rocket.bit</code></pre>
|
||
|
||
<h2>RTL Nasıl Generate Ediliyor?</h2>
|
||
<p>RTL üretimi <code>make -C targets/fpga/vcu118 rtl</code> hedefiyle başlıyor. Ana adımlar:</p>
|
||
<ol>
|
||
<li><code>make -C soc/software/hello_uart</code> ile hello ELF build edilir.</li>
|
||
<li><code>make -C soc/software/bootloader</code> ile flash bootloader build edilir ve BootROM imajı üretilir.</li>
|
||
<li><code>mill -i 'rocketchip[6.7.0].assembly'</code> Rocket Chip Scala/Chisel kodunu assembly jar haline getirir.</li>
|
||
<li><code>java -jar out/rocketchip/6.7.0/assembly.dest/out.jar</code> komutu Rocket generator'ı çalıştırır.</li>
|
||
<li>Generator top olarak <code>freechips.rocketchip.fpga.VCU118RocketSystem</code>, config olarak <code>freechips.rocketchip.fpga.VCU118RocketConfig</code> kullanır.</li>
|
||
<li>Çıktı olarak <code>hardware/generated/vcu118-soc/VCU118RocketSystem.fir</code> oluşur.</li>
|
||
<li><code>firtool</code> bu FIRRTL dosyasını <code>hardware/generated/vcu118-soc-verilog/</code> altında SystemVerilog dosyalarına böler.</li>
|
||
<li><code>filelist.f</code> oluşturulur ve generated assert satırları FPGA sentezini sadeleştirmek için temizlenir.</li>
|
||
<li><code>gen_vcu118_wrapper.py</code> generated top portlarını okuyup <code>targets/fpga/vcu118/out/build/generated/vcu118_rocket_top.sv</code> wrapper'ını üretir.</li>
|
||
</ol>
|
||
|
||
<p>Bu zinciri çalıştıran ana komutlar:</p>
|
||
<pre><code>make -C targets/fpga/vcu118 soc # Chisel/FIRRTL/SystemVerilog üretir
|
||
make -C targets/fpga/vcu118 bitstream # soc + Vivado non-project FPGA build</code></pre>
|
||
|
||
<h2>Bizim Rocket Config Neyi Oluşturuyor?</h2>
|
||
<p>VCU118'e özel SoC tanımı <code>hardware/soc/scala/fpga/VCU118Peripherals.scala</code> içinde. Ana sınıflar:</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Sınıf / Config</th>
|
||
<th>Rolü</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>VCU118RocketSystem</code></td>
|
||
<td>Rocket subsystem üzerine BootROM, UART, SPI, GPS APB blokları ve top-level IO bridge bağlantılarını ekleyen SoC top generator.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>VCU118RocketSystemModuleImp</code></td>
|
||
<td>Generated top-level portları oluşturur: <code>uart_rxd</code>, <code>uart_txd</code>, <code>spi_sck</code>, <code>spi_csn</code>, <code>spi_dq_i/o/oe</code>, <code>gps_rf_clk</code>, <code>gps_rf_data</code>.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>VCU118RocketConfig</code></td>
|
||
<td>SoC konfigürasyon zinciri. BootROM, debug SBA, harici memory portlarının kapatılması, hard double FPU, 1 MiB DTIM, UART, SPI, GPS APB blokları ve tek küçük Rocket core burada seçilir.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>WithVCU118OneMiBDTIM</code></td>
|
||
<td>Harici AXI RAM yerine Rocket D-cache scratchpad/DTIM kullanır. Varsayılan adres <code>0x80000000</code>, boyut 1 MiB.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>WithVCU118FlashBootROM</code></td>
|
||
<td>BootROM içeriğini <code>soc/out/software/bootloader/bootloader.img</code> dosyasından alır.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>WithVCU118UART</code></td>
|
||
<td>UART peripheral'ı <code>0x10013000</code> adresine ekler. Clock 100 MHz, baud 115200.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>WithVCU118SPI</code></td>
|
||
<td>SPI flash controller'ı <code>0x10014000</code> adresine ekler.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>WithVCU118GPS</code></td>
|
||
<td>1 acquisition ve 9 tracking APB wrapper'ını <code>0x10020000</code> tabanından başlayarak 4 KiB pencereler halinde ekler.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p><code>VCU118RocketConfig</code> özeti:</p>
|
||
<pre><code>new WithVCU118FlashBootROM ++
|
||
new WithDebugSBA ++
|
||
new WithoutTLMonitors ++
|
||
new WithNoMemPort ++
|
||
new WithNoMMIOPort ++
|
||
new WithNoSlavePort ++
|
||
new WithNMemoryChannels(0) ++
|
||
new WithNBanks(0) ++
|
||
new WithVCU118OneMiBDTIM ++
|
||
new WithVCU118UART(0x10013000L, 100000000, 115200) ++
|
||
new WithVCU118SPI(0x10014000L) ++
|
||
new WithVCU118GPS(0x10020000L, 9) ++
|
||
new WithNExtTopInterrupts(0) ++
|
||
new WithNSmallCores(1) ++
|
||
new WithIncoherentBusTopology ++
|
||
new BaseConfig</code></pre>
|
||
|
||
<h2>Peripheral'leri Nasıl Ekledik?</h2>
|
||
<h3>UART</h3>
|
||
<p><code>TLFpgaUART</code>, Rocket'ın peripheral bus'ına bağlı bir <code>TLRegisterNode</code> olarak yazıldı. Register map:</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Offset</th>
|
||
<th>Register</th>
|
||
<th>Görev</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td><code>0x00</code></td><td><code>txdata</code></td><td>Bir byte yaz, UART TX başlasın.</td></tr>
|
||
<tr><td><code>0x04</code></td><td><code>rxdata</code></td><td>Alınan byte oku.</td></tr>
|
||
<tr><td><code>0x08</code></td><td><code>txready</code></td><td>TX yeni byte kabul edebilir mi?</td></tr>
|
||
<tr><td><code>0x0c</code></td><td><code>rxvalid</code></td><td>RX byte hazır mı?</td></tr>
|
||
<tr><td><code>0x10</code></td><td><code>clockfreq</code></td><td>UART clock frekansı.</td></tr>
|
||
<tr><td><code>0x14</code></td><td><code>baudrate</code></td><td>UART baud değeri.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p>UART pinleri doğrudan TileLink içinde kalmıyor; <code>BundleBridgeSource/Sink</code> ile top-level IO'ya çıkarılıyor. Wrapper bu portları VCU118 RS232 UART pinlerine bağlıyor.</p>
|
||
|
||
<h3>SPI Flash Controller</h3>
|
||
<p><code>TLFpgaSPI</code> da peripheral bus üzerinde <code>TLRegisterNode</code>. Register map:</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Offset</th>
|
||
<th>Register</th>
|
||
<th>Görev</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td><code>0x00</code></td><td><code>ctrl</code></td><td><code>busy</code>, <code>csn</code>, <code>quad</code> kontrol/status bitleri.</td></tr>
|
||
<tr><td><code>0x04</code></td><td><code>tx</code></td><td>Transfer başlatır.</td></tr>
|
||
<tr><td><code>0x08</code></td><td><code>rx</code></td><td>Son alınan shift register değeri.</td></tr>
|
||
<tr><td><code>0x0c</code></td><td><code>ready</code></td><td>Controller hazır mı?</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p>SPI controller serial ve quad mod için <code>sck</code>, <code>csn</code>, <code>dq_i</code>, <code>dq_o</code>, <code>dq_oe</code> sinyalleri üretir. Bu sinyaller top-level porta çıkarılır ve wrapper içinde <code>STARTUPE3</code> ile FPGA config flash pinlerine bağlanır.</p>
|
||
|
||
<h3>GPS APB IP'leri ve AFE Yardımcı Bloğu</h3>
|
||
<p>GPS entegrasyonu <code>/home/ankaoc/Desktop/anka-rocket/gps_system</code> altındaki SystemVerilog wrapper'ları kullanır. SoC içinde bir adet <code>acq_wrapper</code>, dokuz adet <code>tracking_wrapper</code> ve APB peripheral olmayan bir <code>afe_top</code> instance edilir.</p>
|
||
<p><code>APBGpsBlock</code> TileLink peripheral bus'tan <code>TLToAPB</code> ile APB'ye geçer. Acquisition ve tracking blokları APB register penceresi olarak görünür; <code>afe_top</code> sadece yardımcı logic olduğu için address map'e girmez.</p>
|
||
<p>GPS APB baz adresi <code>0x10020000</code>. Her IP'ye <code>0x1000</code> byte pencere ayrılır. Tracking adres formülü: <code>0x10020000 + (tracking_index + 1) * 0x1000</code>.</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Blok</th>
|
||
<th>GPS base'e göre offset</th>
|
||
<th>Absolute base</th>
|
||
<th>Adres aralığı</th>
|
||
<th>Interrupt</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td><code>gps-acq</code></td><td><code>0x00000</code></td><td><code>0x10020000</code></td><td><code>0x10020000..0x10020fff</code></td><td>PLIC source <code>1</code></td></tr>
|
||
<tr><td><code>gps-tracking0</code></td><td><code>0x01000</code></td><td><code>0x10021000</code></td><td><code>0x10021000..0x10021fff</code></td><td>PLIC source <code>2</code></td></tr>
|
||
<tr><td><code>gps-tracking1</code></td><td><code>0x02000</code></td><td><code>0x10022000</code></td><td><code>0x10022000..0x10022fff</code></td><td>PLIC source <code>3</code></td></tr>
|
||
<tr><td><code>gps-tracking2</code></td><td><code>0x03000</code></td><td><code>0x10023000</code></td><td><code>0x10023000..0x10023fff</code></td><td>PLIC source <code>4</code></td></tr>
|
||
<tr><td><code>gps-tracking3</code></td><td><code>0x04000</code></td><td><code>0x10024000</code></td><td><code>0x10024000..0x10024fff</code></td><td>PLIC source <code>5</code></td></tr>
|
||
<tr><td><code>gps-tracking4</code></td><td><code>0x05000</code></td><td><code>0x10025000</code></td><td><code>0x10025000..0x10025fff</code></td><td>PLIC source <code>6</code></td></tr>
|
||
<tr><td><code>gps-tracking5</code></td><td><code>0x06000</code></td><td><code>0x10026000</code></td><td><code>0x10026000..0x10026fff</code></td><td>PLIC source <code>7</code></td></tr>
|
||
<tr><td><code>gps-tracking6</code></td><td><code>0x07000</code></td><td><code>0x10027000</code></td><td><code>0x10027000..0x10027fff</code></td><td>PLIC source <code>8</code></td></tr>
|
||
<tr><td><code>gps-tracking7</code></td><td><code>0x08000</code></td><td><code>0x10028000</code></td><td><code>0x10028000..0x10028fff</code></td><td>PLIC source <code>9</code></td></tr>
|
||
<tr><td><code>gps-tracking8</code></td><td><code>0x09000</code></td><td><code>0x10029000</code></td><td><code>0x10029000..0x10029fff</code></td><td>PLIC source <code>10</code></td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h4>Scala/Chisel ile SoC'ye Nasıl Ekleniyor?</h4>
|
||
<p><code>FpgaGPSParams</code> base address, tracking sayısı ve pencere boyunu taşır. <code>WithVCU118GPS(0x10020000L, 9)</code> config fragment'i bu parametreyi aktif eder. <code>VCU118RocketSystem</code> içinde <code>p(VCU118GPSKey)</code> okunur, <code>APBGpsBlock</code> yaratılır ve <code>PBUS</code> üzerinden APB'ye bağlanır.</p>
|
||
<pre><code>val pbus = locateTLBusWrapper(PBUS)
|
||
val domain = pbus.generateSynchronousDomain("FPGAGPS")
|
||
val dev = domain { LazyModule(new APBGpsBlock(params, pbus.beatBytes)) }
|
||
|
||
domain {
|
||
dev.node := TLToAPB() := pbus.coupleTo("gps") {
|
||
TLFragmenter(pbus, Some("FPGAGPS")) := _
|
||
}
|
||
ibus.fromSync := dev.intnode
|
||
}</code></pre>
|
||
<p>APB wrapper'ların lokal adres genişliği pencere boyundan gelir. Varsayılan 4 KiB pencere için <code>PADDR</code> 12 bittir; üst adres bitleri boşa taşınmaz.</p>
|
||
|
||
<h4>Clock, RF IO ve AFE Bağlantısı</h4>
|
||
<p>FPGA wrapper 100 MHz fabric clock, 100 MHz acquisition clock ve 125 MHz tracking clock üretir. RF clock üretilmez; <code>gps_rf_clk</code> dış IO olarak gelir ve 16.368 MHz olarak constrain edilir. RF sample datası da <code>gps_rf_data[3:0]</code> dış IO'dur.</p>
|
||
<p><code>afe_top.start_i</code> acquisition wrapper'ın <code>afe_start</code> çıkışından, <code>sample_counter_clear_i</code> ise <code>sample_counter_clear</code> çıkışından sürülür. <code>afe_top.sample_align_o</code> acquisition ve bütün tracking wrapper'lara, <code>afe_top.sample_counter_o</code> ise bütün tracking wrapper'lara bağlanır.</p>
|
||
<p>ASIC sim top'unda RF clock yine PLL modelinden çıkmaz; testbench <code>gps_rf_clk</code> girişini 16.368 MHz dış kaynak gibi sürer.</p>
|
||
|
||
<h3>BootROM ve Bootloader</h3>
|
||
<p>BootROM imajı <code>soc/out/software/bootloader/bootloader.img</code>. Bootloader şunları yapar:</p>
|
||
<ol>
|
||
<li>UART üzerinden durum mesajı basar.</li>
|
||
<li>SPI flash reset ve JEDEC ID okuma komutlarını gönderir.</li>
|
||
<li><code>APP_FLASH_OFFSET</code> adresinden 64 byte header okur.</li>
|
||
<li>Header'daki load address, entry, size ve CRC alanlarını doğrular.</li>
|
||
<li>Payload'u flash'tan DTIM'e kopyalar.</li>
|
||
<li>CRC doğruysa entry adresine jump eder.</li>
|
||
</ol>
|
||
|
||
<h2>FPGA İçin Eklenen Wrapper ve Shell Modülleri</h2>
|
||
<p>Generated Rocket RTL doğrudan VCU118 kartına uygun değildir. Bu yüzden <code>gen_vcu118_wrapper.py</code> bir shell top üretir:</p>
|
||
<ul>
|
||
<li><code>IBUFDS</code> ile VCU118 <code>sysclk1_300</code> differential clock alınır.</li>
|
||
<li><code>BUFGCE_DIV</code> ile fabric clock üretilir.</li>
|
||
<li>Reset ve LED gibi kart seviyesi sinyaller bağlanır.</li>
|
||
<li>UART portları kart RS232 UART pinlerine bağlanır.</li>
|
||
<li>BSCAN-DMI debug tunnel daha önceki JTAG/RAM load testleri için wrapper'a bağlanır.</li>
|
||
<li>SPI flash portları <code>STARTUPE3</code> primitive'ine bağlanır.</li>
|
||
</ul>
|
||
|
||
<p><code>STARTUPE3</code> bağlantısının amacı: VCU118 üzerindeki config QSPI flash pinleri normal user IO gibi dışarı çıkmaz. UltraScale+ cihazda user design bu flash'a <code>STARTUPE3</code> üzerinden erişir. Bizim wrapper'da bağlantı şu mantıkla yapılıyor:</p>
|
||
<pre><code>assign spi_dq_i = cfg_flash_dq_i;
|
||
|
||
STARTUPE3 u_cfg_flash_startup (
|
||
.DI(cfg_flash_dq_i),
|
||
.DO(spi_dq_o),
|
||
.DTS(~spi_dq_oe),
|
||
.FCSBO(spi_csn),
|
||
.FCSBTS(1'b0),
|
||
.USRCCLKO(spi_sck),
|
||
.USRCCLKTS(1'b0),
|
||
...
|
||
);</code></pre>
|
||
|
||
<h2>Vivado FPGA Flow Scriptleri Nasıl Çalışıyor?</h2>
|
||
<p>Vivado tarafı non-project mode. Ana script <code>targets/fpga/vcu118/flow/vivado/scripts/build.tcl</code>:</p>
|
||
<ol>
|
||
<li>Part: <code>xcvu9p-flga2104-2L-e</code>.</li>
|
||
<li>Board part: <code>xilinx.com:vcu118:part0:2.4</code>.</li>
|
||
<li><code>hardware/generated/vcu118-soc-verilog/filelist.f</code> içindeki Rocket SystemVerilog dosyalarını okur.</li>
|
||
<li><code>hardware/upstream/rocket-chip/src/main/resources/vsrc/plusarg_reader.v</code> ve GPS APB/native RTL kaynaklarını okur.</li>
|
||
<li><code>hardware/fpga/vcu118/rtl/*.sv</code> içindeki FPGA shell yardımcı RTL dosyalarını okur.</li>
|
||
<li><code>targets/fpga/vcu118/out/build/generated/vcu118_rocket_top.sv</code> wrapper'ını okur.</li>
|
||
<li><code>hardware/fpga/vcu118/constraints/vcu118_rocket.xdc</code> constraint dosyasını okur.</li>
|
||
<li><code>synth_design</code>, <code>opt_design</code>, <code>place_design</code>, <code>phys_opt_design</code>, <code>route_design</code> çalıştırır.</li>
|
||
<li>Timing, utilization, DRC ve power raporları üretir.</li>
|
||
<li><code>targets/fpga/vcu118/out/bitstream/vcu118_rocket.bit</code> dosyasını yazar.</li>
|
||
</ol>
|
||
|
||
<h2>Kaynaklar</h2>
|
||
<ul>
|
||
<li><a href="https://www.chisel-lang.org/">Chisel resmi sitesi</a></li>
|
||
<li><a href="https://github.com/chipsalliance/rocket-chip">Rocket Chip Generator resmi deposu</a></li>
|
||
<li><a href="https://mill-build.org/mill/index.html">Mill build tool resmi dokümanı</a></li>
|
||
<li><a href="https://circt.llvm.org/docs/Dialects/FIRRTL/RationaleFIRRTL/">CIRCT / FIRRTL ve firtool dokümantasyonu</a></li>
|
||
</ul>
|
||
|
||
<h2>Önemli Dosyalar</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Dosya</th>
|
||
<th>Görev</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>hardware/soc/scala/fpga/VCU118Peripherals.scala</code></td>
|
||
<td>VCU118 Rocket config, UART, SPI flash controller, GPS APB blokları, AFE bağlantısı ve BootROM ayarları.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>/home/ankaoc/Desktop/anka-rocket/gps_system/</code></td>
|
||
<td>Acquisition, tracking, common AFE helper ve APB wrapper SystemVerilog kaynakları.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>targets/fpga/vcu118/tools/gen_vcu118_wrapper.py</code></td>
|
||
<td>Generated Rocket top modülünü VCU118 wrapper'a bağlar; SPI flash için <code>STARTUPE3</code> ekler.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>targets/fpga/vcu118/flow/vivado/scripts/build.tcl</code></td>
|
||
<td>Vivado non-project sentez, place, route ve bitstream üretim akışı.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>targets/fpga/vcu118/Makefile</code></td>
|
||
<td>Tek giriş noktası; software, RTL, bitstream, flash image ve programlama hedeflerini yönetir.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>soc/software/tools/make_flash_image.py</code></td>
|
||
<td>ELF dosyasını bootloader'ın okuyacağı header + payload flash imajına çevirir.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>targets/fpga/vcu118/flow/vivado/scripts/write_flash_regions.tcl</code></td>
|
||
<td>Bitstream ve app için ayrı MCS üretir; aradaki boşluğu MCS'e koymaz.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>targets/fpga/vcu118/flow/vivado/scripts/program_flash.tcl</code></td>
|
||
<td>Vivado hw_server üzerinden seçilen MCS region dosyalarını flash'a yazar.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>soc/software/bootloader/</code></td>
|
||
<td>BootROM'a gömülen SPI flash bootloader kaynakları.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>soc/software/hello_uart/</code></td>
|
||
<td>DTIM'e yüklenip çalışan bare-metal hello UART uygulaması.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h2>Flash Bellek Yerleşimi</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Bölge</th>
|
||
<th>Varsayılan Offset</th>
|
||
<th>İçerik</th>
|
||
<th>Nasıl Değiştirilir?</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Bitstream</td>
|
||
<td><code>0x00000000</code></td>
|
||
<td>FPGA konfigürasyon bitstream'i.</td>
|
||
<td><code>BITSTREAM_OFFSET=0x...</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Application / ELF image</td>
|
||
<td><code>0x06000000</code></td>
|
||
<td>Bootloader header + ELF payload.</td>
|
||
<td><code>APP_FLASH_OFFSET=0x...</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p>Son doğrulanan app image bilgisi:</p>
|
||
<pre><code>flash_offset: 0x06000000
|
||
payload_flash_offset: 0x06000040
|
||
load_addr: 0x80000000
|
||
entry: 0x80000000
|
||
payload size: 425 bytes
|
||
crc32: 0xf5aa3b60</code></pre>
|
||
|
||
<h2>Normal Geliştirme Akışı</h2>
|
||
<h3>1. SoC ve bitstream üret</h3>
|
||
<pre><code>cd /home/ankaoc/Desktop/rocket/rocket-chip
|
||
make -C targets/fpga/vcu118 bitstream</code></pre>
|
||
<p>Bu hedef software'i de build eder, Rocket Verilog üretir, wrapper oluşturur ve Vivado non-project flow ile <code>targets/fpga/vcu118/out/bitstream/vcu118_rocket.bit</code> dosyasını üretir.</p>
|
||
|
||
<h3>2. Bitstream'i flash'a bir kere yaz</h3>
|
||
<pre><code>make -C targets/fpga/vcu118 program-flash-bitstream</code></pre>
|
||
<p>Bu işlem uzun sürer. Sadece bitstream region MCS'i üretir ve <code>BITSTREAM_OFFSET</code> adresine yazar. App offset'e kadar olan boşluğu programlamaz.</p>
|
||
|
||
<h3>3. ELF / uygulama değiştikçe sadece app region yaz</h3>
|
||
<pre><code>make -C targets/fpga/vcu118 program-flash</code></pre>
|
||
<p>Bu hızlı yoldur. <code>APP_ELF</code> dosyasından flash image üretir, <code>APP_FLASH_OFFSET</code> adresine küçük app MCS'i yazar. Bitstream'e dokunmaz.</p>
|
||
|
||
<h3>4. FPGA'yı JTAG üzerinden SRAM'e programlayıp hızlı test et</h3>
|
||
<pre><code>make -C targets/fpga/vcu118 program-fpga</code></pre>
|
||
<p>Bu komut flash'taki bitstream'i kullanmaz; direkt <code>.bit</code> dosyasını FPGA SRAM'e yükler. Bootloader yine kart üzerindeki SPI flash'tan uygulamayı okur. Geliştirme sırasında en hızlı test yöntemi budur.</p>
|
||
|
||
<h3>5. Power-cycle sonrası flash'tan kalkış</h3>
|
||
<p>Bitstream region bir kere doğru yazıldıktan sonra kart power-cycle veya program/reset sonrası flash'taki bitstream'den konfigüre olabilir. Ardından BootROM bootloader çalışır ve app region'daki imajı okur.</p>
|
||
|
||
<h2>Komut Referansı</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Komut</th>
|
||
<th>Açıklama</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>make -C targets/fpga/vcu118 software</code></td>
|
||
<td>Bootloader, hello app ve app flash image üretir.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>make -C targets/fpga/vcu118 soc</code></td>
|
||
<td>Rocket Verilog ve VCU118 wrapper üretir.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>make -C targets/fpga/vcu118 bitstream</code></td>
|
||
<td>Vivado non-project flow ile bitstream üretir.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>make -C targets/fpga/vcu118 flash-regions</code></td>
|
||
<td>Seçilen region'lar için ayrı MCS üretir. Varsayılan auto/all davranışı için <code>FLASH_REGIONS</code> kullanılır.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>make -C targets/fpga/vcu118 program-flash-bitstream</code></td>
|
||
<td>Sadece bitstream region'ını flash'a yazar.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>make -C targets/fpga/vcu118 program-flash</code></td>
|
||
<td>Sadece ELF/app image region'ını flash'a yazar. Günlük uygulama geliştirme için ana komut budur.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>make -C targets/fpga/vcu118 program-flash-elf</code></td>
|
||
<td><code>program-flash</code> ile aynı alias.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>make -C targets/fpga/vcu118 program-flash-all</code></td>
|
||
<td>Bitstream ve app'i iki ayrı region MCS ile yazar; aradaki gap'i yazmaz.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>make -C targets/fpga/vcu118 program-flash-combined</code></td>
|
||
<td>Eski tek MCS yolu. Bitstream + app tek MCS/BIN içinde üretilir ve programlanır.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>make -C targets/fpga/vcu118 program-fpga</code></td>
|
||
<td>Bitstream'i JTAG ile FPGA SRAM'e yükler.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>make -C targets/fpga/vcu118 prune</code></td>
|
||
<td>Büyük geçici çıktıları siler; mevcut bitstream, app image ve küçük region MCS dosyalarını korur.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>make -C targets/asic/rocket sim</code></td>
|
||
<td>Questa/ModelSim batch sanity sim çalıştırır. Varsayılan app <code>hello_uart</code>.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>make -C targets/asic/rocket sim GUI=1</code></td>
|
||
<td>Questa/ModelSim graphical simulator açar; <code>-voptargs="+acc"</code> ile sinyal erişimini açar, <code>onfinish stop</code> ayarlar, <code>hardware/sim/asic/rocket/wave.do</code> dosyasını yükler ve <code>run 0ns</code> yaparak GUI'yi debug için hazır bırakır.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h2>Ayarlanabilir Değişkenler</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Değişken</th>
|
||
<th>Varsayılan</th>
|
||
<th>Açıklama</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>BITSTREAM_OFFSET</code></td>
|
||
<td><code>0x00000000</code></td>
|
||
<td>Bitstream flash başlangıç adresi.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>APP_FLASH_OFFSET</code></td>
|
||
<td><code>0x06000000</code></td>
|
||
<td>Bootloader'ın app header beklediği flash adresi.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>APP_ELF</code></td>
|
||
<td><code>soc/out/software/hello_uart/hello_uart.elf</code></td>
|
||
<td>Flash'a konacak bare-metal ELF.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>BIT_FILE</code></td>
|
||
<td><code>targets/fpga/vcu118/out/bitstream/vcu118_rocket.bit</code></td>
|
||
<td>FPGA bitstream dosyası.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>FLASH_VERIFY</code></td>
|
||
<td><code>0</code></td>
|
||
<td>Vivado flash verify aç/kapat. Uzun sürdüğü için kapalı.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>CFGMEM_PART</code></td>
|
||
<td><code>mt25qu01g-spi-x1_x2_x4</code></td>
|
||
<td>VCU118 üzerindeki MT25QU01G flash için Vivado programlama parçası.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>FLASH_REGIONS</code></td>
|
||
<td><code>auto</code></td>
|
||
<td><code>app</code>, <code>bitstream</code>, <code>all</code> veya virgüllü liste.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3>Örnek: farklı app offset ile yazmak</h3>
|
||
<pre><code>APP_FLASH_OFFSET=0x06400000 make -C targets/fpga/vcu118 program-flash</code></pre>
|
||
<p class="warn">Bootloader build edilirken de aynı <code>APP_FLASH_OFFSET</code> değeri kullanılır. Flow bunu software build'e geçirir; offset değiştiğinde bootloader içeriği de yeniden oluşur. Bootloader BootROM'a gömülü olduğu için offset değişikliği tam SoC/bitstream rebuild gerektirebilir.</p>
|
||
|
||
<h3>Örnek: farklı ELF yazmak</h3>
|
||
<pre><code>APP_ELF=/path/to/my_app.elf make -C targets/fpga/vcu118 program-flash</code></pre>
|
||
|
||
<h3>Örnek: Questa GUI sim açmak</h3>
|
||
<pre><code>make -C targets/asic/rocket sim GUI=1</code></pre>
|
||
<p>Bu komut compile/DPI hazırlığını yapar, <code>asic_rocket_tb</code> testbench'ini <code>-voptargs="+acc"</code> ile GUI'de yükler, <code>onfinish stop</code> kurar, <code>hardware/sim/asic/rocket/wave.do</code> dosyasını çalıştırır ve otomatik olarak sadece <code>run 0ns</code> çalıştırır. Devam etmek için Questa transcript'te elle <code>run -all</code> veya istediğin süre kadar <code>run 1 us</code> yazabilirsin.</p>
|
||
|
||
<h2>Test Sonucu</h2>
|
||
<p>1 Temmuz 2026 tarihinde yapılan kart testinde şu adımlar çalıştırıldı:</p>
|
||
<pre><code>make -C targets/fpga/vcu118 program-flash
|
||
make -C targets/fpga/vcu118 program-fpga</code></pre>
|
||
<p>UART cihazı: <code>/dev/ttyUSB2</code>, baud: <code>115200</code>.</p>
|
||
<p>Alınan UART çıktısı:</p>
|
||
<pre><code>VCU118 Rocket flash bootloader
|
||
Header offset: 0x06000000
|
||
Flash JEDEC ID: 0x0020bb21
|
||
Load: flash 0x06000040 -> mem 0x80000000, size 0x000001a9
|
||
CRC ok, jumping 0x80000000
|
||
|
||
Rocket Chip VCU118 hello UART
|
||
DTIM: 0x80000000..0x800fffff (1 MiB)
|
||
UART clock Hz: 0x047868c0
|
||
UART baud: 0x0001c200</code></pre>
|
||
<div class="ok">
|
||
<p><strong>Doğrulama:</strong> Bootloader flash JEDEC ID'yi doğru okudu, app header'ını <code>0x06000000</code> adresinden buldu, payload'u <code>0x80000000</code> DTIM adresine kopyaladı, CRC kontrolü geçti ve hello UART uygulaması çalıştı.</p>
|
||
</div>
|
||
|
||
<h2>Notlar ve Riskler</h2>
|
||
<ul>
|
||
<li><code>program-flash-bitstream</code> uzun sürer; bunu sadece bitstream değiştiğinde çalıştır.</li>
|
||
<li>Günlük bare-metal geliştirmede yalnız <code>program-flash</code> yeterlidir.</li>
|
||
<li><code>program-fpga</code> SRAM'e yükler; power-cycle sonrası kalıcı değildir.</li>
|
||
<li>Power-cycle sonrası kendi kendine boot için bitstream'in flash'ta doğru programlanmış olması gerekir.</li>
|
||
<li>Tek MCS opsiyonu <code>program-flash-combined</code> olarak duruyor; bu yol büyük MCS yazdığı için yavaştır.</li>
|
||
</ul>
|
||
</main>
|
||
</body>
|
||
</html>
|