|
|
|
|
|
|
|
|
|
|
|
|
PLI Example
|
|
|
As in the rest of the tutorial, let's verify the counter example with test vector generation, monitor, checker: everything built with C code. Most of the common pli functions have been used to show their usage. |
|
|
|
|
|
The testbench will have these components: |
|
|
|
|
|
- Clock generation in C
- Clock generator HDL wrapper
- Test generation in C
- Test generator HDL Wrapper
- Monitor and Checker in C
- Monitor Checker HDL Wrapper
- DUT/Monitor/Clock/Test Generation instance in Verilog
|
|
|
|
|
|
It is a good idea to write HDL wrappers for the C functions that will be calling them. |
|
|
|
|
|
Clock Generator
|
|
|
Normally we don't want to have clock generators in PLI, it is always better to put them in 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 = %d\n",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 }
You could download file clkGen.c here
|
|
|
|
|
|
Clock Generator HDL Wrapper
|
|
|
|
|
|
1 module clkGen(clk);
2 output clk;
3 reg clk;
4
5 initial $clkGen(clk);
6
7 endmodule
You could download file clkGen.v here
|
|
|
|
|
|
Counter Monitor
|
|
|
|
|
|
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 detected\n", sim_time);
33 bin = 0;
34 } else if (*(in_string + i) == 122) {
35 io_printf ("%d counterMonitor : WARNING : Z detected\n", 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)
51 && (strcmp(counter->clk_last_value,"0") == 0)) {
52 // Conver the current count value
53 current_value =
54 pliConv(counter->count_value,counter->count_width,time);
55 // Check input control signal to floating or UnKnown
56 if (strcmp(counter->reset_value,"x") == 0) {
57 io_printf("%d counterMonitor : WARNING : reset is x\n", time);
58 }
59 if (strcmp(counter->reset_value,"z") == 0) {
60 io_printf("%d counterMonitor : WARNING : reset is z\n", time);
61 }
62 if (strcmp(counter->enable_value,"x") == 0) {
63 io_printf("%d counterMonitor : WARNING : enable is x\n", time);
64 }
65 if (strcmp(counter->enable_value,"z") == 0) {
66 io_printf("%d counterMonitor : WARNING : enable is z\n", time);
67 }
68 // Increment monitor counter and compare only if
69 // enable is 1 and reset is not active
70 if (strcmp(counter->enable_value,"1") == 0
71 && strcmp(counter->reset_value,"0") == 0) {
72 if (counter->checker_count ! = current_value) {
73 io_printf
74 ("%d counterMonitor : ERROR : Current value of monitor is %d dut is %d\n",
75 time, counter->checker_count, current_value);
76 counter->error ++;
77 if (counter->error == 1) counter->error_time = time;
78 } else {
79 io_printf
80 ("%d counterMonitor : INFO : Current value of monitor is %d dut is %d\n",
81 time, counter->checker_count, current_value);
82 }
83 counter->checker_count =
84 (counter->checker_count == 15) ? 0 : counter->checker_count + 1;
85 // Reset monitor counter if reset is active
86 } else if (strcmp(counter->reset_value,"1") == 0) {
87 io_printf("%d counterMonitor : INFO : Reset is asserted\n", time);
88 counter->checker_count = 0;
89 }
90 }
91 // Update the clock state
92 strcpy(counter->clk_last_value,counter->clk_value);
93 }
94
95 // misctf
96 void counterMonitor(int data, int reason, int paramvc) {
97 struct myCounter *counter =
98 (struct myCounter *) tf_igetworkarea(tf_getinstance());
99 if ((reason == reason_paramvc) || (reason == reason_paramdrc)) {
100 tf_synchronize( );
101 } else if (reason == reason_synch) {
102 counter->clk = acc_handle_tfarg(1);
103 counter->reset = acc_handle_tfarg(2);
104 counter->enable = acc_handle_tfarg(3);
105 counter->count = acc_handle_tfarg(4);
106 // Get the values
107 counter->clk_value = acc_fetch_value(counter->clk, "%b", 0);
108 counter->reset_value = acc_fetch_value(counter->reset, "%b", 0);
109 counter->enable_value = acc_fetch_value(counter->enable, "%b", 0);
110 counter->count_value = acc_fetch_value(counter->count, "%b", 0);
111 counter->count_width = acc_fetch_size (counter->count);
112 // Call the counter model
113 counterModel (counter);
114 }
115 // Print simulation stats when $finish is called
116 if (reason == reason_finish) {
117 io_printf("=========================================\n");
118 if (counter->error ! = 0) {
119 io_printf (" Simulation : FAILED\n");
120 io_printf (" Mismatched %d\n",counter->error);
121 io_printf (" First Mismatch at time %d\n", counter->error_time);
122 } else {
123 io_printf (" Simulation : PASSED\n");
124 }
125 io_printf("=========================================\n");
126 }
127 }
128
129 // calltf()
130 void initCounter(int data, int reason) {
131 struct myCounter *counter;
132 // Allocate memory for all variables necessary to manage a
133 // single instance of the model.
134 counter = (struct myCounter *) malloc (sizeof(struct myCounter));
135 // Initialize this instance of the model.
136 counter->clk = acc_handle_tfarg(1);
137 counter->reset = acc_handle_tfarg(2);
138 counter->enable = acc_handle_tfarg(3);
139 counter->count = acc_handle_tfarg(4);
140 // Save a copy of the present clk value.
141 counter->clk_last_value = acc_fetch_value(counter->clk, "%b", 0);
142 // Enable callback of `counter_monitor` whenever
143 // any argument to `$counter_monitor` changes.
144 tf_asynchon();
145 // Set initial counter value to 0.
146 counter->checker_count = 0;
147 counter->error = 0;
148 counter-> error_time = 0;
149 // Save the model data with this instance of `$counterMonitor`.
150 tf_setworkarea((char *)counter);
151 }
You could download file counterMonitor.c here
|
|
|
|
|
|
|
|
|
|
|
|
Counter Monitor HDL Wrapper
|
|
|
|
|
|
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
You could download file counterMonitor.v here
|
|
|
|
|
|
Counter TestGen
|
|
|
|
|
|
Syntax for test file |
|
|
|
|
|
delay : Command = Value |
|
|
|
|
|
Where |
|
|
Delay : Delay in clock ticks |
|
|
Command : reset or enable |
|
|
Value : 0 or 1 |
|
|
|
|
|
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)
52 printf("Line %d: %s\n", object->cmdSize,
53 object->cmdArray[object->cmdSize]);
54 // Get each line from the test file
55 object->cmdSize ++;
56 }
57 // Close the test file
58 fclose(testFile);
59 }
60
61 // This function process command line options
62 void processCmdOptions () {
63 // Get debug option
64 if (mc_scan_plusargs("plidebug") ! = NULL) {
65 object->debug = 1;
66 } else {
67 object->debug = 0;
68 }
69 // Get test file name
70 if (mc_scan_plusargs("test=") == NULL) {
71 printf("ERROR : No test file option passed, use +test=testfile\n");
72 } else {
73 object->testFile = mc_scan_plusargs("test=");
74 if (object->debug) printf("Test file name %s\n",object->testFile);
75 }
76 }
77
78 void doTest() {
79 char* ptoks;
80 char* tcmd;
81 s_setval_delay delay_s;
82 s_setval_value value_s;
83 // Get current clock value
84 object->clk_value = acc_fetch_value(object->clk, "%b", 0);
85 // BFM drives only at rising edge of clock
86 if ( ! strcmp(object->clk_last_value,"1") && ! strcmp(object->clk_value,"0")) {
87 switch (object->state) {
88 case IDLE :
89 if (object->debug) printf("%d Current state is IDLE\n", tf_gettime());
90 if (object->CmdPointer < object->cmdSize) {
91 tcmd = object->cmdArray[object->CmdPointer];
92 if (object->debug) printf ("Test line %d current command-%s",
93 object->CmdPointer, tcmd);
94 ptoks = strtok(tcmd, ":");
95 int lcnt = 0;
96 while(ptoks ! = NULL) {
97 if (*ptoks ! = '=') {
98 if (lcnt == 0) {
99 object->wait = atoi(ptoks);
100 if (object->debug) printf("Wait : %d\n", object->wait);
101 } else if (lcnt == 1) {
102 object->command = ptoks;
103 if (object->debug) printf("Command : %s\n", ptoks);
104 } else {
105 object->value = atoi(ptoks);
106 if (object->debug) printf("Value : %d\n", object->value);
107 }
108 lcnt ++;
109 }
110 ptoks = strtok(NULL, " ");
111 }
112 object->CmdPointer ++ ;
113 if (object->wait == 0) {
114 if (object->debug) printf("%d Next State DRIVE\n", tf_gettime());
115 object->state = DRIVE;
116 doTest();
117 } else {
118 if (object->debug) printf("%d Next State WAIT\n", tf_gettime());
119 object->state = WAIT;
120 }
121 } else {
122 if (object->debug) printf("%d Next State DONE\n", tf_gettime());
123 object->state = DONE;
124 }
125 break;
126 case WAIT :
127 if (object->debug) printf("%d Current state is WAIT : %d\n",
128 tf_gettime(), object->clkCnt);
129 if ((object->clkCnt + 1) >= object->wait) {
130 object->wait = 0;
131 object->clkCnt = 0;
132 if (object->debug) printf("%d Next State DRIVE\n", tf_gettime());
133 object->state = DRIVE;
134 doTest();
135 } else {
136 waitTicks();
137 }
138 break;
139 case DRIVE :
140 if (object->debug) printf("%d Current state is DRIVE\n", tf_gettime());
141 value_s.format = accIntVal;
142 delay_s.model = accNoDelay;
143 delay_s.time.type = accTime;
144 delay_s.time.low = 0;
145 delay_s.time.high = 0;
146 if ( ! strcmp(object->command,"reset")) {
147 value_s.value.integer = object->value;
148 acc_set_value(object->reset,&value_s,&delay_s);
149 } else if ( ! strcmp(object->command,"enable")) {
150 value_s.value.integer = object->value;
151 acc_set_value(object->enable,&value_s,&delay_s);
152 } else {
153 if (object->debug) printf("ERROR : What command do you want\n");
154 }
155 if (object->debug) printf("%d Next State IDLE\n", tf_gettime());
156 object->state = IDLE;
157 break;
158 case DONE :
159 if (object->debug) printf("%d Current state is DONE\n", tf_gettime());
160 tf_dofinish();
161 break;
162 default : object->state = IDLE;
163 break;
164 }
165 }
166 object->clk_last_value = acc_fetch_value(object->clk, "%b", 0);
167 }
168
169 void initCounterTestGen () {
170 //acc_initialize( );
171 //acc_configure( accDisplayErrors, "false" );
172 object = (struct testGenObject *) malloc (sizeof(struct testGenObject));
173 // Load the initial and defaule values
174 object->testFile = "simple.tst";
175 object->cmdSize = 0;
176 object->CmdPointer = 0;
177 object->clkCnt = 0;
178 object->state = IDLE;
179 // Initialize this instance of the model.
180 object->clk = acc_handle_tfarg(1);
181 object->reset = acc_handle_tfarg(2);
182 object->enable = acc_handle_tfarg(3);
183 object->count = acc_handle_tfarg(4);
184 // Drive inactive signals on all inputs
185 tf_putp (2, 0);
186 tf_putp (3, 0);
187 // Save a copy of the present clk value.
188 object->clk_last_value = acc_fetch_value(object->clk, "%b", 0);
189 // Get the command line testfile name and debug option
190 processCmdOptions();
191 // Open the testfile and make array of command
192 loadTest(object);
193 // Add callback when ever clock changes
194 acc_vcl_add( object->clk, doTest, object->clk_value, vcl_verilog_logic );
195 // All acc routines should have this
196 acc_close();
197 }
You could download file counterTestGen.c here
|
|
|
|
|
|
Counter TestGen HDL Wrapper
|
|
|
|
|
|
1 module counterTestGen (clk, reset, enable, count);
2 input clk;
3 output reset, enable;
4 input [3:0] count;
5
6 wire clk;
7 reg reset, enable;
8 wire [3:0] count;
9
10 initial $counterTestGen(clk,reset,enable,count);
11
12 endmodule
You could download file counterTestGen.v here
|
|
|
|
|
|
HDL TestBench Top
|
|
|
|
|
|
1 module top();
2 wire clk;
3 wire [3:0] count;
4 wire enable;
5 wire reset;
6
7 // Connect the clk generator
8 clkGen clkGen(.clk (clk));
9
10 // Connect the DUT
11 counter dut(
12 .clk (clk),
13 .reset (reset),
14 .enable (enable),
15 .count (count)
16 );
17
18 // Connect the Monitor/Checker
19 counterMonitor monitor(
20 .clk (clk),
21 .reset (reset),
22 .enable (enable),
23 .count (count)
24 );
25
26 // Connect the Test Generator
27 counterTestGen test(
28 .clk (clk),
29 .reset (reset),
30 .enable (enable),
31 .count (count)
32 );
33
34 endmodule
You could download file top.v here
|
|
|
|
|
|
Sample : Test File
|
|
|
|
|
|
1 : reset = 1
10 : reset = 0
5 : enable = 1
20 : enable = 0
|
|
|
|
|
|
Sample Ouput
|
|
|
|
|
|
5 counterMonitor : WARNING : X detected
5 counterMonitor : WARNING : X detected
5 counterMonitor : WARNING : X detected
5 counterMonitor : WARNING : X detected
17 counterMonitor : WARNING : X detected
17 counterMonitor : WARNING : X detected
17 counterMonitor : WARNING : X detected
17 counterMonitor : WARNING : X detected
29 counterMonitor : WARNING : X detected
29 counterMonitor : WARNING : X detected
29 counterMonitor : WARNING : X detected
29 counterMonitor : WARNING : X detected
29 counterMonitor : INFO : Reset is asserted
41 counterMonitor : INFO : Reset is asserted
53 counterMonitor : INFO : Reset is asserted
65 counterMonitor : INFO : Reset is asserted
77 counterMonitor : INFO : Reset is asserted
89 counterMonitor : INFO : Reset is asserted
101 counterMonitor : INFO : Reset is asserted
113 counterMonitor : INFO : Reset is asserted
125 counterMonitor : INFO : Reset is asserted
137 counterMonitor : INFO : Reset is asserted
149 counterMonitor : INFO : Reset is asserted
233 counterMonitor : INFO : Current value of monitor is 0 dut is 0
245 counterMonitor : INFO : Current value of monitor is 1 dut is 1
257 counterMonitor : INFO : Current value of monitor is 2 dut is 2
269 counterMonitor : INFO : Current value of monitor is 3 dut is 3
281 counterMonitor : INFO : Current value of monitor is 4 dut is 4
293 counterMonitor : INFO : Current value of monitor is 5 dut is 5
305 counterMonitor : INFO : Current value of monitor is 6 dut is 6
317 counterMonitor : INFO : Current value of monitor is 7 dut is 7
329 counterMonitor : INFO : Current value of monitor is 8 dut is 8
341 counterMonitor : INFO : Current value of monitor is 9 dut is 9
353 counterMonitor : INFO : Current value of monitor is 10 dut is 10
365 counterMonitor : INFO : Current value of monitor is 11 dut is 11
377 counterMonitor : INFO : Current value of monitor is 12 dut is 12
389 counterMonitor : INFO : Current value of monitor is 13 dut is 13
401 counterMonitor : INFO : Current value of monitor is 14 dut is 14
413 counterMonitor : INFO : Current value of monitor is 15 dut is 15
425 counterMonitor : INFO : Current value of monitor is 0 dut is 0
437 counterMonitor : INFO : Current value of monitor is 1 dut is 1
449 counterMonitor : INFO : Current value of monitor is 2 dut is 2
461 counterMonitor : INFO : Current value of monitor is 3 dut is 3
473 counterMonitor : INFO : Current value of monitor is 4 dut is 4
=========================================
Simulation : PASSED
=========================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|