#5: Modüllerde port isimlendirmeleri mümkünse standart olmalı ve mümkün mertebe açıklayıcı olmalıdır
Kapsamlı bir sayısal devre tasarımı yaparken giriş/çıkış kapıları ile ilgili yaşanan temel sorunlar ve zorluklar:

  • Kapı bir giriş (input) portu yoksa giriş/çıkış (inout) portu mu?
  • Kapı bir çıkış (output) portu yoksa giriş/çıkış (inout) portu mu?
  • Bu bir tel (wire) veya yazmaç (register) olmasın?
  • Bu hatta tampon (buffer) uygulamış mıydım?
  • Bu dışarıdan gelen saat (clock) hattı mıydı?

şeklinde olabilmektedir. Örneğin şu tasarım kesitini inceleyelim:

...
always @ (posedge clock_200MHz) begin
	if(!reset) begin
		veri <=  8'h00;
	end
	else begin
		veri <=  sinyal1 | sinyal2;
	end
end
...

Kod oldukça açıklayıcı ve basit. Fakat anlaşılabilirlik o kadar yüksek değil. Bu koda baktığımda beni tereddüte düşüren aşağıdaki noktalar oluyor:

  • sinyal1 veya sinyal2 hattı dışarıdan gelen bir input hattı mı yoksa inout hattı mıdır?
  • Saat sinyali olan clock_200MHz dışarıdan gelen bir saat sinyali mi yoksa tasarım içerisinde ben mi oluşturdum?
  • reset sinyali dışarıdan gelen bir genel (global) ilklendirme sinyali midir?

Bu soruların cevabını alabilmek için tasarımımın kalan kısmına bakıp durumu çözmem gerekiyor. Bunun yerine şu yazım stilini tercih etseydim:

...
always @ (posedge in_clock_200MHz) begin
	if(!in_reset) begin
		veri <=  8'h00;
	end
	else begin
		veri <=  in_sinyal1 | sinyal2;
	end
end
...

Yukarıdaki tasarım kesitini inceleyince ise sorularımın tamamının cevaplandığını görüyorum, ek bir dokümentasyon yada 800 satırlık tasarımımı baştan incelememe gerek yok.

Fakat burada da bir sorun var, in_reset sinyalim sanki ters mantık (negative logic) çalışıyor, öyle olmayadabilir. Şu yazımı tercih etseydim emin olabilirdim:

...
always @ (posedge in_clock_200MHz) begin
	if(!in_reset_n) begin
		veri <=  8'h00;
	end
	else begin
		veri <=  in_sinyal1 | sinyal2;
	end
end
...

Tamamen yazım tarzı mı? Evet. Fonksiyonel bir fark yaratıyor mu? Hayır. Tasarımı yapan kişi için kolaylıklar sağlıyor mu? Evet. O zaman buna benzer bir standart kod içerisinde bulunması ve genel olarak tasarladığınız bütün IP core’larda bulunması tasarım anlaşılabilirliği ve bakımını kolaylaştıracak/ucuzlatacaktır.

Bunun için benim kullandığım yöntem genel olarak:

...
input in_bir_input;
output out_bir_output;
input inout_yuksek_empedans_hatti;
input in_reset_n;
...

şeklindedir. Benzer bir standart tabii ki dilendiği gibi oluşturulabilir.

#6: Register, wire ve parametre isimlendirmeleri mümkünse standart olmalı ve mümkün mertebe açıklayıcı olmalıdır
Bir üstteki öneri aynen register, wire ve parametre tanımlamalarında da geçerlidir. Örneği hafif değiştirerek tekrarlayalım:

...
always @ (posedge in_clock_200MHz) begin
	if(!in_reset_n) begin
		veri <=  8'h00;
	end
	else begin
		if(deger1 == deger2) begin
			veri <=  in_sinyal1 | sinyal2;
		end
		else begin
			veri <= sinyal3;
		end
	end
end
...

Böyle bir tasarım gördüğümde aklımda şu sorular beliriyor:

  • deger1 bir register mı? Wire mı?
  • deger2 bir parameter mı yoksa?
  • sinyal2 bir wire olsa gerek?
  • veri umarım bir registerdır?
  • Ya sinyal3 de nedir?

Bu kod bloğunu çözebilmek için büyük ihtimalle tasarımın birkaç noktasına daha bakmak veya bir çeşit dokümantasyona bakmak gerekiyor. Bunun yerine şöyle bir stil tercih edilebilir:

...
always @ (posedge in_clock_200MHz) begin
	if(!in_reset_n) begin
		veri_r <=  8'h00;
	end
	else begin
		if(deger1_r == deger2_p) begin
			veri_r <=  in_sinyal1 | sinyal2_r;
		end
		else begin
			veri_r <= sinyal3_w;
		end
	end
end
...

Her şey çok daha açık. “veri_r” bir yazmaç, sinyal3_w bir tel, deger2_p ise bir parametre. En azından register/wire/parametre sorularının hepsine sadece stile bakarak cevap vermek mümkün olabiliyor, bu birimlerin fiili olarak ne işe yaradığı ise ayrı bir konu. Özetle:

...
wire bir_tel_w;
reg bir_yazmac_r;
parameter bir_parametre_p;
...

gibi bir standart yazım tarzı anlaşılabilirliği arttırmaktadır.

#7: case/end kullanımı yapılıyorsa bu ayrı bir always bloğunda olmalıdır
Bir always bloğu içerisinde if/else ve case/end kullanımı beraber olmamalıdır, mümkünse bu iki blok parçalanmalıdır.

Mantıken, sayısal tasarım yapıldığından, bu iki ifade şekli ile farklı yapılar ve genelde de 2 farklı sayısal tasarım bloğu ifade edilmek istenmektedir. Bu durumda da bunları iki farklı always bloğuna parçalamak hem anlaşılabilirliği hem de hata ihtimallerini ortadan kaldırmaktadır. Şu örneğe bir göz atalım:

...
always @(posedge in_clock) begin
	case (status_r)
		IDLE :begin  
			clock_count_r <= 0;
		end
		RUN : begin
			clock_count_r <= clock_count_r + 1;
		end 
		default:    
			clock_count_r <= clock_count_r;
	endcase
	if((clock_count_r < 503) && (reg_in_flow_control==1'b1))begin
        cts_r <= 1'b1; 
    end
    else begin
        cts_r <= 1'b0;
    end
end  
...

Bir always bloğunu verimli kullanmak dışında pratikte hiçbir anlamı yoktur. Bunun yerine aşağıdaki yazımın tercih edilmesi (özellikle case bloğunda 2’den fazla bölüm içeren anlamlı büyük bir tasarımda) tasarımın niyetini daha anlaşılabilir kılacaktır:

...
always @(posedge in_clock) begin
	case (status_r)
		IDLE :begin  
			clock_count_r <= 0;
		end
		RUN : begin
			clock_count_r <= clock_count_r + 1;
		end 
		default:    
			clock_count_r <= clock_count_r;
	endcase

end  

always @(posedge in_clock) begin
    if((clock_count_r < 503) && (reg_in_flow_control==1'b1))begin
        cts_r <= 1'b1; 
    end
    else begin
        cts_r <= 1'b0;
    end
end 
...

Çok basit bir ayrım yaparak anlaşılabilirliği oldukça yüksek derecede arttırabildik. İlk always bloğunun bariz bir biçimde 3 farklı durumu mux‘layan bir yapı, ikinci always bloğunun ise basit bir karşılaştırma ışığında register setleme veya resetleme olduğunu görebiliyoruz.

#8: always blokları mümkün mertebe parçalı olmalıdır
Mantık olarak bir önceki örnek ile aynı örnek. İnceleyelim:

...
always @(posedge in_clock) begin
    if((sinyal1_r < 503) && (reg_in_control==1'b1))begin
        cts_r <= 1'b1; 
    end
    else begin
        cts_r <= 1'b0;
    end
    if((sinyal2_r < 503) && (reg_in_control==1'b1))begin
        rts_r <= 1'b1; 
    end
    else begin
        rts_r <= 1'b0;
    end
end 
...

Sanki biraz karmaşık bir kod gibi görünüyor, halbuki çok basit register set/reset işlemi. Şu şekilde yazılınca daha az tehditkar oluyor:

...
always @(posedge in_clock) begin
    if((sinyal1_r < 503) && (reg_in_control==1'b1))begin
        cts_r <= 1'b1; 
    end
    else begin
        cts_r <= 1'b0;
    end
end

always @(posedge in_clock) begin
    if((sinyal2_r < 503) && (reg_in_control==1'b1))begin
        rts_r <= 1'b1; 
    end
    else begin
        rts_r <= 1'b0;
    end
end 
...

Umarım öneriler işinize yarar, iyi tasarımlar.