在之前的内容中我们已经完成了monitor的搭建,在这一章节中我们就要开始搭建scoreboard了,理解scoreboard是如何从monitor中拿到数据并进行比较的。

scoreboard和ref_model的区别:

  • reference_model对于复杂的设计而言,就是需要单独做一个模型来模拟设计的部分,例如MCDF中的model就是模拟了设计中仲裁等功能;
  • scoreboard对于一些简单的设计而言,可以从memory中存放数据的一些功能就放到scoreboard中,拿到数据以后对数据进行归类,排序,整理和比较,如果排序和整理的过程比较复杂,那就会使用reference_model进行替换:
    • scoreboard需要同时具备扮演memory模型的能力:
      • 知道序列的位宽;
      • 首地址和结束的地址;
    • 这些信息可以放在ahb_config中。

5.1 ahb_config及test对信息的配置

  • 在Verilog中的地址是14位的,我们在TB中连接参数时的地址默认是十六位的,所以这里的首地址和结束的地址采用16位即可;
  • config中的首地址和结束的地址我们在test中对他们进行配置,这两个参数应当和设计中的位宽一致,如果放进来的是不在这个地址范围内的数据,那么memory是吃不进去的。也就是说scoreboard应该和design中的参数一致,scoreboard起到模拟design的作用。 ``systemverilogifndef AHB_RAM_CONFIG_SV `define AHB_RAM_CONFIG_SV

class ahb_ram_config extends uvm_object;

int seq_check_count; int seq_check_error;

int scb_check_count; int scb_check_error;

bit scb_enable = 1; bit cov_enable = 1;

bit [31:0] addr_start; bit [31:0] addr_end;

ahb_agent_configuration ahb_cfg; virtual ahb_ram_if vif; ahb_ram_rgm rgm;

`uvm_object_utils(ahb_ram_config)

// USER to specify the config items

function new (string name = “ahb_ram_config”); super.new(name); ahb_cfg = ahb_agent_configuration::type_id::create(“ahb_cfg”); endfunction : new

endclass

