SystemVerilog concepts which are asked in the interviews usually target the logical and implemenattion ability of the interviewee. This means that some interviewers try to judge if the candidate has any understanding of the concept and also try to see if the candidate is able to apply the so learnt concepts. The easiest way to crack this kind of proccess is to be throughly aware of the different ways in which the same concept can be applied in practice.
module top; initial begin fork begin #0 $display( "A" ); end begin #0 #0 $display( "B" ); end join end endmodule
int ARR[*][]
for( int i = 0; i < 3; i++ ) begin fork $display( "i : %0d", i ); join_none end $display( "Hello World" );
/* Output : Hello World 3 3 3 Reason : 1. If you will look at the SystemVerilog Section on "fork-join_none", you will see that the fork_join_none statements are scheduled to be executed at the end of the active region, hence "Hello World" prints first and then before moving to inactive regions, the simulator runs the "i : %0d" statements; 2. The reason why 3,3,3 is printed is because, since it is a static variable, the i value updates after every loop, but since the statement itself never executed the value last value of i before moving to inactive region is used to executed the fork-join_none scheduled statements. 3. The reason why 3 is printed and not 2, is because in the "for" statement, i increments first in the previous loop and then the condition for i < 3 is checked. 4. The solution for this problem is, by utilizing an automatic variable. Solution is given below */ for( int i = 0; i < 3; i++ ) begin automatic k = i; fork $display( "i : %0d", k ); join_none end $display( "Hello World" );
/* ----------------------- DUT : Asynchronous FIFO ----------------------- Depth : 8 DATA_IN_WIDTH : data_in_1byte DATA_OUT_WIDTH : data_out_1byte PUSH : push_en_in_1bit POP : pop_en_in_1bit FULL : out_full_1bit EMPTY : out_empty_1bit RESET : reset_n_in_1bit */
/* -------- Testplan -------- 1. asyn_fifo_1_element_push_test Description : Simple FIFO push after reset 2. asyn_fifo_1_element_pop_test Description : Simple FIFO pop after push 3. asyn_fifo_full_push_test Description : Push elements until FULL flag is high and see it that is happening exactly after 8 PUSH 4. asyn_fifo_full_push_empty_pop_test Description : Push until FULL and then POP 8 times to see if EMPTY flag is begin asserted 5. asyn_fifo_full_push_4_empty_pop_4_full_push_test Description : PUSH until FULL, then POP 4 times and PUSH 4 times to see if the internal rollover logic for full condition is correctly working or not 6. asyn_fifo_push_4_empty_pop_4_full_push_8_empty_pop_test Description : PUSH unit 4 elements are in, then POP until EMPTY is high. Then see if after doing 8 PUSH and 8 POP, any data is recurring. This would mean that internally meomory is accessing only data location, which is incorrect behaviour. 7. asyn_fifo_full_push_2_extra_push_8_empty_pop_test Description : After 8 PUSH, PUSH 2 more times and then POP to see if any successfully PUSHed data has been overwritten or not during the additonal PUSH. 8. asyn_fifo_empty_to_full_to_empty_2_extra_pop_test Description : PUSH until full, then POP until empty and then POP 2 more times to see if the output is changing. 9. asyn_fifo_empty_to_full_4_pop_reset_empty_flag_test Description : PUSH 4 elements, then RESET and see if EMPTY is high. 10. asyn_fifo_push_pop_same_time_priority_test Description : PUSH and POP at the same clock edge of the two clock domains. As per design one of them will internally have a priority. This needs to be verified. */
/* Read Rate : 6 per 20 cycles Write Rate : 13 per 25 cycles Considering the worst case scenario for two back to back 25 cycles ( 25 since the nunmber is higher ) :- 1. 6 READS ( for the first 20 cycles ) + 6 READS ( for next 20 cycles ) + 0 READS ( for next 5 cycles. this number is in case of worst case ) = 12 READS per 50 cycles ( in worst case ) 2. 13 WRITES ( first 25 cycles ) + 13 WRITES ( next 25 cycles ) = 26 WRITES per 50 cycles ( worst case ) 3. Difference per 50 cycles : 26 WRITES - 12 READS = 14 WRITES ( remember that this is in worst case ) 4. Hence, a buffer/FIFO of depth 14 will be required. Solution : FIFO of 14 depth is required */
module ff (q,clk,t); input clk t; output reg q=0; always @(posedge clk) begin if (t) q <= ~q; end endmodule module tb; reg clk=0;t=1; wire q=0; always @#5 = ~clk; ff u1 (q,clk,t); always @(posedge clk) $display ($time,q); endmodule /* In above code since we are providing q with 0 and expecting it to invert its value at every posedge Expected value on $display #5 -> 1; #10 -> 0; #15 -> 1; #20 -> 0; But insted we will recieve #5 -> 0; #10 -> 1; #15 -> 0; #20 -> 1; This is happening because in the prponed region, value 0 has been sampled but in NBA region the inverse value will be assigned on next edge. So, if we prints value at current edge, value will be 0 for the preponed region. */
module top; int A[][*]; initial begin A = new[5]; A[0]["HELLO"] = 100; A[1]["BYE"] = 200; $display( "%p", A[0] ); $display( "%p", A[1] ); end endmodule
int num = ( %urandom >> 1 ) << 1; /* <OR> */ class ran; rand int num; constraint even { num[0] == 0; } endclass
class ran; rand bit [63:0] ph_num; constraint valid_nums { ph_num inside { 64'd0, 64'd911, [64'd1000_0000:64'd1999_9999], [64'd10_0000_0000:64'd19_9999_9999] }; } endclass
class ran; rand int base_addr[]; rand int offset[]; /* Example: ---------- -> 1024KB ( 2^20 -> 20bits ) | | | | | | | | | | | | -> offset[n+1]+base_addr[n+1] | | -> base_addr[n+1] -> ( SEGMENT N+1 ) | | | | | | | | | | -> offset[n]+base_addr[n] | | | | | | -> base_addr[n] -> ( SEGMENT N ) . . . . . . | | ---------- -> 0B */ constraint segments { base_addr.size == 8; offset.size == 8; } constraint addr { foreach( base_addr[i] ) { if( i > 0 ) { base_addr[i] >= base_addr[i-1]+offset[i-1]; } base_addr[i] >= 0 && base_addr[i] < 2**20; offset[i] >= 0 && offset[i] < 2**20; } } endclass
class ran; int num; function void post_randomize(); num = ( $urandom % 91 ) + 10; endfunction endclass
class ran; rand bit [31:0] data[]; rand int m0; rand int m1; constraint m0_m1_con { m0 < 10; m1 < 10; m0 > 0; m1 > 0; } constraint pattern { data.size == ( m0 + m1 ); foreach( data[i] ) { if( i < m0 ) { data[i] == 32'hFFFF_FFFF; } else { data[i] == 32'h0000_0000; } } } function void post_randomize; data.shuffle(); endfunction endclass
class ex; rand bit rand_arr [8][8]; // Horizontal, Vertical rand bit [7:0] h [8]; rand bit [7:0] v [8]; constraint board_conditions { foreach( h[i] ) { $countones(h[i]) == 1; $countones(v[i]) == 1; } foreach( v[i] ) { foreach( v[j] ) { v[j][i] == h[i][j]; rand_arr[i][j] == h[i][j]; } } } function void print_board(); int t; t = 0; foreach( rand_arr[i] ) begin foreach( rand_arr[j] ) begin if( t != i ) begin $display( "" ); t = i; end $write( "%1d ", rand_arr[i][j] ); end end $display( "\n" ); endfunction endclass module top; ex R; initial begin R = new(); repeat( 10 ) begin R.randomize(); R.print_board(); end end endmodule
module abc(); class abc; rand int arr[]; rand int num1,num2; constraint siz {arr.size ==10; num1 >0 ; num1 < arr.size; num2 >0; num2 <arr.size; num2 > num1+1; arr[num1] == arr[num2]; } constraint values { foreach(arr[i]) { arr[i] inside{[0:99]}; if (i<9) { arr[i] != arr[i+1]; } } } function new (int seed); srandom(seed); endfunction endclass initial begin abc a; a=new(10); void'(a.randomize); foreach (a.arr[i]) $display (a.arr[i]); $display ("karan num1 %0d num2 =%0d " ,a.num1,a.num2); end endmodule
class ran; rand int a; constraint con { $countones( a ) == 5; } /* There are more ways to solve this. See if you can try to think of them. */ endclass
class ran; rand int arr[]; constraint size_arr { arr.size == 15; } constraint sum { arr.sum() <= 100; } endclass
module top; bit [7:0] mask; bit [7:0] val_in; initial begin mask = $urandom; val_in = $urandom; val_in = mask ^ val_in; // XOR acts as an inverter when input is XORed with 1 and when XORed with 0, the input remains the same $display( "Output : %h", val_in ); end endmodule
/* Arbiter _____ R0 -| |- G0 R1 -| |- G1 R2 -| |- G2 R3 -|_____|- G3 1. This arbiter is driven by a simple CLK. There is no reset pin. 2. At any point of time, there can be multiple requests, but they are granted on a first come basis. 3. Grants can last from anywhere from 1-10 clock cycles. 4. REQ for any pin, must be high for only 1 clock cycle to be registered. More than that, and multiple requests for same source will be registered. */
class seq_item; rand bit [3:0] req[]; bit [3:0] gnt[]; // This is the value which tell which all req pins are high at a certain clock. // The array exists to give the seq the ability to drive request values over multiple // subsequent clock cycles // For example, if you wish to drive R0 as high for 2 clocks and R3 high for 3 clocks from the second clock cycle, // then the req variable will need to have values like this // req[0] = { 1'b0, 1'b0, 1'b0, 1'b1 } // req[1] = { 1'b1, 1'b0, 1'b0, 1'b1 } // req[2] = { 1'b1, 1'b0, 1'b0, 1'b0 } // req[3] = { 1'b1, 1'b0, 1'b0, 1'b0 } // When the above example is driven by the druven, the waveform will look like this // ___ ___ ___ ___ ___ // clock __| |___| |___| |___| |___| |___ // _______________ // req[0] __| |_______________________ // // req[1] __________________________________________ // // req[2] __________________________________________ // ______________________ // req[3] __________________| | constraint req_size { soft req.size == 4; // Default value ( as an example ) } constraint req_order { foreach( req[i] ) { soft $onehot( req[i] ); // Not exclusively neccessary } } // This function is used just to sample gnt values from the dut and send the framed packet to monitor function update_gnt( bit [3:0] gnt_val ); if( gnt.size ) begin gnt = new[gnt.size+1](gnt); gnt[gnt.size-1] = gnt_val; end else begin gnt = new[1]; gnt[0] = gnt_val; end endfunction endclass class driver; seq_item pkt; virtual intf_if intf; function new(); // Get intf ( using config_db::get ) endfunction task run_phase(); fork driving_logic(); sampling_logic(); join_none endtask task driving_logic(); forever begin // // get_next_item // foreach( pkt.req[i] ) begin @( posedge intf.clk ); { intf.R3, intf.R2, intf.R1, intf.R0 } <= pkt.req[i]; end // item_done end endtask task sampling_logic(); seq_item sampled; bit [3:0] sampled_val; forever begin sampled_val = 0; sampled = new; sampled_val = { intf.R3, intf.R2, intf.R1, intf.R0 }; begin wait( { intf.G3, intf.G2, intf.G1, intf.G0 } ); sampled.update_gnt( { intf.G3, intf.G2, intf.G1, intf.G0 } ); end @( posedge intf.clk ); // send pkt to SCB/MON end endtask endclass
function void check_string( string s ); signed int nb; signed int cb; signed int sb; foreach( s[i] ) begin case( s[i] ) "[" : sb++; "{" : cb++; "(" : nb++; "]" : sb--; "}" : cb--; ")" : nb--; endcase if( sb < 0 || cb < 0 || nb < 0 ) begin $display( "Error : String does not follow the requirements" ); return; end end if( sb == 0 && cb == 0 && nb == 0 ) begin $display( "Pass : String does follow the requirements" ); return; end else begin $display( "Error : String does not close properly" ); return; end endfunction
class sampler_pkt; bit [4-1:0] pattern_detect; bit start_transmit; byte pkg; int count; virtual interface dut_intf; // All functions execution function function new(); // Code to get the dut_intf using any method fork detect_and_transmit_func(); join endfunction function detect_and_transmit_func(); // Thread fork begin forever begin @( posedge dut_intf.clk ) begin pattern_detect = {pattern_detect[4-1-1:0],dut_intf.in}; if( start_transmit ) begin pkg = {pkg[8-1-1:0],dut_intf.in}; count++; if( count == 8 ) begin count = 0; // Send pkt to scbd CODE goes HERE // pkg variable is transmisste end end if( pattern_detect == 4'b1011 && !start_transmit ) begin start_transmit = 1; count = 0; end end end end join endfunction endclass
/* --- DUT --- _____ | | D_IN ---| |--- D_OUT T_IN ---| |--- T_OUT EN ---| |--- VLD |_____| 1. D_IN & D_OUT -> Input and Output pins respectively ( each of 8bits each ) 2. T_IN & T_OUT -> 1bit inputs and outputs respectively ( represent a TAG which is added along with the input data ) 3. EN -> 1bit enable input pin 4. VLD -> 1bit output valid pin which tells that the current output is valid transaction 5. Assume there is a CLK and RESET 6. D_IN and T_IN togather form a transaction which is sampled by the DUT only when EN is high 7. Multiple transactions with the same tag can be sampled and will be valid 8. For the same tag, at the output side, transaction will come out in ORDER 9. For different tags, the output can come out in any order Example : Sampled Input Transaction ------------------------- A. D_IN : 10, T_IN : 0 B. D_IN : 20, T_IN : 1 C. D_IN : 30, T_IN : 0 D. D_IN : 40, T_IN : 0 E. D_IN : 50, T_IN : 1 Acceptable Behaviour at output :> B - A - C - E - D Unacceptable Behaviour at output :> E - C - A - B - D ( here if you observer, E & B are out of order. Same for C & A. ) 10. Assume there are 4 monitors, two at the input and two at the output. The input ones individually work on transactions depending on the tag value. Same applies for the output. In short, for example a monitor at the input will only monitor all transactions with T_IN as 0 while the other input monitor will monitor all the transactions with the T_IN as 1. Same goes for the output monitors. */
class ran; rand int num; bit is_first = 1; bit even_odd; // 0 : Even, 1 : Odd constraint toggle { is_first == 0 -> num[0] != even_odd; } function void post_randomize(); even_odd = num[0]; is_first = 0; endfunction endclass
`timescale 1ns / 1ps module ClockGenerator #( parameter N = 1, // Output clock frequency in MHz parameter M = 50 // Duty cycle as a percentage )( output reg clk_out = 0 // Output clock with configurable duty cycle ); localparam PERIOD = 1000 / N; // Period of the clock in ns localparam HIGH_TIME = PERIOD * M / 100; // High time in ns localparam LOW_TIME = PERIOD - HIGH_TIME; // Low time in ns initial begin forever begin clk_out = 1'b1; #HIGH_TIME; clk_out = 1'b0; #LOW_TIME; end end endmodule // Testbench module tb_ClockGenerator(); wire clk_out; // Clock generator instance (e.g., 10 MHz with 25% duty cycle) ClockGenerator #(.N(10), .M(25)) generator ( .clk_out(clk_out) ); initial begin // Simulation run for 2000 ns to observe the clock $dumpfile("clock_generator.vcd"); $dumpvars(0, tb_ClockGenerator); #2000; $finish; end endmodule
`timescale 1ns / 1ps module PRBS_Generator( input clk, // Clock input input rst_n, // Active-low reset output reg prbs_out // Output pseudo-random bit sequence ); reg [8:0] lfsr; // 9-bit linear feedback shift register always @(posedge clk or negedge rst_n) begin if (!rst_n) begin lfsr <= 9'b1; // Reset LFSR to non-zero value end else begin // Feedback polynomial: X^9 + X^5 + X^1 + 1 lfsr <= {lfsr[7:0], (lfsr[8] ^ lfsr[4] ^ lfsr[0])}; end prbs_out <= lfsr[8]; // Output the MSB of the LFSR end endmodule // Testbench module tb_PRBS_Generator(); reg clk = 0; reg rst_n = 0; wire prbs_out; PRBS_Generator generator ( .clk(clk), .rst_n(rst_n), .prbs_out(prbs_out) ); // Clock generation always #10 clk = ~clk; // Generate a clock with a period of 20 ns initial begin // Reset sequence rst_n = 0; #40 rst_n = 1; // Release reset after 40 ns // Monitor output $monitor("At time %t, PRBS Output = %b", $time, prbs_out); // Run simulation for 2000 ns to observe behavior #2000; $finish; end endmodule
// Code your testbench here // or browse Examples `timescale 1ns / 1ps module SerialToParallel( input clk, // Clock input input rst_n, // Asynchronous reset, active low input serial_in, // Serial input data output reg [15:0] parallel_out, // Parallel output data output reg data_valid // Data valid output ); reg [4:0] bit_count = 0; // Counter to track the number of bits received always @(posedge clk or negedge rst_n) begin if (!rst_n) begin bit_count <= 0; parallel_out <= 0; data_valid <= 0; end else begin if (bit_count < 16) begin parallel_out <= (parallel_out << 1) | serial_in; // Shift in new bit bit_count <= bit_count + 1; data_valid <= 0; // Data is not yet valid end if (bit_count == 16) begin data_valid <= 1; // Indicate that the data is now valid bit_count <= 0; // Reset bit counter end end end endmodule // Testbench module tb_SerialToParallel(); reg clk = 0; reg rst_n = 0; reg serial_in = 0; wire [15:0] parallel_out; wire data_valid; SerialToParallel converter ( .clk(clk), .rst_n(rst_n), .serial_in(serial_in), .parallel_out(parallel_out), .data_valid(data_valid) ); // Clock generation always #5 clk = ~clk; // Generate a clock with a period of 10 ns initial begin // Reset sequence rst_n = 0; #15 rst_n = 1; // Release reset after 15 ns // Sending serial data #10 serial_in = 1; #10 serial_in = 0; #10 serial_in = 1; #10 serial_in = 1; #10 serial_in = 0; #10 serial_in = 1; #10 serial_in = 0; #10 serial_in = 0; #10 serial_in = 1; #10 serial_in = 0; #10 serial_in = 1; #10 serial_in = 0; #10 serial_in = 1; #10 serial_in = 1; #10 serial_in = 0; #10 serial_in = 1; // Observe output for additional 50 ns #50; $finish; end initial begin $monitor("At time %t, Output = %b, Data Valid = %b", $time, parallel_out, data_valid); end endmodule
`timescale 1ns / 1ps module OnesCounter( input [15:0] data_in, // 16-bit input bus output reg [4:0] count // 5-bit output for the count of 1's ); // Function to manually count the number of 1s function [4:0] countOnes(input [15:0] data); integer i; begin countOnes = 0; for (i = 0; i < 16; i = i + 1) begin countOnes = countOnes + data[i]; end end endfunction // Count the 1s whenever data_in changes always @(data_in) begin count = countOnes(data_in); end endmodule // Testbench module tb_OnesCounter(); reg [15:0] data_in; wire [4:0] count; OnesCounter counter( .data_in(data_in), .count(count) ); initial begin // Monitor changes $monitor("At time %t, Input = %b, Number of 1s = %d", $time, data_in, count); // Test different values data_in = 16'hFFFF; // All ones, expect 16 #10; data_in = 16'hF0F0; // 8 ones #10; data_in = 16'h1234; // 5 ones #10; data_in = 16'h0000; // No ones #10; data_in = 16'h8001; // 2 ones #10; // Finish the simulation $finish; end endmodule
`timescale 1ns / 1ps class Person; string name; int age; int scores[]; // Dynamic array of scores // Constructor to initialize the Person object function new(string init_name, int init_age, int init_scores[]); this.name = init_name; this.age = init_age; this.scores = new[int'(init_scores.size())] (init_scores); endfunction // Deep copy function function Person deep_copy(); Person copy = new(this.name, this.age, this.scores); return copy; endfunction endclass // Testbench to demonstrate the use of deep copy module tb_Person; Person original; Person copy; initial begin int scores[] = {90, 85, 93}; original = new("John Doe", 30, scores); copy = original.deep_copy(); // Display original and copied data $display("Original: %s, %d, Scores: %p", original.name, original.age, original.scores); $display("Copy: %s, %d, Scores: %p", copy.name, copy.age, copy.scores); // Modify the original object to demonstrate that the copy is independent original.name = "Jane Doe"; original.age = 32; original.scores[0] = 75; // Display modified original and unchanged copy $display("Modified Original: %s, %d, Scores: %p", original.name, original.age, original.scores); $display("Unchanged Copy: %s, %d, Scores: %p", copy.name, copy.age, copy.scores); // Finish the simulation $finish; end endmodule
class RAND; rand int DARR0[]; rand int DARR_EVEN[]; endclass
class RAND; rand int DARR0[]; rand int DARR_EVEN[]; rand int ctr[]; constraint darr0 { DARR0.size < 101; ctr.size == DARR0.size; foreach( DARR0[i] ) { DARR0[i] > 0; DARR0[i] < 1000; } } constraint darr_even { foreach( ctr[i] ) { foreach( DARR0[j] ) { if( j > i && DARR[j] % 2 == 0 ) { DARR_EVEN[i] == DARR[j]; } } } } endclass