1. Transaction.
class my_transaction extends uvm_sequence_item;
`uvm_object_utils(my_transaction)
[Khai báo các biến giao tiếp với DUT ở đây]
[Khai báo các constraint cho các biến bên trên ở đây]
function new (string name = "");
super.new(name);
endfunction
endclass: my_transaction
Như vậy với transaction ta chỉ cần khai báo các biến giao tiếp với DUT và contrainst của nó.
2. Sequence.
class my_sequence extends uvm_sequence#(my_transaction);
`uvm_object_utils(my_sequence)
`uvm_declare_p_sequencer (my_sequencer)
function new (string name = "");
super.new(name);
endfunction
task pre_body ();
[Code kiểm tra tín hiệu cờ]
endtask
task body;
[Tạo tín hiệu random ở đây]
endtask: body
task post_body();
[Code kiểm tra tín hiệu cờ sau “task body”]
endtask
endclass: my_sequence
Với bộ sequence ta cần code trong task body, pre_body, post_body.
Trong đó task pre_body và post_body là không bắt buộc. Thậm chí nếu bạn sử dụng macros uvm_do* trong “task body” thì “pre_body” và “post_body” là không cần thiết. Trong thực tế người verify sẽ làm việc nhiều với sequence. Nó thật ra là công việc tạo test pattern trong verify.
3. Driver.
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver)
virtual dut_if dut_vif;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
[Add code của bạn ở đây]
endfunction
task run_phase(uvm_phase phase);
[Add code của bạn ở đây]
endtask
endclass: my_driver
4. Monitor.
class my_monitor extends uvm_monitor;
`uvm_component_utils (my_monitor)
virtual dut_if vif;
uvm_analysis_port #(my_data) mon_analysis_port;
function new (string name, uvm_component parent= null);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
[Thêm code tại đây]
endfunction
virtual task run_phase (uvm_phase phase);
[Thêm code tại đây]
endtask
endclass
5. Scoreboard.
class my_scoreboard extends uvm_scoreboard;
`uvm_component_utils (my_scoreboard)
function new (string name = "my_scoreboard", uvm_component parent);
super.new (name, parent);
endfunction
uvm_analysis_imp #(apb_pkt, my_scoreboard) apb_export;
function void build_phase (uvm_phase phase);
[Thêm code bạn ở đây]
endfunction
virtual function void write (apb_pkt data);
[In dữ liệu ra để quan sát]
endfunction
virtual task run_phase (uvm_phase phase);
[Nhận data và gòi hàm so sánh kết quả]
endtask
virtual function void check_phase (uvm_phase phase);
[So sánh kết quả output từ DUT và kết quả dự đoán]
endfunction
endclass
6. Agent.
class my_agent extends uvm_agent;
`uvm_component_utils (my_agent)
my_driver m_drv0;
my_monitor m_mon0;
uvm_sequencer #(my_data) m_seqr0;
agent_cfg m_agt_cfg;
function new (string name = "my_agent", uvm_component parent=null);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
[Thêm code tại đây]
endfunction
virtual function void connect_phase (uvm_phase phase);
[Thêm code tại đây để connect 3 thành phần driver, sequencer, monitor lại với nhau]
endfunction
endclass
Hướng dẫn người khác là cách học nhanh nhất. Hi vọng với những bài đăng của mình trong lĩnh vực vi mạch, photoshop và lập trình unity, mọi người sẽ giải quyết được các vấn đề đang gặp phải. Nếu bạn có câu hỏi gì cần giải quyết, để lại comment, Chúng ta sẽ cùng trao đổi và học hỏi lẫn nhau.
Thursday, April 25, 2019
[Bài 11][ UVM ] Tìm hiểu về UVM package
Tôi nghĩ rằng hầu hết chúng ta đều nhìn thấy một vài dòng
code sau trong môi trường UVM rồi đúng không.
`include "uvm_macros.svh"
import uvm_pkg::*;
Thế nhưng bạn có biết chức năng thực sự của các package này
là gì không ?
Trước khi phân tích về chức năng chính của các package chúng
ta sẽ tìm hiểu vị trí các định nghĩa class được thực hiện.
Tôi có hai giả thiết, Thứ nhất bạn phải định nghĩa class bên
trong một module như bên dưới:
module A;
class B;
….
endclass: B
class C;
…
endclass: C
…
endmodule
Đối với cách này, bạn không cần package, tuy nhiên nếu số lượng
class lớn và phức tạp thì việc viết như thế này sẽ làm ảnh hưởng đến khả năng quản
lí các class.
Cách thứ hai là bạn định nghĩa các class bên ngoài module
nhưng nằm trong cùng 1 file:
class B;
….
endclass: B
module A;
….
endmodule
Việc này cũng được chắp nhận tuy nhiên có một vài lưu ý nhỏ
về vị trí định nghĩa các class, Ta xét ví dụ bên dưới:
class test1;
logic [1:0] sw;
test tes;
function new ();
sw[1:0] = 2'b00;
endfunction
task show;
sw = 2'b11;
$display("sw
= %b", sw);
endtask
endclass: test1
class test;
rand logic [1:0];
endclass: test
module tb;
initial begin
test1 te;
te = new();
te.show;
end
endmodule
Trường hợp này sẽ báo lỗi vì class test1 được biên dịch trước
class test nhưng trong class test1 có 1 biến được khai báo kiểu test. Như vậy
khi khai báo các class cần phải theo thứ tự nghĩa là có một class khai báo 1 biến
có kiểu dữ liệu là 1 class khác thì class khác đó phải được compile trước.
Trong môi trường uvm các class rất phức tạp vì vậy chúng sẽ
được chia ra thành các file riêng lẽ mà không nằm trong cùng 1 file đễ dễ quản
lí. Tối lấy ví dụ một môi trường uvm cho bộ cộng 4 bit sẽ có các thành phần như
bên dưới:
add4bit_agent.sv
add4bit_config.sv
add4bit_driver.sv
add4bit_env.sv
add4bit_if.sv
add4bit_monitor.sv
add4bit_scoreboard.sv
add4bit_sequense.sv
add4bit_tb_top.sv
add4bit_test.sv
Bạn có hai cách để gọi các thành phần này trong module
testbench top, để biên dịch chúng.
Thêm các dòng code sau:
`include
"add4bit_sequense.sv"
`include
"add4bit_monitor.sv"
`include
"add4bit_driver.sv"
`include
"add4bit_agent.sv"
`include
"add4bit_scoreboard.sv"
`include
"add4bit_config.sv"
`include
"add4bit_env.sv"
`include
"add4bit_test.sv"
Điều này cũng được đấy. Tuy nhiên cứ mỗi lần gọi các class này
bạn lại cứ tốn nhiều dòng code để include chúng. Đôi khi bạn include thiếu,
include không đúng thứ tự ưu tiên của class dẫn đến error xảy ra trong quá
trình compile. Từ đó khái niệm package sẽ giúp bạn giải quyết vấn đề này.
Cấu trúc của một package:
package add4bit_pkg;
`include
"add4bit_sequense.sv"
………….
`include
"add4bit_test.sv"
endpackage: add4bit_pkg
Khi bạn muốn gọi các file định nghĩa class. Bạn chỉ cần 2
dòng code sau là có thể định nghĩa được chúng.
`include “tên_file_package”
import tên_package::*;
Việc này ngắn hơn, đơn giản hơn các cách trước và tránh được
các lỗi không đáng có khiến bạn mất thời gian để debug.
Một cách định nghĩa khác bạn có thể thực hiện là định nghĩa
các class trực tiếp trong cấu trúc
package như bên dưới:
package A;
class C;
…
endclass: C
class B;
…
endclass: B
….
endpackage: A
Cách gọi package thì tương tự như 2 dòng lệnh trên.
Đến đây thì bạn đã rõ về cách sử dụng package rồi phải không
nào.
Dưới đây là một số cấu trúc package bạn có thể tham khảo:
//Author: SCD/BUS/TrongTran
//Date : 03/29/2019
`ifndef _UVM_PKG_
`define _UVM_PKG_
`include "uvm_macros.svh"
package add4bit_pkg;
import uvm_pkg::*;
`include
"add4bit_sequense.sv"
`include
"add4bit_monitor.sv"
`include
"add4bit_driver.sv"
`include
"add4bit_agent.sv"
`include
"add4bit_scoreboard.sv"
`include
"add4bit_config.sv"
`include
"add4bit_env.sv"
`include
"add4bit_test.sv"
endpackage: add4bit_pkg
`endif
Một mẫu cho cách khai báo thứ hai là:
package my_testbench_pkg;
import uvm_pkg::*;
// The UVM sequence,
transaction item, and driver are in these files:
`include
"my_sequence.svh"
`include
"my_driver.svh"
// The agent
contains sequencer, driver, and monitor (not included)
class my_agent
extends uvm_agent;
`uvm_component_utils(my_agent)
my_driver driver;
uvm_sequencer#(my_transaction) sequencer;
function
new(string name, uvm_component parent);
super.new(name,
parent);
endfunction
function void
build_phase(uvm_phase phase);
driver =
my_driver ::type_id::create("driver", this);
sequencer =
uvm_sequencer#(my_transaction)::type_id::create("sequencer",
this);
endfunction
// In UVM connect
phase, we connect the sequencer to the driver.
function void
connect_phase(uvm_phase phase);
driver.seq_item_port.connect(sequencer.seq_item_export);
endfunction
task
run_phase(uvm_phase phase);
// We raise
objection to keep the test from completing
phase.raise_objection(this);
begin
my_sequence
seq;
seq =
my_sequence::type_id::create("seq");
seq.start(sequencer);
end
// We drop
objection to allow the test to complete
phase.drop_objection(this);
endtask
endclass
class my_env extends
uvm_env;
`uvm_component_utils(my_env)
my_agent agent;
function
new(string name, uvm_component parent);
super.new(name,
parent);
endfunction
function void
build_phase(uvm_phase phase);
agent =
my_agent::type_id::create("agent", this);
endfunction
endclass
class my_test
extends uvm_test;
`uvm_component_utils(my_test)
my_env env;
function
new(string name, uvm_component parent);
super.new(name,
parent);
endfunction
function void
build_phase(uvm_phase phase);
env =
my_env::type_id::create("env", this);
endfunction
task
run_phase(uvm_phase phase);
// We raise
objection to keep the test from completing
phase.raise_objection(this);
#10;
`uvm_warning("", "Hello World!")
// We drop
objection to allow the test to complete
phase.drop_objection(this);
endtask
endclass
endpackage
Wednesday, April 24, 2019
[Verification] Tìm hiểu về combine test (CT), unit test (UT) và system test (ST) trong thiết kế SoC
1. Combine test.
Không sử dụng model, Môi trường combine test thường rất lớn,
Bao gồm toàn bộ các rtl (Verilog hoặc system Verilog) được kết nối lại với
nhau. Combine test thường cho việc kiểm tra đọc/ghi thanh ghi, kiểm tra
connection giữa các thành phần.
Chúng ta thường tạo pattern bằng cách viết ngôn ngữ assembly
để CPU có thể access đến các thanh ghi cụ thể (Thông qua địa chỉ của các thanh
ghi này).
2. Unit test.
Dùng để kiểm tra tính đúng đắn của function. Môi trường unit
test thường rất nhỏ vì chỉ test cho một function nhất định. Chúng ta thường
dùng thư viện uvm (build môi trường random test) để tạo môi trường.
Nó không cần toàn bộ rtl của hệ thống mà các chỉ cần một DUT
(Design under test) cụ thể cần được test. Sau đó , các connection đến nó sẽ được
giả lập bởi các model (Tùy theo chuẩn giao tiếp mà bạn đang dùng). Model là một
code được viết bằng ngôn ngữ Verilog hoặc system Verilog được cung cấp bởi
cadence hoặc synopsys. Một model sẽ có đầy đủ các input/output cần connect đến
DUT nhầm thực hiện chức năng nhận data và trả data (giả lập như một rtl hoàn chỉnh).
Với môi trường unit test, kết quả được đánh giá là “PASS” nếu
như function của bạn chạy đúng.
Ví dụ:
Bạn có 1 DUT thực hiện tính năng cộng giữa hai số. Bạn đánh
giá kết quả là “PASS” khi bạn đưa input vào và kết quả gửi ra đúng như mong đợi
(Kết quả mong đợi dựa vào spec).
Người chạy unit test chỉ cần tạo pattern. Partern ở đây
không giống với pattern của combine test vì nó là đưa vào các input cụ thể hoặc
các tín hiệu kích thích ngẫu nhiên. Từ các tín hiệu đó chúng ta sẽ quan sát kết
quả đầu ra. Nếu đúng với kết quả chúng ta mong đợi thì đó là “PASS”. Tôi lấy ví
dụ ta có bộ cộng như bên dưới:
Nhiệm vụ của ta là tạo ra 2 tín hiệu input chứa tất cả các
trường hợp có thể có. Đưa vào DUT và quan sát kết quả đầu ra.
Giả sử nếu ta đưa vào a = 2 và b = 3 các trường hợp có thể xẩy
ra:
- Kết quả đầu ra c= 4 => Fail vì 2+3 # 4.
- Kết quả đầu ra c = 5 => Pass vì 2+3 = 5.
- Quá trình mô phỏng treo.
Bên cạnh việc kiểm tra kết quả ngõ ra, Bạn còn phải đảm bảo
rằng bạn đưa vào đủ các trường hợp có thể có của a và b. Điều này được tính
toán bởi quá trình lấy coverage. Nếu coverage đạt trên bao nhiêu phần % sẽ được
chấp nhận. Vậy tại sao chúng ta cần coverage. Đơn giản là vì nếu bạn sử dụng
môi trường random test các tín hiệu a và b sẽ được tạo ra một cách ngẫu nhiên.
Khi đó bạn không thể đảm bảo rằng nó đưa vào đủ hết các trường hợp. Việc tính
toán coverage là cần thiết để biết chắc bạn đã “PASS” hết tất cả các trường hợp
có thể xảy ra.
3. System test.
System test là cách để bạn kiểm tra hiệu xuất làm việc của
toàn bộ hệ thống.
Tôi lấy ví dụ với unit test bạn chỉ cần kết quả gửi ra đúng
(Hay nói đúng hơn miễn sao function của bạn đúng) là bạn có thể đánh giá kết quả
là “PASS”. Tuy nhiên, thực tế có một số trường hợp đặc biệt bạn không thể đoán
trước được. Tôi lấy ví dụ master A gửi quá nhiều transaction đến slave B. Đôi khi
slave B không thể xử lí kịp và trả về kết quả . Điều này dẫn đến kết quả dữ liệu
bạn nhận được khi read/write có thể bị mất, hoặc hệ thống bị treo…
Để kiểm tra cho việc này chúng ta cần phải có một môi trường
để kiểm tra nó. Và system test ra đời.
Một lưu ý quan trọng là system test rất khó debug vì vậy nó
chỉ nên được chạy tại các giai đoạn sau cùng.
Nghĩa là khi combine test (CT) và unit (UT) đã “PASS”. Việc
đảm bảo function đúng để tránh lãng phí thời gian debug cho system test.
Về cơ bản System test là việc kiểm tra các bus, hay các
interconnection có đáp ứng được các điều kiện hoạt động hay không.
Có đảm bảo performance hay không. Vì hầu như các master hay
slave đều được thay thế bằng model.
Nhiệm vụ của người chạy là tạo các patern (Hay sequence) bằng
cách thiết lập các thông số cho model. Ví dụ bandwidth là bao nhiêu,data width…v…v
(Còn tiếp).
Monday, April 22, 2019
[System verilog][ Bài 3] Tìm hiểu về mảng đóng gói và mảng không đóng gói (Packed array và unpacked array)
Mảng đóng gói (Hay còn gọi là packed array) và mảng không
đóng gói ( Hay còn gọi là unpacked array).
Trong bài này chúng ta sẽ cùng tìm hiểu về mảng packed và mảng
unpacked.
Đây là 2 kiểu mảng được sử sụng khá phổ biến nhưng chúng ta
có thể chưa nhận ra nó. Tôi lấy ví dụ khi bạn định nghĩa kiểu dữ liệu như bên
dưới:
bit [3:0] var;
logic var [3:0];
Bạn đang sử dụng mảng packed và unpacked rồi đấy.
Vậy tại sao ta gọi đó là 1 mảng mà không gọi là 1 biến.
Đầu tiên bạn có thể xem “logic” hay “bit” là các kiểu dữ liệu
1 bit và như vậy với biến var ta đang có 4 phần tử 1 bit kết hợp lại. Điều này
có thể xem xét như là mảng 1 chiều có 4 phần tử, mỗi phần tử 1 bit.
Bây giờ ta sẽ đi vào phân tích hai loại mảng trên để thấy được điểm khác biệt giữa chúng.
1.Mảng đóng gói (Hay
còn gọi là packed array).
Kích thước mảng được khai báo trước tên biến. Packed array
có thể được xem xét như một vector.
Ví dụ:
bit [7:0] led;
bit [3:0] sw;
Để xác định kích thước của mảng ta dùng hàm $size(tên mảng).
Ta xét một số ví dụ bên dưới:
module tb;
bit [3:0]sw;
initial begin
sw = 4'b1011;
for (int i = 0; i
< $size(sw); i++) begin
$display
("sw[%0d] = %b", i, sw[i]);
end
end
endmodule
Ta xem xét ví dụ như bên trên và dưới đây là kết quả:
ncsim> run
sw[0] = 1
sw[1] = 1
sw[2] = 0
sw[3] = 1
Thêm một ví dụ khác về packed array hai chiều:
module tb;
bit [1:0][3:0] sw;
initial begin
sw = 8'hAB;
$display ("sw
= 0x%0h", sw);
for (int i = 0; i
< $size(sw); i++) begin
$display
("sw[%0d] = %b (0x%0h)", i, sw[i], sw[i]);
end
end
endmodule
Và đây là kết quả:
ncsim> run
sw = 0xab
sw[0] = 1011 (0xb)
sw[1] = 1010 (0xa)
module tb;
bit [1:0][3:0] sw;
initial begin
sw[0] = 4'hB;
sw[1] = 4'hA;
$display ("sw
= 0x%0h", sw);
for (int i = 0; i
< $size(sw); i++) begin
$display
("sw[%0d] = %b (0x%0h)", i, sw[i], sw[i]);
for (int j = 0;
j < $size(sw[i]); j++) begin
$display
("sw[%0d][%0d] = %b (0x%0h)", i ,j , sw[i][j], sw[i][j]);
end
end
end
endmodule
ncsim> run
sw = 0xab
sw[0] = 1011 (0xb)
sw[0][0] = 1 (0x1)
sw[0][1] = 1 (0x1)
sw[0][2] = 0 (0x0)
sw[0][3] = 1 (0x1)
sw[1] = 1010 (0xa)
sw[1][0] = 0 (0x0)
sw[1][1] = 1 (0x1)
sw[1][2] = 0 (0x0)
sw[1][3] = 1 (0x1)
2.Mảng không đóng gói
(Hay còn gọi là unpacked array)
Bây giờ chúng ta sẽ cùng nhau tìm hiểu về unpacked array.
Khác với packed array, Unpacked array có kích thước được định
nghĩa sau tên biến.
Ví dụ:
logic var [3:0];
bit sw [1:0];
Unpacked array có thể là fixed-size arrays, dynamic arrays,
associative arrays hoặc là queues.
Ta xét một số ví dụ như bên dưới để có thể thấy rõ sự khác
biệt giữa packed array và unpacked array.
module tb;
byte sw [4];
initial begin
foreach (sw[i])
begin
sw[i] = $random;
$display
("Assign 0x%h to index %d", sw[i], i);
end
$display ("sw
= %p", sw);
end
endmodule
Ta có kết quả như bên dưới:
ncsim> run
Assign 0x24 to index 0
Assign 0x81 to index 1
Assign 0x09 to index 2
Assign 0x63 to index 3
sw = '{'h24, 'h81, 'h9, 'h63}
Như vậy có thể thấy, unpacked array là một mảng thật sự. Các
phần tử tách biệt nhau.
module tb;
byte sw [2][2];
initial begin
foreach (sw[i])
begin
foreach
(sw[i][j]) begin
sw[i][j] =
$random;
$display
("sw[%d][%d] = 0x%h", i, j, sw[i][j]);
end
end
$display ("sw
= %p", sw);
end
endmodule
Kết quả in ra màn hình của chương trình trên:
ncsim> run
sw[ 0][ 0] = 0x24
sw[ 0][ 1] = 0x81
sw[ 1][ 0] = 0x09
sw[ 1][ 1] = 0x63
sw = '{'{'h24, 'h81}, '{'h9, 'h63}}
Thursday, April 18, 2019
[Synthesis][ Bài 1] Giới thiệu cơ bản về việc thay thế FF (flip flop) hậu tổng hợp (Synthesis)
Tìm hiểu về việc thay thế FF (Flip flop) trong quá trình hậu synthesis.
Synthesis là một bước khá quan trọng và cần rất nhiều nỗ lực để kiểm tra, test (timing, power…). Trong bài này tôi sẽ trình bày cho các bạn về việc thay thế các FF trên gate nestlist (Sau khi synthesis).
I. Meta FF
Đây là một công việc của Backend team. Nhiệm vụ của họ là thay thế các FF đã được liệt kê trong một danh sách được cung cấp bởi designer hoặc người synthesis (Frontend team) trên gate nestlist bởi 1 flip flop thuộc loại khác.
Với phương pháp này ta thấy thông thường nhất là việc thay các flip flop trong mạch đồng bộ tín hiệu async 2 flip flop.
Ta có sơ đồ mạch như hình bên dưới:
Hình 1. Mạch đồng bộ tín hiệu async 2 flip flop và vị trí thay thế Meta FF
Như vậy ta có cấu trúc FF1 -> FF2. Nhiệm vụ của designer hay nói đúng hơn là người thực hiện quá trình tổng hợp (synthesis) là sẽ liệt kê ra tên của FF1 trong mạch đồng bộ tín hiệu async. Có nhiều cách để liệt kê, ở đây tôi đề cập đến 2 cách:
Cách 1: Là cách phổ biến nhất, đó là dựa vào report của quá trình STAcheck. Đối với mạch async, STAcheck sẽ báo ra các lỗi để người dùng confirm. Đồng thời tên của FF1 cũng sẽ được liệt kê ra trong report. Nhiệm vụ của người synthesis chỉ là thu thập chúng, tạo file và gửi cho Backend team.
Cách 2: Dành cho những người hiểu rõ về thiết kế của mạch. Người thu thập thông tin cần biết được các port input nào hoạt động với clock async (Nghĩa là clock khác với clock của module). Từ các port đó ta cần lần theo các connection trong gate nestlist để xác định được cập flip flop nào có chức năng đồng bộ tín hiệu async. Với cách làm này, chỉ có thể áp dụng cho module có rất ít port async và module nhỏ. Với một module lớn và có nhiều port async bạn sẽ phải tốn thời gian để liệt kê đầy đủ thông tin.
Vậy mục đích của việc thay thế này là gì. Đó là các FF1 sẽ được thay thế bởi các flip flop có tuổi thọ lâu hơn, tốt hơn. Hãy tưởng tượng khi FF1 bị die thì mạch chúng ta sẽ làm việc không đúng nữa. Làm việc với mạch async mang lại rất nhiều rủi ro nếu ta không kiểm tra đầy đủ.
II. TMR.
Việc thay thế số 2 tôi nói đến trong bài này là TMR (Triple modular redundancy): Đây cũng là việc thay thế của Backend team và Fronend team cũng là những người cần cung cấp các list cần thay thế.
Việc thông thường là ta sẽ thay thế 1 flip flop bởi nhiều flip flop mắc song song.
Tôi lấy ví dụ. Khi bạn có một tín hiệu để xuất kết quả error ra đến module interrupt. Giả sử trên đường truyền tín hiệu error này, vì một lí do nào đó tín hiệu của bạn bị sai (tín hiệu error thường 1 bit vì vậy xác xuất sai của bạn khá cao) khi đó hệ thống của bạn dù không lỗi cũng có thể bị interrupt (tín hiệu error truyền sai từ giá trị 0 -> 1). Hoặc dù có lỗi nhưng vẩn không bị interrupt (Tín hiệu error truyền sai từ giá trị 1 ->0). Điều này thật sự nghiêm trọng nhất là đối với việc các module connect với nhau. TMR sẽ giúp chúng ta giải quyết vấn đề này bằng cách nhân 3 mạch hay FF lên (Hay nói đúng hơn là thay thế mạch FF hiện tại bằng 3 mạch tương tự mắc song song với nhau, 3 mạch này có output đi vào bộ compare) sau đó so sánh kết quả của 3 mạch hay FF này nếu có tối thiểu 2 tín hiệu trong 3 tín hiệu này mang giá tri 0 thì giá trị ra mang giá trị 0 và ngược lại.
Hình bên dưới biểu thị rõ nét mạch nhân 3 để check tín hiệu ra.
Hình 2. Thay thế mạch 1 flip flop bởi mạch 3 flip flop mắc song song.
Tương tự ta có thể nhân 5 mạch để xác xuất lỗi giảm xuống. Nếu 2 tín hiệu bị lỗi và 3 tín hiệu chính xác thì tín hiệu ngõ ra sẽ mang giá trị 3 tín hiệu đó.
Tuy nhiên lựa chọn nhân 3 mạch là lựa chọn khả dĩ nhất vì nếu bạn tăng nhiều mạch gate size của bạn sẽ tăng lên đáng kể mà xác xuất chính xác cũng không hiệu quả hơn là bao so với nhân 3 mạch.
Synthesis là một bước khá quan trọng và cần rất nhiều nỗ lực để kiểm tra, test (timing, power…). Trong bài này tôi sẽ trình bày cho các bạn về việc thay thế các FF trên gate nestlist (Sau khi synthesis).
I. Meta FF
Đây là một công việc của Backend team. Nhiệm vụ của họ là thay thế các FF đã được liệt kê trong một danh sách được cung cấp bởi designer hoặc người synthesis (Frontend team) trên gate nestlist bởi 1 flip flop thuộc loại khác.
Với phương pháp này ta thấy thông thường nhất là việc thay các flip flop trong mạch đồng bộ tín hiệu async 2 flip flop.
Ta có sơ đồ mạch như hình bên dưới:
Hình 1. Mạch đồng bộ tín hiệu async 2 flip flop và vị trí thay thế Meta FF
Như vậy ta có cấu trúc FF1 -> FF2. Nhiệm vụ của designer hay nói đúng hơn là người thực hiện quá trình tổng hợp (synthesis) là sẽ liệt kê ra tên của FF1 trong mạch đồng bộ tín hiệu async. Có nhiều cách để liệt kê, ở đây tôi đề cập đến 2 cách:
Cách 1: Là cách phổ biến nhất, đó là dựa vào report của quá trình STAcheck. Đối với mạch async, STAcheck sẽ báo ra các lỗi để người dùng confirm. Đồng thời tên của FF1 cũng sẽ được liệt kê ra trong report. Nhiệm vụ của người synthesis chỉ là thu thập chúng, tạo file và gửi cho Backend team.
Cách 2: Dành cho những người hiểu rõ về thiết kế của mạch. Người thu thập thông tin cần biết được các port input nào hoạt động với clock async (Nghĩa là clock khác với clock của module). Từ các port đó ta cần lần theo các connection trong gate nestlist để xác định được cập flip flop nào có chức năng đồng bộ tín hiệu async. Với cách làm này, chỉ có thể áp dụng cho module có rất ít port async và module nhỏ. Với một module lớn và có nhiều port async bạn sẽ phải tốn thời gian để liệt kê đầy đủ thông tin.
Vậy mục đích của việc thay thế này là gì. Đó là các FF1 sẽ được thay thế bởi các flip flop có tuổi thọ lâu hơn, tốt hơn. Hãy tưởng tượng khi FF1 bị die thì mạch chúng ta sẽ làm việc không đúng nữa. Làm việc với mạch async mang lại rất nhiều rủi ro nếu ta không kiểm tra đầy đủ.
II. TMR.
Việc thay thế số 2 tôi nói đến trong bài này là TMR (Triple modular redundancy): Đây cũng là việc thay thế của Backend team và Fronend team cũng là những người cần cung cấp các list cần thay thế.
Việc thông thường là ta sẽ thay thế 1 flip flop bởi nhiều flip flop mắc song song.
Tôi lấy ví dụ. Khi bạn có một tín hiệu để xuất kết quả error ra đến module interrupt. Giả sử trên đường truyền tín hiệu error này, vì một lí do nào đó tín hiệu của bạn bị sai (tín hiệu error thường 1 bit vì vậy xác xuất sai của bạn khá cao) khi đó hệ thống của bạn dù không lỗi cũng có thể bị interrupt (tín hiệu error truyền sai từ giá trị 0 -> 1). Hoặc dù có lỗi nhưng vẩn không bị interrupt (Tín hiệu error truyền sai từ giá trị 1 ->0). Điều này thật sự nghiêm trọng nhất là đối với việc các module connect với nhau. TMR sẽ giúp chúng ta giải quyết vấn đề này bằng cách nhân 3 mạch hay FF lên (Hay nói đúng hơn là thay thế mạch FF hiện tại bằng 3 mạch tương tự mắc song song với nhau, 3 mạch này có output đi vào bộ compare) sau đó so sánh kết quả của 3 mạch hay FF này nếu có tối thiểu 2 tín hiệu trong 3 tín hiệu này mang giá tri 0 thì giá trị ra mang giá trị 0 và ngược lại.
Hình bên dưới biểu thị rõ nét mạch nhân 3 để check tín hiệu ra.
Hình 2. Thay thế mạch 1 flip flop bởi mạch 3 flip flop mắc song song.
Tương tự ta có thể nhân 5 mạch để xác xuất lỗi giảm xuống. Nếu 2 tín hiệu bị lỗi và 3 tín hiệu chính xác thì tín hiệu ngõ ra sẽ mang giá trị 3 tín hiệu đó.
Tuy nhiên lựa chọn nhân 3 mạch là lựa chọn khả dĩ nhất vì nếu bạn tăng nhiều mạch gate size của bạn sẽ tăng lên đáng kể mà xác xuất chính xác cũng không hiệu quả hơn là bao so với nhân 3 mạch.
Thursday, April 11, 2019
[Bài 10][ UVM ] Tạo env trong môi trường UVM
Về khối env, tôi thấy nó cũng khá giống với khối agent. Chức năng chính của khối này là tiến hành connect hai khối scoreboard và khối agent lại với nhau thông qua chuẩn giao tiếp TLM. Tuy nhiên, vì chúng ta có hai luồng dữ liệu khác nhau nên ta phải khai báo 2 biến TLM. Dùng để truyền output của DUT và truyền kết quả dự đoán từ khối agent đến khối scoreboard.
Dưới đây là cấu trúc cơ bản của khối env:
`ifndef _UVM_ENV_
`define _UVM_ENV_
class add4bit_env extends uvm_env;
`uvm_component_utils(add4bit_env)
add4bit_scoreboard score_trans;
add4bit_agent agent_trans;
function new(string name = "", uvm_component parent);
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
score_trans = add4bit_scoreboard::type_id::create(.name("score_trans"), .parent(this));
agent_trans = add4bit_agent::type_id::create(.name("agent_trans"), .parent(this));
endfunction: build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
agent_trans.agent_before.connect(score_trans.monitor_before);
agent_trans.agent_after.connect(score_trans.monitor_after);
endfunction: connect_phase
endclass: add4bit_env
`endif
Dưới đây là cấu trúc cơ bản của khối env:
`ifndef _UVM_ENV_
`define _UVM_ENV_
class add4bit_env extends uvm_env;
`uvm_component_utils(add4bit_env)
add4bit_scoreboard score_trans;
add4bit_agent agent_trans;
function new(string name = "", uvm_component parent);
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
score_trans = add4bit_scoreboard::type_id::create(.name("score_trans"), .parent(this));
agent_trans = add4bit_agent::type_id::create(.name("agent_trans"), .parent(this));
endfunction: build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
agent_trans.agent_before.connect(score_trans.monitor_before);
agent_trans.agent_after.connect(score_trans.monitor_after);
endfunction: connect_phase
endclass: add4bit_env
`endif
Subscribe to:
Posts (Atom)
Cách tính BW và latency trong 1 hệ thống SoC sử dụng chuẩn giao tiếp AXI protocol
Tác giả: TrongTran Ngày: 31/12/2019 Nếu bạn nào đang làm về verification cho system performance (ST) thì bài này sẽ bổ ích cho bạn. Ngày ...
-
Tác giả: TrongTran Ngày: 16/09/2019 Tài liệu tham khảo: www-inst.eecs.berkeley.edu/~cs61c/fa18/img/riscvcard.pdf và tài liệu học tập ...
-
Tác giả: TrongTran Ngày: 17/09/2019 Tài liệu tham khảo: www-inst.eecs.berkeley.edu/~cs61c/fa18/img/riscvcard.pdf và tài liệu học tập ...
-
Tác giả: TrongTran Ngày: 16/09/2019 Tài liệu tham khảo: www-inst.eecs.berkeley.edu/~cs61c/fa18/img/riscvcard.pdf và tài liệu học tập từ t...