`endif // AHB_RAM_CONFIG_SV

  1. ```systemverilog
  2. `ifndef AHB_RAM_BASE_TEST_SV
  3. `define AHB_RAM_BASE_TEST_SV
  4. virtual class ahb_ram_base_test extends uvm_test;
  5. ahb_ram_config cfg;
  6. ahb_ram_env env;
  7. ahb_ram_rgm rgm;
  8. function new(string name = "ahb_ram_base_test", uvm_component parent);
  9. super.new(name, parent);
  10. endfunction
  11. function void build_phase(uvm_phase phase);
  12. super.build_phase(phase);
  13. rgm = ahb_ram_rgm::type_id::create("rgm");
  14. rgm.build();
  15. uvm_config_db#(ahb_ram_rgm)::set(this, "env", "rgm", rgm);
  16. cfg = ahb_ram_config::type_id::create("cfg");
  17. cfg.rgm = rgm;
  18. // do parameter configuration
  19. cfg.addr_start = 32'h0;
  20. cfg.addr_end = 32'h0000_ffff;
  21. if(!uvm_config_db#(virtual ahb_ram_if)::get(this,"","vif", cfg.vif)) begin
  22. `uvm_fatal("GETCFG","cannot get virtual interface from config DB")
  23. end
  24. uvm_config_db#(ahb_ram_config)::set(this, "env", "cfg", cfg);
  25. env = ahb_ram_env::type_id::create("env", this);
  26. endfunction
  27. function void connect_phase(uvm_phase phase);
  28. super.connect_phase(phase);
  29. endfunction
  30. task run_phase(uvm_phase phase);
  31. super.run_phase(phase);
  32. phase.phase_done.set_drain_time(this, 1us);
  33. phase.raise_objection(this);
  34. phase.drop_objection(this);
  35. endtask
  36. endclass
  37. `endif //AHB_RAM_BASE_TEST

5.2 scoreboard的编辑

5.2.1 读写数据采样及比较的实现

  • scoreboard是继承于subscriber的,所以我们在scoreboard中的write函数进行补充。
  • scoreboard和monitor都是env层次下的,所以他们的连接也发生在env的connect_phase中。
  • 接下来我们就需要考虑来自monitor的数据应该放到scoreboard中的什么地方?
    • 在scoreboard中声明一个32bit的关联数组用于存放数据,通过32位的无符号数来进行索引。 :::info
  1. 写入数据,首先需要判断进来的addr在不在我们之前定义的范围里面(is_trans_valid);
  2. 接下来需要考虑如何存储data和addr:
    1. 首先根据关键词burst_type,使用case语句来存放数据;
    2. 根据burst_type判断完成以后,接下来需要根据关键词hsize来进行判断,存放addr和data。对于SINGLE操作而言,它是只有一个数据的,所以我们将data数组中的第一个数据进行存放即可。
    3. 既然根据burst_type进行判断以后并使用hsize函数来存放addr和data,接下来我们就需要实现hsize对于数据的存放,通过这个函数将数据存放到mem中。
  3. 在smoke_test中我们也知道对于transaction而言,既有写操作也有读操作,因此我们需要将这两部分分开进行表达,在进行完成读操作以后就可以对写入的数据和读出来的数据进行比较:
    1. 读回来的数据也需要根据hsize来读取数据,这样读取到的数据是准确的,在读到数据以后带给出一个返回值即可;
    2. 如果在scoreboard中我们想要进行一个数据的比较,根据single传输数据的方式而言,我们可以对每一个数据进行比较而不需要采用burst的方式一起比较,那么check_data_with_burst这个函数我们可以声明成带有返回值的类型。 :::

      在使用函数的值的时候它是由返回值的,但是只return的话是不会打印信息的,所以我们需要使用if_else语句对返回值再进行一次判断,同时将打印的信息作为执行的语句。

  1. `ifndef AHB_RAM_SCOREBOARD_SV
  2. `define AHB_RAM_SCOREBOARD_SV
  3. class ahb_ram_scoreboard extends ahb_ram_subscriber;
  4. bit [31:0] mem [int unsigned];
  5. // Events of scoreboard
  6. `uvm_component_utils(ahb_ram_scoreboard)
  7. function new (string name = "ahb_ram_scoreboard", uvm_component parent);
  8. super.new(name, parent);
  9. endfunction
  10. function void build_phase(uvm_phase phase);
  11. super.build_phase(phase);
  12. endfunction
  13. task run_phase(uvm_phase phase);
  14. super.run_phase(phase);
  15. endtask
  16. virtual function void write(ahb_transaction tr);
  17. if(is_trans_valid(tr)) begin
  18. case(tr.xact_type)
  19. WRITE : store_data_with_burst(tr);
  20. READ : check_data_with_burst(tr);
  21. endcase
  22. end
  23. store_data_with_burst(tr);
  24. endfunction
  25. function is_trans_valid(ahb_transaction tr);
  26. if(tr.addr >= cfg.addr_start && tr.addr <= cfg.addr_end);
  27. return 1;
  28. endfunction
  29. function void store_data_with_burst(ahb_transaction tr);
  30. case(tr.burst_type)
  31. SINGLE : begin
  32. store_data_with_hsize(tr, 0);
  33. end
  34. INCR : begin `uvm_error("TYPEERR", "burst type not supported yet") end
  35. WRAP4 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
  36. WRAP8 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
  37. WRAP16 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
  38. INCR4 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
  39. INCR8 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
  40. INCR16 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
  41. default : begin `uvm_error("TYPEERR", "burst type not defined") end
  42. endcase
  43. endfunction
  44. function void store_data_with_hsize(ahb_transaction tr, int beat);
  45. case(tr.burst_size)
  46. BURST_SIZE_8BIT : mem[tr.addr] = tr.data[beat] & 'h0000_00FF;
  47. BURST_SIZE_16BIT : mem[tr.addr] = tr.data[beat] & 'h0000_FFFF;
  48. BURST_SIZE_32BIT : mem[tr.addr] = tr.data[beat] & 'hFFFF_FFFF;
  49. BURST_SIZE_64BIT : begin `uvm_error("TYPEERR", "burst_size is not supported yet") end
  50. default : begin `uvm_error("TYPEERR", "burst_size is not supported yet") end
  51. endcase
  52. endfunction
  53. function bit check_data_with_burst(ahb_transaction tr);
  54. case(tr.burst_type)
  55. SINGLE : begin
  56. check_data_with_burst = check_data_with_hsize(tr, 0);
  57. end
  58. INCR : begin `uvm_error("TYPEERR", "burst type not supported yet") end
  59. WRAP4 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
  60. WRAP8 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
  61. WRAP16 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
  62. INCR4 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
  63. INCR8 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
  64. INCR16 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
  65. default : begin `uvm_error("TYPEERR", "burst type not defined") end
  66. endcase
  67. if(check_data_with_burst) begin
  68. `uvm_info("DATACHECK", $sformatf("ahbram[0%x] hburst[0%x] is as expected", tr.addr, tr.burst_type), UVM_HIGH)
  69. end
  70. else begin
  71. `uvm_error("DATACHECK", $sformatf("ahbram[0%x] hburst[0%x] is not as expected", tr.addr, tr.burst_type))
  72. end
  73. endfunction
  74. function bit check_data_with_hsize(ahb_transaction tr, int beat);
  75. bit [63:0] data;
  76. case(tr.burst_size)
  77. BURST_SIZE_8BIT : begin data = tr.data[beat] & 'h0000_00FF; if(mem[tr.addr] == data & 'h0000_00FF) return 1; end
  78. BURST_SIZE_16BIT : begin data = tr.data[beat] & 'h0000_FFFF; if(mem[tr.addr] == data & 'h0000_FFFF) return 1; end
  79. BURST_SIZE_32BIT : begin data = tr.data[beat] & 'hFFFF_FFFF; if(mem[tr.addr] == data & 'hFFFF_FFFF) return 1; end
  80. BURST_SIZE_64BIT : begin `uvm_error("TYPEERR", "burst_size is not supported yet") end
  81. default : begin `uvm_error("TYPEERR", "burst_size is not supported yet") end
  82. endcase
  83. if(check_data_with_hsize) begin
  84. `uvm_info("DATACHECK", $sformatf("ahb_ram[%0x] data expected 'h%0x = actual 'h%0x", tr.addr, mem[tr.addr], data), UVM_HIGH)
  85. end
  86. else begin
  87. `uvm_error("DATACHECK", $sformatf("ahb_ram[%0x] data expected 'h%0x != actual 'h%0x", tr.addr, mem[tr.addr], data))
  88. end
  89. endfunction
  90. task do_listen_events();
  91. endtask
  92. virtual task do_data_check();
  93. endtask
  94. endclass
  95. `endif // AHB_RAM_SCOREBOARD_SV

【思考】为什么在这里要使用按位与呢?

  • 对于一个8bit数据而言,其有效的数据就是最低的8位,所以我们让他和最两位为FF的值进行按位与,与出来的结果就是高bit是无效位,低bit是我们关心的数据;
  • 同样的,对于一个16bit的数据而言,我们就让他跟’FFFF按位与。

5.2.2 比较次数的实现

同之前的项目一样,在实现了scoreboard以后我们是要对比较的次数进行补充,分别对比较的次数和error的次数都进行添加

  1. function bit check_data_with_hsize(ahb_transaction tr, int beat);
  2. bit [63:0] data;
  3. case(tr.burst_size)
  4. BURST_SIZE_8BIT : begin data = tr.data[beat] & 'h0000_00FF; if(mem[tr.addr] == data & 'h0000_00FF) return 1; end
  5. BURST_SIZE_16BIT : begin data = tr.data[beat] & 'h0000_FFFF; if(mem[tr.addr] == data & 'h0000_FFFF) return 1; end
  6. BURST_SIZE_32BIT : begin data = tr.data[beat] & 'hFFFF_FFFF; if(mem[tr.addr] == data & 'hFFFF_FFFF) return 1; end
  7. BURST_SIZE_64BIT : begin `uvm_error("TYPEERR", "burst_size is not supported yet") end
  8. default : begin `uvm_error("TYPEERR", "burst_size is not supported yet") end
  9. endcase
  10. cfg.scb_check_count++;
  11. if(check_data_with_hsize) begin
  12. `uvm_info("DATACHECK", $sformatf("ahb_ram[%0x] data expected 'h%0x = actual 'h%0x", tr.addr, mem[tr.addr], data), UVM_HIGH)
  13. end
  14. else begin
  15. cfg.scb_check_error++;
  16. `uvm_error("DATACHECK", $sformatf("ahb_ram[%0x] data expected 'h%0x != actual 'h%0x", tr.addr, mem[tr.addr], data))
  17. end
  18. endfunction

