【问题标题】:Verifying single port RAM using Verilog使用 Verilog 验证单端口 RAM
【发布时间】:2021-11-05 14:28:51
【问题描述】:

我已经为单个 PORT SRAM 编写了一个 Verilog 测试平台,在地址 i 进行写操作,并在 i-1 地址连续读取它。下面是这样的

task write_read;
integer i;
begin
 for (i=1,i<=20,i=i+1) begin
  write_mode(i,$urandom);  // i= address, $urandom=data
  read_mode(i-1);  //i-1 address
 end
end
endtask

PS:write_mode、read_mode 是设置 wen、cs 和一些扫描模式引脚以及延迟的任务。

我在 Verdi 波形中直观地看到了正确的读写操作。但是,我想验证我正在写入的地址处的数据与我从日志文件中读取的地址处的数据相同。如果它们不匹配,它应该显示一个错误。 我不确定如何在代码中实现这一点。当我有数百个编译器时,我无法进入所有编译器路径,打开它们的波形文件并手动检查读写操作。

我尝试为特定地址位置存储 $urandom 数据,但它会在每个迭代周期中被覆盖。我可以使用函数返回 $urandom 值,但是我的环境包含延迟,所以我不能使用函数。

简而言之,我正在寻找关于内存验证的 Verilog 代码帮助,而无需转储波形。 有人可以帮忙吗?请告诉我是否需要更多详细信息

谢谢

【问题讨论】:

标签: memory verilog test-bench


【解决方案1】:

使用 Kristen 框架,您可以生成一些可供重用的测试台代码。下面是来自该测试平台的一个名为 test_support.vh 的文件。该文件包含显示错误和计算错误的函数。我建议您在比较内存位置时使用 === 或 !== ,因为未定义的信号可能会无意中匹配。在测试结束时,您调用 display_test_final_status,这将在日志文件中为您创建一个总体测试报告。完全回归完成后,您现在可以在日志文件上运行 grep 以查找 ERROR,它会以一致的方式显示任何失败的内容。

我有一个主要的 Perl 回归脚本,它运行我的所有测试并自动执行 grep 并发送成功或失败的电子邮件。

在每个测试的基础上,您需要设置一些定义来指示测试名称,以及您是否希望错误立即停止测试。

祝你好运。

// -----------------------------------------------------------
// test_support.vh                                         
// Generated file specifies which numerical test cases to run.                 
// Kristen Software License - Version 1.0 - January 1st, 2019                  
//                                                                             
// Permission is hereby granted, free of charge, to any person or organization 
// obtaining a copy of the software and accompanying documentation covered by  
// this license (the "Software") to use, reproduce, display, distribute,     
// execute, and transmit the Software, and to prepare derivative works of the  
// Software, and to permit third-parties to whom the Software is furnished to  
// do so, all subject to the following:                                        
//                                                                             
// The copyright notices in the Software and this entire statement, including  
// the above license grant, this restriction and the following disclaimer,     
// must be included in all copies of the Software, in whole or in part, and    
// all derivative works of the Software, unless such copies or derivative      
// works are solely in the form of machine-executable object code generated by 
// a source language processor.                                                
//                                                                             
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,    
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT   
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE   
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.                                                   
                                                                               
// GENERATED FILE - DO NOT MODIFY THIS FILE MANUALLY.                          
// -----------------------------------------------------------

logic[63:0] error_count = 0;
logic[63:0] lcl_error_count = 0;
logic bool_quick_mode = 0;
logic[511:0] support_test_passes;
logic[15:0] support_test_fails [0:511];
logic[511:0] support_test_was_run;
task test_init;
error_count = 0;     
lcl_error_count = 0; 
endtask

task display_test_begin_status;
    begin 
        
        `ifdef QUICK_MODE
        bool_quick_mode = 1;
        `endif

        $display("=========================================================================");
        $display("| Test: %s    QUICK_MODE = %s", `TEST_NAME_STR, bool_quick_mode ? "ON" : "OFF");
        $display("| VERBOSE = %0d", `VERBOSE);
        $display("=========================================================================");
    end
endtask

task display_test_start;
    input[31:0] test_id;
    input string test_description;

    begin
        lcl_error_count = 0;
        $display("=========================================================================");
        $display("%0t: Test %0d : %s.", $time, test_id, test_description);
        $display("=========================================================================");
    end
endtask

