İşlemcimizin tasarımını tamamlamışken en heyecanlı yerde bırakmak olmazdı. Bu yazıda bazı çevrebirimlerin tasarımına ve C ile programlar geliştirmeye değineceğiz. Ayrıca işlemcimizin fiziksel ortamda (FPGA) çalıştığına şahit olacağız.
Belleğe Eşlemeli Çevrebirimleri
Belleğimiz 2 KiB boyutundaydı, 32-bit genişlikte bir adres alanı için gayet ufak. Peki boş kalan adres uzayını farklı amaçlar için kullansak nasıl olur? Güncel bilgisayar mimarilerinde, sıklıkla bellek haritasına eşlenmiş (memory mapped) çevrebirimlerine rastlarız. Çevrebirimleri sayesinde çeşitli girdi çıktı işlemlerini yönetebiliriz. Örneğin bir mikrodenetleyicin genel amaçlı giriş çıkış pinlerine LED bağlayıp, yakıp söndürebiliriz. Veya seri uçbirime “merhaba dünya” yazdırabiliriz. Bunların her biri bir çevrebirim vasıtasıyla mümkün olmaktadır.
Peki işlemci, çevrebirimleri ile nasıl anlaşır? Her çevrebirimini yönetmek için ek buyruklar eklemeyi düşünebilirdik, fakat çevrebirimlerinin çeşidi ve adedi arttıkça işlemcimiz oldukça karışmaya başlayacaktır. Daha iyi bir çözüm olarak bellek adres uzayının bazı kısımlarını çevrebirimleri ile iletişim kurmak için ayırabiliriz. Neticede gigabaytlarca bir alanımız var ve biz burada belleklerden bahsederken kilobaytları kullanıyoruz. Belleğe eşleme yöntemi ile tıpkı belleğe erişir gibi (load-store buyrukları ile) çevrebirimlerimizi yönetebilmekteyiz.
Bu bölümde 3 basit çevrebiriminin tasarımına bakacağız. Bunlar: 8 pin GPIO çıkış, 32-bit saat sayacı ve UART TX. Bu çevrebirimlerinin bellek haritasına yerleşimi aşağıdaki gibi olacak.
İşlemcimizin bellek uzayı
GPIO
GPIO deyimi aslında ayarlanabilir genel amaçlı giriş çıkış çevrebirimini tarif etse de biz gayet basit, 8 pinli sabit çıkışlı bir birim tasarlayacağız. Bu çıkış pinlerimiz ile LED, step motor ve röle gibi elektronik parçaları kontrol edebiliriz.
İşlemcimiz GPIO çevrebirimini gpio_o
kaydedicisi üzerinden kontrol edecek. gpio_o
kaydedicisi işlemci tarafından hem okunabilir hem de yazılabilir durumda olacak.
Adres | Kaydedici | Bitler | İzinler |
---|---|---|---|
0x80001000 | gpio_o | boş[31:8], çıkış değeri[7:0] | okuma/yazma |
gpio.v:
// Matrak M10 RV32I RISC-V Processor
// Gülpare II Architechture 2023
// GPIO Output Module
module gpio (
input clk_i,
input rst_i,
input sel_i, // Seçim sinyali
input wen_i, // Yazma yetkilendirme
input [31:0] data_i, // Veri girişi, işlemciden geliyor.
output [31:0] data_o, // Veri çıkışı, işlemciye gidiyor.
output reg [7:0] gpio_o // GPIO çıkış pinleri
);
// Çevrebirim seçilmişse ve yazma etkin değilse (okuma etkin) çıkış kaydedicisinin değerini işlemciye gönder.
assign data_o = (sel_i & !wen_i) ? {24'b0, gpio_o} : 32'b0;
always @(posedge clk_i, posedge rst_i) begin
if (rst_i) begin // Çıkışlar sıfırlanıyor.
gpio_o <= 8'b0;
end else begin
// Çevrebirim seçilmişse ve yazma etkinse işlemciden gelen değeri çıkış kaydedicisine aktar.
if (sel_i & wen_i) begin
gpio_o <= data_i[7:0];
end
end
end
endmodule
Saat Sayacı
Bu birim her saat vuruşunda değeri 1 artan 32-bit genişlikte bir sayaç. Saat sayacını zamanlama gerektiren uygulamalarda kullanacağız.
Adres | Kaydedici | Bitler | İzinler |
---|---|---|---|
0x80003000 | counter_reg | saat sayacı[31:0] | okuma |
clock_counter.v:
// Matrak M10 RV32I RISC-V Processor
// Gülpare II Architechture 2023
// Clock Counter Register
module clock_counter (
input clk_i,
input rst_i,
input sel_i, // Seçim sinyali
output [31:0] data_o // Veri çıkışı, işlemciye gidiyor.
);
reg [31:0] counter_reg;
assign data_o = sel_i ? counter_reg : 32'b0;
always @(posedge clk_i, posedge rst_i) begin
if (rst_i) begin
counter_reg <= 32'b0;
end else begin
counter_reg <= counter_reg + 1;
end
end
endmodule
UART TX
Seri haberleşme işlemcimiz için oldukça önemli. Şimdilik sadece UART verici (transmitter) kısmı ile ilgileneceğiz. UART çevrebirimlerinde genelde baudrate değeri ayarlanabilir olur ama biz işi basit tutacağız. UART çevrebirimimiz sabit 115200 baudrate değerinde çalışacak.
Adres | Kaydedici | Bitler | İzinler |
---|---|---|---|
0x80002000 | uart_transmit | boş[31:8], gönderilecek veri[7:0] | yazma |
0x80002004 | uart_status | boş[31:1], hazır[0] | okuma |
UART ile veri göndermeden önce 0x80002004
adresindeki uart_status
kaydedicisinin 0. bitini kontrol etmeliyiz. Bu bit 1 ise, UART veri göndermeye hazırdır. 0x80002000
adresindeki uart_transmit
kaydedicisine göndermek istediğimiz karakteri yazmalıyız. Bu işlemin ardından UART çevrebirimi veri aktarımını başlatacaktır.
📌 Esasında UART modül tasarımı tek başına bölüm olmayı hak edecek kadar önemli bir konu. Fakat biz UART çevrebirimini şimdilik sadece işlemcimizin çalıştığını gözlemlemek için kullanacağız ve tasarım detaylarına değinmeyeceğiz.
uart.v:
// Matrak M10 RV32I RISC-V Processor
// Gülpare II Architechture 2023
// UART TX Module
module uart (
input clk_i,
input rst_i,
input sel_i, // Seçim sinyali
input wen_i, // Yazma yetkilendirme
input [31:0] addr_i, // Adres girişi, işlemciden geliyor.
input [31:0] data_i, // Veri girişi, işlemciden geliyor.
output [31:0] data_o, // Veri çıkışı, işlemciye gidiyor.
output uart_tx_o // UART TX bağlantısı
);
localparam UART_TRANSMIT_REG = 4'h0;
localparam UART_STATUS_REG = 4'h4;
wire done;
// Kaydedici adresi çözümleniyor.
wire tx_sel = (UART_TRANSMIT_REG == addr_i[3:0]);
wire status_sel = (UART_STATUS_REG == addr_i[3:0]);
// Gönderilecek veri yazılıyor. (gönderimi başlat)
wire tx_en = sel_i & wen_i & tx_sel;
// Durum okunuyor.
wire status_en = sel_i & status_sel;
assign data_o = status_en ? {30'b0, done} : 31'b0;
transmitter t1 (
.clk_i(clk_i),
.rst_i(rst_i),
.tx_data_i(data_i[7:0]),
.tx_en_i(tx_en),
.tx_done_o(done),
.tx_o(uart_tx_o)
);
endmodule
module transmitter (
input clk_i,
input rst_i,
input [7:0] tx_data_i,
input tx_en_i,
output reg tx_done_o,
output reg tx_o
);
localparam IDLE = 2'b00;
localparam START = 2'b01;
localparam TRANSMIT = 2'b10;
localparam DONE = 2'b11;
localparam CLKFREQ = 50_000_000;
localparam BAUD_RATE = 115200;
localparam BAUD_DIV = CLKFREQ/BAUD_RATE;
reg [15:0] t_counter;
reg [2:0] b_counter;
reg [7:0] shr;
reg [1:0] state;
always @(posedge clk_i, posedge rst_i) begin
if (rst_i) begin
state <= IDLE;
t_counter <= 0;
b_counter <= 0;
shr <= 8'b0;
tx_done_o <= 1'b1;
tx_o <= 1'b1;
end else begin
case (state)
IDLE : begin
b_counter <= 0;
tx_done_o <= 1'b1;
tx_o <= 1'b1;
if (tx_en_i) begin
tx_o <= 1'b0;
shr <= tx_data_i;
state <= START;
end else begin
state <= IDLE;
end
end
START : begin
tx_done_o <= 1'b0;
if (t_counter == BAUD_DIV-1) begin
t_counter <= 0;
shr[7] <= shr[0];
shr[6:0] <= shr[7:1];
tx_o <= shr[0];
state <= TRANSMIT;
end else begin
t_counter <= t_counter + 1;
end
end
TRANSMIT : begin
tx_done_o <= 1'b0;
if (b_counter == 7) begin
if (t_counter == BAUD_DIV-1) begin
t_counter <= 0;
b_counter <= 0;
tx_o <= 1'b1;
state <= DONE;
end else begin
t_counter <= t_counter + 1;
end
end else begin
if (t_counter == BAUD_DIV-1) begin
t_counter <= 0;
b_counter <= b_counter + 1;
shr[7] <= shr[0];
shr[6:0] <= shr[7:1];
tx_o <= shr[0];
end else begin
t_counter <= t_counter + 1;
end
end
end
DONE : begin
if (t_counter == BAUD_DIV-1) begin
t_counter <= 0;
tx_done_o <= 1'b1;
state <= IDLE;
end else begin
t_counter <= t_counter + 1;
end
end
default : state <= IDLE;
endcase
end
end
endmodule
İşlemci ve Çevrebirimleri Bir Araya getirme
Çevrebirimlerimizi tasarladık. Şimdi işlemcimiz ile çevrebirimlerini birleştireceğiz. Önceki bölümlerde load ve store işlemleri yalnızca bellekten yapılabiliyordu. Artık işlemciden yazma veya okuma isteği geldiğinde adresi çözümleyip bellek, GPIO, UART ve saat sayacı çevrebirimlerinden birine yönlendirmemiz gerekiyor.
top.v:
// Matrak M10 RV32I RISC-V Processor
// Gülpare II Architechture 2023
// Top Module
module top (
input clk_i,
input rst_i,
output [7:0] gpio_o,
output uart_tx_o
);
// İşlemci bağlantıları
wire stall;
wire wen;
wire ren;
wire [3:0] stb;
wire [31:0] inst_addr;
wire [31:0] data_addr;
wire [31:0] wdata;
wire [31:0] rdata;
matrak mt1 (
.clk_i(clk_i),
.rst_i(rst_i),
.stall_i(stall),
.inst_i(mem_rdata),
.data_i(rdata),
.wen_o(wen),
.ren_o(ren),
.stb_o(stb),
.inst_addr_o(inst_addr),
.data_addr_o(data_addr),
.data_o(wdata)
);
// Bellek bağlantıları
wire [31:0] mem_addr;
wire [31:0] mem_rdata;
wire mem_wen;
memory me1 (
.clk_i(clk_i),
.wen_i(mem_wen),
.stb_i(stb),
.addr_i(mem_addr),
.data_i(wdata),
.data_o(mem_rdata)
);
// GPIO bağlantıları
wire [31:0] gpio_rdata;
wire gpio_request;
gpio g1 (
.clk_i(clk_i),
.rst_i(rst_i),
.sel_i(gpio_request),
.wen_i(wen),
.data_i(wdata),
.data_o(gpio_rdata),
.gpio_o(gpio_o)
);
// UART bağlantıları
wire uart_request;
wire [31:0] uart_rdata;
uart u1 (
.clk_i(clk_i),
.rst_i(rst_i),
.sel_i(uart_request),
.wen_i(wen),
.addr_i(data_addr),
.data_i(wdata),
.data_o(uart_rdata),
.uart_tx_o(uart_tx_o)
);
// Saat sayacı bağlantıları
wire clock_counter_request;
wire [31:0] clock_counter_rdata;
clock_counter cc1(
.clk_i(clk_i),
.rst_i(rst_i),
.sel_i(clock_counter_request),
.data_o(clock_counter_rdata)
);
wire loadstore_request;
wire peripheral_access;
wire memory_ls_access;
wire [31:0] periph_rdata;
// Load Store isteği kontrol ediliyor.
assign loadstore_request = wen | ren;
// Adres: 0x8XXXXXXX, çevrebirimlere yönlendiriliyor.
assign peripheral_access = loadstore_request & data_addr[31];
// Okuma yazma isteği belleğe yönlendiriliyor.
assign memory_ls_access = loadstore_request & !peripheral_access;
// Çevrebirimlerden okunan veri seçiliyor.
assign periph_rdata = gpio_request ? gpio_rdata : (uart_request ? uart_rdata :
(clock_counter_request ? clock_counter_rdata : 32'b0));
// Adrese göre seçilen çevrebirim belirleniyor.
assign gpio_request = peripheral_access & data_addr[12] & !data_addr[13]; // 0x80001000
assign uart_request = peripheral_access & data_addr[13] & !data_addr[12]; // 0x80002000
assign clock_counter_request = peripheral_access & data_addr[12] & data_addr[13]; // 0x80003000
// Belleğe yazma isteği gönderiliyor.
assign mem_wen = wen & memory_ls_access;
// Belleğe aktarılacak adres seçiliyor. (veri adresi veya buyruk adresi)
assign mem_addr = memory_ls_access ? data_addr : inst_addr;
// İşlemciye gönderilecek veri seçiliyor. (bellekten veya çevrebirimlerinden okunan veri)
assign rdata = memory_ls_access ? mem_rdata : periph_rdata;
// Bellek meşgulse işlemciyi durdur.
assign stall = memory_ls_access;
endmodule
📌 Üst modül kodunda görebileceğiniz gibi adresi, 32-bit yerine birkaç bit üzerinden kontrol ediyoruz. Bu bize donanımı basitleştirme imkanı sağlasa da, aynı çevrebirimine birden fazla adresle erişme durumunu ortaya çıkarıyor.
📌 Çevrebirim adresini (0x8XXXXXXX) kesin belirlemek için
loadstore_request & data_addr[31]
ifadesi yerineloadstore_request & (data_addr[31:28] == 4’b1000)
yazmalıyız. Biz bu kadar yüksek adreslere başka amaçlar için erişmeyeceğiz, dolayısıyla herhangi bir değişiklik yapmamıza lüzum yok.
C ile Programlama
Buraya kadar işlemcimizi assembly ile test ettik. Artık C programlama diline geçmeye hazırız. Öncelikle derleyici araçlarını (toolchain) bilgisayarımıza kurmalıyız. Açık kaynaklı derleyici araçlarını RISC-V GNU Compiler Toolchain GitHub reposundan edinebiliriz. Derleyici araçlarını kurmak için öncelikle bilgisayarınızda derlemeniz gerekiyor. Toolchain kurulumunun “Newlib” ve “Linux” olarak ayrıldığını görüyoruz. Linux işletim sistemi çalıştıran bir RISC-V işlemciniz varsa C kütüphanesi tercihinizi Linux’tan yana kullanmalısınız. Biz sıfırdan tasarladığımız “barebone” işlemcimiz için Newlib kütüphanesini seçmeliyiz. İşlemci mimarimiz de -march rv32i
olarak seçilmeli.
📌 Bu kısım Linux işletim sistemi (Ubuntu) üzerinde çalıştığımız farzedilerek anlatılmıştır.
💡 Derleme işlemi, yeni başlayanlar için meşakkatli gelebir. Uğraşmak istemeyenler bu önceden derlenmiş (resmi olmayan) araçları edinebilirler.
Eğer önceden derlenmiş bir toolchain kullanmayı tercih ederseniz arşivden çıkarttığınız dizini Path’e eklemeniz gerekir. Uçbirime aşağıdaki komutları girerek toolchain’i kalıcı olarak Path’e ekleyebilirsiniz.
$ cd
$ nano .bashrc
Nano editöründe açılan .bashrc dosyasının uygun bir yerine export PATH="/$HOME/kurulum_dizini/riscv32-unknown-elf/bin/:$PATH"
komutunu yapıştırın ve kaydedip çıkın.
Her şey doğru gittiyse uçbirime riscv32-unknown-elf-gcc
komutu girdiğinizde uygulamanın çağrılmış olması gerekir.
Öyleyse Matrak işlemcimiz için ilk C kodumuzu yazmaya başlayalım. Şimdilik boş bir şablon hazırladık.
demo.c:
void main(void) {
while (1) {
}
}
Bu C kodu RV32I mimarisi için derlenirse aşağıdaki assembly koduna dönüşecek.
main:
addi sp,sp,-16
sw s0,12(sp)
addi s0,sp,16
.L2:
j .L2
💡 Yüksek seviyeli programlama dili ile hazırladığınız kodların assembly karşılıklarını incelemek için Compiler Explorer sitesini ziyaret edebilirsiniz.
Fakat bu kodu doğrudan işlemcimize yükleyemeyiz. Evvelinde kaydedici değerlerini sıfırlamak ve stack pointer (sp) kaydedicisine değer yüklemek gibi bazı başlangıç ayarlamalarını gerçekleştirmemiz gerekiyor. Bunu crt0.s assembly kodu ile halledeceğiz. crt0 ilk olarak başlangıç rutinlerini çalıştıracak ve ardından C kodumuzdaki main etiketine dallanacak.
Hatırlarsanız benzetim ortamında kaydedici dosyamızın başlangıç değerleri belirsiz olarak gözüküyordu. Bu yüzden ilk olarak tüm kaydedicilere 0 değerini yüklüyoruz. Daha sonra stack pointer kaydedicimize (x2) belleğimizin en yüksek adresini yüklüyoruz. Bu işlemlerin ardından main fonksiyonuna atlayabiliriz.
crt0.s:
.section .text
.global _start
_start:
# Kaydedicileri sıfırla
mv x1, x0
mv x2, x0
mv x3, x0
mv x4, x0
mv x5, x0
mv x6, x0
mv x7, x0
mv x8, x0
mv x9, x0
mv x10, x0
mv x11, x0
mv x12, x0
mv x13, x0
mv x14, x0
mv x15, x0
mv x16, x0
mv x17, x0
mv x18, x0
mv x19, x0
mv x20, x0
mv x21, x0
mv x22, x0
mv x23, x0
mv x24, x0
mv x25, x0
mv x26, x0
mv x27, x0
mv x28, x0
mv x29, x0
mv x30, x0
mv x31, x0
# Stack değerini yükle
la x2, 0x800
# Main fonksiyonuna atla
jump_main:
addi a0, x0, 0
addi a1, x0, 0
jal x1, main
# Yürütmeyi durdur
sleep_loop:
nop
j sleep_loop
📌 İşlemcimiz bir şekilde
sleep_loop
etiketine atlarsa sıfırlanıncaya kadar buradan çıkamaz. Bu, Motorola 6800 işlemcilerde iki illegal buyruğun yürütülmesiyle ortaya çıkan “halt and catch fire” (durdur ve yak) durumuna benziyor. Bu durum meydana gelirse işlemci sıfırlanıncaya kadar takılı kalıyormuş.
Kodlarımız hazır. Peki derleyici, buyrukları hangi adreslere yerleştirecek ve ne kadar belleğimizin olduğunu nereden bilecek? Burada linker script devreye giriyor. Linker script ile bellek boyutumuzu, program (text) ve verinin (data) nasıl yerleşeceği gibi bilgileri derleyiciye aktarıyoruz.
mem.ld:
OUTPUT_ARCH( "riscv" )
MEMORY
{
RAM : ORIGIN = 0x00000000, LENGTH = 0x800
}
SECTIONS
{
.text : {
. = ALIGN(4);
*(.text)
*(.text.*)
. = ALIGN(4);
} > RAM
.rodata : {
. = ALIGN(4);
*(.srodata)
*(.srodata.*)
*(.rodata);
*(.rodata.*)
. = ALIGN(4);
} > RAM
.data : {
. = ALIGN(4);
*(.sdata)
*(.sdata.*)
*(.data);
*(.data.*)
. = ALIGN(4);
} > RAM
}
Artık derleme işlemine geçebiliriz. Öncelikle crt0.s assembly kodunu derliyoruz. crt0.o adında bir obje dosyası oluşacak. Bunu, bir sonraki adımda C kodumuz ile birleştireceğiz.
$ riscv32-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -Wl,-T,mem.ld -c crt0.s -o crt0.o
Bu işlemin ardından C kodumuz ve crt0.o dosyasından ELF (Executable Linkable Format) dosyası elde edeceğiz.
$ riscv32-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -Wl,-T,mem.ld crt0.o demo.c -o demo.elf
Üretilen ELF dosyasını işlemcimize yükleyebilmek için ikilik tabanda makine koduna dönüştürüyoruz.
$ riscv32-unknown-elf-objcopy -O binary demo.elf demo.bin
Aslına bakarsanız bin uzantılı dosya, belleğe yazılmak için hazır ancak biz belleğimizi program.mem dosyasına yazdığımız onaltılık tabandaki kod ile programlıyoruz. Bin uzantılı dosyayı belleğe yüklemeden önce onaltılık tabana çevirmemiz gerekiyor. Bu işlemi gerçekleştirmek için üçüncü parti bir araç kullanabilir veya kendimiz bir tane yazabiliriz.
Neyse ki SiFive şirketinin bu işlemi gerçekleştirmek için yazmış olduğu bin2hex aracı var. Ancak bu araç C ile yazılmış, pratik olarak kullanmak için bunu Python scripti haline dönüştürmeliyiz.
bin2hex.py:
# Based on: https://github.com/sifive/freedom-elf2hex/blob/master/util/freedom-bin2hex.c
import sys
def help():
print("Convert a binary file to a format that can be read in verilog via $readmemh().")
print("By default read from stdin and write to stdout using a bit width of 8.")
print("\n")
print("python bin2hex.py [--help|-h] [--bit-width|-w INT] [--input|-i BIN] [--output|-o HEX]")
def dump(output_stream, byte_width, byte_array):
byte_width = int(byte_width)
for i in range(byte_width - 1, -1, -1):
output_stream.write(f'{byte_array[i]:02x}')
output_stream.write('\n')
def main():
ARRAY_SIZE = 1024
bit_width = 32
input_stream = sys.stdin.buffer
output_stream = sys.stdout
for i in range(len(sys.argv)):
if sys.argv[i] in ["--help", "-h"]:
help()
return
if sys.argv[i] in ["--bit-width", "-w"]:
if i + 1 == len(sys.argv):
sys.stderr.write("No arg for --bit-width|-w option.\n")
return 1
bit_width = int(sys.argv[i + 1])
if sys.argv[i] in ["--input", "-i"]:
if i + 1 == len(sys.argv):
sys.stderr.write("No arg for --input|-i option.\n")
return 1
input_stream = open(sys.argv[i + 1], 'rb')
if sys.argv[i] in ["--output", "-o"]:
if i + 1 == len(sys.argv):
sys.stderr.write("No arg for --output|-o option.\n")
return 1
output_stream = open(sys.argv[i + 1], 'w')
if bit_width < 8:
sys.stderr.write("Bit width cannot be negative or less than 8.\n")
return 2
if bit_width % 8 != 0:
sys.stderr.write("Cannot handle non-multiple-of-8 bit width yet.\n")
return 2
if bit_width > (ARRAY_SIZE * 8):
sys.stderr.write("Bit width is out of range (max supported is 8192).\n")
return 3
byte_width = bit_width / 8
byte_value = 0
byte_count = 0
byte_array = bytearray([0] * ARRAY_SIZE)
while True:
byte_value = input_stream.read(1)
if not byte_value:
break
byte_array[byte_count] = ord(byte_value)
byte_count += 1
if byte_count == byte_width:
byte_count = 0
dump(output_stream, byte_width, byte_array)
byte_array = bytearray([0] * ARRAY_SIZE)
if byte_count > 0:
dump(output_stream, byte_width, byte_array)
if __name__ == "__main__":
main()
Şimdi kodumuzu onaltılık tabana çevirebiliriz.
$ python3 bin2hex.py -i demo.bin -o demo.hex
Derleme komutlarını uçbirime tek tek girmek zahmetli olacağından işleri otomatize etmek için bir Makefile dosyası hazırlayabiliriz.
makefile:
CC = riscv32-unknown-elf-gcc
OBJCOPY = riscv32-unknown-elf-objcopy
CFLAGS = -march=rv32i -mabi=ilp32 -nostdlib -Wl,-T,mem.ld
all: demo.hex
crt0.o: crt0.s
$(CC) $(CFLAGS) -c crt0.s -o crt0.o
demo.elf: crt0.o demo.c
$(CC) $(CFLAGS) crt0.o demo.c -o demo.elf
demo.bin: demo.elf
$(OBJCOPY) -O binary demo.elf demo.bin
demo.hex: demo.bin
python3 bin2hex.py -i demo.bin -o demo.hex
clean:
rm -f crt0.o demo.elf demo.bin demo.hex
Şimdi demo.hex dosyasını oluşturmak için tek yapmamız gereken, uçbirime make all
veya make demo.hex
komutunu girmek.
Matrak Kütüphanesi
Sıra geldi çevrebirimlerimiz için bir sürücü kütüphanesi yazmaya. Kaydedici adreslerimizi gösteren pointer’ları struct içinde tanımlıyoruz. Kütüphanemiz birkaç fonksiyondan ibaret olacak.
gpio_write()
: gpio_pin
argümanında verilen pini, value
argümanında verilen değere göre 1 ya da 0 yapar.
delay_ms()
: ms türünden verilen süre boyunca işlemcinin beklemesini sağlar.
put_char()
: verilen karakteri UART üzerinden iletir.
put_str()
: verilen karakter dizisinindeki elemanları put_char()
fonksiyonunu çağırarak UART üzerinden iletir.
matrak.h:
// Matrak Çevrebirim Kütüphanesi
#include <stdint.h>
#define GPIO0_BASE (0x80001000U)
#define GPIO0 ((GPIO*) GPIO0_BASE)
#define UART0_BASE (0x80002000U)
#define UART0 ((UART*) UART0_BASE)
#define CYC_C0_BASE (0x80003000U)
#define CYC_C0 ((CYC_C*) CYC_C0_BASE)
#define CLK_FREQ 50000000
typedef struct {
volatile uint32_t gpio_output; // 0x80001000 (RW) OUTPUT REGISTER
} GPIO;
typedef struct {
volatile uint32_t uart_transmit; // 0x80002000 (W) TX DATA REGISTER
volatile uint32_t uart_status; // 0x80002004 (R) TX STATUS REGISTER
} UART;
typedef struct {
volatile uint32_t clock_counter; // 0x80003000 (R) CLOCK COUNTER REGISTER
} CYC_C;
void gpio_write(int gpio_pin, int value) {
if (value) {
GPIO0->gpio_output |= (1<<gpio_pin);
}
else {
GPIO0->gpio_output &= ~(1<<gpio_pin);
}
}
void delay_ms(int time) {
uint32_t clk_value = CYC_C0->clock_counter + (time * CLK_FREQ) / 1000;
while (CYC_C0->clock_counter < clk_value);
}
void put_char(int c) {
uint32_t done = 0;
UART0->uart_transmit = c;
while (done == 0) {
done = UART0->uart_status;
}
}
void put_str(const char* s) {
for (const char* p=s; *p; ++p) {
put_char(*p);
}
}
Kütüphanemiz hazır. Öyleyse kütüphanemizi kullanarak bir led yakma söndürme uygulaması yapalım. 4 numaralı çıkış pinini, 500 ms aralıkla 1 ve 0 arasında döndürüyoruz.
blink.c:
#include "matrak.h"
#define LED_PIN 4
#define HIGH 1
#define LOW 0
void main(void) {
while (1) {
gpio_write(LED_PIN, HIGH);
delay_ms(500);
gpio_write(LED_PIN, LOW);
delay_ms(500);
}
}
Oldukça basit değil mi? Belki biraz daha göze hoş gelen bir şeyler yapabiliriz. İşlemcimizin nasıl çalıştığı görmek için sabırsızlanıyoruz ancak yapacak son bir işimiz kaldı; FPGA ortamımızı hazırlamalıyız.
FPGA Üzerinde Çalıştırma
Matrak işlemcimizi test etmek için Nexys A7 FPGA kartını seçtik. Bu kartın merkezinde Artix 7 (XC7A100T-1CSG324C) FPGA yongası yer alıyor. Kartta bulunan bazı bileşenler ise şöyle:
Nexys A7 FPGA
- 3-eksen ivmeölçer
- PDM mikrofon
- PWM ses çıkışı
- Sıcaklık sensörü
- 2 adet dört dijit yedi segment ekran
- USB HID
- 16 anahtar
- 16 LED
- 2 RGB LED
- 12-bit VGA çıkışı
- 100 Mhz kristal osilatör
Kartın üzerinde çok fazla bileşen var. Biz şimdilik sadece LED’lerle ilgileneceğiz.
Vivado üzerinde yeni proje oluşturarak başlıyoruz. Açılan pencereden projemize bir isim verip ilerliyoruz. Bir sonraki pencede projenin kaynak dosyaları isteniyor. Burada işlemcimizin kaynak dosyalarını yüklüyoruz. Bu dosyalar; matrak.v, memory.v, program.mem, gpio.v, uart.v, clock_counter.v, top.v, top_tb.v ve wrapper.v
📌 Matrak işlemcimiz, Artix 7 FPGA üzerinde 100 Mhz frekansında çalışamıyor. Bu yüzden ilerleyen kısımda “Clocking Wizard” IP ile saat frekansını 50 Mhz değerine düşüreceğiz. Bunun için wrapper.v adı altında top.v modülünün üzerinde yer alan yeni bir üst modül hazırladık.
matrak.v: kodu göstermek için tıklayın
// Matrak M10 RV32I RISC-V Processor
// Gülpare II Architechture 2023
// Processor Module
module matrak (
input clk_i,
input rst_i,
input stall_i, // İşlemci durdurma sinyali
input [31:0] inst_i, // Buyruk girişi
input [31:0] data_i, // Veri girişi
output wen_o, // Yazma yetkilendirme
output ren_o, // Okuma yetkilendirme
output [3:0] stb_o, // Bayt seçim sinyali
output [31:0] inst_addr_o, // Buyruk adresi
output [31:0] data_addr_o, // Veri adresi
output [31:0] data_o // Belleğe yazılacak veri
);
// Getirme birimi bağlantıları
wire c2f_pc_sel;
wire [31:0] ac2f_pc_ext;
wire [31:0] f2fd_pc_plus;
fetch f1 (
.clk_i(clk_i),
.rst_i(rst_i),
.stall_i(stall_i),
.pc_sel_i(c2f_pc_sel),
.pc_ext_i(ac2f_pc_ext),
.pc_o(inst_addr_o),
.pc_plus_o(f2fd_pc_plus)
);
// Boru hattı kaydedicisi bağlantıları
wire [31:0] fd2d_inst;
wire [31:0] fd2ac_pc;
wire [31:0] fd2w_pc_plus;
wire c2fd_clear;
// Boru hattı temizleme sinyali
wire clear = c2fd_clear | stall_i;
fd_regs fd1 (
.clk_i(clk_i),
.rst_i(rst_i),
.clear_i(clear),
.inst_f_i(inst_i),
.pc_f_i(inst_addr_o),
.pc_plus_f_i(f2fd_pc_plus),
.inst_d_o(fd2d_inst),
.pc_d_o(fd2ac_pc),
.pc_plus_d_o(fd2w_pc_plus)
);
// Çözme modülü bağlantıları
wire c2d_regfile_wen;
wire [2:0] c2d_imm_ext_sel;
wire [31:0] w2d_result;
wire [31:0] d2a_reg_a;
wire [31:0] d2a_reg_b;
wire [31:0] d2a_imm_ext;
decode d1 (
.clk_i(clk_i),
.regfile_wen_i(c2d_regfile_wen),
.imm_ext_sel_i(c2d_imm_ext_sel),
.inst_i(fd2d_inst),
.result_i(w2d_result),
.reg_a_o(d2a_reg_a),
.reg_b_o(d2a_reg_b),
.imm_ext_o(d2a_imm_ext)
);
// ALU bağlantıları
wire c2a_alu_sel;
wire [3:0] c2a_alu_fun;
wire [31:0] a2w_alu_out;
wire a2c_alu_zero;
alu a1 (
.alu_sel_i(c2a_alu_sel),
.alu_fun_i(c2a_alu_fun),
.reg_a_i(d2a_reg_a),
.reg_b_i(d2a_reg_b),
.imm_ext_i(d2a_imm_ext),
.alu_zero_o(a2c_alu_zero),
.alu_out_o(a2w_alu_out)
);
// Adres hesaplayıcı bağlantıları
wire c2ac_ac_sel;
address_calculator ac1(
.ac_sel_i(c2ac_ac_sel),
.pc_i(fd2ac_pc),
.imm_ext_i(d2a_imm_ext),
.reg_a_i(d2a_reg_a),
.pc_ext_o(ac2f_pc_ext)
);
// Geriyazma birimi bağlantıları
wire [2:0] c2w_result_sel;
wire [31:0] ls2w_rdata;
writeback w1 (
.result_sel_i(c2w_result_sel),
.alu_out_i(a2w_alu_out),
.pc_plus_i(fd2w_pc_plus),
.imm_ext_i(d2a_imm_ext),
.pc_ext_i(ac2f_pc_ext),
.ls_rdata_i(ls2w_rdata),
.result_o(w2d_result)
);
// Yükleme depolama birimi bağlantıları
wire c2ls_wen;
wire c2ls_ren;
wire [2:0] c2ls_fmt;
load_store ls1 (
.ls_wen_i(c2ls_wen),
.ls_ren_i(c2ls_ren),
.ls_fmt_i(c2ls_fmt),
.ls_addr_i(a2w_alu_out),
.ls_wdata_i(d2a_reg_b),
.ls_rdata_i(data_i),
.ls_wen_o(wen_o),
.ls_ren_o(ren_o),
.ls_stb_o(stb_o),
.ls_addr_o(data_addr_o),
.ls_wdata_o(data_o),
.ls_rdata_o(ls2w_rdata)
);
controller c1 (
.inst_i(fd2d_inst),
.alu_zero_i(a2c_alu_zero),
.regfile_wen_o(c2d_regfile_wen),
.imm_ext_sel_o(c2d_imm_ext_sel),
.alu_sel_o(c2a_alu_sel),
.alu_fun_o(c2a_alu_fun),
.pc_sel_o(c2f_pc_sel),
.ac_sel_o(c2ac_ac_sel),
.result_sel_o(c2w_result_sel),
.ls_wen_o(c2ls_wen),
.ls_ren_o(c2ls_ren),
.ls_fmt_o(c2ls_fmt),
.clear_o(c2fd_clear)
);
endmodule
module fetch (
input clk_i,
input rst_i,
input stall_i, // Program sayacı durdurma girişi
input pc_sel_i, // Program sayacı seçim girişi
input [31:0] pc_ext_i, // Dallanma adres girişi
output reg [31:0] pc_o, // Program sayacı çıkışı
output [31:0] pc_plus_o // Program sayacı + 4 çıkışı
);
// Program sayacına 4 ekle
assign pc_plus_o = pc_o + 4;
// Dallanma adresi veya PC + 4
wire [31:0] pc_next = pc_sel_i ? pc_ext_i : pc_plus_o;
always @(posedge clk_i, posedge rst_i) begin
if (rst_i) begin
pc_o <= 32'h0000_0000;
end else begin
if (!stall_i) begin // Durdurma isteği yoksa PC'yi güncelle
pc_o <= pc_next;
end
end
end
endmodule
module fd_regs (
input clk_i,
input rst_i,
input clear_i, // Sıfırlama sinyali (boru hattı boşaltma)
input [31:0] inst_f_i, // Buyruk girişi (bellekten geliyor)
input [31:0] pc_f_i, // Progam sayacı girişi (getirme biriminden geliyor)
input [31:0] pc_plus_f_i, // Program sayacı + 4 girişi (getirme biriminden geliyor)
output reg [31:0] inst_d_o, // Buyruk çıkışı (yürütme aşamasına gidiyor)
output reg [31:0] pc_d_o, // Program sayacı çıkışı (yürütme aşamasına gidiyor)
output reg [31:0] pc_plus_d_o // Program sayacı + 4 çıkışı (yürütme aşamasına gidiyor)
);
always @(posedge clk_i, posedge rst_i) begin
if (rst_i) begin
inst_d_o <= 32'b0;
pc_d_o <= 32'b0;
pc_plus_d_o <= 32'b0;
end else begin
if (clear_i) begin // Boru hattı boşaltılıyor.
inst_d_o <= 32'b0;
pc_d_o <= 32'b0;
pc_plus_d_o <= 32'b0;
end else begin
inst_d_o <= inst_f_i;
pc_d_o <= pc_f_i;
pc_plus_d_o <= pc_plus_f_i;
end
end
end
endmodule
module decode (
input clk_i,
input regfile_wen_i, // Kaydedici dosyası yazma yetkilendirme
input [2:0] imm_ext_sel_i, // İvedi genişletici format seçimi
input [31:0] inst_i, // Boru hattı kaydedicisinden gelen buyruk
input [31:0] result_i, // Hedef kaydedicisine (rd) yazılacak değer
output [31:0] reg_a_o, // Birinci kaynak kaydedicisinin (rs1) değeri
output [31:0] reg_b_o, // İkinci kaynak kaydedicisinin (rs2) değeri
output reg [31:0] imm_ext_o // İvedi genişleticinin çıkışı
);
// 32 bit genişlikte 32 adet kaydedicili kaydedici dosyası
reg [31:0] regfile [31:0];
// Kaydedici adreslerini buyruktan ayıkla
wire [4:0] reg_a_addr = inst_i[19:15]; // rs1 adres
wire [4:0] reg_b_addr = inst_i[24:20]; // rs2 adres
wire [4:0] target_reg_addr = inst_i[11:7]; // rd adres
// Kaydedici dosyasından oku
assign reg_a_o = (reg_a_addr == 5'b0) ? 32'b0 : regfile[reg_a_addr]; // rs1 değeri
assign reg_b_o = (reg_b_addr == 5'b0) ? 32'b0 : regfile[reg_b_addr]; // rs2 değeri
// Kaydedici dosyasına yaz
always @(posedge clk_i) begin
if (regfile_wen_i) begin
regfile[target_reg_addr] <= result_i;
end
end
// İvedi genişletici
always @(*) begin
case (imm_ext_sel_i)
3'b000 : imm_ext_o = {{20{inst_i[31]}}, inst_i[31:20]}; // I-type
3'b001 : imm_ext_o = {{20{inst_i[31]}}, inst_i[7], inst_i[30:25], inst_i[11:8], 1'b0}; // B-type
3'b010 : imm_ext_o = {{12{inst_i[31]}}, inst_i[19:12], inst_i[20], inst_i[30:21], 1'b0}; // J-type
3'b011 : imm_ext_o = {inst_i[31:12], 12'b0}; // U-type
3'b100 : imm_ext_o = {{20{inst_i[31]}}, inst_i[31:25], inst_i[11:7]}; // S-type
default : imm_ext_o = 32'b0;
endcase
end
endmodule
module alu (
input alu_sel_i, // İkinci işlenenin seçim sinyali (rs2 veya imm)
input [3:0] alu_fun_i, // İşlem seçim sinyali
input [31:0] reg_a_i, // rs1 değeri
input [31:0] reg_b_i, // rs2 değeri
input [31:0] imm_ext_i, // imm değeri
output alu_zero_o, // Sonuç sıfır sinyali
output reg [31:0] alu_out_o // Sonuç değeri
);
// Birinci işlenen iki buyruk formatında da sabit.
wire signed [31:0] alu_a = reg_a_i;
// İkinci işlenen seçim sinyaline göre belirleniyor.
wire signed [31:0] alu_b = alu_sel_i ? imm_ext_i : reg_b_i;
// Sonuç 0'a eşit ise alu_zero_o sinyali 1 olur.
assign alu_zero_o = ~(|alu_out_o);
always @(*) begin
case (alu_fun_i)
4'b0000 : alu_out_o = alu_a + alu_b; // Toplama
4'b0001 : alu_out_o = alu_a - alu_b; // Çıkarma
4'b0010 : alu_out_o = alu_a & alu_b; // VE
4'b0011 : alu_out_o = alu_a ^ alu_b; // XOR
4'b0100 : alu_out_o = alu_a | alu_b; // VEYA
4'b0101 : alu_out_o = alu_a << alu_b[4:0]; // Sola kaydırma
4'b0110 : alu_out_o = alu_a >> alu_b[4:0]; // Sağa kaydırma
4'b0111 : alu_out_o = alu_a >>> alu_b[4:0]; // Aritmetik sağa kaydırma
4'b1000 : alu_out_o = {31'b0, alu_a == alu_b}; // Eşitse alu_out_o = 1, değilse alu_out_o = 0 (beq, bne)
4'b1001 : alu_out_o = {31'b0, alu_a < alu_b}; // Küçükse alu_out_o = 1, değilse alu_out_o = 0 (blt, bge, slt, slti)
4'b1010 : alu_out_o = {31'b0, $unsigned(alu_a) < $unsigned(alu_b)}; // (İşaretsiz) küçükse alu_out_o = 1, değilse alu_out_o = 0 (bltu, bgeu, sltu, sltiu)
default : alu_out_o = 32'bx; // Geçersiz alu_fun_i sinyali
endcase
end
endmodule
module address_calculator (
input ac_sel_i, // Kontrol biriminden gelen kaynak seçim sinyali
input [31:0] pc_i, // Boru hattı kaydedicisinden gelen program sayacının değeri
input [31:0] imm_ext_i, // Çözme biriminden gelen ivedi değer
input [31:0] reg_a_i, // Çözme biriminden gelen rs1 değeri
output [31:0] pc_ext_o // Program sayacına yazılacak adres
);
wire [31:0] operand = ac_sel_i ? reg_a_i : pc_i;
assign pc_ext_o = operand + imm_ext_i;
endmodule
module writeback (
input [2:0] result_sel_i, // Kontrol biriminden gelen seçim sinyali
input [31:0] alu_out_i, // ALU sonucu
input [31:0] pc_plus_i, // Program sayacı + 4
input [31:0] imm_ext_i, // İvedi değer
input [31:0] pc_ext_i, // Adres hesaplayıcıdan gelen adres
input [31:0] ls_rdata_i, // Bellekten okunan değer
output reg [31:0] result_o // Kaydedici dosyasına yazılacak değer
);
always @(*) begin
case (result_sel_i)
3'b000 : result_o = alu_out_i;
3'b001 : result_o = pc_plus_i;
3'b010 : result_o = imm_ext_i;
3'b011 : result_o = pc_ext_i;
3'b100 : result_o = ls_rdata_i;
default : result_o = 32'bx;
endcase
end
endmodule
module load_store (
input ls_wen_i, // Yazma yetkilendirme, kontrol biriminden geliyor.
input ls_ren_i, // Okuma yetkilendirme, kontrol biriminden geliyor.
input [2:0] ls_fmt_i, // funct3 değeri, kontrol biriminden geliyor.
input [31:0] ls_addr_i, // Adres girişi, ALU'dan geliyor.
input [31:0] ls_wdata_i, // rs2 değeri, çözme biriminden geliyor.
input [31:0] ls_rdata_i, // Bellekten okunan değer.
output ls_wen_o, // Yazma yetkilendirme, belleğe gidiyor.
output ls_ren_o, // Okuma yetkilendirme, belleğe gidiyor.
output reg [3:0] ls_stb_o, // Bayt seçim sinyali, belleğe gidiyor.
output [31:0] ls_addr_o, // Adres çıkışı, belleğe gidiyor.
output [31:0] ls_wdata_o, // Yazılacak veri, belleğe gidiyor.
output reg [31:0] ls_rdata_o // Okunan veri, geriyazma birimine gidiyor.
);
// Bu sinyaller doğrudan belleğe gidiyor.
assign ls_addr_o = ls_addr_i;
assign ls_wen_o = ls_wen_i;
assign ls_ren_o = ls_ren_i;
// Kaydırılacak değer hesaplanıyor.
wire [4:0] shift_value = ls_addr_i[1:0] << 5'd3;
// Belleğe yazılacak veri hizalanıyor.
assign ls_wdata_o = ls_wdata_i << shift_value;
// Bellekten okunan veri hizalanıyor.
wire [31:0] aligned_data = ls_rdata_i >> shift_value;
// Bellekten okunan hizalanmış veri genişletiliyor.
always @(*) begin
case (ls_fmt_i[1:0])
2'b00 : ls_rdata_o = {{24{~ls_fmt_i[2] & aligned_data[7]}}, aligned_data[7:0]}; // lb, lbu
2'b01 : ls_rdata_o = {{16{~ls_fmt_i[2] & aligned_data[15]}}, aligned_data[15:0]}; // lh, lhu
2'b10 : ls_rdata_o = aligned_data[31:0]; // lw
default : ls_rdata_o = 32'bx;
endcase
end
// Bayt seçim sinyali ayarlanıyor.
always @(*) begin
case (ls_fmt_i[1:0])
2'b00 : ls_stb_o = 4'b0001 << ls_addr_i[1:0]; // sb
2'b01 : ls_stb_o = 4'b0011 << ls_addr_i[1:0]; // sh
2'b10 : ls_stb_o = 4'b1111 << ls_addr_i[1:0]; // sw
default : ls_stb_o = 4'b0;
endcase
end
endmodule
module controller (
input [31:0] inst_i, // Boru hattı kaydedicisinden gelen buyruk
input alu_zero_i, // ALU'dan gelen sonuç sıfır sinyali
output regfile_wen_o, // Kaydedici dosyası yazma yetkilendirme sinyali
output [2:0] imm_ext_sel_o, // İvedi genişletici format seçim sinyali
output alu_sel_o, // ALU ikinci işlenen seçim sinyali
output reg [3:0] alu_fun_o, // ALU işlem seçim sinyali
output pc_sel_o, // Program sayacı adres seçim sinyali
output ac_sel_o, // Adres hesaplayıcı kaynak seçim sinyali
output [2:0] result_sel_o, // Geriyazma kaynak seçim sinyali
output ls_wen_o, // Bellek yazma yetkilendirme sinyali
output ls_ren_o, // Bellek okuma yetkilendirme sinyali
output [2:0] ls_fmt_o, // Yükleme depolama birimi için funct3 sinyali
output clear_o // Boru hattı boşaltma sinyali
);
// Buyruğun gerekli bölümleri ayıklanıyor.
wire [6:0] opcode = inst_i[6:0];
wire [2:0] funct3 = inst_i[14:12];
wire [6:0] funct7 = inst_i[31:25];
assign ls_fmt_o = funct3;
wire [1:0] alu_dec;
wire branch_op;
wire jump_op;
reg [14:0] control_signals;
assign {regfile_wen_o, imm_ext_sel_o, alu_sel_o, alu_dec, branch_op, jump_op, ac_sel_o, result_sel_o, ls_wen_o, ls_ren_o} = control_signals;
always @(*) begin
case (opcode)
7'b0110011 : control_signals = 15'b1_xxx_0_11_0_0_0_000_0_0; // R-type buyruk
7'b0010011 : control_signals = 15'b1_000_1_11_0_0_0_000_0_0; // I-type buyruk
7'b1100011 : control_signals = 15'b0_001_0_01_1_0_0_000_0_0; // B-type buyruk
7'b1101111 : control_signals = 15'b1_010_0_00_0_1_0_001_0_0; // jal
7'b1100111 : control_signals = 15'b1_000_0_00_0_1_1_001_0_0; // jalr
7'b0110111 : control_signals = 15'b1_011_0_00_0_0_0_010_0_0; // lui
7'b0010111 : control_signals = 15'b1_011_0_00_0_0_0_011_0_0; // auipc
7'b0000011 : control_signals = 15'b1_000_1_10_0_0_0_100_0_1; // load buyrukları
7'b0100011 : control_signals = 15'b0_100_1_10_0_0_0_000_1_0; // store buyrukları
7'b0000000 : control_signals = 15'b0_000_0_00_0_0_0_000_0_0; // Sıfırlama durumu
default : control_signals = 15'bx_xxx_x_xx_x_x_x_xxx_x_x; // Geçersiz buyruk
endcase
end
// Buyruk R-type ise ve funct7 değeri 0x20 ise çıkarma işlemi anlamına gelir.
wire sub = opcode[5] & funct7[5];
// ALU'da yapılacak işlem belirleniyor.
always @(*) begin
case (alu_dec)
2'b01 : // B-type
case (funct3)
3'b000 : alu_fun_o = 4'b1000; // beq
3'b001 : alu_fun_o = 4'b1000; // bne
3'b100 : alu_fun_o = 4'b1001; // blt
3'b101 : alu_fun_o = 4'b1001; // bge
3'b110 : alu_fun_o = 4'b1010; // bltu
3'b111 : alu_fun_o = 4'b1010; // bgeu
default : alu_fun_o = 4'bx;
endcase
2'b11 : // R-type veya I-type
case (funct3)
3'b000 : // add-addi veya sub buyruğu
if (sub) begin
alu_fun_o = 4'b0001; // sub
end else begin
alu_fun_o = 4'b0000; // add, addi
end
3'b001 : alu_fun_o = 4'b0101; // sll, slli
3'b010 : alu_fun_o = 4'b1001; // slt, slti
3'b011 : alu_fun_o = 4'b1010; // sltu, sltiu
3'b100 : alu_fun_o = 4'b0011; // xor, xori
3'b101 : // srl, srli, sra, srai
if (funct7[5]) begin
alu_fun_o = 4'b0111; // sra, srai
end else begin
alu_fun_o = 4'b0110; // srl, srli
end
3'b110 : alu_fun_o = 4'b0100; // or, ori
3'b111 : alu_fun_o = 4'b0010; // and, andi
default : alu_fun_o = 4'b0000;
endcase
default : alu_fun_o = 4'b0000; // Varsayılan işlem toplama
endcase
end
reg branch_valid;
always @(*) begin
case (funct3)
3'b000 : branch_valid = !alu_zero_i; // beq
3'b001 : branch_valid = alu_zero_i; // bne
3'b100 : branch_valid = !alu_zero_i; // blt
3'b101 : branch_valid = alu_zero_i; // bge
3'b110 : branch_valid = !alu_zero_i; // bltu
3'b111 : branch_valid = alu_zero_i; // bgeu
default : branch_valid = 1'b0;
endcase
end
assign pc_sel_o = (branch_op & branch_valid) | jump_op; // Dallanma ve atlama durumu kontrol ediliyor.
assign clear_o = pc_sel_o; // Boru hattını boşalt
endmodule
memory.v: kodu göstermek için tıklayın
// Matrak M10 RV32I RISC-V Processor
// Gülpare II Architechture 2023
// Main Memory Module
module memory (
input clk_i,
input wen_i, // Yazma yetkilendirme girişi
input [3:0] stb_i, // Bayt seçim girişi
input [31:0] addr_i, // Adres girişi
input [31:0] data_i, // Yazılacak veri
input [31:0] data_o // Okunan veri
);
// 512x32 bit = 16384 bit = 2048 bayt = 2 kibibayt (2 KiB)
reg [31:0] mem [511:0];
// Programı belleğe yükle
initial begin
$readmemh("program.mem", mem);
end
// Asenkron okuma
assign data_o = mem[addr_i[31:2]];
// Senkron yazma
always @(posedge clk_i) begin
if (wen_i) begin
if (stb_i[0]) mem[addr_i[31:2]][0+:8] <= data_i[0+:8];
if (stb_i[1]) mem[addr_i[31:2]][8+:8] <= data_i[8+:8];
if (stb_i[2]) mem[addr_i[31:2]][16+:8] <= data_i[16+:8];
if (stb_i[3]) mem[addr_i[31:2]][24+:8] <= data_i[24+:8];
end
end
endmodule
gpio.v: kodu göstermek için tıklayın
// Matrak M10 RV32I RISC-V Processor
// Gülpare II Architechture 2023
// GPIO Output Module
module gpio (
input clk_i,
input rst_i,
input sel_i, // Seçim sinyali
input wen_i, // Yazma yetkilendirme
input [31:0] data_i, // Veri girişi, işlemciden geliyor.
output [31:0] data_o, // Veri çıkışı, işlemciye gidiyor.
output reg [7:0] gpio_o // GPIO çıkış pinleri
);
// Çevrebirim seçilmişse ve yazma etkin değilse (okuma etkin) çıkış kaydedicisinin değerini işlemciye gönder.
assign data_o = (sel_i & !wen_i) ? {24'b0, gpio_o} : 32'b0;
always @(posedge clk_i, posedge rst_i) begin
if (rst_i) begin // Çıkışlar sıfırlanıyor.
gpio_o <= 8'b0;
end else begin
// Çevrebirim seçilmişse ve yazma etkinse işlemciden gelen değeri çıkış kaydedicisine aktar.
if (sel_i & wen_i) begin
gpio_o <= data_i[7:0];
end
end
end
endmodule
uart.v: kodu göstermek için tıklayın
// Matrak M10 RV32I RISC-V Processor
// Gülpare II Architechture 2023
// UART TX Module
module uart (
input clk_i,
input rst_i,
input sel_i, // Seçim sinyali
input wen_i, // Yazma yetkilendirme
input [31:0] addr_i, // Adres girişi, işlemciden geliyor.
input [31:0] data_i, // Veri girişi, işlemciden geliyor.
output [31:0] data_o, // Veri çıkışı, işlemciye gidiyor.
output uart_tx_o // UART TX bağlantısı
);
localparam UART_TRANSMIT_REG = 4'h0;
localparam UART_STATUS_REG = 4'h4;
wire done;
// Kaydedici adresi çözümleniyor.
wire tx_sel = (UART_TRANSMIT_REG == addr_i[3:0]);
wire status_sel = (UART_STATUS_REG == addr_i[3:0]);
// Gönderilecek veri yazılıyor. (gönderimi başlat)
wire tx_en = sel_i & wen_i & tx_sel;
// Durum okunuyor.
wire status_en = sel_i & status_sel;
assign data_o = status_en ? {30'b0, done} : 31'b0;
transmitter t1 (
.clk_i(clk_i),
.rst_i(rst_i),
.tx_data_i(data_i[7:0]),
.tx_en_i(tx_en),
.tx_done_o(done),
.tx_o(uart_tx_o)
);
endmodule
module transmitter (
input clk_i,
input rst_i,
input [7:0] tx_data_i,
input tx_en_i,
output reg tx_done_o,
output reg tx_o
);
localparam IDLE = 2'b00;
localparam START = 2'b01;
localparam TRANSMIT = 2'b10;
localparam DONE = 2'b11;
localparam CLKFREQ = 50_000_000;
localparam BAUD_RATE = 115200;
localparam BAUD_DIV = CLKFREQ/BAUD_RATE;
reg [15:0] t_counter;
reg [2:0] b_counter;
reg [7:0] shr;
reg [1:0] state;
always @(posedge clk_i, posedge rst_i) begin
if (rst_i) begin
state <= IDLE;
t_counter <= 0;
b_counter <= 0;
shr <= 8'b0;
tx_done_o <= 1'b1;
tx_o <= 1'b1;
end else begin
case (state)
IDLE : begin
b_counter <= 0;
tx_done_o <= 1'b1;
tx_o <= 1'b1;
if (tx_en_i) begin
tx_o <= 1'b0;
shr <= tx_data_i;
state <= START;
end else begin
state <= IDLE;
end
end
START : begin
tx_done_o <= 1'b0;
if (t_counter == BAUD_DIV-1) begin
t_counter <= 0;
shr[7] <= shr[0];
shr[6:0] <= shr[7:1];
tx_o <= shr[0];
state <= TRANSMIT;
end else begin
t_counter <= t_counter + 1;
end
end
TRANSMIT : begin
tx_done_o <= 1'b0;
if (b_counter == 7) begin
if (t_counter == BAUD_DIV-1) begin
t_counter <= 0;
b_counter <= 0;
tx_o <= 1'b1;
state <= DONE;
end else begin
t_counter <= t_counter + 1;
end
end else begin
if (t_counter == BAUD_DIV-1) begin
t_counter <= 0;
b_counter <= b_counter + 1;
shr[7] <= shr[0];
shr[6:0] <= shr[7:1];
tx_o <= shr[0];
end else begin
t_counter <= t_counter + 1;
end
end
end
DONE : begin
if (t_counter == BAUD_DIV-1) begin
t_counter <= 0;
tx_done_o <= 1'b1;
state <= IDLE;
end else begin
t_counter <= t_counter + 1;
end
end
default : state <= IDLE;
endcase
end
end
endmodule
clock_counter.v: kodu göstermek için tıklayın
// Matrak M10 RV32I RISC-V Processor
// Gülpare II Architechture 2023
// Clock Counter Register
module clock_counter (
input clk_i,
input rst_i,
input sel_i, // Seçim sinyali
output [31:0] data_o // Veri çıkışı, işlemciye gidiyor.
);
reg [31:0] counter_reg;
assign data_o = sel_i ? counter_reg : 32'b0;
always @(posedge clk_i, posedge rst_i) begin
if (rst_i) begin
counter_reg <= 32'b0;
end else begin
counter_reg <= counter_reg + 1;
end
end
endmodule
top.v: kodu göstermek için tıklayın
// Matrak M10 RV32I RISC-V Processor
// Gülpare II Architechture 2023
// Top Module
module top (
input clk_i,
input rst_i,
output [7:0] gpio_o,
output uart_tx_o
);
// İşlemci bağlantıları
wire stall;
wire wen;
wire ren;
wire [3:0] stb;
wire [31:0] inst_addr;
wire [31:0] data_addr;
wire [31:0] wdata;
wire [31:0] rdata;
matrak mt1 (
.clk_i(clk_i),
.rst_i(rst_i),
.stall_i(stall),
.inst_i(mem_rdata),
.data_i(rdata),
.wen_o(wen),
.ren_o(ren),
.stb_o(stb),
.inst_addr_o(inst_addr),
.data_addr_o(data_addr),
.data_o(wdata)
);
// Bellek bağlantıları
wire [31:0] mem_addr;
wire [31:0] mem_rdata;
wire mem_wen;
memory me1 (
.clk_i(clk_i),
.wen_i(mem_wen),
.stb_i(stb),
.addr_i(mem_addr),
.data_i(wdata),
.data_o(mem_rdata)
);
// GPIO bağlantıları
wire [31:0] gpio_rdata;
wire gpio_request;
gpio g1 (
.clk_i(clk_i),
.rst_i(rst_i),
.sel_i(gpio_request),
.wen_i(wen),
.data_i(wdata),
.data_o(gpio_rdata),
.gpio_o(gpio_o)
);
// UART bağlantıları
wire uart_request;
wire [31:0] uart_rdata;
uart u1 (
.clk_i(clk_i),
.rst_i(rst_i),
.sel_i(uart_request),
.wen_i(wen),
.addr_i(data_addr),
.data_i(wdata),
.data_o(uart_rdata),
.uart_tx_o(uart_tx_o)
);
// Saat sayacı bağlantıları
wire clock_counter_request;
wire [31:0] clock_counter_rdata;
clock_counter cc1(
.clk_i(clk_i),
.rst_i(rst_i),
.sel_i(clock_counter_request),
.data_o(clock_counter_rdata)
);
wire loadstore_request;
wire peripheral_access;
wire memory_ls_access;
wire [31:0] periph_rdata;
// Load Store isteği kontrol ediliyor.
assign loadstore_request = wen | ren;
// Adres: 0x8XXXXXXX, çevrebirimlere yönlendiriliyor.
assign peripheral_access = loadstore_request & data_addr[31];
// Okuma yazma isteği belleğe yönlendiriliyor.
assign memory_ls_access = loadstore_request & !peripheral_access;
// Çevrebirimlerden okunan veri seçiliyor.
assign periph_rdata = gpio_request ? gpio_rdata : (uart_request ? uart_rdata :
(clock_counter_request ? clock_counter_rdata : 32'b0));
// Adrese göre seçilen çevrebirim belirleniyor.
assign gpio_request = peripheral_access & data_addr[12] & !data_addr[13]; // 0x80001000
assign uart_request = peripheral_access & data_addr[13] & !data_addr[12]; // 0x80002000
assign clock_counter_request = peripheral_access & data_addr[12] & data_addr[13]; // 0x80003000
// Belleğe yazma isteği gönderiliyor.
assign mem_wen = wen & memory_ls_access;
// Belleğe aktarılacak adres seçiliyor. (veri adresi veya buyruk adresi)
assign mem_addr = memory_ls_access ? data_addr : inst_addr;
// İşlemciye gönderilecek veri seçiliyor. (bellekten veya çevrebirimlerinden okunan veri)
assign rdata = memory_ls_access ? mem_rdata : periph_rdata;
// Bellek meşgulse işlemciyi durdur.
assign stall = memory_ls_access;
endmodule
top_tb.v: kodu göstermek için tıklayın
// Matrak M10 RV32I RISC-V Processor
// Gülpare II Architechture 2023
// Top Module Testbench
module top_tb ();
reg tb_clk_i;
reg tb_rst_i;
wire [7:0] tb_gpio_o;
wire tb_uart_tx_o;
top t1 (
.clk_i(tb_clk_i),
.rst_i(tb_rst_i),
.gpio_o(tb_gpio_o),
.uart_tx_o(tb_uart_tx_o)
);
initial begin
tb_clk_i = 1'b0;
tb_rst_i = 1'b0;
#1 tb_rst_i = 1'b1;
#1 tb_rst_i = 1'b0;
forever begin
#1 tb_clk_i = ~tb_clk_i;
end
end
endmodule
wrapper.v: kodu göstermek için tıklayın
// Matrak M10 RV32I RISC-V Processor
// Gülpare II Architechture 2023
// Nexys A7 Wrapper Module
module wrapper (
input clk_100_i,
input rst_i,
output [7:0] gpio_o,
output uart_tx_o
);
wire clk_50;
clk_50mhz clk1 (
.clk_out1(clk_50),
.clk_in1(clk_100_i)
);
top t1 (
.clk_i(clk_50),
.rst_i(!rst_i),
.gpio_o(gpio_o),
.uart_tx_o(uart_tx_o)
);
endmodule
Ardından bizden bir “constraint” dosyası eklememiz isteniyor. Bu dosya üst modülümüz ile FPGA’nın fiziksel pinleri arasındaki bağlantıları belirtecek. 100 Mhz kristal osilatör girişini wrapper modülümüzün clk_100_i
girişine, reset butonunu wrapper modülümüzün rst_i
girişine bağlıyoruz. FPGA kartının ilk 8 LED’ini gpio_o
çıkışına bağlıyoruz. İşlemcimizin uart_tx_o
çıkışını FPGA kartının FTDI FT2232 USB-UART köprü yongasının RXD girişine bağlıyoruz.
📌 Nexys A7 kartı USB üzerinden programlanabildiği gibi aynı USB üzerinden seri haberleşmeyi de destekliyor. Eğer bu mümkün olmasaydı UART çıkışımızı, FPGA kartının dışarıdan erişebileceğimiz pinlerine yönlendirip bu pinlerden USB-TTL dönüştürücü bağlamamız gerekirdi.
Nexys-A7-100T-Master.xdc: kodu göstermek için tıklayın
## This file is a general .xdc for the Nexys A7-100T
## To use it in a project:
## - uncomment the lines corresponding to used pins
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project
## Clock signal
set_property -dict { PACKAGE_PIN E3 IOSTANDARD LVCMOS33 } [get_ports { clk_100_i }]; #IO_L12P_T1_MRCC_35 Sch=clk100mhz
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports {clk_100_i}];
##Switches
#set_property -dict { PACKAGE_PIN J15 IOSTANDARD LVCMOS33 } [get_ports { SW[0] }]; #IO_L24N_T3_RS0_15 Sch=sw[0]
#set_property -dict { PACKAGE_PIN L16 IOSTANDARD LVCMOS33 } [get_ports { SW[1] }]; #IO_L3N_T0_DQS_EMCCLK_14 Sch=sw[1]
#set_property -dict { PACKAGE_PIN M13 IOSTANDARD LVCMOS33 } [get_ports { SW[2] }]; #IO_L6N_T0_D08_VREF_14 Sch=sw[2]
#set_property -dict { PACKAGE_PIN R15 IOSTANDARD LVCMOS33 } [get_ports { SW[3] }]; #IO_L13N_T2_MRCC_14 Sch=sw[3]
#set_property -dict { PACKAGE_PIN R17 IOSTANDARD LVCMOS33 } [get_ports { SW[4] }]; #IO_L12N_T1_MRCC_14 Sch=sw[4]
#set_property -dict { PACKAGE_PIN T18 IOSTANDARD LVCMOS33 } [get_ports { SW[5] }]; #IO_L7N_T1_D10_14 Sch=sw[5]
#set_property -dict { PACKAGE_PIN U18 IOSTANDARD LVCMOS33 } [get_ports { SW[6] }]; #IO_L17N_T2_A13_D29_14 Sch=sw[6]
#set_property -dict { PACKAGE_PIN R13 IOSTANDARD LVCMOS33 } [get_ports { SW[7] }]; #IO_L5N_T0_D07_14 Sch=sw[7]
#set_property -dict { PACKAGE_PIN T8 IOSTANDARD LVCMOS18 } [get_ports { SW[8] }]; #IO_L24N_T3_34 Sch=sw[8]
#set_property -dict { PACKAGE_PIN U8 IOSTANDARD LVCMOS18 } [get_ports { SW[9] }]; #IO_25_34 Sch=sw[9]
#set_property -dict { PACKAGE_PIN R16 IOSTANDARD LVCMOS33 } [get_ports { SW[10] }]; #IO_L15P_T2_DQS_RDWR_B_14 Sch=sw[10]
#set_property -dict { PACKAGE_PIN T13 IOSTANDARD LVCMOS33 } [get_ports { SW[11] }]; #IO_L23P_T3_A03_D19_14 Sch=sw[11]
#set_property -dict { PACKAGE_PIN H6 IOSTANDARD LVCMOS33 } [get_ports { SW[12] }]; #IO_L24P_T3_35 Sch=sw[12]
#set_property -dict { PACKAGE_PIN U12 IOSTANDARD LVCMOS33 } [get_ports { SW[13] }]; #IO_L20P_T3_A08_D24_14 Sch=sw[13]
#set_property -dict { PACKAGE_PIN U11 IOSTANDARD LVCMOS33 } [get_ports { SW[14] }]; #IO_L19N_T3_A09_D25_VREF_14 Sch=sw[14]
#set_property -dict { PACKAGE_PIN V10 IOSTANDARD LVCMOS33 } [get_ports { SW[15] }]; #IO_L21P_T3_DQS_14 Sch=sw[15]
## LEDs
set_property -dict { PACKAGE_PIN H17 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[0] }]; #IO_L18P_T2_A24_15 Sch=led[0]
set_property -dict { PACKAGE_PIN K15 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[1] }]; #IO_L24P_T3_RS1_15 Sch=led[1]
set_property -dict { PACKAGE_PIN J13 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[2] }]; #IO_L17N_T2_A25_15 Sch=led[2]
set_property -dict { PACKAGE_PIN N14 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[3] }]; #IO_L8P_T1_D11_14 Sch=led[3]
set_property -dict { PACKAGE_PIN R18 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[4] }]; #IO_L7P_T1_D09_14 Sch=led[4]
set_property -dict { PACKAGE_PIN V17 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[5] }]; #IO_L18N_T2_A11_D27_14 Sch=led[5]
set_property -dict { PACKAGE_PIN U17 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[6] }]; #IO_L17P_T2_A14_D30_14 Sch=led[6]
set_property -dict { PACKAGE_PIN U16 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[7] }]; #IO_L18P_T2_A12_D28_14 Sch=led[7]
#set_property -dict { PACKAGE_PIN V16 IOSTANDARD LVCMOS33 } [get_ports { LED[8] }]; #IO_L16N_T2_A15_D31_14 Sch=led[8]
#set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS33 } [get_ports { LED[9] }]; #IO_L14N_T2_SRCC_14 Sch=led[9]
#set_property -dict { PACKAGE_PIN U14 IOSTANDARD LVCMOS33 } [get_ports { LED[10] }]; #IO_L22P_T3_A05_D21_14 Sch=led[10]
#set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 } [get_ports { LED[11] }]; #IO_L15N_T2_DQS_DOUT_CSO_B_14 Sch=led[11]
#set_property -dict { PACKAGE_PIN V15 IOSTANDARD LVCMOS33 } [get_ports { LED[12] }]; #IO_L16P_T2_CSI_B_14 Sch=led[12]
#set_property -dict { PACKAGE_PIN V14 IOSTANDARD LVCMOS33 } [get_ports { LED[13] }]; #IO_L22N_T3_A04_D20_14 Sch=led[13]
#set_property -dict { PACKAGE_PIN V12 IOSTANDARD LVCMOS33 } [get_ports { LED[14] }]; #IO_L20N_T3_A07_D23_14 Sch=led[14]
#set_property -dict { PACKAGE_PIN V11 IOSTANDARD LVCMOS33 } [get_ports { LED[15] }]; #IO_L21N_T3_DQS_A06_D22_14 Sch=led[15]
## RGB LEDs
#set_property -dict { PACKAGE_PIN R12 IOSTANDARD LVCMOS33 } [get_ports { LED16_B }]; #IO_L5P_T0_D06_14 Sch=led16_b
#set_property -dict { PACKAGE_PIN M16 IOSTANDARD LVCMOS33 } [get_ports { LED16_G }]; #IO_L10P_T1_D14_14 Sch=led16_g
#set_property -dict { PACKAGE_PIN N15 IOSTANDARD LVCMOS33 } [get_ports { LED16_R }]; #IO_L11P_T1_SRCC_14 Sch=led16_r
#set_property -dict { PACKAGE_PIN G14 IOSTANDARD LVCMOS33 } [get_ports { LED17_B }]; #IO_L15N_T2_DQS_ADV_B_15 Sch=led17_b
#set_property -dict { PACKAGE_PIN R11 IOSTANDARD LVCMOS33 } [get_ports { LED17_G }]; #IO_0_14 Sch=led17_g
#set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 } [get_ports { LED17_R }]; #IO_L11N_T1_SRCC_14 Sch=led17_r
##7 segment display
#set_property -dict { PACKAGE_PIN T10 IOSTANDARD LVCMOS33 } [get_ports { CA }]; #IO_L24N_T3_A00_D16_14 Sch=ca
#set_property -dict { PACKAGE_PIN R10 IOSTANDARD LVCMOS33 } [get_ports { CB }]; #IO_25_14 Sch=cb
#set_property -dict { PACKAGE_PIN K16 IOSTANDARD LVCMOS33 } [get_ports { CC }]; #IO_25_15 Sch=cc
#set_property -dict { PACKAGE_PIN K13 IOSTANDARD LVCMOS33 } [get_ports { CD }]; #IO_L17P_T2_A26_15 Sch=cd
#set_property -dict { PACKAGE_PIN P15 IOSTANDARD LVCMOS33 } [get_ports { CE }]; #IO_L13P_T2_MRCC_14 Sch=ce
#set_property -dict { PACKAGE_PIN T11 IOSTANDARD LVCMOS33 } [get_ports { CF }]; #IO_L19P_T3_A10_D26_14 Sch=cf
#set_property -dict { PACKAGE_PIN L18 IOSTANDARD LVCMOS33 } [get_ports { CG }]; #IO_L4P_T0_D04_14 Sch=cg
#set_property -dict { PACKAGE_PIN H15 IOSTANDARD LVCMOS33 } [get_ports { DP }]; #IO_L19N_T3_A21_VREF_15 Sch=dp
#set_property -dict { PACKAGE_PIN J17 IOSTANDARD LVCMOS33 } [get_ports { AN[0] }]; #IO_L23P_T3_FOE_B_15 Sch=an[0]
#set_property -dict { PACKAGE_PIN J18 IOSTANDARD LVCMOS33 } [get_ports { AN[1] }]; #IO_L23N_T3_FWE_B_15 Sch=an[1]
#set_property -dict { PACKAGE_PIN T9 IOSTANDARD LVCMOS33 } [get_ports { AN[2] }]; #IO_L24P_T3_A01_D17_14 Sch=an[2]
#set_property -dict { PACKAGE_PIN J14 IOSTANDARD LVCMOS33 } [get_ports { AN[3] }]; #IO_L19P_T3_A22_15 Sch=an[3]
#set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { AN[4] }]; #IO_L8N_T1_D12_14 Sch=an[4]
#set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { AN[5] }]; #IO_L14P_T2_SRCC_14 Sch=an[5]
#set_property -dict { PACKAGE_PIN K2 IOSTANDARD LVCMOS33 } [get_ports { AN[6] }]; #IO_L23P_T3_35 Sch=an[6]
#set_property -dict { PACKAGE_PIN U13 IOSTANDARD LVCMOS33 } [get_ports { AN[7] }]; #IO_L23N_T3_A02_D18_14 Sch=an[7]
##CPU Reset Button
set_property -dict { PACKAGE_PIN C12 IOSTANDARD LVCMOS33 } [get_ports { rst_i }]; #IO_L3P_T0_DQS_AD1P_15 Sch=cpu_resetn
##Buttons
#set_property -dict { PACKAGE_PIN N17 IOSTANDARD LVCMOS33 } [get_ports { BTNC }]; #IO_L9P_T1_DQS_14 Sch=btnc
#set_property -dict { PACKAGE_PIN M18 IOSTANDARD LVCMOS33 } [get_ports { BTNU }]; #IO_L4N_T0_D05_14 Sch=btnu
#set_property -dict { PACKAGE_PIN P17 IOSTANDARD LVCMOS33 } [get_ports { BTNL }]; #IO_L12P_T1_MRCC_14 Sch=btnl
#set_property -dict { PACKAGE_PIN M17 IOSTANDARD LVCMOS33 } [get_ports { BTNR }]; #IO_L10N_T1_D15_14 Sch=btnr
#set_property -dict { PACKAGE_PIN P18 IOSTANDARD LVCMOS33 } [get_ports { BTND }]; #IO_L9N_T1_DQS_D13_14 Sch=btnd
##Pmod Headers
##Pmod Header JA
#set_property -dict { PACKAGE_PIN C17 IOSTANDARD LVCMOS33 } [get_ports { JA[1] }]; #IO_L20N_T3_A19_15 Sch=ja[1]
#set_property -dict { PACKAGE_PIN D18 IOSTANDARD LVCMOS33 } [get_ports { JA[2] }]; #IO_L21N_T3_DQS_A18_15 Sch=ja[2]
#set_property -dict { PACKAGE_PIN E18 IOSTANDARD LVCMOS33 } [get_ports { JA[3] }]; #IO_L21P_T3_DQS_15 Sch=ja[3]
#set_property -dict { PACKAGE_PIN G17 IOSTANDARD LVCMOS33 } [get_ports { JA[4] }]; #IO_L18N_T2_A23_15 Sch=ja[4]
#set_property -dict { PACKAGE_PIN D17 IOSTANDARD LVCMOS33 } [get_ports { JA[7] }]; #IO_L16N_T2_A27_15 Sch=ja[7]
#set_property -dict { PACKAGE_PIN E17 IOSTANDARD LVCMOS33 } [get_ports { JA[8] }]; #IO_L16P_T2_A28_15 Sch=ja[8]
#set_property -dict { PACKAGE_PIN F18 IOSTANDARD LVCMOS33 } [get_ports { JA[9] }]; #IO_L22N_T3_A16_15 Sch=ja[9]
#set_property -dict { PACKAGE_PIN G18 IOSTANDARD LVCMOS33 } [get_ports { JA[10] }]; #IO_L22P_T3_A17_15 Sch=ja[10]
##Pmod Header JB
#set_property -dict { PACKAGE_PIN D14 IOSTANDARD LVCMOS33 } [get_ports { JB[1] }]; #IO_L1P_T0_AD0P_15 Sch=jb[1]
#set_property -dict { PACKAGE_PIN F16 IOSTANDARD LVCMOS33 } [get_ports { JB[2] }]; #IO_L14N_T2_SRCC_15 Sch=jb[2]
#set_property -dict { PACKAGE_PIN G16 IOSTANDARD LVCMOS33 } [get_ports { JB[3] }]; #IO_L13N_T2_MRCC_15 Sch=jb[3]
#set_property -dict { PACKAGE_PIN H14 IOSTANDARD LVCMOS33 } [get_ports { JB[4] }]; #IO_L15P_T2_DQS_15 Sch=jb[4]
#set_property -dict { PACKAGE_PIN E16 IOSTANDARD LVCMOS33 } [get_ports { JB[7] }]; #IO_L11N_T1_SRCC_15 Sch=jb[7]
#set_property -dict { PACKAGE_PIN F13 IOSTANDARD LVCMOS33 } [get_ports { JB[8] }]; #IO_L5P_T0_AD9P_15 Sch=jb[8]
#set_property -dict { PACKAGE_PIN G13 IOSTANDARD LVCMOS33 } [get_ports { JB[9] }]; #IO_0_15 Sch=jb[9]
#set_property -dict { PACKAGE_PIN H16 IOSTANDARD LVCMOS33 } [get_ports { JB[10] }]; #IO_L13P_T2_MRCC_15 Sch=jb[10]
##Pmod Header JC
#set_property -dict { PACKAGE_PIN K1 IOSTANDARD LVCMOS33 } [get_ports { JC[1] }]; #IO_L23N_T3_35 Sch=jc[1]
#set_property -dict { PACKAGE_PIN F6 IOSTANDARD LVCMOS33 } [get_ports { JC[2] }]; #IO_L19N_T3_VREF_35 Sch=jc[2]
#set_property -dict { PACKAGE_PIN J2 IOSTANDARD LVCMOS33 } [get_ports { JC[3] }]; #IO_L22N_T3_35 Sch=jc[3]
#set_property -dict { PACKAGE_PIN G6 IOSTANDARD LVCMOS33 } [get_ports { JC[4] }]; #IO_L19P_T3_35 Sch=jc[4]
#set_property -dict { PACKAGE_PIN E7 IOSTANDARD LVCMOS33 } [get_ports { JC[7] }]; #IO_L6P_T0_35 Sch=jc[7]
#set_property -dict { PACKAGE_PIN J3 IOSTANDARD LVCMOS33 } [get_ports { JC[8] }]; #IO_L22P_T3_35 Sch=jc[8]
#set_property -dict { PACKAGE_PIN J4 IOSTANDARD LVCMOS33 } [get_ports { JC[9] }]; #IO_L21P_T3_DQS_35 Sch=jc[9]
#set_property -dict { PACKAGE_PIN E6 IOSTANDARD LVCMOS33 } [get_ports { JC[10] }]; #IO_L5P_T0_AD13P_35 Sch=jc[10]
##Pmod Header JD
#set_property -dict { PACKAGE_PIN H4 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[0] }]; #IO_L21N_T3_DQS_35 Sch=jd[1]
#set_property -dict { PACKAGE_PIN H1 IOSTANDARD LVCMOS33 } [get_ports { JD[2] }]; #IO_L17P_T2_35 Sch=jd[2]
#set_property -dict { PACKAGE_PIN G1 IOSTANDARD LVCMOS33 } [get_ports { JD[3] }]; #IO_L17N_T2_35 Sch=jd[3]
#set_property -dict { PACKAGE_PIN G3 IOSTANDARD LVCMOS33 } [get_ports { JD[4] }]; #IO_L20N_T3_35 Sch=jd[4]
#set_property -dict { PACKAGE_PIN H2 IOSTANDARD LVCMOS33 } [get_ports { JD[7] }]; #IO_L15P_T2_DQS_35 Sch=jd[7]
#set_property -dict { PACKAGE_PIN G4 IOSTANDARD LVCMOS33 } [get_ports { JD[8] }]; #IO_L20P_T3_35 Sch=jd[8]
#set_property -dict { PACKAGE_PIN G2 IOSTANDARD LVCMOS33 } [get_ports { JD[9] }]; #IO_L15N_T2_DQS_35 Sch=jd[9]
#set_property -dict { PACKAGE_PIN F3 IOSTANDARD LVCMOS33 } [get_ports { JD[10] }]; #IO_L13N_T2_MRCC_35 Sch=jd[10]
##Pmod Header JXADC
#set_property -dict { PACKAGE_PIN A14 IOSTANDARD LVCMOS33 } [get_ports { XA_N[1] }]; #IO_L9N_T1_DQS_AD3N_15 Sch=xa_n[1]
#set_property -dict { PACKAGE_PIN A13 IOSTANDARD LVCMOS33 } [get_ports { XA_P[1] }]; #IO_L9P_T1_DQS_AD3P_15 Sch=xa_p[1]
#set_property -dict { PACKAGE_PIN A16 IOSTANDARD LVCMOS33 } [get_ports { XA_N[2] }]; #IO_L8N_T1_AD10N_15 Sch=xa_n[2]
#set_property -dict { PACKAGE_PIN A15 IOSTANDARD LVCMOS33 } [get_ports { XA_P[2] }]; #IO_L8P_T1_AD10P_15 Sch=xa_p[2]
#set_property -dict { PACKAGE_PIN B17 IOSTANDARD LVCMOS33 } [get_ports { XA_N[3] }]; #IO_L7N_T1_AD2N_15 Sch=xa_n[3]
#set_property -dict { PACKAGE_PIN B16 IOSTANDARD LVCMOS33 } [get_ports { XA_P[3] }]; #IO_L7P_T1_AD2P_15 Sch=xa_p[3]
#set_property -dict { PACKAGE_PIN A18 IOSTANDARD LVCMOS33 } [get_ports { XA_N[4] }]; #IO_L10N_T1_AD11N_15 Sch=xa_n[4]
#set_property -dict { PACKAGE_PIN B18 IOSTANDARD LVCMOS33 } [get_ports { XA_P[4] }]; #IO_L10P_T1_AD11P_15 Sch=xa_p[4]
##VGA Connector
#set_property -dict { PACKAGE_PIN A3 IOSTANDARD LVCMOS33 } [get_ports { VGA_R[0] }]; #IO_L8N_T1_AD14N_35 Sch=vga_r[0]
#set_property -dict { PACKAGE_PIN B4 IOSTANDARD LVCMOS33 } [get_ports { VGA_R[1] }]; #IO_L7N_T1_AD6N_35 Sch=vga_r[1]
#set_property -dict { PACKAGE_PIN C5 IOSTANDARD LVCMOS33 } [get_ports { VGA_R[2] }]; #IO_L1N_T0_AD4N_35 Sch=vga_r[2]
#set_property -dict { PACKAGE_PIN A4 IOSTANDARD LVCMOS33 } [get_ports { VGA_R[3] }]; #IO_L8P_T1_AD14P_35 Sch=vga_r[3]
#set_property -dict { PACKAGE_PIN C6 IOSTANDARD LVCMOS33 } [get_ports { VGA_G[0] }]; #IO_L1P_T0_AD4P_35 Sch=vga_g[0]
#set_property -dict { PACKAGE_PIN A5 IOSTANDARD LVCMOS33 } [get_ports { VGA_G[1] }]; #IO_L3N_T0_DQS_AD5N_35 Sch=vga_g[1]
#set_property -dict { PACKAGE_PIN B6 IOSTANDARD LVCMOS33 } [get_ports { VGA_G[2] }]; #IO_L2N_T0_AD12N_35 Sch=vga_g[2]
#set_property -dict { PACKAGE_PIN A6 IOSTANDARD LVCMOS33 } [get_ports { VGA_G[3] }]; #IO_L3P_T0_DQS_AD5P_35 Sch=vga_g[3]
#set_property -dict { PACKAGE_PIN B7 IOSTANDARD LVCMOS33 } [get_ports { VGA_B[0] }]; #IO_L2P_T0_AD12P_35 Sch=vga_b[0]
#set_property -dict { PACKAGE_PIN C7 IOSTANDARD LVCMOS33 } [get_ports { VGA_B[1] }]; #IO_L4N_T0_35 Sch=vga_b[1]
#set_property -dict { PACKAGE_PIN D7 IOSTANDARD LVCMOS33 } [get_ports { VGA_B[2] }]; #IO_L6N_T0_VREF_35 Sch=vga_b[2]
#set_property -dict { PACKAGE_PIN D8 IOSTANDARD LVCMOS33 } [get_ports { VGA_B[3] }]; #IO_L4P_T0_35 Sch=vga_b[3]
#set_property -dict { PACKAGE_PIN B11 IOSTANDARD LVCMOS33 } [get_ports { VGA_HS }]; #IO_L4P_T0_15 Sch=vga_hs
#set_property -dict { PACKAGE_PIN B12 IOSTANDARD LVCMOS33 } [get_ports { VGA_VS }]; #IO_L3N_T0_DQS_AD1N_15 Sch=vga_vs
##Micro SD Connector
#set_property -dict { PACKAGE_PIN E2 IOSTANDARD LVCMOS33 } [get_ports { SD_RESET }]; #IO_L14P_T2_SRCC_35 Sch=sd_reset
#set_property -dict { PACKAGE_PIN A1 IOSTANDARD LVCMOS33 } [get_ports { SD_CD }]; #IO_L9N_T1_DQS_AD7N_35 Sch=sd_cd
#set_property -dict { PACKAGE_PIN B1 IOSTANDARD LVCMOS33 } [get_ports { SD_SCK }]; #IO_L9P_T1_DQS_AD7P_35 Sch=sd_sck
#set_property -dict { PACKAGE_PIN C1 IOSTANDARD LVCMOS33 } [get_ports { SD_CMD }]; #IO_L16N_T2_35 Sch=sd_cmd
#set_property -dict { PACKAGE_PIN C2 IOSTANDARD LVCMOS33 } [get_ports { SD_DAT[0] }]; #IO_L16P_T2_35 Sch=sd_dat[0]
#set_property -dict { PACKAGE_PIN E1 IOSTANDARD LVCMOS33 } [get_ports { SD_DAT[1] }]; #IO_L18N_T2_35 Sch=sd_dat[1]
#set_property -dict { PACKAGE_PIN F1 IOSTANDARD LVCMOS33 } [get_ports { SD_DAT[2] }]; #IO_L18P_T2_35 Sch=sd_dat[2]
#set_property -dict { PACKAGE_PIN D2 IOSTANDARD LVCMOS33 } [get_ports { SD_DAT[3] }]; #IO_L14N_T2_SRCC_35 Sch=sd_dat[3]
##Accelerometer
#set_property -dict { PACKAGE_PIN E15 IOSTANDARD LVCMOS33 } [get_ports { ACL_MISO }]; #IO_L11P_T1_SRCC_15 Sch=acl_miso
#set_property -dict { PACKAGE_PIN F14 IOSTANDARD LVCMOS33 } [get_ports { ACL_MOSI }]; #IO_L5N_T0_AD9N_15 Sch=acl_mosi
#set_property -dict { PACKAGE_PIN F15 IOSTANDARD LVCMOS33 } [get_ports { ACL_SCLK }]; #IO_L14P_T2_SRCC_15 Sch=acl_sclk
#set_property -dict { PACKAGE_PIN D15 IOSTANDARD LVCMOS33 } [get_ports { ACL_CSN }]; #IO_L12P_T1_MRCC_15 Sch=acl_csn
#set_property -dict { PACKAGE_PIN B13 IOSTANDARD LVCMOS33 } [get_ports { ACL_INT[1] }]; #IO_L2P_T0_AD8P_15 Sch=acl_int[1]
#set_property -dict { PACKAGE_PIN C16 IOSTANDARD LVCMOS33 } [get_ports { ACL_INT[2] }]; #IO_L20P_T3_A20_15 Sch=acl_int[2]
##Temperature Sensor
#set_property -dict { PACKAGE_PIN C14 IOSTANDARD LVCMOS33 } [get_ports { TMP_SCL }]; #IO_L1N_T0_AD0N_15 Sch=tmp_scl
#set_property -dict { PACKAGE_PIN C15 IOSTANDARD LVCMOS33 } [get_ports { TMP_SDA }]; #IO_L12N_T1_MRCC_15 Sch=tmp_sda
#set_property -dict { PACKAGE_PIN D13 IOSTANDARD LVCMOS33 } [get_ports { TMP_INT }]; #IO_L6N_T0_VREF_15 Sch=tmp_int
#set_property -dict { PACKAGE_PIN B14 IOSTANDARD LVCMOS33 } [get_ports { TMP_CT }]; #IO_L2N_T0_AD8N_15 Sch=tmp_ct
##Omnidirectional Microphone
#set_property -dict { PACKAGE_PIN J5 IOSTANDARD LVCMOS33 } [get_ports { M_CLK }]; #IO_25_35 Sch=m_clk
#set_property -dict { PACKAGE_PIN H5 IOSTANDARD LVCMOS33 } [get_ports { M_DATA }]; #IO_L24N_T3_35 Sch=m_data
#set_property -dict { PACKAGE_PIN F5 IOSTANDARD LVCMOS33 } [get_ports { M_LRSEL }]; #IO_0_35 Sch=m_lrsel
##PWM Audio Amplifier
#set_property -dict { PACKAGE_PIN A11 IOSTANDARD LVCMOS33 } [get_ports { AUD_PWM }]; #IO_L4N_T0_15 Sch=aud_pwm
#set_property -dict { PACKAGE_PIN D12 IOSTANDARD LVCMOS33 } [get_ports { AUD_SD }]; #IO_L6P_T0_15 Sch=aud_sd
##USB-RS232 Interface
#set_property -dict { PACKAGE_PIN C4 IOSTANDARD LVCMOS33 } [get_ports { UART_TXD_IN }]; #IO_L7P_T1_AD6P_35 Sch=uart_txd_in
set_property -dict { PACKAGE_PIN D4 IOSTANDARD LVCMOS33 } [get_ports { uart_tx_o }]; #IO_L11N_T1_SRCC_35 Sch=uart_rxd_out
#set_property -dict { PACKAGE_PIN D3 IOSTANDARD LVCMOS33 } [get_ports { UART_CTS }]; #IO_L12N_T1_MRCC_35 Sch=uart_cts
#set_property -dict { PACKAGE_PIN E5 IOSTANDARD LVCMOS33 } [get_ports { UART_RTS }]; #IO_L5N_T0_AD13N_35 Sch=uart_rts
##USB HID (PS/2)
#set_property -dict { PACKAGE_PIN F4 IOSTANDARD LVCMOS33 } [get_ports { PS2_CLK }]; #IO_L13P_T2_MRCC_35 Sch=ps2_clk
#set_property -dict { PACKAGE_PIN B2 IOSTANDARD LVCMOS33 } [get_ports { PS2_DATA }]; #IO_L10N_T1_AD15N_35 Sch=ps2_data
##SMSC Ethernet PHY
#set_property -dict { PACKAGE_PIN C9 IOSTANDARD LVCMOS33 } [get_ports { ETH_MDC }]; #IO_L11P_T1_SRCC_16 Sch=eth_mdc
#set_property -dict { PACKAGE_PIN A9 IOSTANDARD LVCMOS33 } [get_ports { ETH_MDIO }]; #IO_L14N_T2_SRCC_16 Sch=eth_mdio
#set_property -dict { PACKAGE_PIN B3 IOSTANDARD LVCMOS33 } [get_ports { ETH_RSTN }]; #IO_L10P_T1_AD15P_35 Sch=eth_rstn
#set_property -dict { PACKAGE_PIN D9 IOSTANDARD LVCMOS33 } [get_ports { ETH_CRSDV }]; #IO_L6N_T0_VREF_16 Sch=eth_crsdv
#set_property -dict { PACKAGE_PIN C10 IOSTANDARD LVCMOS33 } [get_ports { ETH_RXERR }]; #IO_L13N_T2_MRCC_16 Sch=eth_rxerr
#set_property -dict { PACKAGE_PIN C11 IOSTANDARD LVCMOS33 } [get_ports { ETH_RXD[0] }]; #IO_L13P_T2_MRCC_16 Sch=eth_rxd[0]
#set_property -dict { PACKAGE_PIN D10 IOSTANDARD LVCMOS33 } [get_ports { ETH_RXD[1] }]; #IO_L19N_T3_VREF_16 Sch=eth_rxd[1]
#set_property -dict { PACKAGE_PIN B9 IOSTANDARD LVCMOS33 } [get_ports { ETH_TXEN }]; #IO_L11N_T1_SRCC_16 Sch=eth_txen
#set_property -dict { PACKAGE_PIN A10 IOSTANDARD LVCMOS33 } [get_ports { ETH_TXD[0] }]; #IO_L14P_T2_SRCC_16 Sch=eth_txd[0]
#set_property -dict { PACKAGE_PIN A8 IOSTANDARD LVCMOS33 } [get_ports { ETH_TXD[1] }]; #IO_L12N_T1_MRCC_16 Sch=eth_txd[1]
#set_property -dict { PACKAGE_PIN D5 IOSTANDARD LVCMOS33 } [get_ports { ETH_REFCLK }]; #IO_L11P_T1_SRCC_35 Sch=eth_refclk
#set_property -dict { PACKAGE_PIN B8 IOSTANDARD LVCMOS33 } [get_ports { ETH_INTN }]; #IO_L12P_T1_MRCC_16 Sch=eth_intn
##Quad SPI Flash
#set_property -dict { PACKAGE_PIN K17 IOSTANDARD LVCMOS33 } [get_ports { QSPI_DQ[0] }]; #IO_L1P_T0_D00_MOSI_14 Sch=qspi_dq[0]
#set_property -dict { PACKAGE_PIN K18 IOSTANDARD LVCMOS33 } [get_ports { QSPI_DQ[1] }]; #IO_L1N_T0_D01_DIN_14 Sch=qspi_dq[1]
#set_property -dict { PACKAGE_PIN L14 IOSTANDARD LVCMOS33 } [get_ports { QSPI_DQ[2] }]; #IO_L2P_T0_D02_14 Sch=qspi_dq[2]
#set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { QSPI_DQ[3] }]; #IO_L2N_T0_D03_14 Sch=qspi_dq[3]
#set_property -dict { PACKAGE_PIN L13 IOSTANDARD LVCMOS33 } [get_ports { QSPI_CSN }]; #IO_L6P_T0_FCS_B_14 Sch=qspi_csn
Constaint dosyamızı yükledikten sonra Nexys A7-100T kartımızı seçmeliyiz.
Vivado kart seçimi
Projemizi oluşturduk. Şimdi saat frekansını düşürmek için Clocking Wizard IP eklemeliyiz. Flow Navigator menüsü altında bulunan IP Catalog’a tıklıyoruz. Açılan menüden Clocking Wizard IP’sine çift tıklıyoruz.
IP Catalog menüsü
Açılan pencereden IP’ye clk_50mhz
adını veriyoruz, ardından output sekmesi altında yer alan Requested Output Freq metin kutusuna 50.000 değerini giriyoruz.
Clocking Wizard IP penceresi
Bu işlemin ardından generate butonuna tıklamalıyız.
Generate Output Products penceresi
Şimdi sentez, implementation ve generate bitstream komutlarını vererek matrak işlemcimizi FPGA üzerinde çalıştırmaya hazırız.
Kara Şimşek Uygulaması
LED’leri kullanarak birçok uygulama yapabiliriz. Biz klasik Kara Şimşek projesini tercih edeceğiz. Bunun için aşağıdaki kodu hazırladık.
knight.c:
#include "matrak.h"
#define HIGH 1
#define LOW 0
void main(void) {
while (1) {
for (int i = 0; i < 8; i++) {
gpio_write(i, HIGH);
delay_ms(100);
gpio_write(i, LOW);
}
for (int j = 7; j > -1; j--) {
gpio_write(j, HIGH);
delay_ms(100);
gpio_write(j, LOW);
}
}
}
Makefile dosyamızı düzenledikten sonra make knight.hex
komutunu vererek kodumuzu derliyoruz. knight.hex dosyasının içeriğini program.mem dosyasına aktarıp Vivado üzerinden generate bitstream’e tıklıyoruz. Ardından Hardware Manager penceresinden üretilen bitstream dosyasını FPGA’ya yüklüyoruz.
knight.hex: kodu göstermek için tıklayın
00000093
00000113
00000193
00000213
00000293
00000313
00000393
00000413
00000493
00000513
00000593
00000613
00000693
00000713
00000793
00000813
00000893
00000913
00000993
00000a13
00000a93
00000b13
00000b93
00000c13
00000c93
00000d13
00000d93
00000e13
00000e93
00000f13
00000f93
00001137
80010113
00000513
00000593
1a8000ef
00000013
ffdff06f
fe010113
00812e23
02010413
fea42623
feb42423
fe842783
02078663
800017b7
0007a703
fec42783
00100693
00f697b3
00078693
800017b7
00d76733
00e7a023
02c0006f
800017b7
0007a703
fec42783
00100693
00f697b3
fff7c793
00078693
800017b7
00d77733
00e7a023
00000013
01c12403
02010113
00008067
fd010113
02812623
03010413
fca42e23
800037b7
0007a683
fdc42703
00070793
00179793
00e787b3
00679613
00c787b3
00279793
00e787b3
00279793
00e787b3
00479793
00f687b3
fef42623
00000013
800037b7
0007a783
fec42703
fee7eae3
00000013
00000013
02c12403
03010113
00008067
fd010113
02812623
03010413
fca42e23
fe042623
800027b7
fdc42703
00e7a023
0100006f
800027b7
0047a783
fef42623
fec42783
fe0788e3
00000013
00000013
02c12403
03010113
00008067
fd010113
02112623
02812423
03010413
fca42e23
fdc42783
fef42623
0200006f
fec42783
0007c783
00078513
f89ff0ef
fec42783
00178793
fef42623
fec42783
0007c783
fc079ee3
00000013
00000013
02c12083
02812403
03010113
00008067
fe010113
00112e23
00812c23
02010413
fe042623
0300006f
00100593
fec42503
e45ff0ef
06400513
eb9ff0ef
00000593
fec42503
e31ff0ef
fec42783
00178793
fef42623
fec42703
00700793
fce7d6e3
00700793
fef42423
0300006f
00100593
fe842503
e01ff0ef
06400513
e75ff0ef
00000593
fe842503
dedff0ef
fe842783
fff78793
fef42423
fe842783
fc07d8e3
f81ff06f
Ve sonuç:
Kara Şimşek, basit ve etkileyici. Gerçek bir klasik.
UART Uygulaması
Elimiz değmişken UART çevrebirimimizi de test edelim. Takip edeceğimiz işlemler önceki uygulama ile aynı. UART test kodu aşağıda verilmiştir.
uart.c:
#include "matrak.h"
void main(void) {
put_str("Iskeleden uzaklasan bir gemi\n");
put_str("Hatirlatir bana mazide kalan gunlerimi\n");
put_str("Gordugum su mavi deniz ufkumu aydinlatir\n");
put_str("Ucup giden bir marti yitirdiklerimi\n");
while(1);
}
uart.hex: kodu göstermek için tıklayın
00000093
00000113
00000193
00000213
00000293
00000313
00000393
00000413
00000493
00000513
00000593
00000613
00000693
00000713
00000793
00000813
00000893
00000913
00000993
00000a13
00000a93
00000b13
00000b93
00000c13
00000c93
00000d13
00000d93
00000e13
00000e93
00000f13
00000f93
00001137
80010113
00000513
00000593
1a8000ef
00000013
ffdff06f
fe010113
00812e23
02010413
fea42623
feb42423
fe842783
02078663
800017b7
0007a703
fec42783
00100693
00f697b3
00078693
800017b7
00d76733
00e7a023
02c0006f
800017b7
0007a703
fec42783
00100693
00f697b3
fff7c793
00078693
800017b7
00d77733
00e7a023
00000013
01c12403
02010113
00008067
fd010113
02812623
03010413
fca42e23
800037b7
0007a683
fdc42703
00070793
00179793
00e787b3
00679613
00c787b3
00279793
00e787b3
00279793
00e787b3
00479793
00f687b3
fef42623
00000013
800037b7
0007a783
fec42703
fee7eae3
00000013
00000013
02c12403
03010113
00008067
fd010113
02812623
03010413
fca42e23
fe042623
800027b7
fdc42703
00e7a023
0100006f
800027b7
0047a783
fef42623
fec42783
fe0788e3
00000013
00000013
02c12403
03010113
00008067
fd010113
02112623
02812423
03010413
fca42e23
fdc42783
fef42623
0200006f
fec42783
0007c783
00078513
f89ff0ef
fec42783
00178793
fef42623
fec42783
0007c783
fc079ee3
00000013
00000013
02c12083
02812403
03010113
00008067
ff010113
00112623
00812423
01010413
26800513
f8dff0ef
28800513
f85ff0ef
2b000513
f7dff0ef
2dc00513
f75ff0ef
0000006f
656b7349
6564656c
7a75206e
616c6b61
206e6173
20726962
696d6567
0000000a
69746148
74616c72
62207269
20616e61
697a616d
6b206564
6e616c61
6e756720
6972656c
000a696d
64726f47
6d756775
20757320
6976616d
6e656420
75207a69
6d756b66
79612075
6c6e6964
72697461
0000000a
70756355
64696720
62206e65
6d207269
69747261
74697920
69647269
72656c6b
0a696d69
00000000
UART çalışıyor gibi görünüyor.
Daha yapılacak çok iş var ama şimdilik bitti.
📜 Bu bölümün kodlarına erişmek için tıklayın.