Interface nâng cao: tham số, nhiều lane và kết nối theo tên

Khoảng 45 phútMức 2/3Bản nháp: một số khẳng định chưa đối chiếu spec

Sau bài này bạn sẽ

  • Tách value parameter và type parameter tại elaboration
  • Dùng genvar cho phần tử interface array, giữ từng lane độc lập
  • Giải thích .name và .* qua các kết nối đã kiểm

Nhân bản lane không được nhân bản cả lỗi nối dây

Một bộ cộng 1 bit và một bank ba bộ cộng 3 bit có thể dùng chung cấu trúc interface. Khó khăn không nằm ở việc đổi một con số trong khai báo: output cần đủ carry, từng lane phải có instance riêng và checker phải phát hiện khi lane k vô tình đọc tín hiệu của lane 0.

Bài này ghép ba thí nghiệm: bank W=1/N=2, bank W=3/N=3 và module copy có type parameter là packed struct. Mỗi phần có số check và điều kiện kết thúc riêng. Chúng chạy song song; tổng số check là phép cộng, còn thời điểm kết thúc là thời gian của nhóm lâu nhất.

W là cấu hình elaboration của từng instance

sized_link #(.W(W)) links[N](); tạo N interface với input rộng W và total rộng W+1. W được chọn khi elaboration, không phải biến đổi từ 1 thành 3 giữa hai vòng lặp runtime. Hai group trong top tạo hai specialization cùng tồn tại.

Một parameter cùng tên ở module khác không tự đồng bộ theo interface. Main tránh khai báo thêm width riêng trong sum_bank: phép cộng lấy trực tiếp member của links[k] và thêm một bit 0 ở mỗi toán hạng. Nếu module có storage nội bộ phụ thuộc W, cần truyền hoặc suy ra kích thước đúng và kiểm consistency. Không lấy việc nối được port làm bằng chứng mọi width đã phù hợp.

[1]

Array interface là nhiều instance trong hierarchy

links[N]() gồm N instance, mỗi phần tử có a/b/total riêng. Đây khác với một logic vector chứa N giá trị. Tên links[0] chỉ đến một instance cụ thể trong hierarchy; không mặc định coi mọi cách index runtime của data array cũng dùng được cho interface array.

Main dùng for(genvar k=0;k<N;k++) để elaboration tạo kết nối và process từng lane. Trong mỗi bản sao, k là hằng. Ngược lại, probe dùng một biến int để chọn links[index].a bị pyslang từ chối. Nếu cần chọn đích ở runtime trong class, hãy xem cơ chế virtual interface thay vì thay genvar bằng int mà giữ nguyên truy cập hierarchy.

[2]

Dữ liệu từng lane phải phân biệt được lỗi alias

Ở mỗi lane k, a quét 0..D−1 và b được đặt bằng (b_loop+k) modulo D, với D=2^W. Phép dịch vòng theo k vẫn phủ toàn bộ cặp đầu vào cho từng lane, đồng thời tạo khác biệt giữa các lane tại cùng thời điểm.

Checker đối chiếu total với a_loop+(b_loop+k)%D. Không dùng lại links[k].b làm oracle, vì lỗi nối dây có thể khiến cả stimulus lẫn phép tự kiểm cùng đọc nhầm. Bản sao DUT dùng links[0].b cho mọi lane bị runtime checker bắt; không chỉ đếm đủ loop rồi gọi là functional coverage hoàn chỉnh.

Tên trùng là điều kiện kết nối, không phải suy đoán ý nghĩa

sum_bank #(.N(N)) dut(.links) viết tắt của .links(links). Formal links được nối với tên links ở scope instantiation. copy_word #(.T(Token)) dut(.*) nối data/result cùng tên trong type_group; hai cổng này được kiểm thực sự bằng mô phỏng.

.* không tìm một tín hiệu có ý nghĩa gần giống tên cổng, cũng không tự ghép field struct theo vị trí. Khi local signal có tên khác, dùng kết nối tường minh .data(source_word); khi review, mở declaration và scope để biết actual là gì. Bài không dùng bảng khái quát của tutorial về mọi port không match để suy rằng thiếu tên sẽ luôn im lặng; giữ warning/error của cấu hình tool thật.

