Tạo đồng hồ thích hợp cho các bài kiểm tra VHDL


7

Trong nhiều băng ghế thử nghiệm tôi thấy mô hình sau đây để tạo đồng hồ:

process
begin
    clk <= '0';
    wait for 10 NS;
    clk <= '1';
    wait for 10 NS;
end process;

Trong các trường hợp khác tôi thấy:

clk <= not clk after 10 ns;

Cái sau được cho là tốt hơn, bởi vì nó được lên lịch trước khi bất kỳ quá trình nào được thực thi, và do đó các tín hiệu được thay đổi đồng bộ sang cạnh clk được xử lý đúng cách. Các phần sau của LRM dường như có thể hỗ trợ lý thuyết này:

Trang 169: 12.6.4 Chu trình mô phỏng Chu trình mô phỏng bao gồm các bước sau:

  • b) Mỗi ​​tín hiệu rõ ràng hoạt động trong mô hình được cập nhật. (Kết quả là sự kiện có thể xảy ra trên các tín hiệu.)

Đây phải là tín hiệu có giá trị dự kiến ​​mới, chẳng hạn như tín hiệu bị trễ after.

  • d) Đối với mỗi quá trình P, nếu P hiện nhạy cảm với tín hiệu S và nếu một sự kiện đã xảy ra trên S trong chu kỳ mô phỏng này, thì P sẽ tiếp tục.

Đó sẽ là hầu hết logic được mô phỏng

  • e) Mỗi ​​quy trình không được đăng ký đã được nối lại trong chu trình mô phỏng hiện tại được thực hiện cho đến khi nó tạm dừng.

Và bây giờ tất cả các quy trình bị đình chỉ bởi một wait forđược thực hiện.

TL; DR:

  • afterphương pháp luôn luôn vượt trội so với wait forphương pháp?
  • Nó có giúp ngăn chặn các vấn đề đặt đồng bộ tín hiệu đầu vào không?

Tất nhiên giải pháp thích hợp là không thay đổi tín hiệu đầu vào cùng lúc với cạnh. Nhưng đây không phải là điểm của cuộc thảo luận này ở đây.
Karsten Becker

Bạn có ý nghĩa gì bởi "tín hiệu thay đổi đồng bộ thành clk"? o bạn có nghĩa là các tín hiệu cũng thay đổi ở bội số 10ns (thông qua wait fors) hoặc tín hiệu được kích hoạt bởi cạnh của clk và do đó là một chu kỳ delta sau này?
Martin Thompson

Ngoài ra, bạn có tham chiếu đến tuyên bố rằng afterphiên bản tốt hơn không? Tôi chưa bao giờ được dạy nó tốt hơn, tôi chỉ thích nó như một dòng duy nhất :)
Martin Thompson

Bạn có thể xem ví dụ này tại đây: edaplayground.com/x/J8_ Thực thể đồng hồ hiển thị hành vi xấu, thực thể clock2 dường như khắc phục nó, cho đến khi clock3 phá vỡ nó một lần nữa.
Karsten Becker

Và có, tôi có nghĩa là thay đổi đầu vào với cạnh tăng bằng cách sử dụng wait untilhoặc wait for.
Karsten Becker

Câu trả lời:


6

Trình giả lập thực sự không thể bị đổ lỗi vì đôi khi hoạt động như đồng hồ xảy ra ngay sau hoặc ngay trước khi thay đổi đầu vào, nếu bạn chỉ định cả hai clkvà đầu vào sử dụng wait for. Hỏi một phong cách là vượt trội hay kém hơn phong cách kia, theo một cách nào đó, là câu hỏi sai. Bạn cần chỉ định hành vi theo cách không mơ hồ, nếu bạn mong muốn một đầu ra xác định và không mơ hồ.

Những gì tôi đã làm trong nhiều năm và đã làm việc cho tôi khá hoàn hảo (đối với các thiết kế đồng bộ) là gán các đầu vào trước chúng bằng wait until rising_edge(clk)hoặc wait until falling_edge(clk). Làm thế nào bạn tạo ra clktrở nên không quan trọng. Đối với đơn giản testbenches các afterone-liner làm công việc độc đáo và ngắn gọn, nhưng không cung cấp sự linh hoạt của một processvới wait forhoặc wait untilbáo cáo.

Tôi có một thủ tục đơn giản đã phục vụ tốt cho tôi:

procedure wait_until_rising_edges(signal clk : in std_logic; n : in integer) is
begin
    for i in 1 to n loop
        wait until rising_edge(clk);
    end loop;
end procedure;

Mà tôi giữ trong một tb_pkg.vhdmà tôi luôn luôn usetrong testbenches.

Một ví dụ sử dụng có thể là:

