ALU Design Under Test(DUT)
ALU Tasarım Kodları | Bitirme projesi
Bu bölümde, tüm doğrulama projemizin merkezinde yer alan DUT (Design Under Test / Test Edilen Tasarım) olan 8-bit ALU modülünü inceliyoruz. Testbench mimarisinde DUT, doğruluğunu kanıtlamaya çalıştığımız asıl donanımdır; driver onu uyarır, monitor onu gözler ve scoreboard onun çıkışlarını referans modelle karşılaştırır.
DUT Nedir ve Mimarideki Yeri
DUT (Design Under Test), doğrulama ortamının kalbidir. Testbench'in tüm diğer bileşenleri (interface, driver, monitor, scoreboard...) yalnızca bu modülün doğru çalışıp çalışmadığını anlamak için vardır.
Bu ALU şu temel sözleşmeyi (contract) sunar:
- Girişler:
clk,rst_n,in_valid,operand_a,operand_b,opcode - Çıkışlar:
out_valid,result,flags - DUT, dış dünya ile yalnızca bu portlar üzerinden konuşur. Testbench içindeki
alu_ifarayüzü tam olarak bu portlara bağlanır.
Senkron Tasarım ve Pipeline Mantığı
ALU iki ayrı bloktan oluşur:
- Kombinasyonel blok (
always_comb): Seçilenopcode'a göre aritmetik/mantıksal işlemi yapar ve sonucunext_resultile bayraklarınext_flagsara sinyallerinde üretir. - Sıralı blok (
always_ff): Saat kenarında bu ara sonuçları çıkış register'larına (result,flags,out_valid) yazar. Bu yapı bir pipeline (boru hattı) oluşturur: bir girişin sonucu bir saat çevrimi gecikmeyle çıkışta görünür.
Bu gecikme, testbench tarafında çok önemlidir: monitor, çıkışın hangi saat kenarında geçerli olacağını (out_valid) bilmek zorundadır.
Opcode ve Bayrak Sözleşmesi
Tasarımdaki localparam opcode tanımları (OP_ADD, OP_SUB, ...), ALU_Transaction sınıfındaki opcode_e enum değerleriyle birebir aynı olacak şekilde seçilmiştir. Bu uyum, transaction'ı doğrudan DUT pinlerine sürebilmemizi sağlar. Benzer şekilde bayrak bitleri (FLAG_Z, FLAG_N, FLAG_C, FLAG_P) sabit pozisyonlarda tanımlanmıştır.
Kaynak Kod
module alu (
input logic clk,
input logic rst_n,
input logic in_valid,
input logic [7:0] operand_a,
input logic [7:0] operand_b,
input logic [2:0] opcode,
output logic out_valid,
output logic [15:0] result,
output logic [3:0] flags
);
// Internal signals for combinational logic outputs
logic [15:0] next_result;
logic [3:0] next_flags;
// Opcode definitions perfectly matched with Testbench ALU_Transaction enum
localparam bit [2:0]
OP_ADD = 3'b000,
OP_SUB = 3'b001,
OP_AND = 3'b010,
OP_OR = 3'b011,
OP_XOR = 3'b100,
OP_NOT = 3'b101,
OP_SHL = 3'b110,
OP_SHR = 3'b111;
// Flag bit positions
localparam int FLAG_Z = 0; // Zero
localparam int FLAG_N = 1; // Negative
localparam int FLAG_C = 2; // Carry (for 8-bit operations)
localparam int FLAG_P = 3; // Parity
// =========================================================================
// Combinational Block: Arithmetic & Logic Operations
// =========================================================================
always_comb begin
// Default initializations to prevent latches
next_result = 16'd0;
next_flags = 4'd0;
// Perform the operation based on the synced opcode
case (opcode)
OP_ADD: next_result = operand_a + operand_b;
OP_SUB: next_result = operand_a - operand_b;
OP_AND: next_result = {8'h00, operand_a & operand_b};
OP_OR: next_result = {8'h00, operand_a | operand_b};
OP_XOR: next_result = {8'h00, operand_a ^ operand_b};
OP_NOT: next_result = {8'h00, ~operand_a};
OP_SHL: next_result = {8'h00, operand_a} << operand_b[2:0];
OP_SHR: next_result = {8'h00, operand_a} >> operand_b[2:0];
default: next_result = 16'd0;
endcase
// Calculate status flags
next_flags[FLAG_Z] = (next_result == 16'd0);
next_flags[FLAG_N] = next_result[15];
// Carry flag makes sense mainly for ADD/SUB out of the 8th bit
if (opcode == OP_ADD || opcode == OP_SUB) begin
next_flags[FLAG_C] = next_result[8];
end else begin
next_flags[FLAG_C] = 1'b0;
end
// Parity flag (XOR reduction of the result)
next_flags[FLAG_P] = ^next_result;
end
// =========================================================================
// Sequential Block: Output Registers (Pipelining)
// =========================================================================
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
// Reset state
out_valid <= 1'b0;
result <= 16'd0;
flags <= 4'd0;
end else begin
// Pipeline the valid signal
out_valid <= in_valid;
// Only update outputs when input data is valid
if (in_valid) begin
result <= next_result;
flags <= next_flags;
end
end
end
endmodule
Kodun Açıklaması
- Modül arayüzü:
module alu (...)8-bitoperand_a/operand_bgirişlerini, 3-bitopcode'u vein_validel sıkışma sinyalini alır; 16-bitresult, 4-bitflagsveout_validüretir. Sonucun 16-bit olması toplama/çarpma benzeri işlemlerde taşmayı temsil edebilmek içindir. - Opcode sabitleri:
localparam bit [2:0] OP_ADD = 3'b000 ... OP_SHR = 3'b111ile 8 işlem kodlanır. Bu kodlamanın transaction enum'u ile aynı olması, doğrulamanın temel taşıdır. - Bayrak pozisyonları:
FLAG_Z(sıfır),FLAG_N(negatif),FLAG_C(carry),FLAG_P(parity) bayraklarının bit konumlarını sabitler. always_combbloğu: Latch oluşmasını engellemek için öncenext_resultvenext_flagsvarsayılan olarak sıfırlanır. Ardındancase (opcode)ile ilgili işlem yapılır. Mantıksal işlemlerde (OP_AND,OP_OR,OP_XOR,OP_NOT) 8-bit sonuç{8'h00, ...}ile 16-bit'e genişletilir; kaydırma işlemleri (OP_SHL,OP_SHR)operand_b[2:0]kadar kaydırır.- Bayrak hesabı:
FLAG_Zsonucun sıfır olup olmadığını,FLAG_Nen üst bitnext_result[15]'i izler.FLAG_CyalnızcaOP_ADD/OP_SUBiçinnext_result[8]'den alınır, diğer işlemlerde0'dır.FLAG_P,^next_resultile sonucun parite (XOR indirgeme) değerini verir. always_ffbloğu: Asenkron aktif-düşük reset (!rst_n) durumunda çıkışlar temizlenir. Normal çalışmadaout_valid <= in_validile geçerli sinyali bir çevrim geciktirilerek pipeline'a sokulur; çıkış register'ları yalnızcain_validyüksekken güncellenir.
Önemli Noktalar
always_combiçinde varsayılan atama yapmak, istenmeyen latch'lerin oluşmasını engeller; sentezlenebilir ve simülasyonda tutarlı tasarımın temel kuralıdır.- Çıkış register'ları bir saat çevrimi gecikme getirir (
out_valid <= in_valid). Driver ve monitor bu gecikmeyi dikkate alacak şekilde tasarlanmalıdır; aksi halde yanlış zamanda örnekleme yapılır. - Carry bayrağı yalnızca aritmetik işlemlerde anlamlıdır; mantıksal işlemlerde bilinçli olarak
0yapılmıştır. Referans model (scoreboard) bu davranışı birebir taklit etmelidir. - DUT'taki opcode kodlaması ile transaction enum'u senkron tutulmalıdır; biri değişirse diğeri de değişmeli, yoksa tüm karşılaştırmalar bozulur.
- Reset asenkron olarak
negedge rst_nile tetiklenir; testbench reset sırasında en az birkaç saat çevrimirst_n'i düşük tutmalıdır.