Chủ Nhật, 22 tháng 9, 2019

[UVM] Bài 7 - Mô tả về các checker của môi trường

Bài viết này sẽ mô tả về các checker được sử dụng trong môi trường UVM là "apb_protocol_checker" và "uart_protocol_checker".
Các bài viết cùng chủ đề có thể tìm thấy ở đây.

1) Tổng quan về checker
Checker là một thành phần hỗ trợ kiểm tra một vài chức năng nào đó của DUT hoặc các thành phần khác trong môi trường test. Checker có nhiệm vụ giám sát điểm cần kiểm tra và đưa ra các cảnh báo nếu điểm cần kiểm tra vi phạm các điều kiện nhất định.
Với ý nghĩa giám sát và kiểm tra, tùy vào từng trường hợp, chức nằng của checker có thể là một thành phần độc lập viết bằng System Verilog đơn thuần hoặc tích hợp trong Monitor hay Scoreboard. 
Như vậy, việc có hay không có checker là một tùy chọn của người build môi trường.
Trong thực tế, đối với các thiết kế không thể được tiếp cận sâu hơn vào RTL code mà chỉ có thể test (kiểm tra) thông qua các port (input và output), thì việc thực hiện một thành phần checker độc lập, phần lớn là không cần thiết vì chức năng giám sát và kiểm tra được tích hợp vào Monitor và Scoreboard. Ví dụ, một thiết kế của một nhà cung cấp lõi IP được mua về để sử dụng nhưng bị đóng gói (mã hóa) RTL code.
Đối với các thiết kế tự phát triển, ví dụ như lõi IP UART-APB trong loạt bài viết này, checker có thể giúp giám sát và kiểm tra hoạt động của các tín hiệu nội nằm bên sâu bên trong thiết kế thông qua việc sự dụng đường dẫn cấu trúc, ví dụ như:
testTop.dut_top.uart_0.receiver.<tên tín hiệu>
Trong ví dụ trên, chúng ta lấy một tín hiệu chứa trong instance "receiver" của "uart_0".
Việc sử dụng checker giúp kiểm tra và xác thực hoạt động chính xác của các tín hiệu nội trong các điều kiện test. Bên cạnh đó, checker có thể hỗ trợ phát hiện lỗi bên trong thiết kế một cách hiệu quả.
Hình 1: Minh họa kết nối tín hiệu cần kiểm tra đến checker
Đối với môi trường này, hai checker được sử dụng để kiểm tra một số điều kiện lỗi trên giao thức APB và UART. Việc kiểm tra này có thể thực hiện bằng cách cách khác. Tuy nhiên, các bạn hãy xem đây như một tham khảo về việc thực hiện checker.
2) Mô tả các thực hiện checker
Trong môi trường, các checker được đặt trong thư mục "UvmEnvUartApb/checker". Mỗi checker gồm 2 phần:
  • Lõi checker: là một module thực thi giám sát và kiểm tra chức năng các input của nó. Các input là các điểm cần test.
    • <tên checker>.sv
  • Kết nối checker (Top checker): là một module gọi (instance) lõi checker và kết nối các điểm cần check của môi trường đến lõi checker.
    • <tên checker>_top.sv
Trong đó, Top checker sẽ được instance ở file top của môi trường, file testTop.sv, và cùng cấp với instance của top DUT là module dut_top.
Hình 2: Vị trí của checker trong môi trường
Môi trường hiện tại có hai checker là:
  • APB protocol checker
  • UART protocol checker
Hai checker này được gọi thành 4 instance trong môi trường là:
  • APB protocol checker có 2 instance:
    • apb0_chk nối với APB interface giữa Driver của coApbMasterAgentTx và uart_0 trong dut_top
    • apb1_chk nối với APB interface giữa Driver của coApbMasterAgentRx và uart_1 trong dut_top
  • UART protocol checker có 2 instance:
    • uart0_chk nối với:
      • APB interface giữa Driver của coApbMasterAgentTx và uart_0 trong dut_top
      • đường truyền UART uart_0to1, đường kết nối từ port truyền nối tiếp uart_tx của uart_0 đến port nhận nối tiếp của uart_1
    • apb1_chk nối với:
      • APB interface giữa Driver của coApbMasterAgentRx và uart_1 trong dut_top
      • đường truyền UART uart_1to0, đường kết nối từ port truyền nối tiếp uart_tx của uart_1 đến port nhận nối tiếp của uart_0
