在剩下的教程中,让我们用测试向量发生器,监测器,检验器以及任何用C代码编写的来验证计数器例子。
Testbench将有下列:
- C编写的时钟发生器
- 时钟发生器的HDL wrapper
- C编写的测试产生器
- 测试产生器的HDL wrapper
- C编写的监测器和检验器
- Mointor Checker HDL wrapper
- 在verilog中的DUT/Monitor/clock/test 产生器的例化
时钟发生器
一般我们不希望在PLI中产生时钟发生器,最好是在verilog中实现。
| 1 | #include "acc_user.h" | ||
| 2 | #include "veriuser.h" | ||
| 3 | |||
| 4 | // Define the ON and OFF time of clock | ||
| 5 | #define PERIOD 5 | ||
| 6 | |||
| 7 | // Data structure | ||
| 8 | struct clkData { | ||
| 9 | int clk; | ||
| 10 | int clkCnt; | ||
| 11 | }; | ||
| 12 | |||
| 13 | // Main routine which toggles the clock | ||
| 14 | void clkGen () { | ||
| 15 | // Get the stored workarea | ||
| 16 | struct clkData *data = ( struct clkData * )tf_igetworkarea(tf_getinstance()); | ||
| 17 | if (data->clkCnt == PERIOD) { | ||
| 18 | data->clk = (data->clk == 0) ? 1 : 0; | ||
| 19 | data->clkCnt = 0; | ||
| 20 | //io_printf("%d Current clk = %dn",tf_gettime(), data->clk); | ||
| 21 | } else { | ||
| 22 | data->clkCnt ++; | ||
| 23 | } | ||
| 24 | // Drive the clock signal in HDL | ||
| 25 | tf_putp (1, data->clk); | ||
| 26 | } | ||
| 27 | |||
| 28 | // checktf() routine | ||
| 29 | // This function inits the objects and also stores the object in workarea | ||
| 30 | void clkInit() { | ||
| 31 | struct clkData *data = ( struct clkData * )malloc( sizeof( struct clkData ) ); | ||
| 32 | data->clkCnt = 0; | ||
| 33 | data->clk = 0; | ||
| 34 | tf_setworkarea(data); | ||
| 35 | } | ||
| 36 | |||
| 37 | // misctf() routine | ||
| 38 | // This routine is called after 1 tick | ||
| 39 | void clkReactive (int data, int reason, int paramvc) { | ||
| 40 | // if callback reason is reactive, then call clkGen function | ||
| 41 | if (reason == reason_reactivate) { | ||
| 42 | clkGen(); | ||
| 43 | } | ||
| 44 | // Set the callback delay to 1 tick | ||
| 45 | tf_setdelay(1); | ||
| 46 | } | ||
| 1 | module clkGen(clk); |
| 2 | output clk; |
| 3 | reg clk; |
| 4 | |
| 5 | initial $clkGen(clk); |
| 6 | |
| 7 | endmodule |
| 1 | #include "acc_user.h" | ||||
| 2 | #include "veriuser.h" | ||||
| 3 | #include <malloc.h> | ||||
| 4 | #include <string.h> | ||||
| 5 | |||||
| 6 | struct myCounter { | ||||
| 7 | handle count; | ||||
| 8 | handle enable; | ||||
| 9 | handle reset; | ||||
| 10 | handle clk; | ||||
| 11 | char *count_value; | ||||
| 12 | char *enable_value; | ||||
| 13 | char *reset_value; | ||||
| 14 | char *clk_value; | ||||
| 15 | char *clk_last_value; | ||||
| 16 | int checker_count; | ||||
| 17 | int count_width; | ||||
| 18 | int error; | ||||
| 19 | int error_time; | ||||
| 20 | }; | ||||
| 21 | |||||
| 22 | // Multi-bit vector to integer conversion. | ||||
| 23 | int pliConv (char *in_string, int no_bits, int sim_time) { | ||||
| 24 | int conv = 0; | ||||
| 25 | int i = 0; | ||||
| 26 | int j = 0; | ||||
| 27 | int bin = 0; | ||||
| 28 | for ( i = no_bits-1; i >= 0; i = i - 1) { | ||||
| 29 | if (*(in_string + i) == 49) { | ||||
| 30 | bin = 1; | ||||
| 31 | } else if (*(in_string + i) == 120) { | ||||
| 32 | io_printf ( "%d counterMonitor : WARNING : X detectedn" , sim_time); | ||||
| 33 | bin = 0; | ||||
| 34 | } else if (*(in_string + i) == 122) { | ||||
| 35 | io_printf ( "%d counterMonitor : WARNING : Z detectedn" , sim_time); | ||||
| 36 | bin = 0; | ||||
| 37 | } else { | ||||
| 38 | bin = 0; | ||||
| 39 | } | ||||
| 40 | conv = conv + (1 << j)*bin; | ||||
| 41 | j ++; | ||||
| 42 | } | ||||
| 43 | return conv; | ||||
| 44 | } | ||||
| 45 | |||||
| 46 | void counterModel (struct myCounter *counter) { | ||||
| 47 | int current_value ; | ||||
| 48 | int time = tf_gettime(); | ||||
| 49 | // Our model checks only at posedge | ||||
| 50 | if ((strcmp(counter->clk_value, "1" ) == 0) && (strcmp(counter->clk_last_value, "0" ) == 0)) { | ||||
| 51 | // Conver the current count value | ||||
| 52 | current_value = pliConv(counter->count_value,counter->count_width,time); | ||||
| 53 | // Check input control signal to floating or UnKnown | ||||
| 54 | if (strcmp(counter->reset_value, "x" ) == 0) { | ||||
| 55 | io_printf( "%d counterMonitor : WARNING : reset is xn" , time); | ||||
| 56 | } | ||||
| 57 | if (strcmp(counter->reset_value, "z" ) == 0) { | ||||
| 58 | io_printf( "%d counterMonitor : WARNING : reset is zn" , time); | ||||
| 59 | } | ||||
| 60 | if (strcmp(counter->enable_value, "x" ) == 0) { | ||||
| 61 | io_printf( "%d counterMonitor : WARNING : enable is xn" , time); | ||||
| 62 | } | ||||
| 63 | if (strcmp(counter->enable_value, "z" ) == 0) { | ||||
| 64 | io_printf( "%d counterMonitor : WARNING : enable is zn" , time); | ||||
| 65 | } | ||||
| 66 | // Increment monitor counter and compare only if enable is 1 and reset is not active | ||||
| 67 | if (strcmp(counter->enable_value, "1" ) == 0 && strcmp(counter->reset_value, "0" ) == 0) { | ||||
| 68 | if (counter->checker_count != current_value) { | ||||
| 69 | io_printf( "%d counterMonitor : ERROR : Current value of monitor is %d dut is %dn" , | ||||
| 70 | time, counter->checker_count, current_value); | ||||
| 71 | counter->error ++; | ||||
| 72 | if (counter->error == 1) counter->error_time = time; | ||||
| 73 | } else { | ||||
| 74 | io_printf( "%d counterMonitor : INFO : Current value of monitor is %d dut is %dn" , | ||||
| 75 | time, counter->checker_count, current_value); | ||||
| 76 | } | ||||
| 77 | counter->checker_count = (counter->checker_count == 15) ? 0 : counter->checker_count + 1; | ||||
| 78 | // Reset monitor counter if reset is active | ||||
| 79 | } else if (strcmp(counter->reset_value, "1" ) == 0) { | ||||
| 80 | io_printf( "%d counterMonitor : INFO : Reset is assertedn" , time); | ||||
| 81 | counter->checker_count = 0; | ||||
| 82 | } | ||||
| 83 | } | ||||
| 84 | // Update the clock state | ||||
| 85 | strcpy(counter->clk_last_value,counter->clk_value); | ||||
| 86 | } | ||||
| 87 | |||||
| 88 | // misctf | ||||
| 89 | void counterMonitor(int data, int reason, int paramvc) { | ||||
| 90 | struct myCounter *counter = (struct myCounter *) tf_igetworkarea(tf_getinstance()); | ||||
| 91 | if ((reason == reason_paramvc) || (reason == reason_paramdrc)) { | ||||
| 92 | tf_synchronize( ); | ||||
| 93 | } else if (reason == reason_synch) { | ||||
| 94 | counter->clk = acc_handle_tfarg(1); | ||||
| 95 | counter->reset = acc_handle_tfarg(2); | ||||
| 96 | counter->enable = acc_handle_tfarg(3); | ||||
| 97 | counter->count = acc_handle_tfarg(4); | ||||
| 98 | // Get the values | ||||
| 99 | counter->clk_value = acc_fetch_value(counter->clk, "%b" , 0); | ||||
| 100 | counter->reset_value = acc_fetch_value(counter->reset, "%b" , 0); | ||||
| 101 | counter->enable_value = acc_fetch_value(counter->enable, "%b" , 0); | ||||
| 102 | counter->count_value = acc_fetch_value(counter->count, "%b" , 0); | ||||
| 103 | counter->count_width = acc_fetch_size (counter->count); | ||||
| 104 | // Call the counter model | ||||
| 105 | counterModel (counter); | ||||
| 106 | } | ||||
| 107 | // Print simulation stats when $finish is called | ||||
| 108 | if (reason == reason_finish) { | ||||
| 109 | io_printf( "=========================================n" ); | ||||
| 110 | if (counter->error != 0) { | ||||
| 111 | io_printf ( " Simulation : FAILEDn" ); | ||||
| 112 | io_printf ( " Mismatched %dn" ,counter->error); | ||||
| 113 | io_printf ( " First Mismatch at time %dn" , counter->error_time); | ||||
| 114 | } else { | ||||
| 115 | io_printf ( " Simulation : PASSEDn" ); | ||||
| 116 | } | ||||
| 117 | io_printf( "=========================================n" ); | ||||
| 118 | } | ||||
| 119 | } | ||||
| 120 | |||||
| 121 | // calltf() | ||||
| 122 | void initCounter(int data, int reason) { | ||||
| 123 | struct myCounter *counter; | ||||
| 124 | // Allocate memory for all variables necessary to manage a | ||||
| 125 | // single instance of the model. | ||||
| 126 | counter = (struct myCounter *) malloc (sizeof(struct myCounter)); | ||||
| 127 | // Initialize this instance of the model. | ||||
| 128 | counter->clk = acc_handle_tfarg(1); | ||||
| 129 | counter->reset = acc_handle_tfarg(2); | ||||
| 130 | counter->enable = acc_handle_tfarg(3); | ||||
| 131 | counter->count = acc_handle_tfarg(4); | ||||
| 132 | // Save a copy of the present clk value. | ||||
| 133 | counter->clk_last_value = acc_fetch_value(counter->clk, "%b" , 0); | ||||
| 134 | // Enable callback of `counter_monitor` whenever | ||||
| 135 | // any argument to `$counter_monitor` changes. | ||||
| 136 | tf_asynchon(); | ||||
| 137 | // Set initial counter value to 0. | ||||
| 138 | counter->checker_count = 0; | ||||
| 139 | counter->error = 0; | ||||
| 140 | counter-> error_time = 0; | ||||
| 141 | // Save the model data with this instance of `$counterMonitor`. | ||||
| 142 | tf_setworkarea((char *)counter); | ||||
| 143 | } | ||||
| 1 | module counterMonitor (clk, reset, enable, count); |
| 2 | input clk, reset, enable; |
| 3 | input [3:0] count; |
| 4 | |
| 5 | wire clk, reset, enable; |
| 6 | wire [3:0] count; |
| 7 | |
| 8 | initial $counterMonitor(clk,reset,enable,count); |
| 9 | |
| 10 | endmodule |
| 1 | #include "acc_user.h" | ||||||||
| 2 | #include "veriuser.h" | ||||||||
| 3 | #include "string.h" | ||||||||
| 4 | #include "stdio.h" | ||||||||
| 5 | |||||||||
| 6 | #define IDLE 0 | ||||||||
| 7 | #define INCR 1 | ||||||||
| 8 | #define WAIT 2 | ||||||||
| 9 | #define DRIVE 3 | ||||||||
| 10 | #define DONE 4 | ||||||||
| 11 | |||||||||
| 12 | struct testGenObject { | ||||||||
| 13 | char* testFile; | ||||||||
| 14 | int debug; | ||||||||
| 15 | char cmdArray[100] [100]; | ||||||||
| 16 | int cmdSize; | ||||||||
| 17 | int CmdPointer; | ||||||||
| 18 | char* command; | ||||||||
| 19 | int wait; | ||||||||
| 20 | int value; | ||||||||
| 21 | int clkCnt; | ||||||||
| 22 | int state; | ||||||||
| 23 | handle count; | ||||||||
| 24 | handle enable; | ||||||||
| 25 | handle reset; | ||||||||
| 26 | handle clk; | ||||||||
| 27 | char* clk_value; | ||||||||
| 28 | char *clk_last_value; | ||||||||
| 29 | }; | ||||||||
| 30 | |||||||||
| 31 | static struct testGenObject *object; | ||||||||
| 32 | |||||||||
| 33 | // Increment counter | ||||||||
| 34 | void waitTicks () { | ||||||||
| 35 | object->clkCnt = object->clkCnt + 1; | ||||||||
| 36 | } | ||||||||
| 37 | |||||||||
| 38 | // This function loads the content of test file into | ||||||||
| 39 | // object command array | ||||||||
| 40 | void loadTest() { | ||||||||
| 41 | FILE *testFile; | ||||||||
| 42 | char currentLine [100]; | ||||||||
| 43 | object->cmdSize = 0; | ||||||||
| 44 | if((testFile = fopen(object->testFile, "r" )) == NULL) { | ||||||||
| 45 | printf( "Error Opening File.n" ); | ||||||||
| 46 | } | ||||||||
| 47 | while (fgets(currentLine, sizeof(currentLine), testFile) != NULL ) { | ||||||||
| 48 | // Store the line cmdArray | ||||||||
| 49 | strcpy(object->cmdArray[object->cmdSize], currentLine); | ||||||||
| 50 | // print the line number and data | ||||||||
| 51 | if (object->debug) printf( "Line %d: %sn" , object->cmdSize, object->cmdArray[object->cmdSize]); | ||||||||
| 52 | // Get each line from the test file | ||||||||
| 53 | object->cmdSize ++; | ||||||||
| 54 | } | ||||||||
| 55 | // Close the test file | ||||||||
| 56 | fclose(testFile); | ||||||||
| 57 | } | ||||||||
| 58 | |||||||||
| 59 | // This function process command line options | ||||||||
| 60 | void processCmdOptions () { | ||||||||
| 61 | // Get debug option | ||||||||
| 62 | if (mc_scan_plusargs( "plidebug" ) != NULL) { | ||||||||
| 63 | object->debug = 1; | ||||||||
| 64 | } else { | ||||||||
| 65 | object->debug = 0; | ||||||||
| 66 | } | ||||||||
| 67 | // Get test file name | ||||||||
| 68 | if (mc_scan_plusargs( "test=" ) == NULL) { | ||||||||
| 69 | printf( "ERROR : No test file option passed, use +test=testfilen" ); | ||||||||
| 70 | } else { | ||||||||
| 71 | object->testFile = mc_scan_plusargs( "test=" ); | ||||||||
| 72 | if (object->debug) printf( "Test file name %sn" ,object->testFile); | ||||||||
| 73 | } | ||||||||
| 74 | } | ||||||||
| 75 | |||||||||
| 76 | void doTest() { | ||||||||
| 77 | char* ptoks; | ||||||||
| 78 | char* tcmd; | ||||||||
| 79 | s_setval_delay delay_s; | ||||||||
| 80 | s_setval_value value_s; | ||||||||
| 81 | // Get current clock value | ||||||||
| 82 | object->clk_value = acc_fetch_value(object->clk, "%b" , 0); | ||||||||
| 83 | // BFM drives only at rising edge of clock | ||||||||
| 84 | if (!strcmp(object->clk_last_value, "1" ) && !strcmp(object->clk_value, "0" )) { | ||||||||
| 85 | switch (object->state) { | ||||||||
| 86 | case IDLE : if (object->debug) printf( "%d Current state is IDLEn" , tf_gettime()); | ||||||||
| 87 | if (object->CmdPointer < object->cmdSize) { | ||||||||
| 88 | tcmd = object->cmdArray[object->CmdPointer]; | ||||||||
| 89 | if (object->debug) printf ( "Test line %d current command-%s" ,object->CmdPointer, tcmd); | ||||||||
| 90 | ptoks = strtok(tcmd, ":" ); | ||||||||
| 91 | int lcnt = 0; | ||||||||
| 92 | while(ptoks != NULL) { | ||||||||
| 93 | if (*ptoks != '=') { | ||||||||
| 94 | if (lcnt == 0) { | ||||||||
| 95 | object->wait = atoi(ptoks); | ||||||||
| 96 | if (object->debug) printf( "Wait : %dn" , object->wait); | ||||||||
| 97 | } else if (lcnt == 1) { | ||||||||
| 98 | object->command = ptoks; | ||||||||
| 99 | if (object->debug) printf( "Command : %sn" , ptoks); | ||||||||
| 100 | } else { | ||||||||
| 101 | object->value = atoi(ptoks); | ||||||||
| 102 | if (object->debug) printf( "Value : %dn" , object->value); | ||||||||
| 103 | } | ||||||||
| 104 | lcnt ++; | ||||||||
| 105 | } | ||||||||
| 106 | ptoks = strtok(NULL, " " ); | ||||||||
| 107 | } | ||||||||
| 108 | object->CmdPointer ++ ; | ||||||||
| 109 | if (object->wait == 0) { | ||||||||
| 110 | if (object->debug) printf( "%d Next State DRIVEn" , tf_gettime()); | ||||||||
| 111 | object->state = DRIVE; | ||||||||
| 112 | doTest(); | ||||||||
| 113 | } else { | ||||||||
| 114 | if (object->debug) printf( "%d Next State WAITn" , tf_gettime()); | ||||||||
| 115 | object->state = WAIT; | ||||||||
| 116 | } | ||||||||
| 117 | } else { | ||||||||
| 118 | if (object->debug) printf( "%d Next State DONEn" , tf_gettime()); | ||||||||
| 119 | object->state = DONE; | ||||||||
| 120 | } | ||||||||
| 121 | break; | ||||||||
| 122 | case WAIT : if (object->debug) printf( "%d Current state is WAIT : %dn" , tf_gettime(), object->clkCnt); | ||||||||
| 123 | if ((object->clkCnt + 1) >= object->wait) { | ||||||||
| 124 | object->wait = 0; | ||||||||
| 125 | object->clkCnt = 0; | ||||||||
| 126 | if (object->debug) printf( "%d Next State DRIVEn" , tf_gettime()); | ||||||||
| 127 | object->state = DRIVE; | ||||||||
| 128 | doTest(); | ||||||||
| 129 | } else { | ||||||||
| 130 | waitTicks(); | ||||||||
| 131 | } | ||||||||
| 132 | break; | ||||||||
| 133 | case DRIVE : if (object->debug) printf( "%d Current state is DRIVEn" , tf_gettime()); | ||||||||
| 134 | value_s.format = accIntVal; | ||||||||
| 135 | delay_s.model = accNoDelay; | ||||||||
| 136 | delay_s.time.type = accTime; | ||||||||
| 137 | delay_s.time.low = 0; | ||||||||
| 138 | delay_s.time.high = 0; | ||||||||
| 139 | if (!strcmp(object->command, "reset" )) { | ||||||||
| 140 | value_s.value.integer = object->value; | ||||||||
| 141 | acc_set_value(object->reset,&value_s,&delay_s); | ||||||||
| 142 | } else if (!strcmp(object->command, "enable" )) { | ||||||||
| 143 | value_s.value.integer = object->value; | ||||||||
| 144 | acc_set_value(object->enable,&value_s,&delay_s); | ||||||||
| 145 | } else { | ||||||||
| 146 | if (object->debug) printf( "ERROR : What command do you wantn" ); | ||||||||
| 147 | } | ||||||||
| 148 | if (object->debug) printf( "%d Next State IDLEn" , tf_gettime()); | ||||||||
| 149 | object->state = IDLE; | ||||||||
| 150 | break; | ||||||||
| 151 | case DONE : if (object->debug) printf( "%d Current state is DONEn" , tf_gettime()); | ||||||||
| 152 | tf_dofinish(); | ||||||||
| 153 | break; | ||||||||
| 154 | default : object->state = IDLE; | ||||||||
| 155 | break; | ||||||||
| 156 | } | ||||||||
| 157 | } | ||||||||
| 158 | object->clk_last_value = acc_fetch_value(object->clk, "%b" , 0); | ||||||||
| 159 | } | ||||||||
| 160 | |||||||||
| 161 | void initCounterTestGen () { | ||||||||
| 162 | //acc_initialize( ); | ||||||||
| 163 | //acc_configure( accDisplayErrors, "false" ); | ||||||||
| 164 | object = (struct testGenObject *) malloc (sizeof(struct testGenObject)); | ||||||||