Saturday, August 24, 2013

SV testbench try 1

Was shifting to a new apartment for the umpteenth time since I moved to Melbourne. While gathering my stuff I came across my Bachelor's files for VLSI design, Embedded stuff etc. My parents had shipped all that to US, and though it will be landing into recycling I decided to go through the programs once before parting ways with the files. Coincidently, they sent my friend Sonaki's VLSI file, some more happy memories from my Bachelor's.
I will be starting SV soon at work, and I decided to use the same undergrad simple kiddo programs to start with SV testbenches. So here it goes.

//----------------------------------------******************----------------------------
//8:1 mux, with enable
//-----------------------------------------****************-------------------------------
//Dataflow
//note: if you are using ncvhdl, the ports will be of type std_ulogic, and std_ulogic_vector.
library IEEE;
use IEEE.STD_LOGIC_1164.all;

entity mux is
    port( EN, S0, S1, S2: in STD_LOGIC;
        I: in STD_LOGIC_VECTOR(7 downto 0);
        Y: out STD_LOGIC );
end mux;

architecture dataflow of mux is
signal S0b, S1b, S2b, t0, t1, t2, t3, t4, t5, t6, t7, Y1, Y2: std_logic;
begin
S0b <= not S0;
S1b <= not S1;
S2b <= not S2;
t0<= I(0) AND S2b AND S1b AND S0b;
t1<= I(1) AND S2b AND S1b AND S0;
t2<= I(2) AND S2b AND S1 AND S0b;
t3<= I(3) AND S2b AND S1 AND S0;
t4<= I(4) AND S2 AND S1b AND S0b;
t5<= I(5) AND S2 AND S1b AND S0;
t6<= I(6) AND S2 AND S1 AND S0b;
t7<= I(7) AND S2 AND S1 AND S0;
Y1<= t0 OR t1 OR t2 OR t3;
Y2<= t4 OR t5 OR t6 OR t7;
Y <= (Y1 OR Y2) AND EN;
end dataflow;