Hình 3: Vị trí các checker apb_protocol_checker_top và uart_protocol_checker_top trong đường bao số 2
Mỗi checker sẽ có 3 cấp cảnh báo lỗi, gọi là SEVERITY, là:
  • ERROR - Lỗi phải sửa
  • WARNING - Cảnh báo phải kiểm tra
  • INFO - Thông tin giúp việc debug và trace dễ dàng hơn
Mỗi cấp độ cảnh báo lỗi sẽ được quy định có hiển thị hay không thông qua các `define sau khi chạy mô phỏng:
  • APB_ERROR_SEVERITY - Chỉ hiện thị các thông điệp ERROR
  • APB_WARNING_SEVERITY - Chỉ hiện thị các thông điệp ERROR và WARNING
  • APB_INFO_SEVERITY - Hiện thị các thông điệp ERROR, WARNING và INFO
Mặc định là APB_ERROR_SEVERITY, nghĩa là chỉ có các thông điệp ở mức ERROR được hiển thị nếu có.
2) APB protocol checker
APB protocol checker là checker giám sát kiểm tra giao thức APB giữa Driver và DUT (dut_top).
Checker này sẽ dùng một biến trạng thái apb_state[1:0] để giám sát trạng thái SETUP và ACCESS của một APB transfer.
Hình 4: Các trạng thái hoạt động của APB protocol, tham khảo mục Operating states trong tài liệu  AMBA3 APB Protocol specification của ARM 
Máy trạng thái của checker hoạt động như sau:
  • Luôn giữ trong trạng thái IDLE khi reset đang xảy ra, preset_n=0
  • Chuyển từ IDLE sang trạng thái giám sát SETUP, SETUP_CHECK, ngay khi hết reset, preset_n=1
  • Chuyển từ trạng thái SETUP_CHECK sang trạng thái ACCESS_CHECK khi psel=1
  • Chuyển từ trạng thái ACCESS_CHECK về SETUP_CHECK khi pready=1

Hình 5: Máy trạng thái (FSM) của APB protocol checker
Ví dụ 1 - Máy trạng thái của APB protocol checker
always @ (posedge pclk, negedge preset_n) begin
    if (~preset_n) apb_state <= `DLYCHK IDLE;
    else apb_state[1:0] <= `DLYCHK apb_next_state[1:0];
  end
  always @ (*) begin
    case (apb_state[1:0])
      IDLE: begin
        apb_next_state[1:0] = SETUP_CHECK;
      end
      SETUP_CHECK: begin
        if (psel)
          apb_next_state[1:0] = ACCESS_CHECK;
        else
          apb_next_state[1:0] = SETUP_CHECK;
      end
      ACCESS_CHECK: begin
        if (pready)
          apb_next_state[1:0] = SETUP_CHECK;
        else
          apb_next_state[1:0] = ACCESS_CHECK;
      end
      default: begin
        apb_next_state[1:0] = apb_state[1:0];
      end
    endcase
  end
Checker sẽ báo lỗi trong hai trường hợp sau:
  • [APB_ERROR] psel penable cùng tích cực trong trạng thái SETUP_CHECK
  • [APB_ERROR] penable không tích cực trong trạng thái ACCESS_CHECK
Ví dụ 2 - Code báo lỗi APB protocol
always @ (posedge pclk) begin
    case (apb_state[1:0])
      SETUP_CHECK: begin
        if (psel) begin
          //Check 1
          if (penable) begin
            $display ("[APB_ERROR][%s][%t] PSEL and PENABLE are asserted 1 in the SETUP phase\n", INST_NAME, $time);
          end
        end
      end
      ACCESS_CHECK: begin
        //Check 1
        if (~penable) begin
          $display ("[APB_ERROR][%s][%t] PENABLE is not asserted 1 in the ACCESS phase\n", INST_NAME, $time);
        end
      end
    endcase
  end
Bên cạnh đó, checker còn hỗ trợ cảnh báo giá trị "x" và "z" trên các tín hiệu APB trong khi hoạt động, bao gồm:
  1. [APB_ERROR] Báo lỗi psel bị "x" hoặc "z"
  2. [APB_ERROR] Báo lỗi penable bị "x" hoặc "z"
  3. [APB_ERROR] Báo lỗi pwrite bị "x" hoặc "z"
  4. [APB_ERROR] Báo lỗi paddr bị "x" hoặc "z"
  5. [APB_ERROR] Báo lỗi pstrb bị "x" hoặc "z" trong Write transfer
  6. [APB_ERROR] Báo lỗi pready bị "x" hoặc "z"
  7. [APB_ERROR] Báo lỗi pslverr bị "x" hoặc "z"
  8. [APB_WARNING] Báo lỗi pwdata bị "x" hoặc "z"
  9. [APB_WARNING] Báo lỗi prdata bị "x" hoặc "z"
Ví dụ 3 - Code kiểm tra giá trị "x" và "z" trên các tín hiệu APB
  assign paddr_or  = |paddr;
  assign pstrb_or  = |pstrb;
  assign pwdata_or = |pwdata;
  assign prdata_or = |prdata;
  always @ (posedge pclk) begin
    if (preset_n & psel) begin
      //Check 2
      case (penable)
        1'bx: $display ("[APB_ERROR][%s][%t] PENABLE is x\n", INST_NAME, $time);
        1'bz: $display ("[APB_ERROR][%s][%t] PENABLE is z\n", INST_NAME, $time);
      endcase
      //Check 3
      case (pwrite)
        1'bx: $display ("[APB_ERROR][%s][%t] PWRITE is x\n", INST_NAME, $time);
        1'bz: $display ("[APB_ERROR][%s][%t] PWRITE is z\n", INST_NAME, $time);
      endcase
      //Check 4
      case (paddr_or)
        1'bx: $display ("[APB_ERROR][%s][%t] PADDR is x\n", INST_NAME, $time);
        1'bz: $display ("[APB_ERROR][%s][%t] PADDR is z\n", INST_NAME, $time);
      endcase
      //Check 5
      if (pwrite) begin
        case (pstrb_or)
          1'bx: $display ("[APB_ERROR][%s][%t] PSTRB is x\n", INST_NAME, $time);
          1'bz: $display ("[APB_ERROR][%s][%t] PSTRB is z\n", INST_NAME, $time);
        endcase
      end
      //Check 7
      case (pready)
        1'bx: $display ("[APB_ERROR][%s][%t] PREADY is x\n", INST_NAME, $time);
        1'bz: $display ("[APB_ERROR][%s][%t] PREADY is z\n", INST_NAME, $time);
      endcase
      //Check 8
      if (pready) begin
        case (pslverr)
          1'bx: $display ("[APB_ERROR][%s][%t] PSLVERR is x\n", INST_NAME, $time);
          1'bz: $display ("[APB_ERROR][%s][%t] PSLVERR is z\n", INST_NAME, $time);
        endcase
      end
      //Check 6
      `ifdef APB_WARNING_SEVERITY
        if (pwrite) begin
          case (pwdata_or)
            1'bx: $display ("[APB_WARNING][%s][%t] PWDATA is x\n", INST_NAME, $time);
            1'bz: $display ("[APB_WARNING][%s][%t] PWDATA is z\n", INST_NAME, $time);
          endcase
        end
        //Check 9
        if (pready) begin
          case (prdata_or)
            1'bx: $display ("[APB_WARNING][%s][%t] PRDATA is x\n", INST_NAME, $time);
            1'bz: $display ("[APB_WARNING][%s][%t] PRDATA is z\n", INST_NAME, $time);
          endcase
        end
      `endif
    end
    //Check 10
    case (psel)
      1'bx: $display ("[APB_ERROR][%s][%t] PSEL is x\n", INST_NAME, $time);
      1'bz: $display ("[APB_ERROR][%s][%t] PSEL is z\n", INST_NAME, $time);
    endcase
  end
Cuối cùng, checker hỗ trợ hiển thị thông tin các Read/Write transfer trên các APB interface hỗ trợ bedug khi cần.
Ví dụ 4 - Code in thông tin read/write transfer của APB
always @ (posedge pclk) begin
  if ((apb_state[1:0] == ACCESS_CHECK) & pready) begin
          if (pwrite) begin
            $display ("[APB_INFO][%s][%t] A WRITE transaction PADDR=%8h PWDATA=%8h PSTRB=%4h PSLVERR=%b\n", INST_NAME, $time, paddr, pwdata, pstrb, pslverr);
          end
          else begin
         $display ("[APB_INFO][%s][%t] A READ transaction PADDR=%8h PRDATA=%h PSLVERR=%b\n", INST_NAME, $time, paddr, prdata, pslverr);
          end
        end
      end
    end

3) UART protocol checker
UART protocol checker là checker giám sát độ rộng bit truyền trên đường truyền UART, từ chân TX của UART này đến chân RX của UART kia. Để thực hiện được điều này, checker sẽ cần biết các thông tin cấu hình liên quan đến khung truyền UART. Vì vậy, ngoài UART interface, checker còn được kết nối đến APB interface để cập nhật thông tin cấu hình của DUT khi thông tin này bị thay đổi.
Các thông tin liên quan đến khung truyền UART bao gồm:
  • UART có được enable hay không? bit 0 của thanh ghi SE, địa chỉ offset 0x04
  • UART có truyền parity hay không? bit 1 của thanh ghi SE, địa chỉ offset 0x04
  • Tốc độ baud được thiết lập là bao nhiêu? thanh ghi BR, địa chỉ offset 0x08
Ví dụ 5 - Code lưu lại thông tin cấu hình của UART
  assign reg_sel = psel & penable & pready;
  assign reg_we = pwrite & reg_sel & (&pstrb[3:0]);
  assign se_we  = reg_we & (paddr[4:0] == 5'b0_0100);
  assign br_we  = reg_we & (paddr[4:0] == 5'b0_1000);
  //Enable register
  always @(posedge pclk)begin
    if(~preset_n) apb_chk_se_info[2:0] <= `DLYCHK 3'd0;
    else if(se_we) apb_chk_se_info[2:0] <= `DLYCHK pwdata[2:0];
  end
  //Baud rate register
  always @(posedge pclk)begin
    if(~preset_n) apb_chk_br_info[7:0] <= `DLYCHK 8'd0;
    else if(br_we) apb_chk_br_info[7:0] <= `DLYCHK pwdata[7:0];
  end
Về phía đường truyền UART, một bit trạng thái được sử dụng để giám sát khi nào đường truyền cần được kiểm tra. Bit này được tích cực 1 nếu phát hiện một cạnh xuống trên đường truyền UART và xóa khi kết thúc của bit STOP.
Ví dụ 6 - Code của FSM
// Detect the falling edge
  always @ (posedge pclk, negedge preset_n) begin
    if (~preset_n) uart_net_sync <= `DLYCHK 1'b1;
    else uart_net_sync <= `DLYCHK uart_net;
  end
  assign uart_net_rising  = ~uart_net_sync & uart_net;
  assign uart_net_falling = uart_net_sync & ~uart_net;
  //Select the number of bits of UART frame
  always @ (posedge pclk, negedge preset_n) begin
    if (~preset_n)
      chk_uart_state <= `DLYCHK CHK_UART_IDLE;
    else if (frame_start)
      chk_uart_state <= `DLYCHK CHK_UART_CHECK;
    else if (frame_end)
      chk_uart_state <= `DLYCHK CHK_UART_IDLE;
  end
  assign frame_end   = next_bit & apb_chk_se_info[0]
                       & (bit_count[3:0] == uart_bit_num[3:0]);
  assign frame_start = uart_net_falling & (~chk_uart_state | frame_end);
