Chủ Nhật, 23 tháng 7, 2017

[Verilog] Các cách mô tả một mạch tổ hợp với ngôn ngữ Verilog

Nội dung bài này đưa ra các cách khác nhau có thể sử dụng để mô tả một mạch tổ hợp sử dụng Verilog HDL.

Mạch tổ hợp hình 1 sẽ được sử dụng để minh họa các cách khác nhau được sử dụng để cùng mô tả một mạch tổ hợp. Mạch này là mạch tạo ngõ ra GS cho bộ mã hóa ưu tiên 8 bit. Xem thêm bài "Làm thế nào để mô tả mạch tổ hợp bằng ngôn ngữ Verilog" để biết thêm chi tiết.

Hình 1. Mạch tổ hợp tạo ngõ ra GS (Group Select) cho bộ mã hóa ưu tiên 8 bit

1. Sử dụng phát biểu assign

Phát biểu assign là một trong những từ khóa được sử dụng phổ biến hàng đầu trong ngôn ngữ Verilog HDL để mô tả mạch tổ hợp. Thông qua việc kết hợp với các toán tử khác, phát biểu assign  trở nên linh hoạt trong việc mô tả loại mạch này.
Hình 2. Các toán tử trong ngôn ngữ Verilog
Với mạch hình 1, chúng ta có thể mô tả bằng assign như sau:

assign GS   = Ein? (|D[7:0]): 0;

Cũng có thể chia mô tả trên thành mạch OR và mạch MUX riêng để có cách mô tả tương đương như sau:

assign GS      = Ein? or_out: 0;
assign or_out = |D[7:0];
Mạch MUX có thể thay thế tương đương với một cổng AND như sau:

assign GS   = Ein & (|D[7:0]);
2. Sử dụng cấu trúc always

Cấu trúc always cũng là một trong những cấu trúc có tần suất sử dụng nhiều nhất. Có thể sử dụng thay thế tương đương cho phát biểu assign trong mô tả mạch tổ hợp. Nó có thể kết hợp thêm với các phát biểu điều kiện (if-else-if), phát biểu case (case/casez/casex), ... làm RTL code trở nên dễ đọc và hiểu hơn.

Việc thay thế tương đương giữa always assign như sau:

always @ (*) begin
  GS   = Ein & (|D[7:0]);
end

hoặc

always @ (*) begin
  GS      = Ein? or_out: 0;
  or_out  = |D[7:0];
end
hoặc

always @ (*) begin
  GS   = Ein & (|D[7:0]);
end

Tuy nhiên cần lưu ý, kiểu dữ liệu của GS or_out trong phát biểu assign và cấu trúc always là khác nhau. Sử dụng assign, chúng được khai báo kiểu wire. Sử dụng always, chúng được khai báo kiểu reg.

Sử dụng always kết hợp với if-else-if:

always @ (*) begin
  if (Ein) GS = |D[7:0];
  else GS = 0;
end
Sử dụng always kết hợp với case:

always @ (*) begin
  case (Ein)
    1'b1: GS = |D[7:0];
default: GS = 0;
  endcase
end
3. Sử dụng function

Từ khóa function là một dạng khác so với assignalways. Mạch được mô tả bởi function sẽ không được tổng hợp nếu không được gọi. Việc sử dụng function cần 2 bước:
  1. Tạo (khai báo) function là để tạo một mô tả mạch chức năng
  2. Gọi function là để sử dụng hoặc tạo ra mạch chức năng mong muốn khi chạy mô phỏng hoặc tổng hợp. Function được gọi thông qua các phát biểu khác như assign hay always.
Đối với mạch hình 1, chúng ta có thể tạo function như sau:


function group_select;
  input enable;
  input [7:0] data_in;

  group_select   = enable & (|data_in[7:0]);
endfunction
hoặc:
function group_select;
  input enable;
  input [7:0] data_in;

  if (enable) group_select = |data_in[7:0];
  else group_select = 0;
endfunction

Để sử dụng function đã tạo ra, việc gọi function có thể thực hiện như sau:

assign GS = group_select(Ein, D[7:0]);

hoặc:
always @ (*) begin
  GS = group_select(Ein, D[7:0]);
end
Tuy nhiên, function sử dụng chủ yếu cho việc mô tả các mạch có cấu trúc cố định nhưng mạch này được sử dụng lặp lại nhiều lần trong một thiết kế. Điều này được minh họa trên mạch tạo Q[2:0] trong bộ mã hóa ưu tiên 8 bit.