[3]

Parameter type thay kiểu của cổng, không cấp phát object

Module copy_word khai báo parameter type T=logic[3:0], rồi có input T data và output T result. Override .T(Token) dùng packed struct gồm valid 1 bit và tag 3 bit. T là kiểu, không phải giá trị width; không truyền số 4 để thay cho tên kiểu Token.

Main quét đủ 16 bit-pattern của Token. Ngoài equality toàn word, checker kiểm valid bằng bit cao của số vòng lặp và tag bằng ba bit thấp, nên lỗi đảo bit không bị giấu sau một counter đúng. Đây là truyền một packed value; không phải deep clone object, không chứng minh mọi kiểu class/unpacked payload dùng được với mọi implementation.

[4]

216 phép kiểm nhưng chỉ 64 ns

Với W=1, mỗi lane có 2×2=4 cặp; hai lane cho 8 check. Với W=3, mỗi lane có 8×8=64 cặp; ba lane cho 192 check. Nhóm type có 16 pattern nên tổng là 8+192+16=216.

Các lane phát stimulus và chờ #1 song song. Nhóm nhỏ xong ở 4 ns, nhóm type ở 16 ns, nhóm rộng ở 64 ns. Top đợi cả ba done nên kết thúc ở 64 ns, không phải 84 hay 216 ns. Mỗi lane chỉ set done sau check cuối và group cộng lane_checks sau khi đủ done; đếm thời gian hay số process không thay cho phép đối chiếu chức năng.

Hai cấu hình width và một cấu hình type

Expected trước chạy: INTERFACE ADV CHECK widths=1,3 lanes=2,3 lane_checks=200 type_checks=16 total=216 time_ns=64. Parsed/simulated chỉ áp dụng source và tool đã ghi, không phải mọi width tùy ý hoặc bằng chứng tổng hợp.

Interface nâng cao: tham số, nhiều lane và kết nối theo tên

Expected tính trước chạy: INTERFACE ADV CHECK widths=1,3 lanes=2,3 lane_checks=200 type_checks=16 total=216 time_ns=64