Việc phát hiện START và STOP bit của checker hoạt động độc lập với DUT. Một bộ đếm số bit truyền, bit_count, trong khung truyền UART sẽ giám sát khi nào một khung truyền UART được hoàn thành.
Ví dụ 7 - Code của bộ đếm số bit trong truyền UART
assign bit_count_clr = frame_end;
assign bit_count_inc = (chk_uart_state == CHK_UART_CHECK) & next_bit;
  always @ (posedge pclk, negedge preset_n) begin
    if (~preset_n)
      bit_count[3:0] <= `DLYCHK 4'd0;
    else if (bit_count_clr)
      bit_count[3:0] <= `DLYCHK 4'd0;
    else if (bit_count_inc)
      bit_count[3:0] <= `DLYCHK bit_count[3:0] + 1'b1;
  end
  // Parity:    START - 8 DATA - 1 Parity - 1 STOP
  // No parity: START - 8 DATA - 1 STOP
  assign uart_bit_num[3:0] = apb_chk_se_info[1]? 4'd10: 4'd9;
  //UART bit width
  assign bit_width[12:0] = 16*(apb_chk_br_info[7:0] + 1'b1) - 1'b1;
  //Bit width counter
 assign next_bit = (width_count[12:0] == bit_width[12:0]);
Nguyên tắc kiểm tra độ rộng bit như sau:
  • Giá trị bit sẽ được lưu lại trong 1 biến, bitCheckedValue, khi bắt đầu mỗi bit. Một bộ đếm độ rộng bit, width_count, được sử dụng để giám sát khi nào kết thúc 1 bit. Bộ đếm độ rộng bit đếm số xung clock theo công thức baud rate mà DUT hỗ trợ.
  • Trong suốt qua trình kiểm tra, nếu giá trị trên đường truyền UART khác giá trị bit cần kiểm tra lưu trong  bitCheckedValue thì một lỗi sinh ra.

Ví dụ 8 - Code bộ đếm độ rộng bit
assign clr_width_count = next_bit;
assign inc_width_count = chk_uart_state;
always @ (posedge pclk, negedge preset_n) begin
    if (~preset_n) width_count[12:0] <= `DLYCHK 13'd0;
    else if (clr_width_count)
      width_count[12:0] <= `DLYCHK 13'd0;
    else if (inc_width_count)
      width_count[12:0] <= `DLYCHK width_count[12:0] + 1'b1;
  end