Hình 3. Mạch tạo ngõ ra trong bộ mã hóa ưu tiên 8 bit
Mạch này sử dụng nhiều bộ MUX 2 ngõ vào, mỗi ngõ vào 3 bit. Bộ MUX này được tạo bằng function như sau:
function [2:0] mux2;
  input sel;
  input [2:0] in1;
  input [2:0] in2;

  mux2 = sel? in1: in2;
endfunction
Mạch hình 3 sẽ được mô tả bằng cách gọi function trên như sau:
assign Q[2:0]     = mux2(Ein,  out_7,  3'b000);
assign out_7[2:0] = mux2(D[7], 3'b111, out_6);
assign out_6[2:0] = mux2(D[6], 3'b110, out_5);
assign out_5[2:0] = mux2(D[5], 3'b101, out_4);
assign out_4[2:0] = mux2(D[4], 3'b100, out_3);
assign out_3[2:0] = mux2(D[3], 3'b011, out_2);
assign out_2[2:0] = mux2(D[2], 3'b010, out_1);
assign out_1[2:0] = mux2(D[1], 3'b001, 3'b000);

4. Sử dụng các thành phần xây dựng sẵn

Những thành phần xây dựng sẵn trong Verilog, gọi là built-in primitive, chứa các cổng được xây dựng sẵn, gọi là built-in gate. Các cổng xây dựng sẵn này được gọi và sử dụng tương tự như việc gọi một module có sẵn.

Hình 4. Các cổng xây dựng sẵn (built-in gate) có thể tổng hợp được trong Verilog 
Để mô tả một mạch tổ hợp bằng cách sử dụng các cổng này thì tất cả các ngõ ra cần phải đưa về dạng biểu thức boolean hoặc các mạch nguyên lý phải ở dưới dạng các cổng cơ bản như liệt kê trong hình 4.
Như đã trình bày, MUX trong mạch hình 1 có thể thay bằng cổng AND như hình sau.

Hình 5. Mạch nguyên lý tương đương của hình 1
Như vậy GS có thể được mô tả bằng các built-in gate như sau:
and and_gate (GS, Ein, or_out);
or  or_gate  (or_out, D[7], D[6], D[5], D[4], D[3], D[2], D[1], D[0]);
Chú ý, and or là loại built-in gate có n ngõ vào.

5. Sử dụng các standard cell của thư viện tổng hợp

Thư viện tổng hợp sẽ chứa các cell chuẩn (standard cell) được sử dụng để biên dịch RTL code thành file netlist. File netlist chứa kết nối của các cell chuẩn của thư viện.

Ở đây, tôi sử dụng thư viện cell chuẩn 180um của TSMC để minh họa, các bạn có thể tìm và download dễ dàng tài liệu mô tả về thư viện này "TSMC 0.18µm Process 1.8-Volt SAGE-XTM Standard Cell Library Databook".

Các họ cell được chọn để mô tả cho mạch hình 1 là  MX2, OR4 và OR2. Gọi là họ cell vì mỗi tên này đại diện cho một nhóm cell có cùng chức năng nhưng có khả năng lái tải ngõ ra (Driver strength) khác nhau.

Hình 5. Các họ cell chuẩn được chọn để mô tả RTL code
Mạch hình 1 sẽ được biến đổi thành mạch dưới đây để có thể sử dụng các họ cell đã nói.

Hình 7. Biến đổi lại mạch nguyên lý hình 1
RTL code của GS sử dụng các cell chuẩn như sau:
MX2X2 mux_gate  (.Y(GS), .S0(Ein), .A(1'b0), .B(or_out));
OR2X2 or2_gate  (.Y.(or_out), .A(or4_1), .B(or4_2));
OR4X2 or4_gate1 (.Y(or4_1), .A(D[7]), .B(D[6]), .C(D[5]), .D(D[4]));
OR4X2 or4_gate2 (.Y(or4_2), .A(D[3]), .B(D[2]), .C(D[1]), .D(D[0]));
Chú ý, nếu sử dụng cách này RTL code sẽ chỉ tổng hợp được trên thư viện cố định có chứa các cell đã chọn. Việc mô phỏng chức năng (function) mạch ở mức RTL code độc lập với thư viện tổng hợp cũng thể thực hiện được.

6. Cách mô tả tổng hợp

Cách mô tả tổng hợp là sử dụng kết hợp linh hoạt các cách đã trình bày trên đây để tạo ra một RTL code đúng chức năng yêu cầu, tổng hợp được, dễ đọc và dễ debug.

0 bình luận:

Đăng nhận xét