//------------------*********------------
//Mux test bench - in verilog
//--------------------------------------------
`timescale 1ns/1ps

module test_mux(EN, bit3ctr, I, Y);
output reg EN; //DUT inputs reg
output reg [7:0] I;
input wire Y;  //DUT outputs wire
output reg [2:0] bit3ctr;

mux DUT(
    .EN (EN),
    .S0 (bit3ctr[0]),
    .S1 (bit3ctr[1]),
    .S2 (bit3ctr[2]),
    .I (I),
    .Y (Y)
);

//assign S0 = bit3ctr[0];  //got error that A reg is not a legal lvalue in this context

initial begin
    $monitor ("At t=%t, EN=%b, sel = %b, I = %b, Y = %b", $time, EN, bit3ctr, I, Y);
     EN = 0;
        bit3ctr = 3'b000;
    I = 8'h38;
    #55 EN = 1;
// while {} does not seem to work, took really long time but never went to next step!
// cannot use while (bit3ctr != 3'b000) begin #5 end because once u use begin, it expects a statement
// while (bit3ctr != 3'b000) #5 kind of acted like a if loop, when bit3ctr wasnt 0, it just went to next statement: I =AA
    while (bit3ctr != 3'b000) begin I = 8'h83; #5; end
//zero delay stuff creates problems. If you dont add some delay between the 2 whiles, it never enters while 2
     #5;
    while (bit3ctr != 3'b000) begin I = 8'hAA; #5; end
    #5;
    while (bit3ctr != 3'b000) begin I = 8'h55; #5; end
    #55 I = 8'hA5;
    #55 I = 8'h5A;
     #55
    $finish;
end

always
begin
    #5 bit3ctr = bit3ctr + 3'b001;    
end

endmodule


//-----------------------Commands to run------------
ncvhdl mux.vhd
ncvlog mux_test.v
ncelab test_mux         //test_mux is the name of testbench module
ncsim WORKLIB.test_mux:module


//-------I ran the testbench till 325 ns, some select Results to keep the blog short-----
At t=                   0, EN=0, sel = 000, I = 00111000, Y = 0
At t=               55000, EN=1, sel = 011, I = 10000011, Y = 0
At t=               75000, EN=1, sel = 111, I = 10000011, Y = 1
At t=              105000, EN=1, sel = 101, I = 10101010, Y = 1
At t=              140000, EN=1, sel = 100, I = 01010101, Y = 1

//----------------Some other design architectures for mux----
architecture structural of mux is
component inv
    port (A: in std_logic;
          B: out std_logic);
end component;

component and5
    port (A0, A1, A2, A3, A4: in std_logic;
          Y: out std_logic);
end component;

component or8
    port (A0, A1, A2, A3, A4, A5, A6, A7: in std_logic;
          Y: out std_logic);
end component;

signal S0b, S1b, S2b, t0, t1, t2, t3, t4, t5, t6, t7, Y1, Y2: std_logic;
begin
    U_I0: inv port map (S0, S0b);
    U_I1: inv port map (S1, S1b);
    U_I2: inv port map (S2, S2b);

    U_A0: and5 port map (I(0), S2b, S1b, S0b, EN, t0);
    U_A1: and5 port map (I(1), S2b, S1b, S0, EN, t1);
    U_A2: and5 port map (I(2), S2b, S1, S0b, EN, t2);
    U_A3: and5 port map (I(3), S2b, S1, S0, EN, t3);
    U_A4: and5 port map (I(4), S2, S1b, S0b, EN, t4);
    U_A5: and5 port map (I(5), S2, S1b, S0, EN, t5);
    U_A6: and5 port map (I(6), S2, S1, S0b, EN, t6);
    U_A7: and5 port map (I(7), S2, S1, S0, EN, t7);
    U_O1: or8 port map (t0, t1, t2, t3, t4, t5, t6, t7, Y);
end structural;

now you need to add entity + architecture description for all components: inv, and5, or8

//---------------behav architecture of mux-------
//Note: S is a vector in behav, S: in STD_LOGIC_VECTOR (2 downto 0);
architecture behav of mux is
begin
    process(S0, I, EN)
    begin
        if EN='0' then y<='0';
        else
            case S is
                when "000" => y<=I(0);
                when "001" => y<=I(1);
                when "010" => y<=I(2);
                when "011" => y<=I(3);
                when "100" => y<=I(4);
                when "101" => y<=I(5);
                when "110" => y<=I(6);
                when "111" => y<=I(7);
            end case;
        end if;
    end process;
end behav;

//-------Comments on the testbench--------
// In this test bench user needs to check each output against Inputs and selects. I shifted next to //System Verilog because you can use "assert" and make your job much easier.


//-----------------------------*****************-----------------
//2:4 decoder
//-----------------------------*****************-----------------
//2:4 decoder combinational = dataflow
//Y(0) = S1b.S0b
//Y(1) = S1b.S0
//Y(2) = S1.S0b
//Y(3) = S1.S0

library IEEE;
use IEEE.STD_LOGIC_1164.all;

entity dec24 is
    port( EN: in STD_uLOGIC;
        S: in STD_uLOGIC_VECTOR(1 downto 0);
        Y: out STD_uLOGIC_VECTOR(3 downto 0));
end dec24;

architecture behav of dec24 is
begin
process(S, EN)
begin
    if EN ='1' then
        case S is
            when "00"=>y<="0001";
            when "01"=>y<="0010";
            when "10"=>y<="0100";
            when "11"=>y<="1000";
            when others=>y<="0000"
        end case;
    else
        y<="0000";
    end if;
end process;
end behav;

// structural architecture of 2:4 decoder
//UI1: inv port map (S0, S0b);
//UI2: inv port map (S1, S1b);
//UA0: and3 port map (S0b, S1b, EN, Y(0));
//UA1: and3 port map (S0, S1b, EN, Y(1));
//UA2: and3 port map (S0b, S1, EN, Y(2));
//UA3: and3 port map (S0, S1, EN, Y(3));

//-----------------------------*****************-----------------
//My 1st system verilog testbench: for 2:4 decoder
//-----------------------------*****************-----------------
`timescale 1ns/1ps

module test_dec24(EN, S, Y);
output reg EN; //DUT inputs reg
output reg [1:0] S;
input wire [3:0] Y;  //DUT outputs wire

dec24 DUT(EN, S, Y);

