Project Report
Design and Verification
of a Mod-12 Counter
Index
1. Introduction
2. Specifications
3. Tools Used
4. RTL Design
5. Verification Plan
6. Testbench Architecture
7. Assertions (ABV)
8. Functional Coverage
9. SystemVerilog Testbench
10. Simulation Results
11. Synthesis Results
1. Introduction
Counters are fundamental sequential circuits widely used in timers, digital clocks, and state
machines.
This project designs a Mod-12 Counter that supports load, reset, up-count, down-count, and
wraparound, and verifies it using assertion-based verification (ABV) and functional coverage
2. Specifications
• Range: 0–11 (4-bit counter).
• Reset: Synchronous, active-high.
• Load: Loads a 4-bit value into the counter.
• Mode: 1 = Up count, 0 = Down count.
• Wraparound:
• Up: 11 → 0
• Down: 0 → 11
\\\\
3. Tools Used
• RTL & Synthesis: Xilinx ISE / Vivado
• Verification (Simulation): QuestaSim
• Languages: Verilog (RTL), SystemVerilog (Testbench)
•
4.RTL Design
module counter (input clk,
input rst,
input load,
input mode,
input [3:0] data_in,
output reg [3:0] data_out
);
always@(posedge clk)
begin
if(rst)
data_out <= 4'd0;
else if(load)
begin
data_out <= data_in;
end
else if(mode)
begin
if(data_out == 4'd11)
data_out <= 4'd0;
else
data_out <= data_out + 1'b1; //upcounting
end
else
begin
if(data_out==4'd0)
data_out <=4'd11;
else
data_out <= data_out - 1'b1; //down counting
end
end
endmodule
1. // *************************** RTL MOD 12 COUNTER **********************
2. module counter (
3. input clk,
4. input rst,
5. input load,
6. input mode,
7. input [3:0] data_in,
8. output reg [3:0] count
9. );
10.
11. always @(posedge clk) begin
12. if (rst) begin
13. count <= 4'd0; // Synchronous reset
14. end else if (load) begin
15. count <= data_in; // Load data
16. end else if (mode) begin // Count up mode
17. if (count == 4'd11) begin
18. count <= 4'd0; // Wrap around at 11
19. end else begin
20. count <= count + 1'b1; // Increment count
21. end
22. end else begin // Count down mode
23. if (count == 4'd0) begin
24. count <= 4'd11; // Wrap around at 0
25. end else begin
26. count <= count - 1'b1; // Decrement count
27. end
28. end
29. end
30.
31. endmodule
32.
33. // ********************************** INTERFACE ***************
34. interface count_if(input bit clk);
35. // Signal declarations
36. logic [3:0] data_in;
37. logic [3:0] count;
38. logic rst;
39. logic mode;
40. logic load;
41.
42. // Clocking block for write driver
43. clocking count_wr_drv @(posedge clk);
44. default input #1 output #1;
45. output data_in;
46. output mode;
47. output rst;
48. output load;
49. endclocking
50.
51. // Clocking block for write monitor
52. clocking count_wr_mon @(posedge clk);
53. default input #1 output #1;
54. input data_in;
55. input mode;
56. input rst;
57. input load;
58. endclocking
59.
60. // Clocking block for read monitor
61. clocking count_rd_mon @(posedge clk);
62. default input #1 output #1;
63. input count;
64. endclocking
65.
66. // Modports for different components
67. modport WR_DRV(clocking count_wr_drv, output rst);
68. modport WR_MON(clocking count_wr_mon, input rst);
69. modport RD_MON(clocking count_rd_mon);
70.
71. endinterface
72.
73. // *********************** TRANSACTION CLASS ********************
74. class transaction;
75. // Random variables for DUT inputs/outputs
76. rand bit mode;
77. rand bit [3:0] data_in;
78. rand bit [3:0] count;
79. rand bit load;
80. rand bit rst;
81. int trans_id;
82.
83. // Enhanced constraints for better coverage
84. constraint c1 { rst dist{0:=80, 1:=20}; } // More realistic reset distribution
85. constraint c2 { data_in inside{[0:11]}; } // Valid data input range
86. constraint c3 { load dist{0:=60, 1:=40}; } // Balanced load distribution
87. constraint c4 { mode dist{0:=50, 1:=50}; } // 50% count down, 50% count up
88.
89. // New constraints for sequence coverage
90. constraint c5 { if (!rst && !load) {
91. if (mode) { // Count up
92. count inside {[0:10]}; // Don't start at 11 to allow transitions
93. } else { // Count down
94. count inside {[1:11]}; // Don't start at 0 to allow transitions
95. }
96. }}
97.
98. // Display function for debugging
99. function void display(input string message);
100.
$display("==========================================================
=================================");
101. $display("%s ", message);
102. $display("\t mode of counter = %b", mode);
103. $display("\t input data for counter = %0d", data_in);
104. $display("\t count = %0d", count);
105. $display("\t load = %b", load);
106. $display("\t rst = %b", rst);
107.
$display("==========================================================
=================================");
108. endfunction
109.
110. // Copy function for transaction cloning
111. function transaction copy();
112. copy = new();
113. copy.mode = this.mode;
114. copy.data_in = this.data_in;
115. copy.count = this.count;
116. copy.load = this.load;
117. copy.rst = this.rst;
118. copy.trans_id = this.trans_id;
119. endfunction
120.
121. endclass
122.
123. // ******************* GENERATOR ****************
124. class generator;
125. transaction gen_trans; // Transaction object
126. mailbox #(transaction) gen2wr; // Mailbox to driver
127. int number_of_transaction; // Number of transactions to generate
128.
129. // Constructor
130. function new(mailbox #(transaction) gen2wr);
131. this.gen2wr = gen2wr;
132. this.gen_trans = new();
133. endfunction
134.
135. // Start task - generates transactions
136. virtual task start();
137. for (int i = 0; i < number_of_transaction; i++) begin
138. gen_trans.trans_id = i;
139. assert(gen_trans.randomize()); // Randomize transaction
140. gen2wr.put(gen_trans.copy()); // Send copy to driver
141. #10; // Delay between transactions
142. end
143. endtask
144.
145. endclass
146.
147. // **************************** WRITE DRIVER***********
148. class count_write_drv;
149. virtual count_if.WR_DRV wr_drv_if; // Virtual interface
150. mailbox #(transaction) gen2wr; // Mailbox from generator
151.
152. // Constructor
153. function new(virtual count_if.WR_DRV wr_drv_if,
154. mailbox #(transaction) gen2wr);
155. this.wr_drv_if = wr_drv_if;
156. this.gen2wr = gen2wr;
157. endfunction
158.
159. // Drive task - applies transaction to DUT
160. virtual task drive(transaction data2duv);
161. wr_drv_if.rst <= data2duv.rst;
162. wr_drv_if.count_wr_drv.data_in <= data2duv.data_in;
163. wr_drv_if.count_wr_drv.mode <= data2duv.mode;
164. wr_drv_if.count_wr_drv.load <= data2duv.load;
165. @(wr_drv_if.count_wr_drv); // Wait for next clock edge
166. endtask
167.
168. // Start task - continuously drives transactions
169. virtual task start();
170. transaction data2duv;
171. forever begin
172. gen2wr.get(data2duv); // Get transaction from generator
173. data2duv.display("DRIVER SENDING DATA");
174. drive(data2duv); // Drive to DUT
175. end
176. endtask
177.
178. endclass
179.
180. // *************************** WRITE MONIT***************
181. class count_write_mon;
182. virtual count_if.WR_MON wr_mon_if; // Virtual interface
183. mailbox #(transaction) mon2rm; // Mailbox to reference model
184. mailbox #(transaction) mon2cov; // Mailbox to coverage collector
185.
186. // Constructor
187. function new(virtual count_if.WR_MON wr_mon_if,
188. mailbox #(transaction) mon2rm,
189. mailbox #(transaction) mon2cov = null);
190. this.wr_mon_if = wr_mon_if;
191. this.mon2rm = mon2rm;
192. this.mon2cov = mon2cov;
193. endfunction
194.
195. // Monitor task - captures input transactions
196. task monitor();
197. transaction data2rm = new();
198. transaction data2cov = new();
199. @(wr_mon_if.count_wr_mon); // Wait for clock edge
200.
201. // Capture input signals
202. data2rm.data_in = wr_mon_if.count_wr_mon.data_in;
203. data2rm.load = wr_mon_if.count_wr_mon.load;
204. data2rm.mode = wr_mon_if.count_wr_mon.mode;
205. data2rm.rst = wr_mon_if.rst;
206.
207. // Send to reference model
208. mon2rm.put(data2rm);
209.
210. // Send to coverage if connected
211. if (mon2cov != null) begin
212. data2cov = data2rm.copy();
213. mon2cov.put(data2cov);
214. end
215.
216. data2rm.display("DATA FROM WRITE MONITOR");
217. endtask
218.
219. // Start task - continuously monitors inputs
220. task start();
221. forever begin
222. monitor();
223. end
224. endtask
225.
226. endclass
227.
228. // ****************************** READ MONITOR******************
229. class count_read_mon;
230. virtual count_if.RD_MON rd_mon_if; // Virtual interface
231. mailbox #(transaction) rm2sb; // Mailbox to scoreboard
232. mailbox #(transaction) mon2cov; // Mailbox to coverage collector
233.
234. // Constructor
235. function new(virtual count_if.RD_MON rd_mon_if,
236. mailbox #(transaction) rm2sb,
237. mailbox #(transaction) mon2cov = null);
238. this.rd_mon_if = rd_mon_if;
239. this.rm2sb = rm2sb;
240. this.mon2cov = mon2cov;
241. endfunction
242.
243. // Monitor task - captures output transactions
244. task monitor();
245. transaction data2sb = new();
246. transaction data2cov = new();
247. @(rd_mon_if.count_rd_mon); // Wait for clock edge
248.
249. // Capture output count
250. data2sb.count = rd_mon_if.count_rd_mon.count;
251.
252. // Send to scoreboard
253. rm2sb.put(data2sb);
254.
255. // Send to coverage if connected
256. if (mon2cov != null) begin
257. data2cov.count = data2sb.count;
258. mon2cov.put(data2cov);
259. end
260.
261. data2sb.display("DATA FROM READ MONITOR");
262. endtask
263.
264. // Start task - continuously monitors outputs
265. task start();
266. forever begin
267. monitor();
268. end
269. endtask
270.
271. endclass
272.
273. // *************************** REFERENCE MODEL ******************
274. class count_ref;
275. mailbox #(transaction) wm2ref; // Mailbox from write monitor
276. mailbox #(transaction) ref2sb; // Mailbox to scoreboard
277. bit [3:0] ref_count = 0; // Internal reference count
278.
279. // Constructor
280. function new(mailbox #(transaction) wm2ref,
281. mailbox #(transaction) ref2sb);
282. this.wm2ref = wm2ref;
283. this.ref2sb = ref2sb;
284. endfunction
285.
286. // Count modeling task - implements counter behavior
287. task count_mod(transaction mod_count);
288. if (mod_count.rst) begin
289. ref_count <= 4'd0; // Reset condition
290. end else if (mod_count.load) begin
291. ref_count <= mod_count.data_in; // Load condition
292. end else if (mod_count.mode) begin // Count up mode
293. if (ref_count == 4'd11) begin
294. ref_count <= 4'd0; // Wrap around
295. end else begin
296. ref_count <= ref_count + 1'b1; // Increment
297. end
298. end else begin // Count down mode
299. if (ref_count == 4'd0) begin
300. ref_count <= 4'd11; // Wrap around
301. end else begin
302. ref_count <= ref_count - 1'b1; // Decrement
303. end
304. end
305. endtask
306.
307. // Start task - processes transactions
308. task start();
309. transaction data2sb;
310. forever begin
311. wm2ref.get(data2sb); // Get transaction
312. count_mod(data2sb); // Model counter behavior
313. data2sb.count = ref_count; // Update with predicted count
314. ref2sb.put(data2sb); // Send to scoreboard
315. data2sb.display("REFERENCE MODEL PREDICTION");
316. end
317. endtask
318.
319. endclass
320.
321. // ********************** SCOREBOARD *************************
322. class scoreboard;
323. mailbox #(transaction) rm2sb; // Mailbox from read monitor
324. mailbox #(transaction) ref2sb; // Mailbox from reference model
325. int error_count = 0; // Error counter
326. int total_count = 0; // Total transaction counter
327.
328. // Constructor
329. function new(mailbox #(transaction) rm2sb,
330. mailbox #(transaction) ref2sb);
331. this.rm2sb = rm2sb;
332. this.ref2sb = ref2sb;
333. endfunction
334.
335. // Check task - compares DUT vs reference
336. task check();
337. transaction dut_data, ref_data;
338. forever begin
339. rm2sb.get(dut_data); // Get DUT output
340. ref2sb.get(ref_data); // Get reference output
341. total_count++;
342.
343. // Compare DUT and reference
344. if (dut_data.count !== ref_data.count) begin
345. $display("ERROR: Mismatch at transaction %0d", total_count);
346. $display(" DUT count: %0d, Reference count: %0d", dut_data.count,
ref_data.count);
347. error_count++;
348. end else begin
349. $display("PASS: Transaction %0d - Count: %0d", total_count,
dut_data.count);
350. end
351. end
352. endtask
353.
354. // Report task - simulation summary
355. task report();
356. $display("=========================================");
357. $display("SIMULATION SUMMARY");
358. $display("Total transactions: %0d", total_count);
359. $display("Errors: %0d", error_count);
360. $display("Success rate: %0.2f%%", (real'(total_count - error_count) / total_count
* 100));
361. $display("=========================================");
362. endtask
363.
364. endclass
365.
366. // *********************** COVERAGE COLLECTOR ***************
367. class coverage_collector;
368. // Coverage groups
369. covergroup counter_basic_cg with function sample(bit rst_val, bit load_val, bit
mode_val, bit [3:0] data_in_val, bit [3:0] count_val);
370. // Reset coverage
371. rst_cp: coverpoint rst_val {
372. bins active = {1};
373. bins inactive = {0};
374. }
375.
376. // Load coverage
377. load_cp: coverpoint load_val {
378. bins active = {1};
379. bins inactive = {0};
380. }
381.
382. // Mode coverage
383. mode_cp: coverpoint mode_val {
384. bins count_up = {1};
385. bins count_down = {0};
386. }
387.
388. // Data input coverage - all valid values
389. data_in_cp: coverpoint data_in_val {
390. bins valid_values[] = {[0:11]};
391. illegal_bins invalid = {[12:15]};
392. }
393.
394. // Count value coverage - all valid states
395. count_cp: coverpoint count_val {
396. bins all_values[] = {[0:11]};
397. illegal_bins invalid = {[12:15]};
398. }
399.
400. // Cross coverage
401. rst_x_load: cross rst_cp, load_cp;
402. rst_x_mode: cross rst_cp, mode_cp;
403. load_x_mode: cross load_cp, mode_cp;
404. load_x_data: cross load_cp, data_in_cp;
405. mode_x_count: cross mode_cp, count_cp;
406.
407. endgroup
408.
409. covergroup counter_sequence_cg with function sample(bit [3:0] prev_count_val,
bit [3:0] current_count_val, bit mode_val);
410. // Count transitions - specific sequences
411. count_trans_up: coverpoint current_count_val {
412. bins up_transitions[] = ([0:10] => [1:11]);
413. bins up_wrap = (11 => 0);
414. }
415.
416. count_trans_down: coverpoint current_count_val {
417. bins down_transitions[] = ([1:11] => [0:10]);
418. bins down_wrap = (0 => 11);
419. }
420.
421. // Mode-specific transitions
422. mode_x_transition: cross mode_val, count_trans_up, count_trans_down;
423.
424. // Specific boundary transitions
425. boundary: coverpoint current_count_val {
426. bins wrap_around_up = (11 => 0);
427. bins wrap_around_down = (0 => 11);
428. bins normal_transition = ([0:10] => [1:11]), ([1:11] => [0:10]);
429. }
430.
431. endgroup
432.
433. covergroup counter_scenario_cg with function sample(bit rst_val, bit load_val, bit
mode_val);
434. // Complex scenarios
435. scenario: coverpoint {rst_val, load_val, mode_val} {
436. // Reset scenarios
437. bins reset_only = {3'b100};
438. bins reset_with_load = {3'b110};
439. bins reset_with_mode = {3'b101};
440.
441. // Load scenarios
442. bins load_only = {3'b010};
443. bins load_with_mode_up = {3'b011};
444. bins load_with_mode_down = {3'b010}; // Same as load_only for down mode
445.
446. // Counting scenarios
447. bins count_up = {3'b001};
448. bins count_down = {3'b000};
449.
450. // Transition scenarios
451. bins reset_to_load = (3'b100 => 3'b010);
452. bins load_to_count = (3'b010 => 3'b001), (3'b010 => 3'b000);
453. bins count_to_reset = (3'b001 => 3'b100), (3'b000 => 3'b100);
454. bins mode_switch = (3'b001 => 3'b000), (3'b000 => 3'b001);
455. }
456.
457. endgroup
458.
459. // Variables for sequence tracking
460. bit [3:0] prev_count = 0;
461. bit prev_mode = 0;
462. bit prev_rst = 0;
463. bit prev_load = 0;
464.
465. // Constructor
466. function new();
467. counter_basic_cg = new();
468. counter_sequence_cg = new();
469. counter_scenario_cg = new();
470. endfunction
471.
472. // Sample task - collects coverage data
473. task sample(transaction tr);
474. // Sample basic coverage
475. counter_basic_cg.sample(tr.rst, tr.load, tr.mode, tr.data_in, tr.count);
476.
477. // Sample sequence coverage when not in reset/load
478. if (!tr.rst && !tr.load && !prev_rst && !prev_load) begin
479. counter_sequence_cg.sample(prev_count, tr.count, prev_mode);
480. end
481.
482. // Sample scenario coverage
483. counter_scenario_cg.sample(tr.rst, tr.load, tr.mode);
484.
485. // Update previous values
486. prev_count = tr.count;
487. prev_mode = tr.mode;
488. prev_rst = tr.rst;
489. prev_load = tr.load;
490. endtask
491.
492. // Report function - coverage results
493. function void report_coverage();
494. real basic_percent = counter_basic_cg.get_inst_coverage();
495. real sequence_percent = counter_sequence_cg.get_inst_coverage();
496. real scenario_percent = counter_scenario_cg.get_inst_coverage();
497. real overall_percent = (basic_percent + sequence_percent + scenario_percent) /
3;
498.
499. $display("=========================================");
500. $display("FUNCTIONAL COVERAGE REPORT");
501. $display("=========================================");
502. $display("Basic Coverage: %0.2f%%", basic_percent);
503. $display("Sequence Coverage: %0.2f%%", sequence_percent);
504. $display("Scenario Coverage: %0.2f%%", scenario_percent);
505. $display("-----------------------------------------");
506. $display("Overall Coverage: %0.2f%%", overall_percent);
507. $display("=========================================");
508.
509. // Detailed coverage information
510. if (basic_percent < 100) begin
511. $display("Missing basic coverage - run more transactions with varied inputs");
512. end
513. if (sequence_percent < 100) begin
514. $display("Missing sequence coverage - need more count transitions and
boundary tests");
515. end
516. if (scenario_percent < 100) begin
517. $display("Missing scenario coverage - test more operational scenarios");
518. end
519. endfunction
520.
521. endclass//
522.
523. *****************************ENVIRONMENT ***********************
524. class environment;
525. // Virtual interfaces
526. virtual count_if.WR_DRV wr_drv_if;
527. virtual count_if.WR_MON wr_mon_if;
528. virtual count_if.RD_MON rd_mon_if;
529.
530. // Component handles
531. generator gen_h;
532. count_write_drv drv_h;
533. count_write_mon wr_mon_h;
534. count_read_mon rd_mon_h;
535. count_ref ref_h;
536. scoreboard sb_h;
537. coverage_collector cov_collector;
538.
539. // Mailboxes
540. mailbox #(transaction) gen2drv;
541. mailbox #(transaction) wr_mon2ref;
542. mailbox #(transaction) rd_mon2sb;
543. mailbox #(transaction) ref2sb;
544. mailbox #(transaction) mon2cov;
545.
546. // Constructor
547. function new(virtual count_if.WR_DRV wr_drv_if,
548. virtual count_if.WR_MON wr_mon_if,
549. virtual count_if.RD_MON rd_mon_if);
550. this.wr_drv_if = wr_drv_if;
551. this.wr_mon_if = wr_mon_if;
552. this.rd_mon_if = rd_mon_if;
553.
554. // Initialize mailboxes
555. gen2drv = new();
556. wr_mon2ref = new();
557. rd_mon2sb = new();
558. ref2sb = new();
559. mon2cov = new();
560. endfunction
561.
562. // Build task - creates components
563. task build();
564. gen_h = new(gen2drv);
565. drv_h = new(wr_drv_if, gen2drv);
566. wr_mon_h = new(wr_mon_if, wr_mon2ref, mon2cov);
567. rd_mon_h = new(rd_mon_if, rd_mon2sb, mon2cov);
568. ref_h = new(wr_mon2ref, ref2sb);
569. sb_h = new(rd_mon2sb, ref2sb);
570. cov_collector = new();
571. endtask
572.
573. // Coverage collection task
574. task collect_coverage();
575. transaction cov_tr;
576. forever begin
577. mon2cov.get(cov_tr); // Get transaction for coverage
578. cov_collector.sample(cov_tr); // Sample coverage
579. end
580. endtask
581.
582. // Run task - starts all components
583. task run();
584. fork
585. gen_h.start(); // Start generator
586. drv_h.start(); // Start driver
587. wr_mon_h.start(); // Start write monitor
588. rd_mon_h.start(); // Start read monitor
589. ref_h.start(); // Start reference model
590. sb_h.check(); // Start scoreboard checking
591. collect_coverage(); // Start coverage collection
592. join_none
593.
594. // Wait for simulation completion
595. wait (gen_h.number_of_transaction > 0 &&
596. sb_h.total_count >= gen_h.number_of_transaction);
597. #100;
598.
599. // Report results
600. cov_collector.report_coverage();
601. sb_h.report();
602. $finish;
603. endtask
604.
605. endclass
606.
607. // *********************************************** TEST
**********************************************************
608. class test;
609. // Virtual interfaces
610. virtual count_if.WR_DRV wr_drv_if;
611. virtual count_if.WR_MON wr_mon_if;
612. virtual count_if.RD_MON rd_mon_if;
613.
614. environment env_h; // Environment handle
615.
616. // Constructor
617. function new(virtual count_if.WR_DRV wr_drv_if,
618. virtual count_if.WR_MON wr_mon_if,
619. virtual count_if.RD_MON rd_mon_if);
620. this.wr_drv_if = wr_drv_if;
621. this.wr_mon_if = wr_mon_if;
622. this.rd_mon_if = rd_mon_if;
623. env_h = new(wr_drv_if, wr_mon_if, rd_mon_if);
624. endfunction
625.
626. // Build task
627. task build();
628. env_h.build();
629. endtask
630.
631. // Run task
632. task run();
633. env_h.gen_h.number_of_transaction = 50; // Set number of transactions
634. env_h.run();
635. endtask
636.
637. endclass
638.
639. // *********************************************** TOP MODULE
****************************************************
640. module top();
641. bit clk; // Clock signal
642. count_if DUV_IF(clk); // Interface instance
643. test t_h; // Test handle
644.
645. // DUT instantiation
646. counter DUV(
647. .clk(DUV_IF.clk),
648. .rst(DUV_IF.rst),
649. .data_in(DUV_IF.data_in),
650. .load(DUV_IF.load),
651. .mode(DUV_IF.mode),
652. .count(DUV_IF.count)
653. );
654.
655. // Clock generation
656. initial begin
657. clk = 0;
658. forever #10 clk = ~clk;
659. end
660.
661. // Test execution
662. initial begin
663. #1; // Small delay for initialization
664. t_h = new(DUV_IF, DUV_IF, DUV_IF); // Create test
665. t_h.build(); // Build environment
666. t_h.run(); // Run test
667. end
668.
669. // Waveform dumping
670. initial begin
671. $dumpfile("counter.vcd");
672. $dumpvars(0, top);
673. end
674.
675. // Simulation timeout
676. initial begin
677. #1000000; // 1ms timeout
678. $display("Simulation timeout!");
679. $finish;
680. end
681.
682. endmodule
Simulation Wavefrom And Fucnctional coverage repeot