task display_test_end;
    input[31:0] test_id;
    begin
        $display("=========================================================================");
        $display("%0t: Test %0d Complete with %0d ERRORS.", $time, test_id, lcl_error_count);
        $display("=========================================================================");
        support_test_was_run[test_id] = 1'b1;
        if (lcl_error_count == 0) 
            support_test_passes[test_id] = 1'b1;
        else
            support_test_fails[test_id] = lcl_error_count;
    end
endtask

task display_error_inc;
    input string error_description;
    begin
       error_count++;
       lcl_error_count++;
       //$display("=========================================================================");
       $display("%0t: ERROR:  %s : error_count: %0d",$time, error_description, error_count );
       //$display("=========================================================================");
       `ifdef TEST_STOP_ON_ERROR
        if (error_count >= `TEST_STOP_ON_ERROR_LIMIT) begin
            $display("%0t, Stopping on error count = %d, %m", $time, error_count); 
            $finish();
        end
       `endif
    end
endtask

task display_test_final_status;
    //input string testname;
    begin
       $display("=========================================================================");
       $display("%0t: Test %s %s with %0d ERRORS",$time, `TEST_NAME_STR, error_count > 0 ? "FAILS" : "PASSES",error_count);
       $display("=========================================================================");
       if (error_count !== 'h0)
           begin
              $display("Test failures:");
              for (int err_fail_cnt = 0;err_fail_cnt < 512; err_fail_cnt = err_fail_cnt + 1)
                  begin
                      if (support_test_was_run[err_fail_cnt] == 1'b1 && support_test_passes[err_fail_cnt] != 1'b1)
                          begin
                             $display("Test %d, fails with %d errors", err_fail_cnt, support_test_fails[err_fail_cnt]);
                          end
                  end
           end
    end
endtask

task display_no_test_found;
    input[31:0] test_id;
    input string test_description;

    begin
        lcl_error_count = 0;
        $display("=========================================================================");
        $display("%0t: Test %0d : %s NOT FOUND SKIPPING.", $time, test_id, test_description);
        $display("=========================================================================");
    end
endtask

从 Kristen 生成的测试台中,当检测到 RAM 单元时,这就是运行的测试。我会将其视为一种算法解决方案,因为您必须生成几个向量字符串和将这些字符串转换为地址的附加函数。

xreg_v1_0_0_write_mpi_test 是写入 RAM 的任务,而 xreg_v1_0_0_read_mpi_test 是从 RAM 读取的任务。有一个比较任务 xreg_v1_0_0_register_compare_with_error 显然会产生错误。我也会在下面展示。

    // This is a casex snippet from the RAM test
   7'd2: begin
         // xreg_v1_0_0_mem_ack test
         $display("%0t, xreg_v1_0_0_mem_ack test", $time);
         oo_limit = get_entries_by_index(tc_index) > 256 ? 256 : get_entries_by_index(tc_index);  //Max number of entries for this test
         mm_step  = get_entry_offset(tc_index);
         nn_limit = perfect_by_datawidth(get_size_by_index(get_address_by_index(tc_index)),DATAPATH_WIDTH)/8; // Number of bytes for this entry
         nn_step = DATAPATH_WIDTH == 8  ? 1 : DATAPATH_WIDTH == 16 ? 2 : DATAPATH_WIDTH == 32 ? 4 : 8;
         for(oo = 0; oo < oo_limit; oo = oo + 1) // Move through each entry we will be testing
         begin
            for (nn = 0; nn < nn_limit; nn = nn + nn_step) // Walk through each access of a given entry
            begin
            test_address = get_address_by_index(tc_index) + (oo*mm_step) + nn;
            expected = oo[7:0] ^ nn[7:0] ^ mm_step[7:0] ^ nn_step[7:0]; 
        expected = {expected[6:0],expected[7],expected[2:0],expected[7:3],expected[5],expected[3],expected[1],expected[7],expected[0],expected[2],expected[6],expected[4],expected[7:0]};
            xreg_v1_0_0_write_mpi_test(aclk, test_address, expected);
            end
         end
         for(oo = 0; oo < oo_limit; oo = oo + 1) // Move through each entry we will be testing
         begin
            for (nn = 0; nn < nn_limit; nn = nn + nn_step) // Walk through each access of a given entry
            begin
            test_address = get_address_by_index(tc_index) + (oo*mm_step) + nn;
        mask = get_entry_mask_by_index(tc_index);
            expected = oo[7:0] ^ nn[7:0] ^ mm_step[7:0] ^ nn_step[7:0]; 
        expected = {expected[6:0],expected[7],expected[2:0],expected[7:3],expected[5],expected[3],expected[1],expected[7],expected[0],expected[2],expected[6],expected[4],expected[7:0]} & (get_entry_mask_by_index(tc_index) >> (nn*8));
            xreg_v1_0_0_read_mpi_test(aclk, test_address, result);
            xreg_v1_0_0_register_compare_with_error(test_address,expected,result);
            end
         end
    end

以下是一些参考任务,它们显示了已实现的写入、读取和比较。我认为其中一些是 System Verilog,因此您必须将其视为算法。

task automatic xreg_v1_0_0_read_mpi_test;
    ref logic clock;
    input  [`TEST_ADDR_WIDTH-1:0] address;
    output [`TEST_DATA_WIDTH-1:0] result;
    begin
        $display("ENTER >>> xreg_v1_0_0_read_mpi_test");
        repeat(1) @(posedge clock);
        `TEST_MPI_ADDR = address;
        `TEST_MPI_RD_REQ = 1'b1;
        `TEST_MPI_ENABLE = 1'b1;
        while (!`TEST_MPI_ACK) repeat(1) @(posedge clock);
        `TEST_MPI_ADDR = 'h0;
        `TEST_MPI_RD_REQ = 1'b0;
        `TEST_MPI_ENABLE = 1'b0;
        result = `TEST_MPI_RD_DATA;
        $display("WAITING ACK <<< xreg_v1_0_0_read_mpi_test");
        while (`TEST_MPI_ACK) repeat(1) @(posedge clock);
        $display("EXIT <<< xreg_v1_0_0_read_mpi_test");
    end
endtask

task automatic xreg_v1_0_0_write_mpi_test;
    ref logic clock;
    input  [`TEST_ADDR_WIDTH-1:0] address;
    input [`TEST_DATA_WIDTH-1:0]  data;
    begin
        $display("ENTER >>> xreg_v1_0_0_write_mpi_test");
        repeat(1) @(posedge clock);
        `TEST_MPI_ADDR = address;
        `TEST_MPI_WR_DATA = data;
        `TEST_MPI_WR_REQ = 1'b1;
        `TEST_MPI_ENABLE = 1'b1;
        while (!`TEST_MPI_ACK) repeat(1) @(posedge clock);
        `TEST_MPI_ADDR = 'h0;
        `TEST_MPI_WR_DATA = 'h0;
        `TEST_MPI_WR_REQ = 1'b0;
        `TEST_MPI_ENABLE = 1'b0;
        $display("WAITING ACK <<< xreg_v1_0_0_write_mpi_test");
        while (`TEST_MPI_ACK) repeat(1) @(posedge clock);
        $display("EXIT <<< xreg_v1_0_0_write_mpi_test");
    end
endtask

task automatic xreg_v1_0_0_register_compare_with_error;
    input [`TEST_ADDR_WIDTH-1:0] address;
    input [`TEST_DATA_WIDTH-1:0] expected;
    input [`TEST_DATA_WIDTH-1:0] result;
    begin
        $display("ENTER >>> xreg_v1_0_0_register_compare_with_error");
        if (expected !== result)
            begin 
            $display("%0t, Address = 0x%X", $time, address);
            $display("%0t, Expected 0x%X", $time, expected);
            $display("%0t, Read     0x%X", $time, result);
            display_error_inc("xreg_v1_0_0_rdconst_test: Mismatch between read and expected data.");
            end
        $display("EXIT <<< xreg_v1_0_0_register_compare_with_error");
    end
endtask

【讨论】:

  • 谢谢,但我认为您已经为我的次要问题提供了解决方案。我想要verilog中的东西来验证我正在写入的地址处的数据与我正在读取的地址处的数据相同。如果它们不匹配,它应该显示一个错误。我不确定如何在代码中实现这一点。
  • 我添加了更多内容来显示比较代码。不确定这是否是您在市场上的目的。 LMK。
【解决方案2】:

您可能希望首先将写入数据存储在队列中。然后在读取 SRAM 时将地址传递给队列并比较 queue_data_out 和 sram_data_out。如果比较失败,则打印 ERROR。 请参阅下面的伪代码。

task write_read;
integer i;
bit write_data_queue [$];
begin
 for (i=1,i<=20,i=i+1) begin
  write_mode(i,$urandom);  // i= address, $urandom=data

  write_data_queue.push_front($urandom);

  read_mode(i-1);  //i-1 address

  queue_data = write_data_queue.pop_front();

  if(queue_data != read_mode(i-1))
   $display("ERROR'\n")

 end
end
endtask

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    相关资源
    最近更新 更多