initial begin
repeat(15)
    begin
    #2 S = $random ; EN = $random;
        #3
        if (EN==1'b0) assert (Y == 4'b0);
    else
    begin
        if (S==2'b00) assert (Y== 4'b0001);
        if (S==2'b01) assert (Y== 4'b0010);
        if (S==2'b10) assert (Y== 4'b0100);
        if (S==2'b11) assert (Y== 4'b1000);
    end
    $display("EN=%b, S=%b, Y=%b", EN, S, Y);
end
$finish;
end
endmodule

//---------commands to run---------
ncvhdl demux.vhd
ncvlog -sv dec24_test.sv
ncelab test_dec24
ncsim WORKLIB.test_dec24:module 



//----select output, to keep the blog short
EN=1, S=00, Y=0001
EN=1, S=01, Y=0010
EN=1, S=10, Y=0100
EN=0, S=01, Y=0000

//-----------------------------*****************-----------------
//Full adder
//-----------------------------*****************-----------------
// Note: if you are using this code on FPGA, Sonaki wrote in her report that less macrocells were used up with dataflow architecture.

architecture dataflow of fadd is
signal c:std_logic_vector(3 downto 0);
begin
    process(a, b,cin, c)
    begin
        s(0)<=a(0) xor b(0) xor cin;
        c(0)<=(a(0) and b(0)) or (b(0) and cin) or (a(0) and cin);
        s(1)<=a(1) xor b(1) xor c(0);
        c(1)<=(a(1) and b(1)) or (b(1) and c(0)) or (a(1) and c(0));
        s(2)<=a(2) xor b(2) xor c(1);
        c(2)<=(a(2) and b(2)) or (b(2) and c(1)) or (a(2) and c(1));
        s(3)<=a(3) xor b(3) xor c(2);
        c(3)<=(a(3) and b(3)) or (b(3) and c(2)) or (a(3) and c(2));
        cout<=c(3);
    end process;
end dataflow;

// Side note: I wrote a SV testbench, and assigned values to a,b using $random. I forgot to assign a //value to cin, and look what happened :) :
//Assertion test_bit4fadd.__assert_1 has failed a=0100, b=0001, sum=001xx
//Assertion test_bit4fadd.__assert_1 has failed a=1001, b=0011, sum=0110x


//--------------Testbench that tests Adder + mux-------
// Output of adder is input of mux
`timescale 1ns/1ps

module test_fadd_mux(cin, a, b, sel, Y);
output reg cin; //DUT inputs reg
output reg [3:0] a, b;
output reg [1:0] sel;
input wire Y;  //DUT outputs wire

//intermed
wire [4:0] SUM;  //DUT outputs wire


bit4fadd DUT1(
.cin (cin),
.a (a),
.b (b),
.cout (SUM[4]),
.s (SUM[3:0])
);

mux DUT2(
    .EN (1'b1),
    .S0 (sel[0]),
    .S1 (sel[1]),
    .S2 (cin),
     .I ({3'b0, SUM[4:0]}),
    .Y (Y)
); 

initial begin
repeat(15)
    begin
    a = $random ; b = $random; cin=$random; sel=$random;
        #4
        assert (SUM == a+b+cin);
    //assert (Y==SUM[sel]);
    if (cin==1'b0) assert (Y==SUM[sel]);
    if (cin==1'b1 && sel==2'b00) assert (Y==SUM[4]);
    if (cin==1'b1 && sel>2'b00) assert (Y==1'b0);
    $display("a=%b, b=%b, cin=%b, sum=%b, sel[2]=%b, sel[1:0]=%b, mux_output Y=%b", a, b, cin, SUM, cin, sel, Y);
end
$finish;
end
endmodule

//--------commands to run----
ncvhdl 4bitadder.vhd  
ncvhdl mux.vhd
ncvlog -sv fadd_mux_test.sv
ncelab test_fadd_mux
ncsim WORKLIB.test_fadd_mux:module 


Parting thoughts:
Assertion based test will only check code + functionality. We still have a long way to go in verification. If we were to test, say an FSM with a assertion based test case, we are essentially only checking it at discrete time stamps. We need a better test case, to test the rtl at each instant of time.
Its called self checking test, I am yet to try my hand at that. 

No comments:

Post a Comment