some_stim_proc : process
begin
    some_signal <= '0';
    wait_until_rising_edges(clk,900);
    some_signal <= '1';
    wait_until_rising_edges(clk,100);
end process;

Một số nhà thiết kế gán tín hiệu kích thích của họ ở cạnh đối diện với những gì đơn vị được thử nghiệm nhạy cảm. Cá nhân tôi không thích làm điều này bởi vì đó không phải là phần còn lại của mạch sẽ mô phỏng, nơi tín hiệu thay đổi ở cạnh 'bộ kích hoạt'. Nhưng chắc chắn không có gì sai với cách tiếp cận đó. Các thủ tục trên cũng có thể được sử dụng cho nó.


Tôi thích thay đổi các tín hiệu ở cạnh đối diện, bởi vì nó gần với hầu hết các bảng dữ liệu trông như thế nào. Cũng hoàn toàn từ góc độ quang học, nó có thể trông giống như thể đầu ra dựa trên các tín hiệu được áp dụng sau khi đồng hồ được xử lý. Nhưng tôi thực sự thích chức năng nhỏ của bạn. Ý tưởng gọn gàng.
Karsten Becker

3

Tôi đã đọc chủ đề này từ lâu, nhưng không có thời gian để trả lời cho đến khi nghỉ hưu.

Thật thú vị khi thấy cách những người khác nhau tạo ra đồng hồ testbench của họ và các tiêu chí được sử dụng để đánh giá mức độ "tốt" của họ. Đối với tôi, tôi nhìn vào mã và hỏi: nó linh hoạt đến mức nào? Làm thế nào là khó để sửa đổi nếu tốc độ đồng hồ thay đổi? Hoặc nếu tôi muốn sử dụng testbench trong mô phỏng cổng chú thích ngược?

Vì vậy, đây là cách rất dài của tôi để đảm bảo sửa đổi dễ dàng và testbench rất linh hoạt:

Tốc độ đồng hồ, thời gian thiết lập dữ liệu và thời gian giữ dữ liệu phải được xác định là tổng quát hoặc hằng số, ví dụ:

generic (
  CLK_CYCLE_TIME  : time     := 10 ns;
  CLK_HIGH_TIME   : time     := 5 ns;
  DATA_SETUP_TIME : time     := 4 ns;
  DATA_HOLD_TIME  : time     := 4 ns;

Sau đó, tạo các cạnh cho testbench để sử dụng để cung cấp dữ liệu, xóa dữ liệu và cho "sự kiện" tăng đồng hồ và đồng hồ rơi:

begin
  -- generate events for data setup, clock rise, data hold and clock fall times:
  data_setup_event <= transport not data_setup_event after CLK_CYCLE_TIME;
  clk_rise_event   <= transport     data_setup_event after DATA_SETUP_TIME;
  clk_fall_event   <= transport     clk_rise_event   after CLK_HIGH_TIME;
  data_hold_event  <= transport     clk_rise_event   after DATA_HOLD_TIME;

  -- actual clock signal generation:
  clk_gen_p:process is
  begin
    wait on clk_rise_event;
    clk <= '1';
    wait on clk_fall_event;
    clk <= '0';
  end process;

Kích thích sau đó có thể được áp dụng (đi bộ một đầu vào với giao thức vld / rdy được sử dụng cho ví dụ này):

  apply_stimulus_p : process is
    variable v_walking_one : std_logic_vector(i_dat'range) := std_logic_vector(to_unsigned(1, i_dat'length));
  begin
    wait on data_setup_event;
    rst <= '1';
    wait on data_hold_event;
    rst <= '0';

    for i in 0 to TCOUNT-1 loop
      wait on data_setup_event;
      i_vld       <= '1';
      i_dat       <= v_walking_one;
      i_side_pipe <= std_logic_vector(to_unsigned(i, i_side_pipe'length));

      wait on clk_rise_event;
      while i_rdy /= '1' loop
        wait on clk_rise_event;
      end loop;

      wait on data_hold_event;
      i_vld       <= '0';
      i_dat       <= (others => 'X');
      i_side_pipe <= (others => 'X');

      -- rotate the walking one:
      v_walking_one := rotate_right(v_walking_one);
    end loop;
    wait;
  end process;

Tất cả các chuyển đổi dữ liệu đầu vào đều cách xa cạnh đồng hồ đang hoạt động (tăng). Nó thậm chí còn bắt lỗi như đảo ngược thêm trong đồng hồ.

Đó là thanh lịch, dễ đọc, linh hoạt và dễ dàng sửa đổi. Không có chữ nào phụ thuộc vào tần số xung nhịp hoặc độ rộng dữ liệu. Đây là cách tôi sẽ viết nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.