tb.sv testbench
`timescale 1ns/1ps// Expected: W1/N2 ->8checks; W3/N3 ->192checks; type-parameter struct ->16checks; total216.interface sized_link #(parameter int W=4);  logic[W-1:0] a,b;  logic[W:0] total;  modport calc(input a,b,output total);endinterfacemodule sum_bank #(parameter int N=2)(sized_link.calc links[N]);  for(genvar k=0;k<N;k++)begin:units    assign links[k].total={1'b0,links[k].a}+{1'b0,links[k].b};  endendmodulemodule interface_group #(parameter int W=1,N=2)(output bit done,output int checks);  localparam int D=1<<W;  sized_link#(.W(W)) links[N]();  sum_bank#(.N(N)) dut(.links);  bit[N-1:0] lane_done='0;  int lane_checks[N];  for(genvar k=0;k<N;k++)begin:lanes    initial begin      for(int a=0;a<D;a++)for(int b=0;b<D;b++)begin        links[k].a=W'(a);links[k].b=W'((b+k)%D);        #1;        if(int'(links[k].total)!=a+(b+k)%D)$fatal(1,"parameterized interface lane oracle");        lane_checks[k]++;      end      if(lane_checks[k]!=D*D)$fatal(1,"lane accounting");      lane_done[k]=1;    end  end  initial begin    checks=0;wait(&lane_done);    for(int k=0;k<N;k++)checks+=lane_checks[k];    if(checks!=N*D*D)$fatal(1,"group accounting");done=1;  endendmodulemodule copy_word #(parameter type T=logic[3:0])(input T data,output T result);  assign result=data;endmodulemodule type_group(output bit done,output int checks);  typedef struct packed {bit valid;bit[2:0] tag;} Token;  Token data,result;  copy_word#(.T(Token)) dut(.*);  initial begin    checks=0;    for(int value=0;value<16;value++)begin      data=Token'(value);#1;      if(result!==data||result.valid!=((value>>3)&1)||int'(result.tag)!=(value&7))$fatal(1,"module type parameter oracle");      checks++;    end    done=1;  endendmodulemodule tb_interface_adv;  bit small_done,wide_done,type_done;  int small_checks,wide_checks,type_checks;  interface_group#(.W(1),.N(2)) edge_group(small_done,small_checks);  interface_group#(.W(3),.N(3)) wide_group(wide_done,wide_checks);  type_group word_group(type_done,type_checks);  initial begin    wait(small_done&&wide_done&&type_done);    if(small_checks!=8||wide_checks!=192||type_checks!=16||$time!=64)$fatal(1,"advanced interface accounting");    $display("INTERFACE ADV CHECK widths=1,3 lanes=2,3 lane_checks=200 type_checks=16 total=216 time_ns=64");$finish;  endendmodule

Đã kiểm chứng thế nào

  • Phân tích và elaborate theo LRM (pyslang 12.1.0)
  • Mô phỏng, exit 0 và output khớp kỳ vọng tính tay (Verilator 5.050, 2-state)

Output phải khớp: INTERFACE ADV CHECK widths=1,3 lanes=2,3 lane_checks=200 type_checks=16 total=216 time_ns=64

Verilator 5.050/2-state; chỉ source/cấu hình đã chạy. Không claim X/Z, timing vật lý, synthesis, UVM hay mọi parameter specialization.

Oracle nguyên bản theo input vòng lặp; fault probes giữ riêng, không làm yếu checker.

Các kiểm tra chạy lúc xây dựng nội dung (2026-11-06), không chạy trong trình duyệt. Kỳ vọng được tính tay trước khi chạy.

Hai mutant đã giữ checker: đọc b của lane 0 cho các lane khác và đảo dữ liệu ở module copy. Probe runtime index là lỗi parser riêng. Khi thêm W=0 hoặc N=0, phải định nghĩa hợp đồng mới và kiểm tool, không suy từ hai cấu hình dương đã chạy.

[5]

Tự kiểm tra

Tự kiểm tra: interface-adv

  1. Tổng số phép kiểm ở hai bank và nhóm type là bao nhiêu?
  2. Ba nhóm chạy song song trong main. Thời điểm kết thúc dự kiến là?
  3. Cách nào được main dùng để truy cập từng instance trong interface array?
  4. dut(.links) trong main tương đương với cách viết nào?
  5. Override .T(Token) của copy_word thay điều gì?

Đếm specialization, instance rồi mới đếm dữ liệu

Khi debug một bank, kiểm W/N ở nơi tạo instance, mapping từng lane và formal/actual của .name hay .*. Sau đó so kiểu của module nhận dữ liệu và oracle của từng lane. Chạy lại biên nhỏ, dữ liệu phân biệt lane và điều kiện hoàn tất giúp tìm lỗi mà một ví dụ “compile được” chưa phát hiện.

Nguồn và mức độ đối chiếu

  1. SystemVerilog Interface Parameters — bản chụp resources.zip, Basic Syntax; Overriding the Default at Instantiation; Connecting a Parameterized Interface to a Design. Trích dẫn; nguồn đã đọc các mục liên quan.
  2. SystemVerilog Interface Array — bản chụp resources.zip, Declaring an Interface Array; Connecting Individual Array Elements to a Design. Trích dẫn; nguồn đã đọc các mục liên quan.
  3. SystemVerilog Implicit Port Connections — bản chụp resources.zip, The .name Shorthand; The .* Wildcard. Trích dẫn; nguồn đã đọc các mục liên quan.
  4. SystemVerilog Module Type Parameters — bản chụp resources.zip, Declaring a Type Parameter; Overriding a Type Parameter at Instantiation. Trích dẫn; nguồn đã đọc các mục liên quan.
  5. Thí nghiệm nguyên bản ex.b4.interface-adv, Source, self-checks, verification.json và fault probes. Trích dẫn; nguồn đã đọc toàn văn.
  6. IEEE 1800-2023 SystemVerilog LRM, Interfaces, modports, parameterization and virtual interfaces — chưa đối chiếu LRM. Chưa đối chiếu với văn bản nguồn (chưa mở).
  7. SystemVerilog Implicit Port Connections — bản chụp resources.zip, The `.name` Shorthand; The `.*` Wildcard. Trích dẫn; nguồn đã đọc các mục liên quan.