Ví dụ 9 - Code của phần kiểm tra giá trị bit truyền ứng với độ rộng bit đã thiết lập
always @ (posedge pclk) begin
    if (preset_n & next_bit)
      bitCheckedValue <= `DLYCHK uart_net;
  end
assign baud_rate_error = (bitCheckedValue != uart_net_sync) & chk_uart_state;
  always @ (posedge pclk, negedge preset_n) begin
    if (~preset_n) begin
      baud_rate_error_sync <= `DLYCHK 1'b0;
    end
    else begin
      baud_rate_error_sync <= `DLYCHK baud_rate_error;
    end
  end
  assign baud_rate_error_rising = ~baud_rate_error_sync & baud_rate_error;
  //
  always @ (posedge pclk, negedge preset_n) begin
    if (~preset_n) begin
      baud_rate_error_report <= `DLYCHK 1'b0;
    end
    else if (preset_n & baud_rate_error) begin
      baud_rate_error_report <= `DLYCHK 1'b1;
    end
  end
Cuối cùng, checker sẽ cảnh báo các lỗi sau đây:
  • Lỗi đường truyền UART bị "x" hoặc "z".
  • Lỗi nếu độ rộng bit bị vi phạm, vi phạm này xảy ra khi giá trị 1 bit bị thay đổi trước khi bit này đạt được độ rộng mong muốn.

Ví dụ 10 - Code của phần cảnh báo lỗi "x" và "z" trên đường truyền UART
always @ (posedge pclk) begin
    if (preset_n) begin
      case (uart_net)
        1'bx: $display ("[UART_ERROR][%t][%s] %s is x\n", $time, INST_NAME, INST_NET);
        1'bz: $display ("[UART_ERROR][%t][%s] %s is z\n", $time, INST_NAME, INST_NET);
      endcase
    end
  end
Ví dụ 11 - Code của phần cảnh báo lỗi độ rộng bit
always @ (posedge pclk, negedge preset_n) begin
    if (preset_n & baud_rate_error_rising) begin
      $display ("[UART_ERROR][%t][%s] %s Bit width is violated  \n-- Expected bit value: %b\n-- Actual bit value: %b", $time, INST_NAME, INST_NET, bitCheckedValue, uart_net_sync);
    end
  end

Chi tiết hơn các bạn hãy tải code của checker, chạy mô phỏng thử, xem waveform để phân tích.

Dữ liệu có thể tải:

Lịch sử cập nhật:
1/ 2019.09.22 - Tạo lần đầu

Danh sách tác giả:
1. Phạm Thanh Trâm
2. Nguyễn Sinh Tơn
3. Đoàn Đức Hoàng (email: hoangbk154@gmail.com)
4. Trương Công Hoàng Việt
5. Nguyễn Hùng Quân

0 bình luận:

Đăng nhận xét