写入数据以后就会进行一次读操作,因此我们只需要在读数据的时候将计数的个数+1即可

【调试及结果】

image.png

在命令窗口中查看结果,一共比较了10次,均成功且没有错误。

image.png

但是在之前的命令行中提示了get_register by offset: block rgm is not locked的bug,我们这里采用DVT进行调试。


另外,调试结果中并没有将scoreboard中的信息打印出来
image.png
解决的方案就是在Makefile中设置信息的优先级。

  1. #############################
  2. # User variables
  3. #############################
  4. TB = ahb_ram_tb
  5. SEED = 1
  6. GUI ?= 0
  7. COV ?= 0
  8. DOTCL ?= 1
  9. VERB ?= UVM_HIGH
  10. OUT ?= out
  11. TESTNAME ?= ahb_ram_smoke_test
  12. DFILES = ../../verilog/ahb_blockram_32.v
  13. VFILES += ../vip_lib/ahb_pkg/ahb_pkg.sv \
  14. ../vip_lib/ahb_pkg/ahb_if.sv \
  15. ../env/ahb_ram_pkg.sv \
  16. ../tb/ahb_ram_if.sv \
  17. ../tb/ahb_ram_tb.sv
  18. #############################
  19. # Environment variables
  20. #############################
  21. VCOMP_INC = +incdir+../../verilog \
  22. +incdir+../vip_lib/ahb_pkg/{.,sequence_lib} \
  23. +incdir+../{cfg,cov,reg,env,seq_lib,seq_lib/elem_seqs,test}
  24. VCOMP = vlogan -full64 -ntb_opts uvm-1.2 -sverilog -timescale=1ps/1ps -nc -l $(OUT)/log/comp.log $(VCOMP_INC)
  25. ELAB = vcs -full64 -ntb_opts uvm-1.2 -debug_acc+all -l $(OUT)/log/elab.log -sim_res=1ps
  26. RUN = $(OUT)/obj/$(TB).simv -l run.log -sml +ntb_random_seed=$(SEED) +UVM_TESTNAME=$(TESTNAME) +UVM_VERBOSITY=$(VERB) -cm_dir $(CM_DIR) -cm_name $(CM_NAME)
  27. COV_OPTS = -full64 -dir $(CM_DIR)
  28. CM_DIR ?= $(OUT)/cov.vdb
  29. CM_NAME ?= $(TESTNAME)_$(SEED)
  30. SIMRUNFILE = ahb_ram_sim_run.do
  31. ifeq ($(GUI),1)
  32. RUN += -gui
  33. endif
  34. ifeq ($(DOTCL),1)
  35. RUN += -ucli -do $(SIMRUNFILE)
  36. endif
  37. ifeq ($(COV),1)
  38. ELAB += -cm line+cond+fsm+tgl+branch+assert -cm_dir $(CM_DIR)
  39. RUN += -cm line+cond+fsm+tgl+branch+assert -covg_cont_on_error
  40. endif
  41. prepare:
  42. mkdir -p $(OUT)/work
  43. mkdir -p $(OUT)/log
  44. mkdir -p $(OUT)/sim
  45. mkdir -p $(OUT)/obj
  46. mkdir -p .shadow
  47. comp: prepare
  48. $(VCOMP)
  49. $(VCOMP) $(DFILES) $(VFILES)
  50. elab: comp
  51. $(ELAB) -top $(TB) -o $(OUT)/obj/$(TB).simv
  52. run:
  53. $(RUN)
  54. mergecov:
  55. urg -format both $(COV_OPTS)
  56. dvecov:
  57. dve $(COV_OPTS)
  58. verdicov:
  59. verdi -cov -covdir $(CM_DIR)
  60. htmlcov:
  61. firefox urgReport/dashboard.html
  62. clean:
  63. rm -rf $(OUT) 64 AN.DB DVEfiles csrc *.simv *.simv.daidir *.simv.vdb ucli.key
  64. rm -rf *.log* *.vpd *.h urgReport

image.png

5.3 验证结构层次

image.png
到此为止,验证层次结构如上图所示